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

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)