From: Andrew Morton Signed-off-by: Andrew Morton --- block/ll_rw_blk.c | 35 +++++++++++++++++++++++++++++++--- fs/buffer.c | 1 include/linux/backing-dev.h | 2 + mm/page-writeback.c | 14 +++++++++++-- 4 files changed, 47 insertions(+), 5 deletions(-) diff -puN block/ll_rw_blk.c~per-backing_dev-dirty-and-writeback-page-accounting block/ll_rw_blk.c --- a/block/ll_rw_blk.c~per-backing_dev-dirty-and-writeback-page-accounting +++ a/block/ll_rw_blk.c @@ -208,6 +208,8 @@ EXPORT_SYMBOL(blk_queue_softirq_done); **/ void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn) { + struct backing_dev_info *bdi = &q->backing_dev_info; + /* * set defaults */ @@ -215,9 +217,11 @@ void blk_queue_make_request(request_queu blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS); blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS); q->make_request_fn = mfn; - q->backing_dev_info.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; - q->backing_dev_info.state = 0; - q->backing_dev_info.capabilities = BDI_CAP_MAP_COPY; + bdi->ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; + bdi->state = 0; + bdi->capabilities = BDI_CAP_MAP_COPY; + atomic_long_set(&bdi->nr_dirty, 0); + atomic_long_set(&bdi->nr_writeback, 0); blk_queue_max_sectors(q, SAFE_MAX_SECTORS); blk_queue_hardsect_size(q, 512); blk_queue_dma_alignment(q, 511); @@ -3934,6 +3938,19 @@ static ssize_t queue_max_hw_sectors_show return queue_var_show(max_hw_sectors_kb, (page)); } +static ssize_t queue_nr_dirty_show(struct request_queue *q, char *page) +{ + return sprintf(page, "%lu\n", + atomic_long_read(&q->backing_dev_info.nr_dirty)); + +} + +static ssize_t queue_nr_writeback_show(struct request_queue *q, char *page) +{ + return sprintf(page, "%lu\n", + atomic_long_read(&q->backing_dev_info.nr_writeback)); + +} static struct queue_sysfs_entry queue_requests_entry = { .attr = {.name = "nr_requests", .mode = S_IRUGO | S_IWUSR }, @@ -3958,6 +3975,16 @@ static struct queue_sysfs_entry queue_ma .show = queue_max_hw_sectors_show, }; +static struct queue_sysfs_entry queue_nr_dirty_entry = { + .attr = {.name = "nr_dirty", .mode = S_IRUGO }, + .show = queue_nr_dirty_show, +}; + +static struct queue_sysfs_entry queue_nr_writeback_entry = { + .attr = {.name = "nr_writeback", .mode = S_IRUGO }, + .show = queue_nr_writeback_show, +}; + static struct queue_sysfs_entry queue_iosched_entry = { .attr = {.name = "scheduler", .mode = S_IRUGO | S_IWUSR }, .show = elv_iosched_show, @@ -3969,6 +3996,8 @@ static struct attribute *default_attrs[] &queue_ra_entry.attr, &queue_max_hw_sectors_entry.attr, &queue_max_sectors_entry.attr, + &queue_nr_dirty_entry.attr, + &queue_nr_writeback_entry.attr, &queue_iosched_entry.attr, NULL, }; diff -puN fs/buffer.c~per-backing_dev-dirty-and-writeback-page-accounting fs/buffer.c --- a/fs/buffer.c~per-backing_dev-dirty-and-writeback-page-accounting +++ a/fs/buffer.c @@ -732,6 +732,7 @@ int __set_page_dirty_buffers(struct page if (page->mapping) { /* Race with truncate? */ if (mapping_cap_account_dirty(mapping)) { __inc_zone_page_state(page, NR_FILE_DIRTY); + atomic_long_inc(&mapping->backing_dev_info->nr_dirty); task_io_account_write(PAGE_CACHE_SIZE); } radix_tree_tag_set(&mapping->page_tree, diff -puN include/linux/backing-dev.h~per-backing_dev-dirty-and-writeback-page-accounting include/linux/backing-dev.h --- a/include/linux/backing-dev.h~per-backing_dev-dirty-and-writeback-page-accounting +++ a/include/linux/backing-dev.h @@ -28,6 +28,8 @@ struct backing_dev_info { unsigned long ra_pages; /* max readahead in PAGE_CACHE_SIZE units */ unsigned long state; /* Always use atomic bitops on this */ unsigned int capabilities; /* Device capabilities */ + atomic_long_t nr_dirty; /* Pages dirty against this BDI */ + atomic_long_t nr_writeback;/* Pages under writeback against this BDI */ congested_fn *congested_fn; /* Function pointer if device is md/dm */ void *congested_data; /* Pointer to aux data for congested func */ void (*unplug_io_fn)(struct backing_dev_info *, struct page *); diff -puN mm/page-writeback.c~per-backing_dev-dirty-and-writeback-page-accounting mm/page-writeback.c --- a/mm/page-writeback.c~per-backing_dev-dirty-and-writeback-page-accounting +++ a/mm/page-writeback.c @@ -771,6 +771,8 @@ int __set_page_dirty_nobuffers(struct pa BUG_ON(mapping2 != mapping); if (mapping_cap_account_dirty(mapping)) { __inc_zone_page_state(page, NR_FILE_DIRTY); + atomic_long_inc(&mapping->backing_dev_info-> + nr_dirty); task_io_account_write(PAGE_CACHE_SIZE); } radix_tree_tag_set(&mapping->page_tree, @@ -868,6 +870,7 @@ int test_clear_page_dirty(struct page *p if (mapping_cap_account_dirty(mapping)) { page_mkclean(page); dec_zone_page_state(page, NR_FILE_DIRTY); + atomic_long_dec(&mapping->backing_dev_info->nr_dirty); } return 1; } @@ -901,6 +904,7 @@ int clear_page_dirty_for_io(struct page if (mapping_cap_account_dirty(mapping)) { page_mkclean(page); dec_zone_page_state(page, NR_FILE_DIRTY); + atomic_long_dec(&mapping->backing_dev_info->nr_dirty); } return 1; } @@ -918,10 +922,13 @@ int test_clear_page_writeback(struct pag write_lock_irqsave(&mapping->tree_lock, flags); ret = TestClearPageWriteback(page); - if (ret) + if (ret) { radix_tree_tag_clear(&mapping->page_tree, page_index(page), PAGECACHE_TAG_WRITEBACK); + atomic_long_dec(&mapping->backing_dev_info-> + nr_writeback); + } write_unlock_irqrestore(&mapping->tree_lock, flags); } else { ret = TestClearPageWriteback(page); @@ -941,10 +948,13 @@ int test_set_page_writeback(struct page write_lock_irqsave(&mapping->tree_lock, flags); ret = TestSetPageWriteback(page); - if (!ret) + if (!ret) { radix_tree_tag_set(&mapping->page_tree, page_index(page), PAGECACHE_TAG_WRITEBACK); + atomic_long_inc(&mapping->backing_dev_info-> + nr_writeback); + } if (!PageDirty(page)) radix_tree_tag_clear(&mapping->page_tree, page_index(page), _