introduce bio-cgroup into io-throttle
Subsystems (14)
cover introduce bio-cgroup into io-throttle
Hi all,
For the moment, io-throttle can trace buffered-io by means of memcg. This
patchset introduces bio-cgroup into io-throttle, and splits it from memcg.
For the current implemetation, there are two ways can be used to trace
buffered-io. The first is mount io-throttle and bio-cgroup together.
This is a aggressive way, because there might have some troubles if other
subsystem also want to use bio-cgroup.
The other way is more gentle, io-throttle can use the bio-cgroup id to
associate with a given bio-cgroup. If an association is created, synchronization
between two groups will be performed automatically. This means if one task
adds into or removes from an associated bio-cgroup group, the corresponding
io-throttle group will also add or remove this task.
If one io-throttle associates with a bio-cgroup group, tasks moving in this
io-throttle group is forbidden.
A new io-throttle file blockio.bio_id is added. This file is used to create or remove an association. blockio.bio_id accessing in root hierarchy is not allowed. Following command
is valid.
$echo 1 > /mnt/throttle/group1/blockio.bio_id (associate this io-throttle group with bio-cgroup 1) $echo -1 > /mnt/throttle/group1/blockio.bio_id (remove association between this io-throttle group and bio-cgroup 1)
One bio-cgroup group can't be associated twice. If you do so, error message will show. If io-throttle has been mounted with bio-cgroup, all blockio.bio_id related actions are of no effect.
Dependency checking callback is introduced into cgroup.
You can't mount io-throttle with other subsystems except bio-cgroup. Beacuse other subsystem might break the association between io-throttle and bio-cgroup.
This patchset is against 2.6.28-rc2-mm1.
Signature · 2 lines
Regards Gui Jianfeng
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/7 porting bio-cgroup to 2.6.28-rc2-mm1
From: Ryo Tsuruta <[email protected]>
porting bio-cgroup to 2.6.28-rc2-mm1
--- block/blk-ioc.c | 30 +++-- fs/buffer.c | 2 + fs/direct-io.c | 2 + include/linux/biotrack.h | 82 ++++++++++++ include/linux/cgroup_subsys.h | 6 + include/linux/iocontext.h | 1 + include/linux/memcontrol.h | 14 ++- include/linux/mmzone.h | 4 +- include/linux/page_cgroup.h | 11 ++- init/Kconfig | 15 +++ mm/Makefile | 4 +- mm/biotrack.c | 274 +++++++++++++++++++++++++++++++++++++++++ mm/bounce.c | 2 + mm/filemap.c | 2 + mm/memcontrol.c | 5 + mm/memory.c | 5 + mm/page-writeback.c | 2 + mm/page_cgroup.c | 15 ++- mm/swap_state.c | 2 + 19 files changed, 452 insertions(+), 26 deletions(-) create mode 100644 include/linux/biotrack.h create mode 100644 mm/biotrack.c
block/blk-ioc.c +17 -13
@@ -84,24 +84,28 @@ void exit_io_context(void) } } +void init_io_context (struct io_context *ioc) +{ +atomic_set (&ioc->refcount,1 ); +atomic_set (&ioc->nr_tasks,1 ); +spin_lock_init (&ioc->lock); + ioc->ioprio_changed =0 ; + ioc->ioprio =0 ; + ioc->last_waited = jiffies;/* doesn't matter... */ + ioc->nr_batch_requests =0 ;/* because this is 0 */ + ioc->aic =NULL ; +INIT_RADIX_TREE (&ioc->radix_root,GFP_ATOMIC | __GFP_HIGH); +INIT_HLIST_HEAD (&ioc->cic_list); + ioc->ioc_data =NULL ; +} +struct io_context *alloc_io_context (gfp_t gfp_flags,int node) {struct io_context *ret; ret =kmem_cache_alloc_node (iocontext_cachep, gfp_flags, node); -if (ret) { -atomic_set (&ret->refcount,1 ); -atomic_set (&ret->nr_tasks,1 ); -spin_lock_init (&ret->lock); - ret->ioprio_changed =0 ; - ret->ioprio =0 ; - ret->last_waited = jiffies;/* doesn't matter... */ - ret->nr_batch_requests =0 ;/* because this is 0 */ - ret->aic =NULL ; -INIT_RADIX_TREE (&ret->radix_root,GFP_ATOMIC | __GFP_HIGH); -INIT_HLIST_HEAD (&ret->cic_list); - ret->ioc_data =NULL ; - } +if (ret) +init_io_context (ret);return ret; }
fs/buffer.c +2 -0
@@ -36,6 +36,7 @@#include <linux/buffer_head.h> #include <linux/task_io_accounting_ops.h> #include <linux/bio.h> +#include <linux/biotrack.h> #include <linux/notifier.h> #include <linux/cpu.h> #include <linux/bitops.h>
@@ -779,6 +780,7 @@ static int __set_page_dirty(struct page *page, BDI_RECLAIMABLE);task_io_account_write (PAGE_CACHE_SIZE ); } +bio_cgroup_reset_owner_pagedirty (page, current->mm);radix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); }
fs/direct-io.c +2 -0
@@ -33,6 +33,7 @@#include <linux/err.h> #include <linux/blkdev.h> #include <linux/buffer_head.h> +#include <linux/biotrack.h> #include <linux/rwsem.h> #include <linux/uio.h> #include <asm/atomic.h>
@@ -799,6 +800,7 @@ static int do_direct_IO(struct dio *dio) ret =PTR_ERR (page); goto out; } +bio_cgroup_reset_owner (page, current->mm);while (block_in_page < blocks_per_page) {unsigned offset_in_page = block_in_page << blkbits;
include/linux/biotrack.h new file +82 -0
82 lines not on this page · raw message
include/linux/cgroup_subsys.h +6 -0
@@ -43,6 +43,12 @@ SUBSYS(mem_cgroup)/* */ +#ifdef CONFIG_CGROUP_BIO +SUBSYS(bio_cgroup) +#endif + +/* */ +#ifdef CONFIG_CGROUP_DEVICE SUBSYS(devices)#endif
include/linux/iocontext.h +1 -0
@@ -104,6 +104,7 @@ int put_io_context(struct io_context *ioc);void exit_io_context (void );struct io_context *get_io_context (gfp_t gfp_flags,int node);struct io_context *alloc_io_context (gfp_t gfp_flags,int node); +void init_io_context (struct io_context *ioc);void copy_io_context (struct io_context **pdst,struct io_context **psrc);#else static inline void exit_io_context (void )
include/linux/memcontrol.h +13 -1
@@ -20,12 +20,14 @@#ifndef _LINUX_MEMCONTROL_H#define _LINUX_MEMCONTROL_H -struct mem_cgroup ; +#struct mem_cgroup;struct page_cgroup ;struct page ;struct mm_struct ;#ifdef CONFIG_CGROUP_MEM_RES_CTLR +extern void __init_mem_page_cgroup (struct page_cgroup *pc); +#define mem_cgroup_disabled () mem_cgroup_subsys.disabledextern int mem_cgroup_charge (struct page *page,struct mm_struct *mm,gfp_t gfp_mask);
@@ -71,6 +73,16 @@ extern long mem_cgroup_calc_reclaim(struct mem_cgroup *mem, struct zone *zone,#else /* CONFIG_CGROUP_MEM_RES_CTLR */ +static inline void __init_mem_page_cgroup (struct page_cgroup *pc) +{ +} + +static inline int mem_cgroup_disabled (void ) +{ +return 1 ; +} + +static inline int mem_cgroup_charge (struct page *page,struct mm_struct *mm,gfp_t gfp_mask) {
include/linux/mmzone.h +2 -2
@@ -603,7 +603,7 @@ typedef struct pglist_data {int nr_zones;#ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */ struct page *node_mem_map; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_CGROUP_PAGE struct page_cgroup *node_page_cgroup;#endif #endif
@@ -952,7 +952,7 @@ struct mem_section {/* See declaration of similar field in struct zone */ unsigned long *pageblock_flags; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_CGROUP_PAGE /* * If !SPARSEMEM, pgdat doesn't have page_cgroup pointer. We use * section. (see memcontrol.h/page_cgroup.h about this.)
include/linux/page_cgroup.h +8 -3
@@ -1,7 +1,7 @@#ifndef __LINUX_PAGE_CGROUP_H#define __LINUX_PAGE_CGROUP_H -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_CGROUP_PAGE #include <linux/bit_spinlock.h> /* * Page Cgroup can be considered as an extended mem_map.
@@ -12,9 +12,14 @@*/ struct page_cgroup {unsigned long flags; -struct mem_cgroup *mem_cgroup;struct page *page; +#ifdef CONFIG_CGROUP_MEM_RES_CTLR +struct mem_cgroup *mem_cgroup;struct list_head lru;/* per cgroup LRU list */ +#endif +#ifdef CONFIG_CGROUP_BIO +int bio_cgroup_id; +#endif };void __init pgdat_page_cgroup_init (struct pglist_data *pgdat);
@@ -88,7 +93,7 @@ static inline void unlock_page_cgroup(struct page_cgroup *pc) bit_spin_unlock(PCG_LOCK, &pc->flags); } -#else /* CONFIG_CGROUP_MEM_RES_CTLR */ +#else /* CONFIG_CGROUP_PAGE */ struct page_cgroup ;static inline void pgdat_page_cgroup_init (struct pglist_data *pgdat)
init/Kconfig +15 -0
@@ -393,6 +393,21 @@ config RESOURCE_COUNTERS infrastructure that works with cgroups depends on CGROUPS +config CGROUP_BIO + bool "Block I/O cgroup subsystem" + depends on CGROUPS && BLOCK + select MM_OWNER + help + Provides a Resource Controller which enables to track the onwner + of every Block I/O requests. + The information this subsystem provides can be used from any + kind of module such as dm-ioband device mapper modules or + the cfq-scheduler. + +config CGROUP_PAGE + def_bool y + depends on CGROUP_MEM_RES_CTLR || CGROUP_BIO + config MM_OWNER bool
mm/Makefile +3 -1
@@ -34,5 +34,7 @@ obj-$(CONFIG_FS_XIP) += filemap_xip.o obj-$(CONFIG_MIGRATION) += migrate.o obj-$(CONFIG_SMP) += allocpercpu.o obj-$(CONFIG_QUICKLIST) += quicklist.o -obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o +obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o +obj-$(CONFIG_CGROUP_PAGE) += page_cgroup.o +obj-$(CONFIG_CGROUP_BIO) += biotrack.o obj-$(CONFIG_KMEMTRACE) += kmemtrace.o
mm/biotrack.c new file +274 -0
274 lines not on this page · raw message
mm/bounce.c +2 -0
@@ -14,6 +14,7 @@#include <linux/hash.h> #include <linux/highmem.h> #include <linux/blktrace_api.h> +#include <linux/biotrack.h> #include <asm/tlbflush.h> #define POOL_SIZE 64
@@ -204,6 +205,7 @@ static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig, to->bv_len = from->bv_len; to->bv_offset = from->bv_offset;inc_zone_page_state (to->bv_page,NR_BOUNCE ); +bio_cgroup_copy_owner (to->bv_page, page);if (rw ==WRITE ) {char *vto, *vfrom;
mm/filemap.c +2 -0
@@ -33,6 +33,7 @@#include <linux/cpuset.h> #include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */ #include <linux/memcontrol.h> +#include <linux/biotrack.h> #include <linux/mm_inline.h> /* for page_is_file_cache() */ #include "internal.h"
@@ -464,6 +465,7 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping, gfp_mask & ~__GFP_HIGHMEM);if (error) goto out; +bio_cgroup_set_owner (page, current->mm); error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM);if (error ==0 ) {
mm/memcontrol.c +5 -0
@@ -157,6 +157,11 @@ pcg_default_flags[NR_CHARGE_TYPE] = { 0, /* FORCE */ }; +void __meminit __init_mem_page_cgroup (struct page_cgroup *pc) +{ + pc->mem_cgroup =NULL ; +} +/* * Always modified under lru lock. Then, not necessary to preempt_disable() */
mm/memory.c +5 -0
@@ -51,6 +51,7 @@#include <linux/init.h> #include <linux/writeback.h> #include <linux/memcontrol.h> +#include <linux/biotrack.h> #include <linux/mmu_notifier.h> #include <asm/pgalloc.h>
@@ -1915,6 +1916,7 @@ gotten:*/ ptep_clear_flush_notify (vma, address, page_table);SetPageSwapBacked (new_page); +bio_cgroup_set_owner (new_page, mm);lru_cache_add_active_or_unevictable (new_page, vma);page_add_new_anon_rmap (new_page, vma, address);
@@ -2353,6 +2355,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,flush_icache_page (vma, page);set_pte_at (mm, address, page_table, pte);page_add_anon_rmap (page, vma, address); +bio_cgroup_reset_owner (page, mm);swap_free (entry);if (vm_swap_full () || (vma->vm_flags &VM_LOCKED ) ||PageMlocked (page))
@@ -2414,6 +2417,7 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, goto release;inc_mm_counter (mm, anon_rss);SetPageSwapBacked (page); +bio_cgroup_set_owner (page, mm);lru_cache_add_active_or_unevictable (page, vma);page_add_new_anon_rmap (page, vma, address);set_pte_at (mm, address, page_table, entry);
@@ -2563,6 +2567,7 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,if (anon) {inc_mm_counter (mm, anon_rss);SetPageSwapBacked (page); +bio_cgroup_set_owner (page, mm);lru_cache_add_active_or_unevictable (page, vma);page_add_new_anon_rmap (page, vma, address); }else {
mm/page-writeback.c +2 -0
@@ -23,6 +23,7 @@#include <linux/init.h> #include <linux/backing-dev.h> #include <linux/task_io_accounting_ops.h> +#include <linux/biotrack.h> #include <linux/blkdev.h> #include <linux/mpage.h> #include <linux/rmap.h>
@@ -1100,6 +1101,7 @@ int __set_page_dirty_nobuffers(struct page *page) BDI_RECLAIMABLE);task_io_account_write (PAGE_CACHE_SIZE ); } +bio_cgroup_reset_owner_pagedirty (page, current->mm);radix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); }
mm/page_cgroup.c +9 -6
@@ -8,13 +8,16 @@#include <linux/memory.h> #include <linux/vmalloc.h> #include <linux/cgroup.h> +#include <linux/memcontrol.h> +#include <linux/biotrack.h> static void __meminit __init_page_cgroup (struct page_cgroup *pc,unsigned long pfn) { pc->flags =0 ; - pc->mem_cgroup =NULL ; pc->page =pfn_to_page (pfn); +__init_mem_page_cgroup (pc); +__init_bio_page_cgroup (pc); }static unsigned long total_usage;
@@ -69,7 +72,7 @@ void __init page_cgroup_init(void)int nid, fail; -if (mem_cgroup_subsys.disabled) +if (mem_cgroup_disabled () &&bio_cgroup_disabled ())return ;for_each_online_node (nid) {
@@ -78,12 +81,12 @@ void __init page_cgroup_init(void) goto fail; }printk (KERN_INFO "allocated %ld bytes of page_cgroup\n" , total_usage); -printk (KERN_INFO "please try cgroup_disable=memory option if you" +printk (KERN_INFO "please try cgroup_disable=memory,bio option if you" " don't want\n" );return ; fail:printk (KERN_CRIT "allocation of page_cgroup was failed.\n" ); -printk (KERN_CRIT "please try cgroup_disable=memory boot option\n" ); +printk (KERN_CRIT "please try cgroup_disable=memory,bio boot options\n" );panic ("Out of memory" ); }
@@ -229,7 +232,7 @@ void __init page_cgroup_init(void)unsigned long pfn;int fail =0 ; -if (mem_cgroup_subsys.disabled) +if (mem_cgroup_disabled () &&bio_cgroup_disabled ())return ;for (pfn =0 ; !fail && pfn < max_pfn; pfn +=PAGES_PER_SECTION ) {
@@ -244,7 +247,7 @@ void __init page_cgroup_init(void)hotplug_memory_notifier (page_cgroup_callback,0 ); }printk (KERN_INFO "allocated %ld bytes of page_cgroup\n" , total_usage); -printk (KERN_INFO "please try cgroup_disable=memory option if you don't" +printk (KERN_INFO "please try cgroup_disable=memory,bio option if you don't" " want\n" ); }
mm/swap_state.c +2 -0
@@ -17,6 +17,7 @@#include <linux/backing-dev.h> #include <linux/pagevec.h> #include <linux/migrate.h> +#include <linux/biotrack.h> #include <asm/pgtable.h>
@@ -305,6 +306,7 @@ struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,*/ __set_page_locked (new_page);SetPageSwapBacked (new_page); +bio_cgroup_set_owner (new_page, current->mm); err =add_to_swap_cache (new_page, entry, gfp_mask &GFP_KERNEL );if (likely (!err)) {/*
-- 1.5.4.rc3
PATCH 2/7 Porting io-throttle v11 to 2.6.28-rc2-mm1
In reply to a message not in the archiveFrom: Andrea Righi <[email protected]>
Porting io-throttle v11 to 2.6.28-rc2-mm1
--- Documentation/controllers/io-throttle.txt | 409 ++++++++++++++++ block/Makefile | 2 + block/blk-core.c | 4 + block/blk-io-throttle.c | 735 +++++++++++++++++++++++++++++ fs/aio.c | 12 + fs/direct-io.c | 3 + fs/proc/base.c | 18 + include/linux/blk-io-throttle.h | 95 ++++ include/linux/cgroup_subsys.h | 6 + include/linux/memcontrol.h | 5 +- include/linux/res_counter.h | 69 ++- include/linux/sched.h | 7 + init/Kconfig | 10 + kernel/fork.c | 8 + kernel/res_counter.c | 73 +++- mm/memcontrol.c | 30 ++ mm/page-writeback.c | 4 + mm/readahead.c | 3 + 18 files changed, 1474 insertions(+), 19 deletions(-) create mode 100644 Documentation/controllers/io-throttle.txt create mode 100644 block/blk-io-throttle.c create mode 100644 include/linux/blk-io-throttle.h
Documentation/controllers/io-throttle.txt new file +409 -0
@@ -0,0 +1,409 @@ + + Block device I/O bandwidth controller + +---------------------------------------------------------------------- +1. DESCRIPTION + +This controller allows to limit the I/O bandwidth of specific block devices for +specific process containers (cgroups [1]) imposing additional delays on I/O +requests for those processes that exceed the limits defined in the control +group filesystem. + +Bandwidth limiting rules offer better control over QoS with respect to priority +or weight-based solutions that only give information about applications' +relative performance requirements. Nevertheless, priority based solutions are +affected by performance bursts, when only low-priority requests are submitted +to a general purpose resource dispatcher. + +The goal of the I/O bandwidth controller is to improve performance +predictability from the applications' point of view and provide performance +isolation of different control groups sharing the same block devices. + +NOTE #1: If you're looking for a way to improve the overall throughput of the +system probably you should use a different solution. + +NOTE #2: The current implementation does not guarantee minimum bandwidth +levels, the QoS is implemented only slowing down I/O "traffic" that exceeds the +limits specified by the user; minimum I/O rate thresholds are supposed to be +guaranteed if the user configures a proper I/O bandwidth partitioning of the +block devices shared among the different cgroups (theoretically if the sum of +all the single limits defined for a block device doesn't exceed the total I/O +bandwidth of that device). + +---------------------------------------------------------------------- +2. USER INTERFACE + +A new I/O limitation rule is described using the files: +- blockio.bandwidth-max +- blockio.iops-max + +The I/O bandwidth (blockio.bandwidth-max) can be used to limit the throughput +of a certain cgroup, while blockio.iops-max can be used to throttle cgroups +containing applications doing a sparse/seeky I/O workload. Any combination of +them can be used to define more complex I/O limiting rules, expressed both in +terms of iops/s and bandwidth. + +The same files can be used to set multiple rules for different block devices +relative to the same cgroup. + +The following syntax can be used to configure any limiting rule: + +# /bin/echo DEV:LIMIT:STRATEGY:BUCKET_SIZE > CGROUP/FILE + +- DEV is the name of the device the limiting rule is applied to. + +- LIMIT is the maximum I/O activity allowed on DEV by CGROUP; LIMIT can + represent a bandwidth limitation (expressed in bytes/s) when writing to + blockio.bandwidth-max, or a limitation to the maximum I/O operations per + second (expressed in iops/s) issued by CGROUP. + + A generic I/O limiting rule for a block device DEV can be removed setting the + LIMIT to 0. + +- STRATEGY is the throttling strategy used to throttle the applications' I/O + requests from/to device DEV. At the moment two different strategies can be + used [2][3]: + + 0 = leaky bucket: the controller accepts at most B bytes (B = LIMIT * time) + or O operations (O = LIMIT * time); further I/O requests + are delayed scheduling a timeout for the tasks that made + those requests. + + Different I/O flow + | | | + | v | + | v + v + ....... + \ / + \ / leaky-bucket + --- + ||| + vvv + Smoothed I/O flow + + 1 = token bucket: LIMIT tokens are added to the bucket every seconds; the + bucket can hold at the most BUCKET_SIZE tokens; I/O + requests are accepted if there are available tokens in the + bucket; when a request of N bytes arrives N tokens are + removed from the bucket; if fewer than N tokens are + available the request is delayed until a sufficient amount + of token is available in the bucket. + + Tokens (I/O rate) + o + o + o + ....... <--. + \ / | Bucket size (burst limit) + \ooo/ | + --- <--' + |ooo + Incoming --->|---> Conforming + I/O |oo I/O + requests -->|--> requests + | + ---->| + + Leaky bucket is more precise than token bucket to respect the limits, because + bursty workloads are always smoothed. Token bucket, instead, allows a small + irregularity degree in the I/O flows (burst limit), and, for this, it is + better in terms of efficiency (bursty workloads are not smoothed when there + are sufficient tokens in the bucket). + +- BUCKET_SIZE is used only with token bucket (STRATEGY == 1) and defines the + size of the bucket in bytes (blockio.bandwidth-max) or in I/O operations + (blockio.iops-max). + +- CGROUP is the name of the limited process container. + +Also the following syntaxes are allowed: + +- remove an I/O bandwidth limiting rule +# /bin/echo DEV:0 > CGROUP/blockio.bandwidth-max + +- configure a limiting rule using leaky bucket throttling (ignore bucket size): +# /bin/echo DEV:LIMIT:0 > CGROUP/blockio.bandwidth-max + +- configure a limiting rule using token bucket throttling + (with bucket size == LIMIT): +# /bin/echo DEV:LIMIT:1 > CGROUP/blockio.bandwidth-max + +2.2. Show I/O limiting rules + +All the defined rules and statistics for a specific cgroup can be shown reading +the files blockio.bandwidth-max for bandwidth constraints and blockio.iops-max +for I/O operations per second constraints. + +The following syntax is used: + +$ cat CGROUP/blockio.bandwidth-max +MAJOR MINOR LIMIT STRATEGY LEAKY_STAT BUCKET_SIZE BUCKET_FILL TIME_DELTA + +- MAJOR is the major device number of DEV (defined above) + +- MINOR is the minor device number of DEV (defined above) + +- LIMIT, STRATEGY and BUCKET_SIZE are the same parameters defined above + +- LEAKY_STAT is the amount of bytes (blockio.bandwidth-max) or I/O operations + (blockio.iops-max) currently allowed by the I/O controller (only used with + leaky bucket strategy - STRATEGY == 0) + +- BUCKET_FILL represents the amount of tokens present in the bucket (only used + with token bucket strategy - STRATEGY == 1) + +- TIME_DELTA can be one of the following: + - the amount of jiffies elapsed from the last I/O request (token bucket) + - the amount of jiffies during which the bytes or the number of I/O + operations given by LEAKY_STAT have been accumulated (leaky bucket) + +Multiple per-block device rules are reported in multiple rows +(DEVi, i = 1 .. n): + +$ cat CGROUP/blockio.bandwidth-max +MAJOR1 MINOR1 BW1 STRATEGY1 LEAKY_STAT1 BUCKET_SIZE1 BUCKET_FILL1 TIME_DELTA1 +MAJOR1 MINOR1 BW2 STRATEGY2 LEAKY_STAT2 BUCKET_SIZE2 BUCKET_FILL2 TIME_DELTA2 +... +MAJORn MINORn BWn STRATEGYn LEAKY_STATn BUCKET_SIZEn BUCKET_FILLn TIME_DELTAn + +The same fields are used to describe I/O operations/sec rules. The only +difference is that the cost of each I/O operation is scaled up by a factor of +1000. This allows to apply better fine grained sleeps and provide a more +precise throttling. + +$ cat CGROUP/blockio.iops-max +MAJOR MINOR LIMITx1000 STRATEGY LEAKY_STATx1000 BUCKET_SIZEx1000 BUCKET_FILLx1000 TIME_DELTA +... + +2.3. Additional I/O statistics + +Additional cgroup I/O throttling statistics are reported in +blockio.throttlecnt: + +$ cat CGROUP/blockio.throttlecnt +MAJOR MINOR BW_COUNTER BW_SLEEP IOPS_COUNTER IOPS_SLEEP + + - MAJOR, MINOR are respectively the major and the minor number of the device + the following statistics refer to + - BW_COUNTER gives the number of times that the cgroup bandwidth limit of + this particular device was exceeded + - BW_SLEEP is the amount of sleep time measured in clock ticks (divide + by sysconf(_SC_CLK_TCK)) imposed to the processes of this cgroup that + exceeded the bandwidth limit for this particular device + - IOPS_COUNTER gives the number of times that the cgroup I/O operation per + second limit of this particular device was exceeded + - IOPS_SLEEP is the amount of sleep time measured in clock ticks (divide + by sysconf(_SC_CLK_TCK)) imposed to the processes of this cgroup that + exceeded the I/O operations per second limit for this particular device + +Example: +$ cat CGROUP/blockio.throttlecnt +8 0 0 0 0 0 +^ ^ ^ ^ ^ ^ + \ \ \ \ \ \___iops sleep (in clock ticks) + \ \ \ \ \____iops throttle counter + \ \ \ \_____bandwidth sleep (in clock ticks) + \ \ \______bandwidth throttle counter + \ \_______minor dev. number + \________major dev. number + +Distinct statistics for each process are reported in +/proc/PID/io-throttle-stat: + +$ cat /proc/PID/io-throttle-stat +BW_COUNTER BW_SLEEP IOPS_COUNTER IOPS_SLEEP + +Example: +$ cat /proc/$$/io-throttle-stat +0 0 0 0 +^ ^ ^ ^ + \ \ \ \_____global iops sleep (in clock ticks) + \ \ \______global iops counter + \ \_______global bandwidth sleep (clock ticks) + \________global bandwidth counter + +2.4. Examples + +* Mount the cgroup filesystem (blockio subsystem): + # mkdir /mnt/cgroup + # mount -t cgroup -oblockio blockio /mnt/cgroup + +* Instantiate the new cgroup "foo": + # mkdir /mnt/cgroup/foo + --> the cgroup foo has been created + +* Add the current shell process to the cgroup "foo": + # /bin/echo $$ > /mnt/cgroup/foo/tasks + --> the current shell has been added to the cgroup "foo" + +* Give maximum 1MiB/s of I/O bandwidth on /dev/sda for the cgroup "foo", using + leaky bucket throttling strategy: + # /bin/echo /dev/sda:$((1024 * 1024)):0:0 > \ + > /mnt/cgroup/foo/blockio.bandwidth-max + # sh + --> the subshell 'sh' is running in cgroup "foo" and it can use a maximum I/O + bandwidth of 1MiB/s on /dev/sda + +* Give maximum 8MiB/s of I/O bandwidth on /dev/sdb for the cgroup "foo", using + token bucket throttling strategy, bucket size = 8MiB: + # /bin/echo /dev/sdb:$((8 * 1024 * 1024)):1:$((8 * 1024 * 1024)) > \ + > /mnt/cgroup/foo/blockio.bandwidth-max + # sh + --> the subshell 'sh' is running in cgroup "foo" and it can use a maximum I/O + bandwidth of 1MiB/s on /dev/sda (controlled by leaky bucket throttling) + and 8MiB/s on /dev/sdb (controlled by token bucket throttling) + +* Run a benchmark doing I/O on /dev/sda and /dev/sdb; I/O limits and usage + defined for cgroup "foo" can be shown as following: + # cat /mnt/cgroup/foo/blockio.bandwidth-max + 8 16 8388608 1 0 8388608 -522560 48 + 8 0 1048576 0 737280 0 0 216 + +* Extend the maximum I/O bandwidth for the cgroup "foo" to 16MiB/s on /dev/sda: + # /bin/echo /dev/sda:$((16 * 1024 * 1024)):0:0 > \ + > /mnt/cgroup/foo/blockio.bandwidth-max + # cat /mnt/cgroup/foo/blockio.bandwidth-max + 8 16 8388608 1 0 8388608 -84432 206436 + 8 0 16777216 0 0 0 0 15212 + +* Remove limiting rule on /dev/sdb for cgroup "foo": + # /bin/echo /dev/sdb:0:0:0 > /mnt/cgroup/foo/blockio.bandwidth-max + # cat /mnt/cgroup/foo/blockio.bandwidth-max + 8 0 16777216 0 0 0 0 110388 + +* Set a maximum of 100 I/O operations/sec (leaky bucket strategy) to /dev/sdc + for cgroup "foo": + # /bin/echo /dev/sdc:100:0 > /mnt/cgroup/foo/blockio.iops-max + # cat /mnt/cgroup/foo/blockio.iops-max + 8 32 100000 0 846000 0 2113 + ^ ^ + /________/ + / + Remember: these values are scaled up by a factor of 1000 to apply a fine + grained throttling (i.e. LIMIT == 100000 means a maximum of 100 I/O operation + per second) + +* Remove limiting rule for I/O operations from /dev/sdc for cgroup "foo": + # /bin/echo /dev/sdc:0 > /mnt/cgroup/foo/blockio.iops-max + +---------------------------------------------------------------------- +3. ADVANTAGES OF PROVIDING THIS FEATURE + +* Allow I/O traffic shaping for block device shared among different cgroups +* Improve I/O performance predictability on block devices shared between + different cgroups +* Limiting rules do not depend of the particular I/O scheduler (anticipatory, + deadline, CFQ, noop) and/or the type of the underlying block devices +* The bandwidth limitations are guaranteed both for synchronous and + asynchronous operations, even the I/O passing through the page cache or + buffers and not only direct I/O (see below for details) +* It is possible to implement a simple user-space application to dynamically + adjust the I/O workload of different process containers at run-time, + according to the particular users' requirements and applications' performance + constraints + +---------------------------------------------------------------------- +4. DESIGN + +The I/O throttling is performed imposing an explicit timeout, via +schedule_timeout_killable() on the processes that exceed the I/O limits +dedicated to the cgroup they belong to. I/O accounting happens per cgroup. + +It just works as expected for read operations: the real I/O activity is reduced +synchronously according to the defined limitations. + +Multiple re-reads of pages already present in the page cache are not considered +to account the I/O activity, since they actually don't generate any real I/O +operation. + +This means that a process that re-reads multiple times the same blocks of a +file is affected by the I/O limitations only for the actual I/O performed from +the underlying block devices. + +For write operations the scenario is a bit more complex, because the writes in +the page cache are processed asynchronously by kernel threads (pdflush), using +a write-back policy. So the real writes to the underlying block devices occur +in a different I/O context respect to the task that originally generated the +dirty pages. + +The I/O bandwidth controller uses the following solution to resolve this +problem. + +The cost of each I/O operation is always accounted when the operation is +submitted to the I/O subsystem (submit_bio()). + +If the operation is a read then we automatically know that the context of the +request is the current task and so we can charge the cgroup the current task +belongs to. And throttle the current task as well, if it exceeded the cgroup +limitations. + +If the operation is a write, we can charge the right cgroup looking at the +owner of the first page involved in the I/O operation, that gives the context +that generated the I/O activity at the source. This information can be +retrieved using the page_cgroup functionality provided by the cgroup memory +controller [4]. In this way we can correctly account the I/O cost to the right +cgroup, but we cannot throttle the current task in this stage, because, in +general, it is a different task (e.g. a kernel thread that is processing +asynchronously the dirty page). For this reason, throttling of write operations +is always performed asynchronously in balance_dirty_pages_ratelimited_nr(), a +function always called by processes which are dirtying memory. + +Multiple rules for different block devices are stored in a linked list, using +the dev_t number of each block device as key to uniquely identify each element +of the list. RCU synchronization is used to protect the whole list structure, +since the elements in the list are not supposed to change frequently (they +change only when a new rule is defined or an old rule is removed or updated), +while the reads in the list occur at each operation that generates I/O. This +allows to provide zero overhead for cgroups that do not use any limitation. + +WARNING: per-block device limiting rules always refer to the dev_t device +number. If a block device is unplugged (i.e. a USB device) the limiting rules +defined for that device persist and they are still valid if a new device is +plugged in the system and it uses the same major and minor numbers. + +NOTE: explicit sleeps are *not* imposed on tasks doing asynchronous I/O (AIO) +operations; AIO throttling is performed returning -EAGAIN from sys_io_submit(). +Userspace applications must be able to handle this error code opportunely. + +---------------------------------------------------------------------- +5. TODO + +* Implement a rbtree per request queue; all the requests queued to the I/O + subsystem first will go in this rbtree. Then based on cgroup grouping and + control policy dispatch the requests and pass them to the elevator associated + with the queue. This would allow to provide both bandwidth limiting and + proportional bandwidth functionalities using a generic approach (suggested by + Vivek Goyal) + +* Improve fair throttling: distribute the time to sleep among all the tasks of + a cgroup that exceeded the I/O limits, depending of the amount of IO activity + previously generated in the past by each task (see task_io_accounting) + +* Try to reduce the cost of calling cgroup_io_throttle() on every submit_bio(); + this is not too much expensive, but the call of task_subsys_state() has + surely a cost. A possible solution could be to temporarily account I/O in the + current task_struct and call cgroup_io_throttle() only on each X MB of I/O. + Or on each Y number of I/O requests as well. Better if both X and/or Y can be + tuned at runtime by a userspace tool + +* Think an alternative design for general purpose usage; special purpose usage + right now is restricted to improve I/O performance predictability and + evaluate more precise response timings for applications doing I/O. To a large + degree the block I/O bandwidth controller should implement a more complex + logic to better evaluate real I/O operations cost, depending also on the + particular block device profile (i.e. USB stick, optical drive, hard disk, + etc.). This would also allow to appropriately account I/O cost for seeky + workloads, respect to large stream workloads. Instead of looking at the + request stream and try to predict how expensive the I/O cost will be, a + totally different approach could be to collect request timings (start time / + elapsed time) and based on collected informations, try to estimate the I/O + cost and usage + +---------------------------------------------------------------------- +6. REFERENCES + +[1] Documentation/cgroups/cgroups.txt +[2] http://en.wikipedia.org/wiki/Leaky_bucket +[3] http://en.wikipedia.org/wiki/Token_bucket +[4] Documentation/controllers/memory.txt
block/Makefile +2 -0
@@ -13,6 +13,8 @@ obj-$(CONFIG_IOSCHED_AS) += as-iosched.o obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o +obj-$(CONFIG_CGROUP_IO_THROTTLE) += blk-io-throttle.o + obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o
block/blk-core.c +4 -0
@@ -26,6 +26,7 @@#include <linux/swap.h> #include <linux/writeback.h> #include <linux/task_io_accounting_ops.h> +#include <linux/blk-io-throttle.h> #include <linux/blktrace_api.h> #include <linux/fault-inject.h>
@@ -1536,9 +1537,12 @@ void submit_bio(int rw, struct bio *bio)if (bio_has_data (bio)) {if (rw &WRITE ) {count_vm_events (PGPGOUT , count); +cgroup_io_throttle (bio_iovec_idx (bio,0 )->bv_page, + bio->bi_bdev, bio->bi_size,0 ); }else {task_io_account_read (bio->bi_size);count_vm_events (PGPGIN , count); +cgroup_io_throttle (NULL , bio->bi_bdev, bio->bi_size,1 ); }if (unlikely (block_dump)) {
block/blk-io-throttle.c new file +735 -0
735 lines not on this page · raw message
fs/aio.c +12 -0
@@ -22,6 +22,7 @@#include <linux/sched.h> #include <linux/fs.h> #include <linux/file.h> +#include <linux/blk-io-throttle.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/slab.h>
@@ -1558,6 +1559,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, {struct kiocb *req;struct file *file; +struct block_device *bdev;ssize_t ret;/* enforce forwards compatibility on users */
@@ -1580,6 +1582,14 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,if (unlikely (!file))return -EBADF ; +/* check if we're exceeding the IO throttling limits */ + bdev =as_to_bdev (file->f_mapping); + ret =cgroup_io_throttle (NULL , bdev,0 ,0 ); +if (unlikely (ret)) { +fput (file); +return -EAGAIN ; + } + req =aio_get_req (ctx);/* returns with 2 references to req */ if (unlikely (!req)) {fput (file);
@@ -1622,12 +1632,14 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, goto out_put_req;spin_lock_irq (&ctx->ctx_lock); +set_in_aio ();aio_run_iocb (req);if (!list_empty (&ctx->run_list)) {/* drain the run list */ while (__aio_run_iocbs (ctx)) ; } +unset_in_aio ();spin_unlock_irq (&ctx->ctx_lock);aio_put_req (req);/* drop extra ref to req */ return 0 ;
fs/direct-io.c +3 -0
@@ -28,6 +28,7 @@#include <linux/highmem.h> #include <linux/pagemap.h> #include <linux/task_io_accounting_ops.h> +#include <linux/blk-io-throttle.h> #include <linux/bio.h> #include <linux/wait.h> #include <linux/err.h>
@@ -658,10 +659,12 @@ submit_page_section(struct dio *dio, struct page *page,int ret =0 ;if (dio->rw &WRITE ) { +struct block_device *bdev = dio->inode->i_sb->s_bdev;/* * Read accounting is performed in submit_bio() */ task_io_account_write (len); +cgroup_io_throttle (NULL , bdev,0 ,1 ); }/*
fs/proc/base.c +18 -0
@@ -54,6 +54,7 @@#include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/task_io_accounting_ops.h> +#include <linux/blk-io-throttle.h> #include <linux/init.h> #include <linux/capability.h> #include <linux/file.h>
@@ -2458,6 +2459,17 @@ static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns,return 0 ; } +#ifdef CONFIG_CGROUP_IO_THROTTLE +static int proc_iothrottle_stat (struct task_struct *task,char *buffer) +{ +return sprintf (buffer,"%llu %llu %llu %llu\n" , +get_io_throttle_cnt (task,IOTHROTTLE_BANDWIDTH ), +get_io_throttle_sleep (task,IOTHROTTLE_BANDWIDTH ), +get_io_throttle_cnt (task,IOTHROTTLE_IOPS ), +get_io_throttle_sleep (task,IOTHROTTLE_IOPS )); +} +#endif /* CONFIG_CGROUP_IO_THROTTLE */ +/* * Thread groups */
@@ -2534,6 +2546,9 @@ static const struct pid_entry tgid_base_stuff[] = {#ifdef CONFIG_TASK_IO_ACCOUNTING INF("io", S_IRUGO, tgid_io_accounting),#endif +#ifdef CONFIG_CGROUP_IO_THROTTLE + INF("io-throttle-stat", S_IRUGO, iothrottle_stat), +#endif }; static int proc_tgid_base_readdir(struct file * filp,
@@ -2866,6 +2881,9 @@ static const struct pid_entry tid_base_stuff[] = {#ifdef CONFIG_TASK_IO_ACCOUNTING INF("io", S_IRUGO, tid_io_accounting),#endif +#ifdef CONFIG_CGROUP_IO_THROTTLE + INF("io-throttle-stat", S_IRUGO, iothrottle_stat), +#endif }; static int proc_tid_base_readdir(struct file * filp,
include/linux/blk-io-throttle.h new file +95 -0
95 lines not on this page · raw message
include/linux/cgroup_subsys.h +6 -0
@@ -55,6 +55,12 @@ SUBSYS(devices)/* */ +#ifdef CONFIG_CGROUP_IO_THROTTLE +SUBSYS(iothrottle) +#endif + +/* */ +#ifdef CONFIG_CGROUP_FREEZER SUBSYS(freezer)#endif
include/linux/memcontrol.h +4 -1
@@ -20,7 +20,7 @@#ifndef _LINUX_MEMCONTROL_H#define _LINUX_MEMCONTROL_H -#struct mem_cgroup; +struct mem_cgroup ;struct page_cgroup ;struct page ;struct mm_struct ;
@@ -49,6 +49,9 @@ int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem);extern struct mem_cgroup *mem_cgroup_from_task (struct task_struct *p); +extern struct cgroup *get_cgroup_from_page (struct page *page); +extern void put_cgroup_from_page (struct page *page); +#define mm_match_cgroup (mm, cgroup) \ ((cgroup) == mem_cgroup_from_task((mm)->owner))
include/linux/res_counter.h +52 -17
100 lines not on this page · raw message
include/linux/sched.h +7 -0
@@ -1250,6 +1250,13 @@ struct task_struct {unsigned long ptrace_message;siginfo_t *last_siginfo;/* For ptrace use. */ struct task_io_accounting ioac; +#ifdef CONFIG_CGROUP_IO_THROTTLE +atomic_t in_aio; +unsigned long long io_throttle_bw_cnt; +unsigned long long io_throttle_bw_sleep; +unsigned long long io_throttle_iops_cnt; +unsigned long long io_throttle_iops_sleep; +#endif #if defined(CONFIG_TASK_XACCT )u64 acct_rss_mem1;/* accumulated rss usage */ u64 acct_vm_mem1;/* accumulated virtual memory usage */
init/Kconfig +10 -0
@@ -313,6 +313,16 @@ config CGROUP_DEVICE Provides a cgroup implementing whitelists for devices which a process in the cgroup can mknod or open. +config CGROUP_IO_THROTTLE + bool "Enable cgroup I/O throttling (EXPERIMENTAL)" + depends on CGROUPS && CGROUP_MEM_RES_CTLR && RESOURCE_COUNTERS && EXPERIMENTAL + help + This allows to limit the maximum I/O bandwidth for specific + cgroup(s). + See Documentation/controllers/io-throttle.txt for more information. + + If unsure, say N. + config CPUSETS bool "Cpuset support" depends on SMP && CGROUPS
kernel/fork.c +8 -0
@@ -1025,6 +1025,14 @@ static struct task_struct *copy_process(unsigned long clone_flags,task_io_accounting_init (&p->ioac);acct_clear_integrals (p); +#ifdef CONFIG_CGROUP_IO_THROTTLE +atomic_set (&p->in_aio,0 ); + p->io_throttle_bw_cnt =0 ; + p->io_throttle_bw_sleep =0 ; + p->io_throttle_iops_cnt =0 ; + p->io_throttle_iops_sleep =0 ; +#endif +posix_cpu_timers_init (p); p->lock_depth =-1 ;/* -1 = no lock */
kernel/res_counter.c +72 -1
100 lines not on this page · raw message
mm/memcontrol.c +30 -0
@@ -241,6 +241,36 @@ struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p) struct mem_cgroup, css); } +struct cgroup *get_cgroup_from_page (struct page *page) +{ +struct page_cgroup *pc; +struct cgroup *cgrp =NULL ; + + pc =lookup_page_cgroup (page); +if (pc) { +lock_page_cgroup (pc); +if (pc->mem_cgroup) { +css_get (&pc->mem_cgroup->css); + cgrp = pc->mem_cgroup->css.cgroup; + } +unlock_page_cgroup (pc); + } + +return cgrp; +} + +void put_cgroup_from_page (struct page *page) +{ +struct page_cgroup *pc; + + pc =lookup_page_cgroup (page); +if (pc) { +lock_page_cgroup (pc); +css_put (&pc->mem_cgroup->css); +unlock_page_cgroup (pc); + } +} +static void __mem_cgroup_remove_list (struct mem_cgroup_per_zone *mz,struct page_cgroup *pc) {
mm/page-writeback.c +4 -0
@@ -20,6 +20,7 @@#include <linux/slab.h> #include <linux/pagemap.h> #include <linux/writeback.h> +#include <linux/blk-io-throttle.h> #include <linux/init.h> #include <linux/backing-dev.h> #include <linux/task_io_accounting_ops.h>
@@ -557,6 +558,9 @@ void balance_dirty_pages_ratelimited_nr(struct address_space *mapping, static DEFINE_PER_CPU(unsigned long, ratelimits) = 0;unsigned long ratelimit;unsigned long *p; +struct block_device *bdev =as_to_bdev (mapping); + +cgroup_io_throttle (NULL , bdev,0 ,1 ); ratelimit = ratelimit_pages;if (mapping->backing_dev_info->dirty_exceeded)
mm/readahead.c +3 -0
@@ -14,6 +14,7 @@#include <linux/blkdev.h> #include <linux/backing-dev.h> #include <linux/task_io_accounting_ops.h> +#include <linux/blk-io-throttle.h> #include <linux/pagevec.h> #include <linux/pagemap.h>
@@ -58,6 +59,7 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages, int (*filler)(void *, struct page *), void *data) { struct page *page; +struct block_device *bdev =as_to_bdev (mapping);int ret =0 ;while (!list_empty (pages)) {
@@ -76,6 +78,7 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages,break ; }task_io_account_read (PAGE_CACHE_SIZE ); +cgroup_io_throttle (NULL , bdev,PAGE_CACHE_SIZE ,1 ); }return ret; }
-- 1.5.4.rc3
PATCH 3/7 Introduction for new feature
In reply to a message not in the archiveDocumentation of using bio-cgroup in io-throttle.
--- Documentation/controllers/io-throttle.txt | 29 ++++++++++++++++++++++++++++- 1 files changed, 28 insertions(+), 1 deletions(-)
Documentation/controllers/io-throttle.txt +28 -1
@@ -223,7 +223,34 @@ $ cat /proc/$$/io-throttle-stat \ \_______global bandwidth sleep (clock ticks) \________global bandwidth counter -2.4. Examples +2.4. Buffered-io tracking +bio-cgroup can be used to track buffered-io(in delay-write condition) and for +proper throttling. You can directly mount io-throttle and bio-cgroup together +to track buffered-io. An alternative choice is making use of bio-cgroup id. An +association between a given io-throttle cgroup and a given bio-cgroup cgroup can +be built by echo a bio-cgroup id to the file blockio.bio_id. This file is exported +for the purpose of associating io-throttle and bio-cgroup groups. If you'd like to +create an association, you must ensure the io-throttle group is empty, that is, +there are no tasks in this group. otherwise, association creating will fail. If an +association is successfully built, task moving in this group will be denied. Of +course, you can remove an association, just echo an negative number into +blockio.bio_id. + +Example: +* Create an association between a given io-throttle group and a given bio-cgroup +group. +$ mount -t cgroup -o bio bio-cgroup /mnt/bio-cgroup/ +$ cd /mnt/bio-cgroup/ +$ mkdir bio-grp +$ cat bio-grp/bio.id +1 + +$ mount -t cgroup -o blockio blockio /mnt/throttle +$ cd /mnt/throttle +$ mkdir foo +$ echo 1 > foo/blockio.bio_id + +2.5. Examples * Mount the cgroup filesystem (blockio subsystem): # mkdir /mnt/cgroup
-- 1.5.4.rc3
PATCH 4/7 enables bio-cgroup in io-throttle, have to mount together
In reply to a message not in the archiveThis patch enables bio-cgroup in io-throttle, but you have to mount them together.
--- block/blk-io-throttle.c | 7 +++++-- include/linux/biotrack.h | 2 ++ include/linux/memcontrol.h | 3 --- init/Kconfig | 2 +- mm/biotrack.c | 35 +++++++++++++++++++++++++++++++++++ mm/memcontrol.c | 30 ------------------------------ 6 files changed, 43 insertions(+), 36 deletions(-)
block/blk-io-throttle.c +5 -2
@@ -22,7 +22,7 @@#include <linux/init.h> #include <linux/module.h> #include <linux/res_counter.h> -#include <linux/memcontrol.h> +#include <linux/biotrack.h> #include <linux/slab.h> #include <linux/gfp.h> #include <linux/err.h>
@@ -649,7 +649,10 @@ static struct iothrottle *get_iothrottle_from_page(struct page *page)if (!cgrp)return NULL ; iot =cgroup_to_iothrottle (cgrp); -css_get (&iot->css); +if (iot) +css_get (&iot->css); +else +return NULL ;put_cgroup_from_page (page);return iot;
include/linux/biotrack.h +2 -0
@@ -21,6 +21,8 @@ static inline void __init_bio_page_cgroup(struct page_cgroup *pc) { pc->bio_cgroup_id =0 ; } +extern struct cgroup *get_cgroup_from_page (struct page *page); +extern void put_cgroup_from_page (struct page *page);static inline int bio_cgroup_disabled (void ) {
include/linux/memcontrol.h +0 -3
@@ -49,9 +49,6 @@ int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem);extern struct mem_cgroup *mem_cgroup_from_task (struct task_struct *p); -extern struct cgroup *get_cgroup_from_page (struct page *page); -extern void put_cgroup_from_page (struct page *page); -#define mm_match_cgroup (mm, cgroup) \ ((cgroup) == mem_cgroup_from_task((mm)->owner))
init/Kconfig +1 -1
@@ -315,7 +315,7 @@ config CGROUP_DEVICE config CGROUP_IO_THROTTLE bool "Enable cgroup I/O throttling (EXPERIMENTAL)" - depends on CGROUPS && CGROUP_MEM_RES_CTLR && RESOURCE_COUNTERS && EXPERIMENTAL + depends on CGROUPS && CGROUP_BIO && RESOURCE_COUNTERS && EXPERIMENTAL help This allows to limit the maximum I/O bandwidth for specific cgroup(s).
mm/biotrack.c +35 -0
@@ -213,6 +213,41 @@ static struct bio_cgroup *find_bio_cgroup(int id)return biog; } +struct cgroup *get_cgroup_from_page (struct page *page) +{ +struct page_cgroup *pc; +struct bio_cgroup *biog; +struct cgroup *cgrp =NULL ; + + pc =lookup_page_cgroup (page); +if (pc) { +lock_page_cgroup (pc); + biog =find_bio_cgroup (pc->bio_cgroup_id); +if (biog) { +css_get (&biog->css); + cgrp = biog->css.cgroup; + } +unlock_page_cgroup (pc); + } + +return cgrp; +} + +void put_cgroup_from_page (struct page *page) +{ +struct bio_cgroup *biog; +struct page_cgroup *pc; + + pc =lookup_page_cgroup (page); +if (pc) { +lock_page_cgroup (pc); + biog =find_bio_cgroup (pc->bio_cgroup_id); +if (biog) +css_put (&biog->css); +unlock_page_cgroup (pc); + } +} +/* Determine the bio-cgroup id of a given bio. */ int get_bio_cgroup_id (struct bio *bio) {
mm/memcontrol.c +0 -30
@@ -241,36 +241,6 @@ struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p) struct mem_cgroup, css); } -struct cgroup *get_cgroup_from_page (struct page *page) -{ -struct page_cgroup *pc; -struct cgroup *cgrp =NULL ; - - pc =lookup_page_cgroup (page); -if (pc) { -lock_page_cgroup (pc); -if (pc->mem_cgroup) { -css_get (&pc->mem_cgroup->css); - cgrp = pc->mem_cgroup->css.cgroup; - } -unlock_page_cgroup (pc); - } - -return cgrp; -} - -void put_cgroup_from_page (struct page *page) -{ -struct page_cgroup *pc; - - pc =lookup_page_cgroup (page); -if (pc) { -lock_page_cgroup (pc); -css_put (&pc->mem_cgroup->css); -unlock_page_cgroup (pc); - } -} -static void __mem_cgroup_remove_list (struct mem_cgroup_per_zone *mz,struct page_cgroup *pc) {
-- 1.5.4.rc3
PATCH 5/7 announce tasks moving in bio-cgroup
In reply to a message not in the archiveSome subsystems may be of interest to task moving in bio-cgroups. So just announce each task moving.
--- include/linux/biotrack.h | 9 +++++++++ mm/biotrack.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 0 deletions(-)
include/linux/biotrack.h +9 -0
@@ -7,6 +7,15 @@#ifdef CONFIG_CGROUP_BIO +struct tsk_move_msg { +int old_id; +int new_id; +struct task_struct *tsk; +}; + +extern int register_biocgroup_notifier (struct notifier_block *nb); +extern int unregister_biocgroup_notifier (struct notifier_block *nb); +struct io_context ;struct block_device ;
mm/biotrack.c +32 -0
@@ -21,6 +21,22 @@#include <linux/blkdev.h> #include <linux/biotrack.h> +#define MOVETASK 0 +static BLOCKING_NOTIFIER_HEAD (biocgroup_chain); + +int register_biocgroup_notifier (struct notifier_block *nb) +{ +return blocking_notifier_chain_register (&biocgroup_chain, nb); +} +EXPORT_SYMBOL (register_biocgroup_notifier); + +int unregister_biocgroup_notifier (struct notifier_block *nb) +{ +return blocking_notifier_chain_unregister (&biocgroup_chain, nb); +} +EXPORT_SYMBOL (unregister_biocgroup_notifier); + +/* * The block I/O tracking mechanism is implemented on the cgroup memory * controller framework. It helps to find the the owner of an I/O request
@@ -299,11 +315,27 @@ static int bio_cgroup_populate(struct cgroup_subsys *ss, struct cgroup *cgrp)return cgroup_add_files (cgrp, ss, bio_files,ARRAY_SIZE (bio_files)); } +static void bio_cgroup_attach (struct cgroup_subsys *ss, +struct cgroup *cont,struct cgroup *oldcont, +struct task_struct *tsk) +{ +struct tsk_move_msg tmm; +struct bio_cgroup *old_biog, *new_biog; + + old_biog =cgroup_bio (oldcont); + new_biog =cgroup_bio (cont); + tmm.old_id = old_biog->id; + tmm.new_id = new_biog->id; + tmm.tsk = tsk; +blocking_notifier_call_chain (&biocgroup_chain,MOVETASK , &tmm); +} +struct cgroup_subsys bio_cgroup_subsys = { .name ="bio" , .create = bio_cgroup_create, .destroy = bio_cgroup_destroy, .populate = bio_cgroup_populate, + .attach = bio_cgroup_attach, .subsys_id = bio_cgroup_subsys_id, };
-- 1.5.4.rc3
PATCH 6/7 support checking of subsystem dependencies
In reply to a message not in the archiveFrom: Li Zefan <[email protected]>
This allows one subsystem to require that it only be mounted when some other subsystems are also present in or not in the proposed hierarchy.
--- Documentation/cgroups/cgroups.txt | 5 +++++ include/linux/cgroup.h | 2 ++ kernel/cgroup.c | 21 ++++++++++++++++++++- 3 files changed, 27 insertions(+), 1 deletions(-)
Documentation/cgroups/cgroups.txt +5 -0
@@ -534,6 +534,11 @@ and root cgroup. Currently this will only involve movement between the default hierarchy (which never has sub-cgroups) and a hierarchy that is being created/destroyed (and hence has no sub-cgroups). +int subsys_depend(struct cgroup_subsys *ss, unsigned long subsys_bits) +Called when a cgroup subsystem wants to check if some other subsystems +are also present in the proposed hierarchy. If this method returns error, +the mount of the cgroup filesystem will fail. + 4. Questions ============
include/linux/cgroup.h +2 -0
@@ -329,6 +329,8 @@ struct cgroup_subsys { struct cgroup *cgrp);void (*post_clone)(struct cgroup_subsys *ss,struct cgroup *cgrp);void (*bind)(struct cgroup_subsys *ss,struct cgroup *root); +int (*subsys_depend)(struct cgroup_subsys *ss, +unsigned long subsys_bits);/* * This routine is called with the task_lock of mm->owner held */
kernel/cgroup.c +20 -1
@@ -761,6 +761,25 @@ static int cgroup_show_options(struct seq_file *seq, struct vfsmount *vfs)return 0 ; } +static int check_subsys_dependency (unsigned long subsys_bits) +{ +int i; +int ret; +struct cgroup_subsys *ss; + +for (i =0 ; i <CGROUP_SUBSYS_COUNT ; i++) { + ss = subsys[i]; + +if (test_bit (i, &subsys_bits) && ss->subsys_depend) { + ret = ss->subsys_depend (ss, subsys_bits); +if (ret) +return ret; + } + } + +return 0 ; +} +struct cgroup_sb_opts {unsigned long subsys_bits;unsigned long flags;
@@ -821,7 +840,7 @@ static int parse_cgroupfs_options(char *data,if (!opts->subsys_bits)return -EINVAL ; -return 0 ; +return check_subsys_dependency (opts->subsys_bits); }static int cgroup_remount (struct super_block *sb,int *flags,char *data)
-- 1.5.4.rc3
PATCH 7/7 let io-throttle support using bio-cgroup id
In reply to a message not in the archiveThis patch makes io throttle support bio-cgroup id. With this patch, you don't have to mount io-throttle and bio-cgroup together. It's more gentle to other subsystems who also want to use bio-cgroup.
--- block/blk-core.c | 4 +- block/blk-io-throttle.c | 324 ++++++++++++++++++++++++++++++++++++++- include/linux/biotrack.h | 2 + include/linux/blk-io-throttle.h | 5 +- mm/biotrack.c | 11 ++ 5 files changed, 339 insertions(+), 7 deletions(-)
block/blk-core.c +2 -2
@@ -1537,8 +1537,8 @@ void submit_bio(int rw, struct bio *bio)if (bio_has_data (bio)) {if (rw &WRITE ) {count_vm_events (PGPGOUT , count); -cgroup_io_throttle (bio_iovec_idx (bio,0 )->bv_page, - bio->bi_bdev, bio->bi_size,0 ); +cgroup_io_throttle (bio, bio->bi_bdev, + bio->bi_size,0 ); }else {task_io_account_read (bio->bi_size);count_vm_events (PGPGIN , count);
block/blk-io-throttle.c +321 -3
405 lines not on this page · raw message
include/linux/biotrack.h +2 -0
@@ -26,12 +26,14 @@ struct bio_cgroup {/* struct radix_tree_root io_context_root; per device io_context */ }; +static inline void __init_bio_page_cgroup (struct page_cgroup *pc) { pc->bio_cgroup_id =0 ; }extern struct cgroup *get_cgroup_from_page (struct page *page);extern void put_cgroup_from_page (struct page *page); +extern struct cgroup *bio_id_to_cgroup (int id);static inline int bio_cgroup_disabled (void ) {
include/linux/blk-io-throttle.h +3 -2
@@ -14,8 +14,9 @@#define IOTHROTTLE_STAT 3#ifdef CONFIG_CGROUP_IO_THROTTLE +extern unsigned long long -cgroup_io_throttle (struct page *page,struct block_device *bdev, +cgroup_io_throttle (struct bio *bio,struct block_device *bdev,ssize_t bytes,int can_sleep);static inline void set_in_aio (void )
@@ -58,7 +59,7 @@ get_io_throttle_sleep(struct task_struct *t, int type) }#else static inline unsigned long long -cgroup_io_throttle (struct page *page,struct block_device *bdev, +cgroup_io_throttle (struct bio *bio,struct block_device *bdev,ssize_t bytes,int can_sleep) {return 0 ;
mm/biotrack.c +11 -0
@@ -229,6 +229,17 @@ static struct bio_cgroup *find_bio_cgroup(int id)return biog; } +struct cgroup *bio_id_to_cgroup (int id) +{ +struct bio_cgroup *biog; + + biog =find_bio_cgroup (id); +if (biog) +return biog->css.cgroup; + +return NULL ; +} +struct cgroup *get_cgroup_from_page (struct page *page) {struct page_cgroup *pc;
-- 1.5.4.rc3
PATCH 1/7 porting bio-cgroup to 2.6.28-rc2-mm1
In reply to Gui JianfengFrom: Ryo Tsuruta <[email protected]>
porting bio-cgroup to 2.6.28-rc2-mm1
--- block/blk-ioc.c | 30 +++-- fs/buffer.c | 2 + fs/direct-io.c | 2 + include/linux/biotrack.h | 82 ++++++++++++ include/linux/cgroup_subsys.h | 6 + include/linux/iocontext.h | 1 + include/linux/memcontrol.h | 14 ++- include/linux/mmzone.h | 4 +- include/linux/page_cgroup.h | 11 ++- init/Kconfig | 15 +++ mm/Makefile | 4 +- mm/biotrack.c | 274 +++++++++++++++++++++++++++++++++++++++++ mm/bounce.c | 2 + mm/filemap.c | 2 + mm/memcontrol.c | 5 + mm/memory.c | 5 + mm/page-writeback.c | 2 + mm/page_cgroup.c | 15 ++- mm/swap_state.c | 2 + 19 files changed, 452 insertions(+), 26 deletions(-) create mode 100644 include/linux/biotrack.h create mode 100644 mm/biotrack.c
block/blk-ioc.c +17 -13
@@ -84,24 +84,28 @@ void exit_io_context(void) } } +void init_io_context (struct io_context *ioc) +{ +atomic_set (&ioc->refcount,1 ); +atomic_set (&ioc->nr_tasks,1 ); +spin_lock_init (&ioc->lock); + ioc->ioprio_changed =0 ; + ioc->ioprio =0 ; + ioc->last_waited = jiffies;/* doesn't matter... */ + ioc->nr_batch_requests =0 ;/* because this is 0 */ + ioc->aic =NULL ; +INIT_RADIX_TREE (&ioc->radix_root,GFP_ATOMIC | __GFP_HIGH); +INIT_HLIST_HEAD (&ioc->cic_list); + ioc->ioc_data =NULL ; +} +struct io_context *alloc_io_context (gfp_t gfp_flags,int node) {struct io_context *ret; ret =kmem_cache_alloc_node (iocontext_cachep, gfp_flags, node); -if (ret) { -atomic_set (&ret->refcount,1 ); -atomic_set (&ret->nr_tasks,1 ); -spin_lock_init (&ret->lock); - ret->ioprio_changed =0 ; - ret->ioprio =0 ; - ret->last_waited = jiffies;/* doesn't matter... */ - ret->nr_batch_requests =0 ;/* because this is 0 */ - ret->aic =NULL ; -INIT_RADIX_TREE (&ret->radix_root,GFP_ATOMIC | __GFP_HIGH); -INIT_HLIST_HEAD (&ret->cic_list); - ret->ioc_data =NULL ; - } +if (ret) +init_io_context (ret);return ret; }
fs/buffer.c +2 -0
@@ -36,6 +36,7 @@#include <linux/buffer_head.h> #include <linux/task_io_accounting_ops.h> #include <linux/bio.h> +#include <linux/biotrack.h> #include <linux/notifier.h> #include <linux/cpu.h> #include <linux/bitops.h>
@@ -779,6 +780,7 @@ static int __set_page_dirty(struct page *page, BDI_RECLAIMABLE);task_io_account_write (PAGE_CACHE_SIZE ); } +bio_cgroup_reset_owner_pagedirty (page, current->mm);radix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); }
fs/direct-io.c +2 -0
@@ -33,6 +33,7 @@#include <linux/err.h> #include <linux/blkdev.h> #include <linux/buffer_head.h> +#include <linux/biotrack.h> #include <linux/rwsem.h> #include <linux/uio.h> #include <asm/atomic.h>
@@ -799,6 +800,7 @@ static int do_direct_IO(struct dio *dio) ret =PTR_ERR (page); goto out; } +bio_cgroup_reset_owner (page, current->mm);while (block_in_page < blocks_per_page) {unsigned offset_in_page = block_in_page << blkbits;
include/linux/biotrack.h new file +82 -0
82 lines not on this page · raw message
include/linux/cgroup_subsys.h +6 -0
@@ -43,6 +43,12 @@ SUBSYS(mem_cgroup)/* */ +#ifdef CONFIG_CGROUP_BIO +SUBSYS(bio_cgroup) +#endif + +/* */ +#ifdef CONFIG_CGROUP_DEVICE SUBSYS(devices)#endif
include/linux/iocontext.h +1 -0
@@ -104,6 +104,7 @@ int put_io_context(struct io_context *ioc);void exit_io_context (void );struct io_context *get_io_context (gfp_t gfp_flags,int node);struct io_context *alloc_io_context (gfp_t gfp_flags,int node); +void init_io_context (struct io_context *ioc);void copy_io_context (struct io_context **pdst,struct io_context **psrc);#else static inline void exit_io_context (void )
include/linux/memcontrol.h +13 -1
@@ -20,12 +20,14 @@#ifndef _LINUX_MEMCONTROL_H#define _LINUX_MEMCONTROL_H -struct mem_cgroup ; +#struct mem_cgroup;struct page_cgroup ;struct page ;struct mm_struct ;#ifdef CONFIG_CGROUP_MEM_RES_CTLR +extern void __init_mem_page_cgroup (struct page_cgroup *pc); +#define mem_cgroup_disabled () mem_cgroup_subsys.disabledextern int mem_cgroup_charge (struct page *page,struct mm_struct *mm,gfp_t gfp_mask);
@@ -71,6 +73,16 @@ extern long mem_cgroup_calc_reclaim(struct mem_cgroup *mem, struct zone *zone,#else /* CONFIG_CGROUP_MEM_RES_CTLR */ +static inline void __init_mem_page_cgroup (struct page_cgroup *pc) +{ +} + +static inline int mem_cgroup_disabled (void ) +{ +return 1 ; +} + +static inline int mem_cgroup_charge (struct page *page,struct mm_struct *mm,gfp_t gfp_mask) {
include/linux/mmzone.h +2 -2
@@ -603,7 +603,7 @@ typedef struct pglist_data {int nr_zones;#ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */ struct page *node_mem_map; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_CGROUP_PAGE struct page_cgroup *node_page_cgroup;#endif #endif
@@ -952,7 +952,7 @@ struct mem_section {/* See declaration of similar field in struct zone */ unsigned long *pageblock_flags; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_CGROUP_PAGE /* * If !SPARSEMEM, pgdat doesn't have page_cgroup pointer. We use * section. (see memcontrol.h/page_cgroup.h about this.)
include/linux/page_cgroup.h +8 -3
@@ -1,7 +1,7 @@#ifndef __LINUX_PAGE_CGROUP_H#define __LINUX_PAGE_CGROUP_H -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_CGROUP_PAGE #include <linux/bit_spinlock.h> /* * Page Cgroup can be considered as an extended mem_map.
@@ -12,9 +12,14 @@*/ struct page_cgroup {unsigned long flags; -struct mem_cgroup *mem_cgroup;struct page *page; +#ifdef CONFIG_CGROUP_MEM_RES_CTLR +struct mem_cgroup *mem_cgroup;struct list_head lru;/* per cgroup LRU list */ +#endif +#ifdef CONFIG_CGROUP_BIO +int bio_cgroup_id; +#endif };void __init pgdat_page_cgroup_init (struct pglist_data *pgdat);
@@ -88,7 +93,7 @@ static inline void unlock_page_cgroup(struct page_cgroup *pc) bit_spin_unlock(PCG_LOCK, &pc->flags); } -#else /* CONFIG_CGROUP_MEM_RES_CTLR */ +#else /* CONFIG_CGROUP_PAGE */ struct page_cgroup ;static inline void pgdat_page_cgroup_init (struct pglist_data *pgdat)
init/Kconfig +15 -0
@@ -393,6 +393,21 @@ config RESOURCE_COUNTERS infrastructure that works with cgroups depends on CGROUPS +config CGROUP_BIO + bool "Block I/O cgroup subsystem" + depends on CGROUPS && BLOCK + select MM_OWNER + help + Provides a Resource Controller which enables to track the onwner + of every Block I/O requests. + The information this subsystem provides can be used from any + kind of module such as dm-ioband device mapper modules or + the cfq-scheduler. + +config CGROUP_PAGE + def_bool y + depends on CGROUP_MEM_RES_CTLR || CGROUP_BIO + config MM_OWNER bool
mm/Makefile +3 -1
@@ -34,5 +34,7 @@ obj-$(CONFIG_FS_XIP) += filemap_xip.o obj-$(CONFIG_MIGRATION) += migrate.o obj-$(CONFIG_SMP) += allocpercpu.o obj-$(CONFIG_QUICKLIST) += quicklist.o -obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o +obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o +obj-$(CONFIG_CGROUP_PAGE) += page_cgroup.o +obj-$(CONFIG_CGROUP_BIO) += biotrack.o obj-$(CONFIG_KMEMTRACE) += kmemtrace.o
mm/biotrack.c new file +274 -0
274 lines not on this page · raw message
mm/bounce.c +2 -0
@@ -14,6 +14,7 @@#include <linux/hash.h> #include <linux/highmem.h> #include <linux/blktrace_api.h> +#include <linux/biotrack.h> #include <asm/tlbflush.h> #define POOL_SIZE 64
@@ -204,6 +205,7 @@ static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig, to->bv_len = from->bv_len; to->bv_offset = from->bv_offset;inc_zone_page_state (to->bv_page,NR_BOUNCE ); +bio_cgroup_copy_owner (to->bv_page, page);if (rw ==WRITE ) {char *vto, *vfrom;
mm/filemap.c +2 -0
@@ -33,6 +33,7 @@#include <linux/cpuset.h> #include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */ #include <linux/memcontrol.h> +#include <linux/biotrack.h> #include <linux/mm_inline.h> /* for page_is_file_cache() */ #include "internal.h"
@@ -464,6 +465,7 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping, gfp_mask & ~__GFP_HIGHMEM);if (error) goto out; +bio_cgroup_set_owner (page, current->mm); error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM);if (error ==0 ) {
mm/memcontrol.c +5 -0
@@ -157,6 +157,11 @@ pcg_default_flags[NR_CHARGE_TYPE] = { 0, /* FORCE */ }; +void __meminit __init_mem_page_cgroup (struct page_cgroup *pc) +{ + pc->mem_cgroup =NULL ; +} +/* * Always modified under lru lock. Then, not necessary to preempt_disable() */
mm/memory.c +5 -0
@@ -51,6 +51,7 @@#include <linux/init.h> #include <linux/writeback.h> #include <linux/memcontrol.h> +#include <linux/biotrack.h> #include <linux/mmu_notifier.h> #include <asm/pgalloc.h>
@@ -1915,6 +1916,7 @@ gotten:*/ ptep_clear_flush_notify (vma, address, page_table);SetPageSwapBacked (new_page); +bio_cgroup_set_owner (new_page, mm);lru_cache_add_active_or_unevictable (new_page, vma);page_add_new_anon_rmap (new_page, vma, address);
@@ -2353,6 +2355,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,flush_icache_page (vma, page);set_pte_at (mm, address, page_table, pte);page_add_anon_rmap (page, vma, address); +bio_cgroup_reset_owner (page, mm);swap_free (entry);if (vm_swap_full () || (vma->vm_flags &VM_LOCKED ) ||PageMlocked (page))
@@ -2414,6 +2417,7 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, goto release;inc_mm_counter (mm, anon_rss);SetPageSwapBacked (page); +bio_cgroup_set_owner (page, mm);lru_cache_add_active_or_unevictable (page, vma);page_add_new_anon_rmap (page, vma, address);set_pte_at (mm, address, page_table, entry);
@@ -2563,6 +2567,7 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,if (anon) {inc_mm_counter (mm, anon_rss);SetPageSwapBacked (page); +bio_cgroup_set_owner (page, mm);lru_cache_add_active_or_unevictable (page, vma);page_add_new_anon_rmap (page, vma, address); }else {
mm/page-writeback.c +2 -0
@@ -23,6 +23,7 @@#include <linux/init.h> #include <linux/backing-dev.h> #include <linux/task_io_accounting_ops.h> +#include <linux/biotrack.h> #include <linux/blkdev.h> #include <linux/mpage.h> #include <linux/rmap.h>
@@ -1100,6 +1101,7 @@ int __set_page_dirty_nobuffers(struct page *page) BDI_RECLAIMABLE);task_io_account_write (PAGE_CACHE_SIZE ); } +bio_cgroup_reset_owner_pagedirty (page, current->mm);radix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); }
mm/page_cgroup.c +9 -6
@@ -8,13 +8,16 @@#include <linux/memory.h> #include <linux/vmalloc.h> #include <linux/cgroup.h> +#include <linux/memcontrol.h> +#include <linux/biotrack.h> static void __meminit __init_page_cgroup (struct page_cgroup *pc,unsigned long pfn) { pc->flags =0 ; - pc->mem_cgroup =NULL ; pc->page =pfn_to_page (pfn); +__init_mem_page_cgroup (pc); +__init_bio_page_cgroup (pc); }static unsigned long total_usage;
@@ -69,7 +72,7 @@ void __init page_cgroup_init(void)int nid, fail; -if (mem_cgroup_subsys.disabled) +if (mem_cgroup_disabled () &&bio_cgroup_disabled ())return ;for_each_online_node (nid) {
@@ -78,12 +81,12 @@ void __init page_cgroup_init(void) goto fail; }printk (KERN_INFO "allocated %ld bytes of page_cgroup\n" , total_usage); -printk (KERN_INFO "please try cgroup_disable=memory option if you" +printk (KERN_INFO "please try cgroup_disable=memory,bio option if you" " don't want\n" );return ; fail:printk (KERN_CRIT "allocation of page_cgroup was failed.\n" ); -printk (KERN_CRIT "please try cgroup_disable=memory boot option\n" ); +printk (KERN_CRIT "please try cgroup_disable=memory,bio boot options\n" );panic ("Out of memory" ); }
@@ -229,7 +232,7 @@ void __init page_cgroup_init(void)unsigned long pfn;int fail =0 ; -if (mem_cgroup_subsys.disabled) +if (mem_cgroup_disabled () &&bio_cgroup_disabled ())return ;for (pfn =0 ; !fail && pfn < max_pfn; pfn +=PAGES_PER_SECTION ) {
@@ -244,7 +247,7 @@ void __init page_cgroup_init(void)hotplug_memory_notifier (page_cgroup_callback,0 ); }printk (KERN_INFO "allocated %ld bytes of page_cgroup\n" , total_usage); -printk (KERN_INFO "please try cgroup_disable=memory option if you don't" +printk (KERN_INFO "please try cgroup_disable=memory,bio option if you don't" " want\n" ); }
mm/swap_state.c +2 -0
@@ -17,6 +17,7 @@#include <linux/backing-dev.h> #include <linux/pagevec.h> #include <linux/migrate.h> +#include <linux/biotrack.h> #include <asm/pgtable.h>
@@ -305,6 +306,7 @@ struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,*/ __set_page_locked (new_page);SetPageSwapBacked (new_page); +bio_cgroup_set_owner (new_page, current->mm); err =add_to_swap_cache (new_page, entry, gfp_mask &GFP_KERNEL );if (likely (!err)) {/*
-- 1.5.4.rc3
PATCH 2/7 Porting io-throttle v11 to 2.6.28-rc2-mm1
In reply to Gui JianfengFrom: Andrea Righi <[email protected]>
Porting io-throttle v11 to 2.6.28-rc2-mm1
--- Documentation/controllers/io-throttle.txt | 409 ++++++++++++++++ block/Makefile | 2 + block/blk-core.c | 4 + block/blk-io-throttle.c | 735 +++++++++++++++++++++++++++++ fs/aio.c | 12 + fs/direct-io.c | 3 + fs/proc/base.c | 18 + include/linux/blk-io-throttle.h | 95 ++++ include/linux/cgroup_subsys.h | 6 + include/linux/memcontrol.h | 5 +- include/linux/res_counter.h | 69 ++- include/linux/sched.h | 7 + init/Kconfig | 10 + kernel/fork.c | 8 + kernel/res_counter.c | 73 +++- mm/memcontrol.c | 30 ++ mm/page-writeback.c | 4 + mm/readahead.c | 3 + 18 files changed, 1474 insertions(+), 19 deletions(-) create mode 100644 Documentation/controllers/io-throttle.txt create mode 100644 block/blk-io-throttle.c create mode 100644 include/linux/blk-io-throttle.h
Documentation/controllers/io-throttle.txt new file +409 -0
@@ -0,0 +1,409 @@ + + Block device I/O bandwidth controller + +---------------------------------------------------------------------- +1. DESCRIPTION + +This controller allows to limit the I/O bandwidth of specific block devices for +specific process containers (cgroups [1]) imposing additional delays on I/O +requests for those processes that exceed the limits defined in the control +group filesystem. + +Bandwidth limiting rules offer better control over QoS with respect to priority +or weight-based solutions that only give information about applications' +relative performance requirements. Nevertheless, priority based solutions are +affected by performance bursts, when only low-priority requests are submitted +to a general purpose resource dispatcher. + +The goal of the I/O bandwidth controller is to improve performance +predictability from the applications' point of view and provide performance +isolation of different control groups sharing the same block devices. + +NOTE #1: If you're looking for a way to improve the overall throughput of the +system probably you should use a different solution. + +NOTE #2: The current implementation does not guarantee minimum bandwidth +levels, the QoS is implemented only slowing down I/O "traffic" that exceeds the +limits specified by the user; minimum I/O rate thresholds are supposed to be +guaranteed if the user configures a proper I/O bandwidth partitioning of the +block devices shared among the different cgroups (theoretically if the sum of +all the single limits defined for a block device doesn't exceed the total I/O +bandwidth of that device). + +---------------------------------------------------------------------- +2. USER INTERFACE + +A new I/O limitation rule is described using the files: +- blockio.bandwidth-max +- blockio.iops-max + +The I/O bandwidth (blockio.bandwidth-max) can be used to limit the throughput +of a certain cgroup, while blockio.iops-max can be used to throttle cgroups +containing applications doing a sparse/seeky I/O workload. Any combination of +them can be used to define more complex I/O limiting rules, expressed both in +terms of iops/s and bandwidth. + +The same files can be used to set multiple rules for different block devices +relative to the same cgroup. + +The following syntax can be used to configure any limiting rule: + +# /bin/echo DEV:LIMIT:STRATEGY:BUCKET_SIZE > CGROUP/FILE + +- DEV is the name of the device the limiting rule is applied to. + +- LIMIT is the maximum I/O activity allowed on DEV by CGROUP; LIMIT can + represent a bandwidth limitation (expressed in bytes/s) when writing to + blockio.bandwidth-max, or a limitation to the maximum I/O operations per + second (expressed in iops/s) issued by CGROUP. + + A generic I/O limiting rule for a block device DEV can be removed setting the + LIMIT to 0. + +- STRATEGY is the throttling strategy used to throttle the applications' I/O + requests from/to device DEV. At the moment two different strategies can be + used [2][3]: + + 0 = leaky bucket: the controller accepts at most B bytes (B = LIMIT * time) + or O operations (O = LIMIT * time); further I/O requests + are delayed scheduling a timeout for the tasks that made + those requests. + + Different I/O flow + | | | + | v | + | v + v + ....... + \ / + \ / leaky-bucket + --- + ||| + vvv + Smoothed I/O flow + + 1 = token bucket: LIMIT tokens are added to the bucket every seconds; the + bucket can hold at the most BUCKET_SIZE tokens; I/O + requests are accepted if there are available tokens in the + bucket; when a request of N bytes arrives N tokens are + removed from the bucket; if fewer than N tokens are + available the request is delayed until a sufficient amount + of token is available in the bucket. + + Tokens (I/O rate) + o + o + o + ....... <--. + \ / | Bucket size (burst limit) + \ooo/ | + --- <--' + |ooo + Incoming --->|---> Conforming + I/O |oo I/O + requests -->|--> requests + | + ---->| + + Leaky bucket is more precise than token bucket to respect the limits, because + bursty workloads are always smoothed. Token bucket, instead, allows a small + irregularity degree in the I/O flows (burst limit), and, for this, it is + better in terms of efficiency (bursty workloads are not smoothed when there + are sufficient tokens in the bucket). + +- BUCKET_SIZE is used only with token bucket (STRATEGY == 1) and defines the + size of the bucket in bytes (blockio.bandwidth-max) or in I/O operations + (blockio.iops-max). + +- CGROUP is the name of the limited process container. + +Also the following syntaxes are allowed: + +- remove an I/O bandwidth limiting rule +# /bin/echo DEV:0 > CGROUP/blockio.bandwidth-max + +- configure a limiting rule using leaky bucket throttling (ignore bucket size): +# /bin/echo DEV:LIMIT:0 > CGROUP/blockio.bandwidth-max + +- configure a limiting rule using token bucket throttling + (with bucket size == LIMIT): +# /bin/echo DEV:LIMIT:1 > CGROUP/blockio.bandwidth-max + +2.2. Show I/O limiting rules + +All the defined rules and statistics for a specific cgroup can be shown reading +the files blockio.bandwidth-max for bandwidth constraints and blockio.iops-max +for I/O operations per second constraints. + +The following syntax is used: + +$ cat CGROUP/blockio.bandwidth-max +MAJOR MINOR LIMIT STRATEGY LEAKY_STAT BUCKET_SIZE BUCKET_FILL TIME_DELTA + +- MAJOR is the major device number of DEV (defined above) + +- MINOR is the minor device number of DEV (defined above) + +- LIMIT, STRATEGY and BUCKET_SIZE are the same parameters defined above + +- LEAKY_STAT is the amount of bytes (blockio.bandwidth-max) or I/O operations + (blockio.iops-max) currently allowed by the I/O controller (only used with + leaky bucket strategy - STRATEGY == 0) + +- BUCKET_FILL represents the amount of tokens present in the bucket (only used + with token bucket strategy - STRATEGY == 1) + +- TIME_DELTA can be one of the following: + - the amount of jiffies elapsed from the last I/O request (token bucket) + - the amount of jiffies during which the bytes or the number of I/O + operations given by LEAKY_STAT have been accumulated (leaky bucket) + +Multiple per-block device rules are reported in multiple rows +(DEVi, i = 1 .. n): + +$ cat CGROUP/blockio.bandwidth-max +MAJOR1 MINOR1 BW1 STRATEGY1 LEAKY_STAT1 BUCKET_SIZE1 BUCKET_FILL1 TIME_DELTA1 +MAJOR1 MINOR1 BW2 STRATEGY2 LEAKY_STAT2 BUCKET_SIZE2 BUCKET_FILL2 TIME_DELTA2 +... +MAJORn MINORn BWn STRATEGYn LEAKY_STATn BUCKET_SIZEn BUCKET_FILLn TIME_DELTAn + +The same fields are used to describe I/O operations/sec rules. The only +difference is that the cost of each I/O operation is scaled up by a factor of +1000. This allows to apply better fine grained sleeps and provide a more +precise throttling. + +$ cat CGROUP/blockio.iops-max +MAJOR MINOR LIMITx1000 STRATEGY LEAKY_STATx1000 BUCKET_SIZEx1000 BUCKET_FILLx1000 TIME_DELTA +... + +2.3. Additional I/O statistics + +Additional cgroup I/O throttling statistics are reported in +blockio.throttlecnt: + +$ cat CGROUP/blockio.throttlecnt +MAJOR MINOR BW_COUNTER BW_SLEEP IOPS_COUNTER IOPS_SLEEP + + - MAJOR, MINOR are respectively the major and the minor number of the device + the following statistics refer to + - BW_COUNTER gives the number of times that the cgroup bandwidth limit of + this particular device was exceeded + - BW_SLEEP is the amount of sleep time measured in clock ticks (divide + by sysconf(_SC_CLK_TCK)) imposed to the processes of this cgroup that + exceeded the bandwidth limit for this particular device + - IOPS_COUNTER gives the number of times that the cgroup I/O operation per + second limit of this particular device was exceeded + - IOPS_SLEEP is the amount of sleep time measured in clock ticks (divide + by sysconf(_SC_CLK_TCK)) imposed to the processes of this cgroup that + exceeded the I/O operations per second limit for this particular device + +Example: +$ cat CGROUP/blockio.throttlecnt +8 0 0 0 0 0 +^ ^ ^ ^ ^ ^ + \ \ \ \ \ \___iops sleep (in clock ticks) + \ \ \ \ \____iops throttle counter + \ \ \ \_____bandwidth sleep (in clock ticks) + \ \ \______bandwidth throttle counter + \ \_______minor dev. number + \________major dev. number + +Distinct statistics for each process are reported in +/proc/PID/io-throttle-stat: + +$ cat /proc/PID/io-throttle-stat +BW_COUNTER BW_SLEEP IOPS_COUNTER IOPS_SLEEP + +Example: +$ cat /proc/$$/io-throttle-stat +0 0 0 0 +^ ^ ^ ^ + \ \ \ \_____global iops sleep (in clock ticks) + \ \ \______global iops counter + \ \_______global bandwidth sleep (clock ticks) + \________global bandwidth counter + +2.4. Examples + +* Mount the cgroup filesystem (blockio subsystem): + # mkdir /mnt/cgroup + # mount -t cgroup -oblockio blockio /mnt/cgroup + +* Instantiate the new cgroup "foo": + # mkdir /mnt/cgroup/foo + --> the cgroup foo has been created + +* Add the current shell process to the cgroup "foo": + # /bin/echo $$ > /mnt/cgroup/foo/tasks + --> the current shell has been added to the cgroup "foo" + +* Give maximum 1MiB/s of I/O bandwidth on /dev/sda for the cgroup "foo", using + leaky bucket throttling strategy: + # /bin/echo /dev/sda:$((1024 * 1024)):0:0 > \ + > /mnt/cgroup/foo/blockio.bandwidth-max + # sh + --> the subshell 'sh' is running in cgroup "foo" and it can use a maximum I/O + bandwidth of 1MiB/s on /dev/sda + +* Give maximum 8MiB/s of I/O bandwidth on /dev/sdb for the cgroup "foo", using + token bucket throttling strategy, bucket size = 8MiB: + # /bin/echo /dev/sdb:$((8 * 1024 * 1024)):1:$((8 * 1024 * 1024)) > \ + > /mnt/cgroup/foo/blockio.bandwidth-max + # sh + --> the subshell 'sh' is running in cgroup "foo" and it can use a maximum I/O + bandwidth of 1MiB/s on /dev/sda (controlled by leaky bucket throttling) + and 8MiB/s on /dev/sdb (controlled by token bucket throttling) + +* Run a benchmark doing I/O on /dev/sda and /dev/sdb; I/O limits and usage + defined for cgroup "foo" can be shown as following: + # cat /mnt/cgroup/foo/blockio.bandwidth-max + 8 16 8388608 1 0 8388608 -522560 48 + 8 0 1048576 0 737280 0 0 216 + +* Extend the maximum I/O bandwidth for the cgroup "foo" to 16MiB/s on /dev/sda: + # /bin/echo /dev/sda:$((16 * 1024 * 1024)):0:0 > \ + > /mnt/cgroup/foo/blockio.bandwidth-max + # cat /mnt/cgroup/foo/blockio.bandwidth-max + 8 16 8388608 1 0 8388608 -84432 206436 + 8 0 16777216 0 0 0 0 15212 + +* Remove limiting rule on /dev/sdb for cgroup "foo": + # /bin/echo /dev/sdb:0:0:0 > /mnt/cgroup/foo/blockio.bandwidth-max + # cat /mnt/cgroup/foo/blockio.bandwidth-max + 8 0 16777216 0 0 0 0 110388 + +* Set a maximum of 100 I/O operations/sec (leaky bucket strategy) to /dev/sdc + for cgroup "foo": + # /bin/echo /dev/sdc:100:0 > /mnt/cgroup/foo/blockio.iops-max + # cat /mnt/cgroup/foo/blockio.iops-max + 8 32 100000 0 846000 0 2113 + ^ ^ + /________/ + / + Remember: these values are scaled up by a factor of 1000 to apply a fine + grained throttling (i.e. LIMIT == 100000 means a maximum of 100 I/O operation + per second) + +* Remove limiting rule for I/O operations from /dev/sdc for cgroup "foo": + # /bin/echo /dev/sdc:0 > /mnt/cgroup/foo/blockio.iops-max + +---------------------------------------------------------------------- +3. ADVANTAGES OF PROVIDING THIS FEATURE + +* Allow I/O traffic shaping for block device shared among different cgroups +* Improve I/O performance predictability on block devices shared between + different cgroups +* Limiting rules do not depend of the particular I/O scheduler (anticipatory, + deadline, CFQ, noop) and/or the type of the underlying block devices +* The bandwidth limitations are guaranteed both for synchronous and + asynchronous operations, even the I/O passing through the page cache or + buffers and not only direct I/O (see below for details) +* It is possible to implement a simple user-space application to dynamically + adjust the I/O workload of different process containers at run-time, + according to the particular users' requirements and applications' performance + constraints + +---------------------------------------------------------------------- +4. DESIGN + +The I/O throttling is performed imposing an explicit timeout, via +schedule_timeout_killable() on the processes that exceed the I/O limits +dedicated to the cgroup they belong to. I/O accounting happens per cgroup. + +It just works as expected for read operations: the real I/O activity is reduced +synchronously according to the defined limitations. + +Multiple re-reads of pages already present in the page cache are not considered +to account the I/O activity, since they actually don't generate any real I/O +operation. + +This means that a process that re-reads multiple times the same blocks of a +file is affected by the I/O limitations only for the actual I/O performed from +the underlying block devices. + +For write operations the scenario is a bit more complex, because the writes in +the page cache are processed asynchronously by kernel threads (pdflush), using +a write-back policy. So the real writes to the underlying block devices occur +in a different I/O context respect to the task that originally generated the +dirty pages. + +The I/O bandwidth controller uses the following solution to resolve this +problem. + +The cost of each I/O operation is always accounted when the operation is +submitted to the I/O subsystem (submit_bio()). + +If the operation is a read then we automatically know that the context of the +request is the current task and so we can charge the cgroup the current task +belongs to. And throttle the current task as well, if it exceeded the cgroup +limitations. + +If the operation is a write, we can charge the right cgroup looking at the +owner of the first page involved in the I/O operation, that gives the context +that generated the I/O activity at the source. This information can be +retrieved using the page_cgroup functionality provided by the cgroup memory +controller [4]. In this way we can correctly account the I/O cost to the right +cgroup, but we cannot throttle the current task in this stage, because, in +general, it is a different task (e.g. a kernel thread that is processing +asynchronously the dirty page). For this reason, throttling of write operations +is always performed asynchronously in balance_dirty_pages_ratelimited_nr(), a +function always called by processes which are dirtying memory. + +Multiple rules for different block devices are stored in a linked list, using +the dev_t number of each block device as key to uniquely identify each element +of the list. RCU synchronization is used to protect the whole list structure, +since the elements in the list are not supposed to change frequently (they +change only when a new rule is defined or an old rule is removed or updated), +while the reads in the list occur at each operation that generates I/O. This +allows to provide zero overhead for cgroups that do not use any limitation. + +WARNING: per-block device limiting rules always refer to the dev_t device +number. If a block device is unplugged (i.e. a USB device) the limiting rules +defined for that device persist and they are still valid if a new device is +plugged in the system and it uses the same major and minor numbers. + +NOTE: explicit sleeps are *not* imposed on tasks doing asynchronous I/O (AIO) +operations; AIO throttling is performed returning -EAGAIN from sys_io_submit(). +Userspace applications must be able to handle this error code opportunely. + +---------------------------------------------------------------------- +5. TODO + +* Implement a rbtree per request queue; all the requests queued to the I/O + subsystem first will go in this rbtree. Then based on cgroup grouping and + control policy dispatch the requests and pass them to the elevator associated + with the queue. This would allow to provide both bandwidth limiting and + proportional bandwidth functionalities using a generic approach (suggested by + Vivek Goyal) + +* Improve fair throttling: distribute the time to sleep among all the tasks of + a cgroup that exceeded the I/O limits, depending of the amount of IO activity + previously generated in the past by each task (see task_io_accounting) + +* Try to reduce the cost of calling cgroup_io_throttle() on every submit_bio(); + this is not too much expensive, but the call of task_subsys_state() has + surely a cost. A possible solution could be to temporarily account I/O in the + current task_struct and call cgroup_io_throttle() only on each X MB of I/O. + Or on each Y number of I/O requests as well. Better if both X and/or Y can be + tuned at runtime by a userspace tool + +* Think an alternative design for general purpose usage; special purpose usage + right now is restricted to improve I/O performance predictability and + evaluate more precise response timings for applications doing I/O. To a large + degree the block I/O bandwidth controller should implement a more complex + logic to better evaluate real I/O operations cost, depending also on the + particular block device profile (i.e. USB stick, optical drive, hard disk, + etc.). This would also allow to appropriately account I/O cost for seeky + workloads, respect to large stream workloads. Instead of looking at the + request stream and try to predict how expensive the I/O cost will be, a + totally different approach could be to collect request timings (start time / + elapsed time) and based on collected informations, try to estimate the I/O + cost and usage + +---------------------------------------------------------------------- +6. REFERENCES + +[1] Documentation/cgroups/cgroups.txt +[2] http://en.wikipedia.org/wiki/Leaky_bucket +[3] http://en.wikipedia.org/wiki/Token_bucket +[4] Documentation/controllers/memory.txt
block/Makefile +2 -0
@@ -13,6 +13,8 @@ obj-$(CONFIG_IOSCHED_AS) += as-iosched.o obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o +obj-$(CONFIG_CGROUP_IO_THROTTLE) += blk-io-throttle.o + obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o
block/blk-core.c +4 -0
@@ -26,6 +26,7 @@#include <linux/swap.h> #include <linux/writeback.h> #include <linux/task_io_accounting_ops.h> +#include <linux/blk-io-throttle.h> #include <linux/blktrace_api.h> #include <linux/fault-inject.h>
@@ -1536,9 +1537,12 @@ void submit_bio(int rw, struct bio *bio)if (bio_has_data (bio)) {if (rw &WRITE ) {count_vm_events (PGPGOUT , count); +cgroup_io_throttle (bio_iovec_idx (bio,0 )->bv_page, + bio->bi_bdev, bio->bi_size,0 ); }else {task_io_account_read (bio->bi_size);count_vm_events (PGPGIN , count); +cgroup_io_throttle (NULL , bio->bi_bdev, bio->bi_size,1 ); }if (unlikely (block_dump)) {
block/blk-io-throttle.c new file +735 -0
735 lines not on this page · raw message
fs/aio.c +12 -0
@@ -22,6 +22,7 @@#include <linux/sched.h> #include <linux/fs.h> #include <linux/file.h> +#include <linux/blk-io-throttle.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/slab.h>
@@ -1558,6 +1559,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, {struct kiocb *req;struct file *file; +struct block_device *bdev;ssize_t ret;/* enforce forwards compatibility on users */
@@ -1580,6 +1582,14 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,if (unlikely (!file))return -EBADF ; +/* check if we're exceeding the IO throttling limits */ + bdev =as_to_bdev (file->f_mapping); + ret =cgroup_io_throttle (NULL , bdev,0 ,0 ); +if (unlikely (ret)) { +fput (file); +return -EAGAIN ; + } + req =aio_get_req (ctx);/* returns with 2 references to req */ if (unlikely (!req)) {fput (file);
@@ -1622,12 +1632,14 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, goto out_put_req;spin_lock_irq (&ctx->ctx_lock); +set_in_aio ();aio_run_iocb (req);if (!list_empty (&ctx->run_list)) {/* drain the run list */ while (__aio_run_iocbs (ctx)) ; } +unset_in_aio ();spin_unlock_irq (&ctx->ctx_lock);aio_put_req (req);/* drop extra ref to req */ return 0 ;
fs/direct-io.c +3 -0
@@ -28,6 +28,7 @@#include <linux/highmem.h> #include <linux/pagemap.h> #include <linux/task_io_accounting_ops.h> +#include <linux/blk-io-throttle.h> #include <linux/bio.h> #include <linux/wait.h> #include <linux/err.h>
@@ -658,10 +659,12 @@ submit_page_section(struct dio *dio, struct page *page,int ret =0 ;if (dio->rw &WRITE ) { +struct block_device *bdev = dio->inode->i_sb->s_bdev;/* * Read accounting is performed in submit_bio() */ task_io_account_write (len); +cgroup_io_throttle (NULL , bdev,0 ,1 ); }/*
fs/proc/base.c +18 -0
@@ -54,6 +54,7 @@#include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/task_io_accounting_ops.h> +#include <linux/blk-io-throttle.h> #include <linux/init.h> #include <linux/capability.h> #include <linux/file.h>
@@ -2458,6 +2459,17 @@ static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns,return 0 ; } +#ifdef CONFIG_CGROUP_IO_THROTTLE +static int proc_iothrottle_stat (struct task_struct *task,char *buffer) +{ +return sprintf (buffer,"%llu %llu %llu %llu\n" , +get_io_throttle_cnt (task,IOTHROTTLE_BANDWIDTH ), +get_io_throttle_sleep (task,IOTHROTTLE_BANDWIDTH ), +get_io_throttle_cnt (task,IOTHROTTLE_IOPS ), +get_io_throttle_sleep (task,IOTHROTTLE_IOPS )); +} +#endif /* CONFIG_CGROUP_IO_THROTTLE */ +/* * Thread groups */
@@ -2534,6 +2546,9 @@ static const struct pid_entry tgid_base_stuff[] = {#ifdef CONFIG_TASK_IO_ACCOUNTING INF("io", S_IRUGO, tgid_io_accounting),#endif +#ifdef CONFIG_CGROUP_IO_THROTTLE + INF("io-throttle-stat", S_IRUGO, iothrottle_stat), +#endif }; static int proc_tgid_base_readdir(struct file * filp,
@@ -2866,6 +2881,9 @@ static const struct pid_entry tid_base_stuff[] = {#ifdef CONFIG_TASK_IO_ACCOUNTING INF("io", S_IRUGO, tid_io_accounting),#endif +#ifdef CONFIG_CGROUP_IO_THROTTLE + INF("io-throttle-stat", S_IRUGO, iothrottle_stat), +#endif }; static int proc_tid_base_readdir(struct file * filp,
include/linux/blk-io-throttle.h new file +95 -0
95 lines not on this page · raw message
include/linux/cgroup_subsys.h +6 -0
@@ -55,6 +55,12 @@ SUBSYS(devices)/* */ +#ifdef CONFIG_CGROUP_IO_THROTTLE +SUBSYS(iothrottle) +#endif + +/* */ +#ifdef CONFIG_CGROUP_FREEZER SUBSYS(freezer)#endif
include/linux/memcontrol.h +4 -1
@@ -20,7 +20,7 @@#ifndef _LINUX_MEMCONTROL_H#define _LINUX_MEMCONTROL_H -#struct mem_cgroup; +struct mem_cgroup ;struct page_cgroup ;struct page ;struct mm_struct ;
@@ -49,6 +49,9 @@ int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem);extern struct mem_cgroup *mem_cgroup_from_task (struct task_struct *p); +extern struct cgroup *get_cgroup_from_page (struct page *page); +extern void put_cgroup_from_page (struct page *page); +#define mm_match_cgroup (mm, cgroup) \ ((cgroup) == mem_cgroup_from_task((mm)->owner))
include/linux/res_counter.h +52 -17
100 lines not on this page · raw message
include/linux/sched.h +7 -0
@@ -1250,6 +1250,13 @@ struct task_struct {unsigned long ptrace_message;siginfo_t *last_siginfo;/* For ptrace use. */ struct task_io_accounting ioac; +#ifdef CONFIG_CGROUP_IO_THROTTLE +atomic_t in_aio; +unsigned long long io_throttle_bw_cnt; +unsigned long long io_throttle_bw_sleep; +unsigned long long io_throttle_iops_cnt; +unsigned long long io_throttle_iops_sleep; +#endif #if defined(CONFIG_TASK_XACCT )u64 acct_rss_mem1;/* accumulated rss usage */ u64 acct_vm_mem1;/* accumulated virtual memory usage */
init/Kconfig +10 -0
@@ -313,6 +313,16 @@ config CGROUP_DEVICE Provides a cgroup implementing whitelists for devices which a process in the cgroup can mknod or open. +config CGROUP_IO_THROTTLE + bool "Enable cgroup I/O throttling (EXPERIMENTAL)" + depends on CGROUPS && CGROUP_MEM_RES_CTLR && RESOURCE_COUNTERS && EXPERIMENTAL + help + This allows to limit the maximum I/O bandwidth for specific + cgroup(s). + See Documentation/controllers/io-throttle.txt for more information. + + If unsure, say N. + config CPUSETS bool "Cpuset support" depends on SMP && CGROUPS
kernel/fork.c +8 -0
@@ -1025,6 +1025,14 @@ static struct task_struct *copy_process(unsigned long clone_flags,task_io_accounting_init (&p->ioac);acct_clear_integrals (p); +#ifdef CONFIG_CGROUP_IO_THROTTLE +atomic_set (&p->in_aio,0 ); + p->io_throttle_bw_cnt =0 ; + p->io_throttle_bw_sleep =0 ; + p->io_throttle_iops_cnt =0 ; + p->io_throttle_iops_sleep =0 ; +#endif +posix_cpu_timers_init (p); p->lock_depth =-1 ;/* -1 = no lock */
kernel/res_counter.c +72 -1
100 lines not on this page · raw message
mm/memcontrol.c +30 -0
@@ -241,6 +241,36 @@ struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p) struct mem_cgroup, css); } +struct cgroup *get_cgroup_from_page (struct page *page) +{ +struct page_cgroup *pc; +struct cgroup *cgrp =NULL ; + + pc =lookup_page_cgroup (page); +if (pc) { +lock_page_cgroup (pc); +if (pc->mem_cgroup) { +css_get (&pc->mem_cgroup->css); + cgrp = pc->mem_cgroup->css.cgroup; + } +unlock_page_cgroup (pc); + } + +return cgrp; +} + +void put_cgroup_from_page (struct page *page) +{ +struct page_cgroup *pc; + + pc =lookup_page_cgroup (page); +if (pc) { +lock_page_cgroup (pc); +css_put (&pc->mem_cgroup->css); +unlock_page_cgroup (pc); + } +} +static void __mem_cgroup_remove_list (struct mem_cgroup_per_zone *mz,struct page_cgroup *pc) {
mm/page-writeback.c +4 -0
@@ -20,6 +20,7 @@#include <linux/slab.h> #include <linux/pagemap.h> #include <linux/writeback.h> +#include <linux/blk-io-throttle.h> #include <linux/init.h> #include <linux/backing-dev.h> #include <linux/task_io_accounting_ops.h>
@@ -557,6 +558,9 @@ void balance_dirty_pages_ratelimited_nr(struct address_space *mapping, static DEFINE_PER_CPU(unsigned long, ratelimits) = 0;unsigned long ratelimit;unsigned long *p; +struct block_device *bdev =as_to_bdev (mapping); + +cgroup_io_throttle (NULL , bdev,0 ,1 ); ratelimit = ratelimit_pages;if (mapping->backing_dev_info->dirty_exceeded)
mm/readahead.c +3 -0
@@ -14,6 +14,7 @@#include <linux/blkdev.h> #include <linux/backing-dev.h> #include <linux/task_io_accounting_ops.h> +#include <linux/blk-io-throttle.h> #include <linux/pagevec.h> #include <linux/pagemap.h>
@@ -58,6 +59,7 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages, int (*filler)(void *, struct page *), void *data) { struct page *page; +struct block_device *bdev =as_to_bdev (mapping);int ret =0 ;while (!list_empty (pages)) {
@@ -76,6 +78,7 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages,break ; }task_io_account_read (PAGE_CACHE_SIZE ); +cgroup_io_throttle (NULL , bdev,PAGE_CACHE_SIZE ,1 ); }return ret; }
-- 1.5.4.rc3
PATCH 3/7 Introduction for new feature
In reply to Gui JianfengDocumentation of using bio-cgroup in io-throttle.
--- Documentation/controllers/io-throttle.txt | 29 ++++++++++++++++++++++++++++- 1 files changed, 28 insertions(+), 1 deletions(-)
Documentation/controllers/io-throttle.txt +28 -1
@@ -223,7 +223,34 @@ $ cat /proc/$$/io-throttle-stat \ \_______global bandwidth sleep (clock ticks) \________global bandwidth counter -2.4. Examples +2.4. Buffered-io tracking +bio-cgroup can be used to track buffered-io(in delay-write condition) and for +proper throttling. You can directly mount io-throttle and bio-cgroup together +to track buffered-io. An alternative choice is making use of bio-cgroup id. An +association between a given io-throttle cgroup and a given bio-cgroup cgroup can +be built by echo a bio-cgroup id to the file blockio.bio_id. This file is exported +for the purpose of associating io-throttle and bio-cgroup groups. If you'd like to +create an association, you must ensure the io-throttle group is empty, that is, +there are no tasks in this group. otherwise, association creating will fail. If an +association is successfully built, task moving in this group will be denied. Of +course, you can remove an association, just echo an negative number into +blockio.bio_id. + +Example: +* Create an association between a given io-throttle group and a given bio-cgroup +group. +$ mount -t cgroup -o bio bio-cgroup /mnt/bio-cgroup/ +$ cd /mnt/bio-cgroup/ +$ mkdir bio-grp +$ cat bio-grp/bio.id +1 + +$ mount -t cgroup -o blockio blockio /mnt/throttle +$ cd /mnt/throttle +$ mkdir foo +$ echo 1 > foo/blockio.bio_id + +2.5. Examples * Mount the cgroup filesystem (blockio subsystem): # mkdir /mnt/cgroup
-- 1.5.4.rc3
PATCH 4/7 enables bio-cgroup in io-throttle, have to mount together
In reply to Gui JianfengThis patch enables bio-cgroup in io-throttle, but you have to mount them together.
--- block/blk-io-throttle.c | 7 +++++-- include/linux/biotrack.h | 2 ++ include/linux/memcontrol.h | 3 --- init/Kconfig | 2 +- mm/biotrack.c | 35 +++++++++++++++++++++++++++++++++++ mm/memcontrol.c | 30 ------------------------------ 6 files changed, 43 insertions(+), 36 deletions(-)
block/blk-io-throttle.c +5 -2
@@ -22,7 +22,7 @@#include <linux/init.h> #include <linux/module.h> #include <linux/res_counter.h> -#include <linux/memcontrol.h> +#include <linux/biotrack.h> #include <linux/slab.h> #include <linux/gfp.h> #include <linux/err.h>
@@ -649,7 +649,10 @@ static struct iothrottle *get_iothrottle_from_page(struct page *page)if (!cgrp)return NULL ; iot =cgroup_to_iothrottle (cgrp); -css_get (&iot->css); +if (iot) +css_get (&iot->css); +else +return NULL ;put_cgroup_from_page (page);return iot;
include/linux/biotrack.h +2 -0
@@ -21,6 +21,8 @@ static inline void __init_bio_page_cgroup(struct page_cgroup *pc) { pc->bio_cgroup_id =0 ; } +extern struct cgroup *get_cgroup_from_page (struct page *page); +extern void put_cgroup_from_page (struct page *page);static inline int bio_cgroup_disabled (void ) {
include/linux/memcontrol.h +0 -3
@@ -49,9 +49,6 @@ int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem);extern struct mem_cgroup *mem_cgroup_from_task (struct task_struct *p); -extern struct cgroup *get_cgroup_from_page (struct page *page); -extern void put_cgroup_from_page (struct page *page); -#define mm_match_cgroup (mm, cgroup) \ ((cgroup) == mem_cgroup_from_task((mm)->owner))
init/Kconfig +1 -1
@@ -315,7 +315,7 @@ config CGROUP_DEVICE config CGROUP_IO_THROTTLE bool "Enable cgroup I/O throttling (EXPERIMENTAL)" - depends on CGROUPS && CGROUP_MEM_RES_CTLR && RESOURCE_COUNTERS && EXPERIMENTAL + depends on CGROUPS && CGROUP_BIO && RESOURCE_COUNTERS && EXPERIMENTAL help This allows to limit the maximum I/O bandwidth for specific cgroup(s).
mm/biotrack.c +35 -0
@@ -213,6 +213,41 @@ static struct bio_cgroup *find_bio_cgroup(int id)return biog; } +struct cgroup *get_cgroup_from_page (struct page *page) +{ +struct page_cgroup *pc; +struct bio_cgroup *biog; +struct cgroup *cgrp =NULL ; + + pc =lookup_page_cgroup (page); +if (pc) { +lock_page_cgroup (pc); + biog =find_bio_cgroup (pc->bio_cgroup_id); +if (biog) { +css_get (&biog->css); + cgrp = biog->css.cgroup; + } +unlock_page_cgroup (pc); + } + +return cgrp; +} + +void put_cgroup_from_page (struct page *page) +{ +struct bio_cgroup *biog; +struct page_cgroup *pc; + + pc =lookup_page_cgroup (page); +if (pc) { +lock_page_cgroup (pc); + biog =find_bio_cgroup (pc->bio_cgroup_id); +if (biog) +css_put (&biog->css); +unlock_page_cgroup (pc); + } +} +/* Determine the bio-cgroup id of a given bio. */ int get_bio_cgroup_id (struct bio *bio) {
mm/memcontrol.c +0 -30
@@ -241,36 +241,6 @@ struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p) struct mem_cgroup, css); } -struct cgroup *get_cgroup_from_page (struct page *page) -{ -struct page_cgroup *pc; -struct cgroup *cgrp =NULL ; - - pc =lookup_page_cgroup (page); -if (pc) { -lock_page_cgroup (pc); -if (pc->mem_cgroup) { -css_get (&pc->mem_cgroup->css); - cgrp = pc->mem_cgroup->css.cgroup; - } -unlock_page_cgroup (pc); - } - -return cgrp; -} - -void put_cgroup_from_page (struct page *page) -{ -struct page_cgroup *pc; - - pc =lookup_page_cgroup (page); -if (pc) { -lock_page_cgroup (pc); -css_put (&pc->mem_cgroup->css); -unlock_page_cgroup (pc); - } -} -static void __mem_cgroup_remove_list (struct mem_cgroup_per_zone *mz,struct page_cgroup *pc) {
-- 1.5.4.rc3
PATCH 5/7 announce tasks moving in bio-cgroup
In reply to Gui JianfengSome subsystems may be of interest to task moving in bio-cgroups. So just announce each task moving.
--- include/linux/biotrack.h | 9 +++++++++ mm/biotrack.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 0 deletions(-)
include/linux/biotrack.h +9 -0
@@ -7,6 +7,15 @@#ifdef CONFIG_CGROUP_BIO +struct tsk_move_msg { +int old_id; +int new_id; +struct task_struct *tsk; +}; + +extern int register_biocgroup_notifier (struct notifier_block *nb); +extern int unregister_biocgroup_notifier (struct notifier_block *nb); +struct io_context ;struct block_device ;
mm/biotrack.c +32 -0
@@ -21,6 +21,22 @@#include <linux/blkdev.h> #include <linux/biotrack.h> +#define MOVETASK 0 +static BLOCKING_NOTIFIER_HEAD (biocgroup_chain); + +int register_biocgroup_notifier (struct notifier_block *nb) +{ +return blocking_notifier_chain_register (&biocgroup_chain, nb); +} +EXPORT_SYMBOL (register_biocgroup_notifier); + +int unregister_biocgroup_notifier (struct notifier_block *nb) +{ +return blocking_notifier_chain_unregister (&biocgroup_chain, nb); +} +EXPORT_SYMBOL (unregister_biocgroup_notifier); + +/* * The block I/O tracking mechanism is implemented on the cgroup memory * controller framework. It helps to find the the owner of an I/O request
@@ -299,11 +315,27 @@ static int bio_cgroup_populate(struct cgroup_subsys *ss, struct cgroup *cgrp)return cgroup_add_files (cgrp, ss, bio_files,ARRAY_SIZE (bio_files)); } +static void bio_cgroup_attach (struct cgroup_subsys *ss, +struct cgroup *cont,struct cgroup *oldcont, +struct task_struct *tsk) +{ +struct tsk_move_msg tmm; +struct bio_cgroup *old_biog, *new_biog; + + old_biog =cgroup_bio (oldcont); + new_biog =cgroup_bio (cont); + tmm.old_id = old_biog->id; + tmm.new_id = new_biog->id; + tmm.tsk = tsk; +blocking_notifier_call_chain (&biocgroup_chain,MOVETASK , &tmm); +} +struct cgroup_subsys bio_cgroup_subsys = { .name ="bio" , .create = bio_cgroup_create, .destroy = bio_cgroup_destroy, .populate = bio_cgroup_populate, + .attach = bio_cgroup_attach, .subsys_id = bio_cgroup_subsys_id, };
-- 1.5.4.rc3
PATCH 6/7 support checking of subsystem dependencies
In reply to Gui JianfengFrom: Li Zefan <[email protected]>
This allows one subsystem to require that it only be mounted when some other subsystems are also present in or not in the proposed hierarchy.
--- Documentation/cgroups/cgroups.txt | 5 +++++ include/linux/cgroup.h | 2 ++ kernel/cgroup.c | 21 ++++++++++++++++++++- 3 files changed, 27 insertions(+), 1 deletions(-)
Documentation/cgroups/cgroups.txt +5 -0
@@ -534,6 +534,11 @@ and root cgroup. Currently this will only involve movement between the default hierarchy (which never has sub-cgroups) and a hierarchy that is being created/destroyed (and hence has no sub-cgroups). +int subsys_depend(struct cgroup_subsys *ss, unsigned long subsys_bits) +Called when a cgroup subsystem wants to check if some other subsystems +are also present in the proposed hierarchy. If this method returns error, +the mount of the cgroup filesystem will fail. + 4. Questions ============
include/linux/cgroup.h +2 -0
@@ -329,6 +329,8 @@ struct cgroup_subsys { struct cgroup *cgrp);void (*post_clone)(struct cgroup_subsys *ss,struct cgroup *cgrp);void (*bind)(struct cgroup_subsys *ss,struct cgroup *root); +int (*subsys_depend)(struct cgroup_subsys *ss, +unsigned long subsys_bits);/* * This routine is called with the task_lock of mm->owner held */
kernel/cgroup.c +20 -1
@@ -761,6 +761,25 @@ static int cgroup_show_options(struct seq_file *seq, struct vfsmount *vfs)return 0 ; } +static int check_subsys_dependency (unsigned long subsys_bits) +{ +int i; +int ret; +struct cgroup_subsys *ss; + +for (i =0 ; i <CGROUP_SUBSYS_COUNT ; i++) { + ss = subsys[i]; + +if (test_bit (i, &subsys_bits) && ss->subsys_depend) { + ret = ss->subsys_depend (ss, subsys_bits); +if (ret) +return ret; + } + } + +return 0 ; +} +struct cgroup_sb_opts {unsigned long subsys_bits;unsigned long flags;
@@ -821,7 +840,7 @@ static int parse_cgroupfs_options(char *data,if (!opts->subsys_bits)return -EINVAL ; -return 0 ; +return check_subsys_dependency (opts->subsys_bits); }static int cgroup_remount (struct super_block *sb,int *flags,char *data)
-- 1.5.4.rc3
PATCH 7/7 let io-throttle support using bio-cgroup id
In reply to Gui JianfengThis patch makes io throttle support bio-cgroup id. With this patch, you don't have to mount io-throttle and bio-cgroup together. It's more gentle to other subsystems who also want to use bio-cgroup.
--- block/blk-core.c | 4 +- block/blk-io-throttle.c | 324 ++++++++++++++++++++++++++++++++++++++- include/linux/biotrack.h | 2 + include/linux/blk-io-throttle.h | 5 +- mm/biotrack.c | 11 ++ 5 files changed, 339 insertions(+), 7 deletions(-)
block/blk-core.c +2 -2
@@ -1537,8 +1537,8 @@ void submit_bio(int rw, struct bio *bio)if (bio_has_data (bio)) {if (rw &WRITE ) {count_vm_events (PGPGOUT , count); -cgroup_io_throttle (bio_iovec_idx (bio,0 )->bv_page, - bio->bi_bdev, bio->bi_size,0 ); +cgroup_io_throttle (bio, bio->bi_bdev, + bio->bi_size,0 ); }else {task_io_account_read (bio->bi_size);count_vm_events (PGPGIN , count);
block/blk-io-throttle.c +321 -3
405 lines not on this page · raw message
include/linux/biotrack.h +2 -0
@@ -26,12 +26,14 @@ struct bio_cgroup {/* struct radix_tree_root io_context_root; per device io_context */ }; +static inline void __init_bio_page_cgroup (struct page_cgroup *pc) { pc->bio_cgroup_id =0 ; }extern struct cgroup *get_cgroup_from_page (struct page *page);extern void put_cgroup_from_page (struct page *page); +extern struct cgroup *bio_id_to_cgroup (int id);static inline int bio_cgroup_disabled (void ) {
include/linux/blk-io-throttle.h +3 -2
@@ -14,8 +14,9 @@#define IOTHROTTLE_STAT 3#ifdef CONFIG_CGROUP_IO_THROTTLE +extern unsigned long long -cgroup_io_throttle (struct page *page,struct block_device *bdev, +cgroup_io_throttle (struct bio *bio,struct block_device *bdev,ssize_t bytes,int can_sleep);static inline void set_in_aio (void )
@@ -58,7 +59,7 @@ get_io_throttle_sleep(struct task_struct *t, int type) }#else static inline unsigned long long -cgroup_io_throttle (struct page *page,struct block_device *bdev, +cgroup_io_throttle (struct bio *bio,struct block_device *bdev,ssize_t bytes,int can_sleep) {return 0 ;
mm/biotrack.c +11 -0
@@ -229,6 +229,17 @@ static struct bio_cgroup *find_bio_cgroup(int id)return biog; } +struct cgroup *bio_id_to_cgroup (int id) +{ +struct bio_cgroup *biog; + + biog =find_bio_cgroup (id); +if (biog) +return biog->css.cgroup; + +return NULL ; +} +struct cgroup *get_cgroup_from_page (struct page *page) {struct page_cgroup *pc;
-- 1.5.4.rc3