diff --git a/Kernel/arch/arm/configs/android_latona_r08_eng_defconfig b/Kernel/arch/arm/configs/android_latona_r08_eng_defconfig index ba264f42..0a30a293 100755 --- a/Kernel/arch/arm/configs/android_latona_r08_eng_defconfig +++ b/Kernel/arch/arm/configs/android_latona_r08_eng_defconfig @@ -167,10 +167,12 @@ CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y CONFIG_IOSCHED_BFQ=y +CONFIG_IOSCHED_SIO=y # CONFIG_CGROUP_BFQIO is not set # CONFIG_DEFAULT_DEADLINE is not set # CONFIG_DEFAULT_CFQ is not set CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_SIO is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="bfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set diff --git a/Kernel/arch/arm/configs/android_latona_r08_user_defconfig b/Kernel/arch/arm/configs/android_latona_r08_user_defconfig index c1d78ada..8096ec96 100755 --- a/Kernel/arch/arm/configs/android_latona_r08_user_defconfig +++ b/Kernel/arch/arm/configs/android_latona_r08_user_defconfig @@ -167,10 +167,12 @@ CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y CONFIG_IOSCHED_BFQ=y +CONFIG_IOSCHED_SIO=y # CONFIG_CGROUP_BFQIO is not set # CONFIG_DEFAULT_DEADLINE is not set # CONFIG_DEFAULT_CFQ is not set CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_SIO is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="bfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set diff --git a/Kernel/block/Kconfig.iosched b/Kernel/block/Kconfig.iosched index 59054520..73cbb9f9 100755 --- a/Kernel/block/Kconfig.iosched +++ b/Kernel/block/Kconfig.iosched @@ -65,6 +65,16 @@ config CGROUP_BFQIO filesystem interface. The name of the subsystem will be bfqio. +config IOSCHED_SIO + tristate "Simple I/O scheduler" + default y + ---help--- + The Simple I/O scheduler is an extremely simple scheduler, + based on noop and deadline, that relies on deadlines to + ensure fairness. The algorithm does not do any sorting but + basic merging, trying to keep a minimum overhead. It is aimed + mainly for aleatory access devices (eg: flash devices). + choice prompt "Default I/O scheduler" default DEFAULT_CFQ @@ -81,6 +91,9 @@ choice config DEFAULT_BFQ bool "BFQ" if IOSCHED_BFQ=y + config DEFAULT_SIO + bool "SIO" if IOSCHED_SIO=y + config DEFAULT_NOOP bool "No-op" @@ -91,6 +104,7 @@ config DEFAULT_IOSCHED default "deadline" if DEFAULT_DEADLINE default "cfq" if DEFAULT_CFQ default "bfq" if DEFAULT_BFQ + default "sio" if DEFAULT_SIO default "noop" if DEFAULT_NOOP endmenu diff --git a/Kernel/block/Makefile b/Kernel/block/Makefile index 99a93efd..8ccf62be 100755 --- a/Kernel/block/Makefile +++ b/Kernel/block/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o obj-$(CONFIG_IOSCHED_BFQ) += bfq-iosched.o +obj-$(CONFIG_IOSCHED_SIO) += sio-iosched.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o diff --git a/Kernel/block/sio-iosched.c b/Kernel/block/sio-iosched.c new file mode 100755 index 00000000..7b288d6c --- /dev/null +++ b/Kernel/block/sio-iosched.c @@ -0,0 +1,346 @@ +/* + * Simple IO scheduler + * Based on Noop, Deadline and V(R) IO schedulers. + * + * Copyright (C) 2010 Miguel Boton + * + * + * This algorithm does not do any kind of sorting, as it is aimed for + * aleatory access devices, but it does some basic merging. We try to + * keep minimum overhead to achieve low latency. + * + * Asynchronous and synchronous requests are not treated separately, but + * we relay on deadlines to ensure fairness. + * + */ +#include +#include +#include +#include +#include +#include + +enum { + ASYNC, + SYNC, +}; + +/* Tunables */ +static const int sync_expire = HZ / 2; /* max time before a sync is submitted. */ +static const int async_expire = 5 * HZ; /* ditto for async, these limits are SOFT! */ +static const int fifo_batch = 16; /* # of sequential requests treated as one + by the above parameters. For throughput. */ + +/* Elevator data */ +struct sio_data { + /* Request queues */ + struct list_head fifo_list[2]; + + /* Attributes */ + unsigned int batched; + + /* Settings */ + int fifo_expire[2]; + int fifo_batch; +}; + +static void +sio_merged_requests(struct request_queue *q, struct request *rq, + struct request *next) +{ + /* + * If next expires before rq, assign its expire time to rq + * and move into next position (next will be deleted) in fifo. + */ + if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) { + if (time_before(rq_fifo_time(next), rq_fifo_time(rq))) { + list_move(&rq->queuelist, &next->queuelist); + rq_set_fifo_time(rq, rq_fifo_time(next)); + } + } + + /* Delete next request */ + rq_fifo_clear(next); +} + +static void +sio_add_request(struct request_queue *q, struct request *rq) +{ + struct sio_data *sd = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + + /* + * Add request to the proper fifo list and set its + * expire time. + */ + rq_set_fifo_time(rq, jiffies + sd->fifo_expire[sync]); + list_add_tail(&rq->queuelist, &sd->fifo_list[sync]); +} + +static int +sio_queue_empty(struct request_queue *q) +{ + struct sio_data *sd = q->elevator->elevator_data; + + /* Check if fifo lists are empty */ + return list_empty(&sd->fifo_list[SYNC]) && + list_empty(&sd->fifo_list[ASYNC]); +} + +static struct request * +sio_expired_request(struct sio_data *sd, int sync) +{ + struct request *rq; + + if (list_empty(&sd->fifo_list[sync])) + return NULL; + + /* Retrieve request */ + rq = rq_entry_fifo(sd->fifo_list[sync].next); + + /* Request has expired */ + if (time_after(jiffies, rq_fifo_time(rq))) + return rq; + + return NULL; +} + +static struct request * +sio_choose_expired_request(struct sio_data *sd) +{ + struct request *sync = sio_expired_request(sd, SYNC); + struct request *async = sio_expired_request(sd, ASYNC); + + /* + * Check expired requests. Asynchronous requests have + * priority over synchronous. + */ + if (sync && async) + return async; + if (sync) + return sync; + + return async; + +} + +static struct request * +sio_choose_request(struct sio_data *sd) +{ + /* + * Retrieve request from available fifo list. + * Synchronous requests have priority over asynchronous. + */ + if (!list_empty(&sd->fifo_list[SYNC])) + return rq_entry_fifo(sd->fifo_list[SYNC].next); + + if (!list_empty(&sd->fifo_list[ASYNC])) + return rq_entry_fifo(sd->fifo_list[ASYNC].next); + + return NULL; +} + +static inline void +sio_dispatch_request(struct sio_data *sd, struct request *rq) +{ + /* + * Remove the request from the fifo list + * and dispatch it. + */ + rq_fifo_clear(rq); + elv_dispatch_add_tail(rq->q, rq); + + sd->batched++; +} + +static int +sio_dispatch_requests(struct request_queue *q, int force) +{ + struct sio_data *sd = q->elevator->elevator_data; + struct request *rq = NULL; + + /* + * Retrieve any expired request after a batch of + * sequential requests. + */ + if (sd->batched > sd->fifo_batch) { + sd->batched = 0; + rq = sio_choose_expired_request(sd); + } + + /* Retrieve request */ + if (!rq) { + rq = sio_choose_request(sd); + if (!rq) + return 0; + } + + /* Dispatch request */ + sio_dispatch_request(sd, rq); + + return 1; +} + +static struct request * +sio_former_request(struct request_queue *q, struct request *rq) +{ + struct sio_data *sd = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + + if (rq->queuelist.prev == &sd->fifo_list[sync]) + return NULL; + + /* Return former request */ + return list_entry(rq->queuelist.prev, struct request, queuelist); +} + +static struct request * +sio_latter_request(struct request_queue *q, struct request *rq) +{ + struct sio_data *sd = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + + if (rq->queuelist.next == &sd->fifo_list[sync]) + return NULL; + + /* Return latter request */ + return list_entry(rq->queuelist.next, struct request, queuelist); +} + +static void * +sio_init_queue(struct request_queue *q) +{ + struct sio_data *sd; + + /* Allocate structure */ + sd = kmalloc_node(sizeof(*sd), GFP_KERNEL, q->node); + if (!sd) + return NULL; + + /* Initialize fifo lists */ + INIT_LIST_HEAD(&sd->fifo_list[SYNC]); + INIT_LIST_HEAD(&sd->fifo_list[ASYNC]); + + /* Initialize data */ + sd->batched = 0; + sd->fifo_expire[SYNC] = sync_expire; + sd->fifo_expire[ASYNC] = async_expire; + sd->fifo_batch = fifo_batch; + + return sd; +} + +static void +sio_exit_queue(struct elevator_queue *e) +{ + struct sio_data *sd = e->elevator_data; + + BUG_ON(!list_empty(&sd->fifo_list[SYNC])); + BUG_ON(!list_empty(&sd->fifo_list[ASYNC])); + + /* Free structure */ + kfree(sd); +} + +/* + * sysfs code + */ + +static ssize_t +sio_var_show(int var, char *page) +{ + return sprintf(page, "%d\n", var); +} + +static ssize_t +sio_var_store(int *var, const char *page, size_t count) +{ + char *p = (char *) page; + + *var = simple_strtol(p, &p, 10); + return count; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ +{ \ + struct sio_data *sd = e->elevator_data; \ + int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return sio_var_show(__data, (page)); \ +} +SHOW_FUNCTION(sio_sync_expire_show, sd->fifo_expire[SYNC], 1); +SHOW_FUNCTION(sio_async_expire_show, sd->fifo_expire[ASYNC], 1); +SHOW_FUNCTION(sio_fifo_batch_show, sd->fifo_batch, 0); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \ +{ \ + struct sio_data *sd = e->elevator_data; \ + int __data; \ + int ret = sio_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ + return ret; \ +} +STORE_FUNCTION(sio_sync_expire_store, &sd->fifo_expire[SYNC], 0, INT_MAX, 1); +STORE_FUNCTION(sio_async_expire_store, &sd->fifo_expire[ASYNC], 0, INT_MAX, 1); +STORE_FUNCTION(sio_fifo_batch_store, &sd->fifo_batch, 0, INT_MAX, 0); +#undef STORE_FUNCTION + +#define DD_ATTR(name) \ + __ATTR(name, S_IRUGO|S_IWUSR, sio_##name##_show, \ + sio_##name##_store) + +static struct elv_fs_entry sio_attrs[] = { + DD_ATTR(sync_expire), + DD_ATTR(async_expire), + DD_ATTR(fifo_batch), + __ATTR_NULL +}; + +static struct elevator_type iosched_sio = { + .ops = { + .elevator_merge_req_fn = sio_merged_requests, + .elevator_dispatch_fn = sio_dispatch_requests, + .elevator_add_req_fn = sio_add_request, + .elevator_queue_empty_fn = sio_queue_empty, + .elevator_former_req_fn = sio_former_request, + .elevator_latter_req_fn = sio_latter_request, + .elevator_init_fn = sio_init_queue, + .elevator_exit_fn = sio_exit_queue, + }, + + .elevator_attrs = sio_attrs, + .elevator_name = "sio", + .elevator_owner = THIS_MODULE, +}; + +static int __init sio_init(void) +{ + /* Register elevator */ + elv_register(&iosched_sio); + + return 0; +} + +static void __exit sio_exit(void) +{ + /* Unregister elevator */ + elv_unregister(&iosched_sio); +} + +module_init(sio_init); +module_exit(sio_exit); + +MODULE_AUTHOR("Miguel Boton"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simple IO scheduler");