Skip to contentSkip to conversation

simple deadline I/O scheduler

PATCH In review Jens Axboe 5 participants 11 messages Jan 4, 2002 – Mar 15, 2002 Block layer

lore
Share & tools
lore

Jens Axboe

author
#0 lore raw

PATCH 1/1 simple deadline I/O scheduler

Hi,

I've played around with implementing an I/O scheduler that _tries_ to start request within a given time limit. Note that it makes no guarentees of any sort, it's simply a "how does this work in real life" sort of thing. It's main use is actually to properly extend the i/o scheduler / elevator api to be able to implement more advanced schedulers (eg cello).

The construction of this new i/o scheduler is similar to how cello is build -- you have several "low level" schedulers and a class independent one on top of those that decides which one to start processing.

Each request is sorted into two lists -- one is purely sector sorted, the other is sorted [1] by expire time. We always serve request from the sector sorted list, until one of the front requests on the expire list has its deadline violated. Then we start following the sorted list from the point of the deadline violated request. This is always done in batches of eg 8 or 16 requests, to avoid seeking like mad if we can't keep up with the deadlines. It has three configurable parameters:

READ expire time
WRITE expire time
Request batch

The meaning should be clear from the above explanation, I hope :-). The only test I've done so far (besides verifying that it doesn't destroy data), is to start a dd bomb ala

dd if=/dev/zero of=somefile bs=64k

and see if I can get some work done while that is running. find or editor opens of files actually works to some degree with this i/o scheduler, at least better than with the current stock elevator.

So give it a whirl if you want -- i/o scheduler settings are set during kernel config, there you also have the opportunity to select which i/o scheduler should be the kernel default. I didn't bother with configure help entries yet, since I literally just tossed in the configure stuff at the last moment... A few clues -- elevator_linus is the one we currently have in the kernel, elevator_noop is really not recommend to be user settable (mainly a driver thing), elevator_jens is the one I've just described here.

Against 2.5.2-pre7

[1] not really sorted. currently it does not support priorities (at which point we'll probably want to change the expire sort to something faster than a dumb list sort), so it's just two lists (read and write) in fifo order.

drivers/block/Config.in renamed from opt/kernel/linux-2.5.2-pre7/drivers/block/Config.in +19 -0
diff -ur -X exclude /opt/kernel/linux-2.5.2-pre7/drivers/block/Config.in linux/drivers/block/Config.in
@@ -4,6 +4,25 @@
 mainmenu_option next_comment
 comment 'Block devices'
 
+mainmenu_option next_comment
+comment 'I/O schedulers'
+
+comment 'Linus I/O scheduler'
+int '   READ passover seed (kb)' CONFIG_BLK_IOSCHED_LINUS_RL 4096
+int '   WRITE passover seed (kb)' CONFIG_BLK_IOSCHED_LINUS_WL 8192
+
+comment 'Jens I/O scheduler'
+int '   READ expire time (seconds)' CONFIG_BLK_IOSCHED_JENS_RE 1
+int '   WRITE expire time (seconds)' CONFIG_BLK_IOSCHED_JENS_WE 5
+int '   Request batch' CONFIG_BLK_IOSCHED_JENS_RB 16
+
+choice 'default scheduler' \
+	"Linus-iosched			CONFIG_BLK_IOSCHED_DEF_L \
+	 Jens-iosched			CONFIG_BLK_IOSCHED_DEF_J \
+	 Noop-iosched			CONFIG_BLK_IOSCHED_DEF_N" Jens-iosched
+
+endmenu
+
 tristate 'Normal PC floppy disk support' CONFIG_BLK_DEV_FD
 if [ "$CONFIG_AMIGA" = "y" ]; then
    tristate 'Amiga floppy support' CONFIG_AMIGA_FLOPPY
drivers/block/elevator.c renamed from opt/kernel/linux-2.5.2-pre7/drivers/block/elevator.c +269 -4

310 lines not on this page · raw message

drivers/block/ll_rw_blk.c renamed from opt/kernel/linux-2.5.2-pre7/drivers/block/ll_rw_blk.c +15 -3
diff -ur -X exclude /opt/kernel/linux-2.5.2-pre7/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
@@ -798,12 +798,23 @@
  **/
 int blk_init_queue(request_queue_t *q, request_fn_proc *rfn, spinlock_t *lock)
 {
+	elevator_t e;
 	int ret;
 
 	if (blk_init_free_list(q))
 		return -ENOMEM;
 
-	if ((ret = elevator_init(q, &q->elevator, elevator_linus))) {
+#if defined(CONFIG_BLK_IOSCHED_DEF_L)
+	e = elevator_linus;
+#elif defined(CONFIG_BLK_IOSCHED_DEF_J)
+	e = elevator_jens;
+#elif #defined(CONFIG_BLK_IOSCHED_DEF_N)
+	e = elevator_noop;
+#else
+#error No I/O scheduler defined
+#endif
+
+	if ((ret = elevator_init(q, &q->elevator, e))) {
 		blk_cleanup_queue(q);
 		return ret;
 	}
@@ -818,7 +829,6 @@
 	q->plug_tq.data		= q;
 	q->queue_flags		= (1 << QUEUE_FLAG_CLUSTER);
 	q->queue_lock		= lock;
-	q->last_merge		= NULL;
 	
 	blk_queue_segment_boundary(q, 0xffffffff);
 
@@ -966,8 +976,10 @@
 	/*
 	 * it's a bug to let this rq preempt an already started request
 	 */
+#if 0
 	if (insert_here->next != &q->queue_head)
 		BUG_ON(list_entry_rq(insert_here->next)->flags & REQ_STARTED);
+#endif
 
 	/*
 	 * elevator indicated where it wants this request to be
@@ -1701,7 +1713,7 @@
 	 */
 	queue_nr_requests = 64;
 	if (total_ram > MB(32))
-		queue_nr_requests = 256;
+		queue_nr_requests = 512;
 
 	/*
 	 * Batch frees according to queue length
include/linux/elevator.h renamed from opt/kernel/linux-2.5.2-pre7/include/linux/elevator.h +43 -0

49 lines not on this page · raw message

Signature · 1 line
Jens Axboe
author
#1 lore raw
Quoted 1 line · Jens Axboe

Against 2.5.2-pre7

There was a stupid bug in the last version (last minute change...) which caused a non-expired write to preempt an expired read, not optimal :-). Here's a fixed version.

drivers/block/Config.in renamed from opt/kernel/linux-2.5.2-pre7/drivers/block/Config.in +19 -0
diff -ur -X exclude /opt/kernel/linux-2.5.2-pre7/drivers/block/Config.in linux/drivers/block/Config.in
@@ -4,6 +4,25 @@
 mainmenu_option next_comment
 comment 'Block devices'
 
+mainmenu_option next_comment
+comment 'I/O schedulers'
+
+comment 'Linus I/O scheduler'
+int '   READ passover seed (kb)' CONFIG_BLK_IOSCHED_LINUS_RL 4096
+int '   WRITE passover seed (kb)' CONFIG_BLK_IOSCHED_LINUS_WL 8192
+
+comment 'Jens I/O scheduler'
+int '   READ expire time (seconds)' CONFIG_BLK_IOSCHED_JENS_RE 1
+int '   WRITE expire time (seconds)' CONFIG_BLK_IOSCHED_JENS_WE 5
+int '   Request batch' CONFIG_BLK_IOSCHED_JENS_RB 16
+
+choice 'default scheduler' \
+	"Linus-iosched			CONFIG_BLK_IOSCHED_DEF_L \
+	 Jens-iosched			CONFIG_BLK_IOSCHED_DEF_J \
+	 Noop-iosched			CONFIG_BLK_IOSCHED_DEF_N" Jens-iosched
+
+endmenu
+
 tristate 'Normal PC floppy disk support' CONFIG_BLK_DEV_FD
 if [ "$CONFIG_AMIGA" = "y" ]; then
    tristate 'Amiga floppy support' CONFIG_AMIGA_FLOPPY
drivers/block/elevator.c renamed from opt/kernel/linux-2.5.2-pre7/drivers/block/elevator.c +271 -6

319 lines not on this page · raw message

drivers/block/ll_rw_blk.c renamed from opt/kernel/linux-2.5.2-pre7/drivers/block/ll_rw_blk.c +13 -9
diff -ur -X exclude /opt/kernel/linux-2.5.2-pre7/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
@@ -798,12 +798,23 @@
  **/
 int blk_init_queue(request_queue_t *q, request_fn_proc *rfn, spinlock_t *lock)
 {
+	elevator_t e;
 	int ret;
 
 	if (blk_init_free_list(q))
 		return -ENOMEM;
 
-	if ((ret = elevator_init(q, &q->elevator, elevator_linus))) {
+#if defined(CONFIG_BLK_IOSCHED_DEF_L)
+	e = elevator_linus;
+#elif defined(CONFIG_BLK_IOSCHED_DEF_J)
+	e = elevator_jens;
+#elif #defined(CONFIG_BLK_IOSCHED_DEF_N)
+	e = elevator_noop;
+#else
+#error No I/O scheduler defined
+#endif
+
+	if ((ret = elevator_init(q, &q->elevator, e))) {
 		blk_cleanup_queue(q);
 		return ret;
 	}
@@ -818,7 +829,6 @@
 	q->plug_tq.data		= q;
 	q->queue_flags		= (1 << QUEUE_FLAG_CLUSTER);
 	q->queue_lock		= lock;
-	q->last_merge		= NULL;
 	
 	blk_queue_segment_boundary(q, 0xffffffff);
 
@@ -964,12 +974,6 @@
 	drive_stat_acct(req, req->nr_sectors, 1);
 
 	/*
-	 * it's a bug to let this rq preempt an already started request
-	 */
-	if (insert_here->next != &q->queue_head)
-		BUG_ON(list_entry_rq(insert_here->next)->flags & REQ_STARTED);
-
-	/*
 	 * elevator indicated where it wants this request to be
 	 * inserted at elevator_merge time
 	 */
@@ -1701,7 +1705,7 @@
 	 */
 	queue_nr_requests = 64;
 	if (total_ram > MB(32))
-		queue_nr_requests = 256;
+		queue_nr_requests = 512;
 
 	/*
 	 * Batch frees according to queue length
include/linux/elevator.h renamed from opt/kernel/linux-2.5.2-pre7/include/linux/elevator.h +43 -0

49 lines not on this page · raw message

Signature · 1 line
Jens Axboe
author
#2 lore raw
In reply to Jens Axboe
Quoted 6 lines · Jens Axboe
Quoted 1 line · Jens Axboe

Against 2.5.2-pre7

There was a stupid bug in the last version (last minute change...) which caused a non-expired write to preempt an expired read, not optimal :-). Here's a fixed version.

Last version for today, honest :-). Changes:

- 'grab expired read of write' logic changed a bit
- changed expire time configuration to be in miliseconds
- removed annoying banner prints
- add better comments
- remember to reposition in fifo queue when merging two requests and expire time changes

drivers/block/Config.in renamed from opt/kernel/linux-2.5.2-pre7/drivers/block/Config.in +19 -0
diff -ur -X exclude /opt/kernel/linux-2.5.2-pre7/drivers/block/Config.in linux/drivers/block/Config.in
@@ -4,6 +4,25 @@
 mainmenu_option next_comment
 comment 'Block devices'
 
+mainmenu_option next_comment
+comment 'I/O schedulers'
+
+comment 'Linus I/O scheduler'
+int '   READ passover seed (kb)' CONFIG_BLK_IOSCHED_LINUS_RL 4096
+int '   WRITE passover seed (kb)' CONFIG_BLK_IOSCHED_LINUS_WL 8192
+
+comment 'Jens I/O scheduler'
+int '   READ expire time (ms)' CONFIG_BLK_IOSCHED_JENS_RE 1000
+int '   WRITE expire time (ms)' CONFIG_BLK_IOSCHED_JENS_WE 5000
+int '   Request batch' CONFIG_BLK_IOSCHED_JENS_RB 16
+
+choice 'default scheduler' \
+	"Linus-iosched			CONFIG_BLK_IOSCHED_DEF_L \
+	 Jens-iosched			CONFIG_BLK_IOSCHED_DEF_J \
+	 Noop-iosched			CONFIG_BLK_IOSCHED_DEF_N" Jens-iosched
+
+endmenu
+
 tristate 'Normal PC floppy disk support' CONFIG_BLK_DEV_FD
 if [ "$CONFIG_AMIGA" = "y" ]; then
    tristate 'Amiga floppy support' CONFIG_AMIGA_FLOPPY
drivers/block/elevator.c renamed from opt/kernel/linux-2.5.2-pre7/drivers/block/elevator.c +284 -6

331 lines not on this page · raw message

drivers/block/ll_rw_blk.c renamed from opt/kernel/linux-2.5.2-pre7/drivers/block/ll_rw_blk.c +13 -9
diff -ur -X exclude /opt/kernel/linux-2.5.2-pre7/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
@@ -798,12 +798,23 @@
  **/
 int blk_init_queue(request_queue_t *q, request_fn_proc *rfn, spinlock_t *lock)
 {
+	elevator_t e;
 	int ret;
 
 	if (blk_init_free_list(q))
 		return -ENOMEM;
 
-	if ((ret = elevator_init(q, &q->elevator, elevator_linus))) {
+#if defined(CONFIG_BLK_IOSCHED_DEF_L)
+	e = elevator_linus;
+#elif defined(CONFIG_BLK_IOSCHED_DEF_J)
+	e = elevator_jens;
+#elif #defined(CONFIG_BLK_IOSCHED_DEF_N)
+	e = elevator_noop;
+#else
+#error No I/O scheduler defined
+#endif
+
+	if ((ret = elevator_init(q, &q->elevator, e))) {
 		blk_cleanup_queue(q);
 		return ret;
 	}
@@ -818,7 +829,6 @@
 	q->plug_tq.data		= q;
 	q->queue_flags		= (1 << QUEUE_FLAG_CLUSTER);
 	q->queue_lock		= lock;
-	q->last_merge		= NULL;
 	
 	blk_queue_segment_boundary(q, 0xffffffff);
 
@@ -964,12 +974,6 @@
 	drive_stat_acct(req, req->nr_sectors, 1);
 
 	/*
-	 * it's a bug to let this rq preempt an already started request
-	 */
-	if (insert_here->next != &q->queue_head)
-		BUG_ON(list_entry_rq(insert_here->next)->flags & REQ_STARTED);
-
-	/*
 	 * elevator indicated where it wants this request to be
 	 * inserted at elevator_merge time
 	 */
@@ -1701,7 +1705,7 @@
 	 */
 	queue_nr_requests = 64;
 	if (total_ram > MB(32))
-		queue_nr_requests = 256;
+		queue_nr_requests = 512;
 
 	/*
 	 * Batch frees according to queue length
include/linux/elevator.h renamed from opt/kernel/linux-2.5.2-pre7/include/linux/elevator.h +43 -0

49 lines not on this page · raw message

Signature · 1 line
Jens Axboe
#3 lore raw
In reply to Jens Axboe

Hi!

Quoted 6 lines

I've played around with implementing an I/O scheduler that _tries_ to start request within a given time limit. Note that it makes no guarentees of any sort, it's simply a "how does this work in real life" sort of thing. It's main use is actually to properly extend the i/o scheduler / elevator api to be able to implement more advanced schedulers (eg cello).

Would it be possible to introduce concept of I/O priority? I.e. I want updatedb not to load disk if I need it for something else?

Signature · 2 lines
Philips Velo 1: 1"x4"x8", 300gram, 60, 12MB, 40bogomips, linux, mutt,
details at http://atrey.karlin.mff.cuni.cz/~pavel/velo/index.html.
#4 lore raw
Quoted 2 lines

Would it be possible to introduce concept of I/O priority? I.e. I want updatedb not to load disk if I need it for something else?

makes sense to me. actually, VM is another place where priority could be quite useful - for instance, how hard the VM scavenges a proc's pages. oops, there I go advocating a tunable...

VM_SWAP_ME_HARDER anyone?

#5 lore raw
Quoted 8 lines · Mark Hahn
Quoted 2 lines

Would it be possible to introduce concept of I/O priority? I.e. I want updatedb not to load disk if I need it for something else?

makes sense to me. actually, VM is another place where priority could be quite useful - for instance, how hard the VM scavenges a proc's pages. oops, there I go advocating a tunable...

VM_SWAP_ME_HARDER anyone?

This seems to work very badly, making one process swap more means it pagefaults more and sucks up more IO bandwidth ;)

regards,

Rik

Signature · 3 lines
Shortwave goes a long way:  irc.starchat.net  #swl

http://www.surriel.com/		http://distro.conectiva.com/
author
#6 lore raw
In reply to Jens Axboe
Quoted 11 lines · Pavel Machek

Hi!

Quoted 6 lines

I've played around with implementing an I/O scheduler that _tries_ to start request within a given time limit. Note that it makes no guarentees of any sort, it's simply a "how does this work in real life" sort of thing. It's main use is actually to properly extend the i/o scheduler / elevator api to be able to implement more advanced schedulers (eg cello).

Would it be possible to introduce concept of I/O priority? I.e. I want updatedb not to load disk if I need it for something else?

I've been toying with equal i/o distribution between the processes in the system, but it isn't done yet. I know Arjan is working on a priority scheduler, too. So something is bound to materialize sooner or later :)

Signature · 1 line
Jens Axboe
#7 lore raw
In reply to Jens Axboe
Quoted 11 lines · Jens Axboe

I've played around with implementing an I/O scheduler that _tries_ to start request within a given time limit. Note that it makes no guarentees of any sort, it's simply a "how does this work in real life" sort of thing. It's main use is actually to properly extend the i/o scheduler / elevator api to be able to implement more advanced schedulers (eg cello).

The construction of this new i/o scheduler is similar to how cello is build -- you have several "low level" schedulers and a class independent one on top of those that decides which one to start processing.

Quoted 7 lines

Each request is sorted into two lists -- one is purely sector sorted, the other is sorted [1] by expire time. We always serve request from the sector sorted list, until one of the front requests on the expire list has its deadline violated. Then we start following the sorted list from the point of the deadline violated request. This is always done in batches of eg 8 or 16 requests, to avoid seeking like mad if we can't keep up with the deadlines.

This post never seemed to get the attention it deserves. Do you have performance measurements now?

As part of my experimental hack to get rid of buffer_heads I was casting around for a structure to replace the dirty buffer list. I find myself heading towards the conclusion I want a structure that's remarkably similar to what you've cooked up here, but that lives at a higher level in the system. The idea is that a page goes into the queue as soon as it's dirtied and the elevator takes care of scheduling and merging from there. Admittedly these ideas are half-formed at the moment, but what I see developing is a situation where we have attempts at IO scheduling going on at two levels in the system, the VM and bio, and the more I think about it the less sense it makes. It's trying to be one subsystem.

Andrew Morton is also working in here, with a collection of ideas that I hope are complementary if looked at the right way. See his '[patch] delayed disk block allocation' post.

Signature · 1 line
Daniel
author
#8 lore raw
In reply to Jens Axboe
Quoted 20 lines · Daniel Phillips
Quoted 11 lines · Jens Axboe

I've played around with implementing an I/O scheduler that _tries_ to start request within a given time limit. Note that it makes no guarentees of any sort, it's simply a "how does this work in real life" sort of thing. It's main use is actually to properly extend the i/o scheduler / elevator api to be able to implement more advanced schedulers (eg cello).

The construction of this new i/o scheduler is similar to how cello is build -- you have several "low level" schedulers and a class independent one on top of those that decides which one to start processing.

Quoted 7 lines

Each request is sorted into two lists -- one is purely sector sorted, the other is sorted [1] by expire time. We always serve request from the sector sorted list, until one of the front requests on the expire list has its deadline violated. Then we start following the sorted list from the point of the deadline violated request. This is always done in batches of eg 8 or 16 requests, to avoid seeking like mad if we can't keep up with the deadlines.

Quoted 2 lines

This post never seemed to get the attention it deserves. Do you have performance measurements now?

I have some old ones that are not too interesting. Someone else expressed an interest in the deadline scheduler a few days back, so I resurrected it for 2.5.7-pre1 and started testing. Right now I'm just writing a few tools to properly stress it and get the performance measurements that I want. So I'll be back with something soonish, I hope.

Quoted 10 lines

As part of my experimental hack to get rid of buffer_heads I was casting around for a structure to replace the dirty buffer list. I find myself heading towards the conclusion I want a structure that's remarkably similar to what you've cooked up here, but that lives at a higher level in the system. The idea is that a page goes into the queue as soon as it's dirtied and the elevator takes care of scheduling and merging from there. Admittedly these ideas are half-formed at the moment, but what I see developing is a situation where we have attempts at IO scheduling going on at two levels in the system, the VM and bio, and the more I think about it the less sense it makes. It's trying to be one subsystem.

Very close to some ideas I have as well. My first plan was to do 'pull flushing' instead of the current (often horrible) pushes from bdflush etc. Having the vm give up the dirty pages as soon as possible would make it easier to do merging at the vm level as well.

Quoted 3 lines

Andrew Morton is also working in here, with a collection of ideas that I hope are complementary if looked at the right way. See his '[patch] delayed disk block allocation' post.

Right, I noticed. We could get good write clustering in general with vm merging, though.

Signature · 1 line
Jens Axboe
#9 lore raw
In reply to Jens Axboe
Quoted 7 lines · Jens Axboe
Quoted 3 lines · Daniel Phillips

Andrew Morton is also working in here, with a collection of ideas that I hope are complementary if looked at the right way. See his '[patch] delayed disk block allocation' post.

Right, I noticed. We could get good write clustering in general with vm merging, though.

Sorry, I can't parse that, do you mean 'if the elevator merging is merged with vm's' or 'if vm does merging' or... ?

Signature · 1 line
Daniel
author
#10 lore raw
In reply to Jens Axboe
Quoted 11 lines · Daniel Phillips
Quoted 7 lines · Jens Axboe
Quoted 3 lines · Daniel Phillips

Andrew Morton is also working in here, with a collection of ideas that I hope are complementary if looked at the right way. See his '[patch] delayed disk block allocation' post.

Right, I noticed. We could get good write clustering in general with vm merging, though.

Sorry, I can't parse that, do you mean 'if the elevator merging is merged with vm's' or 'if vm does merging' or... ?

Heh, guess it wasn't too clear. It was supposed to say 'if vm does the merging' or at least partially does it.

Signature · 1 line
Jens Axboe