diff --git a/arch/arm64/configs/exynos7885-a7y18lte_defconfig b/arch/arm64/configs/exynos7885-a7y18lte_defconfig index b36db133b..dd0389099 100644 --- a/arch/arm64/configs/exynos7885-a7y18lte_defconfig +++ b/arch/arm64/configs/exynos7885-a7y18lte_defconfig @@ -312,6 +312,7 @@ CONFIG_IOSCHED_FIOPS=y CONFIG_IOSCHED_SIO=y CONFIG_IOSCHED_FIFO=y CONFIG_IOSCHED_SIOPLUS=y +CONFIG_IOSCHED_TRIPNDROID=y CONFIG_DEFAULT_CFQ=y # CONFIG_DEFAULT_BFQ is not set # CONFIG_DEFAULT_NOOP is not set @@ -319,6 +320,7 @@ CONFIG_DEFAULT_CFQ=y # CONFIG_DEFAULT_SIO is not set # CONFIG_DEFAULT_FIFO is not set # CONFIG_DEFAULT_SIOPLUS is not set +# CONFIG_DEFAULT_TRIPNDROID is not set CONFIG_DEFAULT_IOSCHED="cfq" CONFIG_ASN1=y CONFIG_UNINLINE_SPIN_UNLOCK=y diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched index 54e076ec1..aaf98e39a 100644 --- a/block/Kconfig.iosched +++ b/block/Kconfig.iosched @@ -93,6 +93,12 @@ config IOSCHED_SIOPLUS basic merging, trying to keep a minimum overhead. It is aimed mainly for aleatory access devices (eg: flash devices). +config IOSCHED_TRIPNDROID + tristate "Tripndroid" + default y + ---help--- + The Trip N Droid scheduler + choice prompt "Default I/O scheduler" default DEFAULT_CFQ @@ -131,6 +137,9 @@ choice config DEFAULT_SIOPLUS bool "SIOPLUS" if IOSCHED_SIOPLUS=y + config DEFAULT_TRIPNDROID + bool "TRIPNDROID" if IOSCHED_TRIPNDROID=y + endchoice config DEFAULT_IOSCHED @@ -143,6 +152,7 @@ config DEFAULT_IOSCHED default "sio" if DEFAULT_SIO default "fifo" if DEFAULT_FIFO default "sioplus" if DEFAULT_SIOPLUS + default "tripndroid" if DEFAULT_TRIPNDROID endmenu diff --git a/block/Makefile b/block/Makefile index 2115bdfa4..5daa20bd8 100644 --- a/block/Makefile +++ b/block/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_IOSCHED_FIOPS) += fiops-iosched.o obj-$(CONFIG_IOSCHED_SIO) += sio-iosched.o obj-$(CONFIG_IOSCHED_FIFO) += fifo-iosched.o obj-$(CONFIG_IOSCHED_SIOPLUS) += sioplus-iosched.o +obj-$(CONFIG_IOSCHED_TRIPNDROID)+= tripndroid-iosched.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o obj-$(CONFIG_BLK_CMDLINE_PARSER) += cmdline-parser.o diff --git a/block/tripndroid-iosched.c b/block/tripndroid-iosched.c new file mode 100755 index 000000000..89c4119e6 --- /dev/null +++ b/block/tripndroid-iosched.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2013, TripNDroid Mobile Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +enum { ASYNC, SYNC }; + +static const int sync_read_expire = 1 * HZ; /* max time before a sync read is submitted. */ +static const int sync_write_expire = 1 * HZ; /* max time before a sync write is submitted. */ +static const int async_read_expire = 2 * HZ; /* ditto for async, these limits are SOFT! */ +static const int async_write_expire = 2 * HZ; /* ditto for async, these limits are SOFT! */ + +static const int writes_starved = 1; /* max times reads can starve a write */ +static const int fifo_batch = 1; /* # of sequential requests treated as one + by the above parameters. For throughput. */ + +struct tripndroid_data { + + struct list_head fifo_list[2][2]; + + unsigned int batched; + unsigned int starved; + + int fifo_expire[2][2]; + int fifo_batch; + int writes_starved; +}; + +static void tripndroid_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(next->fifo_time, rq->fifo_time)) { + list_move(&rq->queuelist, &next->queuelist); + rq->fifo_time = next->fifo_time; + } + } + + rq_fifo_clear(next); +} + +static void tripndroid_add_request(struct request_queue *q, struct request *rq) +{ + struct tripndroid_data *td = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + const int data_dir = rq_data_dir(rq); + + rq->fifo_time = jiffies + td->fifo_expire[sync][data_dir]; + list_add(&rq->queuelist, &td->fifo_list[sync][data_dir]); +} + +static struct request *tripndroid_expired_request(struct tripndroid_data *td, int sync, int data_dir) +{ + struct list_head *list = &td->fifo_list[sync][data_dir]; + struct request *rq; + + if (list_empty(list)) + return NULL; + + rq = rq_entry_fifo(list->next); + + if (time_after(jiffies, rq->fifo_time)) + return rq; + + return NULL; +} + +static struct request *tripndroid_choose_expired_request(struct tripndroid_data *td) +{ + struct request *rq; + + /* Asynchronous requests have priority over synchronous. + * Write requests have priority over read. */ + + rq = tripndroid_expired_request(td, ASYNC, WRITE); + if (rq) + return rq; + rq = tripndroid_expired_request(td, ASYNC, READ); + if (rq) + return rq; + + rq = tripndroid_expired_request(td, SYNC, WRITE); + if (rq) + return rq; + rq = tripndroid_expired_request(td, SYNC, READ); + if (rq) + return rq; + + return NULL; +} + +static struct request *tripndroid_choose_request(struct tripndroid_data *td, int data_dir) +{ + struct list_head *sync = td->fifo_list[SYNC]; + struct list_head *async = td->fifo_list[ASYNC]; + + if (!list_empty(&sync[data_dir])) + return rq_entry_fifo(sync[data_dir].next); + if (!list_empty(&sync[!data_dir])) + return rq_entry_fifo(sync[!data_dir].next); + + if (!list_empty(&async[data_dir])) + return rq_entry_fifo(async[data_dir].next); + if (!list_empty(&async[!data_dir])) + return rq_entry_fifo(async[!data_dir].next); + + return NULL; +} + +static inline void tripndroid_dispatch_request(struct tripndroid_data *td, struct request *rq) +{ + /* Dispatch the request */ + rq_fifo_clear(rq); + elv_dispatch_add_tail(rq->q, rq); + + td->batched++; + + if (rq_data_dir(rq)) + td->starved = 0; + else + td->starved++; +} + +static int tripndroid_dispatch_requests(struct request_queue *q, int force) +{ + struct tripndroid_data *td = q->elevator->elevator_data; + struct request *rq = NULL; + int data_dir = READ; + + if (td->batched > td->fifo_batch) { + td->batched = 0; + rq = tripndroid_choose_expired_request(td); + } + + if (!rq) { + if (td->starved > td->writes_starved) + data_dir = WRITE; + + rq = tripndroid_choose_request(td, data_dir); + if (!rq) + return 0; + } + + tripndroid_dispatch_request(td, rq); + + return 1; +} + +static struct request *tripndroid_former_request(struct request_queue *q, struct request *rq) +{ + struct tripndroid_data *td = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + const int data_dir = rq_data_dir(rq); + + if (rq->queuelist.prev == &td->fifo_list[sync][data_dir]) + return NULL; + + return list_entry(rq->queuelist.prev, struct request, queuelist); +} + +static struct request *tripndroid_latter_request(struct request_queue *q, struct request *rq) +{ + struct tripndroid_data *td = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + const int data_dir = rq_data_dir(rq); + + if (rq->queuelist.next == &td->fifo_list[sync][data_dir]) + return NULL; + + return list_entry(rq->queuelist.next, struct request, queuelist); +} + +static int tripndroid_init_queue(struct request_queue *q, struct elevator_type *e) +{ + struct tripndroid_data *td; + struct elevator_queue *eq; + + eq = elevator_alloc(q, e); + if (!eq) + return -ENOMEM; + + td = kmalloc_node(sizeof(*td), GFP_KERNEL, q->node); + if (!td) { + kobject_put(&eq->kobj); + return -ENOMEM; + } + eq->elevator_data = td; + + INIT_LIST_HEAD(&td->fifo_list[SYNC][READ]); + INIT_LIST_HEAD(&td->fifo_list[SYNC][WRITE]); + INIT_LIST_HEAD(&td->fifo_list[ASYNC][READ]); + INIT_LIST_HEAD(&td->fifo_list[ASYNC][WRITE]); + + td->batched = 0; + td->fifo_expire[SYNC][READ] = sync_read_expire; + td->fifo_expire[SYNC][WRITE] = sync_write_expire; + td->fifo_expire[ASYNC][READ] = async_read_expire; + td->fifo_expire[ASYNC][WRITE] = async_write_expire; + td->fifo_batch = fifo_batch; + + spin_lock_irq(q->queue_lock); + q->elevator = eq; + spin_unlock_irq(q->queue_lock); + return 0; +} + +static void tripndroid_exit_queue(struct elevator_queue *e) +{ + struct tripndroid_data *td = e->elevator_data; + + BUG_ON(!list_empty(&td->fifo_list[SYNC][READ])); + BUG_ON(!list_empty(&td->fifo_list[SYNC][WRITE])); + BUG_ON(!list_empty(&td->fifo_list[ASYNC][READ])); + BUG_ON(!list_empty(&td->fifo_list[ASYNC][WRITE])); + + kfree(td); +} + +static struct elevator_type iosched_tripndroid = { + .ops = { + .elevator_merge_req_fn = tripndroid_merged_requests, + .elevator_dispatch_fn = tripndroid_dispatch_requests, + .elevator_add_req_fn = tripndroid_add_request, + .elevator_former_req_fn = tripndroid_former_request, + .elevator_latter_req_fn = tripndroid_latter_request, + .elevator_init_fn = tripndroid_init_queue, + .elevator_exit_fn = tripndroid_exit_queue, + }, + .elevator_name = "tripndroid", + .elevator_owner = THIS_MODULE, +}; + +static int __init tripndroid_init(void) +{ + elv_register(&iosched_tripndroid); + return 0; +} + +static void __exit tripndroid_exit(void) +{ + elv_unregister(&iosched_tripndroid); +} + +module_init(tripndroid_init); +module_exit(tripndroid_exit); + +MODULE_AUTHOR("TripNRaVeR"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TripNDroid IO Scheduler");