blob: b591cff1a83e123b9a22d1e3d6a916558277c0d8 [file] [log] [blame]
Jie W7b6b9fb2012-03-20 22:47:35 +08001/*
2 * Simple IO scheduler
3 * Based on Noop, Deadline and V(R) IO schedulers.
4 *
5 * Copyright (C) 2012 Miguel Boton <mboton@gmail.com>
6 *
7 *
8 * This algorithm does not do any kind of sorting, as it is aimed for
9 * aleatory access devices, but it does some basic merging. We try to
10 * keep minimum overhead to achieve low latency.
11 *
12 * Asynchronous and synchronous requests are not treated separately, but
13 * we relay on deadlines to ensure fairness.
14 *
15 */
16#include <linux/blkdev.h>
17#include <linux/elevator.h>
18#include <linux/bio.h>
19#include <linux/module.h>
20#include <linux/slab.h>
21#include <linux/init.h>
22#include <linux/version.h>
23
24enum { ASYNC, SYNC };
25
26/* Tunables */
27static const int sync_read_expire = HZ / 2; /* max time before a sync read is submitted. */
28static const int sync_write_expire = 2 * HZ; /* max time before a sync write is submitted. */
29
30static const int async_read_expire = 4 * HZ; /* ditto for async, these limits are SOFT! */
31static const int async_write_expire = 16 * HZ; /* ditto for async, these limits are SOFT! */
32
33static const int writes_starved = 2; /* max times reads can starve a write */
34static const int fifo_batch = 8; /* # of sequential requests treated as one
35 by the above parameters. For throughput. */
36
37/* Elevator data */
38struct sio_data {
39 /* Request queues */
40 struct list_head fifo_list[2][2];
41
42 /* Attributes */
43 unsigned int batched;
44 unsigned int starved;
45
46 /* Settings */
47 int fifo_expire[2][2];
48 int fifo_batch;
49 int writes_starved;
50};
51
52static void
53sio_merged_requests(struct request_queue *q, struct request *rq,
54 struct request *next)
55{
56 /*
57 * If next expires before rq, assign its expire time to rq
58 * and move into next position (next will be deleted) in fifo.
59 */
60 if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) {
61 if (time_before(rq_fifo_time(next), rq_fifo_time(rq))) {
62 list_move(&rq->queuelist, &next->queuelist);
63 rq_set_fifo_time(rq, rq_fifo_time(next));
64 }
65 }
66
67 /* Delete next request */
68 rq_fifo_clear(next);
69}
70
71static void
72sio_add_request(struct request_queue *q, struct request *rq)
73{
74 struct sio_data *sd = q->elevator->elevator_data;
75 const int sync = rq_is_sync(rq);
76 const int data_dir = rq_data_dir(rq);
77
78 /*
79 * Add request to the proper fifo list and set its
80 * expire time.
81 */
82 rq_set_fifo_time(rq, jiffies + sd->fifo_expire[sync][data_dir]);
83 list_add_tail(&rq->queuelist, &sd->fifo_list[sync][data_dir]);
84}
85
86#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38)
87static int
88sio_queue_empty(struct request_queue *q)
89{
90 struct sio_data *sd = q->elevator->elevator_data;
91
92 /* Check if fifo lists are empty */
93 return list_empty(&sd->fifo_list[SYNC][READ]) && list_empty(&sd->fifo_list[SYNC][WRITE]) &&
94 list_empty(&sd->fifo_list[ASYNC][READ]) && list_empty(&sd->fifo_list[ASYNC][WRITE]);
95}
96#endif
97
98static struct request *
99sio_expired_request(struct sio_data *sd, int sync, int data_dir)
100{
101 struct list_head *list = &sd->fifo_list[sync][data_dir];
102 struct request *rq;
103
104 if (list_empty(list))
105 return NULL;
106
107 /* Retrieve request */
108 rq = rq_entry_fifo(list->next);
109
110 /* Request has expired */
111 if (time_after(jiffies, rq_fifo_time(rq)))
112 return rq;
113
114 return NULL;
115}
116
117static struct request *
118sio_choose_expired_request(struct sio_data *sd)
119{
120 struct request *rq;
121
122 /*
123 * Check expired requests.
124 * Asynchronous requests have priority over synchronous.
125 * Write requests have priority over read.
126 */
127 rq = sio_expired_request(sd, ASYNC, WRITE);
128 if (rq)
129 return rq;
130 rq = sio_expired_request(sd, ASYNC, READ);
131 if (rq)
132 return rq;
133
134 rq = sio_expired_request(sd, SYNC, WRITE);
135 if (rq)
136 return rq;
137 rq = sio_expired_request(sd, SYNC, READ);
138 if (rq)
139 return rq;
140
141 return NULL;
142}
143
144static struct request *
145sio_choose_request(struct sio_data *sd, int data_dir)
146{
147 struct list_head *sync = sd->fifo_list[SYNC];
148 struct list_head *async = sd->fifo_list[ASYNC];
149
150 /*
151 * Retrieve request from available fifo list.
152 * Synchronous requests have priority over asynchronous.
153 * Read requests have priority over write.
154 */
155 if (!list_empty(&sync[data_dir]))
156 return rq_entry_fifo(sync[data_dir].next);
157 if (!list_empty(&async[data_dir]))
158 return rq_entry_fifo(async[data_dir].next);
159
160 if (!list_empty(&sync[!data_dir]))
161 return rq_entry_fifo(sync[!data_dir].next);
162 if (!list_empty(&async[!data_dir]))
163 return rq_entry_fifo(async[!data_dir].next);
164
165 return NULL;
166}
167
168static inline void
169sio_dispatch_request(struct sio_data *sd, struct request *rq)
170{
171 /*
172 * Remove the request from the fifo list
173 * and dispatch it.
174 */
175 rq_fifo_clear(rq);
176 elv_dispatch_add_tail(rq->q, rq);
177
178 sd->batched++;
179
180 if (rq_data_dir(rq))
181 sd->starved = 0;
182 else
183 sd->starved++;
184}
185
186static int
187sio_dispatch_requests(struct request_queue *q, int force)
188{
189 struct sio_data *sd = q->elevator->elevator_data;
190 struct request *rq = NULL;
191 int data_dir = READ;
192
193 /*
194 * Retrieve any expired request after a batch of
195 * sequential requests.
196 */
197 if (sd->batched > sd->fifo_batch) {
198 sd->batched = 0;
199 rq = sio_choose_expired_request(sd);
200 }
201
202 /* Retrieve request */
203 if (!rq) {
204 if (sd->starved > sd->writes_starved)
205 data_dir = WRITE;
206
207 rq = sio_choose_request(sd, data_dir);
208 if (!rq)
209 return 0;
210 }
211
212 /* Dispatch request */
213 sio_dispatch_request(sd, rq);
214
215 return 1;
216}
217
218static struct request *
219sio_former_request(struct request_queue *q, struct request *rq)
220{
221 struct sio_data *sd = q->elevator->elevator_data;
222 const int sync = rq_is_sync(rq);
223 const int data_dir = rq_data_dir(rq);
224
225 if (rq->queuelist.prev == &sd->fifo_list[sync][data_dir])
226 return NULL;
227
228 /* Return former request */
229 return list_entry(rq->queuelist.prev, struct request, queuelist);
230}
231
232static struct request *
233sio_latter_request(struct request_queue *q, struct request *rq)
234{
235 struct sio_data *sd = q->elevator->elevator_data;
236 const int sync = rq_is_sync(rq);
237 const int data_dir = rq_data_dir(rq);
238
239 if (rq->queuelist.next == &sd->fifo_list[sync][data_dir])
240 return NULL;
241
242 /* Return latter request */
243 return list_entry(rq->queuelist.next, struct request, queuelist);
244}
245
246static void *
247sio_init_queue(struct request_queue *q)
248{
249 struct sio_data *sd;
250
251 /* Allocate structure */
252 sd = kmalloc_node(sizeof(*sd), GFP_KERNEL, q->node);
253 if (!sd)
254 return NULL;
255
256 /* Initialize fifo lists */
257 INIT_LIST_HEAD(&sd->fifo_list[SYNC][READ]);
258 INIT_LIST_HEAD(&sd->fifo_list[SYNC][WRITE]);
259 INIT_LIST_HEAD(&sd->fifo_list[ASYNC][READ]);
260 INIT_LIST_HEAD(&sd->fifo_list[ASYNC][WRITE]);
261
262 /* Initialize data */
263 sd->batched = 0;
264 sd->fifo_expire[SYNC][READ] = sync_read_expire;
265 sd->fifo_expire[SYNC][WRITE] = sync_write_expire;
266 sd->fifo_expire[ASYNC][READ] = async_read_expire;
267 sd->fifo_expire[ASYNC][WRITE] = async_write_expire;
268 sd->fifo_batch = fifo_batch;
269
270 return sd;
271}
272
273static void
274sio_exit_queue(struct elevator_queue *e)
275{
276 struct sio_data *sd = e->elevator_data;
277
278 BUG_ON(!list_empty(&sd->fifo_list[SYNC][READ]));
279 BUG_ON(!list_empty(&sd->fifo_list[SYNC][WRITE]));
280 BUG_ON(!list_empty(&sd->fifo_list[ASYNC][READ]));
281 BUG_ON(!list_empty(&sd->fifo_list[ASYNC][WRITE]));
282
283 /* Free structure */
284 kfree(sd);
285}
286
287/*
288 * sysfs code
289 */
290
291static ssize_t
292sio_var_show(int var, char *page)
293{
294 return sprintf(page, "%d\n", var);
295}
296
297static ssize_t
298sio_var_store(int *var, const char *page, size_t count)
299{
300 char *p = (char *) page;
301
302 *var = simple_strtol(p, &p, 10);
303 return count;
304}
305
306#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \
307static ssize_t __FUNC(struct elevator_queue *e, char *page) \
308{ \
309 struct sio_data *sd = e->elevator_data; \
310 int __data = __VAR; \
311 if (__CONV) \
312 __data = jiffies_to_msecs(__data); \
313 return sio_var_show(__data, (page)); \
314}
315SHOW_FUNCTION(sio_sync_read_expire_show, sd->fifo_expire[SYNC][READ], 1);
316SHOW_FUNCTION(sio_sync_write_expire_show, sd->fifo_expire[SYNC][WRITE], 1);
317SHOW_FUNCTION(sio_async_read_expire_show, sd->fifo_expire[ASYNC][READ], 1);
318SHOW_FUNCTION(sio_async_write_expire_show, sd->fifo_expire[ASYNC][WRITE], 1);
319SHOW_FUNCTION(sio_fifo_batch_show, sd->fifo_batch, 0);
320SHOW_FUNCTION(sio_writes_starved_show, sd->writes_starved, 0);
321#undef SHOW_FUNCTION
322
323#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \
324static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \
325{ \
326 struct sio_data *sd = e->elevator_data; \
327 int __data; \
328 int ret = sio_var_store(&__data, (page), count); \
329 if (__data < (MIN)) \
330 __data = (MIN); \
331 else if (__data > (MAX)) \
332 __data = (MAX); \
333 if (__CONV) \
334 *(__PTR) = msecs_to_jiffies(__data); \
335 else \
336 *(__PTR) = __data; \
337 return ret; \
338}
339STORE_FUNCTION(sio_sync_read_expire_store, &sd->fifo_expire[SYNC][READ], 0, INT_MAX, 1);
340STORE_FUNCTION(sio_sync_write_expire_store, &sd->fifo_expire[SYNC][WRITE], 0, INT_MAX, 1);
341STORE_FUNCTION(sio_async_read_expire_store, &sd->fifo_expire[ASYNC][READ], 0, INT_MAX, 1);
342STORE_FUNCTION(sio_async_write_expire_store, &sd->fifo_expire[ASYNC][WRITE], 0, INT_MAX, 1);
343STORE_FUNCTION(sio_fifo_batch_store, &sd->fifo_batch, 0, INT_MAX, 0);
344STORE_FUNCTION(sio_writes_starved_store, &sd->writes_starved, 0, INT_MAX, 0);
345#undef STORE_FUNCTION
346
347#define DD_ATTR(name) \
348 __ATTR(name, S_IRUGO|S_IWUSR, sio_##name##_show, \
349 sio_##name##_store)
350
351static struct elv_fs_entry sio_attrs[] = {
352 DD_ATTR(sync_read_expire),
353 DD_ATTR(sync_write_expire),
354 DD_ATTR(async_read_expire),
355 DD_ATTR(async_write_expire),
356 DD_ATTR(fifo_batch),
357 DD_ATTR(writes_starved),
358 __ATTR_NULL
359};
360
361static struct elevator_type iosched_sio = {
362 .ops = {
363 .elevator_merge_req_fn = sio_merged_requests,
364 .elevator_dispatch_fn = sio_dispatch_requests,
365 .elevator_add_req_fn = sio_add_request,
366#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38)
367 .elevator_queue_empty_fn = sio_queue_empty,
368#endif
369 .elevator_former_req_fn = sio_former_request,
370 .elevator_latter_req_fn = sio_latter_request,
371 .elevator_init_fn = sio_init_queue,
372 .elevator_exit_fn = sio_exit_queue,
373 },
374
375 .elevator_attrs = sio_attrs,
376 .elevator_name = "sio",
377 .elevator_owner = THIS_MODULE,
378};
379
380static int __init sio_init(void)
381{
382 /* Register elevator */
383 elv_register(&iosched_sio);
384
385 return 0;
386}
387
388static void __exit sio_exit(void)
389{
390 /* Unregister elevator */
391 elv_unregister(&iosched_sio);
392}
393
394module_init(sio_init);
395module_exit(sio_exit);
396
397MODULE_AUTHOR("Miguel Boton");
398MODULE_LICENSE("GPL");
399MODULE_DESCRIPTION("Simple IO scheduler");
400MODULE_VERSION("0.2");