dsched - Add request polling wrapper
authorBrills Peng <brills@gmail.com>
Sat, 27 Aug 2011 11:24:35 +0000 (11:24 +0000)
committerAlex Hornung <ahornung@gmail.com>
Sat, 27 Aug 2011 12:34:19 +0000 (12:34 +0000)
 * Add a request polling emulation layer to dsched. This emulated
   request polling as if a disk driver would poll for requests instead
   of requests being actively pushed down.

 * The policy->polling_func() callback is called whenever a BIO
   completes.

 * A field in the diskctx that shows the current tag queue depth and the
   maximum tag queue depth (currently fixed value of 32) are used in the
   policies using request polling directly and is not enforced in the
   dsched layer. That is, a policy using request polling emulation
   should take care of not having (many) more BIOs in flight than
   max_tag_queue_depth.

Sponsored-by: Google Summer of Code
sys/kern/kern_dsched.c
sys/sys/dsched.h

index e0d6f71..c3e7791 100644 (file)
@@ -581,6 +581,68 @@ dsched_strategy_async(struct disk *dp, struct bio *bio, biodone_t *done, void *p
 }
 
 /*
+ * A special bio done call back function
+ * used by policy having request polling implemented.
+ */
+static void
+request_polling_biodone(struct bio *bp)
+{
+       struct dsched_disk_ctx *diskctx = NULL;
+       struct disk *dp = NULL;
+       struct bio *obio;
+       struct dsched_policy *policy;
+
+       dp = dsched_get_bio_dp(bp);
+       policy = dp->d_sched_policy;
+       diskctx = dsched_get_disk_priv(dp);
+       KKASSERT(diskctx && policy);
+       dsched_disk_ctx_ref(diskctx);
+
+       /*
+        * XXX:
+        * the bio_done function should not be blocked !
+        */
+       if (diskctx->dp->d_sched_policy->bio_done)
+               diskctx->dp->d_sched_policy->bio_done(bp);
+
+       obio = pop_bio(bp);
+       biodone(obio);
+
+       atomic_subtract_int(&diskctx->current_tag_queue_depth, 1);
+
+       /* call the polling function,
+        * XXX:
+        * the polling function should not be blocked!
+        */
+       if (policy->polling_func)
+               policy->polling_func(diskctx);
+       else
+               dsched_debug(0, "dsched: the policy uses request polling without a polling function!\n");
+       dsched_disk_ctx_unref(diskctx);
+}
+
+/*
+ * A special dsched strategy used by policy having request polling
+ * (polling function) implemented.
+ *
+ * The strategy is the just like dsched_strategy_async(), but
+ * the biodone call back is set to a preset one.
+ *
+ * If the policy needs its own biodone callback, it should
+ * register it in the policy structure. (bio_done field)
+ *
+ * The current_tag_queue_depth is maintained by this function
+ * and the request_polling_biodone() function
+ */
+
+void
+dsched_strategy_request_polling(struct disk *dp, struct bio *bio, struct dsched_disk_ctx *diskctx)
+{
+       atomic_add_int(&diskctx->current_tag_queue_depth, 1);
+       dsched_strategy_async(dp, bio, request_polling_biodone, dsched_get_bio_priv(bio));
+}
+
+/*
  * Ref and deref various structures.  The 1->0 transition of the reference
  * count actually transitions 1->0x80000000 and causes the object to be
  * destroyed.  It is possible for transitory references to occur on the
@@ -878,6 +940,13 @@ dsched_disk_ctx_alloc(struct disk *dp, struct dsched_policy *pol)
        diskctx->dp = dp;
        DSCHED_DISK_CTX_LOCKINIT(diskctx);
        TAILQ_INIT(&diskctx->tdio_list);
+       /*
+        * XXX: magic number 32: most device has a tag queue
+        * of depth 32.
+        * Better to retrive more precise value from the driver
+        */
+       diskctx->max_tag_queue_depth = 32;
+       diskctx->current_tag_queue_depth = 0;
 
        atomic_add_int(&dsched_stats.diskctx_allocations, 1);
        if (pol->new_diskctx)
index 31d69ad..fb2961a 100644 (file)
@@ -109,6 +109,9 @@ struct dsched_disk_ctx {
        int32_t         refcount;
        int32_t         flags;
 
+       int             max_tag_queue_depth;            /* estimated max tag queue depth */
+       int             current_tag_queue_depth;        /* estimated current tag queue depth */
+
        struct disk     *dp;            /* back pointer to disk struct */
 
        struct sysctl_ctx_list sysctl_ctx;
@@ -142,12 +145,14 @@ typedef void      dsched_teardown_t(struct dsched_disk_ctx *diskctx);
 typedef void   dsched_cancel_t(struct dsched_disk_ctx *diskctx);
 typedef int    dsched_queue_t(struct dsched_disk_ctx *diskctx,
                    struct dsched_thread_io *tdio, struct bio *bio);
+typedef void dsched_dequeue_t(struct dsched_disk_ctx *diskctx);
 
 typedef        void    dsched_new_tdio_t(struct dsched_thread_io *tdio);
 typedef        void    dsched_new_diskctx_t(struct dsched_disk_ctx *diskctx);
 typedef        void    dsched_destroy_tdio_t(struct dsched_thread_io *tdio);
 typedef        void    dsched_destroy_diskctx_t(struct dsched_disk_ctx *diskctx);
-
+typedef void   dsched_bio_done_t(struct bio *bio);
+typedef void   dsched_polling_func_t(struct dsched_disk_ctx *diskctx);
 
 struct dsched_policy {
        char                    name[DSCHED_POLICY_NAME_LENGTH];
@@ -165,6 +170,9 @@ struct dsched_policy {
        dsched_new_diskctx_t    *new_diskctx;
        dsched_destroy_tdio_t   *destroy_tdio;
        dsched_destroy_diskctx_t        *destroy_diskctx;
+
+       dsched_bio_done_t       *bio_done;      /* call back when a bio dispatched by dsched_strategy_request_polling() is done */
+       dsched_polling_func_t   *polling_func; /* it gets called when the disk is idle or about to idle */
 };
 
 TAILQ_HEAD(dsched_policy_head, dsched_policy);
@@ -253,6 +261,7 @@ void        dsched_cancel_bio(struct bio *bp);
 void   dsched_strategy_raw(struct disk *dp, struct bio *bp);
 void   dsched_strategy_sync(struct disk *dp, struct bio *bp);
 void   dsched_strategy_async(struct disk *dp, struct bio *bp, biodone_t *done, void *priv);
+void   dsched_strategy_request_polling(struct disk *bp, struct bio *bio, struct dsched_disk_ctx *diskctx);
 int    dsched_debug(int level, char *fmt, ...) __printflike(2, 3);
 
 void   policy_new(struct disk *dp, struct dsched_policy *pol);