patch-2.4.0-test2 linux/drivers/block/elevator.c
Next file: linux/drivers/block/floppy.c
Previous file: linux/drivers/block/cpqarray.c
Back to the patch index
Back to the overall index
- Lines: 318
- Date:
Fri Jun 23 19:47:06 2000
- Orig file:
v2.4.0-test1/linux/drivers/block/elevator.c
- Orig date:
Tue May 23 15:31:34 2000
diff -u --recursive --new-file v2.4.0-test1/linux/drivers/block/elevator.c linux/drivers/block/elevator.c
@@ -4,6 +4,16 @@
* Block device elevator/IO-scheduler.
*
* Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
+ *
+ * 30042000 Jens Axboe <axboe@suse.de> :
+ *
+ * Split the elevator a bit so that it is possible to choose a different
+ * one or even write a new "plug in". There are three pieces:
+ * - elevator_fn, inserts a new request in the queue list
+ * - elevator_merge_fn, decides whether a new buffer can be merged with
+ * an existing request
+ * - elevator_dequeue_fn, called when a request is taken off the active list
+ *
*/
#include <linux/fs.h>
@@ -12,9 +22,9 @@
#include <linux/blk.h>
#include <asm/uaccess.h>
-static void elevator_default(struct request * req, elevator_t * elevator,
- struct list_head * real_head,
- struct list_head * head, int orig_latency)
+void elevator_default(struct request *req, elevator_t * elevator,
+ struct list_head * real_head,
+ struct list_head * head, int orig_latency)
{
struct list_head * entry = real_head, * point = NULL;
struct request * tmp;
@@ -22,6 +32,12 @@
int latency = orig_latency -= elevator->nr_segments, pass = 0;
int point_latency = 0xbeefbeef;
+ if (list_empty(real_head)) {
+ req->elevator_sequence = elevator_sequence(elevator, orig_latency);
+ list_add(&req->queue, real_head);
+ return;
+ }
+
while ((entry = entry->prev) != head) {
if (!point && latency >= 0) {
point = entry;
@@ -49,19 +65,189 @@
req->elevator_sequence = elevator_sequence(elevator, latency);
}
+int elevator_default_merge(request_queue_t *q, struct request **req,
+ struct buffer_head *bh, int rw,
+ int *max_sectors, int *max_segments)
+{
+ struct list_head *entry, *head = &q->queue_head;
+ unsigned int count = bh->b_size >> 9;
+ elevator_t *elevator = &q->elevator;
+ int orig_latency, latency, sequence, action, starving = 0;
+
+ /*
+ * Avoid write-bombs as not to hurt interactiveness of reads
+ */
+ if (rw == WRITE)
+ *max_segments = elevator->max_bomb_segments;
+
+ latency = orig_latency = elevator_request_latency(elevator, rw);
+ sequence = elevator->sequence;
+
+ entry = head;
+ if (q->head_active && !q->plugged)
+ head = head->next;
+
+ while ((entry = entry->prev) != head && !starving) {
+ *req = blkdev_entry_to_request(entry);
+ latency += (*req)->nr_segments;
+ if (elevator_sequence_before((*req)->elevator_sequence, sequence))
+ starving = 1;
+ if (latency < 0)
+ continue;
+ if ((*req)->sem)
+ continue;
+ if ((*req)->cmd != rw)
+ continue;
+ if ((*req)->nr_sectors + count > *max_sectors)
+ continue;
+ if ((*req)->rq_dev != bh->b_rdev)
+ continue;
+ if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) {
+ if (latency - (*req)->nr_segments < 0)
+ break;
+ action = ELEVATOR_BACK_MERGE;
+ } else if ((*req)->sector - count == bh->b_rsector) {
+ if (starving)
+ break;
+ action = ELEVATOR_FRONT_MERGE;
+ } else {
+ continue;
+ }
+ q->elevator.sequence++;
+ return action;
+ }
+ return ELEVATOR_NO_MERGE;
+}
+
+inline void elevator_default_dequeue(struct request *req)
+{
+ if (req->cmd == READ)
+ req->e->read_pendings--;
+
+ req->e->nr_segments -= req->nr_segments;
+}
+
+/*
+ * Order ascending, but only allow a request to be skipped a certain
+ * number of times
+ */
+void elevator_linus(struct request *req, elevator_t *elevator,
+ struct list_head *real_head,
+ struct list_head *head, int orig_latency)
+{
+ struct list_head *entry = real_head;
+ struct request *tmp;
+
+ if (list_empty(real_head)) {
+ list_add(&req->queue, real_head);
+ return;
+ }
+
+ while ((entry = entry->prev) != head) {
+ tmp = blkdev_entry_to_request(entry);
+ if (!tmp->elevator_sequence)
+ break;
+ if (IN_ORDER(tmp, req))
+ break;
+ tmp->elevator_sequence--;
+ }
+ list_add(&req->queue, entry);
+}
+
+int elevator_linus_merge(request_queue_t *q, struct request **req,
+ struct buffer_head *bh, int rw,
+ int *max_sectors, int *max_segments)
+{
+ struct list_head *entry, *head = &q->queue_head;
+ unsigned int count = bh->b_size >> 9;
+
+ entry = head;
+ if (q->head_active && !q->plugged)
+ head = head->next;
+
+ while ((entry = entry->prev) != head) {
+ *req = blkdev_entry_to_request(entry);
+ if (!(*req)->elevator_sequence)
+ break;
+ if ((*req)->sem)
+ continue;
+ if ((*req)->cmd != rw)
+ continue;
+ if ((*req)->nr_sectors + count > *max_sectors)
+ continue;
+ if ((*req)->rq_dev != bh->b_rdev)
+ continue;
+ if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector)
+ return ELEVATOR_BACK_MERGE;
+ if ((*req)->sector - count == bh->b_rsector)
+ return ELEVATOR_FRONT_MERGE;
+ (*req)->elevator_sequence--;
+ }
+ return ELEVATOR_NO_MERGE;
+}
+
+/*
+ * No request sorting, just add it to the back of the list
+ */
+void elevator_noop(struct request *req, elevator_t *elevator,
+ struct list_head *real_head, struct list_head *head,
+ int orig_latency)
+{
+ list_add_tail(&req->queue, real_head);
+}
+
+/*
+ * See if we can find a request that is buffer can be coalesced with.
+ */
+int elevator_noop_merge(request_queue_t *q, struct request **req,
+ struct buffer_head *bh, int rw,
+ int *max_sectors, int *max_segments)
+{
+ struct list_head *entry, *head = &q->queue_head;
+ unsigned int count = bh->b_size >> 9;
+
+ if (q->head_active && !q->plugged)
+ head = head->next;
+
+ entry = head;
+ while ((entry = entry->prev) != head) {
+ *req = blkdev_entry_to_request(entry);
+ if ((*req)->sem)
+ continue;
+ if ((*req)->cmd != rw)
+ continue;
+ if ((*req)->nr_sectors + count > *max_sectors)
+ continue;
+ if ((*req)->rq_dev != bh->b_rdev)
+ continue;
+ if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector)
+ return ELEVATOR_BACK_MERGE;
+ if ((*req)->sector - count == bh->b_rsector)
+ return ELEVATOR_FRONT_MERGE;
+ }
+ return ELEVATOR_NO_MERGE;
+}
+
+/*
+ * The noop "elevator" does not do any accounting
+ */
+void elevator_noop_dequeue(struct request *req) {}
+
#ifdef ELEVATOR_DEBUG
-void elevator_debug(request_queue_t * q, kdev_t dev)
+void elevator_default_debug(request_queue_t * q, kdev_t dev)
{
int read_pendings = 0, nr_segments = 0;
elevator_t * elevator = &q->elevator;
struct list_head * entry = &q->queue_head;
static int counter;
+ if (elevator->elevator_fn != elevator_default)
+ return;
+
if (counter++ % 100)
return;
- while ((entry = entry->prev) != &q->queue_head)
- {
+ while ((entry = entry->prev) != &q->queue_head) {
struct request * req;
req = blkdev_entry_to_request(entry);
@@ -81,16 +267,14 @@
nr_segments += req->nr_segments;
}
- if (read_pendings != elevator->read_pendings)
- {
+ if (read_pendings != elevator->read_pendings) {
printk(KERN_WARNING
"%s: elevator read_pendings %d should be %d\n",
kdevname(dev), elevator->read_pendings,
read_pendings);
elevator->read_pendings = read_pendings;
}
- if (nr_segments != elevator->nr_segments)
- {
+ if (nr_segments != elevator->nr_segments) {
printk(KERN_WARNING
"%s: elevator nr_segments %d should be %d\n",
kdevname(dev), elevator->nr_segments,
@@ -102,7 +286,6 @@
int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg)
{
- int ret;
blkelv_ioctl_arg_t output;
output.queue_ID = elevator->queue_ID;
@@ -110,44 +293,37 @@
output.write_latency = elevator->write_latency;
output.max_bomb_segments = elevator->max_bomb_segments;
- ret = -EFAULT;
if (copy_to_user(arg, &output, sizeof(blkelv_ioctl_arg_t)))
- goto out;
- ret = 0;
- out:
- return ret;
+ return -EFAULT;
+
+ return 0;
}
int blkelvset_ioctl(elevator_t * elevator, const blkelv_ioctl_arg_t * arg)
{
blkelv_ioctl_arg_t input;
- int ret;
- ret = -EFAULT;
if (copy_from_user(&input, arg, sizeof(blkelv_ioctl_arg_t)))
- goto out;
+ return -EFAULT;
- ret = -EINVAL;
if (input.read_latency < 0)
- goto out;
+ return -EINVAL;
if (input.write_latency < 0)
- goto out;
+ return -EINVAL;
if (input.max_bomb_segments <= 0)
- goto out;
+ return -EINVAL;
elevator->read_latency = input.read_latency;
elevator->write_latency = input.write_latency;
elevator->max_bomb_segments = input.max_bomb_segments;
- ret = 0;
- out:
- return ret;
+ return 0;
}
-void elevator_init(elevator_t * elevator)
+void elevator_init(elevator_t * elevator, elevator_t type)
{
static unsigned int queue_ID;
- *elevator = ELEVATOR_DEFAULTS;
+ *elevator = type;
elevator->queue_ID = queue_ID++;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)