Add I/O Scheduler.

-Fifo
-Fiops
-Sio
-SioPlus
-Tripndroid
-VR
-Zen
This commit is contained in:
ananjaser1211 2018-02-27 18:09:03 +01:00 committed by BlackMesa123
parent 72772c6a4a
commit 8614176c18
9 changed files with 2803 additions and 0 deletions

View File

@ -39,6 +39,61 @@ config CFQ_GROUP_IOSCHED
---help---
Enable group IO scheduling in CFQ.
config IOSCHED_FIFO
bool
default n
---help---
Fifo is similar to no-op I/O scheduler and it's a minimal scheduler that does basic merging
and sorting. Ported from: https://github.com/AndroidDeveloperAlliance/ZenKernel_TUNA/blob/master/block/fifo-iosched.c
config IOSCHED_FIOPS
tristate "IOPS based I/O scheduler"
default n
---help---
This is an IOPS based I/O scheduler. It will try to distribute
IOPS equally among all processes in the system. It's mainly for
Flash based storage.
config IOSCHED_SIO
tristate "Simple I/O scheduler"
default n
---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).
config IOSCHED_SIOPLUS
tristate "Simple I/O scheduler plus"
default n
---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).
config IOSCHED_TRIPNDROID
tristate "Tripndroid"
default n
---help---
The Trip N Droid scheduler
config IOSCHED_VR
tristate "V(R) I/O scheduler"
default n
---help---
Requests are chosen according to SSTF with a penalty of rev_penalty
for switching head direction.
config IOSCHED_ZEN
tristate "Zen I/O scheduler"
default n
---help---
FCFS, dispatches are back-inserted, deadlines ensure fairness.
Should work best with devices where there is no travel delay.
choice
prompt "Default I/O scheduler"
default DEFAULT_CFQ
@ -52,9 +107,30 @@ choice
config DEFAULT_CFQ
bool "CFQ" if IOSCHED_CFQ=y
config DEFAULT_FIFO
bool "fifo" if IOSCHED_FIFO=y
config DEFAULT_FIOPS
bool "FIOPS" if IOSCHED_FIOPS=y
config DEFAULT_NOOP
bool "No-op"
config DEFAULT_SIO
bool "SIO" if IOSCHED_SIO=y
config DEFAULT_SIOPLUS
bool "SIOPLUS" if IOSCHED_SIOPLUS=y
config DEFAULT_TRIPNDROID
bool "TRIPNDROID" if IOSCHED_TRIPNDROID=y
config DEFAULT_VR
bool "V(R)" if IOSCHED_VR=y
config DEFAULT_ZEN
bool "ZEN" if IOSCHED_ZEN=y
endchoice
config DEFAULT_IOSCHED
@ -62,6 +138,13 @@ config DEFAULT_IOSCHED
default "deadline" if DEFAULT_DEADLINE
default "cfq" if DEFAULT_CFQ
default "noop" if DEFAULT_NOOP
default "fifo" if DEFAULT_FIFO
default "fiops" if DEFAULT_FIOPS
default "sio" if DEFAULT_SIO
default "sioplus" if DEFAULT_SIOPLUS
default "tripndroid" if DEFAULT_TRIPNDROID
default "vr" if DEFAULT_VR
default "zen" if DEFAULT_ZEN
endmenu

View File

@ -18,6 +18,13 @@ obj-$(CONFIG_BLK_DEV_THROTTLING) += blk-throttle.o
obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o
obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o
obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o
obj-$(CONFIG_IOSCHED_FIFO) += fifo-iosched.o
obj-$(CONFIG_IOSCHED_FIOPS) += fiops-iosched.o
obj-$(CONFIG_IOSCHED_SIO) += sio-iosched.o
obj-$(CONFIG_IOSCHED_SIOPLUS) += sioplus-iosched.o
obj-$(CONFIG_IOSCHED_TRIPNDROID)+= tripndroid-iosched.o
obj-$(CONFIG_IOSCHED_VR) += vr-iosched.o
obj-$(CONFIG_IOSCHED_ZEN) += zen-iosched.o
obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o
obj-$(CONFIG_BLK_CMDLINE_PARSER) += cmdline-parser.o

99
block/fifo-iosched.c Normal file
View File

@ -0,0 +1,99 @@
/*
* FIFO I/O scheduler (_really_ does no-op)
*/
#include <linux/blkdev.h>
#include <linux/elevator.h>
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
struct fifo_data {
struct list_head queue;
};
static int fifo_dispatch(struct request_queue *q, int force)
{
struct fifo_data *fifo_d = q->elevator->elevator_data;
if (!list_empty(&fifo_d->queue)) {
struct request *req = list_entry(fifo_d->queue.next, struct request, queuelist);
list_del_init(&req->queuelist);
elv_dispatch_add_tail(q, req);
return 1;
}
return 0;
}
static void fifo_add_request(struct request_queue *q, struct request *req)
{
struct fifo_data *fifo_d = q->elevator->elevator_data;
list_add_tail(&req->queuelist, &fifo_d->queue);
}
static int fifo_init_queue(struct request_queue *q, struct elevator_type *e)
{
struct fifo_data *fifo_d;
struct elevator_queue *eq;
eq = elevator_alloc(q, e);
if (!eq)
return -ENOMEM;
fifo_d = kmalloc_node(sizeof(*fifo_d), GFP_KERNEL, q->node);
if (!fifo_d) {
kobject_put(&eq->kobj);
return -ENOMEM;
}
eq->elevator_data = fifo_d;
INIT_LIST_HEAD(&fifo_d->queue);
spin_lock_irq(q->queue_lock);
q->elevator = eq;
spin_unlock_irq(q->queue_lock);
return 0;
}
static void fifo_exit_queue(struct elevator_queue *e)
{
struct fifo_data *fifo_d = e->elevator_data;
BUG_ON(!list_empty(&fifo_d->queue));
kfree(fifo_d);
}
static int fifo_deny_merge(struct request_queue *req_q, struct request *req,
struct bio *bio)
{
return ELEVATOR_NO_MERGE;
}
static struct elevator_type elevator_fifo = {
.ops = {
.elevator_dispatch_fn = fifo_dispatch,
.elevator_add_req_fn = fifo_add_request,
.elevator_allow_merge_fn = fifo_deny_merge,
.elevator_init_fn = fifo_init_queue,
.elevator_exit_fn = fifo_exit_queue,
},
.elevator_name = "fifo",
.elevator_owner = THIS_MODULE,
};
static int __init fifo_init(void)
{
return elv_register(&elevator_fifo);
}
static void __exit fifo_exit(void)
{
elv_unregister(&elevator_fifo);
}
module_init(fifo_init);
module_exit(fifo_exit);
MODULE_AUTHOR("Aaron Carroll");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("No-op IO scheduler that actually does nothing");

779
block/fiops-iosched.c Normal file
View File

@ -0,0 +1,779 @@
/*
* IOPS based IO scheduler. Based on CFQ.
* Copyright (C) 2003 Jens Axboe <axboe@kernel.dk>
* Shaohua Li <shli@kernel.org>
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <linux/elevator.h>
#include <linux/jiffies.h>
#include <linux/rbtree.h>
#include <linux/ioprio.h>
#include <linux/blktrace_api.h>
#include "blk.h"
#define VIOS_SCALE_SHIFT 10
#define VIOS_SCALE (1 << VIOS_SCALE_SHIFT)
#define VIOS_READ_SCALE (1)
#define VIOS_WRITE_SCALE (1)
#define VIOS_SYNC_SCALE (2)
#define VIOS_ASYNC_SCALE (5)
#define VIOS_PRIO_SCALE (5)
struct fiops_rb_root {
struct rb_root rb;
struct rb_node *left;
unsigned count;
u64 min_vios;
};
#define FIOPS_RB_ROOT (struct fiops_rb_root) { .rb = RB_ROOT}
enum wl_prio_t {
IDLE_WORKLOAD = 0,
BE_WORKLOAD = 1,
RT_WORKLOAD = 2,
FIOPS_PRIO_NR,
};
struct fiops_data {
struct request_queue *queue;
struct fiops_rb_root service_tree[FIOPS_PRIO_NR];
unsigned int busy_queues;
unsigned int in_flight[2];
struct work_struct unplug_work;
unsigned int read_scale;
unsigned int write_scale;
unsigned int sync_scale;
unsigned int async_scale;
};
struct fiops_ioc {
struct io_cq icq;
unsigned int flags;
struct fiops_data *fiopsd;
struct rb_node rb_node;
u64 vios; /* key in service_tree */
struct fiops_rb_root *service_tree;
unsigned int in_flight;
struct rb_root sort_list;
struct list_head fifo;
pid_t pid;
unsigned short ioprio;
enum wl_prio_t wl_type;
};
#define ioc_service_tree(ioc) (&((ioc)->fiopsd->service_tree[(ioc)->wl_type]))
#define RQ_CIC(rq) icq_to_cic((rq)->elv.icq)
enum ioc_state_flags {
FIOPS_IOC_FLAG_on_rr = 0, /* on round-robin busy list */
FIOPS_IOC_FLAG_prio_changed, /* task priority has changed */
};
#define FIOPS_IOC_FNS(name) \
static inline void fiops_mark_ioc_##name(struct fiops_ioc *ioc) \
{ \
ioc->flags |= (1 << FIOPS_IOC_FLAG_##name); \
} \
static inline void fiops_clear_ioc_##name(struct fiops_ioc *ioc) \
{ \
ioc->flags &= ~(1 << FIOPS_IOC_FLAG_##name); \
} \
static inline int fiops_ioc_##name(const struct fiops_ioc *ioc) \
{ \
return ((ioc)->flags & (1 << FIOPS_IOC_FLAG_##name)) != 0; \
}
FIOPS_IOC_FNS(on_rr);
FIOPS_IOC_FNS(prio_changed);
#undef FIOPS_IOC_FNS
#define fiops_log_ioc(fiopsd, ioc, fmt, args...) \
blk_add_trace_msg((fiopsd)->queue, "ioc%d " fmt, (ioc)->pid, ##args)
#define fiops_log(fiopsd, fmt, args...) \
blk_add_trace_msg((fiopsd)->queue, "fiops " fmt, ##args)
enum wl_prio_t fiops_wl_type(short prio_class)
{
if (prio_class == IOPRIO_CLASS_RT)
return RT_WORKLOAD;
if (prio_class == IOPRIO_CLASS_BE)
return BE_WORKLOAD;
return IDLE_WORKLOAD;
}
static inline struct fiops_ioc *icq_to_cic(struct io_cq *icq)
{
/* cic->icq is the first member, %NULL will convert to %NULL */
return container_of(icq, struct fiops_ioc, icq);
}
static inline struct fiops_ioc *fiops_cic_lookup(struct fiops_data *fiopsd,
struct io_context *ioc)
{
if (ioc)
return icq_to_cic(ioc_lookup_icq(ioc, fiopsd->queue));
return NULL;
}
/*
* The below is leftmost cache rbtree addon
*/
static struct fiops_ioc *fiops_rb_first(struct fiops_rb_root *root)
{
/* Service tree is empty */
if (!root->count)
return NULL;
if (!root->left)
root->left = rb_first(&root->rb);
if (root->left)
return rb_entry(root->left, struct fiops_ioc, rb_node);
return NULL;
}
static void rb_erase_init(struct rb_node *n, struct rb_root *root)
{
rb_erase(n, root);
RB_CLEAR_NODE(n);
}
static void fiops_rb_erase(struct rb_node *n, struct fiops_rb_root *root)
{
if (root->left == n)
root->left = NULL;
rb_erase_init(n, &root->rb);
--root->count;
}
static inline u64 max_vios(u64 min_vios, u64 vios)
{
s64 delta = (s64)(vios - min_vios);
if (delta > 0)
min_vios = vios;
return min_vios;
}
static void fiops_update_min_vios(struct fiops_rb_root *service_tree)
{
struct fiops_ioc *ioc;
ioc = fiops_rb_first(service_tree);
if (!ioc)
return;
service_tree->min_vios = max_vios(service_tree->min_vios, ioc->vios);
}
/*
* The fiopsd->service_trees holds all pending fiops_ioc's that have
* requests waiting to be processed. It is sorted in the order that
* we will service the queues.
*/
static void fiops_service_tree_add(struct fiops_data *fiopsd,
struct fiops_ioc *ioc)
{
struct rb_node **p, *parent;
struct fiops_ioc *__ioc;
struct fiops_rb_root *service_tree = ioc_service_tree(ioc);
u64 vios;
int left;
/* New added IOC */
if (RB_EMPTY_NODE(&ioc->rb_node)) {
if (ioc->in_flight > 0)
vios = ioc->vios;
else
vios = max_vios(service_tree->min_vios, ioc->vios);
} else {
vios = ioc->vios;
/* ioc->service_tree might not equal to service_tree */
fiops_rb_erase(&ioc->rb_node, ioc->service_tree);
ioc->service_tree = NULL;
}
fiops_log_ioc(fiopsd, ioc, "service tree add, vios %lld", vios);
left = 1;
parent = NULL;
ioc->service_tree = service_tree;
p = &service_tree->rb.rb_node;
while (*p) {
struct rb_node **n;
parent = *p;
__ioc = rb_entry(parent, struct fiops_ioc, rb_node);
/*
* sort by key, that represents service time.
*/
if (vios < __ioc->vios)
n = &(*p)->rb_left;
else {
n = &(*p)->rb_right;
left = 0;
}
p = n;
}
if (left)
service_tree->left = &ioc->rb_node;
ioc->vios = vios;
rb_link_node(&ioc->rb_node, parent, p);
rb_insert_color(&ioc->rb_node, &service_tree->rb);
service_tree->count++;
fiops_update_min_vios(service_tree);
}
/*
* Update ioc's position in the service tree.
*/
static void fiops_resort_rr_list(struct fiops_data *fiopsd,
struct fiops_ioc *ioc)
{
/*
* Resorting requires the ioc to be on the RR list already.
*/
if (fiops_ioc_on_rr(ioc))
fiops_service_tree_add(fiopsd, ioc);
}
/*
* add to busy list of queues for service, trying to be fair in ordering
* the pending list according to last request service
*/
static void fiops_add_ioc_rr(struct fiops_data *fiopsd, struct fiops_ioc *ioc)
{
BUG_ON(fiops_ioc_on_rr(ioc));
fiops_mark_ioc_on_rr(ioc);
fiopsd->busy_queues++;
fiops_resort_rr_list(fiopsd, ioc);
}
/*
* Called when the ioc no longer has requests pending, remove it from
* the service tree.
*/
static void fiops_del_ioc_rr(struct fiops_data *fiopsd, struct fiops_ioc *ioc)
{
BUG_ON(!fiops_ioc_on_rr(ioc));
fiops_clear_ioc_on_rr(ioc);
if (!RB_EMPTY_NODE(&ioc->rb_node)) {
fiops_rb_erase(&ioc->rb_node, ioc->service_tree);
ioc->service_tree = NULL;
}
BUG_ON(!fiopsd->busy_queues);
fiopsd->busy_queues--;
}
/*
* rb tree support functions
*/
static void fiops_del_rq_rb(struct request *rq)
{
struct fiops_ioc *ioc = RQ_CIC(rq);
elv_rb_del(&ioc->sort_list, rq);
}
static void fiops_add_rq_rb(struct request *rq)
{
struct fiops_ioc *ioc = RQ_CIC(rq);
struct fiops_data *fiopsd = ioc->fiopsd;
elv_rb_add(&ioc->sort_list, rq);
if (!fiops_ioc_on_rr(ioc))
fiops_add_ioc_rr(fiopsd, ioc);
}
static void fiops_reposition_rq_rb(struct fiops_ioc *ioc, struct request *rq)
{
elv_rb_del(&ioc->sort_list, rq);
fiops_add_rq_rb(rq);
}
static void fiops_remove_request(struct request *rq)
{
list_del_init(&rq->queuelist);
fiops_del_rq_rb(rq);
}
static u64 fiops_scaled_vios(struct fiops_data *fiopsd,
struct fiops_ioc *ioc, struct request *rq)
{
int vios = VIOS_SCALE;
if (rq_data_dir(rq) == WRITE)
vios = vios * fiopsd->write_scale / fiopsd->read_scale;
if (!rq_is_sync(rq))
vios = vios * fiopsd->async_scale / fiopsd->sync_scale;
vios += vios * (ioc->ioprio - IOPRIO_NORM) / VIOS_PRIO_SCALE;
return vios;
}
/* return vios dispatched */
static u64 fiops_dispatch_request(struct fiops_data *fiopsd,
struct fiops_ioc *ioc)
{
struct request *rq;
struct request_queue *q = fiopsd->queue;
rq = rq_entry_fifo(ioc->fifo.next);
fiops_remove_request(rq);
elv_dispatch_add_tail(q, rq);
fiopsd->in_flight[rq_is_sync(rq)]++;
ioc->in_flight++;
return fiops_scaled_vios(fiopsd, ioc, rq);
}
static int fiops_forced_dispatch(struct fiops_data *fiopsd)
{
struct fiops_ioc *ioc;
int dispatched = 0;
int i;
for (i = RT_WORKLOAD; i >= IDLE_WORKLOAD; i--) {
while (!RB_EMPTY_ROOT(&fiopsd->service_tree[i].rb)) {
ioc = fiops_rb_first(&fiopsd->service_tree[i]);
while (!list_empty(&ioc->fifo)) {
fiops_dispatch_request(fiopsd, ioc);
dispatched++;
}
if (fiops_ioc_on_rr(ioc))
fiops_del_ioc_rr(fiopsd, ioc);
}
}
return dispatched;
}
static struct fiops_ioc *fiops_select_ioc(struct fiops_data *fiopsd)
{
struct fiops_ioc *ioc;
struct fiops_rb_root *service_tree = NULL;
int i;
struct request *rq;
for (i = RT_WORKLOAD; i >= IDLE_WORKLOAD; i--) {
if (!RB_EMPTY_ROOT(&fiopsd->service_tree[i].rb)) {
service_tree = &fiopsd->service_tree[i];
break;
}
}
if (!service_tree)
return NULL;
ioc = fiops_rb_first(service_tree);
rq = rq_entry_fifo(ioc->fifo.next);
/*
* we are the only async task and sync requests are in flight, delay a
* moment. If there are other tasks coming, sync tasks have no chance
* to be starved, don't delay
*/
if (!rq_is_sync(rq) && fiopsd->in_flight[1] != 0 &&
service_tree->count == 1) {
fiops_log_ioc(fiopsd, ioc,
"postpone async, in_flight async %d sync %d",
fiopsd->in_flight[0], fiopsd->in_flight[1]);
return NULL;
}
/* Let sync request preempt async queue */
if (!rq_is_sync(rq) && service_tree->count > 1) {
struct rb_node *tmp = rb_next(&ioc->rb_node);
struct fiops_ioc *sync_ioc = NULL;
while (tmp) {
sync_ioc = rb_entry(tmp, struct fiops_ioc, rb_node);
rq = rq_entry_fifo(sync_ioc->fifo.next);
if (rq_is_sync(rq))
break;
tmp = rb_next(&sync_ioc->rb_node);
}
if (sync_ioc)
ioc = sync_ioc;
}
return ioc;
}
static void fiops_charge_vios(struct fiops_data *fiopsd,
struct fiops_ioc *ioc, u64 vios)
{
struct fiops_rb_root *service_tree = ioc->service_tree;
ioc->vios += vios;
fiops_log_ioc(fiopsd, ioc, "charge vios %lld, new vios %lld", vios, ioc->vios);
if (RB_EMPTY_ROOT(&ioc->sort_list))
fiops_del_ioc_rr(fiopsd, ioc);
else
fiops_resort_rr_list(fiopsd, ioc);
fiops_update_min_vios(service_tree);
}
static int fiops_dispatch_requests(struct request_queue *q, int force)
{
struct fiops_data *fiopsd = q->elevator->elevator_data;
struct fiops_ioc *ioc;
u64 vios;
if (unlikely(force))
return fiops_forced_dispatch(fiopsd);
ioc = fiops_select_ioc(fiopsd);
if (!ioc)
return 0;
vios = fiops_dispatch_request(fiopsd, ioc);
fiops_charge_vios(fiopsd, ioc, vios);
return 1;
}
static void fiops_init_prio_data(struct fiops_ioc *cic)
{
struct task_struct *tsk = current;
struct io_context *ioc = cic->icq.ioc;
int ioprio_class;
if (!fiops_ioc_prio_changed(cic))
return;
ioprio_class = IOPRIO_PRIO_CLASS(ioc->ioprio);
switch (ioprio_class) {
default:
printk(KERN_ERR "fiops: bad prio %x\n", ioprio_class);
case IOPRIO_CLASS_NONE:
/*
* no prio set, inherit CPU scheduling settings
*/
cic->ioprio = task_nice_ioprio(tsk);
cic->wl_type = fiops_wl_type(task_nice_ioclass(tsk));
break;
case IOPRIO_CLASS_RT:
cic->ioprio = IOPRIO_PRIO_DATA(ioc->ioprio);
cic->wl_type = fiops_wl_type(IOPRIO_CLASS_RT);
break;
case IOPRIO_CLASS_BE:
cic->ioprio = IOPRIO_PRIO_DATA(ioc->ioprio);
cic->wl_type = fiops_wl_type(IOPRIO_CLASS_BE);
break;
case IOPRIO_CLASS_IDLE:
cic->wl_type = fiops_wl_type(IOPRIO_CLASS_IDLE);
cic->ioprio = 7;
break;
}
fiops_clear_ioc_prio_changed(cic);
}
static void fiops_insert_request(struct request_queue *q, struct request *rq)
{
struct fiops_ioc *ioc = RQ_CIC(rq);
fiops_init_prio_data(ioc);
list_add_tail(&rq->queuelist, &ioc->fifo);
fiops_add_rq_rb(rq);
}
/*
* scheduler run of queue, if there are requests pending and no one in the
* driver that will restart queueing
*/
static inline void fiops_schedule_dispatch(struct fiops_data *fiopsd)
{
if (fiopsd->busy_queues)
kblockd_schedule_work(&fiopsd->unplug_work);
}
static void fiops_completed_request(struct request_queue *q, struct request *rq)
{
struct fiops_data *fiopsd = q->elevator->elevator_data;
struct fiops_ioc *ioc = RQ_CIC(rq);
fiopsd->in_flight[rq_is_sync(rq)]--;
ioc->in_flight--;
fiops_log_ioc(fiopsd, ioc, "in_flight %d, busy queues %d",
ioc->in_flight, fiopsd->busy_queues);
if (fiopsd->in_flight[0] + fiopsd->in_flight[1] == 0)
fiops_schedule_dispatch(fiopsd);
}
static struct request *
fiops_find_rq_fmerge(struct fiops_data *fiopsd, struct bio *bio)
{
struct task_struct *tsk = current;
struct fiops_ioc *cic;
cic = fiops_cic_lookup(fiopsd, tsk->io_context);
if (cic) {
sector_t sector = bio->bi_iter.bi_sector + bio_sectors(bio);
return elv_rb_find(&cic->sort_list, sector);
}
return NULL;
}
static int fiops_merge(struct request_queue *q, struct request **req,
struct bio *bio)
{
struct fiops_data *fiopsd = q->elevator->elevator_data;
struct request *__rq;
__rq = fiops_find_rq_fmerge(fiopsd, bio);
if (__rq && elv_rq_merge_ok(__rq, bio)) {
*req = __rq;
return ELEVATOR_FRONT_MERGE;
}
return ELEVATOR_NO_MERGE;
}
static void fiops_merged_request(struct request_queue *q, struct request *req,
int type)
{
if (type == ELEVATOR_FRONT_MERGE) {
struct fiops_ioc *ioc = RQ_CIC(req);
fiops_reposition_rq_rb(ioc, req);
}
}
static void
fiops_merged_requests(struct request_queue *q, struct request *rq,
struct request *next)
{
struct fiops_ioc *ioc = RQ_CIC(rq);
struct fiops_data *fiopsd = q->elevator->elevator_data;
fiops_remove_request(next);
ioc = RQ_CIC(next);
/*
* all requests of this task are merged to other tasks, delete it
* from the service tree.
*/
if (fiops_ioc_on_rr(ioc) && RB_EMPTY_ROOT(&ioc->sort_list))
fiops_del_ioc_rr(fiopsd, ioc);
}
static int fiops_allow_merge(struct request_queue *q, struct request *rq,
struct bio *bio)
{
struct fiops_data *fiopsd = q->elevator->elevator_data;
struct fiops_ioc *cic;
/*
* Lookup the ioc that this bio will be queued with. Allow
* merge only if rq is queued there.
*/
cic = fiops_cic_lookup(fiopsd, current->io_context);
return cic == RQ_CIC(rq);
}
static void fiops_exit_queue(struct elevator_queue *e)
{
struct fiops_data *fiopsd = e->elevator_data;
cancel_work_sync(&fiopsd->unplug_work);
kfree(fiopsd);
}
static void fiops_kick_queue(struct work_struct *work)
{
struct fiops_data *fiopsd =
container_of(work, struct fiops_data, unplug_work);
struct request_queue *q = fiopsd->queue;
spin_lock_irq(q->queue_lock);
__blk_run_queue(q);
spin_unlock_irq(q->queue_lock);
}
static int fiops_init_queue(struct request_queue *q, struct elevator_type *e)
{
struct fiops_data *fiopsd;
int i;
struct elevator_queue *eq;
eq = elevator_alloc(q, e);
if (!eq)
return -ENOMEM;
fiopsd = kzalloc_node(sizeof(*fiopsd), GFP_KERNEL, q->node);
if (!fiopsd) {
kobject_put(&eq->kobj);
return -ENOMEM;
}
eq->elevator_data = fiopsd;
fiopsd->queue = q;
spin_lock_irq(q->queue_lock);
q->elevator = eq;
spin_unlock_irq(q->queue_lock);
for (i = IDLE_WORKLOAD; i <= RT_WORKLOAD; i++)
fiopsd->service_tree[i] = FIOPS_RB_ROOT;
INIT_WORK(&fiopsd->unplug_work, fiops_kick_queue);
fiopsd->read_scale = VIOS_READ_SCALE;
fiopsd->write_scale = VIOS_WRITE_SCALE;
fiopsd->sync_scale = VIOS_SYNC_SCALE;
fiopsd->async_scale = VIOS_ASYNC_SCALE;
return 0;
}
static void fiops_init_icq(struct io_cq *icq)
{
struct fiops_data *fiopsd = icq->q->elevator->elevator_data;
struct fiops_ioc *ioc = icq_to_cic(icq);
RB_CLEAR_NODE(&ioc->rb_node);
INIT_LIST_HEAD(&ioc->fifo);
ioc->sort_list = RB_ROOT;
ioc->fiopsd = fiopsd;
ioc->pid = current->pid;
fiops_mark_ioc_prio_changed(ioc);
}
/*
* sysfs parts below -->
*/
static ssize_t
fiops_var_show(unsigned int var, char *page)
{
return sprintf(page, "%d\n", var);
}
static ssize_t
fiops_var_store(unsigned int *var, const char *page, size_t count)
{
char *p = (char *) page;
*var = simple_strtoul(p, &p, 10);
return count;
}
#define SHOW_FUNCTION(__FUNC, __VAR) \
static ssize_t __FUNC(struct elevator_queue *e, char *page) \
{ \
struct fiops_data *fiopsd = e->elevator_data; \
return fiops_var_show(__VAR, (page)); \
}
SHOW_FUNCTION(fiops_read_scale_show, fiopsd->read_scale);
SHOW_FUNCTION(fiops_write_scale_show, fiopsd->write_scale);
SHOW_FUNCTION(fiops_sync_scale_show, fiopsd->sync_scale);
SHOW_FUNCTION(fiops_async_scale_show, fiopsd->async_scale);
#undef SHOW_FUNCTION
#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \
static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \
{ \
struct fiops_data *fiopsd = e->elevator_data; \
unsigned int __data; \
int ret = fiops_var_store(&__data, (page), count); \
if (__data < (MIN)) \
__data = (MIN); \
else if (__data > (MAX)) \
__data = (MAX); \
*(__PTR) = __data; \
return ret; \
}
STORE_FUNCTION(fiops_read_scale_store, &fiopsd->read_scale, 1, 100);
STORE_FUNCTION(fiops_write_scale_store, &fiopsd->write_scale, 1, 100);
STORE_FUNCTION(fiops_sync_scale_store, &fiopsd->sync_scale, 1, 100);
STORE_FUNCTION(fiops_async_scale_store, &fiopsd->async_scale, 1, 100);
#undef STORE_FUNCTION
#define FIOPS_ATTR(name) \
__ATTR(name, S_IRUGO|S_IWUSR, fiops_##name##_show, fiops_##name##_store)
static struct elv_fs_entry fiops_attrs[] = {
FIOPS_ATTR(read_scale),
FIOPS_ATTR(write_scale),
FIOPS_ATTR(sync_scale),
FIOPS_ATTR(async_scale),
__ATTR_NULL
};
static struct elevator_type iosched_fiops = {
.ops = {
.elevator_merge_fn = fiops_merge,
.elevator_merged_fn = fiops_merged_request,
.elevator_merge_req_fn = fiops_merged_requests,
.elevator_allow_merge_fn = fiops_allow_merge,
.elevator_dispatch_fn = fiops_dispatch_requests,
.elevator_add_req_fn = fiops_insert_request,
.elevator_completed_req_fn = fiops_completed_request,
.elevator_former_req_fn = elv_rb_former_request,
.elevator_latter_req_fn = elv_rb_latter_request,
.elevator_init_icq_fn = fiops_init_icq,
.elevator_init_fn = fiops_init_queue,
.elevator_exit_fn = fiops_exit_queue,
},
.icq_size = sizeof(struct fiops_ioc),
.icq_align = __alignof__(struct fiops_ioc),
.elevator_attrs = fiops_attrs,
.elevator_name = "fiops",
.elevator_owner = THIS_MODULE,
};
static int __init fiops_init(void)
{
return elv_register(&iosched_fiops);
}
static void __exit fiops_exit(void)
{
elv_unregister(&iosched_fiops);
}
module_init(fiops_init);
module_exit(fiops_exit);
MODULE_AUTHOR("Jens Axboe, Shaohua Li <shli@kernel.org>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IOPS based IO scheduler");

400
block/sio-iosched.c Normal file
View File

@ -0,0 +1,400 @@
/*
* Simple IO scheduler
* Based on Noop, Deadline and V(R) IO schedulers.
*
* Copyright (C) 2012 Miguel Boton <mboton@gmail.com>
* Copyright (C) 2015 Brandon Berhent <bbedward@gmail.com>
*
*
* 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 <linux/blkdev.h>
#include <linux/elevator.h>
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
enum { ASYNC, SYNC };
/* Tunables */
static const int sync_read_expire = HZ / 4; /* max time before a sync read is submitted. */
static const int sync_write_expire = 2 * HZ; /* max time before a sync write is submitted. */
static const int async_read_expire = 4 * HZ; /* ditto for async, these limits are SOFT! */
static const int async_write_expire = 16 * HZ; /* ditto for async, these limits are SOFT! */
static const int writes_starved = 2; /* max times reads can starve a write */
static const int fifo_batch = 8; /* # 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][2];
/* Attributes */
unsigned int batched;
unsigned int starved;
/* Settings */
int fifo_expire[2][2];
int fifo_batch;
int writes_starved;
};
static inline struct sio_data *
sio_get_data(struct request_queue *q) {
return q->elevator->elevator_data;
}
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(next->fifo_time,rq->fifo_time)) {
list_move(&rq->queuelist, &next->queuelist);
rq->fifo_time = next->fifo_time;
}
}
/* Delete next request */
rq_fifo_clear(next);
}
static void
sio_add_request(struct request_queue *q, struct request *rq)
{
struct sio_data *sd = sio_get_data(q);
const int sync = rq_is_sync(rq);
const int data_dir = rq_data_dir(rq);
/*
* Add request to the proper fifo list and set its
* expire time.
*/
if (sd->fifo_expire[sync][data_dir]) {
rq->fifo_time = jiffies + sd->fifo_expire[sync][data_dir];
list_add_tail(&rq->queuelist, &sd->fifo_list[sync][data_dir]);
}
}
static struct request *
sio_expired_request(struct sio_data *sd, int sync, int data_dir)
{
struct list_head *list = &sd->fifo_list[sync][data_dir];
struct request *rq;
if (list_empty(list))
return NULL;
/* Retrieve request */
rq = rq_entry_fifo(list->next);
/* Request has expired */
if (time_after(jiffies, rq->fifo_time))
return rq;
return NULL;
}
static struct request *
sio_choose_expired_request(struct sio_data *sd)
{
struct request *rq;
/*
* Check expired requests.
* Asynchronous requests have priority over synchronous.
* Write requests have priority over read.
*/
rq = sio_expired_request(sd, ASYNC, WRITE);
if (rq)
return rq;
rq = sio_expired_request(sd, ASYNC, READ);
if (rq)
return rq;
rq = sio_expired_request(sd, SYNC, WRITE);
if (rq)
return rq;
rq = sio_expired_request(sd, SYNC, READ);
if (rq)
return rq;
return NULL;
}
static struct request *
sio_choose_request(struct sio_data *sd, int data_dir)
{
struct list_head *sync = sd->fifo_list[SYNC];
struct list_head *async = sd->fifo_list[ASYNC];
/*
* Retrieve request from available fifo list.
* Synchronous requests have priority over asynchronous.
* Read requests have priority over write.
*/
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(&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);
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++;
if (rq_data_dir(rq))
sd->starved = 0;
else
sd->starved++;
}
static int
sio_dispatch_requests(struct request_queue *q, int force)
{
struct sio_data *sd = sio_get_data(q);
struct request *rq = NULL;
int data_dir = READ;
/*
* 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) {
if (sd->starved > sd->writes_starved)
data_dir = WRITE;
rq = sio_choose_request(sd, data_dir);
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);
const int data_dir = rq_data_dir(rq);
if (rq->queuelist.prev == &sd->fifo_list[sync][data_dir])
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);
const int data_dir = rq_data_dir(rq);
if (rq->queuelist.next == &sd->fifo_list[sync][data_dir])
return NULL;
/* Return latter request */
return list_entry(rq->queuelist.next, struct request, queuelist);
}
static int sio_init_queue(struct request_queue *q, struct elevator_type *e)
{
struct sio_data *sd;
struct elevator_queue *eq;
eq = elevator_alloc(q, e);
if (!eq)
return -ENOMEM;
/* Allocate structure */
sd = kmalloc_node(sizeof(*sd), GFP_KERNEL, q->node);
if (!sd) {
kobject_put(&eq->kobj);
return -ENOMEM;
}
eq->elevator_data = sd;
/* Initialize fifo lists */
INIT_LIST_HEAD(&sd->fifo_list[SYNC][READ]);
INIT_LIST_HEAD(&sd->fifo_list[SYNC][WRITE]);
INIT_LIST_HEAD(&sd->fifo_list[ASYNC][READ]);
INIT_LIST_HEAD(&sd->fifo_list[ASYNC][WRITE]);
/* Initialize data */
sd->batched = 0;
sd->fifo_expire[SYNC][READ] = sync_read_expire;
sd->fifo_expire[SYNC][WRITE] = sync_write_expire;
sd->fifo_expire[ASYNC][READ] = async_read_expire;
sd->fifo_expire[ASYNC][WRITE] = async_write_expire;
sd->fifo_batch = fifo_batch;
spin_lock_irq(q->queue_lock);
q->elevator = eq;
spin_unlock_irq(q->queue_lock);
return 0;
}
static void
sio_exit_queue(struct elevator_queue *e)
{
struct sio_data *sd = e->elevator_data;
BUG_ON(!list_empty(&sd->fifo_list[SYNC][READ]));
BUG_ON(!list_empty(&sd->fifo_list[SYNC][WRITE]));
BUG_ON(!list_empty(&sd->fifo_list[ASYNC][READ]));
BUG_ON(!list_empty(&sd->fifo_list[ASYNC][WRITE]));
/* 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_read_expire_show, sd->fifo_expire[SYNC][READ], 1);
SHOW_FUNCTION(sio_sync_write_expire_show, sd->fifo_expire[SYNC][WRITE], 1);
SHOW_FUNCTION(sio_async_read_expire_show, sd->fifo_expire[ASYNC][READ], 1);
SHOW_FUNCTION(sio_async_write_expire_show, sd->fifo_expire[ASYNC][WRITE], 1);
SHOW_FUNCTION(sio_fifo_batch_show, sd->fifo_batch, 0);
SHOW_FUNCTION(sio_writes_starved_show, sd->writes_starved, 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_read_expire_store, &sd->fifo_expire[SYNC][READ], 0, INT_MAX, 1);
STORE_FUNCTION(sio_sync_write_expire_store, &sd->fifo_expire[SYNC][WRITE], 0, INT_MAX, 1);
STORE_FUNCTION(sio_async_read_expire_store, &sd->fifo_expire[ASYNC][READ], 0, INT_MAX, 1);
STORE_FUNCTION(sio_async_write_expire_store, &sd->fifo_expire[ASYNC][WRITE], 0, INT_MAX, 1);
STORE_FUNCTION(sio_fifo_batch_store, &sd->fifo_batch, 0, INT_MAX, 0);
STORE_FUNCTION(sio_writes_starved_store, &sd->writes_starved, 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_read_expire),
DD_ATTR(sync_write_expire),
DD_ATTR(async_read_expire),
DD_ATTR(async_write_expire),
DD_ATTR(fifo_batch),
DD_ATTR(writes_starved),
__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_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)
{
return elv_register(&iosched_sio);
}
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");
MODULE_VERSION("0.3");

419
block/sioplus-iosched.c Normal file
View File

@ -0,0 +1,419 @@
/*
* Simple IO scheduler plus
* Based on Noop, Deadline and V(R) IO schedulers.
*
* Copyright (C) 2012 Miguel Boton <mboton@gmail.com>
* (C) 2013, 2014 Boy Petersen <boypetersen@gmail.com>
*
* 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.
*
* The plus version incorporates several fixes and logic improvements.
*
*/
#include <linux/blkdev.h>
#include <linux/elevator.h>
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/slab.h>
enum { ASYNC, SYNC };
/* Tunables */
static const int sync_read_expire = (HZ / 4); /* max time before a sync read is submitted. */
static const int sync_write_expire = (HZ / 4) * 5; /* max time before a sync write is submitted. */
static const int async_read_expire = (HZ / 2); /* ditto for async, these limits are SOFT! */
static const int async_write_expire = (HZ * 2); /* 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 = 3; /* # 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][2];
/* Attributes */
unsigned int batched;
unsigned int starved;
/* Settings */
int fifo_expire[2][2];
int fifo_batch;
int writes_starved;
};
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(next->fifo_time, rq->fifo_time)) {
list_move(&rq->queuelist, &next->queuelist);
rq->fifo_time = next->fifo_time;
}
}
/* 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);
const int data_dir = rq_data_dir(rq);
/*
* Add request to the proper fifo list and set its
* expire time.
*/
rq->fifo_time = jiffies + sd->fifo_expire[sync][data_dir];
list_add_tail(&rq->queuelist, &sd->fifo_list[sync][data_dir]);
}
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38)
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][READ]) && list_empty(&sd->fifo_list[SYNC][WRITE]) &&
list_empty(&sd->fifo_list[ASYNC][READ]) && list_empty(&sd->fifo_list[ASYNC][WRITE]);
}
#endif
static struct request *
sio_expired_request(struct sio_data *sd, int sync, int data_dir)
{
struct list_head *list = &sd->fifo_list[sync][data_dir];
struct request *rq;
if (list_empty(list))
return NULL;
/* Retrieve request */
rq = rq_entry_fifo(list->next);
/* Request has expired */
if (time_after_eq(jiffies, rq->fifo_time))
return rq;
return NULL;
}
static struct request *
sio_choose_expired_request(struct sio_data *sd)
{
struct request *rq;
/* Reset (non-expired-)batch-counter */
sd->batched = 0;
/*
* Check expired requests.
* Asynchronous requests have priority over synchronous.
* Write requests have priority over read.
*/
rq = sio_expired_request(sd, ASYNC, WRITE);
if (rq)
return rq;
rq = sio_expired_request(sd, ASYNC, READ);
if (rq)
return rq;
rq = sio_expired_request(sd, SYNC, WRITE);
if (rq)
return rq;
rq = sio_expired_request(sd, SYNC, READ);
if (rq)
return rq;
return NULL;
}
static struct request *
sio_choose_request(struct sio_data *sd, int data_dir)
{
struct list_head *sync = sd->fifo_list[SYNC];
struct list_head *async = sd->fifo_list[ASYNC];
/* Increase (non-expired-)batch-counter */
sd->batched++;
/*
* Retrieve request from available fifo list.
* Synchronous requests have priority over asynchronous.
* Read requests have priority over write.
*/
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(&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);
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);
if (rq_data_dir(rq)) {
sd->starved = 0;
} else {
if (!list_empty(&sd->fifo_list[SYNC][WRITE]) ||
!list_empty(&sd->fifo_list[ASYNC][WRITE]))
sd->starved++;
}
}
static int
sio_dispatch_requests(struct request_queue *q, int force)
{
struct sio_data *sd = q->elevator->elevator_data;
struct request *rq = NULL;
int data_dir = READ;
/*
* Retrieve any expired request after a batch of
* sequential requests.
*/
if (sd->batched >= sd->fifo_batch)
rq = sio_choose_expired_request(sd);
/* Retrieve request */
if (!rq) {
if (sd->starved >= sd->writes_starved)
data_dir = WRITE;
rq = sio_choose_request(sd, data_dir);
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);
const int data_dir = rq_data_dir(rq);
if (rq->queuelist.prev == &sd->fifo_list[sync][data_dir])
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);
const int data_dir = rq_data_dir(rq);
if (rq->queuelist.next == &sd->fifo_list[sync][data_dir])
return NULL;
/* Return latter request */
return list_entry(rq->queuelist.next, struct request, queuelist);
}
static int sio_init_queue(struct request_queue *q, struct elevator_type *e)
{
struct sio_data *sd;
struct elevator_queue *eq;
eq = elevator_alloc(q, e);
if (eq == NULL)
return -ENOMEM;
/* Allocate structure */
sd = kmalloc_node(sizeof(*sd), GFP_KERNEL, q->node);
if (sd == NULL) {
kobject_put(&eq->kobj);
return -ENOMEM;
}
eq->elevator_data = sd;
spin_lock_irq(q->queue_lock);
q->elevator = eq;
spin_unlock_irq(q->queue_lock);
/* Initialize fifo lists */
INIT_LIST_HEAD(&sd->fifo_list[SYNC][READ]);
INIT_LIST_HEAD(&sd->fifo_list[SYNC][WRITE]);
INIT_LIST_HEAD(&sd->fifo_list[ASYNC][READ]);
INIT_LIST_HEAD(&sd->fifo_list[ASYNC][WRITE]);
/* Initialize data */
sd->batched = 0;
sd->fifo_expire[SYNC][READ] = sync_read_expire;
sd->fifo_expire[SYNC][WRITE] = sync_write_expire;
sd->fifo_expire[ASYNC][READ] = async_read_expire;
sd->fifo_expire[ASYNC][WRITE] = async_write_expire;
sd->fifo_batch = fifo_batch;
return 0;
}
static void
sio_exit_queue(struct elevator_queue *e)
{
struct sio_data *sd = e->elevator_data;
BUG_ON(!list_empty(&sd->fifo_list[SYNC][READ]));
BUG_ON(!list_empty(&sd->fifo_list[SYNC][WRITE]));
BUG_ON(!list_empty(&sd->fifo_list[ASYNC][READ]));
BUG_ON(!list_empty(&sd->fifo_list[ASYNC][WRITE]));
/* 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_read_expire_show, sd->fifo_expire[SYNC][READ], 1);
SHOW_FUNCTION(sio_sync_write_expire_show, sd->fifo_expire[SYNC][WRITE], 1);
SHOW_FUNCTION(sio_async_read_expire_show, sd->fifo_expire[ASYNC][READ], 1);
SHOW_FUNCTION(sio_async_write_expire_show, sd->fifo_expire[ASYNC][WRITE], 1);
SHOW_FUNCTION(sio_fifo_batch_show, sd->fifo_batch, 0);
SHOW_FUNCTION(sio_writes_starved_show, sd->writes_starved, 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_read_expire_store, &sd->fifo_expire[SYNC][READ], 0, INT_MAX, 1);
STORE_FUNCTION(sio_sync_write_expire_store, &sd->fifo_expire[SYNC][WRITE], 0, INT_MAX, 1);
STORE_FUNCTION(sio_async_read_expire_store, &sd->fifo_expire[ASYNC][READ], 0, INT_MAX, 1);
STORE_FUNCTION(sio_async_write_expire_store, &sd->fifo_expire[ASYNC][WRITE], 0, INT_MAX, 1);
STORE_FUNCTION(sio_fifo_batch_store, &sd->fifo_batch, 1, INT_MAX, 0);
STORE_FUNCTION(sio_writes_starved_store, &sd->writes_starved, 1, 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_read_expire),
DD_ATTR(sync_write_expire),
DD_ATTR(async_read_expire),
DD_ATTR(async_write_expire),
DD_ATTR(fifo_batch),
DD_ATTR(writes_starved),
__ATTR_NULL
};
static struct elevator_type iosched_sioplus = {
.ops = {
.elevator_merge_req_fn = sio_merged_requests,
.elevator_dispatch_fn = sio_dispatch_requests,
.elevator_add_req_fn = sio_add_request,
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38)
.elevator_queue_empty_fn = sio_queue_empty,
#endif
.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 = "sioplus",
.elevator_owner = THIS_MODULE,
};
static int __init sioplus_init(void)
{
/* Register elevator */
elv_register(&iosched_sioplus);
return 0;
}
static void __exit sioplus_exit(void)
{
/* Unregister elevator */
elv_unregister(&iosched_sioplus);
}
module_init(sioplus_init);
module_exit(sioplus_exit);
MODULE_AUTHOR("Miguel Boton");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple IO scheduler plus");

273
block/tripndroid-iosched.c Normal file
View File

@ -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 <linux/blkdev.h>
#include <linux/elevator.h>
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/version.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(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");

453
block/vr-iosched.c Normal file
View File

@ -0,0 +1,453 @@
/*
* V(R) I/O Scheduler
*
* Copyright (C) 2007 Aaron Carroll <aaronc@gelato.unsw.edu.au>
*
*
* The algorithm:
*
* The next request is decided based on its distance from the last
* request, with a multiplicative penalty of `rev_penalty' applied
* for reversing the head direction. A rev_penalty of 1 means SSTF
* behaviour. As this variable is increased, the algorithm approaches
* pure SCAN. Setting rev_penalty to 0 forces SCAN.
*
* Async and synch requests are not treated seperately. Instead we
* rely on deadlines to ensure fairness.
*
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/elevator.h>
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/compiler.h>
#include <linux/rbtree.h>
#include <linux/version.h>
#include <asm/div64.h>
enum vr_data_dir {
ASYNC,
SYNC,
};
enum vr_head_dir {
FORWARD,
BACKWARD,
};
static const int sync_expire = 1000; /* max time before a sync is submitted. */
static const int async_expire = 5000; /* ditto for async, these limits are SOFT! */
static const int fifo_batch = 1;
static const int rev_penalty = 1; /* penalty for reversing head direction */
struct vr_data {
struct rb_root sort_list;
struct list_head fifo_list[2];
struct request *next_rq;
struct request *prev_rq;
unsigned int nbatched;
sector_t last_sector; /* head position */
int head_dir;
/* tunables */
int fifo_expire[2];
int fifo_batch;
int rev_penalty;
};
static void vr_move_request(struct vr_data *, struct request *);
static inline struct vr_data *
vr_get_data(struct request_queue *q)
{
return q->elevator->elevator_data;
}
static void
vr_add_rq_rb(struct vr_data *vd, struct request *rq)
{
elv_rb_add(&vd->sort_list, rq);
if (blk_rq_pos(rq) >= vd->last_sector) {
if (!vd->next_rq || blk_rq_pos(vd->next_rq) > blk_rq_pos(rq))
vd->next_rq = rq;
} else {
if (!vd->prev_rq || blk_rq_pos(vd->prev_rq) < blk_rq_pos(rq))
vd->prev_rq = rq;
}
BUG_ON(vd->next_rq && vd->next_rq == vd->prev_rq);
BUG_ON(vd->next_rq && vd->prev_rq && blk_rq_pos(vd->next_rq) < blk_rq_pos(vd->prev_rq));
}
static void
vr_del_rq_rb(struct vr_data *vd, struct request *rq)
{
/*
* We might be deleting our cached next request.
* If so, find its sucessor.
*/
if (vd->next_rq == rq)
vd->next_rq = elv_rb_latter_request(NULL, rq);
else if (vd->prev_rq == rq)
vd->prev_rq = elv_rb_former_request(NULL, rq);
BUG_ON(vd->next_rq && vd->next_rq == vd->prev_rq);
BUG_ON(vd->next_rq && vd->prev_rq && blk_rq_pos(vd->next_rq) < blk_rq_pos(vd->prev_rq));
elv_rb_del(&vd->sort_list, rq);
}
/*
* add rq to rbtree and fifo
*/
static void
vr_add_request(struct request_queue *q, struct request *rq)
{
struct vr_data *vd = vr_get_data(q);
const int dir = rq_is_sync(rq);
vr_add_rq_rb(vd, rq);
if (vd->fifo_expire[dir]) {
rq->fifo_time = jiffies + vd->fifo_expire[dir];
list_add_tail(&rq->queuelist, &vd->fifo_list[dir]);
}
}
/*
* remove rq from rbtree and fifo.
*/
static void
vr_remove_request(struct request_queue *q, struct request *rq)
{
struct vr_data *vd = vr_get_data(q);
rq_fifo_clear(rq);
vr_del_rq_rb(vd, rq);
}
static int
vr_merge(struct request_queue *q, struct request **rqp, struct bio *bio)
{
sector_t sector = bio->bi_iter.bi_sector + bio_sectors(bio);
struct vr_data *vd = vr_get_data(q);
struct request *rq = elv_rb_find(&vd->sort_list, sector);
if (rq && elv_rq_merge_ok(rq, bio)) {
*rqp = rq;
return ELEVATOR_FRONT_MERGE;
}
return ELEVATOR_NO_MERGE;
}
static void
vr_merged_request(struct request_queue *q, struct request *req, int type)
{
struct vr_data *vd = vr_get_data(q);
/*
* if the merge was a front merge, we need to reposition request
*/
if (type == ELEVATOR_FRONT_MERGE) {
vr_del_rq_rb(vd, req);
vr_add_rq_rb(vd, req);
}
}
static void
vr_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;
}
}
vr_remove_request(q, next);
}
/*
* move an entry to dispatch queue
*/
static void
vr_move_request(struct vr_data *vd, struct request *rq)
{
struct request_queue *q = rq->q;
if (blk_rq_pos(rq) > vd->last_sector)
vd->head_dir = FORWARD;
else
vd->head_dir = BACKWARD;
vd->last_sector = blk_rq_pos(rq);
vd->next_rq = elv_rb_latter_request(NULL, rq);
vd->prev_rq = elv_rb_former_request(NULL, rq);
BUG_ON(vd->next_rq && vd->next_rq == vd->prev_rq);
vr_remove_request(q, rq);
elv_dispatch_add_tail(q, rq);
vd->nbatched++;
}
/*
* get the first expired request in direction ddir
*/
static struct request *
vr_expired_request(struct vr_data *vd, int ddir)
{
struct request *rq;
if (list_empty(&vd->fifo_list[ddir]))
return NULL;
rq = rq_entry_fifo(vd->fifo_list[ddir].next);
if (time_after(jiffies,rq->fifo_time))
return rq;
return NULL;
}
/*
* Returns the oldest expired request
*/
static struct request *
vr_check_fifo(struct vr_data *vd)
{
struct request *rq_sync = vr_expired_request(vd, SYNC);
struct request *rq_async = vr_expired_request(vd, ASYNC);
if (rq_async && rq_sync) {
if (time_after(rq_async->fifo_time, rq_sync->fifo_time))
return rq_sync;
} else if (rq_sync)
return rq_sync;
return rq_async;
}
/*
* Return the request with the lowest penalty
*/
static struct request *
vr_choose_request(struct vr_data *vd)
{
int penalty = (vd->rev_penalty) ? : INT_MAX;
struct request *next = vd->next_rq;
struct request *prev = vd->prev_rq;
sector_t next_pen, prev_pen;
BUG_ON(prev && prev == next);
if (!prev)
return next;
else if (!next)
return prev;
/* At this point both prev and next are defined and distinct */
next_pen = blk_rq_pos(next) - vd->last_sector;
prev_pen = vd->last_sector - blk_rq_pos(prev);
if (vd->head_dir == FORWARD)
next_pen = do_div(next_pen, penalty);
else
prev_pen = do_div(prev_pen, penalty);
if (next_pen <= prev_pen)
return next;
return prev;
}
static int
vr_dispatch_requests(struct request_queue *q, int force)
{
struct vr_data *vd = vr_get_data(q);
struct request *rq = NULL;
/* Check for and issue expired requests */
if (vd->nbatched > vd->fifo_batch) {
vd->nbatched = 0;
rq = vr_check_fifo(vd);
}
if (!rq) {
rq = vr_choose_request(vd);
if (!rq)
return 0;
}
vr_move_request(vd, rq);
return 1;
}
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38)
static int
vr_queue_empty(struct request_queue *q)
{
struct vr_data *vd = vr_get_data(q);
return RB_EMPTY_ROOT(&vd->sort_list);
}
#endif
static void
vr_exit_queue(struct elevator_queue *e)
{
struct vr_data *vd = e->elevator_data;
BUG_ON(!RB_EMPTY_ROOT(&vd->sort_list));
kfree(vd);
}
/*
* initialize elevator private data (vr_data).
*/
static int vr_init_queue(struct request_queue *q, struct elevator_type *e)
{
struct vr_data *vd;
struct elevator_queue *eq;
eq = elevator_alloc(q, e);
if (!eq)
return -ENOMEM;
vd = kmalloc_node(sizeof(*vd), GFP_KERNEL | __GFP_ZERO, q->node);
if (!vd) {
kobject_put(&eq->kobj);
return -ENOMEM;
}
eq->elevator_data = vd;
INIT_LIST_HEAD(&vd->fifo_list[SYNC]);
INIT_LIST_HEAD(&vd->fifo_list[ASYNC]);
vd->sort_list = RB_ROOT;
vd->fifo_expire[SYNC] = sync_expire;
vd->fifo_expire[ASYNC] = async_expire;
vd->fifo_batch = fifo_batch;
vd->rev_penalty = rev_penalty;
spin_lock_irq(q->queue_lock);
q->elevator = eq;
spin_unlock_irq(q->queue_lock);
return 0;
}
/*
* sysfs parts below
*/
static ssize_t vr_var_show(int var, char *page)
{
return sprintf(page, "%d\n", var);
}
static ssize_t
vr_var_store(int *var, const char *page, size_t count)
{
*var = simple_strtol(page, NULL, 10);
return count;
}
#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \
static ssize_t __FUNC(struct elevator_queue *e, char *page) \
{ \
struct vr_data *vd = e->elevator_data; \
int __data = __VAR; \
if (__CONV) \
__data = jiffies_to_msecs(__data); \
return vr_var_show(__data, (page)); \
}
SHOW_FUNCTION(vr_sync_expire_show, vd->fifo_expire[SYNC], 1);
SHOW_FUNCTION(vr_async_expire_show, vd->fifo_expire[ASYNC], 1);
SHOW_FUNCTION(vr_fifo_batch_show, vd->fifo_batch, 0);
SHOW_FUNCTION(vr_rev_penalty_show, vd->rev_penalty, 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 vr_data *vd = e->elevator_data; \
int __data; \
int ret = vr_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(vr_sync_expire_store, &vd->fifo_expire[SYNC], 0, INT_MAX, 1);
STORE_FUNCTION(vr_async_expire_store, &vd->fifo_expire[ASYNC], 0, INT_MAX, 1);
STORE_FUNCTION(vr_fifo_batch_store, &vd->fifo_batch, 0, INT_MAX, 0);
STORE_FUNCTION(vr_rev_penalty_store, &vd->rev_penalty, 0, INT_MAX, 0);
#undef STORE_FUNCTION
#define DD_ATTR(name) \
__ATTR(name, S_IRUGO|S_IWUSR, vr_##name##_show, \
vr_##name##_store)
static struct elv_fs_entry vr_attrs[] = {
DD_ATTR(sync_expire),
DD_ATTR(async_expire),
DD_ATTR(fifo_batch),
DD_ATTR(rev_penalty),
__ATTR_NULL
};
static struct elevator_type iosched_vr = {
.ops = {
.elevator_merge_fn = vr_merge,
.elevator_merged_fn = vr_merged_request,
.elevator_merge_req_fn = vr_merged_requests,
.elevator_dispatch_fn = vr_dispatch_requests,
.elevator_add_req_fn = vr_add_request,
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38)
.elevator_queue_empty_fn = vr_queue_empty,
#endif
.elevator_former_req_fn = elv_rb_former_request,
.elevator_latter_req_fn = elv_rb_latter_request,
.elevator_init_fn = vr_init_queue,
.elevator_exit_fn = vr_exit_queue,
},
.elevator_attrs = vr_attrs,
.elevator_name = "vr",
.elevator_owner = THIS_MODULE,
};
static int __init vr_init(void)
{
return elv_register(&iosched_vr);
}
static void __exit vr_exit(void)
{
elv_unregister(&iosched_vr);
}
module_init(vr_init);
module_exit(vr_exit);
MODULE_AUTHOR("Aaron Carroll");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("V(R) IO scheduler");

290
block/zen-iosched.c Normal file
View File

@ -0,0 +1,290 @@
/*
* Zen IO scheduler
* Primarily based on Noop, deadline, and SIO IO schedulers.
*
* Copyright (C) 2012 Brandon Berhent <bbedward@gmail.com>
* (C) 2014 LoungeKatt <twistedumbrella@gmail.com>
* (c) 2015 Fixes to stop crashing on 3.10 by Matthew Alex <matthewalex@outlook.com>
*
* FCFS, dispatches are back-inserted, deadlines ensure fairness.
* Should work best with devices where there is no travel delay.
*/
#include <linux/blkdev.h>
#include <linux/elevator.h>
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
enum zen_data_dir { ASYNC, SYNC };
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;
struct zen_data {
/* Runtime Data */
/* Requests are only present on fifo_list */
struct list_head fifo_list[2];
unsigned int batching; /* number of sequential requests made */
/* tunables */
int fifo_expire[2];
int fifo_batch;
};
static inline struct zen_data *
zen_get_data(struct request_queue *q) {
return q->elevator->elevator_data;
}
static void zen_dispatch(struct zen_data *, struct request *);
static void
zen_merged_requests(struct request_queue *q, struct request *req,
struct request *next)
{
/*
* if next expires before rq, assign its expire time to arq
* and move into next position (next will be deleted) in fifo
*/
if (!list_empty(&req->queuelist) && !list_empty(&next->queuelist)) {
if (time_before(next->fifo_time, req->fifo_time)) {
list_move(&req->queuelist, &next->queuelist);
req->fifo_time = next->fifo_time;
}
}
/* next request is gone */
rq_fifo_clear(next);
}
static void zen_add_request(struct request_queue *q, struct request *rq)
{
struct zen_data *zdata = zen_get_data(q);
const int sync = rq_is_sync(rq);
if (zdata->fifo_expire[sync]) {
rq->fifo_time = jiffies + zdata->fifo_expire[sync];
list_add_tail(&rq->queuelist, &zdata->fifo_list[sync]);
}
}
static void zen_dispatch(struct zen_data *zdata, struct request *rq)
{
/* Remove request from list and dispatch it */
rq_fifo_clear(rq);
elv_dispatch_add_tail(rq->q, rq);
/* Increment # of sequential requests */
zdata->batching++;
}
/*
* get the first expired request in direction ddir
*/
static struct request *
zen_expired_request(struct zen_data *zdata, int ddir)
{
struct request *rq;
if (list_empty(&zdata->fifo_list[ddir]))
return NULL;
rq = rq_entry_fifo(zdata->fifo_list[ddir].next);
if (time_after_eq(jiffies, rq->fifo_time))
return rq;
return NULL;
}
/*
* zen_check_fifo returns 0 if there are no expired requests on the fifo,
* otherwise it returns the next expired request
*/
static struct request *
zen_check_fifo(struct zen_data *zdata)
{
struct request *rq_sync = zen_expired_request(zdata, SYNC);
struct request *rq_async = zen_expired_request(zdata, ASYNC);
if (rq_async && rq_sync) {
if (time_after(rq_async->fifo_time, rq_sync->fifo_time))
return rq_sync;
} else if (rq_sync) {
return rq_sync;
} else if (rq_async) {
return rq_async;
}
return 0;
}
static struct request *
zen_choose_request(struct zen_data *zdata)
{
/*
* Retrieve request from available fifo list.
* Synchronous requests have priority over asynchronous.
*/
if (!list_empty(&zdata->fifo_list[SYNC]))
return rq_entry_fifo(zdata->fifo_list[SYNC].next);
if (!list_empty(&zdata->fifo_list[ASYNC]))
return rq_entry_fifo(zdata->fifo_list[ASYNC].next);
return NULL;
}
static int zen_dispatch_requests(struct request_queue *q, int force)
{
struct zen_data *zdata = zen_get_data(q);
struct request *rq = NULL;
/* Check for and issue expired requests */
if (zdata->batching > zdata->fifo_batch) {
zdata->batching = 0;
rq = zen_check_fifo(zdata);
}
if (!rq) {
rq = zen_choose_request(zdata);
if (!rq)
return 0;
}
zen_dispatch(zdata, rq);
return 1;
}
static int zen_init_queue(struct request_queue *q, struct elevator_type *e)
{
struct zen_data *zdata;
struct elevator_queue *eq;
eq = elevator_alloc(q, e);
if (!eq)
return -ENOMEM;
zdata = kmalloc_node(sizeof(*zdata), GFP_KERNEL, q->node);
if (!zdata) {
kobject_put(&eq->kobj);
return -ENOMEM;
}
eq->elevator_data = zdata;
spin_lock_irq(q->queue_lock);
q->elevator = eq;
spin_unlock_irq(q->queue_lock);
INIT_LIST_HEAD(&zdata->fifo_list[SYNC]);
INIT_LIST_HEAD(&zdata->fifo_list[ASYNC]);
zdata->fifo_expire[SYNC] = sync_expire;
zdata->fifo_expire[ASYNC] = async_expire;
zdata->fifo_batch = fifo_batch;
return 0;
}
static void zen_exit_queue(struct elevator_queue *e)
{
struct zen_data *zdata = e->elevator_data;
BUG_ON(!list_empty(&zdata->fifo_list[SYNC]));
BUG_ON(!list_empty(&zdata->fifo_list[ASYNC]));
kfree(zdata);
}
/* Sysfs */
static ssize_t
zen_var_show(int var, char *page)
{
return sprintf(page, "%d\n", var);
}
static ssize_t
zen_var_store(int *var, const char *page, size_t count)
{
*var = simple_strtol(page, NULL, 10);
return count;
}
#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \
static ssize_t __FUNC(struct elevator_queue *e, char *page) \
{ \
struct zen_data *zdata = e->elevator_data; \
int __data = __VAR; \
if (__CONV) \
__data = jiffies_to_msecs(__data); \
return zen_var_show(__data, (page)); \
}
SHOW_FUNCTION(zen_sync_expire_show, zdata->fifo_expire[SYNC], 1);
SHOW_FUNCTION(zen_async_expire_show, zdata->fifo_expire[ASYNC], 1);
SHOW_FUNCTION(zen_fifo_batch_show, zdata->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 zen_data *zdata = e->elevator_data; \
int __data; \
int ret = zen_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(zen_sync_expire_store, &zdata->fifo_expire[SYNC], 0, INT_MAX, 1);
STORE_FUNCTION(zen_async_expire_store, &zdata->fifo_expire[ASYNC], 0, INT_MAX, 1);
STORE_FUNCTION(zen_fifo_batch_store, &zdata->fifo_batch, 0, INT_MAX, 0);
#undef STORE_FUNCTION
#define DD_ATTR(name) \
__ATTR(name, S_IRUGO|S_IWUSR, zen_##name##_show, \
zen_##name##_store)
static struct elv_fs_entry zen_attrs[] = {
DD_ATTR(sync_expire),
DD_ATTR(async_expire),
DD_ATTR(fifo_batch),
__ATTR_NULL
};
static struct elevator_type iosched_zen = {
.ops = {
.elevator_merge_req_fn = zen_merged_requests,
.elevator_dispatch_fn = zen_dispatch_requests,
.elevator_add_req_fn = zen_add_request,
.elevator_former_req_fn = elv_rb_former_request,
.elevator_latter_req_fn = elv_rb_latter_request,
.elevator_init_fn = zen_init_queue,
.elevator_exit_fn = zen_exit_queue,
},
.elevator_attrs = zen_attrs,
.elevator_name = "zen",
.elevator_owner = THIS_MODULE,
};
static int __init zen_init(void)
{
return elv_register(&iosched_zen);
}
static void __exit zen_exit(void)
{
elv_unregister(&iosched_zen);
}
module_init(zen_init);
module_exit(zen_exit);
MODULE_AUTHOR("Brandon Berhent");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Zen IO scheduler");
MODULE_VERSION("1.1");