Skip to content

Commit

Permalink
block: add tripndroid io scheduler
Browse files Browse the repository at this point in the history
(cherry picked from commit 0291164c1b13787673d00bbf2e66f4d778d30932)
(cherry picked from commit c7bc87a15977bef2d945d4181317747b60ce84a4)
(cherry picked from commit 4a27dd2ac6ca739fd4d373ca371973e425c4fccb)
(cherry picked from commit 20a4eb37db54de7143befee716211a2ba5e15a1e)
(cherry picked from commit 5c7ab5bde0b10af4b867c0a709cca6b2a348bc90)
(cherry picked from commit c7ec9a513042ef8719a880bec0fb8d2a3bf901ba)
(cherry picked from commit fe6785721d94135b76e7328fa9c4ea6c7758ee23)
  • Loading branch information
TripNRaVeR authored and acuicultor committed Aug 30, 2021
1 parent c2ea8c6 commit 1f623e0
Show file tree
Hide file tree
Showing 3 changed files with 274 additions and 0 deletions.
12 changes: 12 additions & 0 deletions block/Kconfig.iosched
Expand Up @@ -26,6 +26,14 @@ config IOSCHED_MAPLE
tristate "Maple I/O scheduler"
default y

config IOSCHED_TRIPNDROID
tristate "TripNDroid IO Scheduler"
default n
---help---
The TripNDroid IO Scheduler is based on noop, deadline and vr
it uses deadlines to ensure some fairness. The scheduler is build
to keep a minimum overhead. It is mainly build for mobile devices.

config IOSCHED_CFQ
tristate "CFQ I/O scheduler"
default y
Expand Down Expand Up @@ -80,6 +88,9 @@ choice
config DEFAULT_DEADLINE
bool "Deadline" if IOSCHED_DEADLINE=y

config DEFAULT_TRIPNDROID
bool "TD" if IOSCHED_TRIPNDROID=y

config DEFAULT_CFQ
bool "CFQ" if IOSCHED_CFQ=y

Expand All @@ -103,6 +114,7 @@ endchoice
config DEFAULT_IOSCHED
string
default "deadline" if DEFAULT_DEADLINE
default "tripndroid" if DEFAULT_TRIPNDROID
default "cfq" if DEFAULT_CFQ
default "noop" if DEFAULT_NOOP
default "fiops" if DEFAULT_FIOPS
Expand Down
1 change: 1 addition & 0 deletions block/Makefile
Expand Up @@ -30,6 +30,7 @@ obj-$(CONFIG_IOSCHED_FIOPS) += fiops-iosched.o
obj-$(CONFIG_IOSCHED_SIO) += sio-iosched.o
obj-$(CONFIG_IOSCHED_ZEN) += zen-iosched.o
obj-$(CONFIG_IOSCHED_MAPLE) += maple-iosched.o
obj-$(CONFIG_IOSCHED_TRIPNDROID) += tripndroid-iosched.o

obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o
obj-$(CONFIG_BLK_CMDLINE_PARSER) += cmdline-parser.o
Expand Down
261 changes: 261 additions & 0 deletions block/tripndroid-iosched.c
@@ -0,0 +1,261 @@
/*
* 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 <linux/blkdev.h>
#include <linux/elevator.h>
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/init.h>

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(rq_fifo_time(next), rq_fifo_time(rq))) {
list_move(&rq->queuelist, &next->queuelist);
rq_set_fifo_time(rq, rq_fifo_time(next));
}
}

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_set_fifo_time(rq, 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_eq(jiffies, rq_fifo_time(rq)))
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 void *tripndroid_init_queue(struct request_queue *q)
{
struct tripndroid_data *td;

td = kmalloc_node(sizeof(*td), GFP_KERNEL, q->node);
if (!td)
return NULL;

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;

return td;
}

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");

0 comments on commit 1f623e0

Please sign in to comment.