Skip to contentSkip to conversation

Another proportional weight IO controller

RFC PATCH In review [email protected] 33 participants 279 messages Nov 6, 2008 – Nov 28, 2008 60 messages not in the archive Page 1 of 2

Subsystems (9)
lore
Share & tools
lore
#0 lore raw

cover Another proportional weight IO controller

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

Thanks
Vivek

#1 lore raw

PATCH 1/4 io controller: documentation

Documentation/controllers/io-controller.txt new file +172 -0
Index: linux17/Documentation/controllers/io-controller.txt
===================================================================
@@ -0,0 +1,172 @@
+				IO Controller
+				============
+
+Design
+=====
+This patchset implements a basic version of proportional weight IO controller.
+It is heavily derived from dm-ioband IO controller with one key difference
+and that is, there is no separate device mapper driver and there is no
+need to create a dm-ioband device on top of every block device which needs
+to do the IO control. In this implementation, all the control logic has
+been internalized and has been made per request queue. Enabling or disabling
+IO control on a block device is just a matter of writing a 0 or 1 in
+appropriate sysfs file.
+
+This is a proportional weight controller and that means various cgroups
+are assigned shares and tasks in those cgroups get to dispatch the bio
+in proportion to their cgroup share.
+
+All the contending cgroups are assigned tokens proportionate to their
+weights. One token is charged for one sector of IO. Once all the contending
+cgroups have consumed their tokens, fresh token allocation takes place and
+this is how disk bandwidth allocation proportion to weight is achieved.
+
+The bigger picture is that all the bios being submitted to a block device
+are first inspected by IO controller logic (bio_group_controller()), only if
+IO controller has been enabled on that device. The cgroup of the bio is
+determined and controller checks if this cgroup has sufficient tokens to
+dispatch the bio. If sufficient tokens are there, bio submitting thread
+continues to dispatch the bio through normal path otherwise IO controller
+buffers the bio and submitting thread returns back. These buffered bios
+are dispatched to lower layers later once the associate group (bio group)
+has sufficient tokens to dispatch the bios. This delayed dispatching is
+done with the help of a worker thread (biogroup).
+
+IO control can be enabled/disabled dynamically on any of the block device
+through sysfs file system. For example, to enable IO control on a device
+do following.
+
+echo 1 > /sys/block/sda/biogroup
+
+To disable IO control write 0.
+
+echo 0 > /sys/block/sda/biogroup
+
+This should be doable for any of the block device in the stack. Currently this
+patch places the hooks only for device mapper driver and still need to tweak
+md.
+
+For example, assume there are two cgroups A and B with weights 1024 and 2048
+in the system. Tasks in two cgroups A and B are doing IO to two disks sda and
+sdb in the system. A user has enabled IO control on both sda and sdb. Now on
+both sda and sdb, tasks in cgroup B will get to use 2/3 of disk BW and
+tasks in cgroup A will get to use 1/3 of disk bandwidth, only in case of
+contention. If tasks in any of the groups stop doing IO to a particular disk,
+task in other group will get to use full disk BW for that duration.
+
+
+HOWTO
+====
+- Enable cgroup, memory controller and block IO controller in kernel config
+  file.
+
+- Boot into the kernel and mount io controller.
+
+  mount -t cgroup -o bio none /cgroup/bio/
+
+- Create two cgroups test1 and test2
+
+  cd /cgroup/bio
+  mkdir test1 test2
+
+- Allocate weight 4096 to test1 and weight 2048 to test2
+
+  echo 4096 > /cgroup/bio/test1/bio.shares
+  echo 2048 > /cgroup/bio/test1/bio.shares
+
+- Launch "dd" operations in cgroup test1 and test2.
+
+  echo $$ > /cgroup/bio/test1/tasks
+  dd if=/somefile1 of=/dev/null
+  echo $$ > /cgroup/bio/test2/tasks
+  dd if=/somefile2 of=/dev/null
+
+Job in cgroup test1 should finish before job in cgroup test2. To verify
+that "dd" in cgroup test1 got to dispatch more bio as compared to "dd" in
+test2, look at "bio.aggregate_tokens" in both the cgroup (At same time). At
+any point of time when both the dd's are running, aggregate_tokens in cgroup
+test1 should be approximately double of aggregate_tokens in cgroup test2
+(Because weight of cgroup test1 is double of weight of cgroup test2).
+
+Some Tunables
+=============
+Some tunables appear in cgroup file system and in sysfs for respective
+device for debug and for configuration. Here is a brief description.
+
+Cgroup Files
+============
+bio.shares
+	Specifies the weight of the cgroup.
+
+bio.aggregate_tokens
+	Specifies total number of tokens dispatched by this cgroup. One token
+	represents one sector of IO.
+
+bio.jiffies
+	What was the jiffies values when last bio from this cgroup was released.
+
+bio.nr_token_slices
+	How many times this cgroup got the token allocation done from token
+	slice. We kind of create a token slice and every contending cgroup
+	gets the pie out of the slice based on the share.
+
+bio.nr_off_the_tree
+	How many times this bio group went off the list of contending groups.
+	We maintain an rb-tree of biogroups contending for IO and token
+	allocation takes place to these groups regularly. If some group stops
+	doing IO then it is considered to be idle and removed from the tree
+	and added back later when group has IO to perform. This file just
+	counts how many times this bio group went off the tree.
+
+Sysfs Tunabels
+==============
+/sys/block/{deice name}/biogroup
+	Whether IO controller (bio groups) are active on this device or not.
+
+/sys/block/{deice name}/deftoken
+	Default number of tokens which are given to a bio group upon start
+	if all the bio groups were of same weight. token slice is of dynamic
+	length. So if there are 3 cgroups contending and deftoken is 100 then
+	token slice lenght will be 100*3 = 300 and now out of this slice
+	three groups will get the tokens based on their weights.
+
+/sys/block/{deice name}/idletime
+	The time after which if a bio group does not generate the bio, it is
+	considered idle and removed from the rb-tree. Currently by default it
+	is 8ms.
+
+/sys/block/{deice name}/newslice_count
+	How many times new token allocation took place on this queue.
+
+TODO
+====
+- Do extensive testing in various scenarios and do performance optimization
+  and fix the things where broken.
+
+- IO schedulers derive context information from "current". This assumption
+  will be broken if bios are being submitted by a worker thread (biogroup).
+  Probably we need to put io context pointer in bio itself to get rid of
+  this dependency.
+
+- Allocating tokens for per sector of IO is crude approximation and will lead
+  to unfair bandwidth allocation in case task in cgroup is doing sequential IO
+  and task in other group is doing random IO. Rik Van Riel, suggested that
+  probably we should switch to time based scheme. Keep a track of average time
+  it takes to complete IO from a cgroup and do the allocation accordingly.
+
+- Currently this controller is dependent on memory controller being enabled.
+  Try to reduce this coupling.
+
+ISSUES
+======
+- IO controller can buffer the bios if suffcient tokens were not available
+  at the time of bio submission. Once the tokens are available, these bios
+  are dispatched to elevator/lower layers in first come first serve manner.
+  And this has potential to break CFQ where a RT tasks should be able to
+  dispatch the bio first or a high priority task should be able to release
+  more bio as compared to low priority task in same cgroup.
+
+  Not sure how to fix it. May be we need to maintain another rb-tree and
+  keep track of RT tasks and tasks priorities and dispatch accordingly. This
+  is equivalent of duplicating lots of CFQ logic and not sure how would it
+  impact AS behaviour.
#2 lore raw

PATCH 1/4 io controller: documentation

In reply to [email protected]
Documentation/controllers/io-controller.txt new file +172 -0
Index: linux17/Documentation/controllers/io-controller.txt
===================================================================
@@ -0,0 +1,172 @@
+				IO Controller
+				============
+
+Design
+=====
+This patchset implements a basic version of proportional weight IO controller.
+It is heavily derived from dm-ioband IO controller with one key difference
+and that is, there is no separate device mapper driver and there is no
+need to create a dm-ioband device on top of every block device which needs
+to do the IO control. In this implementation, all the control logic has
+been internalized and has been made per request queue. Enabling or disabling
+IO control on a block device is just a matter of writing a 0 or 1 in
+appropriate sysfs file.
+
+This is a proportional weight controller and that means various cgroups
+are assigned shares and tasks in those cgroups get to dispatch the bio
+in proportion to their cgroup share.
+
+All the contending cgroups are assigned tokens proportionate to their
+weights. One token is charged for one sector of IO. Once all the contending
+cgroups have consumed their tokens, fresh token allocation takes place and
+this is how disk bandwidth allocation proportion to weight is achieved.
+
+The bigger picture is that all the bios being submitted to a block device
+are first inspected by IO controller logic (bio_group_controller()), only if
+IO controller has been enabled on that device. The cgroup of the bio is
+determined and controller checks if this cgroup has sufficient tokens to
+dispatch the bio. If sufficient tokens are there, bio submitting thread
+continues to dispatch the bio through normal path otherwise IO controller
+buffers the bio and submitting thread returns back. These buffered bios
+are dispatched to lower layers later once the associate group (bio group)
+has sufficient tokens to dispatch the bios. This delayed dispatching is
+done with the help of a worker thread (biogroup).
+
+IO control can be enabled/disabled dynamically on any of the block device
+through sysfs file system. For example, to enable IO control on a device
+do following.
+
+echo 1 > /sys/block/sda/biogroup
+
+To disable IO control write 0.
+
+echo 0 > /sys/block/sda/biogroup
+
+This should be doable for any of the block device in the stack. Currently this
+patch places the hooks only for device mapper driver and still need to tweak
+md.
+
+For example, assume there are two cgroups A and B with weights 1024 and 2048
+in the system. Tasks in two cgroups A and B are doing IO to two disks sda and
+sdb in the system. A user has enabled IO control on both sda and sdb. Now on
+both sda and sdb, tasks in cgroup B will get to use 2/3 of disk BW and
+tasks in cgroup A will get to use 1/3 of disk bandwidth, only in case of
+contention. If tasks in any of the groups stop doing IO to a particular disk,
+task in other group will get to use full disk BW for that duration.
+
+
+HOWTO
+====
+- Enable cgroup, memory controller and block IO controller in kernel config
+  file.
+
+- Boot into the kernel and mount io controller.
+
+  mount -t cgroup -o bio none /cgroup/bio/
+
+- Create two cgroups test1 and test2
+
+  cd /cgroup/bio
+  mkdir test1 test2
+
+- Allocate weight 4096 to test1 and weight 2048 to test2
+
+  echo 4096 > /cgroup/bio/test1/bio.shares
+  echo 2048 > /cgroup/bio/test1/bio.shares
+
+- Launch "dd" operations in cgroup test1 and test2.
+
+  echo $$ > /cgroup/bio/test1/tasks
+  dd if=/somefile1 of=/dev/null
+  echo $$ > /cgroup/bio/test2/tasks
+  dd if=/somefile2 of=/dev/null
+
+Job in cgroup test1 should finish before job in cgroup test2. To verify
+that "dd" in cgroup test1 got to dispatch more bio as compared to "dd" in
+test2, look at "bio.aggregate_tokens" in both the cgroup (At same time). At
+any point of time when both the dd's are running, aggregate_tokens in cgroup
+test1 should be approximately double of aggregate_tokens in cgroup test2
+(Because weight of cgroup test1 is double of weight of cgroup test2).
+
+Some Tunables
+=============
+Some tunables appear in cgroup file system and in sysfs for respective
+device for debug and for configuration. Here is a brief description.
+
+Cgroup Files
+============
+bio.shares
+	Specifies the weight of the cgroup.
+
+bio.aggregate_tokens
+	Specifies total number of tokens dispatched by this cgroup. One token
+	represents one sector of IO.
+
+bio.jiffies
+	What was the jiffies values when last bio from this cgroup was released.
+
+bio.nr_token_slices
+	How many times this cgroup got the token allocation done from token
+	slice. We kind of create a token slice and every contending cgroup
+	gets the pie out of the slice based on the share.
+
+bio.nr_off_the_tree
+	How many times this bio group went off the list of contending groups.
+	We maintain an rb-tree of biogroups contending for IO and token
+	allocation takes place to these groups regularly. If some group stops
+	doing IO then it is considered to be idle and removed from the tree
+	and added back later when group has IO to perform. This file just
+	counts how many times this bio group went off the tree.
+
+Sysfs Tunabels
+==============
+/sys/block/{deice name}/biogroup
+	Whether IO controller (bio groups) are active on this device or not.
+
+/sys/block/{deice name}/deftoken
+	Default number of tokens which are given to a bio group upon start
+	if all the bio groups were of same weight. token slice is of dynamic
+	length. So if there are 3 cgroups contending and deftoken is 100 then
+	token slice lenght will be 100*3 = 300 and now out of this slice
+	three groups will get the tokens based on their weights.
+
+/sys/block/{deice name}/idletime
+	The time after which if a bio group does not generate the bio, it is
+	considered idle and removed from the rb-tree. Currently by default it
+	is 8ms.
+
+/sys/block/{deice name}/newslice_count
+	How many times new token allocation took place on this queue.
+
+TODO
+====
+- Do extensive testing in various scenarios and do performance optimization
+  and fix the things where broken.
+
+- IO schedulers derive context information from "current". This assumption
+  will be broken if bios are being submitted by a worker thread (biogroup).
+  Probably we need to put io context pointer in bio itself to get rid of
+  this dependency.
+
+- Allocating tokens for per sector of IO is crude approximation and will lead
+  to unfair bandwidth allocation in case task in cgroup is doing sequential IO
+  and task in other group is doing random IO. Rik Van Riel, suggested that
+  probably we should switch to time based scheme. Keep a track of average time
+  it takes to complete IO from a cgroup and do the allocation accordingly.
+
+- Currently this controller is dependent on memory controller being enabled.
+  Try to reduce this coupling.
+
+ISSUES
+======
+- IO controller can buffer the bios if suffcient tokens were not available
+  at the time of bio submission. Once the tokens are available, these bios
+  are dispatched to elevator/lower layers in first come first serve manner.
+  And this has potential to break CFQ where a RT tasks should be able to
+  dispatch the bio first or a high priority task should be able to release
+  more bio as compared to low priority task in same cgroup.
+
+  Not sure how to fix it. May be we need to maintain another rb-tree and
+  keep track of RT tasks and tasks priorities and dispatch accordingly. This
+  is equivalent of duplicating lots of CFQ logic and not sure how would it
+  impact AS behaviour.
#3 lore raw

PATCH 2/4 io controller: biocgroup implementation

In reply to [email protected]

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

include/linux/cgroup_subsys.h +6 -0
Index: linux17/include/linux/cgroup_subsys.h
===================================================================
@@ -43,6 +43,12 @@ SUBSYS(mem_cgroup)
 
 /* */
 
+#ifdef CONFIG_CGROUP_BIO
+SUBSYS(bio_cgroup)
+#endif
+
+/* */
+
 #ifdef CONFIG_CGROUP_DEVICE
 SUBSYS(devices)
 #endif
init/Kconfig +7 -0
Index: linux17/init/Kconfig
===================================================================
@@ -408,6 +408,13 @@ config CGROUP_MEM_RES_CTLR
 	  This config option also selects MM_OWNER config option, which
 	  could in turn add some fork/exit overhead.
 
+config CGROUP_BIO
+	bool "Block I/O cgroup subsystem"
+	depends on CGROUP_MEM_RES_CTLR
+	select MM_OWNER
+	help
+	  A generic proportinal weight IO controller.
+
 config SYSFS_DEPRECATED
 	bool
 
mm/biocontrol.c new file +409 -0

409 lines not on this page · raw message

include/linux/biocontrol.h new file +174 -0

174 lines not on this page · raw message

mm/Makefile +1 -0
Index: linux17/mm/Makefile
===================================================================
@@ -34,4 +34,5 @@ obj-$(CONFIG_MIGRATION) += migrate.o
 obj-$(CONFIG_SMP) += allocpercpu.o
 obj-$(CONFIG_QUICKLIST) += quicklist.o
 obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o
+obj-$(CONFIG_CGROUP_BIO) += biocontrol.o
 
mm/memcontrol.c +21 -48

171 lines not on this page · raw message

include/linux/memcontrol.h +46 -1

68 lines not on this page · raw message

#4 lore raw

PATCH 1/4 io controller: documentation

In reply to [email protected]
Documentation/controllers/io-controller.txt new file +172 -0
Index: linux17/Documentation/controllers/io-controller.txt
===================================================================
@@ -0,0 +1,172 @@
+				IO Controller
+				============
+
+Design
+=====
+This patchset implements a basic version of proportional weight IO controller.
+It is heavily derived from dm-ioband IO controller with one key difference
+and that is, there is no separate device mapper driver and there is no
+need to create a dm-ioband device on top of every block device which needs
+to do the IO control. In this implementation, all the control logic has
+been internalized and has been made per request queue. Enabling or disabling
+IO control on a block device is just a matter of writing a 0 or 1 in
+appropriate sysfs file.
+
+This is a proportional weight controller and that means various cgroups
+are assigned shares and tasks in those cgroups get to dispatch the bio
+in proportion to their cgroup share.
+
+All the contending cgroups are assigned tokens proportionate to their
+weights. One token is charged for one sector of IO. Once all the contending
+cgroups have consumed their tokens, fresh token allocation takes place and
+this is how disk bandwidth allocation proportion to weight is achieved.
+
+The bigger picture is that all the bios being submitted to a block device
+are first inspected by IO controller logic (bio_group_controller()), only if
+IO controller has been enabled on that device. The cgroup of the bio is
+determined and controller checks if this cgroup has sufficient tokens to
+dispatch the bio. If sufficient tokens are there, bio submitting thread
+continues to dispatch the bio through normal path otherwise IO controller
+buffers the bio and submitting thread returns back. These buffered bios
+are dispatched to lower layers later once the associate group (bio group)
+has sufficient tokens to dispatch the bios. This delayed dispatching is
+done with the help of a worker thread (biogroup).
+
+IO control can be enabled/disabled dynamically on any of the block device
+through sysfs file system. For example, to enable IO control on a device
+do following.
+
+echo 1 > /sys/block/sda/biogroup
+
+To disable IO control write 0.
+
+echo 0 > /sys/block/sda/biogroup
+
+This should be doable for any of the block device in the stack. Currently this
+patch places the hooks only for device mapper driver and still need to tweak
+md.
+
+For example, assume there are two cgroups A and B with weights 1024 and 2048
+in the system. Tasks in two cgroups A and B are doing IO to two disks sda and
+sdb in the system. A user has enabled IO control on both sda and sdb. Now on
+both sda and sdb, tasks in cgroup B will get to use 2/3 of disk BW and
+tasks in cgroup A will get to use 1/3 of disk bandwidth, only in case of
+contention. If tasks in any of the groups stop doing IO to a particular disk,
+task in other group will get to use full disk BW for that duration.
+
+
+HOWTO
+====
+- Enable cgroup, memory controller and block IO controller in kernel config
+  file.
+
+- Boot into the kernel and mount io controller.
+
+  mount -t cgroup -o bio none /cgroup/bio/
+
+- Create two cgroups test1 and test2
+
+  cd /cgroup/bio
+  mkdir test1 test2
+
+- Allocate weight 4096 to test1 and weight 2048 to test2
+
+  echo 4096 > /cgroup/bio/test1/bio.shares
+  echo 2048 > /cgroup/bio/test1/bio.shares
+
+- Launch "dd" operations in cgroup test1 and test2.
+
+  echo $$ > /cgroup/bio/test1/tasks
+  dd if=/somefile1 of=/dev/null
+  echo $$ > /cgroup/bio/test2/tasks
+  dd if=/somefile2 of=/dev/null
+
+Job in cgroup test1 should finish before job in cgroup test2. To verify
+that "dd" in cgroup test1 got to dispatch more bio as compared to "dd" in
+test2, look at "bio.aggregate_tokens" in both the cgroup (At same time). At
+any point of time when both the dd's are running, aggregate_tokens in cgroup
+test1 should be approximately double of aggregate_tokens in cgroup test2
+(Because weight of cgroup test1 is double of weight of cgroup test2).
+
+Some Tunables
+=============
+Some tunables appear in cgroup file system and in sysfs for respective
+device for debug and for configuration. Here is a brief description.
+
+Cgroup Files
+============
+bio.shares
+	Specifies the weight of the cgroup.
+
+bio.aggregate_tokens
+	Specifies total number of tokens dispatched by this cgroup. One token
+	represents one sector of IO.
+
+bio.jiffies
+	What was the jiffies values when last bio from this cgroup was released.
+
+bio.nr_token_slices
+	How many times this cgroup got the token allocation done from token
+	slice. We kind of create a token slice and every contending cgroup
+	gets the pie out of the slice based on the share.
+
+bio.nr_off_the_tree
+	How many times this bio group went off the list of contending groups.
+	We maintain an rb-tree of biogroups contending for IO and token
+	allocation takes place to these groups regularly. If some group stops
+	doing IO then it is considered to be idle and removed from the tree
+	and added back later when group has IO to perform. This file just
+	counts how many times this bio group went off the tree.
+
+Sysfs Tunabels
+==============
+/sys/block/{deice name}/biogroup
+	Whether IO controller (bio groups) are active on this device or not.
+
+/sys/block/{deice name}/deftoken
+	Default number of tokens which are given to a bio group upon start
+	if all the bio groups were of same weight. token slice is of dynamic
+	length. So if there are 3 cgroups contending and deftoken is 100 then
+	token slice lenght will be 100*3 = 300 and now out of this slice
+	three groups will get the tokens based on their weights.
+
+/sys/block/{deice name}/idletime
+	The time after which if a bio group does not generate the bio, it is
+	considered idle and removed from the rb-tree. Currently by default it
+	is 8ms.
+
+/sys/block/{deice name}/newslice_count
+	How many times new token allocation took place on this queue.
+
+TODO
+====
+- Do extensive testing in various scenarios and do performance optimization
+  and fix the things where broken.
+
+- IO schedulers derive context information from "current". This assumption
+  will be broken if bios are being submitted by a worker thread (biogroup).
+  Probably we need to put io context pointer in bio itself to get rid of
+  this dependency.
+
+- Allocating tokens for per sector of IO is crude approximation and will lead
+  to unfair bandwidth allocation in case task in cgroup is doing sequential IO
+  and task in other group is doing random IO. Rik Van Riel, suggested that
+  probably we should switch to time based scheme. Keep a track of average time
+  it takes to complete IO from a cgroup and do the allocation accordingly.
+
+- Currently this controller is dependent on memory controller being enabled.
+  Try to reduce this coupling.
+
+ISSUES
+======
+- IO controller can buffer the bios if suffcient tokens were not available
+  at the time of bio submission. Once the tokens are available, these bios
+  are dispatched to elevator/lower layers in first come first serve manner.
+  And this has potential to break CFQ where a RT tasks should be able to
+  dispatch the bio first or a high priority task should be able to release
+  more bio as compared to low priority task in same cgroup.
+
+  Not sure how to fix it. May be we need to maintain another rb-tree and
+  keep track of RT tasks and tasks priorities and dispatch accordingly. This
+  is equivalent of duplicating lots of CFQ logic and not sure how would it
+  impact AS behaviour.

A message that is not in the archive (sent off-list or not yet received): [email protected]

#6 lore raw

Re: [patch 1/4] io controller: documentation

Quoted 4 lines · On Thu, 06 Nov 2008 10:30:23 -0500 [email protected]…
Documentation/controllers/io-controller.txt new file +3 -0
+ISSUES
+======
+- IO controller can buffer the bios if suffcient tokens were not available

Documentation/controllers/io-controller.txt line 172

Quoted 10 lines
Documentation/controllers/io-controller.txt new file +10 -0
+  at the time of bio submission. Once the tokens are available, these bios
+  are dispatched to elevator/lower layers in first come first serve manner.
+  And this has potential to break CFQ where a RT tasks should be able to
+  dispatch the bio first or a high priority task should be able to release
+  more bio as compared to low priority task in same cgroup.
+
+  Not sure how to fix it. May be we need to maintain another rb-tree and
+  keep track of RT tasks and tasks priorities and dispatch accordingly. This
+  is equivalent of duplicating lots of CFQ logic and not sure how would it
+  impact AS behaviour.

Why you don't isolate RT tasks into other cgroup ?
/cgroup/bio-cgroup/group_for_usual/...usual tasks.
/group_for_RT/ ...RT tasks. you can use high-speed path.

How about adding RT flag to bio-cgroup and skip buffering at bio-cgroup if RT flag is set ? I think handling an usual process and RT process in "a" cgroup just makes the code complex.

Looking into a cpu-scheduler, which is the first module handling RT, it has some tweaks to handle RT in the system.
- special RT scheduler.
- isolated RT domain
- maximum execution time allowed to RT
....

Maybe handling RT in following way is usual way...(if we do something in this layer)

- Allow RT-bio-cgroup to skip limit check.
- But RT-bio-cgroup calculates io-throuput, execution time, statistics...
- When RT tasks in RT-bio-cgroup does excessive I/O which starves the whole system too long, raise safeguard-limitter. and tell users Warning or kill it.

Hmm ?

Thanks,
-Kame

#7 lore raw

Re: [patch 1/4] io controller: documentation

In reply to a message not in the archive

I forget to say that I like your new design in general ;)

Thanks,
-Kame

Quoted 188 lines · On Thu, 06 Nov 2008 10:30:23 -0500 [email protected]…

Index: linux17/Documentation/controllers/io-controller.txt
===================================================================

Documentation/controllers/io-controller.txt new file +172 -0
@@ -0,0 +1,172 @@
+				IO Controller
+				============
+
+Design
+=====
+This patchset implements a basic version of proportional weight IO controller.
+It is heavily derived from dm-ioband IO controller with one key difference
+and that is, there is no separate device mapper driver and there is no
+need to create a dm-ioband device on top of every block device which needs
+to do the IO control. In this implementation, all the control logic has
+been internalized and has been made per request queue. Enabling or disabling
+IO control on a block device is just a matter of writing a 0 or 1 in
+appropriate sysfs file.
+
+This is a proportional weight controller and that means various cgroups
+are assigned shares and tasks in those cgroups get to dispatch the bio
+in proportion to their cgroup share.
+
+All the contending cgroups are assigned tokens proportionate to their
+weights. One token is charged for one sector of IO. Once all the contending
+cgroups have consumed their tokens, fresh token allocation takes place and
+this is how disk bandwidth allocation proportion to weight is achieved.
+
+The bigger picture is that all the bios being submitted to a block device
+are first inspected by IO controller logic (bio_group_controller()), only if
+IO controller has been enabled on that device. The cgroup of the bio is
+determined and controller checks if this cgroup has sufficient tokens to
+dispatch the bio. If sufficient tokens are there, bio submitting thread
+continues to dispatch the bio through normal path otherwise IO controller
+buffers the bio and submitting thread returns back. These buffered bios
+are dispatched to lower layers later once the associate group (bio group)
+has sufficient tokens to dispatch the bios. This delayed dispatching is
+done with the help of a worker thread (biogroup).
+
+IO control can be enabled/disabled dynamically on any of the block device
+through sysfs file system. For example, to enable IO control on a device
+do following.
+
+echo 1 > /sys/block/sda/biogroup
+
+To disable IO control write 0.
+
+echo 0 > /sys/block/sda/biogroup
+
+This should be doable for any of the block device in the stack. Currently this
+patch places the hooks only for device mapper driver and still need to tweak
+md.
+
+For example, assume there are two cgroups A and B with weights 1024 and 2048
+in the system. Tasks in two cgroups A and B are doing IO to two disks sda and
+sdb in the system. A user has enabled IO control on both sda and sdb. Now on
+both sda and sdb, tasks in cgroup B will get to use 2/3 of disk BW and
+tasks in cgroup A will get to use 1/3 of disk bandwidth, only in case of
+contention. If tasks in any of the groups stop doing IO to a particular disk,
+task in other group will get to use full disk BW for that duration.
+
+
+HOWTO
+====
+- Enable cgroup, memory controller and block IO controller in kernel config
+  file.
+
+- Boot into the kernel and mount io controller.
+
+  mount -t cgroup -o bio none /cgroup/bio/
+
+- Create two cgroups test1 and test2
+
+  cd /cgroup/bio
+  mkdir test1 test2
+
+- Allocate weight 4096 to test1 and weight 2048 to test2
+
+  echo 4096 > /cgroup/bio/test1/bio.shares
+  echo 2048 > /cgroup/bio/test1/bio.shares
+
+- Launch "dd" operations in cgroup test1 and test2.
+
+  echo $$ > /cgroup/bio/test1/tasks
+  dd if=/somefile1 of=/dev/null
+  echo $$ > /cgroup/bio/test2/tasks
+  dd if=/somefile2 of=/dev/null
+
+Job in cgroup test1 should finish before job in cgroup test2. To verify
+that "dd" in cgroup test1 got to dispatch more bio as compared to "dd" in
+test2, look at "bio.aggregate_tokens" in both the cgroup (At same time). At
+any point of time when both the dd's are running, aggregate_tokens in cgroup
+test1 should be approximately double of aggregate_tokens in cgroup test2
+(Because weight of cgroup test1 is double of weight of cgroup test2).
+
+Some Tunables
+=============
+Some tunables appear in cgroup file system and in sysfs for respective
+device for debug and for configuration. Here is a brief description.
+
+Cgroup Files
+============
+bio.shares
+	Specifies the weight of the cgroup.
+
+bio.aggregate_tokens
+	Specifies total number of tokens dispatched by this cgroup. One token
+	represents one sector of IO.
+
+bio.jiffies
+	What was the jiffies values when last bio from this cgroup was released.
+
+bio.nr_token_slices
+	How many times this cgroup got the token allocation done from token
+	slice. We kind of create a token slice and every contending cgroup
+	gets the pie out of the slice based on the share.
+
+bio.nr_off_the_tree
+	How many times this bio group went off the list of contending groups.
+	We maintain an rb-tree of biogroups contending for IO and token
+	allocation takes place to these groups regularly. If some group stops
+	doing IO then it is considered to be idle and removed from the tree
+	and added back later when group has IO to perform. This file just
+	counts how many times this bio group went off the tree.
+
+Sysfs Tunabels
+==============
+/sys/block/{deice name}/biogroup
+	Whether IO controller (bio groups) are active on this device or not.
+
+/sys/block/{deice name}/deftoken
+	Default number of tokens which are given to a bio group upon start
+	if all the bio groups were of same weight. token slice is of dynamic
+	length. So if there are 3 cgroups contending and deftoken is 100 then
+	token slice lenght will be 100*3 = 300 and now out of this slice
+	three groups will get the tokens based on their weights.
+
+/sys/block/{deice name}/idletime
+	The time after which if a bio group does not generate the bio, it is
+	considered idle and removed from the rb-tree. Currently by default it
+	is 8ms.
+
+/sys/block/{deice name}/newslice_count
+	How many times new token allocation took place on this queue.
+
+TODO
+====
+- Do extensive testing in various scenarios and do performance optimization
+  and fix the things where broken.
+
+- IO schedulers derive context information from "current". This assumption
+  will be broken if bios are being submitted by a worker thread (biogroup).
+  Probably we need to put io context pointer in bio itself to get rid of
+  this dependency.
+
+- Allocating tokens for per sector of IO is crude approximation and will lead
+  to unfair bandwidth allocation in case task in cgroup is doing sequential IO
+  and task in other group is doing random IO. Rik Van Riel, suggested that
+  probably we should switch to time based scheme. Keep a track of average time
+  it takes to complete IO from a cgroup and do the allocation accordingly.
+
+- Currently this controller is dependent on memory controller being enabled.
+  Try to reduce this coupling.
+
+ISSUES
+======
+- IO controller can buffer the bios if suffcient tokens were not available
+  at the time of bio submission. Once the tokens are available, these bios
+  are dispatched to elevator/lower layers in first come first serve manner.
+  And this has potential to break CFQ where a RT tasks should be able to
+  dispatch the bio first or a high priority task should be able to release
+  more bio as compared to low priority task in same cgroup.
+
+  Not sure how to fix it. May be we need to maintain another rb-tree and
+  keep track of RT tasks and tasks priorities and dispatch accordingly. This
+  is equivalent of duplicating lots of CFQ logic and not sure how would it
+  impact AS behaviour.
Signature · 6 lines
-- 

_______________________________________________
Containers mailing list
Containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
https://lists.linux-foundation.org/mailman/listinfo/containers
#8 lore raw

Re: [patch 1/4] io controller: documentation

In reply to a message not in the archive

Hi, Vivek

Thanks for your work. :)

A question below.

Documentation/controllers/io-controller.txt line 56

Quoted 8 lines
Documentation/controllers/io-controller.txt new file +8 -0
+For example, assume there are two cgroups A and B with weights 1024 and 2048
+in the system. Tasks in two cgroups A and B are doing IO to two disks sda and
+sdb in the system. A user has enabled IO control on both sda and sdb. Now on
+both sda and sdb, tasks in cgroup B will get to use 2/3 of disk BW and
+tasks in cgroup A will get to use 1/3 of disk bandwidth, only in case of
+contention. If tasks in any of the groups stop doing IO to a particular disk,
+task in other group will get to use full disk BW for that duration.
+

So in this example, I can't assign 1/3 of sda's disk BW while 2/3 of sdb's disk BW to A. Am I right?

#9 lore raw

Re: [patch 1/4] io controller: documentation

In reply to [email protected]
Quoted 4 lines · On Thu, 06 Nov 2008 10:30:23 -0500 [email protected] wrote:
Documentation/controllers/io-controller.txt new file +3 -0
+ISSUES
+======
+- IO controller can buffer the bios if suffcient tokens were not available

Documentation/controllers/io-controller.txt line 172

Quoted 10 lines
Documentation/controllers/io-controller.txt new file +10 -0
+  at the time of bio submission. Once the tokens are available, these bios
+  are dispatched to elevator/lower layers in first come first serve manner.
+  And this has potential to break CFQ where a RT tasks should be able to
+  dispatch the bio first or a high priority task should be able to release
+  more bio as compared to low priority task in same cgroup.
+
+  Not sure how to fix it. May be we need to maintain another rb-tree and
+  keep track of RT tasks and tasks priorities and dispatch accordingly. This
+  is equivalent of duplicating lots of CFQ logic and not sure how would it
+  impact AS behaviour.

Why you don't isolate RT tasks into other cgroup ?
/cgroup/bio-cgroup/group_for_usual/...usual tasks.
/group_for_RT/ ...RT tasks. you can use high-speed path.

How about adding RT flag to bio-cgroup and skip buffering at bio-cgroup if RT flag is set ? I think handling an usual process and RT process in "a" cgroup just makes the code complex.

Looking into a cpu-scheduler, which is the first module handling RT, it has some tweaks to handle RT in the system.
- special RT scheduler.
- isolated RT domain
- maximum execution time allowed to RT
....

Maybe handling RT in following way is usual way...(if we do something in this layer)

- Allow RT-bio-cgroup to skip limit check.
- But RT-bio-cgroup calculates io-throuput, execution time, statistics...
- When RT tasks in RT-bio-cgroup does excessive I/O which starves the whole system too long, raise safeguard-limitter. and tell users Warning or kill it.

Hmm ?

Thanks,
-Kame

#10 lore raw

Re: [patch 1/4] io controller: documentation

In reply to [email protected]
Quoted 4 lines · On Thu, 06 Nov 2008 10:30:23 -0500 [email protected] wrote:
Documentation/controllers/io-controller.txt new file +3 -0
+ISSUES
+======
+- IO controller can buffer the bios if suffcient tokens were not available

Documentation/controllers/io-controller.txt line 172

Quoted 10 lines
Documentation/controllers/io-controller.txt new file +10 -0
+  at the time of bio submission. Once the tokens are available, these bios
+  are dispatched to elevator/lower layers in first come first serve manner.
+  And this has potential to break CFQ where a RT tasks should be able to
+  dispatch the bio first or a high priority task should be able to release
+  more bio as compared to low priority task in same cgroup.
+
+  Not sure how to fix it. May be we need to maintain another rb-tree and
+  keep track of RT tasks and tasks priorities and dispatch accordingly. This
+  is equivalent of duplicating lots of CFQ logic and not sure how would it
+  impact AS behaviour.

Why you don't isolate RT tasks into other cgroup ?
/cgroup/bio-cgroup/group_for_usual/...usual tasks.
/group_for_RT/ ...RT tasks. you can use high-speed path.

How about adding RT flag to bio-cgroup and skip buffering at bio-cgroup if RT flag is set ? I think handling an usual process and RT process in "a" cgroup just makes the code complex.

Looking into a cpu-scheduler, which is the first module handling RT, it has some tweaks to handle RT in the system.
- special RT scheduler.
- isolated RT domain
- maximum execution time allowed to RT
....

Maybe handling RT in following way is usual way...(if we do something in this layer)

- Allow RT-bio-cgroup to skip limit check.
- But RT-bio-cgroup calculates io-throuput, execution time, statistics...
- When RT tasks in RT-bio-cgroup does excessive I/O which starves the whole system too long, raise safeguard-limitter. and tell users Warning or kill it.

Hmm ?

Thanks,
-Kame

A message that is not in the archive (sent off-list or not yet received): 20081107113209.a6011c67.kamezawa.hiroyu-+CUm20s59erQFUHtdCDX3A@public.gmane.org

#12 lore raw

Re: [patch 1/4] io controller: documentation

Quoted 31 lines · KAMEZAWA Hiroyuki
Quoted 4 lines · On Thu, 06 Nov 2008 10:30:23 -0500 [email protected]…
Documentation/controllers/io-controller.txt new file +3 -0
+ISSUES
+======
+- IO controller can buffer the bios if suffcient tokens were not available

Documentation/controllers/io-controller.txt line 172

Quoted 10 lines
Documentation/controllers/io-controller.txt new file +10 -0
+  at the time of bio submission. Once the tokens are available, these bios
+  are dispatched to elevator/lower layers in first come first serve manner.
+  And this has potential to break CFQ where a RT tasks should be able to
+  dispatch the bio first or a high priority task should be able to release
+  more bio as compared to low priority task in same cgroup.
+
+  Not sure how to fix it. May be we need to maintain another rb-tree and
+  keep track of RT tasks and tasks priorities and dispatch accordingly. This
+  is equivalent of duplicating lots of CFQ logic and not sure how would it
+  impact AS behaviour.

Why you don't isolate RT tasks into other cgroup ?
/cgroup/bio-cgroup/group_for_usual/...usual tasks.
/group_for_RT/ ...RT tasks. you can use high-speed path.

How about adding RT flag to bio-cgroup and skip buffering at bio-cgroup if RT flag is set ? I think handling an usual process and RT process in "a" cgroup just makes the code complex.

Looking into a cpu-scheduler, which is the first module handling RT, it has some tweaks to handle RT in the system.
- special RT scheduler.
- isolated RT domain
- maximum execution time allowed to RT
....

Quoted 8 lines

Maybe handling RT in following way is usual way...(if we do something in this layer)

- Allow RT-bio-cgroup to skip limit check.
- But RT-bio-cgroup calculates io-throuput, execution time, statistics...
- When RT tasks in RT-bio-cgroup does excessive I/O which starves the whole system too long, raise safeguard-limitter. and tell users Warning or kill it.

Hmm ?

Hi Kame,

Looking at CFQ, there are two issues.

- RT tasks (and RT priorities with in that)
- Best Effort class tasks (and priorities with in that).

To make sure we don't break underlying CFQ elevator, we need to take care of both the things in higher level scheduler. This will mean practically we will end up copying code from CFQ in higher level scheduler.

Even if I do that (Keep track of RT tasks and Best effort class tasks and their proirities and do dispatch accordingly), I am not sure will it have any negative impact when a user is using AS IO scheduler on the leaf node.

That forces me to think that if we can let go the idea of doing proportionate bandwidth allocation at higher level logical device and just do it for leaf nodes, then we can drop the idea of two level scheduler and try to bring unification among four IO schedulers such that with least code copying they can also support proportional weight policies.

Thanks
Vivek

#13 lore raw

Re: [patch 1/4] io controller: documentation

In reply to KAMEZAWA Hiroyuki
Quoted 31 lines · KAMEZAWA Hiroyuki
Quoted 4 lines · On Thu, 06 Nov 2008 10:30:23 -0500 [email protected] wrote:
Documentation/controllers/io-controller.txt new file +3 -0
+ISSUES
+======
+- IO controller can buffer the bios if suffcient tokens were not available

Documentation/controllers/io-controller.txt line 172

Quoted 10 lines
Documentation/controllers/io-controller.txt new file +10 -0
+  at the time of bio submission. Once the tokens are available, these bios
+  are dispatched to elevator/lower layers in first come first serve manner.
+  And this has potential to break CFQ where a RT tasks should be able to
+  dispatch the bio first or a high priority task should be able to release
+  more bio as compared to low priority task in same cgroup.
+
+  Not sure how to fix it. May be we need to maintain another rb-tree and
+  keep track of RT tasks and tasks priorities and dispatch accordingly. This
+  is equivalent of duplicating lots of CFQ logic and not sure how would it
+  impact AS behaviour.

Why you don't isolate RT tasks into other cgroup ?
/cgroup/bio-cgroup/group_for_usual/...usual tasks.
/group_for_RT/ ...RT tasks. you can use high-speed path.

How about adding RT flag to bio-cgroup and skip buffering at bio-cgroup if RT flag is set ? I think handling an usual process and RT process in "a" cgroup just makes the code complex.

Looking into a cpu-scheduler, which is the first module handling RT, it has some tweaks to handle RT in the system.
- special RT scheduler.
- isolated RT domain
- maximum execution time allowed to RT
....

Quoted 8 lines

Maybe handling RT in following way is usual way...(if we do something in this layer)

- Allow RT-bio-cgroup to skip limit check.
- But RT-bio-cgroup calculates io-throuput, execution time, statistics...
- When RT tasks in RT-bio-cgroup does excessive I/O which starves the whole system too long, raise safeguard-limitter. and tell users Warning or kill it.

Hmm ?

Hi Kame,

Looking at CFQ, there are two issues.

- RT tasks (and RT priorities with in that)
- Best Effort class tasks (and priorities with in that).

To make sure we don't break underlying CFQ elevator, we need to take care of both the things in higher level scheduler. This will mean practically we will end up copying code from CFQ in higher level scheduler.

Even if I do that (Keep track of RT tasks and Best effort class tasks and their proirities and do dispatch accordingly), I am not sure will it have any negative impact when a user is using AS IO scheduler on the leaf node.

That forces me to think that if we can let go the idea of doing proportionate bandwidth allocation at higher level logical device and just do it for leaf nodes, then we can drop the idea of two level scheduler and try to bring unification among four IO schedulers such that with least code copying they can also support proportional weight policies.

Thanks
Vivek

#14 lore raw

Re: [patch 1/4] io controller: documentation

In reply to KAMEZAWA Hiroyuki
Quoted 31 lines · KAMEZAWA Hiroyuki
Quoted 4 lines · On Thu, 06 Nov 2008 10:30:23 -0500 [email protected] wrote:
Documentation/controllers/io-controller.txt new file +3 -0
+ISSUES
+======
+- IO controller can buffer the bios if suffcient tokens were not available

Documentation/controllers/io-controller.txt line 172

Quoted 10 lines
Documentation/controllers/io-controller.txt new file +10 -0
+  at the time of bio submission. Once the tokens are available, these bios
+  are dispatched to elevator/lower layers in first come first serve manner.
+  And this has potential to break CFQ where a RT tasks should be able to
+  dispatch the bio first or a high priority task should be able to release
+  more bio as compared to low priority task in same cgroup.
+
+  Not sure how to fix it. May be we need to maintain another rb-tree and
+  keep track of RT tasks and tasks priorities and dispatch accordingly. This
+  is equivalent of duplicating lots of CFQ logic and not sure how would it
+  impact AS behaviour.

Why you don't isolate RT tasks into other cgroup ?
/cgroup/bio-cgroup/group_for_usual/...usual tasks.
/group_for_RT/ ...RT tasks. you can use high-speed path.

How about adding RT flag to bio-cgroup and skip buffering at bio-cgroup if RT flag is set ? I think handling an usual process and RT process in "a" cgroup just makes the code complex.

Looking into a cpu-scheduler, which is the first module handling RT, it has some tweaks to handle RT in the system.
- special RT scheduler.
- isolated RT domain
- maximum execution time allowed to RT
....

Quoted 8 lines

Maybe handling RT in following way is usual way...(if we do something in this layer)

- Allow RT-bio-cgroup to skip limit check.
- But RT-bio-cgroup calculates io-throuput, execution time, statistics...
- When RT tasks in RT-bio-cgroup does excessive I/O which starves the whole system too long, raise safeguard-limitter. and tell users Warning or kill it.

Hmm ?

Hi Kame,

Looking at CFQ, there are two issues.

- RT tasks (and RT priorities with in that)
- Best Effort class tasks (and priorities with in that).

To make sure we don't break underlying CFQ elevator, we need to take care of both the things in higher level scheduler. This will mean practically we will end up copying code from CFQ in higher level scheduler.

Even if I do that (Keep track of RT tasks and Best effort class tasks and their proirities and do dispatch accordingly), I am not sure will it have any negative impact when a user is using AS IO scheduler on the leaf node.

That forces me to think that if we can let go the idea of doing proportionate bandwidth allocation at higher level logical device and just do it for leaf nodes, then we can drop the idea of two level scheduler and try to bring unification among four IO schedulers such that with least code copying they can also support proportional weight policies.

Thanks
Vivek

#15 lore raw

Re: [patch 1/4] io controller: documentation

In reply to [email protected]

Hi, Vivek

Thanks for your work. :)

A question below.

Documentation/controllers/io-controller.txt line 56

Quoted 8 lines
Documentation/controllers/io-controller.txt new file +8 -0
+For example, assume there are two cgroups A and B with weights 1024 and 2048
+in the system. Tasks in two cgroups A and B are doing IO to two disks sda and
+sdb in the system. A user has enabled IO control on both sda and sdb. Now on
+both sda and sdb, tasks in cgroup B will get to use 2/3 of disk BW and
+tasks in cgroup A will get to use 1/3 of disk bandwidth, only in case of
+contention. If tasks in any of the groups stop doing IO to a particular disk,
+task in other group will get to use full disk BW for that duration.
+

So in this example, I can't assign 1/3 of sda's disk BW while 2/3 of sdb's disk BW to A. Am I right?

#16 lore raw

Re: [patch 1/4] io controller: documentation

In reply to [email protected]

Hi, Vivek

Thanks for your work. :)

A question below.

Documentation/controllers/io-controller.txt line 56

Quoted 8 lines
Documentation/controllers/io-controller.txt new file +8 -0
+For example, assume there are two cgroups A and B with weights 1024 and 2048
+in the system. Tasks in two cgroups A and B are doing IO to two disks sda and
+sdb in the system. A user has enabled IO control on both sda and sdb. Now on
+both sda and sdb, tasks in cgroup B will get to use 2/3 of disk BW and
+tasks in cgroup A will get to use 1/3 of disk bandwidth, only in case of
+contention. If tasks in any of the groups stop doing IO to a particular disk,
+task in other group will get to use full disk BW for that duration.
+

So in this example, I can't assign 1/3 of sda's disk BW while 2/3 of sdb's disk BW to A. Am I right?

A message that is not in the archive (sent off-list or not yet received): [email protected]

#18 lore raw

Re: [patch 1/4] io controller: documentation

Quoted 6 lines · Li Zefan

Hi, Vivek

Thanks for your work. :)

A question below.

Quoted 11 lines

Documentation/controllers/io-controller.txt line 56

Quoted 8 lines
Documentation/controllers/io-controller.txt new file +8 -0
+For example, assume there are two cgroups A and B with weights 1024 and 2048
+in the system. Tasks in two cgroups A and B are doing IO to two disks sda and
+sdb in the system. A user has enabled IO control on both sda and sdb. Now on
+both sda and sdb, tasks in cgroup B will get to use 2/3 of disk BW and
+tasks in cgroup A will get to use 1/3 of disk bandwidth, only in case of
+contention. If tasks in any of the groups stop doing IO to a particular disk,
+task in other group will get to use full disk BW for that duration.
+

So in this example, I can't assign 1/3 of sda's disk BW while 2/3 of sdb's disk BW to A. Am I right?

Hi,

Yes you are right. Currently policies are global and not per disk. So in above example, cgroup A will get 1/3 of disk BW both on sda and sdb. And one can not configure in such a manner so that A gets 1/3 of BW on sda and 2/3 of BW on sdb.

I think assigning cgroup weights per disk should be doable but personally I think it makes configuration complex and I am not sure if it really a very useful feature.

Thanks
Vivek

#19 lore raw

Re: [patch 1/4] io controller: documentation

In reply to Li Zefan
Quoted 6 lines · Li Zefan

Hi, Vivek

Thanks for your work. :)

A question below.

Quoted 11 lines

Documentation/controllers/io-controller.txt line 56

Quoted 8 lines
Documentation/controllers/io-controller.txt new file +8 -0
+For example, assume there are two cgroups A and B with weights 1024 and 2048
+in the system. Tasks in two cgroups A and B are doing IO to two disks sda and
+sdb in the system. A user has enabled IO control on both sda and sdb. Now on
+both sda and sdb, tasks in cgroup B will get to use 2/3 of disk BW and
+tasks in cgroup A will get to use 1/3 of disk bandwidth, only in case of
+contention. If tasks in any of the groups stop doing IO to a particular disk,
+task in other group will get to use full disk BW for that duration.
+

So in this example, I can't assign 1/3 of sda's disk BW while 2/3 of sdb's disk BW to A. Am I right?

Hi,

Yes you are right. Currently policies are global and not per disk. So in above example, cgroup A will get 1/3 of disk BW both on sda and sdb. And one can not configure in such a manner so that A gets 1/3 of BW on sda and 2/3 of BW on sdb.

I think assigning cgroup weights per disk should be doable but personally I think it makes configuration complex and I am not sure if it really a very useful feature.

Thanks
Vivek

#20 lore raw

Re: [patch 1/4] io controller: documentation

In reply to Li Zefan
Quoted 6 lines · Li Zefan

Hi, Vivek

Thanks for your work. :)

A question below.

Quoted 11 lines

Documentation/controllers/io-controller.txt line 56

Quoted 8 lines
Documentation/controllers/io-controller.txt new file +8 -0
+For example, assume there are two cgroups A and B with weights 1024 and 2048
+in the system. Tasks in two cgroups A and B are doing IO to two disks sda and
+sdb in the system. A user has enabled IO control on both sda and sdb. Now on
+both sda and sdb, tasks in cgroup B will get to use 2/3 of disk BW and
+tasks in cgroup A will get to use 1/3 of disk bandwidth, only in case of
+contention. If tasks in any of the groups stop doing IO to a particular disk,
+task in other group will get to use full disk BW for that duration.
+

So in this example, I can't assign 1/3 of sda's disk BW while 2/3 of sdb's disk BW to A. Am I right?

Hi,

Yes you are right. Currently policies are global and not per disk. So in above example, cgroup A will get 1/3 of disk BW both on sda and sdb. And one can not configure in such a manner so that A gets 1/3 of BW on sda and 2/3 of BW on sdb.

I think assigning cgroup weights per disk should be doable but personally I think it makes configuration complex and I am not sure if it really a very useful feature.

Thanks
Vivek

#21 lore raw

PATCH 3/4 io controller: Core IO controller implementation logic

In reply to [email protected]

o Core IO controller implementation

mm/biocontrol.c +704 -0
Index: linux2/mm/biocontrol.c
===================================================================
@@ -33,6 +33,7 @@
 #include <linux/err.h>
 #include <linux/biocontrol.h>
 
+void bio_group_inactive_timeout(unsigned long data);
 
 /* return corresponding bio_cgroup object of a cgroup */
 static inline struct bio_cgroup *cgroup_bio(struct cgroup *cgrp)
@@ -407,3 +408,706 @@ struct cgroup_subsys bio_cgroup_subsys =
 	.attach		= bio_cgroup_move_task,
 	.early_init	= 0,
 };
+
+struct bio_group* create_bio_group(struct bio_cgroup *biocg,
+						struct request_queue *q)
+{
+	unsigned long flags;
+	struct bio_group *biog = NULL;
+
+	biog = kzalloc(sizeof(struct bio_group), GFP_ATOMIC);
+	if (!biog)
+		return biog;
+
+	spin_lock_init(&biog->bio_group_lock);
+	biog->q = q;
+	biog->biocg = biocg;
+	INIT_LIST_HEAD(&biog->next);
+	biog->biog_inactive_timer.function = bio_group_inactive_timeout;
+	biog->biog_inactive_timer.data = (unsigned long)biog;
+	init_timer(&biog->biog_inactive_timer);
+	atomic_set(&biog->refcnt, 0);
+	spin_lock_irqsave(&biocg->biog_list_lock, flags);
+	list_add(&biog->next, &biocg->bio_group_list);
+	bio_group_get(biog);
+	spin_unlock_irqrestore(&biocg->biog_list_lock, flags);
+	return biog;
+}
+
+void* alloc_biog_io(void)
+{
+	return kzalloc(sizeof(struct biog_io), GFP_ATOMIC);
+}
+
+void free_biog_io(struct biog_io *biog_io)
+{
+	kfree(biog_io);
+}
+
+/*
+ * Upon succesful completion of bio, this function starts the inactive timer
+ * so that if a bio group stops contending for disk bandwidth, it is removed
+ * from the token allocation race.
+ */
+void biog_io_end(struct bio *bio, int error)
+{
+	struct biog_io *biog_io;
+	struct bio_group *biog;
+	unsigned long flags;
+	struct request_queue *q;
+
+	biog_io = bio->bi_private;
+	biog = biog_io->biog;
+	BUG_ON(!biog);
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	q = biog->q;
+	BUG_ON(!q);
+
+	/* Restore the original bio fields */
+	bio->bi_end_io = biog_io->bi_end_io;
+	bio->bi_private = biog_io->bi_private;
+
+	/* If bio group is still empty, then start the inactive timer */
+	if (bio_group_on_queue(biog) && bio_group_empty(biog)) {
+		mod_timer(&biog->biog_inactive_timer,
+			jiffies + msecs_to_jiffies(q->biogroup_idletime));
+		bio_group_flag_set(BIOG_FLAG_TIMER_ACTIVE, biog);
+	}
+
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	free_biog_io(biog_io);
+	bio_group_put(biog);
+	bio_endio(bio, error);
+}
+
+/* Calculate how many tokens should be allocated to new group based on
+ * the number of share/weight of this group and the number of tokens and
+ * load which is already present on the queue.
+ */
+unsigned long calculate_nr_tokens(struct bio_group *biog,
+					struct request_queue *q)
+{
+	unsigned long nr_tokens, total_slice;
+
+	total_slice = q->biogroup_deftoken * q->nr_biog;
+	nr_tokens = total_slice * biog->biocg->shares/q->total_weight;
+
+	BUG_ON(!nr_tokens);
+	return nr_tokens;
+}
+
+unsigned long alloc_bio_group_key(struct request_queue *q)
+{
+	unsigned long key = 0;
+
+	if (!q->bio_groups.rb.rb_node)
+		return key;
+
+	/* Insert element at the end of tree */
+	key = q->max_key + 1;
+	return key;
+}
+
+/*
+ * The below is leftmost cache rbtree addon
+ */
+struct bio_group *bio_group_rb_first(struct group_rb_root *root)
+{
+	if (!root->left)
+		root->left = rb_first(&root->rb);
+
+	if (root->left)
+		return rb_entry(root->left, struct bio_group, rb_node);
+
+	return NULL;
+}
+
+void remove_bio_group_from_rbtree(struct bio_group *biog,
+					struct request_queue *q)
+{
+	struct group_rb_root *root;
+	struct rb_node *n;
+
+	root = &q->bio_groups;
+	n = &biog->rb_node;
+
+	if (root->left == n)
+		root->left = NULL;
+
+	rb_erase(n, &root->rb);
+	RB_CLEAR_NODE(n);
+
+	if (bio_group_blocked(biog))
+		q->nr_biog_blocked--;
+
+	q->nr_biog--;
+	q->total_weight -= biog->biocg->shares;
+
+	if (!q->total_weight)
+		q->max_key = 0;
+}
+
+
+void insert_bio_group_into_rbtree(struct bio_group *biog,
+					struct request_queue *q)
+{
+	struct rb_node **p;
+	struct rb_node *parent = NULL;
+	struct bio_group *__biog;
+	int leftmost = 1;
+
+	/* Check if any element being inserted has key less than max key */
+	if (biog->key < q->max_key)
+		BUG();
+
+	p = &q->bio_groups.rb.rb_node;
+	while (*p) {
+		parent = *p;
+		__biog = rb_entry(parent, struct bio_group, rb_node);
+
+		/* Should equal key case be a warning? */
+		if (biog->key < __biog->key)
+			p = &(*p)->rb_left;
+		else {
+			p = &(*p)->rb_right;
+			leftmost = 0;
+		}
+	}
+
+	/* Cache the leftmost element */
+	if (leftmost)
+		q->bio_groups.left = &biog->rb_node;
+
+	rb_link_node(&biog->rb_node, parent, p);
+	rb_insert_color(&biog->rb_node, &q->bio_groups.rb);
+
+	/* Update the tokens and weight in request_queue */
+	q->nr_biog++;
+	q->total_weight += biog->biocg->shares;
+	q->max_key = biog->key;
+	if (bio_group_blocked(biog))
+		q->nr_biog_blocked++;
+}
+
+void queue_bio_group(struct bio_group *biog, struct request_queue *q)
+{
+	biog->key = alloc_bio_group_key(q);
+	/* Take another reference on biog. will be decremented once biog
+	 * is off the tree */
+	bio_group_get(biog);
+	insert_bio_group_into_rbtree(biog, q);
+	bio_group_flag_set(BIOG_FLAG_ON_QUEUE, biog);
+	bio_group_flag_clear(BIOG_FLAG_BLOCKED, biog);
+	biog->slice_stamp = q->current_slice;
+}
+
+void start_new_token_slice(struct request_queue *q)
+{
+	struct rb_node *n;
+	struct bio_group *biog = NULL;
+	struct group_rb_root *root;
+	unsigned long flags;
+
+	q->current_slice++;
+
+	/* Traverse the tree and reset the blocked count to zero of all the
+	 * biogs */
+
+	root = &q->bio_groups;
+
+	if (!root->left)
+		root->left = rb_first(&root->rb);
+
+	if (root->left)
+		biog = rb_entry(root->left, struct bio_group, rb_node);
+
+	if (!biog)
+		return;
+
+	n = &biog->rb_node;
+
+	/* Reset blocked count */
+	q->nr_biog_blocked = 0;
+	q->newslice_count++;
+
+	do {
+		biog = rb_entry(n, struct bio_group, rb_node);
+		spin_lock_irqsave(&biog->bio_group_lock, flags);
+		bio_group_flag_clear(BIOG_FLAG_BLOCKED, biog);
+		spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+		n = rb_next(n);
+	} while (n);
+
+}
+
+int should_start_new_token_slice(struct request_queue *q)
+{
+	/*
+	 * if all the biog on the queue are blocked, then start a new
+	 * token slice
+	 */
+	if (q->nr_biog_blocked == q->nr_biog)
+		return 1;
+	return 0;
+}
+
+int is_bio_group_blocked(struct bio_group *biog)
+{
+	unsigned long flags, status = 0;
+
+	/* Do I really need to lock bio group */
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+		if (bio_group_blocked(biog))
+			status = 1;
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return status;
+}
+
+int can_bio_group_dispatch(struct bio_group *biog, struct bio *bio)
+{
+	unsigned long temp = 0, flags;
+	struct request_queue *q;
+	long nr_sectors;
+	int can_dispatch = 0;
+
+	BUG_ON(!biog);
+	BUG_ON(!bio);
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	nr_sectors = bio_sectors(bio);
+	q = biog->q;
+
+	if (time_after(q->current_slice, biog->slice_stamp)) {
+		temp = calculate_nr_tokens(biog, q);
+		biog->credit_tokens += temp;
+		biog->slice_stamp = q->current_slice;
+		biog->biocg->nr_token_slices++;
+	}
+
+	if ((biog->credit_tokens > 0) && (biog->credit_tokens > nr_sectors)) {
+		if (bio_group_flag_test_and_clear(BIOG_FLAG_BLOCKED, biog))
+			q->nr_biog_blocked--;
+		can_dispatch = 1;
+		goto out;
+	}
+
+	if (!bio_group_flag_test_and_set(BIOG_FLAG_BLOCKED, biog))
+		q->nr_biog_blocked++;
+
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return can_dispatch;
+}
+
+/* Should be called without queue lock held */
+void bio_group_deactivate_timer(struct bio_group *biog)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	if (bio_group_flag_test_and_clear(BIOG_FLAG_TIMER_ACTIVE, biog)) {
+		/* Drop the bio group lock so that timer routine could
+		 * finish in case it fires */
+		spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+		del_timer_sync(&biog->biog_inactive_timer);
+		return;
+	}
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+}
+
+int attach_bio_group_io(struct bio_group *biog, struct bio *bio)
+{
+	int err = 0;
+	struct biog_io *biog_io;
+
+	biog_io = alloc_biog_io();
+	if (!biog_io) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* I already have a valid pointer to biog. So it should be ok
+	 * to get a reference to it. */
+	bio_group_get(biog);
+	biog_io->biog = biog;
+	biog_io->bi_end_io = bio->bi_end_io;
+	biog_io->bi_private = bio->bi_private;
+
+	bio->bi_end_io = biog_io_end;
+	bio->bi_private = biog_io;
+out:
+	return err;
+}
+
+int account_bio_to_bio_group(struct bio_group *biog, struct bio *bio)
+{
+	int err = 0;
+	unsigned long flags;
+	struct request_queue *q;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	err = attach_bio_group_io(biog, bio);
+	if (err)
+		goto out;
+
+	biog->nr_bio++;
+	q = biog->q;
+	if (!bio_group_on_queue(biog))
+		queue_bio_group(biog, q);
+
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return err;
+}
+
+int add_bio_to_bio_group_queue(struct bio_group *biog, struct bio *bio)
+{
+	unsigned long flags;
+	struct request_queue *q;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	__bio_group_queue_bio_tail(biog, bio);
+	q = biog->q;
+	q->nr_queued_bio++;
+	queue_delayed_work(q->biogroup_workqueue, &q->biogroup_work, 0);
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return 0;
+}
+
+/*
+ * It determines if the thread submitting the bio can itself continue to
+ * submit the bio or this bio needs to be buffered for later submission
+ */
+int can_biog_do_direct_dispatch(struct bio_group *biog)
+{
+	unsigned long flags, dispatch = 1;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	if (bio_group_blocked(biog)) {
+		dispatch = 0;
+		goto out;
+	}
+
+	/* Make sure there are not other queued bios on the biog. These
+	 * queued bios should get a chance to dispatch first */
+	if (!bio_group_queued_empty(biog))
+		dispatch = 0;
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return dispatch;
+}
+
+void charge_bio_group_for_tokens(struct bio_group *biog, struct bio *bio)
+{
+	unsigned long flags;
+	long dispatched_tokens;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	dispatched_tokens = bio_sectors(bio);
+	biog->nr_bio--;
+
+	biog->credit_tokens -= dispatched_tokens;
+
+	/* debug aid. also update aggregate tokens and jiffies in biocg */
+	biog->biocg->aggregate_tokens += dispatched_tokens;
+	biog->biocg->jiffies = jiffies;
+
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+}
+
+unsigned long __bio_group_try_to_dispatch(struct bio_group *biog,
+							struct bio *bio)
+{
+	struct request_queue *q;
+	int dispatched = 0;
+
+	BUG_ON(!biog);
+	BUG_ON(!bio);
+
+	q = biog->q;
+	BUG_ON(!q);
+retry:
+	if (!can_bio_group_dispatch(biog, bio)) {
+		if (should_start_new_token_slice(q)) {
+			start_new_token_slice(q);
+			goto retry;
+		}
+		goto out;
+	}
+
+	charge_bio_group_for_tokens(biog, bio);
+	dispatched = 1;
+out:
+	return dispatched;
+}
+
+unsigned long bio_group_try_to_dispatch(struct bio_group *biog, struct bio *bio)
+{
+	struct request_queue *q;
+	int dispatched = 0;
+	unsigned long flags;
+
+	q = biog->q;
+	BUG_ON(!q);
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	dispatched = __bio_group_try_to_dispatch(biog, bio);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+
+	return dispatched;
+}
+
+/* Should be called with queue lock and bio group lock held */
+void requeue_bio_group(struct request_queue *q, struct bio_group *biog)
+{
+	remove_bio_group_from_rbtree(biog, q);
+	biog->key = alloc_bio_group_key(q);
+	insert_bio_group_into_rbtree(biog, q);
+}
+
+/* Make a list of queued bios in this bio group which can be dispatched. */
+void make_release_bio_list(struct bio_group *biog,
+					struct bio_list *release_list)
+{
+	unsigned long flags, dispatched = 0;
+	struct bio *bio;
+	struct request_queue *q;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+
+	while (1) {
+		if (bio_group_queued_empty(biog))
+			goto out;
+
+		if (bio_group_blocked(biog))
+			goto out;
+
+		/* Dequeue one bio from bio group */
+		bio = __bio_group_dequeue_bio(biog);
+		BUG_ON(!bio);
+		q = biog->q;
+		q->nr_queued_bio--;
+
+		/* Releasing lock as try to dispatch will acquire it again */
+		spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+		dispatched = __bio_group_try_to_dispatch(biog, bio);
+		spin_lock_irqsave(&biog->bio_group_lock, flags);
+
+		if (dispatched) {
+			/* Add the bio to release list */
+			bio_list_add(release_list, bio);
+			continue;
+		} else {
+			/* Put the bio back into biog */
+			__bio_group_queue_bio_head(biog, bio);
+			q->nr_queued_bio++;
+			goto out;
+		}
+	}
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return;
+}
+
+/*
+ * If a bio group is inactive for q->inactive_timeout, then this group is
+ * considered to be no more contending for the disk bandwidth and removed
+ * from the tree.
+ */
+void bio_group_inactive_timeout(unsigned long data)
+{
+	struct bio_group *biog = (struct bio_group *)data;
+	unsigned long flags, flags1;
+	struct request_queue *q;
+
+	q = biog->q;
+	BUG_ON(!q);
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	spin_lock_irqsave(&biog->bio_group_lock, flags1);
+
+	BUG_ON(!bio_group_on_queue(biog));
+	BUG_ON(biog->nr_bio);
+
+	BUG_ON((biog->bio_group_flags > 7));
+	/* Remove biog from tree */
+	biog->biocg->nr_off_the_tree++;
+	remove_bio_group_from_rbtree(biog, q);
+	bio_group_flag_clear(BIOG_FLAG_ON_QUEUE, biog);
+	bio_group_flag_clear(BIOG_FLAG_BLOCKED, biog);
+	bio_group_flag_clear(BIOG_FLAG_TIMER_ACTIVE, biog);
+
+	/* dm_start_new_slice() takes bio_group_lock. Release it now */
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags1);
+
+	/* Also check if new slice should be started */
+	if ((q->nr_biog) && should_start_new_token_slice(q))
+		start_new_token_slice(q);
+
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	/* Drop the reference to biog */
+	bio_group_put(biog);
+	return;
+}
+
+/*
+ * It is called through worker thread and it takes care of releasing queued
+ * bios to underlying layer
+ */
+void bio_group_dispatch_queued_bio(struct request_queue *q)
+{
+	struct bio_group *biog;
+	unsigned long biog_scanned = 0;
+	unsigned long flags, flags1;
+	struct bio *bio = NULL;
+	int ret;
+	struct bio_list release_list;
+
+	bio_list_init(&release_list);
+
+	spin_lock_irqsave(q->queue_lock, flags);
+
+	while (1) {
+
+		if (!q->nr_biog)
+			goto out;
+
+		if (!q->nr_queued_bio)
+			goto out;
+
+		if (biog_scanned == q->nr_biog) {
+			/* Scanned the whole tree. No eligible biog found */
+			if (q->nr_queued_bio) {
+				queue_delayed_work(q->biogroup_workqueue,
+							&q->biogroup_work, 1);
+			}
+			goto out;
+		}
+
+		biog = bio_group_rb_first(&q->bio_groups);
+		BUG_ON(!biog);
+
+		make_release_bio_list(biog, &release_list);
+
+		/* If there are bios to dispatch, release these */
+		if (!bio_list_empty(&release_list)) {
+			if (q->nr_queued_bio)
+				queue_delayed_work(q->biogroup_workqueue,
+						&q->biogroup_work, 0);
+			goto dispatch_bio;
+		} else {
+			spin_lock_irqsave(&biog->bio_group_lock, flags1);
+			requeue_bio_group(q, biog);
+			biog_scanned++;
+			spin_unlock_irqrestore(&biog->bio_group_lock, flags1);
+			continue;
+		}
+	}
+
+dispatch_bio:
+		spin_unlock_irqrestore(q->queue_lock, flags);
+		bio = bio_list_pop(&release_list);
+		BUG_ON(!bio);
+
+		do {
+			/* Taint the bio with pass through flag */
+			bio->bi_flags |= (1UL << BIO_NOBIOGROUP);
+			do {
+				ret = q->make_request_fn(q, bio);
+			} while (ret);
+			bio = bio_list_pop(&release_list);
+		} while (bio);
+
+		return;
+out:
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	return;
+}
+
+void blk_biogroup_work(struct work_struct *work)
+{
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	struct request_queue *q =
+		container_of(dw, struct request_queue, biogroup_work);
+
+	bio_group_dispatch_queued_bio(q);
+}
+
+/*
+ * This is core IO controller function which tries to dispatch bios to
+ * underlying layers based on cgroup weights.
+ *
+ * If the cgroup bio belongs to has got sufficient tokens, submitting
+ * task/thread is allowed to continue to submit the bio otherwise, bio
+ * is buffered here and submitting thread returns. This buffered bio will
+ * be dispatched to lower layers when cgroup has sufficient tokens.
+ *
+ * Return code:
+ * 0 --> continue submit the bio
+ * 1---> bio buffered by bio group layer. return
+ */
+int bio_group_controller(struct request_queue *q, struct bio *bio)
+{
+
+	struct bio_group *biog;
+	struct bio_cgroup *biocg;
+	int err = 0;
+	unsigned long flags, dispatched = 0;
+
+	/* This bio has already been subjected to resource constraints.
+	 * Let it pass through unconditionally. */
+	if (bio_flagged(bio, BIO_NOBIOGROUP)) {
+		bio->bi_flags &= ~(1UL << BIO_NOBIOGROUP);
+		return 0;
+	}
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	biocg = bio_cgroup_from_bio(bio);
+	BUG_ON(!biocg);
+
+	/* If a biog is found, we also take a reference to it */
+	biog = bio_group_from_cgroup(biocg, q);
+	if (!biog) {
+		/* In case of success, returns with reference to biog */
+		biog = create_bio_group(biocg, q);
+		if (!biog) {
+			err = -ENOMEM;
+			goto end_io;
+		}
+	}
+
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	bio_group_deactivate_timer(biog);
+	spin_lock_irqsave(q->queue_lock, flags);
+
+	err = account_bio_to_bio_group(biog, bio);
+	if (err)
+		goto end_io;
+
+	if (!can_biog_do_direct_dispatch(biog)) {
+		add_bio_to_bio_group_queue(biog, bio);
+		goto buffered;
+	}
+
+	dispatched = __bio_group_try_to_dispatch(biog, bio);
+
+	if (!dispatched) {
+		add_bio_to_bio_group_queue(biog, bio);
+		goto buffered;
+	}
+
+	bio_group_put(biog);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	return 0;
+
+buffered:
+	bio_group_put(biog);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	return 1;
+end_io:
+	bio_group_put(biog);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	bio_endio(bio, err);
+	return 1;
+}
include/linux/bio.h +1 -0
Index: linux2/include/linux/bio.h
===================================================================
@@ -131,6 +131,7 @@ struct bio {
 #define BIO_BOUNCED	5	/* bio is a bounce bio */
 #define BIO_USER_MAPPED 6	/* contains user pages */
 #define BIO_EOPNOTSUPP	7	/* not supported */
+#define BIO_NOBIOGROUP 8       /* Don do bio group control on this bio */
 #define bio_flagged(bio, flag)	((bio)->bi_flags & (1 << (flag)))
 
 /*
block/genhd.c +122 -0

140 lines not on this page · raw message

include/linux/blkdev.h +34 -0
Index: linux2/include/linux/blkdev.h
===================================================================
@@ -289,6 +289,11 @@ struct blk_cmd_filter {
 	struct kobject kobj;
 };
 
+struct group_rb_root {
+	struct rb_root	rb;
+	struct rb_node	*left;
+};
+
 struct request_queue
 {
 	/*
@@ -298,6 +303,33 @@ struct request_queue
 	struct request		*last_merge;
 	elevator_t		*elevator;
 
+	/* rb-tree which contains all the contending bio groups */
+	struct group_rb_root	bio_groups;
+
+	/* Total number of bio_group currently on the request queue */
+	unsigned long		nr_biog;
+	unsigned long		current_slice;
+
+	struct workqueue_struct *biogroup_workqueue;
+	struct delayed_work     biogroup_work;
+	unsigned long		nr_queued_bio;
+
+	/* What's the idletime after which a bio group is considered idle and
+	 * considered no more contending for the bandwidth. */
+	unsigned long		biogroup_idletime;
+	unsigned long		biogroup_deftoken;
+
+	/* Number of biog which can't issue IO because they don't have
+	 * suffifiet tokens */
+	unsigned long		nr_biog_blocked;
+
+	/* Sum of weight of all the cgroups present on this queue */
+	unsigned long		total_weight;
+
+	/* Debug Aid */
+	unsigned long		max_key;
+	unsigned long		newslice_count;
+
 	/*
 	 * the queue request freelist, one for reads and one for writes
 	 */
@@ -421,6 +453,7 @@ struct request_queue
 #define QUEUE_FLAG_ELVSWITCH	8	/* don't use elevator, just do FIFO */
 #define QUEUE_FLAG_BIDI		9	/* queue supports bidi requests */
 #define QUEUE_FLAG_NOMERGES    10	/* disable merge attempts */
+#define QUEUE_FLAG_BIOG_ENABLED    11	/* bio group enabled */
 
 static inline int queue_is_locked(struct request_queue *q)
 {
@@ -527,6 +560,7 @@ enum {
 #define blk_queue_stopped(q)	test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags)
 #define blk_queue_nomerges(q)	test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags)
 #define blk_queue_flushing(q)	((q)->ordseq)
+#define blk_queue_bio_group_enabled(q)	test_bit(QUEUE_FLAG_BIOG_ENABLED, &(q)->queue_flags)
 
 #define blk_fs_request(rq)	((rq)->cmd_type == REQ_TYPE_FS)
 #define blk_pc_request(rq)	((rq)->cmd_type == REQ_TYPE_BLOCK_PC)
block/blk-core.c +15 -0
Index: linux2/block/blk-core.c
===================================================================
@@ -30,6 +30,7 @@
 #include <linux/cpu.h>
 #include <linux/blktrace_api.h>
 #include <linux/fault-inject.h>
+#include <linux/biocontrol.h>
 
 #include "blk.h"
 
@@ -502,6 +503,20 @@ struct request_queue *blk_alloc_queue_no
 	mutex_init(&q->sysfs_lock);
 	spin_lock_init(&q->__queue_lock);
 
+#ifdef CONFIG_CGROUP_BIO
+	/* Initialize default idle time */
+	q->biogroup_idletime = DEFAULT_IDLE_PERIOD;
+	q->biogroup_deftoken = DEFAULT_NR_TOKENS;
+
+	/* Also create biogroup worker threads. It needs to be conditional */
+	if (!bio_cgroup_disabled()) {
+		q->biogroup_workqueue = create_workqueue("biogroup");
+		if (!q->biogroup_workqueue)
+			panic("Failed to create biogroup\n");
+	}
+	INIT_DELAYED_WORK(&q->biogroup_work, blk_biogroup_work);
+#endif
+
 	return q;
 }
 EXPORT_SYMBOL(blk_alloc_queue_node);
include/linux/biocontrol.h +114 -0

132 lines not on this page · raw message

#22 lore raw

PATCH 2/4 io controller: biocgroup implementation

In reply to [email protected]

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

include/linux/cgroup_subsys.h +6 -0
Index: linux17/include/linux/cgroup_subsys.h
===================================================================
@@ -43,6 +43,12 @@ SUBSYS(mem_cgroup)
 
 /* */
 
+#ifdef CONFIG_CGROUP_BIO
+SUBSYS(bio_cgroup)
+#endif
+
+/* */
+
 #ifdef CONFIG_CGROUP_DEVICE
 SUBSYS(devices)
 #endif
init/Kconfig +7 -0
Index: linux17/init/Kconfig
===================================================================
@@ -408,6 +408,13 @@ config CGROUP_MEM_RES_CTLR
 	  This config option also selects MM_OWNER config option, which
 	  could in turn add some fork/exit overhead.
 
+config CGROUP_BIO
+	bool "Block I/O cgroup subsystem"
+	depends on CGROUP_MEM_RES_CTLR
+	select MM_OWNER
+	help
+	  A generic proportinal weight IO controller.
+
 config SYSFS_DEPRECATED
 	bool
 
mm/biocontrol.c new file +409 -0

409 lines not on this page · raw message

include/linux/biocontrol.h new file +174 -0

174 lines not on this page · raw message

mm/Makefile +1 -0
Index: linux17/mm/Makefile
===================================================================
@@ -34,4 +34,5 @@ obj-$(CONFIG_MIGRATION) += migrate.o
 obj-$(CONFIG_SMP) += allocpercpu.o
 obj-$(CONFIG_QUICKLIST) += quicklist.o
 obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o
+obj-$(CONFIG_CGROUP_BIO) += biocontrol.o
 
mm/memcontrol.c +21 -48

171 lines not on this page · raw message

include/linux/memcontrol.h +46 -1

68 lines not on this page · raw message

#23 lore raw

PATCH 4/4 io controller: Put IO controller to use in device mapper and standard make_request() function

In reply to [email protected]

o Tweak standard make_request() function and also the device mapper make request functions to enable use of IO controller.

drivers/md/dm.c +13 -0
Index: linux17/drivers/md/dm.c
===================================================================
@@ -22,6 +22,7 @@
 #include <linux/hdreg.h>
 #include <linux/blktrace_api.h>
 #include <linux/smp_lock.h>
+#include <linux/biocontrol.h>
 
 #define DM_MSG_PREFIX "core"
 
@@ -885,6 +886,7 @@ static int dm_request(struct request_que
 	int r = -EIO;
 	int rw = bio_data_dir(bio);
 	struct mapped_device *md = q->queuedata;
+	int ret;
 
 	/*
 	 * There is no use in forwarding any barrier request since we can't
@@ -895,6 +897,13 @@ static int dm_request(struct request_que
 		return 0;
 	}
 
+	if (!bio_cgroup_disabled() && blk_queue_bio_group_enabled(q)) {
+		ret = bio_group_controller(q, bio);
+		if (ret)
+			/* Either bio got buffered for bio_endio() done */
+			return 0;
+	}
+
 	down_read(&md->io_lock);
 
 	disk_stat_inc(dm_disk(md), ios[rw]);
@@ -1081,6 +1090,10 @@ static struct mapped_device *alloc_dev(i
 	md->queue->unplug_fn = dm_unplug_all;
 	blk_queue_merge_bvec(md->queue, dm_merge_bvec);
 
+	/* Initialize queue spin lock */
+	md->queue->queue_lock = &md->queue->__queue_lock;
+	spin_lock_init(md->queue->queue_lock);
+
 	md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache);
 	if (!md->io_pool)
 		goto bad_io_pool;
block/blk-core.c +9 -1
Index: linux17/block/blk-core.c
===================================================================
@@ -1117,10 +1117,18 @@ static int __make_request(struct request
 	int el_ret, nr_sectors, barrier, err;
 	const unsigned short prio = bio_prio(bio);
 	const int sync = bio_sync(bio);
-	int rw_flags;
+	int rw_flags, ret;
 
 	nr_sectors = bio_sectors(bio);
 
+	if (!bio_cgroup_disabled() && blk_queue_bio_group_enabled(q)) {
+		ret = bio_group_controller(q, bio);
+		if (ret) {
+			/* Either bio got buffered for bio_endio() done */
+			return 0;
+		}
+	}
+
 	/*
 	 * low level driver can indicate that it wants pages above a
 	 * certain limit bounced to low memory (ie for highmem, or even
#24 lore raw

PATCH 2/4 io controller: biocgroup implementation

In reply to [email protected]

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

include/linux/cgroup_subsys.h +6 -0
Index: linux17/include/linux/cgroup_subsys.h
===================================================================
@@ -43,6 +43,12 @@ SUBSYS(mem_cgroup)
 
 /* */
 
+#ifdef CONFIG_CGROUP_BIO
+SUBSYS(bio_cgroup)
+#endif
+
+/* */
+
 #ifdef CONFIG_CGROUP_DEVICE
 SUBSYS(devices)
 #endif
init/Kconfig +7 -0
Index: linux17/init/Kconfig
===================================================================
@@ -408,6 +408,13 @@ config CGROUP_MEM_RES_CTLR
 	  This config option also selects MM_OWNER config option, which
 	  could in turn add some fork/exit overhead.
 
+config CGROUP_BIO
+	bool "Block I/O cgroup subsystem"
+	depends on CGROUP_MEM_RES_CTLR
+	select MM_OWNER
+	help
+	  A generic proportinal weight IO controller.
+
 config SYSFS_DEPRECATED
 	bool
 
mm/biocontrol.c new file +409 -0

409 lines not on this page · raw message

include/linux/biocontrol.h new file +174 -0

174 lines not on this page · raw message

mm/Makefile +1 -0
Index: linux17/mm/Makefile
===================================================================
@@ -34,4 +34,5 @@ obj-$(CONFIG_MIGRATION) += migrate.o
 obj-$(CONFIG_SMP) += allocpercpu.o
 obj-$(CONFIG_QUICKLIST) += quicklist.o
 obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o
+obj-$(CONFIG_CGROUP_BIO) += biocontrol.o
 
mm/memcontrol.c +21 -48

171 lines not on this page · raw message

include/linux/memcontrol.h +46 -1

68 lines not on this page · raw message

A message that is not in the archive (sent off-list or not yet received): [email protected]

#26 lore raw

Re: [patch 2/4] io controller: biocgroup implementation

Quoted 5 lines · On Thu, 06 Nov 2008 10:30:24 -0500 [email protected]…

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

page_cgroup implementation is changed and most of this patch needs rework. please see the latest one. (I think most of new characteristics are useful for you.)

One comment from me is
==

include/linux/memcontrol.h line 59

Quoted 10 lines
include/linux/memcontrol.h +10 -0
+struct page_cgroup {
+	struct list_head lru;		/* per cgroup LRU list */
+	struct page *page;
+	struct mem_cgroup *mem_cgroup;
+	int flags;
+#ifdef CONFIG_CGROUP_BIO
+	struct list_head blist;		/* for bio_cgroup page list */
+	struct bio_cgroup *bio_cgroup;
+#endif
+};

==

this blist is too bad. please keep this object small...

Maybe dm-ioband people will post his own new one. just making use of it is an idea.

Thanks,
-Kame

#27 lore raw

Re: [patch 2/4] io controller: biocgroup implementation

In reply to [email protected]
Quoted 5 lines · On Thu, 06 Nov 2008 10:30:24 -0500 [email protected] wrote:

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

page_cgroup implementation is changed and most of this patch needs rework. please see the latest one. (I think most of new characteristics are useful for you.)

One comment from me is
==

include/linux/memcontrol.h line 59

Quoted 10 lines
include/linux/memcontrol.h +10 -0
+struct page_cgroup {
+	struct list_head lru;		/* per cgroup LRU list */
+	struct page *page;
+	struct mem_cgroup *mem_cgroup;
+	int flags;
+#ifdef CONFIG_CGROUP_BIO
+	struct list_head blist;		/* for bio_cgroup page list */
+	struct bio_cgroup *bio_cgroup;
+#endif
+};

==

this blist is too bad. please keep this object small...

Maybe dm-ioband people will post his own new one. just making use of it is an idea.

Thanks,
-Kame

#28 lore raw

Re: [patch 2/4] io controller: biocgroup implementation

In reply to [email protected]
Quoted 5 lines · On Thu, 06 Nov 2008 10:30:24 -0500 [email protected] wrote:

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

page_cgroup implementation is changed and most of this patch needs rework. please see the latest one. (I think most of new characteristics are useful for you.)

One comment from me is
==

include/linux/memcontrol.h line 59

Quoted 10 lines
include/linux/memcontrol.h +10 -0
+struct page_cgroup {
+	struct list_head lru;		/* per cgroup LRU list */
+	struct page *page;
+	struct mem_cgroup *mem_cgroup;
+	int flags;
+#ifdef CONFIG_CGROUP_BIO
+	struct list_head blist;		/* for bio_cgroup page list */
+	struct bio_cgroup *bio_cgroup;
+#endif
+};

==

this blist is too bad. please keep this object small...

Maybe dm-ioband people will post his own new one. just making use of it is an idea.

Thanks,
-Kame

A message that is not in the archive (sent off-list or not yet received): 20081107115030.7ccf3f07.kamezawa.hiroyu-+CUm20s59erQFUHtdCDX3A@public.gmane.org

#30 lore raw

Re: [patch 2/4] io controller: biocgroup implementation

Hi,

I'm going to release a new version of bio_cgroup soon, which doesn't have "struct list_head blist" anymore and whose overhead is minimized.

Quoted 30 lines
Quoted 4 lines

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

page_cgroup implementation is changed and most of this patch needs rework. please see the latest one. (I think most of new characteristics are useful for you.)

One comment from me is
==

include/linux/memcontrol.h line 59

Quoted 10 lines
include/linux/memcontrol.h +10 -0
+struct page_cgroup {
+	struct list_head lru;		/* per cgroup LRU list */
+	struct page *page;
+	struct mem_cgroup *mem_cgroup;
+	int flags;
+#ifdef CONFIG_CGROUP_BIO
+	struct list_head blist;		/* for bio_cgroup page list */
+	struct bio_cgroup *bio_cgroup;
+#endif
+};

==

this blist is too bad. please keep this object small...

Maybe dm-ioband people will post his own new one. just making use of it is an idea.

Thanks,
-Kame

#31 lore raw

Re: [patch 2/4] io controller: biocgroup implementation

In reply to a message not in the archive
Quoted 4 lines · KAMEZAWA Hiroyuki
Quoted 1 line · On Thu, 06 Nov 2008 10:30:24 -0500 [email protected]…
Quoted 8 lines
Quoted 4 lines

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

page_cgroup implementation is changed and most of this patch needs rework. please see the latest one. (I think most of new characteristics are useful for you.)

Sure I will have a look.

Quoted 14 lines

One comment from me is
==

include/linux/memcontrol.h line 59

Quoted 10 lines
include/linux/memcontrol.h +10 -0
+struct page_cgroup {
+	struct list_head lru;		/* per cgroup LRU list */
+	struct page *page;
+	struct mem_cgroup *mem_cgroup;
+	int flags;
+#ifdef CONFIG_CGROUP_BIO
+	struct list_head blist;		/* for bio_cgroup page list */
+	struct bio_cgroup *bio_cgroup;
+#endif
+};

==

Quoted 2 lines

this blist is too bad. please keep this object small...

This is just another connecting element so that page_cgroup can be on another list also. It is useful in making sure that IO on all the pages of a bio group has completed beofer that bio cgroup is deleted.

Quoted 1 line

Maybe dm-ioband people will post his own new one. just making use of it is an idea.

Sure, I will have a look when dm-ioband people post new version of patch and how they have optimized it further.

Thanks
Vivek

#32 lore raw

Re: [patch 2/4] io controller: biocgroup implementation

In reply to KAMEZAWA Hiroyuki

Hi,

I'm going to release a new version of bio_cgroup soon, which doesn't have "struct list_head blist" anymore and whose overhead is minimized.

Quoted 30 lines
Quoted 4 lines

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

page_cgroup implementation is changed and most of this patch needs rework. please see the latest one. (I think most of new characteristics are useful for you.)

One comment from me is
==

include/linux/memcontrol.h line 59

Quoted 10 lines
include/linux/memcontrol.h +10 -0
+struct page_cgroup {
+	struct list_head lru;		/* per cgroup LRU list */
+	struct page *page;
+	struct mem_cgroup *mem_cgroup;
+	int flags;
+#ifdef CONFIG_CGROUP_BIO
+	struct list_head blist;		/* for bio_cgroup page list */
+	struct bio_cgroup *bio_cgroup;
+#endif
+};

==

this blist is too bad. please keep this object small...

Maybe dm-ioband people will post his own new one. just making use of it is an idea.

Thanks,
-Kame

#33 lore raw

Re: [patch 2/4] io controller: biocgroup implementation

In reply to KAMEZAWA Hiroyuki

Hi,

I'm going to release a new version of bio_cgroup soon, which doesn't have "struct list_head blist" anymore and whose overhead is minimized.

Quoted 30 lines
Quoted 4 lines

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

page_cgroup implementation is changed and most of this patch needs rework. please see the latest one. (I think most of new characteristics are useful for you.)

One comment from me is
==

include/linux/memcontrol.h line 59

Quoted 10 lines
include/linux/memcontrol.h +10 -0
+struct page_cgroup {
+	struct list_head lru;		/* per cgroup LRU list */
+	struct page *page;
+	struct mem_cgroup *mem_cgroup;
+	int flags;
+#ifdef CONFIG_CGROUP_BIO
+	struct list_head blist;		/* for bio_cgroup page list */
+	struct bio_cgroup *bio_cgroup;
+#endif
+};

==

this blist is too bad. please keep this object small...

Maybe dm-ioband people will post his own new one. just making use of it is an idea.

Thanks,
-Kame

#34 lore raw

Re: [patch 2/4] io controller: biocgroup implementation

In reply to KAMEZAWA Hiroyuki
Quoted 4 lines · KAMEZAWA Hiroyuki
Quoted 1 line · On Thu, 06 Nov 2008 10:30:24 -0500 [email protected] wrote:
Quoted 8 lines
Quoted 4 lines

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

page_cgroup implementation is changed and most of this patch needs rework. please see the latest one. (I think most of new characteristics are useful for you.)

Sure I will have a look.

Quoted 14 lines

One comment from me is
==

include/linux/memcontrol.h line 59

Quoted 10 lines
include/linux/memcontrol.h +10 -0
+struct page_cgroup {
+	struct list_head lru;		/* per cgroup LRU list */
+	struct page *page;
+	struct mem_cgroup *mem_cgroup;
+	int flags;
+#ifdef CONFIG_CGROUP_BIO
+	struct list_head blist;		/* for bio_cgroup page list */
+	struct bio_cgroup *bio_cgroup;
+#endif
+};

==

Quoted 2 lines

this blist is too bad. please keep this object small...

This is just another connecting element so that page_cgroup can be on another list also. It is useful in making sure that IO on all the pages of a bio group has completed beofer that bio cgroup is deleted.

Quoted 1 line

Maybe dm-ioband people will post his own new one. just making use of it is an idea.

Sure, I will have a look when dm-ioband people post new version of patch and how they have optimized it further.

Thanks
Vivek

#35 lore raw

Re: [patch 2/4] io controller: biocgroup implementation

In reply to KAMEZAWA Hiroyuki
Quoted 4 lines · KAMEZAWA Hiroyuki
Quoted 1 line · On Thu, 06 Nov 2008 10:30:24 -0500 [email protected] wrote:
Quoted 8 lines
Quoted 4 lines

o biocgroup functionality.
o Implemented new controller "bio"
o Most of it picked from dm-ioband biocgroup implementation patches.

page_cgroup implementation is changed and most of this patch needs rework. please see the latest one. (I think most of new characteristics are useful for you.)

Sure I will have a look.

Quoted 14 lines

One comment from me is
==

include/linux/memcontrol.h line 59

Quoted 10 lines
include/linux/memcontrol.h +10 -0
+struct page_cgroup {
+	struct list_head lru;		/* per cgroup LRU list */
+	struct page *page;
+	struct mem_cgroup *mem_cgroup;
+	int flags;
+#ifdef CONFIG_CGROUP_BIO
+	struct list_head blist;		/* for bio_cgroup page list */
+	struct bio_cgroup *bio_cgroup;
+#endif
+};

==

Quoted 2 lines

this blist is too bad. please keep this object small...

This is just another connecting element so that page_cgroup can be on another list also. It is useful in making sure that IO on all the pages of a bio group has completed beofer that bio cgroup is deleted.

Quoted 1 line

Maybe dm-ioband people will post his own new one. just making use of it is an idea.

Sure, I will have a look when dm-ioband people post new version of patch and how they have optimized it further.

Thanks
Vivek

#36 lore raw

PATCH 3/4 io controller: Core IO controller implementation logic

In reply to [email protected]

o Core IO controller implementation

mm/biocontrol.c +704 -0
Index: linux2/mm/biocontrol.c
===================================================================
@@ -33,6 +33,7 @@
 #include <linux/err.h>
 #include <linux/biocontrol.h>
 
+void bio_group_inactive_timeout(unsigned long data);
 
 /* return corresponding bio_cgroup object of a cgroup */
 static inline struct bio_cgroup *cgroup_bio(struct cgroup *cgrp)
@@ -407,3 +408,706 @@ struct cgroup_subsys bio_cgroup_subsys =
 	.attach		= bio_cgroup_move_task,
 	.early_init	= 0,
 };
+
+struct bio_group* create_bio_group(struct bio_cgroup *biocg,
+						struct request_queue *q)
+{
+	unsigned long flags;
+	struct bio_group *biog = NULL;
+
+	biog = kzalloc(sizeof(struct bio_group), GFP_ATOMIC);
+	if (!biog)
+		return biog;
+
+	spin_lock_init(&biog->bio_group_lock);
+	biog->q = q;
+	biog->biocg = biocg;
+	INIT_LIST_HEAD(&biog->next);
+	biog->biog_inactive_timer.function = bio_group_inactive_timeout;
+	biog->biog_inactive_timer.data = (unsigned long)biog;
+	init_timer(&biog->biog_inactive_timer);
+	atomic_set(&biog->refcnt, 0);
+	spin_lock_irqsave(&biocg->biog_list_lock, flags);
+	list_add(&biog->next, &biocg->bio_group_list);
+	bio_group_get(biog);
+	spin_unlock_irqrestore(&biocg->biog_list_lock, flags);
+	return biog;
+}
+
+void* alloc_biog_io(void)
+{
+	return kzalloc(sizeof(struct biog_io), GFP_ATOMIC);
+}
+
+void free_biog_io(struct biog_io *biog_io)
+{
+	kfree(biog_io);
+}
+
+/*
+ * Upon succesful completion of bio, this function starts the inactive timer
+ * so that if a bio group stops contending for disk bandwidth, it is removed
+ * from the token allocation race.
+ */
+void biog_io_end(struct bio *bio, int error)
+{
+	struct biog_io *biog_io;
+	struct bio_group *biog;
+	unsigned long flags;
+	struct request_queue *q;
+
+	biog_io = bio->bi_private;
+	biog = biog_io->biog;
+	BUG_ON(!biog);
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	q = biog->q;
+	BUG_ON(!q);
+
+	/* Restore the original bio fields */
+	bio->bi_end_io = biog_io->bi_end_io;
+	bio->bi_private = biog_io->bi_private;
+
+	/* If bio group is still empty, then start the inactive timer */
+	if (bio_group_on_queue(biog) && bio_group_empty(biog)) {
+		mod_timer(&biog->biog_inactive_timer,
+			jiffies + msecs_to_jiffies(q->biogroup_idletime));
+		bio_group_flag_set(BIOG_FLAG_TIMER_ACTIVE, biog);
+	}
+
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	free_biog_io(biog_io);
+	bio_group_put(biog);
+	bio_endio(bio, error);
+}
+
+/* Calculate how many tokens should be allocated to new group based on
+ * the number of share/weight of this group and the number of tokens and
+ * load which is already present on the queue.
+ */
+unsigned long calculate_nr_tokens(struct bio_group *biog,
+					struct request_queue *q)
+{
+	unsigned long nr_tokens, total_slice;
+
+	total_slice = q->biogroup_deftoken * q->nr_biog;
+	nr_tokens = total_slice * biog->biocg->shares/q->total_weight;
+
+	BUG_ON(!nr_tokens);
+	return nr_tokens;
+}
+
+unsigned long alloc_bio_group_key(struct request_queue *q)
+{
+	unsigned long key = 0;
+
+	if (!q->bio_groups.rb.rb_node)
+		return key;
+
+	/* Insert element at the end of tree */
+	key = q->max_key + 1;
+	return key;
+}
+
+/*
+ * The below is leftmost cache rbtree addon
+ */
+struct bio_group *bio_group_rb_first(struct group_rb_root *root)
+{
+	if (!root->left)
+		root->left = rb_first(&root->rb);
+
+	if (root->left)
+		return rb_entry(root->left, struct bio_group, rb_node);
+
+	return NULL;
+}
+
+void remove_bio_group_from_rbtree(struct bio_group *biog,
+					struct request_queue *q)
+{
+	struct group_rb_root *root;
+	struct rb_node *n;
+
+	root = &q->bio_groups;
+	n = &biog->rb_node;
+
+	if (root->left == n)
+		root->left = NULL;
+
+	rb_erase(n, &root->rb);
+	RB_CLEAR_NODE(n);
+
+	if (bio_group_blocked(biog))
+		q->nr_biog_blocked--;
+
+	q->nr_biog--;
+	q->total_weight -= biog->biocg->shares;
+
+	if (!q->total_weight)
+		q->max_key = 0;
+}
+
+
+void insert_bio_group_into_rbtree(struct bio_group *biog,
+					struct request_queue *q)
+{
+	struct rb_node **p;
+	struct rb_node *parent = NULL;
+	struct bio_group *__biog;
+	int leftmost = 1;
+
+	/* Check if any element being inserted has key less than max key */
+	if (biog->key < q->max_key)
+		BUG();
+
+	p = &q->bio_groups.rb.rb_node;
+	while (*p) {
+		parent = *p;
+		__biog = rb_entry(parent, struct bio_group, rb_node);
+
+		/* Should equal key case be a warning? */
+		if (biog->key < __biog->key)
+			p = &(*p)->rb_left;
+		else {
+			p = &(*p)->rb_right;
+			leftmost = 0;
+		}
+	}
+
+	/* Cache the leftmost element */
+	if (leftmost)
+		q->bio_groups.left = &biog->rb_node;
+
+	rb_link_node(&biog->rb_node, parent, p);
+	rb_insert_color(&biog->rb_node, &q->bio_groups.rb);
+
+	/* Update the tokens and weight in request_queue */
+	q->nr_biog++;
+	q->total_weight += biog->biocg->shares;
+	q->max_key = biog->key;
+	if (bio_group_blocked(biog))
+		q->nr_biog_blocked++;
+}
+
+void queue_bio_group(struct bio_group *biog, struct request_queue *q)
+{
+	biog->key = alloc_bio_group_key(q);
+	/* Take another reference on biog. will be decremented once biog
+	 * is off the tree */
+	bio_group_get(biog);
+	insert_bio_group_into_rbtree(biog, q);
+	bio_group_flag_set(BIOG_FLAG_ON_QUEUE, biog);
+	bio_group_flag_clear(BIOG_FLAG_BLOCKED, biog);
+	biog->slice_stamp = q->current_slice;
+}
+
+void start_new_token_slice(struct request_queue *q)
+{
+	struct rb_node *n;
+	struct bio_group *biog = NULL;
+	struct group_rb_root *root;
+	unsigned long flags;
+
+	q->current_slice++;
+
+	/* Traverse the tree and reset the blocked count to zero of all the
+	 * biogs */
+
+	root = &q->bio_groups;
+
+	if (!root->left)
+		root->left = rb_first(&root->rb);
+
+	if (root->left)
+		biog = rb_entry(root->left, struct bio_group, rb_node);
+
+	if (!biog)
+		return;
+
+	n = &biog->rb_node;
+
+	/* Reset blocked count */
+	q->nr_biog_blocked = 0;
+	q->newslice_count++;
+
+	do {
+		biog = rb_entry(n, struct bio_group, rb_node);
+		spin_lock_irqsave(&biog->bio_group_lock, flags);
+		bio_group_flag_clear(BIOG_FLAG_BLOCKED, biog);
+		spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+		n = rb_next(n);
+	} while (n);
+
+}
+
+int should_start_new_token_slice(struct request_queue *q)
+{
+	/*
+	 * if all the biog on the queue are blocked, then start a new
+	 * token slice
+	 */
+	if (q->nr_biog_blocked == q->nr_biog)
+		return 1;
+	return 0;
+}
+
+int is_bio_group_blocked(struct bio_group *biog)
+{
+	unsigned long flags, status = 0;
+
+	/* Do I really need to lock bio group */
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+		if (bio_group_blocked(biog))
+			status = 1;
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return status;
+}
+
+int can_bio_group_dispatch(struct bio_group *biog, struct bio *bio)
+{
+	unsigned long temp = 0, flags;
+	struct request_queue *q;
+	long nr_sectors;
+	int can_dispatch = 0;
+
+	BUG_ON(!biog);
+	BUG_ON(!bio);
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	nr_sectors = bio_sectors(bio);
+	q = biog->q;
+
+	if (time_after(q->current_slice, biog->slice_stamp)) {
+		temp = calculate_nr_tokens(biog, q);
+		biog->credit_tokens += temp;
+		biog->slice_stamp = q->current_slice;
+		biog->biocg->nr_token_slices++;
+	}
+
+	if ((biog->credit_tokens > 0) && (biog->credit_tokens > nr_sectors)) {
+		if (bio_group_flag_test_and_clear(BIOG_FLAG_BLOCKED, biog))
+			q->nr_biog_blocked--;
+		can_dispatch = 1;
+		goto out;
+	}
+
+	if (!bio_group_flag_test_and_set(BIOG_FLAG_BLOCKED, biog))
+		q->nr_biog_blocked++;
+
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return can_dispatch;
+}
+
+/* Should be called without queue lock held */
+void bio_group_deactivate_timer(struct bio_group *biog)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	if (bio_group_flag_test_and_clear(BIOG_FLAG_TIMER_ACTIVE, biog)) {
+		/* Drop the bio group lock so that timer routine could
+		 * finish in case it fires */
+		spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+		del_timer_sync(&biog->biog_inactive_timer);
+		return;
+	}
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+}
+
+int attach_bio_group_io(struct bio_group *biog, struct bio *bio)
+{
+	int err = 0;
+	struct biog_io *biog_io;
+
+	biog_io = alloc_biog_io();
+	if (!biog_io) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* I already have a valid pointer to biog. So it should be ok
+	 * to get a reference to it. */
+	bio_group_get(biog);
+	biog_io->biog = biog;
+	biog_io->bi_end_io = bio->bi_end_io;
+	biog_io->bi_private = bio->bi_private;
+
+	bio->bi_end_io = biog_io_end;
+	bio->bi_private = biog_io;
+out:
+	return err;
+}
+
+int account_bio_to_bio_group(struct bio_group *biog, struct bio *bio)
+{
+	int err = 0;
+	unsigned long flags;
+	struct request_queue *q;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	err = attach_bio_group_io(biog, bio);
+	if (err)
+		goto out;
+
+	biog->nr_bio++;
+	q = biog->q;
+	if (!bio_group_on_queue(biog))
+		queue_bio_group(biog, q);
+
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return err;
+}
+
+int add_bio_to_bio_group_queue(struct bio_group *biog, struct bio *bio)
+{
+	unsigned long flags;
+	struct request_queue *q;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	__bio_group_queue_bio_tail(biog, bio);
+	q = biog->q;
+	q->nr_queued_bio++;
+	queue_delayed_work(q->biogroup_workqueue, &q->biogroup_work, 0);
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return 0;
+}
+
+/*
+ * It determines if the thread submitting the bio can itself continue to
+ * submit the bio or this bio needs to be buffered for later submission
+ */
+int can_biog_do_direct_dispatch(struct bio_group *biog)
+{
+	unsigned long flags, dispatch = 1;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	if (bio_group_blocked(biog)) {
+		dispatch = 0;
+		goto out;
+	}
+
+	/* Make sure there are not other queued bios on the biog. These
+	 * queued bios should get a chance to dispatch first */
+	if (!bio_group_queued_empty(biog))
+		dispatch = 0;
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return dispatch;
+}
+
+void charge_bio_group_for_tokens(struct bio_group *biog, struct bio *bio)
+{
+	unsigned long flags;
+	long dispatched_tokens;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	dispatched_tokens = bio_sectors(bio);
+	biog->nr_bio--;
+
+	biog->credit_tokens -= dispatched_tokens;
+
+	/* debug aid. also update aggregate tokens and jiffies in biocg */
+	biog->biocg->aggregate_tokens += dispatched_tokens;
+	biog->biocg->jiffies = jiffies;
+
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+}
+
+unsigned long __bio_group_try_to_dispatch(struct bio_group *biog,
+							struct bio *bio)
+{
+	struct request_queue *q;
+	int dispatched = 0;
+
+	BUG_ON(!biog);
+	BUG_ON(!bio);
+
+	q = biog->q;
+	BUG_ON(!q);
+retry:
+	if (!can_bio_group_dispatch(biog, bio)) {
+		if (should_start_new_token_slice(q)) {
+			start_new_token_slice(q);
+			goto retry;
+		}
+		goto out;
+	}
+
+	charge_bio_group_for_tokens(biog, bio);
+	dispatched = 1;
+out:
+	return dispatched;
+}
+
+unsigned long bio_group_try_to_dispatch(struct bio_group *biog, struct bio *bio)
+{
+	struct request_queue *q;
+	int dispatched = 0;
+	unsigned long flags;
+
+	q = biog->q;
+	BUG_ON(!q);
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	dispatched = __bio_group_try_to_dispatch(biog, bio);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+
+	return dispatched;
+}
+
+/* Should be called with queue lock and bio group lock held */
+void requeue_bio_group(struct request_queue *q, struct bio_group *biog)
+{
+	remove_bio_group_from_rbtree(biog, q);
+	biog->key = alloc_bio_group_key(q);
+	insert_bio_group_into_rbtree(biog, q);
+}
+
+/* Make a list of queued bios in this bio group which can be dispatched. */
+void make_release_bio_list(struct bio_group *biog,
+					struct bio_list *release_list)
+{
+	unsigned long flags, dispatched = 0;
+	struct bio *bio;
+	struct request_queue *q;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+
+	while (1) {
+		if (bio_group_queued_empty(biog))
+			goto out;
+
+		if (bio_group_blocked(biog))
+			goto out;
+
+		/* Dequeue one bio from bio group */
+		bio = __bio_group_dequeue_bio(biog);
+		BUG_ON(!bio);
+		q = biog->q;
+		q->nr_queued_bio--;
+
+		/* Releasing lock as try to dispatch will acquire it again */
+		spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+		dispatched = __bio_group_try_to_dispatch(biog, bio);
+		spin_lock_irqsave(&biog->bio_group_lock, flags);
+
+		if (dispatched) {
+			/* Add the bio to release list */
+			bio_list_add(release_list, bio);
+			continue;
+		} else {
+			/* Put the bio back into biog */
+			__bio_group_queue_bio_head(biog, bio);
+			q->nr_queued_bio++;
+			goto out;
+		}
+	}
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return;
+}
+
+/*
+ * If a bio group is inactive for q->inactive_timeout, then this group is
+ * considered to be no more contending for the disk bandwidth and removed
+ * from the tree.
+ */
+void bio_group_inactive_timeout(unsigned long data)
+{
+	struct bio_group *biog = (struct bio_group *)data;
+	unsigned long flags, flags1;
+	struct request_queue *q;
+
+	q = biog->q;
+	BUG_ON(!q);
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	spin_lock_irqsave(&biog->bio_group_lock, flags1);
+
+	BUG_ON(!bio_group_on_queue(biog));
+	BUG_ON(biog->nr_bio);
+
+	BUG_ON((biog->bio_group_flags > 7));
+	/* Remove biog from tree */
+	biog->biocg->nr_off_the_tree++;
+	remove_bio_group_from_rbtree(biog, q);
+	bio_group_flag_clear(BIOG_FLAG_ON_QUEUE, biog);
+	bio_group_flag_clear(BIOG_FLAG_BLOCKED, biog);
+	bio_group_flag_clear(BIOG_FLAG_TIMER_ACTIVE, biog);
+
+	/* dm_start_new_slice() takes bio_group_lock. Release it now */
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags1);
+
+	/* Also check if new slice should be started */
+	if ((q->nr_biog) && should_start_new_token_slice(q))
+		start_new_token_slice(q);
+
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	/* Drop the reference to biog */
+	bio_group_put(biog);
+	return;
+}
+
+/*
+ * It is called through worker thread and it takes care of releasing queued
+ * bios to underlying layer
+ */
+void bio_group_dispatch_queued_bio(struct request_queue *q)
+{
+	struct bio_group *biog;
+	unsigned long biog_scanned = 0;
+	unsigned long flags, flags1;
+	struct bio *bio = NULL;
+	int ret;
+	struct bio_list release_list;
+
+	bio_list_init(&release_list);
+
+	spin_lock_irqsave(q->queue_lock, flags);
+
+	while (1) {
+
+		if (!q->nr_biog)
+			goto out;
+
+		if (!q->nr_queued_bio)
+			goto out;
+
+		if (biog_scanned == q->nr_biog) {
+			/* Scanned the whole tree. No eligible biog found */
+			if (q->nr_queued_bio) {
+				queue_delayed_work(q->biogroup_workqueue,
+							&q->biogroup_work, 1);
+			}
+			goto out;
+		}
+
+		biog = bio_group_rb_first(&q->bio_groups);
+		BUG_ON(!biog);
+
+		make_release_bio_list(biog, &release_list);
+
+		/* If there are bios to dispatch, release these */
+		if (!bio_list_empty(&release_list)) {
+			if (q->nr_queued_bio)
+				queue_delayed_work(q->biogroup_workqueue,
+						&q->biogroup_work, 0);
+			goto dispatch_bio;
+		} else {
+			spin_lock_irqsave(&biog->bio_group_lock, flags1);
+			requeue_bio_group(q, biog);
+			biog_scanned++;
+			spin_unlock_irqrestore(&biog->bio_group_lock, flags1);
+			continue;
+		}
+	}
+
+dispatch_bio:
+		spin_unlock_irqrestore(q->queue_lock, flags);
+		bio = bio_list_pop(&release_list);
+		BUG_ON(!bio);
+
+		do {
+			/* Taint the bio with pass through flag */
+			bio->bi_flags |= (1UL << BIO_NOBIOGROUP);
+			do {
+				ret = q->make_request_fn(q, bio);
+			} while (ret);
+			bio = bio_list_pop(&release_list);
+		} while (bio);
+
+		return;
+out:
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	return;
+}
+
+void blk_biogroup_work(struct work_struct *work)
+{
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	struct request_queue *q =
+		container_of(dw, struct request_queue, biogroup_work);
+
+	bio_group_dispatch_queued_bio(q);
+}
+
+/*
+ * This is core IO controller function which tries to dispatch bios to
+ * underlying layers based on cgroup weights.
+ *
+ * If the cgroup bio belongs to has got sufficient tokens, submitting
+ * task/thread is allowed to continue to submit the bio otherwise, bio
+ * is buffered here and submitting thread returns. This buffered bio will
+ * be dispatched to lower layers when cgroup has sufficient tokens.
+ *
+ * Return code:
+ * 0 --> continue submit the bio
+ * 1---> bio buffered by bio group layer. return
+ */
+int bio_group_controller(struct request_queue *q, struct bio *bio)
+{
+
+	struct bio_group *biog;
+	struct bio_cgroup *biocg;
+	int err = 0;
+	unsigned long flags, dispatched = 0;
+
+	/* This bio has already been subjected to resource constraints.
+	 * Let it pass through unconditionally. */
+	if (bio_flagged(bio, BIO_NOBIOGROUP)) {
+		bio->bi_flags &= ~(1UL << BIO_NOBIOGROUP);
+		return 0;
+	}
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	biocg = bio_cgroup_from_bio(bio);
+	BUG_ON(!biocg);
+
+	/* If a biog is found, we also take a reference to it */
+	biog = bio_group_from_cgroup(biocg, q);
+	if (!biog) {
+		/* In case of success, returns with reference to biog */
+		biog = create_bio_group(biocg, q);
+		if (!biog) {
+			err = -ENOMEM;
+			goto end_io;
+		}
+	}
+
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	bio_group_deactivate_timer(biog);
+	spin_lock_irqsave(q->queue_lock, flags);
+
+	err = account_bio_to_bio_group(biog, bio);
+	if (err)
+		goto end_io;
+
+	if (!can_biog_do_direct_dispatch(biog)) {
+		add_bio_to_bio_group_queue(biog, bio);
+		goto buffered;
+	}
+
+	dispatched = __bio_group_try_to_dispatch(biog, bio);
+
+	if (!dispatched) {
+		add_bio_to_bio_group_queue(biog, bio);
+		goto buffered;
+	}
+
+	bio_group_put(biog);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	return 0;
+
+buffered:
+	bio_group_put(biog);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	return 1;
+end_io:
+	bio_group_put(biog);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	bio_endio(bio, err);
+	return 1;
+}
include/linux/bio.h +1 -0
Index: linux2/include/linux/bio.h
===================================================================
@@ -131,6 +131,7 @@ struct bio {
 #define BIO_BOUNCED	5	/* bio is a bounce bio */
 #define BIO_USER_MAPPED 6	/* contains user pages */
 #define BIO_EOPNOTSUPP	7	/* not supported */
+#define BIO_NOBIOGROUP 8       /* Don do bio group control on this bio */
 #define bio_flagged(bio, flag)	((bio)->bi_flags & (1 << (flag)))
 
 /*
block/genhd.c +122 -0

140 lines not on this page · raw message

include/linux/blkdev.h +34 -0
Index: linux2/include/linux/blkdev.h
===================================================================
@@ -289,6 +289,11 @@ struct blk_cmd_filter {
 	struct kobject kobj;
 };
 
+struct group_rb_root {
+	struct rb_root	rb;
+	struct rb_node	*left;
+};
+
 struct request_queue
 {
 	/*
@@ -298,6 +303,33 @@ struct request_queue
 	struct request		*last_merge;
 	elevator_t		*elevator;
 
+	/* rb-tree which contains all the contending bio groups */
+	struct group_rb_root	bio_groups;
+
+	/* Total number of bio_group currently on the request queue */
+	unsigned long		nr_biog;
+	unsigned long		current_slice;
+
+	struct workqueue_struct *biogroup_workqueue;
+	struct delayed_work     biogroup_work;
+	unsigned long		nr_queued_bio;
+
+	/* What's the idletime after which a bio group is considered idle and
+	 * considered no more contending for the bandwidth. */
+	unsigned long		biogroup_idletime;
+	unsigned long		biogroup_deftoken;
+
+	/* Number of biog which can't issue IO because they don't have
+	 * suffifiet tokens */
+	unsigned long		nr_biog_blocked;
+
+	/* Sum of weight of all the cgroups present on this queue */
+	unsigned long		total_weight;
+
+	/* Debug Aid */
+	unsigned long		max_key;
+	unsigned long		newslice_count;
+
 	/*
 	 * the queue request freelist, one for reads and one for writes
 	 */
@@ -421,6 +453,7 @@ struct request_queue
 #define QUEUE_FLAG_ELVSWITCH	8	/* don't use elevator, just do FIFO */
 #define QUEUE_FLAG_BIDI		9	/* queue supports bidi requests */
 #define QUEUE_FLAG_NOMERGES    10	/* disable merge attempts */
+#define QUEUE_FLAG_BIOG_ENABLED    11	/* bio group enabled */
 
 static inline int queue_is_locked(struct request_queue *q)
 {
@@ -527,6 +560,7 @@ enum {
 #define blk_queue_stopped(q)	test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags)
 #define blk_queue_nomerges(q)	test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags)
 #define blk_queue_flushing(q)	((q)->ordseq)
+#define blk_queue_bio_group_enabled(q)	test_bit(QUEUE_FLAG_BIOG_ENABLED, &(q)->queue_flags)
 
 #define blk_fs_request(rq)	((rq)->cmd_type == REQ_TYPE_FS)
 #define blk_pc_request(rq)	((rq)->cmd_type == REQ_TYPE_BLOCK_PC)
block/blk-core.c +15 -0
Index: linux2/block/blk-core.c
===================================================================
@@ -30,6 +30,7 @@
 #include <linux/cpu.h>
 #include <linux/blktrace_api.h>
 #include <linux/fault-inject.h>
+#include <linux/biocontrol.h>
 
 #include "blk.h"
 
@@ -502,6 +503,20 @@ struct request_queue *blk_alloc_queue_no
 	mutex_init(&q->sysfs_lock);
 	spin_lock_init(&q->__queue_lock);
 
+#ifdef CONFIG_CGROUP_BIO
+	/* Initialize default idle time */
+	q->biogroup_idletime = DEFAULT_IDLE_PERIOD;
+	q->biogroup_deftoken = DEFAULT_NR_TOKENS;
+
+	/* Also create biogroup worker threads. It needs to be conditional */
+	if (!bio_cgroup_disabled()) {
+		q->biogroup_workqueue = create_workqueue("biogroup");
+		if (!q->biogroup_workqueue)
+			panic("Failed to create biogroup\n");
+	}
+	INIT_DELAYED_WORK(&q->biogroup_work, blk_biogroup_work);
+#endif
+
 	return q;
 }
 EXPORT_SYMBOL(blk_alloc_queue_node);
include/linux/biocontrol.h +114 -0

132 lines not on this page · raw message

#37 lore raw

PATCH 3/4 io controller: Core IO controller implementation logic

In reply to [email protected]

o Core IO controller implementation

mm/biocontrol.c +704 -0
Index: linux2/mm/biocontrol.c
===================================================================
@@ -33,6 +33,7 @@
 #include <linux/err.h>
 #include <linux/biocontrol.h>
 
+void bio_group_inactive_timeout(unsigned long data);
 
 /* return corresponding bio_cgroup object of a cgroup */
 static inline struct bio_cgroup *cgroup_bio(struct cgroup *cgrp)
@@ -407,3 +408,706 @@ struct cgroup_subsys bio_cgroup_subsys =
 	.attach		= bio_cgroup_move_task,
 	.early_init	= 0,
 };
+
+struct bio_group* create_bio_group(struct bio_cgroup *biocg,
+						struct request_queue *q)
+{
+	unsigned long flags;
+	struct bio_group *biog = NULL;
+
+	biog = kzalloc(sizeof(struct bio_group), GFP_ATOMIC);
+	if (!biog)
+		return biog;
+
+	spin_lock_init(&biog->bio_group_lock);
+	biog->q = q;
+	biog->biocg = biocg;
+	INIT_LIST_HEAD(&biog->next);
+	biog->biog_inactive_timer.function = bio_group_inactive_timeout;
+	biog->biog_inactive_timer.data = (unsigned long)biog;
+	init_timer(&biog->biog_inactive_timer);
+	atomic_set(&biog->refcnt, 0);
+	spin_lock_irqsave(&biocg->biog_list_lock, flags);
+	list_add(&biog->next, &biocg->bio_group_list);
+	bio_group_get(biog);
+	spin_unlock_irqrestore(&biocg->biog_list_lock, flags);
+	return biog;
+}
+
+void* alloc_biog_io(void)
+{
+	return kzalloc(sizeof(struct biog_io), GFP_ATOMIC);
+}
+
+void free_biog_io(struct biog_io *biog_io)
+{
+	kfree(biog_io);
+}
+
+/*
+ * Upon succesful completion of bio, this function starts the inactive timer
+ * so that if a bio group stops contending for disk bandwidth, it is removed
+ * from the token allocation race.
+ */
+void biog_io_end(struct bio *bio, int error)
+{
+	struct biog_io *biog_io;
+	struct bio_group *biog;
+	unsigned long flags;
+	struct request_queue *q;
+
+	biog_io = bio->bi_private;
+	biog = biog_io->biog;
+	BUG_ON(!biog);
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	q = biog->q;
+	BUG_ON(!q);
+
+	/* Restore the original bio fields */
+	bio->bi_end_io = biog_io->bi_end_io;
+	bio->bi_private = biog_io->bi_private;
+
+	/* If bio group is still empty, then start the inactive timer */
+	if (bio_group_on_queue(biog) && bio_group_empty(biog)) {
+		mod_timer(&biog->biog_inactive_timer,
+			jiffies + msecs_to_jiffies(q->biogroup_idletime));
+		bio_group_flag_set(BIOG_FLAG_TIMER_ACTIVE, biog);
+	}
+
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	free_biog_io(biog_io);
+	bio_group_put(biog);
+	bio_endio(bio, error);
+}
+
+/* Calculate how many tokens should be allocated to new group based on
+ * the number of share/weight of this group and the number of tokens and
+ * load which is already present on the queue.
+ */
+unsigned long calculate_nr_tokens(struct bio_group *biog,
+					struct request_queue *q)
+{
+	unsigned long nr_tokens, total_slice;
+
+	total_slice = q->biogroup_deftoken * q->nr_biog;
+	nr_tokens = total_slice * biog->biocg->shares/q->total_weight;
+
+	BUG_ON(!nr_tokens);
+	return nr_tokens;
+}
+
+unsigned long alloc_bio_group_key(struct request_queue *q)
+{
+	unsigned long key = 0;
+
+	if (!q->bio_groups.rb.rb_node)
+		return key;
+
+	/* Insert element at the end of tree */
+	key = q->max_key + 1;
+	return key;
+}
+
+/*
+ * The below is leftmost cache rbtree addon
+ */
+struct bio_group *bio_group_rb_first(struct group_rb_root *root)
+{
+	if (!root->left)
+		root->left = rb_first(&root->rb);
+
+	if (root->left)
+		return rb_entry(root->left, struct bio_group, rb_node);
+
+	return NULL;
+}
+
+void remove_bio_group_from_rbtree(struct bio_group *biog,
+					struct request_queue *q)
+{
+	struct group_rb_root *root;
+	struct rb_node *n;
+
+	root = &q->bio_groups;
+	n = &biog->rb_node;
+
+	if (root->left == n)
+		root->left = NULL;
+
+	rb_erase(n, &root->rb);
+	RB_CLEAR_NODE(n);
+
+	if (bio_group_blocked(biog))
+		q->nr_biog_blocked--;
+
+	q->nr_biog--;
+	q->total_weight -= biog->biocg->shares;
+
+	if (!q->total_weight)
+		q->max_key = 0;
+}
+
+
+void insert_bio_group_into_rbtree(struct bio_group *biog,
+					struct request_queue *q)
+{
+	struct rb_node **p;
+	struct rb_node *parent = NULL;
+	struct bio_group *__biog;
+	int leftmost = 1;
+
+	/* Check if any element being inserted has key less than max key */
+	if (biog->key < q->max_key)
+		BUG();
+
+	p = &q->bio_groups.rb.rb_node;
+	while (*p) {
+		parent = *p;
+		__biog = rb_entry(parent, struct bio_group, rb_node);
+
+		/* Should equal key case be a warning? */
+		if (biog->key < __biog->key)
+			p = &(*p)->rb_left;
+		else {
+			p = &(*p)->rb_right;
+			leftmost = 0;
+		}
+	}
+
+	/* Cache the leftmost element */
+	if (leftmost)
+		q->bio_groups.left = &biog->rb_node;
+
+	rb_link_node(&biog->rb_node, parent, p);
+	rb_insert_color(&biog->rb_node, &q->bio_groups.rb);
+
+	/* Update the tokens and weight in request_queue */
+	q->nr_biog++;
+	q->total_weight += biog->biocg->shares;
+	q->max_key = biog->key;
+	if (bio_group_blocked(biog))
+		q->nr_biog_blocked++;
+}
+
+void queue_bio_group(struct bio_group *biog, struct request_queue *q)
+{
+	biog->key = alloc_bio_group_key(q);
+	/* Take another reference on biog. will be decremented once biog
+	 * is off the tree */
+	bio_group_get(biog);
+	insert_bio_group_into_rbtree(biog, q);
+	bio_group_flag_set(BIOG_FLAG_ON_QUEUE, biog);
+	bio_group_flag_clear(BIOG_FLAG_BLOCKED, biog);
+	biog->slice_stamp = q->current_slice;
+}
+
+void start_new_token_slice(struct request_queue *q)
+{
+	struct rb_node *n;
+	struct bio_group *biog = NULL;
+	struct group_rb_root *root;
+	unsigned long flags;
+
+	q->current_slice++;
+
+	/* Traverse the tree and reset the blocked count to zero of all the
+	 * biogs */
+
+	root = &q->bio_groups;
+
+	if (!root->left)
+		root->left = rb_first(&root->rb);
+
+	if (root->left)
+		biog = rb_entry(root->left, struct bio_group, rb_node);
+
+	if (!biog)
+		return;
+
+	n = &biog->rb_node;
+
+	/* Reset blocked count */
+	q->nr_biog_blocked = 0;
+	q->newslice_count++;
+
+	do {
+		biog = rb_entry(n, struct bio_group, rb_node);
+		spin_lock_irqsave(&biog->bio_group_lock, flags);
+		bio_group_flag_clear(BIOG_FLAG_BLOCKED, biog);
+		spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+		n = rb_next(n);
+	} while (n);
+
+}
+
+int should_start_new_token_slice(struct request_queue *q)
+{
+	/*
+	 * if all the biog on the queue are blocked, then start a new
+	 * token slice
+	 */
+	if (q->nr_biog_blocked == q->nr_biog)
+		return 1;
+	return 0;
+}
+
+int is_bio_group_blocked(struct bio_group *biog)
+{
+	unsigned long flags, status = 0;
+
+	/* Do I really need to lock bio group */
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+		if (bio_group_blocked(biog))
+			status = 1;
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return status;
+}
+
+int can_bio_group_dispatch(struct bio_group *biog, struct bio *bio)
+{
+	unsigned long temp = 0, flags;
+	struct request_queue *q;
+	long nr_sectors;
+	int can_dispatch = 0;
+
+	BUG_ON(!biog);
+	BUG_ON(!bio);
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	nr_sectors = bio_sectors(bio);
+	q = biog->q;
+
+	if (time_after(q->current_slice, biog->slice_stamp)) {
+		temp = calculate_nr_tokens(biog, q);
+		biog->credit_tokens += temp;
+		biog->slice_stamp = q->current_slice;
+		biog->biocg->nr_token_slices++;
+	}
+
+	if ((biog->credit_tokens > 0) && (biog->credit_tokens > nr_sectors)) {
+		if (bio_group_flag_test_and_clear(BIOG_FLAG_BLOCKED, biog))
+			q->nr_biog_blocked--;
+		can_dispatch = 1;
+		goto out;
+	}
+
+	if (!bio_group_flag_test_and_set(BIOG_FLAG_BLOCKED, biog))
+		q->nr_biog_blocked++;
+
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return can_dispatch;
+}
+
+/* Should be called without queue lock held */
+void bio_group_deactivate_timer(struct bio_group *biog)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	if (bio_group_flag_test_and_clear(BIOG_FLAG_TIMER_ACTIVE, biog)) {
+		/* Drop the bio group lock so that timer routine could
+		 * finish in case it fires */
+		spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+		del_timer_sync(&biog->biog_inactive_timer);
+		return;
+	}
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+}
+
+int attach_bio_group_io(struct bio_group *biog, struct bio *bio)
+{
+	int err = 0;
+	struct biog_io *biog_io;
+
+	biog_io = alloc_biog_io();
+	if (!biog_io) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* I already have a valid pointer to biog. So it should be ok
+	 * to get a reference to it. */
+	bio_group_get(biog);
+	biog_io->biog = biog;
+	biog_io->bi_end_io = bio->bi_end_io;
+	biog_io->bi_private = bio->bi_private;
+
+	bio->bi_end_io = biog_io_end;
+	bio->bi_private = biog_io;
+out:
+	return err;
+}
+
+int account_bio_to_bio_group(struct bio_group *biog, struct bio *bio)
+{
+	int err = 0;
+	unsigned long flags;
+	struct request_queue *q;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	err = attach_bio_group_io(biog, bio);
+	if (err)
+		goto out;
+
+	biog->nr_bio++;
+	q = biog->q;
+	if (!bio_group_on_queue(biog))
+		queue_bio_group(biog, q);
+
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return err;
+}
+
+int add_bio_to_bio_group_queue(struct bio_group *biog, struct bio *bio)
+{
+	unsigned long flags;
+	struct request_queue *q;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	__bio_group_queue_bio_tail(biog, bio);
+	q = biog->q;
+	q->nr_queued_bio++;
+	queue_delayed_work(q->biogroup_workqueue, &q->biogroup_work, 0);
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return 0;
+}
+
+/*
+ * It determines if the thread submitting the bio can itself continue to
+ * submit the bio or this bio needs to be buffered for later submission
+ */
+int can_biog_do_direct_dispatch(struct bio_group *biog)
+{
+	unsigned long flags, dispatch = 1;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	if (bio_group_blocked(biog)) {
+		dispatch = 0;
+		goto out;
+	}
+
+	/* Make sure there are not other queued bios on the biog. These
+	 * queued bios should get a chance to dispatch first */
+	if (!bio_group_queued_empty(biog))
+		dispatch = 0;
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return dispatch;
+}
+
+void charge_bio_group_for_tokens(struct bio_group *biog, struct bio *bio)
+{
+	unsigned long flags;
+	long dispatched_tokens;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+	dispatched_tokens = bio_sectors(bio);
+	biog->nr_bio--;
+
+	biog->credit_tokens -= dispatched_tokens;
+
+	/* debug aid. also update aggregate tokens and jiffies in biocg */
+	biog->biocg->aggregate_tokens += dispatched_tokens;
+	biog->biocg->jiffies = jiffies;
+
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+}
+
+unsigned long __bio_group_try_to_dispatch(struct bio_group *biog,
+							struct bio *bio)
+{
+	struct request_queue *q;
+	int dispatched = 0;
+
+	BUG_ON(!biog);
+	BUG_ON(!bio);
+
+	q = biog->q;
+	BUG_ON(!q);
+retry:
+	if (!can_bio_group_dispatch(biog, bio)) {
+		if (should_start_new_token_slice(q)) {
+			start_new_token_slice(q);
+			goto retry;
+		}
+		goto out;
+	}
+
+	charge_bio_group_for_tokens(biog, bio);
+	dispatched = 1;
+out:
+	return dispatched;
+}
+
+unsigned long bio_group_try_to_dispatch(struct bio_group *biog, struct bio *bio)
+{
+	struct request_queue *q;
+	int dispatched = 0;
+	unsigned long flags;
+
+	q = biog->q;
+	BUG_ON(!q);
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	dispatched = __bio_group_try_to_dispatch(biog, bio);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+
+	return dispatched;
+}
+
+/* Should be called with queue lock and bio group lock held */
+void requeue_bio_group(struct request_queue *q, struct bio_group *biog)
+{
+	remove_bio_group_from_rbtree(biog, q);
+	biog->key = alloc_bio_group_key(q);
+	insert_bio_group_into_rbtree(biog, q);
+}
+
+/* Make a list of queued bios in this bio group which can be dispatched. */
+void make_release_bio_list(struct bio_group *biog,
+					struct bio_list *release_list)
+{
+	unsigned long flags, dispatched = 0;
+	struct bio *bio;
+	struct request_queue *q;
+
+	spin_lock_irqsave(&biog->bio_group_lock, flags);
+
+	while (1) {
+		if (bio_group_queued_empty(biog))
+			goto out;
+
+		if (bio_group_blocked(biog))
+			goto out;
+
+		/* Dequeue one bio from bio group */
+		bio = __bio_group_dequeue_bio(biog);
+		BUG_ON(!bio);
+		q = biog->q;
+		q->nr_queued_bio--;
+
+		/* Releasing lock as try to dispatch will acquire it again */
+		spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+		dispatched = __bio_group_try_to_dispatch(biog, bio);
+		spin_lock_irqsave(&biog->bio_group_lock, flags);
+
+		if (dispatched) {
+			/* Add the bio to release list */
+			bio_list_add(release_list, bio);
+			continue;
+		} else {
+			/* Put the bio back into biog */
+			__bio_group_queue_bio_head(biog, bio);
+			q->nr_queued_bio++;
+			goto out;
+		}
+	}
+out:
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags);
+	return;
+}
+
+/*
+ * If a bio group is inactive for q->inactive_timeout, then this group is
+ * considered to be no more contending for the disk bandwidth and removed
+ * from the tree.
+ */
+void bio_group_inactive_timeout(unsigned long data)
+{
+	struct bio_group *biog = (struct bio_group *)data;
+	unsigned long flags, flags1;
+	struct request_queue *q;
+
+	q = biog->q;
+	BUG_ON(!q);
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	spin_lock_irqsave(&biog->bio_group_lock, flags1);
+
+	BUG_ON(!bio_group_on_queue(biog));
+	BUG_ON(biog->nr_bio);
+
+	BUG_ON((biog->bio_group_flags > 7));
+	/* Remove biog from tree */
+	biog->biocg->nr_off_the_tree++;
+	remove_bio_group_from_rbtree(biog, q);
+	bio_group_flag_clear(BIOG_FLAG_ON_QUEUE, biog);
+	bio_group_flag_clear(BIOG_FLAG_BLOCKED, biog);
+	bio_group_flag_clear(BIOG_FLAG_TIMER_ACTIVE, biog);
+
+	/* dm_start_new_slice() takes bio_group_lock. Release it now */
+	spin_unlock_irqrestore(&biog->bio_group_lock, flags1);
+
+	/* Also check if new slice should be started */
+	if ((q->nr_biog) && should_start_new_token_slice(q))
+		start_new_token_slice(q);
+
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	/* Drop the reference to biog */
+	bio_group_put(biog);
+	return;
+}
+
+/*
+ * It is called through worker thread and it takes care of releasing queued
+ * bios to underlying layer
+ */
+void bio_group_dispatch_queued_bio(struct request_queue *q)
+{
+	struct bio_group *biog;
+	unsigned long biog_scanned = 0;
+	unsigned long flags, flags1;
+	struct bio *bio = NULL;
+	int ret;
+	struct bio_list release_list;
+
+	bio_list_init(&release_list);
+
+	spin_lock_irqsave(q->queue_lock, flags);
+
+	while (1) {
+
+		if (!q->nr_biog)
+			goto out;
+
+		if (!q->nr_queued_bio)
+			goto out;
+
+		if (biog_scanned == q->nr_biog) {
+			/* Scanned the whole tree. No eligible biog found */
+			if (q->nr_queued_bio) {
+				queue_delayed_work(q->biogroup_workqueue,
+							&q->biogroup_work, 1);
+			}
+			goto out;
+		}
+
+		biog = bio_group_rb_first(&q->bio_groups);
+		BUG_ON(!biog);
+
+		make_release_bio_list(biog, &release_list);
+
+		/* If there are bios to dispatch, release these */
+		if (!bio_list_empty(&release_list)) {
+			if (q->nr_queued_bio)
+				queue_delayed_work(q->biogroup_workqueue,
+						&q->biogroup_work, 0);
+			goto dispatch_bio;
+		} else {
+			spin_lock_irqsave(&biog->bio_group_lock, flags1);
+			requeue_bio_group(q, biog);
+			biog_scanned++;
+			spin_unlock_irqrestore(&biog->bio_group_lock, flags1);
+			continue;
+		}
+	}
+
+dispatch_bio:
+		spin_unlock_irqrestore(q->queue_lock, flags);
+		bio = bio_list_pop(&release_list);
+		BUG_ON(!bio);
+
+		do {
+			/* Taint the bio with pass through flag */
+			bio->bi_flags |= (1UL << BIO_NOBIOGROUP);
+			do {
+				ret = q->make_request_fn(q, bio);
+			} while (ret);
+			bio = bio_list_pop(&release_list);
+		} while (bio);
+
+		return;
+out:
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	return;
+}
+
+void blk_biogroup_work(struct work_struct *work)
+{
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	struct request_queue *q =
+		container_of(dw, struct request_queue, biogroup_work);
+
+	bio_group_dispatch_queued_bio(q);
+}
+
+/*
+ * This is core IO controller function which tries to dispatch bios to
+ * underlying layers based on cgroup weights.
+ *
+ * If the cgroup bio belongs to has got sufficient tokens, submitting
+ * task/thread is allowed to continue to submit the bio otherwise, bio
+ * is buffered here and submitting thread returns. This buffered bio will
+ * be dispatched to lower layers when cgroup has sufficient tokens.
+ *
+ * Return code:
+ * 0 --> continue submit the bio
+ * 1---> bio buffered by bio group layer. return
+ */
+int bio_group_controller(struct request_queue *q, struct bio *bio)
+{
+
+	struct bio_group *biog;
+	struct bio_cgroup *biocg;
+	int err = 0;
+	unsigned long flags, dispatched = 0;
+
+	/* This bio has already been subjected to resource constraints.
+	 * Let it pass through unconditionally. */
+	if (bio_flagged(bio, BIO_NOBIOGROUP)) {
+		bio->bi_flags &= ~(1UL << BIO_NOBIOGROUP);
+		return 0;
+	}
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	biocg = bio_cgroup_from_bio(bio);
+	BUG_ON(!biocg);
+
+	/* If a biog is found, we also take a reference to it */
+	biog = bio_group_from_cgroup(biocg, q);
+	if (!biog) {
+		/* In case of success, returns with reference to biog */
+		biog = create_bio_group(biocg, q);
+		if (!biog) {
+			err = -ENOMEM;
+			goto end_io;
+		}
+	}
+
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	bio_group_deactivate_timer(biog);
+	spin_lock_irqsave(q->queue_lock, flags);
+
+	err = account_bio_to_bio_group(biog, bio);
+	if (err)
+		goto end_io;
+
+	if (!can_biog_do_direct_dispatch(biog)) {
+		add_bio_to_bio_group_queue(biog, bio);
+		goto buffered;
+	}
+
+	dispatched = __bio_group_try_to_dispatch(biog, bio);
+
+	if (!dispatched) {
+		add_bio_to_bio_group_queue(biog, bio);
+		goto buffered;
+	}
+
+	bio_group_put(biog);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	return 0;
+
+buffered:
+	bio_group_put(biog);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	return 1;
+end_io:
+	bio_group_put(biog);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	bio_endio(bio, err);
+	return 1;
+}
include/linux/bio.h +1 -0
Index: linux2/include/linux/bio.h
===================================================================
@@ -131,6 +131,7 @@ struct bio {
 #define BIO_BOUNCED	5	/* bio is a bounce bio */
 #define BIO_USER_MAPPED 6	/* contains user pages */
 #define BIO_EOPNOTSUPP	7	/* not supported */
+#define BIO_NOBIOGROUP 8       /* Don do bio group control on this bio */
 #define bio_flagged(bio, flag)	((bio)->bi_flags & (1 << (flag)))
 
 /*
block/genhd.c +122 -0

140 lines not on this page · raw message

include/linux/blkdev.h +34 -0
Index: linux2/include/linux/blkdev.h
===================================================================
@@ -289,6 +289,11 @@ struct blk_cmd_filter {
 	struct kobject kobj;
 };
 
+struct group_rb_root {
+	struct rb_root	rb;
+	struct rb_node	*left;
+};
+
 struct request_queue
 {
 	/*
@@ -298,6 +303,33 @@ struct request_queue
 	struct request		*last_merge;
 	elevator_t		*elevator;
 
+	/* rb-tree which contains all the contending bio groups */
+	struct group_rb_root	bio_groups;
+
+	/* Total number of bio_group currently on the request queue */
+	unsigned long		nr_biog;
+	unsigned long		current_slice;
+
+	struct workqueue_struct *biogroup_workqueue;
+	struct delayed_work     biogroup_work;
+	unsigned long		nr_queued_bio;
+
+	/* What's the idletime after which a bio group is considered idle and
+	 * considered no more contending for the bandwidth. */
+	unsigned long		biogroup_idletime;
+	unsigned long		biogroup_deftoken;
+
+	/* Number of biog which can't issue IO because they don't have
+	 * suffifiet tokens */
+	unsigned long		nr_biog_blocked;
+
+	/* Sum of weight of all the cgroups present on this queue */
+	unsigned long		total_weight;
+
+	/* Debug Aid */
+	unsigned long		max_key;
+	unsigned long		newslice_count;
+
 	/*
 	 * the queue request freelist, one for reads and one for writes
 	 */
@@ -421,6 +453,7 @@ struct request_queue
 #define QUEUE_FLAG_ELVSWITCH	8	/* don't use elevator, just do FIFO */
 #define QUEUE_FLAG_BIDI		9	/* queue supports bidi requests */
 #define QUEUE_FLAG_NOMERGES    10	/* disable merge attempts */
+#define QUEUE_FLAG_BIOG_ENABLED    11	/* bio group enabled */
 
 static inline int queue_is_locked(struct request_queue *q)
 {
@@ -527,6 +560,7 @@ enum {
 #define blk_queue_stopped(q)	test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags)
 #define blk_queue_nomerges(q)	test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags)
 #define blk_queue_flushing(q)	((q)->ordseq)
+#define blk_queue_bio_group_enabled(q)	test_bit(QUEUE_FLAG_BIOG_ENABLED, &(q)->queue_flags)
 
 #define blk_fs_request(rq)	((rq)->cmd_type == REQ_TYPE_FS)
 #define blk_pc_request(rq)	((rq)->cmd_type == REQ_TYPE_BLOCK_PC)
block/blk-core.c +15 -0
Index: linux2/block/blk-core.c
===================================================================
@@ -30,6 +30,7 @@
 #include <linux/cpu.h>
 #include <linux/blktrace_api.h>
 #include <linux/fault-inject.h>
+#include <linux/biocontrol.h>
 
 #include "blk.h"
 
@@ -502,6 +503,20 @@ struct request_queue *blk_alloc_queue_no
 	mutex_init(&q->sysfs_lock);
 	spin_lock_init(&q->__queue_lock);
 
+#ifdef CONFIG_CGROUP_BIO
+	/* Initialize default idle time */
+	q->biogroup_idletime = DEFAULT_IDLE_PERIOD;
+	q->biogroup_deftoken = DEFAULT_NR_TOKENS;
+
+	/* Also create biogroup worker threads. It needs to be conditional */
+	if (!bio_cgroup_disabled()) {
+		q->biogroup_workqueue = create_workqueue("biogroup");
+		if (!q->biogroup_workqueue)
+			panic("Failed to create biogroup\n");
+	}
+	INIT_DELAYED_WORK(&q->biogroup_work, blk_biogroup_work);
+#endif
+
 	return q;
 }
 EXPORT_SYMBOL(blk_alloc_queue_node);
include/linux/biocontrol.h +114 -0

132 lines not on this page · raw message

A message that is not in the archive (sent off-list or not yet received): [email protected]

#39 lore raw

Re: [patch 3/4] io controller: Core IO controller implementation logic

Quoted 5 lines · On Thu, 06 Nov 2008 10:30:25 -0500 [email protected]…

o Core IO controller implementation

2 comments after a quick look.

- I don't recommend generic work queue. More stacked dependency between "work" is not good. (I think disk-driver uses "work" for their jobs.)

- It seems this bio-cgroup can queue the bio to infinite. Then, a process can submit io unitl cause OOM.
(IIUC, Dirty bit of the page is cleared at submitting I/O.
Then dirty_ratio can't help us.)
please add "wait for congestion by sleeping" code in bio-cgroup.

Thanks,
-Kame

#41 lore raw

Re: [patch 3/4] io controller: Core IO controller implementation logic

In reply to [email protected]
Quoted 5 lines · On Thu, 06 Nov 2008 10:30:25 -0500 [email protected] wrote:

o Core IO controller implementation

2 comments after a quick look.

- I don't recommend generic work queue. More stacked dependency between "work" is not good. (I think disk-driver uses "work" for their jobs.)

- It seems this bio-cgroup can queue the bio to infinite. Then, a process can submit io unitl cause OOM.
(IIUC, Dirty bit of the page is cleared at submitting I/O.
Then dirty_ratio can't help us.)
please add "wait for congestion by sleeping" code in bio-cgroup.

Thanks,
-Kame

#42 lore raw

Re: [patch 3/4] io controller: Core IO controller implementation logic

In reply to [email protected]
Quoted 5 lines · On Thu, 06 Nov 2008 10:30:25 -0500 [email protected] wrote:

o Core IO controller implementation

2 comments after a quick look.

- I don't recommend generic work queue. More stacked dependency between "work" is not good. (I think disk-driver uses "work" for their jobs.)

- It seems this bio-cgroup can queue the bio to infinite. Then, a process can submit io unitl cause OOM.
(IIUC, Dirty bit of the page is cleared at submitting I/O.
Then dirty_ratio can't help us.)
please add "wait for congestion by sleeping" code in bio-cgroup.

Thanks,
-Kame

A message that is not in the archive (sent off-list or not yet received): 20081107122145.69500cd3.kamezawa.hiroyu-+CUm20s59erQFUHtdCDX3A@public.gmane.org

#44 lore raw

Re: [patch 3/4] io controller: Core IO controller implementation logic

Quoted 6 lines · KAMEZAWA Hiroyuki
Quoted 3 lines · On Thu, 06 Nov 2008 10:30:25 -0500 [email protected]…

o Core IO controller implementation

Quoted 7 lines
Quoted 2 lines

2 comments after a quick look.

- I don't recommend generic work queue. More stacked dependency between "work" is not good. (I think disk-driver uses "work" for their jobs.)

Sorry, I did not get this. Are you recommending that don't create a new work queue, instead use existing work queue (say kblockd) to submit the bios here?

I will look into it. I was little worried about a kblockd being overworked in case of too many logical devices enabling IO controller.

Quoted 6 lines

- It seems this bio-cgroup can queue the bio to infinite. Then, a process can submit io unitl cause OOM.
(IIUC, Dirty bit of the page is cleared at submitting I/O.
Then dirty_ratio can't help us.)
please add "wait for congestion by sleeping" code in bio-cgroup.

Yes, you are right. I need to put some kind of control on max number of bios I can queue on a cgroup and after crossing the limit, I should put the submitting task to sleep. (Something like request descriptor kind of flow control implememented by elevators).

Thanks
Vivek

#45 lore raw

Re: [patch 3/4] io controller: Core IO controller implementation logic

In reply to KAMEZAWA Hiroyuki
Quoted 6 lines · KAMEZAWA Hiroyuki
Quoted 3 lines · On Thu, 06 Nov 2008 10:30:25 -0500 [email protected] wrote:

o Core IO controller implementation

Quoted 7 lines
Quoted 2 lines

2 comments after a quick look.

- I don't recommend generic work queue. More stacked dependency between "work" is not good. (I think disk-driver uses "work" for their jobs.)

Sorry, I did not get this. Are you recommending that don't create a new work queue, instead use existing work queue (say kblockd) to submit the bios here?

I will look into it. I was little worried about a kblockd being overworked in case of too many logical devices enabling IO controller.

Quoted 6 lines

- It seems this bio-cgroup can queue the bio to infinite. Then, a process can submit io unitl cause OOM.
(IIUC, Dirty bit of the page is cleared at submitting I/O.
Then dirty_ratio can't help us.)
please add "wait for congestion by sleeping" code in bio-cgroup.

Yes, you are right. I need to put some kind of control on max number of bios I can queue on a cgroup and after crossing the limit, I should put the submitting task to sleep. (Something like request descriptor kind of flow control implememented by elevators).

Thanks
Vivek

#46 lore raw

Re: [patch 3/4] io controller: Core IO controller implementation logic

In reply to KAMEZAWA Hiroyuki
Quoted 6 lines · KAMEZAWA Hiroyuki
Quoted 3 lines · On Thu, 06 Nov 2008 10:30:25 -0500 [email protected] wrote:

o Core IO controller implementation

Quoted 7 lines
Quoted 2 lines

2 comments after a quick look.

- I don't recommend generic work queue. More stacked dependency between "work" is not good. (I think disk-driver uses "work" for their jobs.)

Sorry, I did not get this. Are you recommending that don't create a new work queue, instead use existing work queue (say kblockd) to submit the bios here?

I will look into it. I was little worried about a kblockd being overworked in case of too many logical devices enabling IO controller.

Quoted 6 lines

- It seems this bio-cgroup can queue the bio to infinite. Then, a process can submit io unitl cause OOM.
(IIUC, Dirty bit of the page is cleared at submitting I/O.
Then dirty_ratio can't help us.)
please add "wait for congestion by sleeping" code in bio-cgroup.

Yes, you are right. I need to put some kind of control on max number of bios I can queue on a cgroup and after crossing the limit, I should put the submitting task to sleep. (Something like request descriptor kind of flow control implememented by elevators).

Thanks
Vivek

A message that is not in the archive (sent off-list or not yet received): [email protected]

#48 lore raw

Re: [patch 3/4] io controller: Core IO controller implementationlogic

Quoted 12 lines · Vivek Goyal
Quoted 11 lines · KAMEZAWA Hiroyuki
Quoted 5 lines · On Thu, 06 Nov 2008 10:30:25 -0500 [email protected]…

o Core IO controller implementation

2 comments after a quick look.

Quoted 9 lines
Quoted 3 lines

- I don't recommend generic work queue. More stacked dependency between
"work"
is not good. (I think disk-driver uses "work" for their jobs.)

Sorry, I did not get this. Are you recommending that don't create a new work queue, instead use existing work queue (say kblockd) to submit the bios
here?

Ah, no, recomending new-original its own workqueue. I'm sorry that it seems I missed something at reading your patch.
(other person may have other opinion, here;)

Quoted 3 lines

I will look into it. I was little worried about a kblockd being overworked in case of too many logical devices enabling IO controller.

Thanks,
-Kame

#49 lore raw

Re: [patch 3/4] io controller: Core IO controller implementationlogic

In reply to Vivek Goyal
Quoted 12 lines · Vivek Goyal
Quoted 11 lines · KAMEZAWA Hiroyuki
Quoted 5 lines · On Thu, 06 Nov 2008 10:30:25 -0500 [email protected] wrote:

o Core IO controller implementation

2 comments after a quick look.

Quoted 9 lines
Quoted 3 lines

- I don't recommend generic work queue. More stacked dependency between
"work"
is not good. (I think disk-driver uses "work" for their jobs.)

Sorry, I did not get this. Are you recommending that don't create a new work queue, instead use existing work queue (say kblockd) to submit the bios
here?

Ah, no, recomending new-original its own workqueue. I'm sorry that it seems I missed something at reading your patch.
(other person may have other opinion, here;)

Quoted 3 lines

I will look into it. I was little worried about a kblockd being overworked in case of too many logical devices enabling IO controller.

Thanks,
-Kame

#50 lore raw

Re: [patch 3/4] io controller: Core IO controller implementationlogic

In reply to Vivek Goyal
Quoted 12 lines · Vivek Goyal
Quoted 11 lines · KAMEZAWA Hiroyuki
Quoted 5 lines · On Thu, 06 Nov 2008 10:30:25 -0500 [email protected] wrote:

o Core IO controller implementation

2 comments after a quick look.

Quoted 9 lines
Quoted 3 lines

- I don't recommend generic work queue. More stacked dependency between
"work"
is not good. (I think disk-driver uses "work" for their jobs.)

Sorry, I did not get this. Are you recommending that don't create a new work queue, instead use existing work queue (say kblockd) to submit the bios
here?

Ah, no, recomending new-original its own workqueue. I'm sorry that it seems I missed something at reading your patch.
(other person may have other opinion, here;)

Quoted 3 lines

I will look into it. I was little worried about a kblockd being overworked in case of too many logical devices enabling IO controller.

Thanks,
-Kame

#53 lore raw

PATCH 4/4 io controller: Put IO controller to use in device mapper and standard make_request() function

In reply to [email protected]

o Tweak standard make_request() function and also the device mapper make request functions to enable use of IO controller.

drivers/md/dm.c +13 -0
Index: linux17/drivers/md/dm.c
===================================================================
@@ -22,6 +22,7 @@
 #include <linux/hdreg.h>
 #include <linux/blktrace_api.h>
 #include <linux/smp_lock.h>
+#include <linux/biocontrol.h>
 
 #define DM_MSG_PREFIX "core"
 
@@ -885,6 +886,7 @@ static int dm_request(struct request_que
 	int r = -EIO;
 	int rw = bio_data_dir(bio);
 	struct mapped_device *md = q->queuedata;
+	int ret;
 
 	/*
 	 * There is no use in forwarding any barrier request since we can't
@@ -895,6 +897,13 @@ static int dm_request(struct request_que
 		return 0;
 	}
 
+	if (!bio_cgroup_disabled() && blk_queue_bio_group_enabled(q)) {
+		ret = bio_group_controller(q, bio);
+		if (ret)
+			/* Either bio got buffered for bio_endio() done */
+			return 0;
+	}
+
 	down_read(&md->io_lock);
 
 	disk_stat_inc(dm_disk(md), ios[rw]);
@@ -1081,6 +1090,10 @@ static struct mapped_device *alloc_dev(i
 	md->queue->unplug_fn = dm_unplug_all;
 	blk_queue_merge_bvec(md->queue, dm_merge_bvec);
 
+	/* Initialize queue spin lock */
+	md->queue->queue_lock = &md->queue->__queue_lock;
+	spin_lock_init(md->queue->queue_lock);
+
 	md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache);
 	if (!md->io_pool)
 		goto bad_io_pool;
block/blk-core.c +9 -1
Index: linux17/block/blk-core.c
===================================================================
@@ -1117,10 +1117,18 @@ static int __make_request(struct request
 	int el_ret, nr_sectors, barrier, err;
 	const unsigned short prio = bio_prio(bio);
 	const int sync = bio_sync(bio);
-	int rw_flags;
+	int rw_flags, ret;
 
 	nr_sectors = bio_sectors(bio);
 
+	if (!bio_cgroup_disabled() && blk_queue_bio_group_enabled(q)) {
+		ret = bio_group_controller(q, bio);
+		if (ret) {
+			/* Either bio got buffered for bio_endio() done */
+			return 0;
+		}
+	}
+
 	/*
 	 * low level driver can indicate that it wants pages above a
 	 * certain limit bounced to low memory (ie for highmem, or even
#54 lore raw

PATCH 4/4 io controller: Put IO controller to use in device mapper and standard make_request() function

In reply to [email protected]

o Tweak standard make_request() function and also the device mapper make request functions to enable use of IO controller.

drivers/md/dm.c +13 -0
Index: linux17/drivers/md/dm.c
===================================================================
@@ -22,6 +22,7 @@
 #include <linux/hdreg.h>
 #include <linux/blktrace_api.h>
 #include <linux/smp_lock.h>
+#include <linux/biocontrol.h>
 
 #define DM_MSG_PREFIX "core"
 
@@ -885,6 +886,7 @@ static int dm_request(struct request_que
 	int r = -EIO;
 	int rw = bio_data_dir(bio);
 	struct mapped_device *md = q->queuedata;
+	int ret;
 
 	/*
 	 * There is no use in forwarding any barrier request since we can't
@@ -895,6 +897,13 @@ static int dm_request(struct request_que
 		return 0;
 	}
 
+	if (!bio_cgroup_disabled() && blk_queue_bio_group_enabled(q)) {
+		ret = bio_group_controller(q, bio);
+		if (ret)
+			/* Either bio got buffered for bio_endio() done */
+			return 0;
+	}
+
 	down_read(&md->io_lock);
 
 	disk_stat_inc(dm_disk(md), ios[rw]);
@@ -1081,6 +1090,10 @@ static struct mapped_device *alloc_dev(i
 	md->queue->unplug_fn = dm_unplug_all;
 	blk_queue_merge_bvec(md->queue, dm_merge_bvec);
 
+	/* Initialize queue spin lock */
+	md->queue->queue_lock = &md->queue->__queue_lock;
+	spin_lock_init(md->queue->queue_lock);
+
 	md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache);
 	if (!md->io_pool)
 		goto bad_io_pool;
block/blk-core.c +9 -1
Index: linux17/block/blk-core.c
===================================================================
@@ -1117,10 +1117,18 @@ static int __make_request(struct request
 	int el_ret, nr_sectors, barrier, err;
 	const unsigned short prio = bio_prio(bio);
 	const int sync = bio_sync(bio);
-	int rw_flags;
+	int rw_flags, ret;
 
 	nr_sectors = bio_sectors(bio);
 
+	if (!bio_cgroup_disabled() && blk_queue_bio_group_enabled(q)) {
+		ret = bio_group_controller(q, bio);
+		if (ret) {
+			/* Either bio got buffered for bio_endio() done */
+			return 0;
+		}
+	}
+
 	/*
 	 * low level driver can indicate that it wants pages above a
 	 * certain limit bounced to low memory (ie for highmem, or even

A message that is not in the archive (sent off-list or not yet received): [email protected]

#56 lore raw
Quoted 18 lines · On Thu, 2008-11-06 at 10:30 -0500, [email protected]…

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

Quoted 6 lines

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

please include

QUILT_REFRESH_ARGS="--diffstat --strip-trailing-whitespace"

in your environment or .quiltrc

I would expect all those bio* files to be placed in block/ not mm/

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

#57 lore raw
In reply to a message not in the archive
Quoted 18 lines · [email protected] wrote:

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

Quoted 6 lines

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

Which kernel version is this patch set based on?

Quoted 4 lines

Thanks
Vivek

Signature · 2 lines
Regards
Gui Jianfeng
#58 lore raw
In reply to a message not in the archive

Hi,

From: [email protected]
Subject: [patch 0/4] [RFC] Another proportional weight IO controller
Date: Thu, 06 Nov 2008 10:30:22 -0500

Quoted 18 lines

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

Quoted 6 lines

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

Do you have any benchmark results?
I'm especially interested in the followings:
- Comparison of disk performance with and without the I/O controller patch.
- Put uneven I/O loads. Processes, which belong to a cgroup which is given a smaller weight than another cgroup, put heavier I/O load like the following.

     echo 1024 > /cgroup/bio/test1/bio.shares
     echo 8192 > /cgroup/bio/test2/bio.shares

     echo $$ > /cgroup/bio/test1/tasks
     dd if=/somefile1-1 of=/dev/null &
     dd if=/somefile1-2 of=/dev/null &
     ... 
     dd if=/somefile1-100 of=/dev/null
     echo $$ > /cgroup/bio/test2/tasks
     dd if=/somefile2-1 of=/dev/null &
     dd if=/somefile2-2 of=/dev/null &
     ...
     dd if=/somefile2-10 of=/dev/null &

Thanks,
Ryo Tsuruta

#59 lore raw
In reply to [email protected]
Quoted 18 lines · On Thu, 2008-11-06 at 10:30 -0500, [email protected] wrote:

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

Quoted 6 lines

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

please include

QUILT_REFRESH_ARGS="--diffstat --strip-trailing-whitespace"

in your environment or .quiltrc

I would expect all those bio* files to be placed in block/ not mm/

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

#60 lore raw
In reply to [email protected]
Quoted 18 lines · On Thu, 2008-11-06 at 10:30 -0500, [email protected] wrote:

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

Quoted 6 lines

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

please include

QUILT_REFRESH_ARGS="--diffstat --strip-trailing-whitespace"

in your environment or .quiltrc

I would expect all those bio* files to be placed in block/ not mm/

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

#61 lore raw
Quoted 24 lines · Peter Zijlstra
Quoted 23 lines · On Thu, 2008-11-06 at 10:30 -0500, [email protected]…

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Quoted 8 lines
Quoted 1 line

Your comments are welcome.

please include

QUILT_REFRESH_ARGS="--diffstat --strip-trailing-whitespace"

in your environment or .quiltrc

Sure, I will do. First time user of quilt. :-)

Quoted 2 lines

I would expect all those bio* files to be placed in block/ not mm/

Thinking more about it, probably block/ will be more appropriate place. I will do that.

Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Thanks
Vivek

#62 lore raw
In reply to Peter Zijlstra
Quoted 24 lines · Peter Zijlstra
Quoted 23 lines · On Thu, 2008-11-06 at 10:30 -0500, [email protected] wrote:

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Quoted 8 lines
Quoted 1 line

Your comments are welcome.

please include

QUILT_REFRESH_ARGS="--diffstat --strip-trailing-whitespace"

in your environment or .quiltrc

Sure, I will do. First time user of quilt. :-)

Quoted 2 lines

I would expect all those bio* files to be placed in block/ not mm/

Thinking more about it, probably block/ will be more appropriate place. I will do that.

Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Thanks
Vivek

#63 lore raw
In reply to Peter Zijlstra
Quoted 24 lines · Peter Zijlstra
Quoted 23 lines · On Thu, 2008-11-06 at 10:30 -0500, [email protected] wrote:

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Quoted 8 lines
Quoted 1 line

Your comments are welcome.

please include

QUILT_REFRESH_ARGS="--diffstat --strip-trailing-whitespace"

in your environment or .quiltrc

Sure, I will do. First time user of quilt. :-)

Quoted 2 lines

I would expect all those bio* files to be placed in block/ not mm/

Thinking more about it, probably block/ will be more appropriate place. I will do that.

Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Thanks
Vivek

A message that is not in the archive (sent off-list or not yet received): [email protected]

#65 lore raw
Quoted 8 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Quoted 8 lines

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

Could we make the io-schedulers aware of this hierarchy?

#66 lore raw
In reply to Vivek Goyal
Quoted 8 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Quoted 8 lines

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

Could we make the io-schedulers aware of this hierarchy?

#67 lore raw
In reply to Vivek Goyal
Quoted 8 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Quoted 8 lines

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

Could we make the io-schedulers aware of this hierarchy?

#68 lore raw
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Thanks
Vivek

#69 lore raw
In reply to Peter Zijlstra
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Thanks
Vivek

#70 lore raw
In reply to Peter Zijlstra
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Thanks
Vivek

#71 lore raw
Quoted 60 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 1 line
Quoted 4 lines
Quoted 1 line

Could we make the io-schedulers aware of this hierarchy?

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

#72 lore raw
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Signature · 1 line
All rights reversed.
#73 lore raw
In reply to Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Signature · 1 line
All rights reversed.
#74 lore raw
In reply to Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Signature · 1 line
All rights reversed.

A message that is not in the archive (sent off-list or not yet received): [email protected]

#76 lore raw
Quoted 10 lines · Rik van Riel
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

#77 lore raw
In reply to Rik van Riel
Quoted 10 lines · Rik van Riel
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

#78 lore raw
In reply to Rik van Riel
Quoted 10 lines · Rik van Riel
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

#79 lore raw
Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Rik van Riel
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Quoted 8 lines
Quoted 5 lines

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

In what way are they risky?

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#80 lore raw
In reply to Peter Zijlstra
Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Rik van Riel
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Quoted 8 lines
Quoted 5 lines

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

In what way are they risky?

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#81 lore raw
In reply to Peter Zijlstra
Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Rik van Riel
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Quoted 8 lines
Quoted 5 lines

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

In what way are they risky?

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#82 lore raw
Quoted 9 lines · Dave Chinner
Quoted 8 lines · Peter Zijlstra
Quoted 7 lines · Rik van Riel
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

In what way are they risky?

You loose all your data when one disk dies, so your mtbf decreases with the number of disks in your linear span.

And you get non of the benefits from having multiple disks, like extra speed from striping, or redundancy from raid.

Therefore I say that linear volumes are the absolute worst choice.

#83 lore raw
In reply to Dave Chinner
Quoted 9 lines · Dave Chinner
Quoted 8 lines · Peter Zijlstra
Quoted 7 lines · Rik van Riel
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

In what way are they risky?

You loose all your data when one disk dies, so your mtbf decreases with the number of disks in your linear span.

And you get non of the benefits from having multiple disks, like extra speed from striping, or redundancy from raid.

Therefore I say that linear volumes are the absolute worst choice.

#84 lore raw
In reply to Dave Chinner
Quoted 9 lines · Dave Chinner
Quoted 8 lines · Peter Zijlstra
Quoted 7 lines · Rik van Riel
Quoted 2 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

In what way are they risky?

You loose all your data when one disk dies, so your mtbf decreases with the number of disks in your linear span.

And you get non of the benefits from having multiple disks, like extra speed from striping, or redundancy from raid.

Therefore I say that linear volumes are the absolute worst choice.

#85 lore raw
Quoted 18 lines · Peter Zijlstra
Quoted 17 lines · Dave Chinner
Quoted 15 lines · Peter Zijlstra
Quoted 11 lines · Rik van Riel
Quoted 3 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

Quoted 6 lines
Quoted 1 line

In what way are they risky?

You loose all your data when one disk dies, so your mtbf decreases with the number of disks in your linear span. And you get non of the benefits from having multiple disks, like extra speed from striping, or redundancy from raid.

Fmeh. Step back and think for a moment. How does every major distro build redundant root drives?

Yeah, they build a mirror and then put LVM on top of the mirror to partition it. Each partition is a *linear volume*, but no single disk failure is going to lose data because it's been put on top of a mirror.

IOWs, reliability of linear volumes is only an issue if you don't build redundancy into your storage stack. Just like RAID0, a single disk failure will lose data. So, most people use linear volumes on top of RAID1 or RAID5 to avoid such a single disk failure problem. People do the same thing with RAID0 - it's what RAID10 and RAID50 do....

Also, linear volume performance scalability is on a different axis to striping. Striping improves bandwidth, but each disk in a stripe tends to make the same head movements. Hence striping improves sequential throughput but only provides limited iops scalability. Effectively, striping only improves throughput while the disks are not seeking a lot. Add a few parallel I/O streams, and a stripe will start to slow down as each disk seeks between streams. i.e. disks in stripes cannot be considered to be able to operate independently.

Linear voulmes create independent regions within the address space - the regions can seek independently when under concurrent I/O and hence iops scalability is much greater. Aggregate bandwidth is the same a striping, it's just that a single stream is limited in throughput. If you want to improve single stream throughput, you stripe before you concatenate.

That's why people create layered storage systems like this:

	linear volume
	  |->stripe
	      |-> md RAID5
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
	      |-> md RAID5
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
	  |->stripe
	      |-> md RAID5
	      ......
	  |->stripe
	  ......

What you then need is a filesystem that can spread the load over such a layout. Lets use, for argument's sake, XFS and tell it the geometry of the RAID5 luns that make up the volume so that it's allocation is all nicely aligned. Then we match the allocation group size to the size of each independent part of the linear volume. Now when XFS spreads it's inodes and data over multiple AGs, it's spreading the load across disks that can operate concurrently....

Effectively, linear volumes are about as dangerous as striping. If you don't build in redundancy at a level below the linear volume or stripe, then you lose when something fails.

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#86 lore raw
In reply to Peter Zijlstra
Quoted 18 lines · Peter Zijlstra
Quoted 17 lines · Dave Chinner
Quoted 15 lines · Peter Zijlstra
Quoted 11 lines · Rik van Riel
Quoted 3 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

Quoted 6 lines
Quoted 1 line

In what way are they risky?

You loose all your data when one disk dies, so your mtbf decreases with the number of disks in your linear span. And you get non of the benefits from having multiple disks, like extra speed from striping, or redundancy from raid.

Fmeh. Step back and think for a moment. How does every major distro build redundant root drives?

Yeah, they build a mirror and then put LVM on top of the mirror to partition it. Each partition is a *linear volume*, but no single disk failure is going to lose data because it's been put on top of a mirror.

IOWs, reliability of linear volumes is only an issue if you don't build redundancy into your storage stack. Just like RAID0, a single disk failure will lose data. So, most people use linear volumes on top of RAID1 or RAID5 to avoid such a single disk failure problem. People do the same thing with RAID0 - it's what RAID10 and RAID50 do....

Also, linear volume performance scalability is on a different axis to striping. Striping improves bandwidth, but each disk in a stripe tends to make the same head movements. Hence striping improves sequential throughput but only provides limited iops scalability. Effectively, striping only improves throughput while the disks are not seeking a lot. Add a few parallel I/O streams, and a stripe will start to slow down as each disk seeks between streams. i.e. disks in stripes cannot be considered to be able to operate independently.

Linear voulmes create independent regions within the address space - the regions can seek independently when under concurrent I/O and hence iops scalability is much greater. Aggregate bandwidth is the same a striping, it's just that a single stream is limited in throughput. If you want to improve single stream throughput, you stripe before you concatenate.

That's why people create layered storage systems like this:

	linear volume
	  |->stripe
	      |-> md RAID5
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
	      |-> md RAID5
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
	  |->stripe
	      |-> md RAID5
	      ......
	  |->stripe
	  ......

What you then need is a filesystem that can spread the load over such a layout. Lets use, for argument's sake, XFS and tell it the geometry of the RAID5 luns that make up the volume so that it's allocation is all nicely aligned. Then we match the allocation group size to the size of each independent part of the linear volume. Now when XFS spreads it's inodes and data over multiple AGs, it's spreading the load across disks that can operate concurrently....

Effectively, linear volumes are about as dangerous as striping. If you don't build in redundancy at a level below the linear volume or stripe, then you lose when something fails.

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#87 lore raw
In reply to Peter Zijlstra
Quoted 18 lines · Peter Zijlstra
Quoted 17 lines · Dave Chinner
Quoted 15 lines · Peter Zijlstra
Quoted 11 lines · Rik van Riel
Quoted 3 lines · Peter Zijlstra

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

Linear volumes may well be the most common ones.

People start out with the filesystems at a certain size, increasing onto a second (new) disk later, when more space is required.

Are they aware of how risky linear volumes are? I would discourage anyone from using them.

Quoted 6 lines
Quoted 1 line

In what way are they risky?

You loose all your data when one disk dies, so your mtbf decreases with the number of disks in your linear span. And you get non of the benefits from having multiple disks, like extra speed from striping, or redundancy from raid.

Fmeh. Step back and think for a moment. How does every major distro build redundant root drives?

Yeah, they build a mirror and then put LVM on top of the mirror to partition it. Each partition is a *linear volume*, but no single disk failure is going to lose data because it's been put on top of a mirror.

IOWs, reliability of linear volumes is only an issue if you don't build redundancy into your storage stack. Just like RAID0, a single disk failure will lose data. So, most people use linear volumes on top of RAID1 or RAID5 to avoid such a single disk failure problem. People do the same thing with RAID0 - it's what RAID10 and RAID50 do....

Also, linear volume performance scalability is on a different axis to striping. Striping improves bandwidth, but each disk in a stripe tends to make the same head movements. Hence striping improves sequential throughput but only provides limited iops scalability. Effectively, striping only improves throughput while the disks are not seeking a lot. Add a few parallel I/O streams, and a stripe will start to slow down as each disk seeks between streams. i.e. disks in stripes cannot be considered to be able to operate independently.

Linear voulmes create independent regions within the address space - the regions can seek independently when under concurrent I/O and hence iops scalability is much greater. Aggregate bandwidth is the same a striping, it's just that a single stream is limited in throughput. If you want to improve single stream throughput, you stripe before you concatenate.

That's why people create layered storage systems like this:

	linear volume
	  |->stripe
	      |-> md RAID5
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
	      |-> md RAID5
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
		   |-> disk
	  |->stripe
	      |-> md RAID5
	      ......
	  |->stripe
	  ......

What you then need is a filesystem that can spread the load over such a layout. Lets use, for argument's sake, XFS and tell it the geometry of the RAID5 luns that make up the volume so that it's allocation is all nicely aligned. Then we match the allocation group size to the size of each independent part of the linear volume. Now when XFS spreads it's inodes and data over multiple AGs, it's spreading the load across disks that can operate concurrently....

Effectively, linear volumes are about as dangerous as striping. If you don't build in redundancy at a level below the linear volume or stripe, then you lose when something fails.

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#88 lore raw
In reply to Peter Zijlstra
Quoted 63 lines · Peter Zijlstra
Quoted 62 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

#89 lore raw
In reply to Peter Zijlstra
Quoted 63 lines · Peter Zijlstra
Quoted 62 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

#90 lore raw
In reply to Peter Zijlstra
Quoted 63 lines · Peter Zijlstra
Quoted 62 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

A message that is not in the archive (sent off-list or not yet received): [email protected]

#92 lore raw

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.
(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Thanks.
--
Nauman

Quoted 104 lines · Vivek Goyal
Quoted 63 lines · Peter Zijlstra
Quoted 62 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#93 lore raw
In reply to Vivek Goyal

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.
(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Thanks.
--
Nauman

Quoted 104 lines · Vivek Goyal
Quoted 63 lines · Peter Zijlstra
Quoted 62 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#94 lore raw
In reply to Vivek Goyal

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.
(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Thanks.
--
Nauman

Quoted 104 lines · Vivek Goyal
Quoted 63 lines · Peter Zijlstra
Quoted 62 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#95 lore raw
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 9 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 10 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

Quoted 8 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Thanks
Vivek

Quoted 109 lines

Thanks.
--
Nauman

Quoted 104 lines · Vivek Goyal
Quoted 63 lines · Peter Zijlstra
Quoted 62 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

A message that is not in the archive (sent off-list or not yet received): [email protected]

#97 lore raw
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Quoted 14 lines
Quoted 10 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

Thanks.
--
Nauman

Quoted 114 lines

Thanks
Vivek

Quoted 109 lines

Thanks.
--
Nauman

Quoted 104 lines · Vivek Goyal
Quoted 63 lines · Peter Zijlstra
Quoted 62 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#98 lore raw
In reply to Vivek Goyal
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Quoted 14 lines
Quoted 10 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

Thanks.
--
Nauman

Quoted 114 lines

Thanks
Vivek

Quoted 109 lines

Thanks.
--
Nauman

Quoted 104 lines · Vivek Goyal
Quoted 63 lines · Peter Zijlstra
Quoted 62 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#99 lore raw
In reply to Vivek Goyal
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Quoted 14 lines
Quoted 10 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

Thanks.
--
Nauman

Quoted 114 lines

Thanks
Vivek

Quoted 109 lines

Thanks.
--
Nauman

Quoted 104 lines · Vivek Goyal
Quoted 63 lines · Peter Zijlstra
Quoted 62 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

A message that is not in the archive (sent off-list or not yet received): e98e18940811071336n58a073d8w2cbaeddd5657d1e9-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#101 lore raw
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

Thanks
Vivek

Quoted 107 lines
Quoted 107 lines
Quoted 106 lines · Vivek Goyal
Quoted 104 lines · Peter Zijlstra
Quoted 63 lines · Vivek Goyal
Quoted 62 lines · Peter Zijlstra
Quoted 13 lines · Vivek Goyal
Quoted 12 lines
Quoted 3 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#102 lore raw
In reply to Nauman Rafique
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

Thanks
Vivek

Quoted 107 lines
Quoted 107 lines
Quoted 106 lines · Vivek Goyal
Quoted 104 lines · Peter Zijlstra
Quoted 63 lines · Vivek Goyal
Quoted 62 lines · Peter Zijlstra
Quoted 13 lines · Vivek Goyal
Quoted 12 lines
Quoted 3 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#103 lore raw
In reply to Nauman Rafique
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

Thanks
Vivek

Quoted 107 lines
Quoted 107 lines
Quoted 106 lines · Vivek Goyal
Quoted 104 lines · Peter Zijlstra
Quoted 63 lines · Vivek Goyal
Quoted 62 lines · Peter Zijlstra
Quoted 13 lines · Vivek Goyal
Quoted 12 lines
Quoted 3 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

A message that is not in the archive (sent off-list or not yet received): [email protected]

#105 lore raw
Quoted 137 lines · Vivek Goyal
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

Quoted 6 lines

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

What do you think about elevator based solutions like 2 level cfq patches submitted by Satoshi and Vasily earlier? CFQ can be trivially modified to do proportional division (i.e give time slices in proportion to weight instead of priority). And such a solution would avoid idleness problem like the one you mentioned above and can also avoid burstiness issues (see smoothing patches -- v1.2.0 and v1.3.0 -- of dm-ioband) in token based schemes.

Also doing time based token allocation (as you mentioned in TODO list) sounds very interesting. Can we look at the disk time taken by each bio and use that to account for tokens? The problem is that the time taken is not available when the requests are sent to disk, but we can do delayed token charging (i.e deduct tokens after the request is completed?). It seems that such an approach should work. What do you think?

Quoted 112 lines

Thanks
Vivek

Quoted 107 lines
Quoted 107 lines
Quoted 106 lines · Vivek Goyal
Quoted 104 lines · Peter Zijlstra
Quoted 63 lines · Vivek Goyal
Quoted 62 lines · Peter Zijlstra
Quoted 13 lines · Vivek Goyal
Quoted 12 lines
Quoted 3 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#106 lore raw
In reply to Vivek Goyal
Quoted 137 lines · Vivek Goyal
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

Quoted 6 lines

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

What do you think about elevator based solutions like 2 level cfq patches submitted by Satoshi and Vasily earlier? CFQ can be trivially modified to do proportional division (i.e give time slices in proportion to weight instead of priority). And such a solution would avoid idleness problem like the one you mentioned above and can also avoid burstiness issues (see smoothing patches -- v1.2.0 and v1.3.0 -- of dm-ioband) in token based schemes.

Also doing time based token allocation (as you mentioned in TODO list) sounds very interesting. Can we look at the disk time taken by each bio and use that to account for tokens? The problem is that the time taken is not available when the requests are sent to disk, but we can do delayed token charging (i.e deduct tokens after the request is completed?). It seems that such an approach should work. What do you think?

Quoted 112 lines

Thanks
Vivek

Quoted 107 lines
Quoted 107 lines
Quoted 106 lines · Vivek Goyal
Quoted 104 lines · Peter Zijlstra
Quoted 63 lines · Vivek Goyal
Quoted 62 lines · Peter Zijlstra
Quoted 13 lines · Vivek Goyal
Quoted 12 lines
Quoted 3 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#107 lore raw
In reply to Vivek Goyal
Quoted 137 lines · Vivek Goyal
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

Quoted 6 lines

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

What do you think about elevator based solutions like 2 level cfq patches submitted by Satoshi and Vasily earlier? CFQ can be trivially modified to do proportional division (i.e give time slices in proportion to weight instead of priority). And such a solution would avoid idleness problem like the one you mentioned above and can also avoid burstiness issues (see smoothing patches -- v1.2.0 and v1.3.0 -- of dm-ioband) in token based schemes.

Also doing time based token allocation (as you mentioned in TODO list) sounds very interesting. Can we look at the disk time taken by each bio and use that to account for tokens? The problem is that the time taken is not available when the requests are sent to disk, but we can do delayed token charging (i.e deduct tokens after the request is completed?). It seems that such an approach should work. What do you think?

Quoted 112 lines

Thanks
Vivek

Quoted 107 lines
Quoted 107 lines
Quoted 106 lines · Vivek Goyal
Quoted 104 lines · Peter Zijlstra
Quoted 63 lines · Vivek Goyal
Quoted 62 lines · Peter Zijlstra
Quoted 13 lines · Vivek Goyal
Quoted 12 lines
Quoted 3 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

A message that is not in the archive (sent off-list or not yet received): e98e18940811111155q4bd73480pebe088fa1adbe2e4-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#109 lore raw
Quoted 145 lines · Nauman Rafique
Quoted 137 lines · Vivek Goyal
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

Quoted 6 lines

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

Quoted 2 lines

What do you think about elevator based solutions like 2 level cfq patches submitted by Satoshi and Vasily earlier?

I have had a very high level look at Satoshi's patch. I will go into details soon. I was thinking that this patch solves the problem only for CFQ. Can we create a common layer which can be shared by all the four IO schedulers.

So this one common layer can take care of all the management w.r.t per device per cgroup data structures and track all the groups, their limits (either token based or time based scheme), and control the dispatch of requests.

This way we can enable IO controller not only for CFQ but for all the IO schedulers without duplicating too much of code.

This is what I am playing around with currently. At this point I am not sure, how much of common ground I can have between all the IO schedulers.

Quoted 5 lines

CFQ can be trivially
modified to do proportional division (i.e give time slices in proportion to weight instead of priority).
And such a solution would
avoid idleness problem like the one you mentioned above.

Can you just elaborate a little on how do you get around idleness problem? If you don't create idleness than if two tasks in two cgroups are doing sequential IO, they might simply get into lockstep and we will not achieve any differentiated service proportionate to their weight.

Quoted 11 lines

and can also
avoid burstiness issues (see smoothing patches -- v1.2.0 and v1.3.0 -- of dm-ioband) in token based schemes.

Also doing time based token allocation (as you mentioned in TODO list) sounds very interesting. Can we look at the disk time taken by each bio and use that to account for tokens? The problem is that the time taken is not available when the requests are sent to disk, but we can do delayed token charging (i.e deduct tokens after the request is completed?). It seems that such an approach should work. What do you think?

This is a good idea. Charging the cgroup based on time actually consumed should be doable. I will look into it. I think in the past somebody mentioned that how do you account for the seek time taken because of switchover between cgroups? May be average time per cgroup can help here a bit.

This is more about refining the dispatch algorightm once we have agreed upon other semantics like 2 level scheduler and can we come up with a common layer which can be shared by all four IO schedulers. Once common layer is possible, we can always change the common layer algorithm from token based to time based to achive better accuracy.

Thanks
Vivek

#110 lore raw
In reply to Nauman Rafique
Quoted 145 lines · Nauman Rafique
Quoted 137 lines · Vivek Goyal
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

Quoted 6 lines

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

Quoted 2 lines

What do you think about elevator based solutions like 2 level cfq patches submitted by Satoshi and Vasily earlier?

I have had a very high level look at Satoshi's patch. I will go into details soon. I was thinking that this patch solves the problem only for CFQ. Can we create a common layer which can be shared by all the four IO schedulers.

So this one common layer can take care of all the management w.r.t per device per cgroup data structures and track all the groups, their limits (either token based or time based scheme), and control the dispatch of requests.

This way we can enable IO controller not only for CFQ but for all the IO schedulers without duplicating too much of code.

This is what I am playing around with currently. At this point I am not sure, how much of common ground I can have between all the IO schedulers.

Quoted 5 lines

CFQ can be trivially
modified to do proportional division (i.e give time slices in proportion to weight instead of priority).
And such a solution would
avoid idleness problem like the one you mentioned above.

Can you just elaborate a little on how do you get around idleness problem? If you don't create idleness than if two tasks in two cgroups are doing sequential IO, they might simply get into lockstep and we will not achieve any differentiated service proportionate to their weight.

Quoted 11 lines

and can also
avoid burstiness issues (see smoothing patches -- v1.2.0 and v1.3.0 -- of dm-ioband) in token based schemes.

Also doing time based token allocation (as you mentioned in TODO list) sounds very interesting. Can we look at the disk time taken by each bio and use that to account for tokens? The problem is that the time taken is not available when the requests are sent to disk, but we can do delayed token charging (i.e deduct tokens after the request is completed?). It seems that such an approach should work. What do you think?

This is a good idea. Charging the cgroup based on time actually consumed should be doable. I will look into it. I think in the past somebody mentioned that how do you account for the seek time taken because of switchover between cgroups? May be average time per cgroup can help here a bit.

This is more about refining the dispatch algorightm once we have agreed upon other semantics like 2 level scheduler and can we come up with a common layer which can be shared by all four IO schedulers. Once common layer is possible, we can always change the common layer algorithm from token based to time based to achive better accuracy.

Thanks
Vivek

#111 lore raw
In reply to Nauman Rafique
Quoted 145 lines · Nauman Rafique
Quoted 137 lines · Vivek Goyal
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

Quoted 6 lines

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

Quoted 2 lines

What do you think about elevator based solutions like 2 level cfq patches submitted by Satoshi and Vasily earlier?

I have had a very high level look at Satoshi's patch. I will go into details soon. I was thinking that this patch solves the problem only for CFQ. Can we create a common layer which can be shared by all the four IO schedulers.

So this one common layer can take care of all the management w.r.t per device per cgroup data structures and track all the groups, their limits (either token based or time based scheme), and control the dispatch of requests.

This way we can enable IO controller not only for CFQ but for all the IO schedulers without duplicating too much of code.

This is what I am playing around with currently. At this point I am not sure, how much of common ground I can have between all the IO schedulers.

Quoted 5 lines

CFQ can be trivially
modified to do proportional division (i.e give time slices in proportion to weight instead of priority).
And such a solution would
avoid idleness problem like the one you mentioned above.

Can you just elaborate a little on how do you get around idleness problem? If you don't create idleness than if two tasks in two cgroups are doing sequential IO, they might simply get into lockstep and we will not achieve any differentiated service proportionate to their weight.

Quoted 11 lines

and can also
avoid burstiness issues (see smoothing patches -- v1.2.0 and v1.3.0 -- of dm-ioband) in token based schemes.

Also doing time based token allocation (as you mentioned in TODO list) sounds very interesting. Can we look at the disk time taken by each bio and use that to account for tokens? The problem is that the time taken is not available when the requests are sent to disk, but we can do delayed token charging (i.e deduct tokens after the request is completed?). It seems that such an approach should work. What do you think?

This is a good idea. Charging the cgroup based on time actually consumed should be doable. I will look into it. I think in the past somebody mentioned that how do you account for the seek time taken because of switchover between cgroups? May be average time per cgroup can help here a bit.

This is more about refining the dispatch algorightm once we have agreed upon other semantics like 2 level scheduler and can we come up with a common layer which can be shared by all four IO schedulers. Once common layer is possible, we can always change the common layer algorithm from token based to time based to achive better accuracy.

Thanks
Vivek

A message that is not in the archive (sent off-list or not yet received): [email protected]

#113 lore raw
Quoted 159 lines · Vivek Goyal
Quoted 145 lines · Nauman Rafique
Quoted 137 lines · Vivek Goyal
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

Quoted 6 lines

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

Quoted 2 lines

What do you think about elevator based solutions like 2 level cfq patches submitted by Satoshi and Vasily earlier?

I have had a very high level look at Satoshi's patch. I will go into details soon. I was thinking that this patch solves the problem only for CFQ. Can we create a common layer which can be shared by all the four IO schedulers.

So this one common layer can take care of all the management w.r.t per device per cgroup data structures and track all the groups, their limits (either token based or time based scheme), and control the dispatch of requests.

Quoted 6 lines

This way we can enable IO controller not only for CFQ but for all the IO schedulers without duplicating too much of code.

This is what I am playing around with currently. At this point I am not sure, how much of common ground I can have between all the IO schedulers.

I see your point. But having some common code in different schedulers is not worse than what we have today (cfq, as, and deadline all have some common code). Besides, each lower level (elevator level) scheduler might impose certain requirements on higher level schedulers (e.g RT requests for cfq that we talked about earlier).

Quoted 11 lines
Quoted 6 lines

CFQ can be trivially
modified to do proportional division (i.e give time slices in proportion to weight instead of priority).
And such a solution would
avoid idleness problem like the one you mentioned above.

Can you just elaborate a little on how do you get around idleness problem? If you don't create idleness than if two tasks in two cgroups are doing sequential IO, they might simply get into lockstep and we will not achieve any differentiated service proportionate to their weight.

I was thinking of a more cfq-like solution for proportional division at the elevator level (i.e. not a token based solution). There are two options for proportional bandwidth division at elevator level: 1) change the size of the time slice in proportion to the weights or 2) allocate equal time slice each time but allocate more slices to cgroup with more weight. For (2), we can actually keep track of time taken to serve requests and allocate time slices in such a way that the actual disk time is proportional to the weight. We can adopt a fair-queuing
(http://lkml.org/lkml/2008/4/1/234) like approach for this if we want to go that way.

I am not sure if the solutions mentioned above will have the lockstep problem you mentioned above or not. Since we are allocating time slices, and would have anticipation built in (just like cfq), we would have some level of idleness. But this idleness can be predicted based on a thread behavior. Can we use AS like algorithm for predicting idle time before starting new epoch in your token based patch?

Quoted 28 lines
Quoted 5 lines

and can also
avoid burstiness issues (see smoothing patches -- v1.2.0 and v1.3.0 -- of dm-ioband) in token based schemes.

Quoted 7 lines

Also doing time based token allocation (as you mentioned in TODO list) sounds very interesting. Can we look at the disk time taken by each bio and use that to account for tokens? The problem is that the time taken is not available when the requests are sent to disk, but we can do delayed token charging (i.e deduct tokens after the request is completed?). It seems that such an approach should work. What do you think?

This is a good idea. Charging the cgroup based on time actually consumed should be doable. I will look into it. I think in the past somebody mentioned that how do you account for the seek time taken because of switchover between cgroups? May be average time per cgroup can help here a bit.

This is more about refining the dispatch algorightm once we have agreed upon other semantics like 2 level scheduler and can we come up with a common layer which can be shared by all four IO schedulers. Once common layer is possible, we can always change the common layer algorithm from token based to time based to achive better accuracy.

Thanks
Vivek

#114 lore raw
In reply to Vivek Goyal
Quoted 159 lines · Vivek Goyal
Quoted 145 lines · Nauman Rafique
Quoted 137 lines · Vivek Goyal
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

Quoted 6 lines

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

Quoted 2 lines

What do you think about elevator based solutions like 2 level cfq patches submitted by Satoshi and Vasily earlier?

I have had a very high level look at Satoshi's patch. I will go into details soon. I was thinking that this patch solves the problem only for CFQ. Can we create a common layer which can be shared by all the four IO schedulers.

So this one common layer can take care of all the management w.r.t per device per cgroup data structures and track all the groups, their limits (either token based or time based scheme), and control the dispatch of requests.

Quoted 6 lines

This way we can enable IO controller not only for CFQ but for all the IO schedulers without duplicating too much of code.

This is what I am playing around with currently. At this point I am not sure, how much of common ground I can have between all the IO schedulers.

I see your point. But having some common code in different schedulers is not worse than what we have today (cfq, as, and deadline all have some common code). Besides, each lower level (elevator level) scheduler might impose certain requirements on higher level schedulers (e.g RT requests for cfq that we talked about earlier).

Quoted 11 lines
Quoted 6 lines

CFQ can be trivially
modified to do proportional division (i.e give time slices in proportion to weight instead of priority).
And such a solution would
avoid idleness problem like the one you mentioned above.

Can you just elaborate a little on how do you get around idleness problem? If you don't create idleness than if two tasks in two cgroups are doing sequential IO, they might simply get into lockstep and we will not achieve any differentiated service proportionate to their weight.

I was thinking of a more cfq-like solution for proportional division at the elevator level (i.e. not a token based solution). There are two options for proportional bandwidth division at elevator level: 1) change the size of the time slice in proportion to the weights or 2) allocate equal time slice each time but allocate more slices to cgroup with more weight. For (2), we can actually keep track of time taken to serve requests and allocate time slices in such a way that the actual disk time is proportional to the weight. We can adopt a fair-queuing
(http://lkml.org/lkml/2008/4/1/234) like approach for this if we want to go that way.

I am not sure if the solutions mentioned above will have the lockstep problem you mentioned above or not. Since we are allocating time slices, and would have anticipation built in (just like cfq), we would have some level of idleness. But this idleness can be predicted based on a thread behavior. Can we use AS like algorithm for predicting idle time before starting new epoch in your token based patch?

Quoted 28 lines
Quoted 5 lines

and can also
avoid burstiness issues (see smoothing patches -- v1.2.0 and v1.3.0 -- of dm-ioband) in token based schemes.

Quoted 7 lines

Also doing time based token allocation (as you mentioned in TODO list) sounds very interesting. Can we look at the disk time taken by each bio and use that to account for tokens? The problem is that the time taken is not available when the requests are sent to disk, but we can do delayed token charging (i.e deduct tokens after the request is completed?). It seems that such an approach should work. What do you think?

This is a good idea. Charging the cgroup based on time actually consumed should be doable. I will look into it. I think in the past somebody mentioned that how do you account for the seek time taken because of switchover between cgroups? May be average time per cgroup can help here a bit.

This is more about refining the dispatch algorightm once we have agreed upon other semantics like 2 level scheduler and can we come up with a common layer which can be shared by all four IO schedulers. Once common layer is possible, we can always change the common layer algorithm from token based to time based to achive better accuracy.

Thanks
Vivek

#115 lore raw
In reply to Vivek Goyal
Quoted 159 lines · Vivek Goyal
Quoted 145 lines · Nauman Rafique
Quoted 137 lines · Vivek Goyal
Quoted 17 lines · Nauman Rafique
Quoted 9 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Quoted 6 lines

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 12 lines

It will probably not. But then we have a cfq-like scheduler at higher level and we can agree that the combinations "cfq(higher level)-noop(lower level)", "cfq-deadline", "cfq-as" and "cfq-cfq" would probably work. But if we implement one high level cfq-like scheduler at a higher level, we would not take care of somebody who wants noop-noop or propotional-noop. The point I am trying to make is that there is probably no single one-size-fits-all solution for a higher level scheduler. And we should limit the arbitrary mixing and matching of higher level schedulers and elevator schedulers. That being said, the existence of a higher level scheduler is still a point of debate I guess, see my comments below.

Ya, implemeting CFQ like thing in higher level scheduler will make things complex.

Quoted 23 lines
Quoted 15 lines
Quoted 11 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

Quoted 7 lines

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 8 lines

I cannot agree with you more on this that there is only contention at the leaf level physical devices and bandwidth should be managed only there. But having seen earlier posts on this list, i feel some folks might not agree with us. For example, if we have RAID-0 striping, we might want to schedule requests based on accumulative bandwidth used over all devices. Again, I myself don't agree with moving scheduling at a higher level just to support that.

Hmm.., I am not very convinced that we need to do resource management at RAID0 device. The common case of resource management is that a higher priority task group is not deprived of resources because of lower priority task group. So if there is no contention between two task groups (At leaf node), then I might as well let them give them full access to RAID 0 logical device without any control.

Hope people who have requirement of control at higher level devices can pitch in now and share their perspective.

Quoted 51 lines
Quoted 12 lines
Quoted 11 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Quoted 7 lines

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

I was thinking that we can keep track of disk time used at each device, and keep the cumulative number in a per cgroup data structure. But that is only if we want to support bandwidth division across devices. You and me both agree that we probably do not need to do that.

Quoted 10 lines
Quoted 9 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

Quoted 3 lines

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Yes, that seems to be a solution to avoid replication of code. But we should also look at any other solutions that avoid replication of code, and also avoid scheduling in two different layers.
In my opinion, scheduling at two different layers is problematic because
(a) Any buffering done at a higher level will be artificial, unless the queues at lower levels are completely full. And if there is no buffering at a higher level, any scheduling scheme would be ineffective.
(b) We cannot have an arbitrary mixing and matching of higher and lower level schedulers.

Quoted 4 lines

(a) would exist in any solution in which requests are queued at multiple levels. Can you please comment on this with respect to the patch that you have posted?

I am not very sure about the queustion, but in my patch, buffering at at higher layer is irrespective of the status of underlying queue. We try our best to fill underlying queue with request, only subject to the criteria of proportional bandwidth.

Quoted 6 lines

So, if there are two cgroups A and B and we allocate two cgroups 2000 tokens each to begin with. If A has consumed all the tokens soon and B has not, then we will stop A from dispatching more requests and wait for B to either issue more IO and consume tokens or get out of contention. This can leave disk idle for sometime. We can probably do some optimizations here.

Quoted 2 lines

What do you think about elevator based solutions like 2 level cfq patches submitted by Satoshi and Vasily earlier?

I have had a very high level look at Satoshi's patch. I will go into details soon. I was thinking that this patch solves the problem only for CFQ. Can we create a common layer which can be shared by all the four IO schedulers.

So this one common layer can take care of all the management w.r.t per device per cgroup data structures and track all the groups, their limits (either token based or time based scheme), and control the dispatch of requests.

Quoted 6 lines

This way we can enable IO controller not only for CFQ but for all the IO schedulers without duplicating too much of code.

This is what I am playing around with currently. At this point I am not sure, how much of common ground I can have between all the IO schedulers.

I see your point. But having some common code in different schedulers is not worse than what we have today (cfq, as, and deadline all have some common code). Besides, each lower level (elevator level) scheduler might impose certain requirements on higher level schedulers (e.g RT requests for cfq that we talked about earlier).

Quoted 11 lines
Quoted 6 lines

CFQ can be trivially
modified to do proportional division (i.e give time slices in proportion to weight instead of priority).
And such a solution would
avoid idleness problem like the one you mentioned above.

Can you just elaborate a little on how do you get around idleness problem? If you don't create idleness than if two tasks in two cgroups are doing sequential IO, they might simply get into lockstep and we will not achieve any differentiated service proportionate to their weight.

I was thinking of a more cfq-like solution for proportional division at the elevator level (i.e. not a token based solution). There are two options for proportional bandwidth division at elevator level: 1) change the size of the time slice in proportion to the weights or 2) allocate equal time slice each time but allocate more slices to cgroup with more weight. For (2), we can actually keep track of time taken to serve requests and allocate time slices in such a way that the actual disk time is proportional to the weight. We can adopt a fair-queuing
(http://lkml.org/lkml/2008/4/1/234) like approach for this if we want to go that way.

I am not sure if the solutions mentioned above will have the lockstep problem you mentioned above or not. Since we are allocating time slices, and would have anticipation built in (just like cfq), we would have some level of idleness. But this idleness can be predicted based on a thread behavior. Can we use AS like algorithm for predicting idle time before starting new epoch in your token based patch?

Quoted 28 lines
Quoted 5 lines

and can also
avoid burstiness issues (see smoothing patches -- v1.2.0 and v1.3.0 -- of dm-ioband) in token based schemes.

Quoted 7 lines

Also doing time based token allocation (as you mentioned in TODO list) sounds very interesting. Can we look at the disk time taken by each bio and use that to account for tokens? The problem is that the time taken is not available when the requests are sent to disk, but we can do delayed token charging (i.e deduct tokens after the request is completed?). It seems that such an approach should work. What do you think?

This is a good idea. Charging the cgroup based on time actually consumed should be doable. I will look into it. I think in the past somebody mentioned that how do you account for the seek time taken because of switchover between cgroups? May be average time per cgroup can help here a bit.

This is more about refining the dispatch algorightm once we have agreed upon other semantics like 2 level scheduler and can we come up with a common layer which can be shared by all four IO schedulers. Once common layer is possible, we can always change the common layer algorithm from token based to time based to achive better accuracy.

Thanks
Vivek

#116 lore raw

Hi,

Quoted 3 lines

From: Nauman Rafique <[email protected]>
Date: Wed, Nov 12, 2008 01:20:13PM -0800

...

Quoted 22 lines
Quoted 10 lines
Quoted 5 lines

CFQ can be trivially
modified to do proportional division (i.e give time slices in proportion to weight instead of priority).
And such a solution would
avoid idleness problem like the one you mentioned above.

Can you just elaborate a little on how do you get around idleness problem? If you don't create idleness than if two tasks in two cgroups are doing sequential IO, they might simply get into lockstep and we will not achieve any differentiated service proportionate to their weight.

I was thinking of a more cfq-like solution for proportional division at the elevator level (i.e. not a token based solution). There are two options for proportional bandwidth division at elevator level: 1) change the size of the time slice in proportion to the weights or 2) allocate equal time slice each time but allocate more slices to cgroup with more weight. For (2), we can actually keep track of time taken to serve requests and allocate time slices in such a way that the actual disk time is proportional to the weight. We can adopt a fair-queuing
(http://lkml.org/lkml/2008/4/1/234) like approach for this if we want to go that way.

Quoted 5 lines

I am not sure if the solutions mentioned above will have the lockstep problem you mentioned above or not. Since we are allocating time slices, and would have anticipation built in (just like cfq), we would have some level of idleness. But this idleness can be predicted based on a thread behavior.

if I understand that correctly, the problem may arise whenever you
have to deal with *synchronous* I/O, where you may not see the streams of requests generated by tasks as continuously backlogged (and the algorithm used to distribute bandwidth does the implicit assumption that they are, as in the cfq case).

A cfq-like solution with idling enabled AFAIK should not suffer from this problem, as it creates backlog for the process being anticipated. But anticipation is not always used, and cfq currently disables it for SSDs and in other cases where it may hurt performance (e.g., NCQ drives in presence of seeky loads, etc). So, in these cases, something still needs to be done if we want a proportional bandwidth distribution, and we don't want to pay the extra cost of idling when it's not strictly necessary.

A message that is not in the archive (sent off-list or not yet received): e98e18940811121320w5f321302n13b526887cbb4012-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#118 lore raw

Hi,

Quoted 3 lines

From: Nauman Rafique <[email protected]>
Date: Wed, Nov 12, 2008 01:20:13PM -0800

...

Quoted 22 lines
Quoted 10 lines
Quoted 5 lines

CFQ can be trivially
modified to do proportional division (i.e give time slices in proportion to weight instead of priority).
And such a solution would
avoid idleness problem like the one you mentioned above.

Can you just elaborate a little on how do you get around idleness problem? If you don't create idleness than if two tasks in two cgroups are doing sequential IO, they might simply get into lockstep and we will not achieve any differentiated service proportionate to their weight.

I was thinking of a more cfq-like solution for proportional division at the elevator level (i.e. not a token based solution). There are two options for proportional bandwidth division at elevator level: 1) change the size of the time slice in proportion to the weights or 2) allocate equal time slice each time but allocate more slices to cgroup with more weight. For (2), we can actually keep track of time taken to serve requests and allocate time slices in such a way that the actual disk time is proportional to the weight. We can adopt a fair-queuing
(http://lkml.org/lkml/2008/4/1/234) like approach for this if we want to go that way.

Quoted 5 lines

I am not sure if the solutions mentioned above will have the lockstep problem you mentioned above or not. Since we are allocating time slices, and would have anticipation built in (just like cfq), we would have some level of idleness. But this idleness can be predicted based on a thread behavior.

if I understand that correctly, the problem may arise whenever you
have to deal with *synchronous* I/O, where you may not see the streams of requests generated by tasks as continuously backlogged (and the algorithm used to distribute bandwidth does the implicit assumption that they are, as in the cfq case).

A cfq-like solution with idling enabled AFAIK should not suffer from this problem, as it creates backlog for the process being anticipated. But anticipation is not always used, and cfq currently disables it for SSDs and in other cases where it may hurt performance (e.g., NCQ drives in presence of seeky loads, etc). So, in these cases, something still needs to be done if we want a proportional bandwidth distribution, and we don't want to pay the extra cost of idling when it's not strictly necessary.

#119 lore raw
In reply to Nauman Rafique

On Wed, Nov 12, 2008 at 01:20:13PM -0800, Nauman Rafique wrote:

[..]

Quoted 38 lines
Quoted 13 lines
Quoted 2 lines

What do you think about elevator based solutions like 2 level cfq patches submitted by Satoshi and Vasily earlier?

I have had a very high level look at Satoshi's patch. I will go into details soon. I was thinking that this patch solves the problem only for CFQ. Can we create a common layer which can be shared by all the four IO schedulers.

So this one common layer can take care of all the management w.r.t per device per cgroup data structures and track all the groups, their limits (either token based or time based scheme), and control the dispatch of requests.

Quoted 6 lines

This way we can enable IO controller not only for CFQ but for all the IO schedulers without duplicating too much of code.

This is what I am playing around with currently. At this point I am not sure, how much of common ground I can have between all the IO schedulers.

I see your point. But having some common code in different schedulers is not worse than what we have today (cfq, as, and deadline all have some common code). Besides, each lower level (elevator level) scheduler might impose certain requirements on higher level schedulers (e.g RT requests for cfq that we talked about earlier).

Quoted 11 lines
Quoted 6 lines

CFQ can be trivially
modified to do proportional division (i.e give time slices in proportion to weight instead of priority).
And such a solution would
avoid idleness problem like the one you mentioned above.

Can you just elaborate a little on how do you get around idleness problem? If you don't create idleness than if two tasks in two cgroups are doing sequential IO, they might simply get into lockstep and we will not achieve any differentiated service proportionate to their weight.

Quoted 10 lines

I was thinking of a more cfq-like solution for proportional division at the elevator level (i.e. not a token based solution). There are two options for proportional bandwidth division at elevator level: 1) change the size of the time slice in proportion to the weights or 2) allocate equal time slice each time but allocate more slices to cgroup with more weight. For (2), we can actually keep track of time taken to serve requests and allocate time slices in such a way that the actual disk time is proportional to the weight. We can adopt a fair-queuing
(http://lkml.org/lkml/2008/4/1/234) like approach for this if we want to go that way.

Hi Nauman,

I think doing proportional weight division at elevator level will be little difficult, because if we go for a full hierarchical solution then we will be doing proportional weight division among tasks as well as groups.

For example, consider this. Assume at root level there are three tasks A, B, C and two cgroups D and E. Now for proportional weight division we should consider A, B, C, D and E at same level and then try to divide the BW (Thanks to peterz for clarifying this).

Other approach could be that consider A, B, C in root cgroup and then consider root, D and E competing groups and try to divide the BW. But this is not how cpu controller operates and this approach I think was initially implemented for group scheduling in cpu controller and later changed.

How the proportional weight division is done among tasks is a property of IO scheduler. cfq decides to use time slices according to priority and bfq decides to use tokens. So probably we can't move this to common elevator layer.

I think Satoshi's cfq controller patches also do not seem to be considering A, B, C, D and E to be at same level, instead it treats cgroup "/" , D and E at same level and tries to do proportional BW division among these. Satoshi, please correct me, if that's not the case.

Above example, raises another question and that is what to do wih IO schedulers which do not differentiate between tasks. For example, noop. It simply has got one single linked list and does not have the notion of io context and does not differentiate between IO coming from different tasks. In that case probably we have no choice but to group A, B, C's bio in root cgroup and do proportional weight division among "root", D and E groups. I have not looked at deadline and AS yet.

So at this point of time I think that probably porting BFQ's hierarchical scheduling implementation to other IO schedulers might make sense. Thoughts?

While doing this may be we can try to keep some functionality like cgroup interface common among various IO schedulers.

Thanks
Vivek

#120 lore raw
Quoted 4 lines

From: Vivek Goyal <[email protected]>
Date: Thu, Nov 13, 2008 01:08:21PM -0500

On Wed, Nov 12, 2008 at 01:20:13PM -0800, Nauman Rafique wrote:

...

Quoted 29 lines
Quoted 10 lines

I was thinking of a more cfq-like solution for proportional division at the elevator level (i.e. not a token based solution). There are two options for proportional bandwidth division at elevator level: 1) change the size of the time slice in proportion to the weights or 2) allocate equal time slice each time but allocate more slices to cgroup with more weight. For (2), we can actually keep track of time taken to serve requests and allocate time slices in such a way that the actual disk time is proportional to the weight. We can adopt a fair-queuing
(http://lkml.org/lkml/2008/4/1/234) like approach for this if we want to go that way.

Hi Nauman,

I think doing proportional weight division at elevator level will be little difficult, because if we go for a full hierarchical solution then we will be doing proportional weight division among tasks as well as groups.

For example, consider this. Assume at root level there are three tasks A, B, C and two cgroups D and E. Now for proportional weight division we should consider A, B, C, D and E at same level and then try to divide the BW (Thanks to peterz for clarifying this).

Other approach could be that consider A, B, C in root cgroup and then consider root, D and E competing groups and try to divide the BW. But this is not how cpu controller operates and this approach I think was initially implemented for group scheduling in cpu controller and later changed.

Quoted 5 lines

How the proportional weight division is done among tasks is a property of IO scheduler. cfq decides to use time slices according to priority and bfq decides to use tokens. So probably we can't move this to common elevator layer.

cfq and bfq are pretty similar in the concepts they adopt, and the pure time-based approach of cfq can be extended to arbitrary hierarchies.

Even in bfq, when dealing with groups that generate only seeky traffic we don't try to be fair in the service domain, as it would decrease too much the aggregate throughput, but we fall back to a time-based approach.

[ This is a design choice, but it does not depend on the algorithms,
and of course can be changed... ]

The two approaches can be mixed/unified, for example, using wf2q+ to schedule the slices, in the time domain, of cfq; the main remaining difference would be the ability of bfq to provide service-domain guarantees.

Quoted 5 lines

I think Satoshi's cfq controller patches also do not seem to be considering A, B, C, D and E to be at same level, instead it treats cgroup "/" , D and E at same level and tries to do proportional BW division among these. Satoshi, please correct me, if that's not the case.

Quoted 8 lines

Above example, raises another question and that is what to do wih IO schedulers which do not differentiate between tasks. For example, noop. It simply has got one single linked list and does not have the notion of io context and does not differentiate between IO coming from different tasks. In that case probably we have no choice but to group A, B, C's bio in root cgroup and do proportional weight division among "root", D and E groups. I have not looked at deadline and AS yet.

When you talk about grouping tasks into the root cgroup and then scheduling inside the groups using an existing scheduler, do you mean doing something like creating a ``little as'' or ``little noop'' queue per each group, somehow like what happens with classless leaf qdiscs in network scheduling, and then select first the leaf group to be scheduled, and then using the per-leaf scheduler to select the request from the leaf?

A good thing about this approach would be that idling would still make sense and the upper infrastructure would be the same for all the schedulers (except for cfq and bfq, that in my opinion better fit the cpu scheduler's hierarchical approach, with hierarchies confined into scheduling classes).

Quoted 3 lines

So at this point of time I think that probably porting BFQ's hierarchical scheduling implementation to other IO schedulers might make sense. Thoughts?

IMO for cfq, given the similarities, this can be done without conceptual problems. How to do that for schedulers like as, noop or deadline, and if this is the best solution, is an interesting problem :)

#121 lore raw
Quoted 47 lines · Fabio Checconi
Quoted 4 lines

From: Vivek Goyal <[email protected]>
Date: Thu, Nov 13, 2008 01:08:21PM -0500

On Wed, Nov 12, 2008 at 01:20:13PM -0800, Nauman Rafique wrote:

...

Quoted 29 lines
Quoted 10 lines

I was thinking of a more cfq-like solution for proportional division at the elevator level (i.e. not a token based solution). There are two options for proportional bandwidth division at elevator level: 1) change the size of the time slice in proportion to the weights or 2) allocate equal time slice each time but allocate more slices to cgroup with more weight. For (2), we can actually keep track of time taken to serve requests and allocate time slices in such a way that the actual disk time is proportional to the weight. We can adopt a fair-queuing
(http://lkml.org/lkml/2008/4/1/234) like approach for this if we want to go that way.

Hi Nauman,

I think doing proportional weight division at elevator level will be little difficult, because if we go for a full hierarchical solution then we will be doing proportional weight division among tasks as well as groups.

For example, consider this. Assume at root level there are three tasks A, B, C and two cgroups D and E. Now for proportional weight division we should consider A, B, C, D and E at same level and then try to divide the BW (Thanks to peterz for clarifying this).

Other approach could be that consider A, B, C in root cgroup and then consider root, D and E competing groups and try to divide the BW. But this is not how cpu controller operates and this approach I think was initially implemented for group scheduling in cpu controller and later changed.

Quoted 5 lines

How the proportional weight division is done among tasks is a property of IO scheduler. cfq decides to use time slices according to priority and bfq decides to use tokens. So probably we can't move this to common elevator layer.

cfq and bfq are pretty similar in the concepts they adopt, and the pure time-based approach of cfq can be extended to arbitrary hierarchies.

Even in bfq, when dealing with groups that generate only seeky traffic we don't try to be fair in the service domain, as it would decrease too much the aggregate throughput, but we fall back to a time-based approach.

Quoted 7 lines

[ This is a design choice, but it does not depend on the algorithms,
and of course can be changed... ]

The two approaches can be mixed/unified, for example, using wf2q+ to schedule the slices, in the time domain, of cfq; the main remaining difference would be the ability of bfq to provide service-domain guarantees.

Before going into the design of elevator level scheduler, we should have some consensus on abandoning the two level approach. Infact, it would be useful if we had Ryo and Satoshi jump into this discussion and express their opinion.

Having said that, I think I agree with Fabio on some mix/unification of BFQ and CFQ patches, specially using wf2q+ to schedule time slices would be a small patch into existing 2 level cfqs. That approach might also reduce the resistance to have this in the tree, as CFQ is already the default scheduler.

Quoted 29 lines
Quoted 7 lines

I think Satoshi's cfq controller patches also do not seem to be considering A, B, C, D and E to be at same level, instead it treats cgroup "/" , D and E at same level and tries to do proportional BW division among these. Satoshi, please correct me, if that's not the case.

Quoted 8 lines

Above example, raises another question and that is what to do wih IO schedulers which do not differentiate between tasks. For example, noop. It simply has got one single linked list and does not have the notion of io context and does not differentiate between IO coming from different tasks. In that case probably we have no choice but to group A, B, C's bio in root cgroup and do proportional weight division among "root", D and E groups. I have not looked at deadline and AS yet.

When you talk about grouping tasks into the root cgroup and then scheduling inside the groups using an existing scheduler, do you mean doing something like creating a ``little as'' or ``little noop'' queue per each group, somehow like what happens with classless leaf qdiscs in network scheduling, and then select first the leaf group to be scheduled, and then using the per-leaf scheduler to select the request from the leaf?

A good thing about this approach would be that idling would still make sense and the upper infrastructure would be the same for all the schedulers (except for cfq and bfq, that in my opinion better fit the cpu scheduler's hierarchical approach, with hierarchies confined into scheduling classes).

Quoted 7 lines
Quoted 3 lines

So at this point of time I think that probably porting BFQ's hierarchical scheduling implementation to other IO schedulers might make sense. Thoughts?

IMO for cfq, given the similarities, this can be done without conceptual problems. How to do that for schedulers like as, noop or deadline, and if this is the best solution, is an interesting problem :)

It might be a little too early to start patching things into other schedulers. First, because we still don't have a common ground on the exact approach for proportional bandwidth division. Second, if somebody is using vanilla no-op, deadline or as, do they really care about proportional division? If they did, they would probably be using cfq already. So we can have something going for cfq first, and then we can move to other schedulers.

Quoted 6 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#122 lore raw
Quoted 25 lines

From: Nauman Rafique <[email protected]>
Date: Thu, Nov 13, 2008 02:27:41PM -0800

Quoted 13 lines · Fabio Checconi
Quoted 5 lines

How the proportional weight division is done among tasks is a property of IO scheduler. cfq decides to use time slices according to priority and bfq decides to use tokens. So probably we can't move this to common elevator layer.

cfq and bfq are pretty similar in the concepts they adopt, and the pure time-based approach of cfq can be extended to arbitrary hierarchies.

Even in bfq, when dealing with groups that generate only seeky traffic we don't try to be fair in the service domain, as it would decrease too much the aggregate throughput, but we fall back to a time-based approach.

Quoted 7 lines

[ This is a design choice, but it does not depend on the algorithms,
and of course can be changed... ]

The two approaches can be mixed/unified, for example, using wf2q+ to schedule the slices, in the time domain, of cfq; the main remaining difference would be the ability of bfq to provide service-domain guarantees.

Quoted 5 lines

Before going into the design of elevator level scheduler, we should have some consensus on abandoning the two level approach. Infact, it would be useful if we had Ryo and Satoshi jump into this discussion and express their opinion.

You're right. I was only trying to give some design elements thinking that they could help in the evaluation of the two approaches, talking of the one I know better.

...

Quoted 8 lines
Quoted 7 lines
Quoted 3 lines

So at this point of time I think that probably porting BFQ's hierarchical scheduling implementation to other IO schedulers might make sense. Thoughts?

IMO for cfq, given the similarities, this can be done without conceptual problems. How to do that for schedulers like as, noop or deadline, and if this is the best solution, is an interesting problem :)

Quoted 8 lines

It might be a little too early to start patching things into other schedulers. First, because we still don't have a common ground on the exact approach for proportional bandwidth division. Second, if somebody is using vanilla no-op, deadline or as, do they really care about proportional division? If they did, they would probably be using cfq already. So we can have something going for cfq first, and then we can move to other schedulers.

Sorry for being unclear, I didn't want to start patching anything. In my opinion a hierarchical extension of as/deadline poses some interesting scheduling issues, and exploring them can help in making better decisions.

#123 lore raw
In reply to Vivek Goyal

Hi, Vivek

Quoted 22 lines

I think doing proportional weight division at elevator level will be little difficult, because if we go for a full hierarchical solution then we will be doing proportional weight division among tasks as well as groups.

For example, consider this. Assume at root level there are three tasks A, B, C and two cgroups D and E. Now for proportional weight division we should consider A, B, C, D and E at same level and then try to divide the BW (Thanks to peterz for clarifying this).

Other approach could be that consider A, B, C in root cgroup and then consider root, D and E competing groups and try to divide the BW. But this is not how cpu controller operates and this approach I think was initially implemented for group scheduling in cpu controller and later changed.

How the proportional weight division is done among tasks is a property of IO scheduler. cfq decides to use time slices according to priority and bfq decides to use tokens. So probably we can't move this to common elevator layer.

Quoted 6 lines

I think Satoshi's cfq controller patches also do not seem to be considering A, B, C, D and E to be at same level, instead it treats cgroup "/" , D and E
at same level and tries to do proportional BW division among these. Satoshi, please correct me, if that's not the case.

Yes.
I think that a controller should be divided share among "/(root)" and two groups. This reason is follows:

* If these tasks are handled at same level, it is enough by using a traditional CFQ scheduler.
If you want to make all tasks in the same group the same priority(parameter), It is not I/O control but is parameter control.

* I think that the group means the environment which makes some sense and user want to control I/O per groups.
Next, the group is the environment. So, tasks within the group will have priorities for themselves respectively as traditional environment. Of course, group may not be need to control I/O.
In such time, a ioprio of tasks should be set the same priority.

Therefore, our scheduler controls among group and then among tasks

Satoshi UCHIDA

#124 lore raw
Quoted 16 lines · Satoshi UCHIDA
Quoted 6 lines

I think Satoshi's cfq controller patches also do not seem to be considering A, B, C, D and E to be at same level, instead it treats cgroup "/" , D and E
at same level and tries to do proportional BW division among these. Satoshi, please correct me, if that's not the case.

Yes.
I think that a controller should be divided share among "/(root)" and two groups. This reason is follows:

* If these tasks are handled at same level, it is enough by using a traditional CFQ scheduler.
If you want to make all tasks in the same group the same priority(parameter), It is not I/O control but is parameter control.

Quoted 8 lines

* I think that the group means the environment which makes some sense and user want to control I/O per groups.
Next, the group is the environment. So, tasks within the group will have priorities for themselves respectively as traditional environment. Of course, group may not be need to control I/O.
In such time, a ioprio of tasks should be set the same priority.

Therefore, our scheduler controls among group and then among tasks

I would suggest abandoning this scheme as its different from how the CPU scheduler does it. The CPU scheduler is fully hierarchical and tasks in "/" are on the same level as groups in "/".

That is, we do:

      root
      / | \
     1  2  A
          / \
         B   3
        / \
       4   5

Where digits are tasks, and letters are groups.

Having the two bandwidth (CPU, I/O) doing different things wrt grouping can only be confusing at best.

#125 lore raw

Hi, Vivek.

Quoted 36 lines
Quoted 1 line

I think that a controller should be divided share among "/(root)" and

two groups.

Quoted 3 lines

This reason is follows:

* If these tasks are handled at same level, it is enough by using a

traditional

Quoted 2 lines

CFQ scheduler.
If you want to make all tasks in the same group the same

priority(parameter),

Quoted 3 lines

It is not I/O control but is parameter control.

* I think that the group means the environment which makes some sense

and

Quoted 2 lines

user want to control I/O per groups.
Next, the group is the environment. So, tasks within the group will

have

Quoted 5 lines

priorities for themselves respectively as traditional environment. Of course, group may not be need to control I/O.
In such time, a ioprio of tasks should be set the same priority.

Therefore, our scheduler controls among group and then among tasks

I would suggest abandoning this scheme as its different from how the CPU scheduler does it. The CPU scheduler is fully hierarchical and tasks in "/" are on the same level as groups in "/".

That is, we do:

      root
      / | \
     1  2  A
          / \
         B   3
        / \
       4   5
Quoted 5 lines

Where digits are tasks, and letters are groups.

Having the two bandwidth (CPU, I/O) doing different things wrt grouping can only be confusing at best.

I understand what you mean is as follows.
CPU power for 4 is calculated by 100% * a ratio of A (among 1, 2 and A) * a ratio of B (among 3 and B) * ratio of 4 (among 4 and 5).
However, I/O power for 4 is calculated by 100% * a ratio of B (among root, A and B) * a ratio of 4 (among 4 and 5).
Therefore, its power expression is a different and then user will confuse.

So, in other cgroups controllers, children(tasks and groups) of a group are flat, but in CFQ cgroups controllers, all groups are flat.

I agree this opinion.

I think that CFQ should support multiple layers, and its improvement would be easy (by nesting cfq_data tree) probably.

#126 lore raw
In reply to Nauman Rafique
Quoted 7 lines · Nauman Rafique

It seems that approaches with two level scheduling (DM-IOBand or this patch set on top and another scheduler at elevator) will have the possibility of undesirable interactions (see "issues" listed at the end of the second patch). For example, a request submitted as RT might get delayed at higher layers, even if cfq at elevator level is doing the right thing.

Yep. Buffering of bios at higher layer can break underlying elevator's assumptions.

What if we start keeping track of task priorities and RT tasks in higher level schedulers and dispatch the bios accordingly. Will it break the underlying noop, deadline or AS?

Quoted 9 lines

Moreover, if the requests in the higher level scheduler are dispatched as soon as they come, there would be no queuing at the higher layers, unless the request queue at the lower level fills up and causes a backlog. And in the absence of queuing, any work-conserving scheduler would behave as a no-op scheduler.

These issues motivate to take a second look into two level scheduling. The main motivations for two level scheduling seem to be:
(1) Support bandwidth division across multiple devices for RAID and LVMs.

Nauman, can you give an example where we really need bandwidth division for higher level devices.

I am beginning to think that real contention is at leaf level physical devices and not at higher level logical devices hence we should be doing any resource management only at leaf level and not worry about higher level logical devices.

If this requirement goes away, then case of two level scheduler weakens and one needs to think about doing changes at leaf level IO schedulers.

Quoted 10 lines

(2) Divide bandwidth between different cgroups without modifying each of the existing schedulers (and without replicating the code).

One possible approach to handle (1) is to keep track of bandwidth utilized by each cgroup in a per cgroup data structure (instead of a per cgroup per device data structure) and use that information to make scheduling decisions within the elevator level schedulers. Such a patch can be made flag-disabled if co-ordination across different device schedulers is not required.

Can you give more details about it. I am not sure I understand it. Exactly what information should be stored in each cgroup.

I think per cgroup per device data structures are good so that an scheduer will not worry about other devices present in the system and will just try to arbitrate between various cgroup contending for that device. This goes back to same issue of getting rid of requirement (1) from io controller.

Quoted 8 lines

And (2) can probably be handled by having one scheduler support different modes. For example, one possible mode is "propotional division between crgroups + no-op between threads of a cgroup" or "cfq between cgroups + cfq between threads of a cgroup". That would also help avoid combinations which might not work e.g RT request issue mentioned earlier in this email. And this unified scheduler can re-use code from all the existing patches.

IIUC, you are suggesting some kind of unification between four IO schedulers so that proportional weight code is not replicated and user can switch mode on the fly based on tunables?

Thanks
Vivek

Quoted 109 lines

Thanks.
--
Nauman

Quoted 104 lines · Vivek Goyal
Quoted 63 lines · Peter Zijlstra
Quoted 62 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 2 lines

Could we make the io-schedulers aware of this hierarchy?

Quoted 9 lines
Quoted 2 lines

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I think in this case, definition of fair allocation will be little different. We will do fair allocation only at the leaf nodes where there is actual contention, irrespective of higher level setup.

So if higher level block device issues multiple ios to satisfy one top level io, we will actually do the bandwidth allocation only on those multiple ios because that's the real IO contending for disk bandwidth. And if these multiple ios are going to different physical devices, then contention management will take place on those devices.

IOW, we will not worry about providing fairness at bios submitted to higher level devices. We will just pitch in for contention management only when request from various cgroups are contending for physical device at bottom most layers. Isn't if fair?

Thanks
Vivek

Quoted 6 lines

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

A message that is not in the archive (sent off-list or not yet received): [email protected]

#128 lore raw
Quoted 60 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 1 line
Quoted 4 lines
Quoted 1 line

Could we make the io-schedulers aware of this hierarchy?

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

#129 lore raw
In reply to Vivek Goyal
Quoted 60 lines · Vivek Goyal
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Vivek Goyal
Quoted 2 lines

Does this still require I use dm, or does it also work on regular block devices? Patch 4/4 isn't quite clear on this.

No. You don't have to use dm. It will simply work on regular devices. We shall have to put few lines of code for it to work on devices which don't make use of standard __make_request() function and provide their own make_request function.

Hence for example, I have put that few lines of code so that it can work with dm device. I shall have to do something similar for md too.

Quoted 9 lines
Quoted 5 lines

Though, I am not very sure why do I need to do IO control on higher level devices. Will it be sufficient if we just control only bottom most physical block devices?

Anyway, this approach should work at any level.

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I thought that we should be doing any kind of resource management only at the level where there is actual contention for the resources.So in this case looks like only bottom most devices are slow and don't have infinite bandwidth hence the contention.(I am not taking into account the contention at bus level or contention at interconnect level for external storage, assuming interconnect is not the bottleneck).

For example, lets say there is one linear device mapper device dm-0 on top of physical devices sda and sdb. Assuming two tasks in two different cgroups are reading two different files from deivce dm-0. Now if these files both fall on same physical device (either sda or sdb), then they will be contending for resources. But if files being read are on different physical deivces then practically there is no device contention (Even on the surface it might look like that dm-0 is being contended for). So if files are on different physical devices, IO controller will not know it. He will simply dispatch one group at a time and other device might remain idle.

Keeping that in mind I thought we will be able to make use of full available bandwidth if we do IO control only at bottom most device. Doing it at higher layer has potential of not making use of full available bandwidth.

Quoted 3 lines

Is there any reason we cannot merge this with the regular io-scheduler interface? afaik the only problem with doing group scheduling in the io-schedulers is the stacked devices issue.

I think we should be able to merge it with regular io schedulers. Apart from stacked device issue, people also mentioned that it is so closely tied to IO schedulers that we will end up doing four implementations for four schedulers and that is not very good from maintenance perspective.

But I will spend more time in finding out if there is a common ground between schedulers so that a lot of common IO control code can be used in all the schedulers.

Quoted 1 line
Quoted 4 lines
Quoted 1 line

Could we make the io-schedulers aware of this hierarchy?

You mean IO schedulers knowing that there is somebody above them doing proportional weight dispatching of bios? If yes, how would that help?

Well, take the slightly more elaborate example or a raid[56] setup. This will need to sometimes issue multiple leaf level ios to satisfy one top level io.

How are you going to attribute this fairly?

I don't think the issue of bandwidth availability like above will really be an issue, if your stripe is set up symmetrically, the contention should average out to both (all) disks in equal measures.

The only real issue I can see is with linear volumes, but those are stupid anyway - non of the gains but all the risks.

#130 lore raw
In reply to Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I'm not convinced.

Say that you have two resource groups on a bunch of LVM volumes across two disks.

If one of the resource groups only sends requests to one of the disks, the other resource group should be able to get all of its requests through immediateley at the other disk.

Holding up the second resource group's requests could result in a disk being idle. Worse, once that cgroup's requests finally make it through, the other cgroup might also want to use the disk and they both get slowed down.

When a resource is uncontended, should a potential user be made to wait?

Signature · 1 line
All rights reversed.
#131 lore raw
In reply to Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

Nice, although I would think only doing the higher level devices makes more sense than only doing the leafs.

I'm not convinced.

Say that you have two resource groups on a bunch of LVM volumes across two disks.

If one of the resource groups only sends requests to one of the disks, the other resource group should be able to get all of its requests through immediateley at the other disk.

Holding up the second resource group's requests could result in a disk being idle. Worse, once that cgroup's requests finally make it through, the other cgroup might also want to use the disk and they both get slowed down.

When a resource is uncontended, should a potential user be made to wait?

Signature · 1 line
All rights reversed.
#132 lore raw
In reply to [email protected]
Quoted 18 lines · [email protected] wrote:

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

Quoted 6 lines

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

Which kernel version is this patch set based on?

Quoted 4 lines

Thanks
Vivek

Signature · 2 lines
Regards
Gui Jianfeng
#133 lore raw
In reply to [email protected]
Quoted 18 lines · [email protected] wrote:

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

Quoted 6 lines

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

Which kernel version is this patch set based on?

Quoted 4 lines

Thanks
Vivek

Signature · 2 lines
Regards
Gui Jianfeng

A message that is not in the archive (sent off-list or not yet received): [email protected]

#135 lore raw
Quoted 19 lines · Gui Jianfeng
Quoted 18 lines · [email protected] wrote:

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

Quoted 9 lines
Quoted 6 lines

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

Which kernel version is this patch set based on?

2.6.27

Thanks
Vivek

#136 lore raw
In reply to Gui Jianfeng
Quoted 19 lines · Gui Jianfeng
Quoted 18 lines · [email protected] wrote:

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

Quoted 9 lines
Quoted 6 lines

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

Which kernel version is this patch set based on?

2.6.27

Thanks
Vivek

#137 lore raw
In reply to Gui Jianfeng
Quoted 19 lines · Gui Jianfeng
Quoted 18 lines · [email protected] wrote:

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

Quoted 9 lines
Quoted 6 lines

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

Which kernel version is this patch set based on?

2.6.27

Thanks
Vivek

#138 lore raw
In reply to [email protected]

Hi,

From: [email protected]
Subject: [patch 0/4] [RFC] Another proportional weight IO controller
Date: Thu, 06 Nov 2008 10:30:22 -0500

Quoted 18 lines

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

Quoted 6 lines

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

More details about the design and how to are in documentation patch.

Your comments are welcome.

Do you have any benchmark results?
I'm especially interested in the followings:
- Comparison of disk performance with and without the I/O controller patch.
- Put uneven I/O loads. Processes, which belong to a cgroup which is given a smaller weight than another cgroup, put heavier I/O load like the following.

     echo 1024 > /cgroup/bio/test1/bio.shares
     echo 8192 > /cgroup/bio/test2/bio.shares

     echo $$ > /cgroup/bio/test1/tasks
     dd if=/somefile1-1 of=/dev/null &
     dd if=/somefile1-2 of=/dev/null &
     ... 
     dd if=/somefile1-100 of=/dev/null
     echo $$ > /cgroup/bio/test2/tasks
     dd if=/somefile2-1 of=/dev/null &
     dd if=/somefile2-2 of=/dev/null &
     ...
     dd if=/somefile2-10 of=/dev/null &

Thanks,
Ryo Tsuruta

#139 lore raw
Quoted 27 lines · Ryo Tsuruta

Hi,

From: [email protected]
Subject: [patch 0/4] [RFC] Another proportional weight IO controller
Date: Thu, 06 Nov 2008 10:30:22 -0500

Quoted 21 lines

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

Quoted 7 lines
Quoted 3 lines

More details about the design and how to are in documentation patch.

Your comments are welcome.

Do you have any benchmark results?
I'm especially interested in the followings:
- Comparison of disk performance with and without the I/O controller patch.

If I dynamically disable the bio control, then I did not observe any impact on performance. Because in that case practically it boils down to just an additional variable check in __make_request().

Quoted 4 lines

- Put uneven I/O loads. Processes, which belong to a cgroup which is given a smaller weight than another cgroup, put heavier I/O load like the following.

Quoted 13 lines
     echo 1024 > /cgroup/bio/test1/bio.shares
     echo 8192 > /cgroup/bio/test2/bio.shares

     echo $$ > /cgroup/bio/test1/tasks
     dd if=/somefile1-1 of=/dev/null &
     dd if=/somefile1-2 of=/dev/null &
     ... 
     dd if=/somefile1-100 of=/dev/null
     echo $$ > /cgroup/bio/test2/tasks
     dd if=/somefile2-1 of=/dev/null &
     dd if=/somefile2-2 of=/dev/null &
     ...
     dd if=/somefile2-10 of=/dev/null &

I have not tried this case.

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

I have had a very brief look at BFQ's hierarchical proportional weight/priority IO control and it looks good. May be we can adopt it for other IO schedulers also.

Thanks
Vivek

#140 lore raw
Quoted 59 lines · Vivek Goyal
Quoted 27 lines · Ryo Tsuruta

Hi,

From: [email protected]
Subject: [patch 0/4] [RFC] Another proportional weight IO controller
Date: Thu, 06 Nov 2008 10:30:22 -0500

Quoted 21 lines

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

Quoted 7 lines
Quoted 3 lines

More details about the design and how to are in documentation patch.

Your comments are welcome.

Do you have any benchmark results?
I'm especially interested in the followings:
- Comparison of disk performance with and without the I/O controller patch.

If I dynamically disable the bio control, then I did not observe any impact on performance. Because in that case practically it boils down to just an additional variable check in __make_request().

Quoted 4 lines

- Put uneven I/O loads. Processes, which belong to a cgroup which is given a smaller weight than another cgroup, put heavier I/O load like the following.

Quoted 13 lines
     echo 1024 > /cgroup/bio/test1/bio.shares
     echo 8192 > /cgroup/bio/test2/bio.shares

     echo $$ > /cgroup/bio/test1/tasks
     dd if=/somefile1-1 of=/dev/null &
     dd if=/somefile1-2 of=/dev/null &
     ...
     dd if=/somefile1-100 of=/dev/null
     echo $$ > /cgroup/bio/test2/tasks
     dd if=/somefile2-1 of=/dev/null &
     dd if=/somefile2-2 of=/dev/null &
     ...
     dd if=/somefile2-10 of=/dev/null &
Quoted 5 lines

I have not tried this case.

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

-Divyesh

Quoted 12 lines

I have had a very brief look at BFQ's hierarchical proportional weight/priority IO control and it looks good. May be we can adopt it for other IO schedulers also.

Thanks
Vivek
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#141 lore raw
Quoted 80 lines · Divyesh Shah
Quoted 59 lines · Vivek Goyal
Quoted 27 lines · Ryo Tsuruta

Hi,

From: [email protected]
Subject: [patch 0/4] [RFC] Another proportional weight IO controller
Date: Thu, 06 Nov 2008 10:30:22 -0500

Quoted 21 lines

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

Quoted 7 lines
Quoted 3 lines

More details about the design and how to are in documentation patch.

Your comments are welcome.

Do you have any benchmark results?
I'm especially interested in the followings:
- Comparison of disk performance with and without the I/O controller patch.

If I dynamically disable the bio control, then I did not observe any impact on performance. Because in that case practically it boils down to just an additional variable check in __make_request().

Quoted 4 lines

- Put uneven I/O loads. Processes, which belong to a cgroup which is given a smaller weight than another cgroup, put heavier I/O load like the following.

Quoted 13 lines
     echo 1024 > /cgroup/bio/test1/bio.shares
     echo 8192 > /cgroup/bio/test2/bio.shares

     echo $$ > /cgroup/bio/test1/tasks
     dd if=/somefile1-1 of=/dev/null &
     dd if=/somefile1-2 of=/dev/null &
     ...
     dd if=/somefile1-100 of=/dev/null
     echo $$ > /cgroup/bio/test2/tasks
     dd if=/somefile2-1 of=/dev/null &
     dd if=/somefile2-2 of=/dev/null &
     ...
     dd if=/somefile2-10 of=/dev/null &
Quoted 5 lines

I have not tried this case.

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that

Quoted 9 lines

always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

Hi Divyesh,

I understand that request descriptors can be a bottleneck here. But that should be an issue even today with CFQ where a low priority process consume lots of request descriptors and prevent higher priority process from submitting the request. I think you already said it and I just reiterated it.

I think in that case we need to do something about request descriptor allocation instead of relying on 2nd level of IO scheduler.
At this point I am not sure what to do. May be we can take feedback from the respective queue (like cfqq) of submitting application and if it is already backlogged beyond a certain limit, then we can put that application to sleep and stop it from consuming excessive amount of request descriptors (despite the fact that we have free request descriptors).

Thanks
Vivek

Quoted 14 lines

-Divyesh

Quoted 12 lines

I have had a very brief look at BFQ's hierarchical proportional weight/priority IO control and it looks good. May be we can adopt it for other IO schedulers also.

Thanks
Vivek
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#142 lore raw
Quoted 92 lines · Vivek Goyal
Quoted 80 lines · Divyesh Shah
Quoted 59 lines · Vivek Goyal
Quoted 27 lines · Ryo Tsuruta

Hi,

From: [email protected]
Subject: [patch 0/4] [RFC] Another proportional weight IO controller
Date: Thu, 06 Nov 2008 10:30:22 -0500

Quoted 21 lines

Hi,

If you are not already tired of so many io controller implementations, here is another one.

This is a very eary very crude implementation to get early feedback to see if this approach makes any sense or not.

This controller is a proportional weight IO controller primarily based on/inspired by dm-ioband. One of the things I personally found little odd about dm-ioband was need of a dm-ioband device for every device we want to control. I thought that probably we can make this control per request queue and get rid of device mapper driver. This should make configuration aspect easy.

I have picked up quite some amount of code from dm-ioband especially for biocgroup implementation.

I have done very basic testing and that is running 2-3 dd commands in different cgroups on x86_64. Wanted to throw out the code early to get some feedback.

Quoted 7 lines
Quoted 3 lines

More details about the design and how to are in documentation patch.

Your comments are welcome.

Do you have any benchmark results?
I'm especially interested in the followings:
- Comparison of disk performance with and without the I/O controller patch.

If I dynamically disable the bio control, then I did not observe any impact on performance. Because in that case practically it boils down to just an additional variable check in __make_request().

Quoted 4 lines

- Put uneven I/O loads. Processes, which belong to a cgroup which is given a smaller weight than another cgroup, put heavier I/O load like the following.

Quoted 13 lines
     echo 1024 > /cgroup/bio/test1/bio.shares
     echo 8192 > /cgroup/bio/test2/bio.shares

     echo $$ > /cgroup/bio/test1/tasks
     dd if=/somefile1-1 of=/dev/null &
     dd if=/somefile1-2 of=/dev/null &
     ...
     dd if=/somefile1-100 of=/dev/null
     echo $$ > /cgroup/bio/test2/tasks
     dd if=/somefile2-1 of=/dev/null &
     dd if=/somefile2-2 of=/dev/null &
     ...
     dd if=/somefile2-10 of=/dev/null &
Quoted 5 lines

I have not tried this case.

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that

Quoted 9 lines

always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

Quoted 6 lines

Hi Divyesh,

I understand that request descriptors can be a bottleneck here. But that should be an issue even today with CFQ where a low priority process consume lots of request descriptors and prevent higher priority process from submitting the request.

Yes that is true and that is one of the main reasons why I would lean towards 2-level scheduler coz you get request queue division as well.

I think you already said it and I just

Quoted 9 lines

reiterated it.

I think in that case we need to do something about request descriptor allocation instead of relying on 2nd level of IO scheduler.
At this point I am not sure what to do. May be we can take feedback from the respective queue (like cfqq) of submitting application and if it is already backlogged beyond a certain limit, then we can put that application to sleep and stop it from consuming excessive amount of request descriptors (despite the fact that we have free request descriptors).

This should be done per-cgroup rather than per-process.

IMHO, to abandon the 2-level approach without having a solid plan for tackling this issue might not be the best idea coz that will invalidate the SLA that the proportional b/w controller promises.

Quoted 18 lines

Thanks
Vivek

Quoted 14 lines

-Divyesh

Quoted 12 lines

I have had a very brief look at BFQ's hierarchical proportional weight/priority IO control and it looks good. May be we can adopt it for other IO schedulers also.

Thanks
Vivek
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

A message that is not in the archive (sent off-list or not yet received): af41c7c40811131457w472e4a86tb5344cc1d3d366fb-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#144 lore raw

On Thu, Nov 13, 2008 at 02:57:29PM -0800, Divyesh Shah wrote:

[..]

Quoted 49 lines
Quoted 28 lines
Quoted 18 lines
Quoted 3 lines

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that

Quoted 9 lines

always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

Quoted 6 lines

Hi Divyesh,

I understand that request descriptors can be a bottleneck here. But that should be an issue even today with CFQ where a low priority process consume lots of request descriptors and prevent higher priority process from submitting the request.

Yes that is true and that is one of the main reasons why I would lean towards 2-level scheduler coz you get request queue division as well.

I think you already said it and I just

Quoted 9 lines

reiterated it.

I think in that case we need to do something about request descriptor allocation instead of relying on 2nd level of IO scheduler.
At this point I am not sure what to do. May be we can take feedback from the respective queue (like cfqq) of submitting application and if it is already backlogged beyond a certain limit, then we can put that application to sleep and stop it from consuming excessive amount of request descriptors (despite the fact that we have free request descriptors).

Quoted 2 lines

This should be done per-cgroup rather than per-process.

Yep, per cgroup limit will make more sense. get_request() already calls elv_may_queue() to get a feedback from IO scheduler. May be here IO scheduler can make a decision how many request descriptors are already allocated to this cgroup. And if the queue is congested, then IO scheduler can deny the fresh request allocation.

Thanks
Vivek

#145 lore raw
In reply to Divyesh Shah

On Thu, Nov 13, 2008 at 02:57:29PM -0800, Divyesh Shah wrote:

[..]

Quoted 49 lines
Quoted 28 lines
Quoted 18 lines
Quoted 3 lines

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that

Quoted 9 lines

always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

Quoted 6 lines

Hi Divyesh,

I understand that request descriptors can be a bottleneck here. But that should be an issue even today with CFQ where a low priority process consume lots of request descriptors and prevent higher priority process from submitting the request.

Yes that is true and that is one of the main reasons why I would lean towards 2-level scheduler coz you get request queue division as well.

I think you already said it and I just

Quoted 9 lines

reiterated it.

I think in that case we need to do something about request descriptor allocation instead of relying on 2nd level of IO scheduler.
At this point I am not sure what to do. May be we can take feedback from the respective queue (like cfqq) of submitting application and if it is already backlogged beyond a certain limit, then we can put that application to sleep and stop it from consuming excessive amount of request descriptors (despite the fact that we have free request descriptors).

Quoted 2 lines

This should be done per-cgroup rather than per-process.

Yep, per cgroup limit will make more sense. get_request() already calls elv_may_queue() to get a feedback from IO scheduler. May be here IO scheduler can make a decision how many request descriptors are already allocated to this cgroup. And if the queue is congested, then IO scheduler can deny the fresh request allocation.

Thanks
Vivek

#146 lore raw
In reply to Divyesh Shah

On Thu, Nov 13, 2008 at 02:57:29PM -0800, Divyesh Shah wrote:

[..]

Quoted 49 lines
Quoted 28 lines
Quoted 18 lines
Quoted 3 lines

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that

Quoted 9 lines

always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

Quoted 6 lines

Hi Divyesh,

I understand that request descriptors can be a bottleneck here. But that should be an issue even today with CFQ where a low priority process consume lots of request descriptors and prevent higher priority process from submitting the request.

Yes that is true and that is one of the main reasons why I would lean towards 2-level scheduler coz you get request queue division as well.

I think you already said it and I just

Quoted 9 lines

reiterated it.

I think in that case we need to do something about request descriptor allocation instead of relying on 2nd level of IO scheduler.
At this point I am not sure what to do. May be we can take feedback from the respective queue (like cfqq) of submitting application and if it is already backlogged beyond a certain limit, then we can put that application to sleep and stop it from consuming excessive amount of request descriptors (despite the fact that we have free request descriptors).

Quoted 2 lines

This should be done per-cgroup rather than per-process.

Yep, per cgroup limit will make more sense. get_request() already calls elv_may_queue() to get a feedback from IO scheduler. May be here IO scheduler can make a decision how many request descriptors are already allocated to this cgroup. And if the queue is congested, then IO scheduler can deny the fresh request allocation.

Thanks
Vivek

A message that is not in the archive (sent off-list or not yet received): [email protected]

#148 lore raw

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.
2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.
3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.
c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

Thanks.
Divyesh & Nauman

Quoted 64 lines · Vivek Goyal

On Thu, Nov 13, 2008 at 02:57:29PM -0800, Divyesh Shah wrote:

[..]

Quoted 49 lines
Quoted 28 lines
Quoted 18 lines
Quoted 3 lines

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that

Quoted 9 lines

always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

Quoted 6 lines

Hi Divyesh,

I understand that request descriptors can be a bottleneck here. But that should be an issue even today with CFQ where a low priority process consume lots of request descriptors and prevent higher priority process from submitting the request.

Yes that is true and that is one of the main reasons why I would lean towards 2-level scheduler coz you get request queue division as well.

I think you already said it and I just

Quoted 9 lines

reiterated it.

I think in that case we need to do something about request descriptor allocation instead of relying on 2nd level of IO scheduler.
At this point I am not sure what to do. May be we can take feedback from the respective queue (like cfqq) of submitting application and if it is already backlogged beyond a certain limit, then we can put that application to sleep and stop it from consuming excessive amount of request descriptors (despite the fact that we have free request descriptors).

Quoted 2 lines

This should be done per-cgroup rather than per-process.

Yep, per cgroup limit will make more sense. get_request() already calls elv_may_queue() to get a feedback from IO scheduler. May be here IO scheduler can make a decision how many request descriptors are already allocated to this cgroup. And if the queue is congested, then IO scheduler can deny the fresh request allocation.

Thanks
Vivek

#149 lore raw
In reply to Vivek Goyal

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.
2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.
3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.
c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

Thanks.
Divyesh & Nauman

Quoted 64 lines · Vivek Goyal

On Thu, Nov 13, 2008 at 02:57:29PM -0800, Divyesh Shah wrote:

[..]

Quoted 49 lines
Quoted 28 lines
Quoted 18 lines
Quoted 3 lines

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that

Quoted 9 lines

always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

Quoted 6 lines

Hi Divyesh,

I understand that request descriptors can be a bottleneck here. But that should be an issue even today with CFQ where a low priority process consume lots of request descriptors and prevent higher priority process from submitting the request.

Yes that is true and that is one of the main reasons why I would lean towards 2-level scheduler coz you get request queue division as well.

I think you already said it and I just

Quoted 9 lines

reiterated it.

I think in that case we need to do something about request descriptor allocation instead of relying on 2nd level of IO scheduler.
At this point I am not sure what to do. May be we can take feedback from the respective queue (like cfqq) of submitting application and if it is already backlogged beyond a certain limit, then we can put that application to sleep and stop it from consuming excessive amount of request descriptors (despite the fact that we have free request descriptors).

Quoted 2 lines

This should be done per-cgroup rather than per-process.

Yep, per cgroup limit will make more sense. get_request() already calls elv_may_queue() to get a feedback from IO scheduler. May be here IO scheduler can make a decision how many request descriptors are already allocated to this cgroup. And if the queue is congested, then IO scheduler can deny the fresh request allocation.

Thanks
Vivek

#150 lore raw
In reply to Vivek Goyal

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.
2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.
3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.
c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

Thanks.
Divyesh & Nauman

Quoted 64 lines · Vivek Goyal

On Thu, Nov 13, 2008 at 02:57:29PM -0800, Divyesh Shah wrote:

[..]

Quoted 49 lines
Quoted 28 lines
Quoted 18 lines
Quoted 3 lines

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that

Quoted 9 lines

always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

Quoted 6 lines

Hi Divyesh,

I understand that request descriptors can be a bottleneck here. But that should be an issue even today with CFQ where a low priority process consume lots of request descriptors and prevent higher priority process from submitting the request.

Yes that is true and that is one of the main reasons why I would lean towards 2-level scheduler coz you get request queue division as well.

I think you already said it and I just

Quoted 9 lines

reiterated it.

I think in that case we need to do something about request descriptor allocation instead of relying on 2nd level of IO scheduler.
At this point I am not sure what to do. May be we can take feedback from the respective queue (like cfqq) of submitting application and if it is already backlogged beyond a certain limit, then we can put that application to sleep and stop it from consuming excessive amount of request descriptors (despite the fact that we have free request descriptors).

Quoted 2 lines

This should be done per-cgroup rather than per-process.

Yep, per cgroup limit will make more sense. get_request() already calls elv_may_queue() to get a feedback from IO scheduler. May be here IO scheduler can make a decision how many request descriptors are already allocated to this cgroup. And if the queue is congested, then IO scheduler can deny the fresh request allocation.

Thanks
Vivek

A message that is not in the archive (sent off-list or not yet received): e98e18940811141444u5947b806v27fac453ed1e8a5-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#152 lore raw
Quoted 7 lines · Nauman Rafique

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.

I have had a brief look at both Satoshi's patch and bfq. I kind of like bfq's patches for keeping track of per cgroup, per queue data structures. May be we can look there also.

Quoted 4 lines

2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.

Quoted 8 lines

3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.

Does dm-iobnd support abosolute limit? I think till last version they did not. I have not check the latest version though.

Quoted 12 lines

c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Quoted 3 lines

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

Thanks
Vivek

Quoted 68 lines

Thanks.
Divyesh & Nauman

Quoted 64 lines · Vivek Goyal

On Thu, Nov 13, 2008 at 02:57:29PM -0800, Divyesh Shah wrote:

[..]

Quoted 49 lines
Quoted 28 lines
Quoted 18 lines
Quoted 3 lines

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that

Quoted 9 lines

always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

Quoted 6 lines

Hi Divyesh,

I understand that request descriptors can be a bottleneck here. But that should be an issue even today with CFQ where a low priority process consume lots of request descriptors and prevent higher priority process from submitting the request.

Yes that is true and that is one of the main reasons why I would lean towards 2-level scheduler coz you get request queue division as well.

I think you already said it and I just

Quoted 9 lines

reiterated it.

I think in that case we need to do something about request descriptor allocation instead of relying on 2nd level of IO scheduler.
At this point I am not sure what to do. May be we can take feedback from the respective queue (like cfqq) of submitting application and if it is already backlogged beyond a certain limit, then we can put that application to sleep and stop it from consuming excessive amount of request descriptors (despite the fact that we have free request descriptors).

Quoted 2 lines

This should be done per-cgroup rather than per-process.

Yep, per cgroup limit will make more sense. get_request() already calls elv_may_queue() to get a feedback from IO scheduler. May be here IO scheduler can make a decision how many request descriptors are already allocated to this cgroup. And if the queue is congested, then IO scheduler can deny the fresh request allocation.

Thanks
Vivek

#153 lore raw
In reply to Nauman Rafique
Quoted 7 lines · Nauman Rafique

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.

I have had a brief look at both Satoshi's patch and bfq. I kind of like bfq's patches for keeping track of per cgroup, per queue data structures. May be we can look there also.

Quoted 4 lines

2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.

Quoted 8 lines

3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.

Does dm-iobnd support abosolute limit? I think till last version they did not. I have not check the latest version though.

Quoted 12 lines

c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Quoted 3 lines

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

Thanks
Vivek

Quoted 68 lines

Thanks.
Divyesh & Nauman

Quoted 64 lines · Vivek Goyal

On Thu, Nov 13, 2008 at 02:57:29PM -0800, Divyesh Shah wrote:

[..]

Quoted 49 lines
Quoted 28 lines
Quoted 18 lines
Quoted 3 lines

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that

Quoted 9 lines

always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

Quoted 6 lines

Hi Divyesh,

I understand that request descriptors can be a bottleneck here. But that should be an issue even today with CFQ where a low priority process consume lots of request descriptors and prevent higher priority process from submitting the request.

Yes that is true and that is one of the main reasons why I would lean towards 2-level scheduler coz you get request queue division as well.

I think you already said it and I just

Quoted 9 lines

reiterated it.

I think in that case we need to do something about request descriptor allocation instead of relying on 2nd level of IO scheduler.
At this point I am not sure what to do. May be we can take feedback from the respective queue (like cfqq) of submitting application and if it is already backlogged beyond a certain limit, then we can put that application to sleep and stop it from consuming excessive amount of request descriptors (despite the fact that we have free request descriptors).

Quoted 2 lines

This should be done per-cgroup rather than per-process.

Yep, per cgroup limit will make more sense. get_request() already calls elv_may_queue() to get a feedback from IO scheduler. May be here IO scheduler can make a decision how many request descriptors are already allocated to this cgroup. And if the queue is congested, then IO scheduler can deny the fresh request allocation.

Thanks
Vivek

#154 lore raw
In reply to Nauman Rafique
Quoted 7 lines · Nauman Rafique

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.

I have had a brief look at both Satoshi's patch and bfq. I kind of like bfq's patches for keeping track of per cgroup, per queue data structures. May be we can look there also.

Quoted 4 lines

2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.

Quoted 8 lines

3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.

Does dm-iobnd support abosolute limit? I think till last version they did not. I have not check the latest version though.

Quoted 12 lines

c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Quoted 3 lines

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

Thanks
Vivek

Quoted 68 lines

Thanks.
Divyesh & Nauman

Quoted 64 lines · Vivek Goyal

On Thu, Nov 13, 2008 at 02:57:29PM -0800, Divyesh Shah wrote:

[..]

Quoted 49 lines
Quoted 28 lines
Quoted 18 lines
Quoted 3 lines

Ryo, do you still want to stick to two level scheduling? Given the problem of it breaking down underlying scheduler's assumptions, probably it makes more sense to the IO control at each individual IO scheduler.

Vivek,
I agree with you that 2 layer scheduler *might* invalidate some
IO scheduler assumptions (though some testing might help here to confirm that). However, one big concern I have with proportional division at the IO scheduler level is that there is no means of doing admission control at the request queue for the device. What we need is request queue partitioning per cgroup.
Consider that I want to divide my disk's bandwidth among 3
cgroups(A, B and C) equally. But say some tasks in the cgroup A flood the disk with IO requests and completely use up all of the requests in the rq resulting in the following IOs to be blocked on a slot getting empty in the rq thus affecting their overall latency. One might argue that over the long term though we'll get equal bandwidth division between these cgroups. But now consider that cgroup A has tasks that

Quoted 9 lines

always storm the disk with large number of IOs which can be a problem for other cgroups.
This actually becomes an even larger problem when we want to
support high priority requests as they may get blocked behind other lower priority requests which have used up all the available requests in the rq. With request queue division we can achieve this easily by having tasks requiring high priority IO belong to a different cgroup. dm-ioband and any other 2-level scheduler can do this easily.

Quoted 6 lines

Hi Divyesh,

I understand that request descriptors can be a bottleneck here. But that should be an issue even today with CFQ where a low priority process consume lots of request descriptors and prevent higher priority process from submitting the request.

Yes that is true and that is one of the main reasons why I would lean towards 2-level scheduler coz you get request queue division as well.

I think you already said it and I just

Quoted 9 lines

reiterated it.

I think in that case we need to do something about request descriptor allocation instead of relying on 2nd level of IO scheduler.
At this point I am not sure what to do. May be we can take feedback from the respective queue (like cfqq) of submitting application and if it is already backlogged beyond a certain limit, then we can put that application to sleep and stop it from consuming excessive amount of request descriptors (despite the fact that we have free request descriptors).

Quoted 2 lines

This should be done per-cgroup rather than per-process.

Yep, per cgroup limit will make more sense. get_request() already calls elv_may_queue() to get a feedback from IO scheduler. May be here IO scheduler can make a decision how many request descriptors are already allocated to this cgroup. And if the queue is congested, then IO scheduler can deny the fresh request allocation.

Thanks
Vivek

A message that is not in the archive (sent off-list or not yet received): [email protected]

#156 lore raw
Quoted 26 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.

I have had a brief look at both Satoshi's patch and bfq. I kind of like bfq's patches for keeping track of per cgroup, per queue data structures. May be we can look there also.

Quoted 4 lines

2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.

Quoted 8 lines

3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.

Quoted 3 lines

Does dm-iobnd support abosolute limit? I think till last version they did not. I have not check the latest version though.

No, dm-ioband still provides weight/share control only. Only Andrea Righi's patches support absolute limit.

Quoted 15 lines
Quoted 15 lines

c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

#157 lore raw
In reply to Vivek Goyal
Quoted 26 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.

I have had a brief look at both Satoshi's patch and bfq. I kind of like bfq's patches for keeping track of per cgroup, per queue data structures. May be we can look there also.

Quoted 4 lines

2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.

Quoted 8 lines

3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.

Quoted 3 lines

Does dm-iobnd support abosolute limit? I think till last version they did not. I have not check the latest version though.

No, dm-ioband still provides weight/share control only. Only Andrea Righi's patches support absolute limit.

Quoted 15 lines
Quoted 15 lines

c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

#158 lore raw
In reply to Vivek Goyal
Quoted 26 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.

I have had a brief look at both Satoshi's patch and bfq. I kind of like bfq's patches for keeping track of per cgroup, per queue data structures. May be we can look there also.

Quoted 4 lines

2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.

Quoted 8 lines

3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.

Quoted 3 lines

Does dm-iobnd support abosolute limit? I think till last version they did not. I have not check the latest version though.

No, dm-ioband still provides weight/share control only. Only Andrea Righi's patches support absolute limit.

Quoted 15 lines
Quoted 15 lines

c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

A message that is not in the archive (sent off-list or not yet received): [email protected]

#160 lore raw

If we start with bfq patches, this is how plan would look like:

1 Start with BFQ take 2.
2 Do the following to support proportional division:
a) Expose the per device weight interface to user, instead of calculating from priority.
b) Add support for disk time budgets, besides sector budget that is currently available (configurable option). (Fabio: Do you think we can just emulate
that using the existing code?). Another approach would be to give time slices just like CFQ (discussing?)
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support is provided by Andrea
Righi's patches too.
c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

BFQ's support for hierarchy of cgroups means that its close to where we want to get. Any comments on what approach looks better?

Quoted 27 lines · Li Zefan
Quoted 26 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.

I have had a brief look at both Satoshi's patch and bfq. I kind of like bfq's patches for keeping track of per cgroup, per queue data structures. May be we can look there also.

Quoted 12 lines

2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.
3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.

Quoted 6 lines
Quoted 3 lines

Does dm-iobnd support abosolute limit? I think till last version they did not. I have not check the latest version though.

No, dm-ioband still provides weight/share control only. Only Andrea Righi's patches support absolute limit.

Thanks for the correction.

Quoted 18 lines
Quoted 16 lines
Quoted 16 lines

c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

#161 lore raw
In reply to Li Zefan

If we start with bfq patches, this is how plan would look like:

1 Start with BFQ take 2.
2 Do the following to support proportional division:
a) Expose the per device weight interface to user, instead of calculating from priority.
b) Add support for disk time budgets, besides sector budget that is currently available (configurable option). (Fabio: Do you think we can just emulate
that using the existing code?). Another approach would be to give time slices just like CFQ (discussing?)
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support is provided by Andrea
Righi's patches too.
c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

BFQ's support for hierarchy of cgroups means that its close to where we want to get. Any comments on what approach looks better?

Quoted 27 lines · Li Zefan
Quoted 26 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.

I have had a brief look at both Satoshi's patch and bfq. I kind of like bfq's patches for keeping track of per cgroup, per queue data structures. May be we can look there also.

Quoted 12 lines

2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.
3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.

Quoted 6 lines
Quoted 3 lines

Does dm-iobnd support abosolute limit? I think till last version they did not. I have not check the latest version though.

No, dm-ioband still provides weight/share control only. Only Andrea Righi's patches support absolute limit.

Thanks for the correction.

Quoted 18 lines
Quoted 16 lines
Quoted 16 lines

c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

#162 lore raw
In reply to Li Zefan

If we start with bfq patches, this is how plan would look like:

1 Start with BFQ take 2.
2 Do the following to support proportional division:
a) Expose the per device weight interface to user, instead of calculating from priority.
b) Add support for disk time budgets, besides sector budget that is currently available (configurable option). (Fabio: Do you think we can just emulate
that using the existing code?). Another approach would be to give time slices just like CFQ (discussing?)
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support is provided by Andrea
Righi's patches too.
c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

BFQ's support for hierarchy of cgroups means that its close to where we want to get. Any comments on what approach looks better?

Quoted 27 lines · Li Zefan
Quoted 26 lines · Vivek Goyal
Quoted 7 lines · Nauman Rafique

In an attempt to make sure that this discussion leads to something useful, we have summarized the points raised in this discussion and have come up with a strategy for future. The goal of this is to find common ground between all the approaches proposed on this mailing list.

1 Start with Satoshi's latest patches.

I have had a brief look at both Satoshi's patch and bfq. I kind of like bfq's patches for keeping track of per cgroup, per queue data structures. May be we can look there also.

Quoted 12 lines

2 Do the following to support propotional division:
a) Give time slices in proportion to weights (configurable option). We can support both priorities and weights by doing propotional division between requests with same priorities.
3 Schedule time slices using WF2Q+ instead of round robin.
Test the performance impact (both throughput and jitter in latency).
4 Do the following to support the goals of 2 level schedulers:
a) Limit the request descriptors allocated to each cgroup by adding functionality to elv_may_queue()
b) Add support for putting an absolute limit on IO consumed by a cgroup. Such support exists in dm-ioband and is provided by Andrea Righi's patches too.

Quoted 6 lines
Quoted 3 lines

Does dm-iobnd support abosolute limit? I think till last version they did not. I have not check the latest version though.

No, dm-ioband still provides weight/share control only. Only Andrea Righi's patches support absolute limit.

Thanks for the correction.

Quoted 18 lines
Quoted 16 lines
Quoted 16 lines

c) Add support (configurable option) to keep track of total disk
time/sectors/count
consumed at each device, and factor that into scheduling decision (more discussion needed here)
5 Support multiple layers of cgroups to align IO controller behavior
with CPU scheduling behavior (more discussion?)
6 Incorporate an IO tracking approach which re-uses memory resource controller code but is not dependent on it (may be biocgroup patches from dm-ioband can be used here directly)
7 Start an offline email thread to keep track of progress on the above goals.

Please feel free to add/modify items to the list
when you respond back. Any comments/suggestions are more than welcome.

A message that is not in the archive (sent off-list or not yet received): e98e18940811172101na345b6bh5c73f9e657aac5a7-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org