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