ea599746489a0155490138a5f2c1968a0d7c1e8f
[dragonfly.git] / sys / kern / subr_dsched.c
1 /*
2  * Copyright (c) 2009, 2010 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Alex Hornung <ahornung@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/proc.h>
38 #include <sys/sysctl.h>
39 #include <sys/buf.h>
40 #include <sys/conf.h>
41 #include <sys/diskslice.h>
42 #include <sys/disk.h>
43 #include <sys/malloc.h>
44 #include <sys/sysctl.h>
45 #include <machine/md_var.h>
46 #include <sys/ctype.h>
47 #include <sys/syslog.h>
48 #include <sys/device.h>
49 #include <sys/msgport.h>
50 #include <sys/msgport2.h>
51 #include <sys/buf2.h>
52 #include <sys/dsched.h>
53 #include <sys/fcntl.h>
54 #include <machine/varargs.h>
55
56 MALLOC_DEFINE(M_DSCHED, "dsched", "Disk Scheduler Framework allocations");
57
58 static dsched_prepare_t default_prepare;
59 static dsched_teardown_t        default_teardown;
60 static dsched_flush_t   default_flush;
61 static dsched_cancel_t  default_cancel;
62 static dsched_queue_t   default_queue;
63 #if 0
64 static biodone_t        default_completed;
65 #endif
66
67 dsched_new_buf_t        *default_new_buf;
68 dsched_new_proc_t       *default_new_proc;
69 dsched_new_thread_t     *default_new_thread;
70 dsched_exit_proc_t      *default_exit_proc;
71 dsched_exit_thread_t    *default_exit_thread;
72
73 static d_open_t      dsched_dev_open;
74 static d_close_t     dsched_dev_close;
75 static d_ioctl_t     dsched_dev_ioctl;
76
77 static int dsched_dev_list_disks(struct dsched_ioctl *data);
78 static int dsched_dev_list_disk(struct dsched_ioctl *data);
79 static int dsched_dev_list_policies(struct dsched_ioctl *data);
80 static int dsched_dev_handle_switch(char *disk, char *policy);
81
82
83 struct lock     dsched_lock;
84 static int      dsched_debug_enable = 0;
85 static int      dsched_test1 = 0;
86 static cdev_t   dsched_dev;
87
88 static struct dsched_policy_head dsched_policy_list =
89                 TAILQ_HEAD_INITIALIZER(dsched_policy_list);
90
91 static struct dsched_ops dsched_default_ops = {
92         .head = {
93                 .name = "default"
94         },
95         .prepare = default_prepare,
96         .teardown = default_teardown,
97         .flush = default_flush,
98         .cancel_all = default_cancel,
99         .bio_queue = default_queue,
100 };
101
102
103 static struct dev_ops dsched_dev_ops = {
104         { "dsched", 0, 0 },
105         .d_open = dsched_dev_open,
106         .d_close = dsched_dev_close,
107         .d_ioctl = dsched_dev_ioctl
108 };
109
110 /*
111  * dsched_debug() is a SYSCTL and TUNABLE controlled debug output function
112  * using kvprintf
113  */
114 int
115 dsched_debug(int level, char *fmt, ...)
116 {
117         __va_list ap;
118
119         __va_start(ap, fmt);
120         if (level <= dsched_debug_enable)
121                 kvprintf(fmt, ap);
122         __va_end(ap);
123
124         return 0;
125 }
126
127 /*
128  * Called on disk_create()
129  * tries to read which policy to use from loader.conf, if there's
130  * none specified, the default policy is used.
131  */
132 void
133 dsched_create(struct disk *dp, const char *head_name, int unit)
134 {
135         char tunable_key[SPECNAMELEN + 11];
136         char sched_policy[DSCHED_POLICY_NAME_LENGTH];
137         struct dsched_policy *policy = NULL;
138
139         /* Also look for serno stuff? */
140         /* kprintf("dsched_create() for disk %s%d\n", head_name, unit); */
141         lockmgr(&dsched_lock, LK_EXCLUSIVE);
142
143         ksnprintf(tunable_key, sizeof(tunable_key), "scheduler_%s%d",
144             head_name, unit);
145         if (TUNABLE_STR_FETCH(tunable_key, sched_policy,
146             sizeof(sched_policy)) != 0) {
147                 policy = dsched_find_policy(sched_policy);
148         }
149
150         ksnprintf(tunable_key, sizeof(tunable_key), "scheduler_%s*",
151             head_name);
152         if (!policy && (TUNABLE_STR_FETCH(tunable_key, sched_policy,
153             sizeof(sched_policy)) != 0)) {
154                 policy = dsched_find_policy(sched_policy);
155         }
156
157         ksnprintf(tunable_key, sizeof(tunable_key), "scheduler_*");
158         if (!policy && (TUNABLE_STR_FETCH(tunable_key, sched_policy,
159             sizeof(sched_policy)) != 0)) {
160                 policy = dsched_find_policy(sched_policy);
161         }
162
163         if (!policy) {
164                 dsched_debug(0, "No policy for %s%d specified, "
165                     "or policy not found\n", head_name, unit);
166                 dsched_set_policy(dp, &dsched_default_ops);
167         } else {
168                 dsched_set_policy(dp, policy->d_ops);
169         }
170
171         lockmgr(&dsched_lock, LK_RELEASE);
172 }
173
174 /*
175  * Called on disk_destroy()
176  * shuts down the scheduler core and cancels all remaining bios
177  */
178 void
179 dsched_destroy(struct disk *dp)
180 {
181         struct dsched_ops *old_ops;
182
183         lockmgr(&dsched_lock, LK_EXCLUSIVE);
184
185         old_ops = dp->d_sched_ops;
186         dp->d_sched_ops = &dsched_default_ops;
187         old_ops->cancel_all(dp);
188         old_ops->teardown(dp);
189         atomic_subtract_int(&old_ops->head.ref_count, 1);
190         KKASSERT(old_ops->head.ref_count >= 0);
191
192         lockmgr(&dsched_lock, LK_RELEASE);
193 }
194
195
196 void
197 dsched_queue(struct disk *dp, struct bio *bio)
198 {
199         int error = 0;
200         error = dp->d_sched_ops->bio_queue(dp, bio);
201
202         if (error) {
203                 if (bio->bio_buf->b_cmd == BUF_CMD_FLUSH) {
204                         dp->d_sched_ops->flush(dp, bio);
205                 }
206                 dsched_strategy_raw(dp, bio);
207         }
208 }
209
210
211 /*
212  * Called from each module_init or module_attach of each policy
213  * registers the policy in the local policy list.
214  */
215 int
216 dsched_register(struct dsched_ops *d_ops)
217 {
218         struct dsched_policy *policy;
219         int error = 0;
220
221         lockmgr(&dsched_lock, LK_EXCLUSIVE);
222
223         policy = dsched_find_policy(d_ops->head.name);
224
225         if (!policy) {
226                 if ((d_ops->new_buf != NULL) || (d_ops->new_proc != NULL) ||
227                     (d_ops->new_thread != NULL)) {
228                         /*
229                          * Policy ops has hooks for proc/thread/buf creation,
230                          * so check if there are already hooks for those present
231                          * and if so, stop right now.
232                          */
233                         if ((default_new_buf != NULL) || (default_new_proc != NULL) ||
234                             (default_new_thread != NULL) || (default_exit_proc != NULL) ||
235                             (default_exit_thread != NULL)) {
236                                 dsched_debug(LOG_ERR, "A policy with "
237                                     "proc/thread/buf hooks is already in use!");
238                                 error = 1;
239                                 goto done;
240                         }
241
242                         /* If everything is fine, just register the hooks */
243                         default_new_buf = d_ops->new_buf;
244                         default_new_proc = d_ops->new_proc;
245                         default_new_thread = d_ops->new_thread;
246                         default_exit_proc = d_ops->exit_proc;
247                         default_exit_thread = d_ops->exit_thread;
248                 }
249
250                 policy = kmalloc(sizeof(struct dsched_policy), M_DSCHED, M_WAITOK);
251                 policy->d_ops = d_ops;
252                 TAILQ_INSERT_TAIL(&dsched_policy_list, policy, link);
253                 atomic_add_int(&policy->d_ops->head.ref_count, 1);
254         } else {
255                 dsched_debug(LOG_ERR, "Policy with name %s already registered!\n",
256                     d_ops->head.name);
257                 error = 1;
258         }
259
260 done:
261         lockmgr(&dsched_lock, LK_RELEASE);
262         return error;
263 }
264
265 /*
266  * Called from each module_detach of each policy
267  * unregisters the policy
268  */
269 int
270 dsched_unregister(struct dsched_ops *d_ops)
271 {
272         struct dsched_policy *policy;
273
274         lockmgr(&dsched_lock, LK_EXCLUSIVE);
275         policy = dsched_find_policy(d_ops->head.name);
276
277         if (policy) {
278                 if (policy->d_ops->head.ref_count > 1)
279                         return 1;
280                 TAILQ_REMOVE(&dsched_policy_list, policy, link);
281                 atomic_subtract_int(&policy->d_ops->head.ref_count, 1);
282                 KKASSERT(policy->d_ops->head.ref_count >= 0);
283                 kfree(policy, M_DSCHED);
284         }
285         lockmgr(&dsched_lock, LK_RELEASE);
286         return 0;
287 }
288
289
290 /*
291  * switches the policy by first removing the old one and then
292  * enabling the new one.
293  */
294 int
295 dsched_switch(struct disk *dp, struct dsched_ops *new_ops)
296 {
297         struct dsched_ops *old_ops;
298
299         /* If we are asked to set the same policy, do nothing */
300         if (dp->d_sched_ops == new_ops)
301                 return 0;
302
303         /* lock everything down, diskwise */
304         lockmgr(&dsched_lock, LK_EXCLUSIVE);
305         old_ops = dp->d_sched_ops;
306
307         atomic_subtract_int(&dp->d_sched_ops->head.ref_count, 1);
308         KKASSERT(dp->d_sched_ops->head.ref_count >= 0);
309
310         dp->d_sched_ops = &dsched_default_ops;
311         old_ops->teardown(dp);
312
313         /* Bring everything back to life */
314         dsched_set_policy(dp, new_ops);
315                 lockmgr(&dsched_lock, LK_RELEASE);
316         return 0;
317 }
318
319
320 /*
321  * Loads a given policy and attaches it to the specified disk.
322  * Also initializes the core for the policy
323  */
324 void
325 dsched_set_policy(struct disk *dp, struct dsched_ops *new_ops)
326 {
327         int locked = 0;
328
329         /* Check if it is locked already. if not, we acquire the devfs lock */
330         if (!(lockstatus(&dsched_lock, curthread)) == LK_EXCLUSIVE) {
331                 lockmgr(&dsched_lock, LK_EXCLUSIVE);
332                 locked = 1;
333         }
334
335         new_ops->prepare(dp);
336         dp->d_sched_ops = new_ops;
337         atomic_add_int(&new_ops->head.ref_count, 1);
338         kprintf("disk scheduler: set policy of %s to %s\n", dp->d_cdev->si_name,
339             new_ops->head.name);
340
341         /* If we acquired the lock, we also get rid of it */
342         if (locked)
343                 lockmgr(&dsched_lock, LK_RELEASE);
344 }
345
346 struct dsched_policy*
347 dsched_find_policy(char *search)
348 {
349         struct dsched_policy *policy;
350         struct dsched_policy *policy_found = NULL;
351         int locked = 0;
352
353         /* Check if it is locked already. if not, we acquire the devfs lock */
354         if (!(lockstatus(&dsched_lock, curthread)) == LK_EXCLUSIVE) {
355                 lockmgr(&dsched_lock, LK_EXCLUSIVE);
356                 locked = 1;
357         }
358
359         TAILQ_FOREACH(policy, &dsched_policy_list, link) {
360                 if (!strcmp(policy->d_ops->head.name, search)) {
361                         policy_found = policy;
362                         break;
363                 }
364         }
365
366         /* If we acquired the lock, we also get rid of it */
367         if (locked)
368                 lockmgr(&dsched_lock, LK_RELEASE);
369
370         return policy_found;
371 }
372
373 struct disk*
374 dsched_find_disk(char *search)
375 {
376         struct disk *dp_found = NULL;
377         struct disk *dp = NULL;
378
379         while((dp = disk_enumerate(dp))) {
380                 if (!strcmp(dp->d_cdev->si_name, search)) {
381                         dp_found = dp;
382                         break;
383                 }
384         }
385
386         return dp_found;
387 }
388
389 struct disk*
390 dsched_disk_enumerate(struct disk *dp, struct dsched_ops *ops)
391 {
392         while ((dp = disk_enumerate(dp))) {
393                 if (dp->d_sched_ops == ops)
394                         return dp;
395         }
396
397         return NULL;
398 }
399
400 struct dsched_policy *
401 dsched_policy_enumerate(struct dsched_policy *pol)
402 {
403         if (!pol)
404                 return (TAILQ_FIRST(&dsched_policy_list));
405         else
406                 return (TAILQ_NEXT(pol, link));
407 }
408
409 void
410 dsched_cancel_bio(struct bio *bp)
411 {
412         bp->bio_buf->b_error = ENXIO;
413         bp->bio_buf->b_flags |= B_ERROR;
414         bp->bio_buf->b_resid = bp->bio_buf->b_bcount;
415
416         biodone(bp);
417 }
418
419 void
420 dsched_strategy_raw(struct disk *dp, struct bio *bp)
421 {
422         /*
423          * Ideally, this stuff shouldn't be needed... but just in case, we leave it in
424          * to avoid panics
425          */
426         KASSERT(dp->d_rawdev != NULL, ("dsched_strategy_raw sees NULL d_rawdev!!"));
427         if(bp->bio_track != NULL) {
428                 dsched_debug(LOG_INFO,
429                     "dsched_strategy_raw sees non-NULL bio_track!! "
430                     "bio: %x\n", (uint32_t)bp);
431                 bp->bio_track = NULL;
432         }
433         dev_dstrategy(dp->d_rawdev, bp);
434 }
435
436 void
437 dsched_strategy_sync(struct disk *dp, struct bio *bio)
438 {
439         struct buf *bp, *nbp;
440         struct bio *nbio;
441
442         bp = bio->bio_buf;
443
444         nbp = getpbuf(NULL);
445         nbio = &nbp->b_bio1;
446
447         nbp->b_cmd = bp->b_cmd;
448         nbp->b_bufsize = bp->b_bufsize;
449         nbp->b_runningbufspace = bp->b_runningbufspace;
450         nbp->b_bcount = bp->b_bcount;
451         nbp->b_resid = bp->b_resid;
452         nbp->b_data = bp->b_data;
453         nbp->b_kvabase = bp->b_kvabase;
454         nbp->b_kvasize = bp->b_kvasize;
455         nbp->b_dirtyend = bp->b_dirtyend;
456
457         nbio->bio_done = biodone_sync;
458         nbio->bio_flags |= BIO_SYNC;
459         nbio->bio_track = NULL;
460
461         nbio->bio_caller_info1.ptr = dp;
462         nbio->bio_offset = bio->bio_offset;
463
464         dev_dstrategy(dp->d_rawdev, nbio);
465         biowait(nbio, "dschedsync");
466         bp->b_resid = nbp->b_resid;
467         bp->b_error = nbp->b_error;
468         biodone(bio);
469 }
470
471 void
472 dsched_strategy_async(struct disk *dp, struct bio *bio, biodone_t *done, void *priv)
473 {
474         struct bio *nbio;
475
476         nbio = push_bio(bio);
477         nbio->bio_done = done;
478         nbio->bio_offset = bio->bio_offset;
479
480         dsched_set_bio_dp(nbio, dp);
481         dsched_set_bio_priv(nbio, priv);
482
483         getmicrotime(&nbio->bio_caller_info3.tv);
484         dev_dstrategy(dp->d_rawdev, nbio);
485 }
486
487 void
488 dsched_new_buf(struct buf *bp)
489 {
490         if (default_new_buf != NULL)
491                 default_new_buf(bp);
492 }
493
494
495 void
496 dsched_new_proc(struct proc *p)
497 {
498         if (default_new_proc != NULL)
499                 default_new_proc(p);
500 }
501
502
503 void
504 dsched_new_thread(struct thread *td)
505 {
506         if (default_new_thread != NULL)
507                 default_new_thread(td);
508 }
509
510 void
511 dsched_exit_proc(struct proc *p)
512 {
513         if (default_exit_proc != NULL)
514                 default_exit_proc(p);
515 }
516
517
518 void
519 dsched_exit_thread(struct thread *td)
520 {
521         if (default_exit_thread != NULL)
522                 default_exit_thread(td);
523 }
524
525 int
526 default_prepare(struct disk *dp)
527 {
528         return 0;
529 }
530
531 void
532 default_teardown(struct disk *dp)
533 {
534
535 }
536
537 void
538 default_flush(struct disk *dp, struct bio *bio)
539 {
540
541 }
542
543 void
544 default_cancel(struct disk *dp)
545 {
546
547 }
548
549 int
550 default_queue(struct disk *dp, struct bio *bio)
551 {
552         dsched_strategy_raw(dp, bio);
553 #if 0
554         dsched_strategy_async(dp, bio, default_completed, NULL);
555 #endif
556         return 0;
557 }
558
559 #if 0
560 void
561 default_completed(struct bio *bp)
562 {
563         struct bio *obio;
564
565         obio = pop_bio(bp);
566         biodone(obio);
567 }
568 #endif
569
570 /*
571  * dsched device stuff
572  */
573
574 static int
575 dsched_dev_list_disks(struct dsched_ioctl *data)
576 {
577         struct disk *dp = NULL;
578         uint32_t i;
579
580         for (i = 0; (i <= data->num_elem) && (dp = disk_enumerate(dp)); i++);
581
582         if (dp == NULL)
583                 return -1;
584
585         strncpy(data->dev_name, dp->d_cdev->si_name, sizeof(data->dev_name));
586
587         if (dp->d_sched_ops) {
588                 strncpy(data->pol_name, dp->d_sched_ops->head.name,
589                     sizeof(data->pol_name));
590         } else {
591                 strncpy(data->pol_name, "N/A (error)", 12);
592         }
593
594         return 0;
595 }
596
597 static int
598 dsched_dev_list_disk(struct dsched_ioctl *data)
599 {
600         struct disk *dp = NULL;
601         int found = 0;
602
603         while ((dp = disk_enumerate(dp))) {
604                 if (!strncmp(dp->d_cdev->si_name, data->dev_name,
605                     sizeof(data->dev_name))) {
606                         KKASSERT(dp->d_sched_ops != NULL);
607
608                         found = 1;
609                         strncpy(data->pol_name, dp->d_sched_ops->head.name,
610                             sizeof(data->pol_name));
611                         break;
612                 }
613         }
614         if (!found)
615                 return -1;
616
617         return 0;
618 }
619
620 static int
621 dsched_dev_list_policies(struct dsched_ioctl *data)
622 {
623         struct dsched_policy *pol = NULL;
624         uint32_t i;
625
626         for (i = 0; (i <= data->num_elem) && (pol = dsched_policy_enumerate(pol)); i++);
627
628         if (pol == NULL)
629                 return -1;
630
631         strncpy(data->pol_name, pol->d_ops->head.name, sizeof(data->pol_name));
632         return 0;
633 }
634
635 static int
636 dsched_dev_handle_switch(char *disk, char *policy)
637 {
638         struct disk *dp;
639         struct dsched_policy *pol;
640
641         dp = dsched_find_disk(disk);
642         pol = dsched_find_policy(policy);
643
644         if ((dp == NULL) || (pol == NULL))
645                 return -1;
646
647         return (dsched_switch(dp, pol->d_ops));
648 }
649
650 static int
651 dsched_dev_open(struct dev_open_args *ap)
652 {
653         /*
654          * Only allow read-write access.
655          */
656         if (((ap->a_oflags & FWRITE) == 0) || ((ap->a_oflags & FREAD) == 0))
657                 return(EPERM);
658
659         /*
660          * We don't allow nonblocking access.
661          */
662         if ((ap->a_oflags & O_NONBLOCK) != 0) {
663                 kprintf("dsched_dev: can't do nonblocking access\n");
664                 return(ENODEV);
665         }
666
667         return 0;
668 }
669
670 static int
671 dsched_dev_close(struct dev_close_args *ap)
672 {
673         return 0;
674 }
675
676 static int
677 dsched_dev_ioctl(struct dev_ioctl_args *ap)
678 {
679         int error;
680         struct dsched_ioctl *data;
681
682         error = 0;
683         data = (struct dsched_ioctl *)ap->a_data;
684
685         switch(ap->a_cmd) {
686         case DSCHED_SET_DEVICE_POLICY:
687                 if (dsched_dev_handle_switch(data->dev_name, data->pol_name))
688                         error = ENOENT; /* No such file or directory */
689                 break;
690
691         case DSCHED_LIST_DISK:
692                 if (dsched_dev_list_disk(data) != 0) {
693                         error = EINVAL; /* Invalid argument */
694                 }
695                 break;
696
697         case DSCHED_LIST_DISKS:
698                 if (dsched_dev_list_disks(data) != 0) {
699                         error = EINVAL; /* Invalid argument */
700                 }
701                 break;
702
703         case DSCHED_LIST_POLICIES:
704                 if (dsched_dev_list_policies(data) != 0) {
705                         error = EINVAL; /* Invalid argument */
706                 }
707                 break;
708
709
710         default:
711                 error = ENOTTY; /* Inappropriate ioctl for device */
712                 break;
713         }
714
715         return(error);
716 }
717
718 /*
719  * SYSINIT stuff
720  */
721
722
723 static void
724 dsched_init(void)
725 {
726         lockinit(&dsched_lock, "dsched lock", 0, 0);
727         dsched_register(&dsched_default_ops);
728 }
729
730 static void
731 dsched_uninit(void)
732 {
733 }
734
735 static void
736 dsched_dev_init(void)
737 {
738         dsched_dev = make_dev(&dsched_dev_ops,
739             0,
740             UID_ROOT,
741             GID_WHEEL,
742             0600,
743             "dsched");
744 }
745
746 static void
747 dsched_dev_uninit(void)
748 {
749         destroy_dev(dsched_dev);
750 }
751
752 SYSINIT(subr_dsched_register, SI_SUB_CREATE_INIT-2, SI_ORDER_FIRST, dsched_init, NULL);
753 SYSUNINIT(subr_dsched_register, SI_SUB_CREATE_INIT-2, SI_ORDER_ANY, dsched_uninit, NULL);
754 SYSINIT(subr_dsched_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, dsched_dev_init, NULL);
755 SYSUNINIT(subr_dsched_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, dsched_dev_uninit, NULL);
756
757 /*
758  * SYSCTL stuff
759  */
760 SYSCTL_INT(_kern, OID_AUTO, dsched_debug, CTLFLAG_RW, &dsched_debug_enable,
761                 0, "Enable dsched debugging");
762 SYSCTL_INT(_kern, OID_AUTO, dsched_test1, CTLFLAG_RW, &dsched_test1,
763                 0, "Switch dsched test1 method");