|
| 1 | +/* |
| 2 | + * Anxiety I/O scheduler |
| 3 | + * Copywrite (C) 2018 Draco (Tyler Nijmeh) <[email protected]> |
| 4 | + */ |
| 5 | +#include <linux/blkdev.h> |
| 6 | +#include <linux/elevator.h> |
| 7 | +#include <linux/bio.h> |
| 8 | +#include <linux/module.h> |
| 9 | +#include <linux/slab.h> |
| 10 | +#include <linux/init.h> |
| 11 | + |
| 12 | +#define MAX_WRITES_STARVED 12 |
| 13 | + |
| 14 | +enum {ASYNC, SYNC}; |
| 15 | + |
| 16 | +struct anxiety_data { |
| 17 | + struct list_head queue[2][2]; |
| 18 | + size_t writes_starved; |
| 19 | +}; |
| 20 | + |
| 21 | +static void anxiety_merged_requests(struct request_queue *q, struct request *rq, struct request *next) { |
| 22 | + rq_fifo_clear(next); |
| 23 | +} |
| 24 | + |
| 25 | +static __always_inline struct request *anxiety_choose_request(struct anxiety_data *mdata) { |
| 26 | + // ensure that reads will always take priority unless writes are exceedingly starved |
| 27 | + bool starved = (mdata->writes_starved > MAX_WRITES_STARVED); |
| 28 | + |
| 29 | + // sync read |
| 30 | + if (!starved && !list_empty(&mdata->queue[SYNC][READ])) { |
| 31 | + mdata->writes_starved++; |
| 32 | + return rq_entry_fifo(mdata->queue[SYNC][READ].next); |
| 33 | + } |
| 34 | + |
| 35 | + // sync write |
| 36 | + if (!list_empty(&mdata->queue[SYNC][WRITE])) { |
| 37 | + mdata->writes_starved = 0; |
| 38 | + return rq_entry_fifo(mdata->queue[SYNC][WRITE].next); |
| 39 | + } |
| 40 | + |
| 41 | + // async read |
| 42 | + if (!starved && !list_empty(&mdata->queue[ASYNC][READ])) { |
| 43 | + mdata->writes_starved++; |
| 44 | + return rq_entry_fifo(mdata->queue[ASYNC][READ].next); |
| 45 | + } |
| 46 | + |
| 47 | + // async write |
| 48 | + if (!list_empty(&mdata->queue[ASYNC][WRITE])) { |
| 49 | + mdata->writes_starved = 0; |
| 50 | + return rq_entry_fifo(mdata->queue[ASYNC][WRITE].next); |
| 51 | + } |
| 52 | + |
| 53 | + // all requests are finished |
| 54 | + mdata->writes_starved = 0; |
| 55 | + return NULL; |
| 56 | +} |
| 57 | + |
| 58 | +static int anxiety_dispatch(struct request_queue *q, int force) { |
| 59 | + struct request *rq = anxiety_choose_request(q->elevator->elevator_data); |
| 60 | + if (!rq) |
| 61 | + return 0; |
| 62 | + |
| 63 | + rq_fifo_clear(rq); |
| 64 | + elv_dispatch_add_tail(rq->q, rq); |
| 65 | + return 1; |
| 66 | +} |
| 67 | + |
| 68 | +static void anxiety_add_request(struct request_queue *q, struct request *rq) { |
| 69 | + const uint8_t sync = rq_is_sync(rq); |
| 70 | + const uint8_t read = rq_data_dir(rq); |
| 71 | + list_add_tail(&rq->queuelist, &((struct anxiety_data *) q->elevator->elevator_data)->queue[sync][read]); |
| 72 | +} |
| 73 | + |
| 74 | +static struct request *anxiety_former_request(struct request_queue *q, struct request *rq) { |
| 75 | + const uint8_t sync = rq_is_sync(rq); |
| 76 | + const uint8_t read = rq_data_dir(rq); |
| 77 | + if (rq->queuelist.prev == &((struct anxiety_data *) q->elevator->elevator_data)->queue[sync][read]) |
| 78 | + return NULL; |
| 79 | + return list_prev_entry(rq, queuelist); |
| 80 | +} |
| 81 | + |
| 82 | +static struct request *anxiety_latter_request(struct request_queue *q, struct request *rq) { |
| 83 | + const uint8_t sync = rq_is_sync(rq); |
| 84 | + const uint8_t read = rq_data_dir(rq); |
| 85 | + if (rq->queuelist.next == &((struct anxiety_data *) q->elevator->elevator_data)->queue[sync][read]) |
| 86 | + return NULL; |
| 87 | + return list_next_entry(rq, queuelist); |
| 88 | +} |
| 89 | + |
| 90 | +static int anxiety_init_queue(struct request_queue *q, struct elevator_type *e) { |
| 91 | + struct anxiety_data *nd; |
| 92 | + struct elevator_queue *eq = elevator_alloc(q, e); |
| 93 | + if (!eq) |
| 94 | + return -ENOMEM; |
| 95 | + |
| 96 | + nd = kmalloc_node(sizeof(*nd), GFP_KERNEL, q->node); |
| 97 | + if (!nd) { |
| 98 | + kobject_put(&eq->kobj); |
| 99 | + return -ENOMEM; |
| 100 | + } |
| 101 | + eq->elevator_data = nd; |
| 102 | + |
| 103 | + INIT_LIST_HEAD(&nd->queue[SYNC][READ]); |
| 104 | + INIT_LIST_HEAD(&nd->queue[SYNC][WRITE]); |
| 105 | + INIT_LIST_HEAD(&nd->queue[ASYNC][READ]); |
| 106 | + INIT_LIST_HEAD(&nd->queue[ASYNC][WRITE]); |
| 107 | + nd->writes_starved = 0; |
| 108 | + |
| 109 | + spin_lock_irq(q->queue_lock); |
| 110 | + q->elevator = eq; |
| 111 | + spin_unlock_irq(q->queue_lock); |
| 112 | + return 0; |
| 113 | +} |
| 114 | + |
| 115 | +static struct elevator_type elevator_anxiety = { |
| 116 | + .ops = { |
| 117 | + .elevator_merge_req_fn = anxiety_merged_requests, |
| 118 | + .elevator_dispatch_fn = anxiety_dispatch, |
| 119 | + .elevator_add_req_fn = anxiety_add_request, |
| 120 | + .elevator_former_req_fn = anxiety_former_request, |
| 121 | + .elevator_latter_req_fn = anxiety_latter_request, |
| 122 | + .elevator_init_fn = anxiety_init_queue, |
| 123 | + }, |
| 124 | + .elevator_name = "anxiety", |
| 125 | + .elevator_owner = THIS_MODULE, |
| 126 | +}; |
| 127 | + |
| 128 | +static int __init anxiety_init(void) { |
| 129 | + return elv_register(&elevator_anxiety); |
| 130 | +} |
| 131 | + |
| 132 | +static void __exit anxiety_exit(void) { |
| 133 | + elv_unregister(&elevator_anxiety); |
| 134 | +} |
| 135 | + |
| 136 | +module_init(anxiety_init); |
| 137 | +module_exit(anxiety_exit); |
| 138 | + |
| 139 | +MODULE_AUTHOR("Draco (Tyler Nijmeh)"); |
| 140 | +MODULE_LICENSE("GPL"); |
| 141 | +MODULE_DESCRIPTION("Anxiety IO scheduler"); |
0 commit comments