Skip to content

Commit 225472a

Browse files
tytydracokdrag0n
authored andcommitted
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 <[email protected]> Signed-off-by: kdrag0n <[email protected]> Signed-off-by: Michael <[email protected]>
1 parent 0511d65 commit 225472a

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

block/Kconfig.iosched

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ config IOSCHED_CFQ
3232

3333
This is the default I/O scheduler.
3434

35+
config IOSCHED_ANXIETY
36+
tristate "Anxiety I/O scheduler"
37+
default y
38+
---help---
39+
The Anxiety I/O scheduler prioritizes latency over everything
40+
else. When a request comes in, it will use a lighweight
41+
selection algorithm to swiftly process the current pending task.
42+
3543
config CFQ_GROUP_IOSCHED
3644
bool "CFQ Group Scheduling support"
3745
depends on IOSCHED_CFQ && BLK_CGROUP
@@ -66,11 +74,14 @@ choice
6674
config DEFAULT_TRIPNDROID
6775
bool "TD" if IOSCHED_TRIPNDROID=y
6876

77+
config DEFAULT_ANXIETY
78+
bool "Anxiety" if IOSCHED_ANXIETY=y
6979
endchoice
7080

7181
config DEFAULT_IOSCHED
7282
string
7383
default "deadline" if DEFAULT_DEADLINE
84+
default "anxiety" if DEFAULT_ANXIETY
7485
default "cfq" if DEFAULT_CFQ
7586
default "noop" if DEFAULT_NOOP
7687
default "tripndroid" if DEFAULT_TRIPNDROID

block/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ obj-$(CONFIG_BLK_DEV_THROTTLING) += blk-throttle.o
1818
obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o
1919
obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o
2020
obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o
21+
obj-$(CONFIG_IOSCHED_ANXIETY) += anxiety-iosched.o
2122
obj-$(CONFIG_IOSCHED_TRIPNDROID) += tripndroid-iosched.o
2223

2324
obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o

block/anxiety-iosched.c

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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

Comments
 (0)