From 09f2bfe9d39a346abddc196cafc213e571044dfb Mon Sep 17 00:00:00 2001 From: Brills Peng Date: Sat, 27 Aug 2011 11:24:35 +0000 Subject: [PATCH] dsched - Add request polling wrapper * 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 | 69 ++++++++++++++++++++++++++++++++++++++++++ sys/sys/dsched.h | 11 ++++++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/sys/kern/kern_dsched.c b/sys/kern/kern_dsched.c index e0d6f71cd1..c3e779100b 100644 --- a/sys/kern/kern_dsched.c +++ b/sys/kern/kern_dsched.c @@ -580,6 +580,68 @@ dsched_strategy_async(struct disk *dp, struct bio *bio, biodone_t *done, void *p dev_dstrategy(dp->d_rawdev, nbio); } +/* + * 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 @@ -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) diff --git a/sys/sys/dsched.h b/sys/sys/dsched.h index 31d69ad437..fb2961a267 100644 --- a/sys/sys/dsched.h +++ b/sys/sys/dsched.h @@ -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); -- 2.41.0