| Jie W | 7b6b9fb | 2012-03-20 22:47:35 +0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 24 | enum { ASYNC, SYNC }; |
| 25 | |
| 26 | /* Tunables */ |
| 27 | static const int sync_read_expire = HZ / 2; /* max time before a sync read is submitted. */ |
| 28 | static const int sync_write_expire = 2 * HZ; /* max time before a sync write is submitted. */ |
| 29 | |
| 30 | static const int async_read_expire = 4 * HZ; /* ditto for async, these limits are SOFT! */ |
| 31 | static const int async_write_expire = 16 * HZ; /* ditto for async, these limits are SOFT! */ |
| 32 | |
| 33 | static const int writes_starved = 2; /* max times reads can starve a write */ |
| 34 | static const int fifo_batch = 8; /* # of sequential requests treated as one |
| 35 | by the above parameters. For throughput. */ |
| 36 | |
| 37 | /* Elevator data */ |
| 38 | struct 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 | |
| 52 | static void |
| 53 | sio_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 | |
| 71 | static void |
| 72 | sio_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) |
| 87 | static int |
| 88 | sio_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 | |
| 98 | static struct request * |
| 99 | sio_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 | |
| 117 | static struct request * |
| 118 | sio_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 | |
| 144 | static struct request * |
| 145 | sio_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 | |
| 168 | static inline void |
| 169 | sio_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 | |
| 186 | static int |
| 187 | sio_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 | |
| 218 | static struct request * |
| 219 | sio_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 | |
| 232 | static struct request * |
| 233 | sio_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 | |
| 246 | static void * |
| 247 | sio_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 | |
| 273 | static void |
| 274 | sio_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 | |
| 291 | static ssize_t |
| 292 | sio_var_show(int var, char *page) |
| 293 | { |
| 294 | return sprintf(page, "%d\n", var); |
| 295 | } |
| 296 | |
| 297 | static ssize_t |
| 298 | sio_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) \ |
| 307 | static 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 | } |
| 315 | SHOW_FUNCTION(sio_sync_read_expire_show, sd->fifo_expire[SYNC][READ], 1); |
| 316 | SHOW_FUNCTION(sio_sync_write_expire_show, sd->fifo_expire[SYNC][WRITE], 1); |
| 317 | SHOW_FUNCTION(sio_async_read_expire_show, sd->fifo_expire[ASYNC][READ], 1); |
| 318 | SHOW_FUNCTION(sio_async_write_expire_show, sd->fifo_expire[ASYNC][WRITE], 1); |
| 319 | SHOW_FUNCTION(sio_fifo_batch_show, sd->fifo_batch, 0); |
| 320 | SHOW_FUNCTION(sio_writes_starved_show, sd->writes_starved, 0); |
| 321 | #undef SHOW_FUNCTION |
| 322 | |
| 323 | #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ |
| 324 | static 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 | } |
| 339 | STORE_FUNCTION(sio_sync_read_expire_store, &sd->fifo_expire[SYNC][READ], 0, INT_MAX, 1); |
| 340 | STORE_FUNCTION(sio_sync_write_expire_store, &sd->fifo_expire[SYNC][WRITE], 0, INT_MAX, 1); |
| 341 | STORE_FUNCTION(sio_async_read_expire_store, &sd->fifo_expire[ASYNC][READ], 0, INT_MAX, 1); |
| 342 | STORE_FUNCTION(sio_async_write_expire_store, &sd->fifo_expire[ASYNC][WRITE], 0, INT_MAX, 1); |
| 343 | STORE_FUNCTION(sio_fifo_batch_store, &sd->fifo_batch, 0, INT_MAX, 0); |
| 344 | STORE_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 | |
| 351 | static 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 | |
| 361 | static 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 | |
| 380 | static int __init sio_init(void) |
| 381 | { |
| 382 | /* Register elevator */ |
| 383 | elv_register(&iosched_sio); |
| 384 | |
| 385 | return 0; |
| 386 | } |
| 387 | |
| 388 | static void __exit sio_exit(void) |
| 389 | { |
| 390 | /* Unregister elevator */ |
| 391 | elv_unregister(&iosched_sio); |
| 392 | } |
| 393 | |
| 394 | module_init(sio_init); |
| 395 | module_exit(sio_exit); |
| 396 | |
| 397 | MODULE_AUTHOR("Miguel Boton"); |
| 398 | MODULE_LICENSE("GPL"); |
| 399 | MODULE_DESCRIPTION("Simple IO scheduler"); |
| 400 | MODULE_VERSION("0.2"); |