From 8191389560d2d6cada6b913634fc9533315cba5f Mon Sep 17 00:00:00 2001 From: Tyler Nijmeh Date: Tue, 11 Sep 2018 20:28:16 -0700 Subject: [PATCH] block: introduce Anxiety I/O scheduler Anxiety is a minimal scheduler based on noop. It uses a simple algorithm prioritizing synchronous requests as they are blocking, and reads over writes. Requests are stored in compact FIFO queues composed of doubly linked lists. Latency is the #1 priority. [kdrag0n: squash initial version & add description] Co-authored-by: kdrag0n Signed-off-by: kdrag0n Signed-off-by: Michael --- block/Kconfig.iosched | 11 ++++ block/Makefile | 1 + block/anxiety-iosched.c | 141 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 block/anxiety-iosched.c diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched index dc87484fa731..01dcd380fe4b 100644 --- a/block/Kconfig.iosched +++ b/block/Kconfig.iosched @@ -54,6 +54,14 @@ config IOSCHED_SIO basic merging, trying to keep a minimum overhead. It is aimed mainly for aleatory access devices (eg: flash devices). +config IOSCHED_ANXIETY + tristate "Anxiety I/O scheduler" + default y + ---help--- + The Anxiety I/O scheduler prioritizes latency over everything + else. When a request comes in, it will use a lighweight + selection algorithm to swiftly process the current pending task. + config CFQ_GROUP_IOSCHED bool "CFQ Group Scheduling support" depends on IOSCHED_CFQ && BLK_CGROUP @@ -104,11 +112,14 @@ choice config DEFAULT_TRIPNDROID bool "TD" if IOSCHED_TRIPNDROID=y + config DEFAULT_ANXIETY + bool "Anxiety" if IOSCHED_ANXIETY=y endchoice config DEFAULT_IOSCHED string default "deadline" if DEFAULT_DEADLINE + default "anxiety" if DEFAULT_ANXIETY default "cfq" if DEFAULT_CFQ default "zen" if DEFAULT_ZEN default "noop" if DEFAULT_NOOP diff --git a/block/Makefile b/block/Makefile index b28f0963a801..77e506edb521 100644 --- a/block/Makefile +++ b/block/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o obj-$(CONFIG_IOSCHED_FIOPS) += fiops-iosched.o obj-$(CONFIG_IOSCHED_SIO) += sio-iosched.o obj-$(CONFIG_IOSCHED_ZEN) += zen-iosched.o +obj-$(CONFIG_IOSCHED_ANXIETY) += anxiety-iosched.o obj-$(CONFIG_IOSCHED_MAPLE) += maple-iosched.o obj-$(CONFIG_IOSCHED_TRIPNDROID) += tripndroid-iosched.o diff --git a/block/anxiety-iosched.c b/block/anxiety-iosched.c new file mode 100644 index 000000000000..ddc409369e56 --- /dev/null +++ b/block/anxiety-iosched.c @@ -0,0 +1,141 @@ +/* + * Anxiety I/O scheduler + * Copywrite (C) 2018 Draco (Tyler Nijmeh) + */ +#include +#include +#include +#include +#include +#include + +#define MAX_WRITES_STARVED 12 + +enum {ASYNC, SYNC}; + +struct anxiety_data { + struct list_head queue[2][2]; + size_t writes_starved; +}; + +static void anxiety_merged_requests(struct request_queue *q, struct request *rq, struct request *next) { + rq_fifo_clear(next); +} + +static __always_inline struct request *anxiety_choose_request(struct anxiety_data *mdata) { + // ensure that reads will always take priority unless writes are exceedingly starved + bool starved = (mdata->writes_starved > MAX_WRITES_STARVED); + + // sync read + if (!starved && !list_empty(&mdata->queue[SYNC][READ])) { + mdata->writes_starved++; + return rq_entry_fifo(mdata->queue[SYNC][READ].next); + } + + // sync write + if (!list_empty(&mdata->queue[SYNC][WRITE])) { + mdata->writes_starved = 0; + return rq_entry_fifo(mdata->queue[SYNC][WRITE].next); + } + + // async read + if (!starved && !list_empty(&mdata->queue[ASYNC][READ])) { + mdata->writes_starved++; + return rq_entry_fifo(mdata->queue[ASYNC][READ].next); + } + + // async write + if (!list_empty(&mdata->queue[ASYNC][WRITE])) { + mdata->writes_starved = 0; + return rq_entry_fifo(mdata->queue[ASYNC][WRITE].next); + } + + // all requests are finished + mdata->writes_starved = 0; + return NULL; +} + +static int anxiety_dispatch(struct request_queue *q, int force) { + struct request *rq = anxiety_choose_request(q->elevator->elevator_data); + if (!rq) + return 0; + + rq_fifo_clear(rq); + elv_dispatch_add_tail(rq->q, rq); + return 1; +} + +static void anxiety_add_request(struct request_queue *q, struct request *rq) { + const uint8_t sync = rq_is_sync(rq); + const uint8_t read = rq_data_dir(rq); + list_add_tail(&rq->queuelist, &((struct anxiety_data *) q->elevator->elevator_data)->queue[sync][read]); +} + +static struct request *anxiety_former_request(struct request_queue *q, struct request *rq) { + const uint8_t sync = rq_is_sync(rq); + const uint8_t read = rq_data_dir(rq); + if (rq->queuelist.prev == &((struct anxiety_data *) q->elevator->elevator_data)->queue[sync][read]) + return NULL; + return list_prev_entry(rq, queuelist); +} + +static struct request *anxiety_latter_request(struct request_queue *q, struct request *rq) { + const uint8_t sync = rq_is_sync(rq); + const uint8_t read = rq_data_dir(rq); + if (rq->queuelist.next == &((struct anxiety_data *) q->elevator->elevator_data)->queue[sync][read]) + return NULL; + return list_next_entry(rq, queuelist); +} + +static int anxiety_init_queue(struct request_queue *q, struct elevator_type *e) { + struct anxiety_data *nd; + struct elevator_queue *eq = elevator_alloc(q, e); + if (!eq) + return -ENOMEM; + + nd = kmalloc_node(sizeof(*nd), GFP_KERNEL, q->node); + if (!nd) { + kobject_put(&eq->kobj); + return -ENOMEM; + } + eq->elevator_data = nd; + + INIT_LIST_HEAD(&nd->queue[SYNC][READ]); + INIT_LIST_HEAD(&nd->queue[SYNC][WRITE]); + INIT_LIST_HEAD(&nd->queue[ASYNC][READ]); + INIT_LIST_HEAD(&nd->queue[ASYNC][WRITE]); + nd->writes_starved = 0; + + spin_lock_irq(q->queue_lock); + q->elevator = eq; + spin_unlock_irq(q->queue_lock); + return 0; +} + +static struct elevator_type elevator_anxiety = { + .ops = { + .elevator_merge_req_fn = anxiety_merged_requests, + .elevator_dispatch_fn = anxiety_dispatch, + .elevator_add_req_fn = anxiety_add_request, + .elevator_former_req_fn = anxiety_former_request, + .elevator_latter_req_fn = anxiety_latter_request, + .elevator_init_fn = anxiety_init_queue, + }, + .elevator_name = "anxiety", + .elevator_owner = THIS_MODULE, +}; + +static int __init anxiety_init(void) { + return elv_register(&elevator_anxiety); +} + +static void __exit anxiety_exit(void) { + elv_unregister(&elevator_anxiety); +} + +module_init(anxiety_init); +module_exit(anxiety_exit); + +MODULE_AUTHOR("Draco (Tyler Nijmeh)"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Anxiety IO scheduler");