e0d6f71cd17f6c1a6fb42f463be9b1d3aaf2c9c0
[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 <machine/md_var.h>
45 #include <sys/ctype.h>
46 #include <sys/syslog.h>
47 #include <sys/device.h>
48 #include <sys/msgport.h>
49 #include <sys/msgport2.h>
50 #include <sys/buf2.h>
51 #include <sys/dsched.h>
52 #include <sys/fcntl.h>
53 #include <machine/varargs.h>
54
55 TAILQ_HEAD(tdio_list_head, dsched_thread_io);
56
57 MALLOC_DEFINE(M_DSCHED, "dsched", "dsched allocs");
58
59 static dsched_prepare_t         noop_prepare;
60 static dsched_teardown_t        noop_teardown;
61 static dsched_cancel_t          noop_cancel;
62 static dsched_queue_t           noop_queue;
63
64 static void dsched_sysctl_add_disk(struct dsched_disk_ctx *diskctx, char *name);
65 static void dsched_disk_ctx_destroy(struct dsched_disk_ctx *diskctx);
66 static void dsched_thread_io_destroy(struct dsched_thread_io *tdio);
67 static void dsched_thread_ctx_destroy(struct dsched_thread_ctx *tdctx);
68
69 static int      dsched_inited = 0;
70 static int      default_set = 0;
71
72 struct lock     dsched_lock;
73 static int      dsched_debug_enable = 0;
74
75 struct dsched_stats     dsched_stats;
76
77 struct objcache_malloc_args dsched_disk_ctx_malloc_args = {
78         DSCHED_DISK_CTX_MAX_SZ, M_DSCHED };
79 struct objcache_malloc_args dsched_thread_io_malloc_args = {
80         DSCHED_THREAD_IO_MAX_SZ, M_DSCHED };
81 struct objcache_malloc_args dsched_thread_ctx_malloc_args = {
82         DSCHED_THREAD_CTX_MAX_SZ, M_DSCHED };
83
84 static struct objcache  *dsched_diskctx_cache;
85 static struct objcache  *dsched_tdctx_cache;
86 static struct objcache  *dsched_tdio_cache;
87
88 TAILQ_HEAD(, dsched_thread_ctx) dsched_tdctx_list =
89                 TAILQ_HEAD_INITIALIZER(dsched_tdctx_list);
90
91 struct lock     dsched_tdctx_lock;
92
93 static struct dsched_policy_head dsched_policy_list =
94                 TAILQ_HEAD_INITIALIZER(dsched_policy_list);
95
96 static struct dsched_policy dsched_noop_policy = {
97         .name = "noop",
98
99         .prepare = noop_prepare,
100         .teardown = noop_teardown,
101         .cancel_all = noop_cancel,
102         .bio_queue = noop_queue
103 };
104
105 static struct dsched_policy *default_policy = &dsched_noop_policy;
106
107 /*
108  * dsched_debug() is a SYSCTL and TUNABLE controlled debug output function
109  * using kvprintf
110  */
111 int
112 dsched_debug(int level, char *fmt, ...)
113 {
114         __va_list ap;
115
116         __va_start(ap, fmt);
117         if (level <= dsched_debug_enable)
118                 kvprintf(fmt, ap);
119         __va_end(ap);
120
121         return 0;
122 }
123
124 /*
125  * Called on disk_create()
126  * tries to read which policy to use from loader.conf, if there's
127  * none specified, the default policy is used.
128  */
129 void
130 dsched_disk_create_callback(struct disk *dp, const char *head_name, int unit)
131 {
132         char tunable_key[SPECNAMELEN + 48];
133         char sched_policy[DSCHED_POLICY_NAME_LENGTH];
134         char *ptr;
135         struct dsched_policy *policy = NULL;
136
137         /* Also look for serno stuff? */
138         /* kprintf("dsched_disk_create_callback() for disk %s%d\n", head_name, unit); */
139         lockmgr(&dsched_lock, LK_EXCLUSIVE);
140
141         ksnprintf(tunable_key, sizeof(tunable_key), "dsched.policy.%s%d",
142             head_name, unit);
143         if (TUNABLE_STR_FETCH(tunable_key, sched_policy,
144             sizeof(sched_policy)) != 0) {
145                 policy = dsched_find_policy(sched_policy);
146         }
147
148         ksnprintf(tunable_key, sizeof(tunable_key), "dsched.policy.%s",
149             head_name);
150         for (ptr = tunable_key; *ptr; ptr++) {
151                 if (*ptr == '/')
152                         *ptr = '-';
153         }
154         if (!policy && (TUNABLE_STR_FETCH(tunable_key, sched_policy,
155             sizeof(sched_policy)) != 0)) {
156                 policy = dsched_find_policy(sched_policy);
157         }
158
159         ksnprintf(tunable_key, sizeof(tunable_key), "dsched.policy.default");
160         if (!policy && !default_set && (TUNABLE_STR_FETCH(tunable_key, sched_policy,
161             sizeof(sched_policy)) != 0)) {
162                 policy = dsched_find_policy(sched_policy);
163         }
164
165         if (!policy) {
166                 if (!default_set && bootverbose) {
167                         dsched_debug(0,
168                                      "No policy for %s%d specified, "
169                                      "or policy not found\n",
170                                      head_name, unit);
171                 }
172                 dsched_set_policy(dp, default_policy);
173         } else {
174                 dsched_set_policy(dp, policy);
175         }
176
177         if (strncmp(head_name, "mapper/", strlen("mapper/")) == 0)
178                 ksnprintf(tunable_key, sizeof(tunable_key), "%s", head_name);
179         else
180                 ksnprintf(tunable_key, sizeof(tunable_key), "%s%d", head_name, unit);
181         for (ptr = tunable_key; *ptr; ptr++) {
182                 if (*ptr == '/')
183                         *ptr = '-';
184         }
185         dsched_sysctl_add_disk(
186             (struct dsched_disk_ctx *)dsched_get_disk_priv(dp),
187             tunable_key);
188
189         lockmgr(&dsched_lock, LK_RELEASE);
190 }
191
192 /*
193  * Called from disk_setdiskinfo (or rather _setdiskinfo). This will check if
194  * there's any policy associated with the serial number of the device.
195  */
196 void
197 dsched_disk_update_callback(struct disk *dp, struct disk_info *info)
198 {
199         char tunable_key[SPECNAMELEN + 48];
200         char sched_policy[DSCHED_POLICY_NAME_LENGTH];
201         struct dsched_policy *policy = NULL;
202
203         if (info->d_serialno == NULL)
204                 return;
205
206         lockmgr(&dsched_lock, LK_EXCLUSIVE);
207
208         ksnprintf(tunable_key, sizeof(tunable_key), "dsched.policy.%s",
209             info->d_serialno);
210
211         if((TUNABLE_STR_FETCH(tunable_key, sched_policy,
212             sizeof(sched_policy)) != 0)) {
213                 policy = dsched_find_policy(sched_policy);      
214         }
215
216         if (policy) {
217                 dsched_switch(dp, policy);      
218         }
219
220         dsched_sysctl_add_disk(
221             (struct dsched_disk_ctx *)dsched_get_disk_priv(dp),
222             info->d_serialno);
223
224         lockmgr(&dsched_lock, LK_RELEASE);
225 }
226
227 /*
228  * Called on disk_destroy()
229  * shuts down the scheduler core and cancels all remaining bios
230  */
231 void
232 dsched_disk_destroy_callback(struct disk *dp)
233 {
234         struct dsched_policy *old_policy;
235         struct dsched_disk_ctx *diskctx;
236
237         lockmgr(&dsched_lock, LK_EXCLUSIVE);
238
239         diskctx = dsched_get_disk_priv(dp);
240
241         old_policy = dp->d_sched_policy;
242         dp->d_sched_policy = &dsched_noop_policy;
243         old_policy->cancel_all(dsched_get_disk_priv(dp));
244         old_policy->teardown(dsched_get_disk_priv(dp));
245
246         if (diskctx->flags & DSCHED_SYSCTL_CTX_INITED)
247                 sysctl_ctx_free(&diskctx->sysctl_ctx);
248
249         policy_destroy(dp);
250         atomic_subtract_int(&old_policy->ref_count, 1);
251         KKASSERT(old_policy->ref_count >= 0);
252
253         lockmgr(&dsched_lock, LK_RELEASE);
254 }
255
256
257 void
258 dsched_queue(struct disk *dp, struct bio *bio)
259 {
260         struct dsched_thread_ctx        *tdctx;
261         struct dsched_thread_io         *tdio;
262         struct dsched_disk_ctx          *diskctx;
263
264         int found = 0, error = 0;
265
266         tdctx = dsched_get_buf_priv(bio->bio_buf);
267         if (tdctx == NULL) {
268                 /* We don't handle this case, let dsched dispatch */
269                 atomic_add_int(&dsched_stats.no_tdctx, 1);
270                 dsched_strategy_raw(dp, bio);
271                 return;
272         }
273
274         DSCHED_THREAD_CTX_LOCK(tdctx);
275
276         KKASSERT(!TAILQ_EMPTY(&tdctx->tdio_list));
277         /*
278          * XXX:
279          * iterate in reverse to make sure we find the most up-to-date
280          * tdio for a given disk. After a switch it may take some time
281          * for everything to clean up.
282          */
283         TAILQ_FOREACH_REVERSE(tdio, &tdctx->tdio_list, tdio_list_head, link) {
284                 if (tdio->dp == dp) {
285                         dsched_thread_io_ref(tdio);
286                         found = 1;
287                         break;
288                 }
289         }
290
291         DSCHED_THREAD_CTX_UNLOCK(tdctx);
292         dsched_clr_buf_priv(bio->bio_buf);
293         dsched_thread_ctx_unref(tdctx); /* acquired on new_buf */
294
295         KKASSERT(found == 1);
296         diskctx = dsched_get_disk_priv(dp);
297         dsched_disk_ctx_ref(diskctx);
298
299         if (dp->d_sched_policy != &dsched_noop_policy)
300                 KKASSERT(tdio->debug_policy == dp->d_sched_policy);
301
302         KKASSERT(tdio->debug_inited == 0xF00F1234);
303
304         error = dp->d_sched_policy->bio_queue(diskctx, tdio, bio);
305
306         if (error) {
307                 dsched_strategy_raw(dp, bio);
308         }
309         dsched_disk_ctx_unref(diskctx);
310         dsched_thread_io_unref(tdio);
311 }
312
313
314 /*
315  * Called from each module_init or module_attach of each policy
316  * registers the policy in the local policy list.
317  */
318 int
319 dsched_register(struct dsched_policy *d_policy)
320 {
321         struct dsched_policy *policy;
322         int error = 0;
323
324         lockmgr(&dsched_lock, LK_EXCLUSIVE);
325
326         policy = dsched_find_policy(d_policy->name);
327
328         if (!policy) {
329                 TAILQ_INSERT_TAIL(&dsched_policy_list, d_policy, link);
330                 atomic_add_int(&d_policy->ref_count, 1);
331         } else {
332                 dsched_debug(LOG_ERR, "Policy with name %s already registered!\n",
333                     d_policy->name);
334                 error = EEXIST;
335         }
336
337         lockmgr(&dsched_lock, LK_RELEASE);
338         return error;
339 }
340
341 /*
342  * Called from each module_detach of each policy
343  * unregisters the policy
344  */
345 int
346 dsched_unregister(struct dsched_policy *d_policy)
347 {
348         struct dsched_policy *policy;
349
350         lockmgr(&dsched_lock, LK_EXCLUSIVE);
351         policy = dsched_find_policy(d_policy->name);
352
353         if (policy) {
354                 if (policy->ref_count > 1) {
355                         lockmgr(&dsched_lock, LK_RELEASE);
356                         return EBUSY;
357                 }
358                 TAILQ_REMOVE(&dsched_policy_list, policy, link);
359                 atomic_subtract_int(&policy->ref_count, 1);
360                 KKASSERT(policy->ref_count == 0);
361         }
362         lockmgr(&dsched_lock, LK_RELEASE);
363
364         return 0;
365 }
366
367
368 /*
369  * switches the policy by first removing the old one and then
370  * enabling the new one.
371  */
372 int
373 dsched_switch(struct disk *dp, struct dsched_policy *new_policy)
374 {
375         struct dsched_policy *old_policy;
376
377         /* If we are asked to set the same policy, do nothing */
378         if (dp->d_sched_policy == new_policy)
379                 return 0;
380
381         /* lock everything down, diskwise */
382         lockmgr(&dsched_lock, LK_EXCLUSIVE);
383         old_policy = dp->d_sched_policy;
384
385         atomic_subtract_int(&old_policy->ref_count, 1);
386         KKASSERT(old_policy->ref_count >= 0);
387
388         dp->d_sched_policy = &dsched_noop_policy;
389         old_policy->teardown(dsched_get_disk_priv(dp));
390         policy_destroy(dp);
391
392         /* Bring everything back to life */
393         dsched_set_policy(dp, new_policy);
394         lockmgr(&dsched_lock, LK_RELEASE);
395
396         return 0;
397 }
398
399
400 /*
401  * Loads a given policy and attaches it to the specified disk.
402  * Also initializes the core for the policy
403  */
404 void
405 dsched_set_policy(struct disk *dp, struct dsched_policy *new_policy)
406 {
407         int locked = 0;
408
409         /* Check if it is locked already. if not, we acquire the devfs lock */
410         if (!(lockstatus(&dsched_lock, curthread)) == LK_EXCLUSIVE) {
411                 lockmgr(&dsched_lock, LK_EXCLUSIVE);
412                 locked = 1;
413         }
414
415         DSCHED_GLOBAL_THREAD_CTX_LOCK();
416
417         policy_new(dp, new_policy);
418         new_policy->prepare(dsched_get_disk_priv(dp));
419         dp->d_sched_policy = new_policy;
420
421         DSCHED_GLOBAL_THREAD_CTX_UNLOCK();
422
423         atomic_add_int(&new_policy->ref_count, 1);
424         kprintf("disk scheduler: set policy of %s to %s\n", dp->d_cdev->si_name,
425             new_policy->name);
426
427         /* If we acquired the lock, we also get rid of it */
428         if (locked)
429                 lockmgr(&dsched_lock, LK_RELEASE);
430 }
431
432 struct dsched_policy*
433 dsched_find_policy(char *search)
434 {
435         struct dsched_policy *policy;
436         struct dsched_policy *policy_found = NULL;
437         int locked = 0;
438
439         /* Check if it is locked already. if not, we acquire the devfs lock */
440         if (!(lockstatus(&dsched_lock, curthread)) == LK_EXCLUSIVE) {
441                 lockmgr(&dsched_lock, LK_EXCLUSIVE);
442                 locked = 1;
443         }
444
445         TAILQ_FOREACH(policy, &dsched_policy_list, link) {
446                 if (!strcmp(policy->name, search)) {
447                         policy_found = policy;
448                         break;
449                 }
450         }
451
452         /* If we acquired the lock, we also get rid of it */
453         if (locked)
454                 lockmgr(&dsched_lock, LK_RELEASE);
455
456         return policy_found;
457 }
458
459 struct disk*
460 dsched_find_disk(char *search)
461 {
462         struct disk *dp_found = NULL;
463         struct disk *dp = NULL;
464
465         while((dp = disk_enumerate(dp))) {
466                 if (!strcmp(dp->d_cdev->si_name, search)) {
467                         dp_found = dp;
468                         break;
469                 }
470         }
471
472         return dp_found;
473 }
474
475 struct disk*
476 dsched_disk_enumerate(struct disk *dp, struct dsched_policy *policy)
477 {
478         while ((dp = disk_enumerate(dp))) {
479                 if (dp->d_sched_policy == policy)
480                         return dp;
481         }
482
483         return NULL;
484 }
485
486 struct dsched_policy *
487 dsched_policy_enumerate(struct dsched_policy *pol)
488 {
489         if (!pol)
490                 return (TAILQ_FIRST(&dsched_policy_list));
491         else
492                 return (TAILQ_NEXT(pol, link));
493 }
494
495 void
496 dsched_cancel_bio(struct bio *bp)
497 {
498         bp->bio_buf->b_error = ENXIO;
499         bp->bio_buf->b_flags |= B_ERROR;
500         bp->bio_buf->b_resid = bp->bio_buf->b_bcount;
501
502         biodone(bp);
503 }
504
505 void
506 dsched_strategy_raw(struct disk *dp, struct bio *bp)
507 {
508         /*
509          * Ideally, this stuff shouldn't be needed... but just in case, we leave it in
510          * to avoid panics
511          */
512         KASSERT(dp->d_rawdev != NULL, ("dsched_strategy_raw sees NULL d_rawdev!!"));
513         if(bp->bio_track != NULL) {
514                 dsched_debug(LOG_INFO,
515                     "dsched_strategy_raw sees non-NULL bio_track!! "
516                     "bio: %p\n", bp);
517                 bp->bio_track = NULL;
518         }
519         dev_dstrategy(dp->d_rawdev, bp);
520 }
521
522 void
523 dsched_strategy_sync(struct disk *dp, struct bio *bio)
524 {
525         struct buf *bp, *nbp;
526         struct bio *nbio;
527
528         bp = bio->bio_buf;
529
530         nbp = getpbuf(NULL);
531         nbio = &nbp->b_bio1;
532
533         nbp->b_cmd = bp->b_cmd;
534         nbp->b_bufsize = bp->b_bufsize;
535         nbp->b_runningbufspace = bp->b_runningbufspace;
536         nbp->b_bcount = bp->b_bcount;
537         nbp->b_resid = bp->b_resid;
538         nbp->b_data = bp->b_data;
539 #if 0
540         /*
541          * Buffers undergoing device I/O do not need a kvabase/size.
542          */
543         nbp->b_kvabase = bp->b_kvabase;
544         nbp->b_kvasize = bp->b_kvasize;
545 #endif
546         nbp->b_dirtyend = bp->b_dirtyend;
547
548         nbio->bio_done = biodone_sync;
549         nbio->bio_flags |= BIO_SYNC;
550         nbio->bio_track = NULL;
551
552         nbio->bio_caller_info1.ptr = dp;
553         nbio->bio_offset = bio->bio_offset;
554
555         dev_dstrategy(dp->d_rawdev, nbio);
556         biowait(nbio, "dschedsync");
557         bp->b_resid = nbp->b_resid;
558         bp->b_error = nbp->b_error;
559         biodone(bio);
560 #if 0
561         nbp->b_kvabase = NULL;
562         nbp->b_kvasize = 0;
563 #endif
564         relpbuf(nbp, NULL);
565 }
566
567 void
568 dsched_strategy_async(struct disk *dp, struct bio *bio, biodone_t *done, void *priv)
569 {
570         struct bio *nbio;
571
572         nbio = push_bio(bio);
573         nbio->bio_done = done;
574         nbio->bio_offset = bio->bio_offset;
575
576         dsched_set_bio_dp(nbio, dp);
577         dsched_set_bio_priv(nbio, priv);
578
579         getmicrotime(&nbio->bio_caller_info3.tv);
580         dev_dstrategy(dp->d_rawdev, nbio);
581 }
582
583 /*
584  * Ref and deref various structures.  The 1->0 transition of the reference
585  * count actually transitions 1->0x80000000 and causes the object to be
586  * destroyed.  It is possible for transitory references to occur on the
587  * object while it is being destroyed.  We use bit 31 to indicate that
588  * destruction is in progress and to prevent nested destructions.
589  */
590 void
591 dsched_disk_ctx_ref(struct dsched_disk_ctx *diskctx)
592 {
593         int refcount;
594
595         refcount = atomic_fetchadd_int(&diskctx->refcount, 1);
596 }
597
598 void
599 dsched_thread_io_ref(struct dsched_thread_io *tdio)
600 {
601         int refcount;
602
603         refcount = atomic_fetchadd_int(&tdio->refcount, 1);
604 }
605
606 void
607 dsched_thread_ctx_ref(struct dsched_thread_ctx *tdctx)
608 {
609         int refcount;
610
611         refcount = atomic_fetchadd_int(&tdctx->refcount, 1);
612 }
613
614 void
615 dsched_disk_ctx_unref(struct dsched_disk_ctx *diskctx)
616 {
617         int refs;
618         int nrefs;
619
620         /*
621          * Handle 1->0 transitions for diskctx and nested destruction
622          * recursions.  If the refs are already in destruction mode (bit 31
623          * set) on the 1->0 transition we don't try to destruct it again.
624          *
625          * 0x80000001->0x80000000 transitions are handled normally and
626          * thus avoid nested dstruction.
627          */
628         for (;;) {
629                 refs = diskctx->refcount;
630                 cpu_ccfence();
631                 nrefs = refs - 1;
632
633                 KKASSERT(((refs ^ nrefs) & 0x80000000) == 0);
634                 if (nrefs) {
635                         if (atomic_cmpset_int(&diskctx->refcount, refs, nrefs))
636                                 break;
637                         continue;
638                 }
639                 nrefs = 0x80000000;
640                 if (atomic_cmpset_int(&diskctx->refcount, refs, nrefs)) {
641                         dsched_disk_ctx_destroy(diskctx);
642                         break;
643                 }
644         }
645 }
646
647 static
648 void
649 dsched_disk_ctx_destroy(struct dsched_disk_ctx *diskctx)
650 {
651         struct dsched_thread_io *tdio;
652
653 #if 0
654         kprintf("diskctx (%p) destruction started, trace:\n", diskctx);
655         print_backtrace(4);
656 #endif
657         lockmgr(&diskctx->lock, LK_EXCLUSIVE);
658         while ((tdio = TAILQ_FIRST(&diskctx->tdio_list)) != NULL) {
659                 KKASSERT(tdio->flags & DSCHED_LINKED_DISK_CTX);
660                 TAILQ_REMOVE(&diskctx->tdio_list, tdio, dlink);
661                 atomic_clear_int(&tdio->flags, DSCHED_LINKED_DISK_CTX);
662                 tdio->diskctx = NULL;
663                 /* XXX tdio->diskctx->dp->d_sched_policy->destroy_tdio(tdio);*/
664                 dsched_thread_io_unref(tdio);
665         }
666         lockmgr(&diskctx->lock, LK_RELEASE);
667         if (diskctx->dp->d_sched_policy->destroy_diskctx)
668                 diskctx->dp->d_sched_policy->destroy_diskctx(diskctx);
669         KKASSERT(diskctx->refcount == 0x80000000);
670         objcache_put(dsched_diskctx_cache, diskctx);
671         atomic_subtract_int(&dsched_stats.diskctx_allocations, 1);
672 }
673
674 void
675 dsched_thread_io_unref(struct dsched_thread_io *tdio)
676 {
677         int refs;
678         int nrefs;
679
680         /*
681          * Handle 1->0 transitions for tdio and nested destruction
682          * recursions.  If the refs are already in destruction mode (bit 31
683          * set) on the 1->0 transition we don't try to destruct it again.
684          *
685          * 0x80000001->0x80000000 transitions are handled normally and
686          * thus avoid nested dstruction.
687          */
688         for (;;) {
689                 refs = tdio->refcount;
690                 cpu_ccfence();
691                 nrefs = refs - 1;
692
693                 KKASSERT(((refs ^ nrefs) & 0x80000000) == 0);
694                 if (nrefs) {
695                         if (atomic_cmpset_int(&tdio->refcount, refs, nrefs))
696                                 break;
697                         continue;
698                 }
699                 nrefs = 0x80000000;
700                 if (atomic_cmpset_int(&tdio->refcount, refs, nrefs)) {
701                         dsched_thread_io_destroy(tdio);
702                         break;
703                 }
704         }
705 }
706
707 static void
708 dsched_thread_io_destroy(struct dsched_thread_io *tdio)
709 {
710         struct dsched_thread_ctx *tdctx;
711         struct dsched_disk_ctx  *diskctx;
712
713 #if 0
714         kprintf("tdio (%p) destruction started, trace:\n", tdio);
715         print_backtrace(8);
716 #endif
717         KKASSERT(tdio->qlength == 0);
718
719         while ((diskctx = tdio->diskctx) != NULL) {
720                 dsched_disk_ctx_ref(diskctx);
721                 lockmgr(&diskctx->lock, LK_EXCLUSIVE);
722                 if (diskctx != tdio->diskctx) {
723                         lockmgr(&diskctx->lock, LK_RELEASE);
724                         dsched_disk_ctx_unref(diskctx);
725                         continue;
726                 }
727                 KKASSERT(tdio->flags & DSCHED_LINKED_DISK_CTX);
728                 if (diskctx->dp->d_sched_policy->destroy_tdio)
729                         diskctx->dp->d_sched_policy->destroy_tdio(tdio);
730                 TAILQ_REMOVE(&diskctx->tdio_list, tdio, dlink);
731                 atomic_clear_int(&tdio->flags, DSCHED_LINKED_DISK_CTX);
732                 tdio->diskctx = NULL;
733                 lockmgr(&diskctx->lock, LK_RELEASE);
734                 dsched_disk_ctx_unref(diskctx);
735         }
736         while ((tdctx = tdio->tdctx) != NULL) {
737                 dsched_thread_ctx_ref(tdctx);
738                 lockmgr(&tdctx->lock, LK_EXCLUSIVE);
739                 if (tdctx != tdio->tdctx) {
740                         lockmgr(&tdctx->lock, LK_RELEASE);
741                         dsched_thread_ctx_unref(tdctx);
742                         continue;
743                 }
744                 KKASSERT(tdio->flags & DSCHED_LINKED_THREAD_CTX);
745                 TAILQ_REMOVE(&tdctx->tdio_list, tdio, link);
746                 atomic_clear_int(&tdio->flags, DSCHED_LINKED_THREAD_CTX);
747                 tdio->tdctx = NULL;
748                 lockmgr(&tdctx->lock, LK_RELEASE);
749                 dsched_thread_ctx_unref(tdctx);
750         }
751         KKASSERT(tdio->refcount == 0x80000000);
752         objcache_put(dsched_tdio_cache, tdio);
753         atomic_subtract_int(&dsched_stats.tdio_allocations, 1);
754 #if 0
755         dsched_disk_ctx_unref(diskctx);
756 #endif
757 }
758
759 void
760 dsched_thread_ctx_unref(struct dsched_thread_ctx *tdctx)
761 {
762         int refs;
763         int nrefs;
764
765         /*
766          * Handle 1->0 transitions for tdctx and nested destruction
767          * recursions.  If the refs are already in destruction mode (bit 31
768          * set) on the 1->0 transition we don't try to destruct it again.
769          *
770          * 0x80000001->0x80000000 transitions are handled normally and
771          * thus avoid nested dstruction.
772          */
773         for (;;) {
774                 refs = tdctx->refcount;
775                 cpu_ccfence();
776                 nrefs = refs - 1;
777
778                 KKASSERT(((refs ^ nrefs) & 0x80000000) == 0);
779                 if (nrefs) {
780                         if (atomic_cmpset_int(&tdctx->refcount, refs, nrefs))
781                                 break;
782                         continue;
783                 }
784                 nrefs = 0x80000000;
785                 if (atomic_cmpset_int(&tdctx->refcount, refs, nrefs)) {
786                         dsched_thread_ctx_destroy(tdctx);
787                         break;
788                 }
789         }
790 }
791
792 static void
793 dsched_thread_ctx_destroy(struct dsched_thread_ctx *tdctx)
794 {
795         struct dsched_thread_io *tdio;
796
797 #if 0
798         kprintf("tdctx (%p) destruction started, trace:\n", tdctx);
799         print_backtrace(8);
800 #endif
801         DSCHED_GLOBAL_THREAD_CTX_LOCK();
802
803         lockmgr(&tdctx->lock, LK_EXCLUSIVE);
804
805         while ((tdio = TAILQ_FIRST(&tdctx->tdio_list)) != NULL) {
806                 KKASSERT(tdio->flags & DSCHED_LINKED_THREAD_CTX);
807                 TAILQ_REMOVE(&tdctx->tdio_list, tdio, link);
808                 atomic_clear_int(&tdio->flags, DSCHED_LINKED_THREAD_CTX);
809                 tdio->tdctx = NULL;
810                 dsched_thread_io_unref(tdio);
811         }
812         KKASSERT(tdctx->refcount == 0x80000000);
813         TAILQ_REMOVE(&dsched_tdctx_list, tdctx, link);
814
815         lockmgr(&tdctx->lock, LK_RELEASE);
816
817         DSCHED_GLOBAL_THREAD_CTX_UNLOCK();
818
819         objcache_put(dsched_tdctx_cache, tdctx);
820         atomic_subtract_int(&dsched_stats.tdctx_allocations, 1);
821 }
822
823 struct dsched_thread_io *
824 dsched_thread_io_alloc(struct disk *dp, struct dsched_thread_ctx *tdctx,
825     struct dsched_policy *pol)
826 {
827         struct dsched_thread_io *tdio;
828 #if 0
829         dsched_disk_ctx_ref(dsched_get_disk_priv(dp));
830 #endif
831         tdio = objcache_get(dsched_tdio_cache, M_WAITOK);
832         bzero(tdio, DSCHED_THREAD_IO_MAX_SZ);
833
834         /* XXX: maybe we do need another ref for the disk list for tdio */
835         dsched_thread_io_ref(tdio);
836
837         DSCHED_THREAD_IO_LOCKINIT(tdio);
838         tdio->dp = dp;
839
840         tdio->diskctx = dsched_get_disk_priv(dp);
841         TAILQ_INIT(&tdio->queue);
842
843         if (pol->new_tdio)
844                 pol->new_tdio(tdio);
845
846         lockmgr(&tdio->diskctx->lock, LK_EXCLUSIVE);
847         TAILQ_INSERT_TAIL(&tdio->diskctx->tdio_list, tdio, dlink);
848         atomic_set_int(&tdio->flags, DSCHED_LINKED_DISK_CTX);
849         lockmgr(&tdio->diskctx->lock, LK_RELEASE);
850
851         if (tdctx) {
852                 tdio->tdctx = tdctx;
853                 tdio->p = tdctx->p;
854
855                 /* Put the tdio in the tdctx list */
856                 DSCHED_THREAD_CTX_LOCK(tdctx);
857                 TAILQ_INSERT_TAIL(&tdctx->tdio_list, tdio, link);
858                 DSCHED_THREAD_CTX_UNLOCK(tdctx);
859                 atomic_set_int(&tdio->flags, DSCHED_LINKED_THREAD_CTX);
860         }
861
862         tdio->debug_policy = pol;
863         tdio->debug_inited = 0xF00F1234;
864
865         atomic_add_int(&dsched_stats.tdio_allocations, 1);
866         return tdio;
867 }
868
869
870 struct dsched_disk_ctx *
871 dsched_disk_ctx_alloc(struct disk *dp, struct dsched_policy *pol)
872 {
873         struct dsched_disk_ctx *diskctx;
874
875         diskctx = objcache_get(dsched_diskctx_cache, M_WAITOK);
876         bzero(diskctx, DSCHED_DISK_CTX_MAX_SZ);
877         dsched_disk_ctx_ref(diskctx);
878         diskctx->dp = dp;
879         DSCHED_DISK_CTX_LOCKINIT(diskctx);
880         TAILQ_INIT(&diskctx->tdio_list);
881
882         atomic_add_int(&dsched_stats.diskctx_allocations, 1);
883         if (pol->new_diskctx)
884                 pol->new_diskctx(diskctx);
885         return diskctx;
886 }
887
888
889 struct dsched_thread_ctx *
890 dsched_thread_ctx_alloc(struct proc *p)
891 {
892         struct dsched_thread_ctx        *tdctx;
893         struct dsched_thread_io *tdio;
894         struct disk     *dp = NULL;
895
896         tdctx = objcache_get(dsched_tdctx_cache, M_WAITOK);
897         bzero(tdctx, DSCHED_THREAD_CTX_MAX_SZ);
898         dsched_thread_ctx_ref(tdctx);
899 #if 0
900         kprintf("dsched_thread_ctx_alloc, new tdctx = %p\n", tdctx);
901 #endif
902         DSCHED_THREAD_CTX_LOCKINIT(tdctx);
903         TAILQ_INIT(&tdctx->tdio_list);
904         tdctx->p = p;
905
906         DSCHED_GLOBAL_THREAD_CTX_LOCK();
907         while ((dp = disk_enumerate(dp))) {
908                 tdio = dsched_thread_io_alloc(dp, tdctx, dp->d_sched_policy);
909         }
910
911         TAILQ_INSERT_TAIL(&dsched_tdctx_list, tdctx, link);
912         DSCHED_GLOBAL_THREAD_CTX_UNLOCK();
913
914         atomic_add_int(&dsched_stats.tdctx_allocations, 1);
915         /* XXX: no callback here */
916         return tdctx;
917 }
918
919 void
920 policy_new(struct disk *dp, struct dsched_policy *pol) {
921         struct dsched_thread_ctx *tdctx;
922         struct dsched_disk_ctx *diskctx;
923         struct dsched_thread_io *tdio;
924
925         diskctx = dsched_disk_ctx_alloc(dp, pol);
926         dsched_disk_ctx_ref(diskctx);
927         dsched_set_disk_priv(dp, diskctx);
928
929         TAILQ_FOREACH(tdctx, &dsched_tdctx_list, link) {
930                 tdio = dsched_thread_io_alloc(dp, tdctx, pol);
931         }
932 }
933
934 void
935 policy_destroy(struct disk *dp) {
936         struct dsched_disk_ctx *diskctx;
937
938         diskctx = dsched_get_disk_priv(dp);
939         KKASSERT(diskctx != NULL);
940
941         dsched_disk_ctx_unref(diskctx); /* from prepare */
942         dsched_disk_ctx_unref(diskctx); /* from alloc */
943
944         dsched_set_disk_priv(dp, NULL);
945 }
946
947 void
948 dsched_new_buf(struct buf *bp)
949 {
950         struct dsched_thread_ctx        *tdctx = NULL;
951
952         if (dsched_inited == 0)
953                 return;
954
955         if (curproc != NULL) {
956                 tdctx = dsched_get_proc_priv(curproc);
957         } else {
958                 /* This is a kernel thread, so no proc info is available */
959                 tdctx = dsched_get_thread_priv(curthread);
960         }
961
962 #if 0
963         /*
964          * XXX: hack. we don't want this assert because we aren't catching all
965          *      threads. mi_startup() is still getting away without an tdctx.
966          */
967
968         /* by now we should have an tdctx. if not, something bad is going on */
969         KKASSERT(tdctx != NULL);
970 #endif
971
972         if (tdctx) {
973                 dsched_thread_ctx_ref(tdctx);
974         }
975         dsched_set_buf_priv(bp, tdctx);
976 }
977
978 void
979 dsched_exit_buf(struct buf *bp)
980 {
981         struct dsched_thread_ctx        *tdctx;
982
983         tdctx = dsched_get_buf_priv(bp);
984         if (tdctx != NULL) {
985                 dsched_clr_buf_priv(bp);
986                 dsched_thread_ctx_unref(tdctx);
987         }
988 }
989
990 void
991 dsched_new_proc(struct proc *p)
992 {
993         struct dsched_thread_ctx        *tdctx;
994
995         if (dsched_inited == 0)
996                 return;
997
998         KKASSERT(p != NULL);
999
1000         tdctx = dsched_thread_ctx_alloc(p);
1001         tdctx->p = p;
1002         dsched_thread_ctx_ref(tdctx);
1003
1004         dsched_set_proc_priv(p, tdctx);
1005         atomic_add_int(&dsched_stats.nprocs, 1);
1006 }
1007
1008
1009 void
1010 dsched_new_thread(struct thread *td)
1011 {
1012         struct dsched_thread_ctx        *tdctx;
1013
1014         if (dsched_inited == 0)
1015                 return;
1016
1017         KKASSERT(td != NULL);
1018
1019         tdctx = dsched_thread_ctx_alloc(NULL);
1020         tdctx->td = td;
1021         dsched_thread_ctx_ref(tdctx);
1022
1023         dsched_set_thread_priv(td, tdctx);
1024         atomic_add_int(&dsched_stats.nthreads, 1);
1025 }
1026
1027 void
1028 dsched_exit_proc(struct proc *p)
1029 {
1030         struct dsched_thread_ctx        *tdctx;
1031
1032         if (dsched_inited == 0)
1033                 return;
1034
1035         KKASSERT(p != NULL);
1036
1037         tdctx = dsched_get_proc_priv(p);
1038         KKASSERT(tdctx != NULL);
1039
1040         tdctx->dead = 0xDEAD;
1041         dsched_set_proc_priv(p, NULL);
1042
1043         dsched_thread_ctx_unref(tdctx); /* one for alloc, */
1044         dsched_thread_ctx_unref(tdctx); /* one for ref */
1045         atomic_subtract_int(&dsched_stats.nprocs, 1);
1046 }
1047
1048
1049 void
1050 dsched_exit_thread(struct thread *td)
1051 {
1052         struct dsched_thread_ctx        *tdctx;
1053
1054         if (dsched_inited == 0)
1055                 return;
1056
1057         KKASSERT(td != NULL);
1058
1059         tdctx = dsched_get_thread_priv(td);
1060         KKASSERT(tdctx != NULL);
1061
1062         tdctx->dead = 0xDEAD;
1063         dsched_set_thread_priv(td, 0);
1064
1065         dsched_thread_ctx_unref(tdctx); /* one for alloc, */
1066         dsched_thread_ctx_unref(tdctx); /* one for ref */
1067         atomic_subtract_int(&dsched_stats.nthreads, 1);
1068 }
1069
1070 struct dsched_thread_io *
1071 dsched_new_policy_thread_tdio(struct dsched_disk_ctx *diskctx,
1072     struct dsched_policy *pol) {
1073         struct dsched_thread_ctx *tdctx;
1074         struct dsched_thread_io *tdio;
1075
1076         DSCHED_GLOBAL_THREAD_CTX_LOCK();
1077
1078         tdctx = dsched_get_thread_priv(curthread);
1079         KKASSERT(tdctx != NULL);
1080         tdio = dsched_thread_io_alloc(diskctx->dp, tdctx, pol);
1081
1082         DSCHED_GLOBAL_THREAD_CTX_UNLOCK();
1083
1084         return tdio;
1085 }
1086
1087 /* DEFAULT NOOP POLICY */
1088
1089 static int
1090 noop_prepare(struct dsched_disk_ctx *diskctx)
1091 {
1092         return 0;
1093 }
1094
1095 static void
1096 noop_teardown(struct dsched_disk_ctx *diskctx)
1097 {
1098
1099 }
1100
1101 static void
1102 noop_cancel(struct dsched_disk_ctx *diskctx)
1103 {
1104
1105 }
1106
1107 static int
1108 noop_queue(struct dsched_disk_ctx *diskctx, struct dsched_thread_io *tdio,
1109     struct bio *bio)
1110 {
1111         dsched_strategy_raw(diskctx->dp, bio);
1112 #if 0
1113         dsched_strategy_async(diskctx->dp, bio, noop_completed, NULL);
1114 #endif
1115         return 0;
1116 }
1117
1118 /*
1119  * SYSINIT stuff
1120  */
1121 static void
1122 dsched_init(void)
1123 {
1124         dsched_tdio_cache = objcache_create("dsched-tdio-cache", 0, 0,
1125                                            NULL, NULL, NULL,
1126                                            objcache_malloc_alloc,
1127                                            objcache_malloc_free,
1128                                            &dsched_thread_io_malloc_args );
1129
1130         dsched_tdctx_cache = objcache_create("dsched-tdctx-cache", 0, 0,
1131                                            NULL, NULL, NULL,
1132                                            objcache_malloc_alloc,
1133                                            objcache_malloc_free,
1134                                            &dsched_thread_ctx_malloc_args );
1135
1136         dsched_diskctx_cache = objcache_create("dsched-diskctx-cache", 0, 0,
1137                                            NULL, NULL, NULL,
1138                                            objcache_malloc_alloc,
1139                                            objcache_malloc_free,
1140                                            &dsched_disk_ctx_malloc_args );
1141
1142         bzero(&dsched_stats, sizeof(struct dsched_stats));
1143
1144         lockinit(&dsched_lock, "dsched lock", 0, LK_CANRECURSE);
1145         DSCHED_GLOBAL_THREAD_CTX_LOCKINIT();
1146
1147         dsched_register(&dsched_noop_policy);
1148
1149         dsched_inited = 1;
1150 }
1151
1152 static void
1153 dsched_uninit(void)
1154 {
1155 }
1156
1157 SYSINIT(subr_dsched_register, SI_SUB_CREATE_INIT-1, SI_ORDER_FIRST, dsched_init, NULL);
1158 SYSUNINIT(subr_dsched_register, SI_SUB_CREATE_INIT-1, SI_ORDER_ANY, dsched_uninit, NULL);
1159
1160 /*
1161  * SYSCTL stuff
1162  */
1163 static int
1164 sysctl_dsched_stats(SYSCTL_HANDLER_ARGS)
1165 {
1166         return (sysctl_handle_opaque(oidp, &dsched_stats, sizeof(struct dsched_stats), req));
1167 }
1168
1169 static int
1170 sysctl_dsched_list_policies(SYSCTL_HANDLER_ARGS)
1171 {
1172         struct dsched_policy *pol = NULL;
1173         int error, first = 1;
1174
1175         lockmgr(&dsched_lock, LK_EXCLUSIVE);
1176
1177         while ((pol = dsched_policy_enumerate(pol))) {
1178                 if (!first) {
1179                         error = SYSCTL_OUT(req, " ", 1);
1180                         if (error)
1181                                 break;
1182                 } else {
1183                         first = 0;
1184                 }
1185                 error = SYSCTL_OUT(req, pol->name, strlen(pol->name));
1186                 if (error)
1187                         break;
1188
1189         }
1190
1191         lockmgr(&dsched_lock, LK_RELEASE);
1192
1193         error = SYSCTL_OUT(req, "", 1);
1194
1195         return error;
1196 }
1197
1198 static int
1199 sysctl_dsched_policy(SYSCTL_HANDLER_ARGS)
1200 {
1201         char buf[DSCHED_POLICY_NAME_LENGTH];
1202         struct dsched_disk_ctx *diskctx = arg1;
1203         struct dsched_policy *pol = NULL;
1204         int error;
1205
1206         if (diskctx == NULL) {
1207                 return 0;
1208         }
1209
1210         lockmgr(&dsched_lock, LK_EXCLUSIVE);
1211
1212         pol = diskctx->dp->d_sched_policy;
1213         memcpy(buf, pol->name, DSCHED_POLICY_NAME_LENGTH);
1214
1215         error = sysctl_handle_string(oidp, buf, DSCHED_POLICY_NAME_LENGTH, req);
1216         if (error || req->newptr == NULL) {
1217                 lockmgr(&dsched_lock, LK_RELEASE);
1218                 return (error);
1219         }
1220
1221         pol = dsched_find_policy(buf);
1222         if (pol == NULL) {
1223                 lockmgr(&dsched_lock, LK_RELEASE);
1224                 return 0;
1225         }
1226
1227         dsched_switch(diskctx->dp, pol);
1228
1229         lockmgr(&dsched_lock, LK_RELEASE);
1230
1231         return error;
1232 }
1233
1234 static int
1235 sysctl_dsched_default_policy(SYSCTL_HANDLER_ARGS)
1236 {
1237         char buf[DSCHED_POLICY_NAME_LENGTH];
1238         struct dsched_policy *pol = NULL;
1239         int error;
1240
1241         lockmgr(&dsched_lock, LK_EXCLUSIVE);
1242
1243         pol = default_policy;
1244         memcpy(buf, pol->name, DSCHED_POLICY_NAME_LENGTH);
1245
1246         error = sysctl_handle_string(oidp, buf, DSCHED_POLICY_NAME_LENGTH, req);
1247         if (error || req->newptr == NULL) {
1248                 lockmgr(&dsched_lock, LK_RELEASE);
1249                 return (error);
1250         }
1251
1252         pol = dsched_find_policy(buf);
1253         if (pol == NULL) {
1254                 lockmgr(&dsched_lock, LK_RELEASE);
1255                 return 0;
1256         }
1257
1258         default_set = 1;
1259         default_policy = pol;
1260
1261         lockmgr(&dsched_lock, LK_RELEASE);
1262
1263         return error;
1264 }
1265
1266 SYSCTL_NODE(, OID_AUTO, dsched, CTLFLAG_RD, NULL,
1267     "Disk Scheduler Framework (dsched) magic");
1268 SYSCTL_NODE(_dsched, OID_AUTO, policy, CTLFLAG_RW, NULL,
1269     "List of disks and their policies");
1270 SYSCTL_INT(_dsched, OID_AUTO, debug, CTLFLAG_RW, &dsched_debug_enable,
1271     0, "Enable dsched debugging");
1272 SYSCTL_PROC(_dsched, OID_AUTO, stats, CTLTYPE_OPAQUE|CTLFLAG_RD,
1273     0, sizeof(struct dsched_stats), sysctl_dsched_stats, "dsched_stats",
1274     "dsched statistics");
1275 SYSCTL_PROC(_dsched, OID_AUTO, policies, CTLTYPE_STRING|CTLFLAG_RD,
1276     NULL, 0, sysctl_dsched_list_policies, "A", "names of available policies");
1277 SYSCTL_PROC(_dsched_policy, OID_AUTO, default, CTLTYPE_STRING|CTLFLAG_RW,
1278     NULL, 0, sysctl_dsched_default_policy, "A", "default dsched policy");
1279
1280 static void
1281 dsched_sysctl_add_disk(struct dsched_disk_ctx *diskctx, char *name)
1282 {
1283         if (!(diskctx->flags & DSCHED_SYSCTL_CTX_INITED)) {
1284                 diskctx->flags |= DSCHED_SYSCTL_CTX_INITED;
1285                 sysctl_ctx_init(&diskctx->sysctl_ctx);
1286         }
1287
1288         SYSCTL_ADD_PROC(&diskctx->sysctl_ctx, SYSCTL_STATIC_CHILDREN(_dsched_policy),
1289             OID_AUTO, name, CTLTYPE_STRING|CTLFLAG_RW,
1290             diskctx, 0, sysctl_dsched_policy, "A", "policy");
1291 }