diff options
author | Song Liu <songliubraving@fb.com> | 2016-11-24 09:50:39 +0300 |
---|---|---|
committer | Shaohua Li <shli@fb.com> | 2016-11-28 08:35:38 +0300 |
commit | d7bd398e97f236a2353689eca5e8950f67cd34d5 (patch) | |
tree | ed071303317acc6ba7008e808fec5845ab5fe45b /drivers/md/raid5-cache.c | |
parent | 034e33f5eda3c61edb838471f69ec42d64e1e94e (diff) | |
download | linux-d7bd398e97f236a2353689eca5e8950f67cd34d5.tar.xz |
md/r5cache: handle alloc_page failure
RMW of r5c write back cache uses an extra page to store old data for
prexor. handle_stripe_dirtying() allocates this page by calling
alloc_page(). However, alloc_page() may fail.
To handle alloc_page() failures, this patch adds an extra page to
disk_info. When alloc_page fails, handle_stripe() trys to use these
pages. When these pages are used by other stripe (R5C_EXTRA_PAGE_IN_USE),
the stripe is added to delayed_list.
Signed-off-by: Song Liu <songliubraving@fb.com>
Reviewed-by: NeilBrown <neilb@suse.com>
Signed-off-by: Shaohua Li <shli@fb.com>
Diffstat (limited to 'drivers/md/raid5-cache.c')
-rw-r--r-- | drivers/md/raid5-cache.c | 27 |
1 files changed, 26 insertions, 1 deletions
diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c index 5f817bdaceb9..5d3d238921e8 100644 --- a/drivers/md/raid5-cache.c +++ b/drivers/md/raid5-cache.c @@ -2326,15 +2326,40 @@ int r5c_try_caching_write(struct r5conf *conf, */ void r5c_release_extra_page(struct stripe_head *sh) { + struct r5conf *conf = sh->raid_conf; int i; + bool using_disk_info_extra_page; + + using_disk_info_extra_page = + sh->dev[0].orig_page == conf->disks[0].extra_page; for (i = sh->disks; i--; ) if (sh->dev[i].page != sh->dev[i].orig_page) { struct page *p = sh->dev[i].orig_page; sh->dev[i].orig_page = sh->dev[i].page; - put_page(p); + if (!using_disk_info_extra_page) + put_page(p); } + + if (using_disk_info_extra_page) { + clear_bit(R5C_EXTRA_PAGE_IN_USE, &conf->cache_state); + md_wakeup_thread(conf->mddev->thread); + } +} + +void r5c_use_extra_page(struct stripe_head *sh) +{ + struct r5conf *conf = sh->raid_conf; + int i; + struct r5dev *dev; + + for (i = sh->disks; i--; ) { + dev = &sh->dev[i]; + if (dev->orig_page != dev->page) + put_page(dev->orig_page); + dev->orig_page = conf->disks[i].extra_page; + } } /* |