[PATCH v5 08/16] md/raid5: resize stripes and set correct offset when reshape array

Yufen Yu
2020-07-02T12:06:20Z

When reshape array, we try to reuse shared pages of old stripe_head,
and allocate more for the new one if needed. At the same time, set
correct offset when call handle_stripe_expansion().

By the way, we call resize_stripes() only when grow raid array disks,
so that don't worry about memleak for old r5pages.

Signed-off-by: Yufen Yu <yuyufen@huawei.com>
---
drivers/md/raid5.c | 51 +++++++++++++++++++++++++++++++++++++++-------
1 file changed, 44 insertions(+), 7 deletions(-)

diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index e554d073113b..689a36c8e723 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2493,10 +2493,20 @@ static int resize_stripes(struct r5conf *conf, int newsize)
osh = get_free_stripe(conf, hash);
unlock_device_hash_lock(conf, hash);

- for(i=0; i<conf->pool_size; i++) {
+ if (raid5_stripe_pages_shared(conf)) {
+ /* We reuse pages in r5pages of old stripe head */
+ for (i = 0; i < osh->pages.size; i++) {
+ nsh->pages.page[i] = osh->pages.page[i];
+ osh->pages.page[i] = NULL;
+ }
+ }
+
+ for (i = 0; i < conf->pool_size; i++) {
nsh->dev[i].page = osh->dev[i].page;
nsh->dev[i].orig_page = osh->dev[i].page;
+ nsh->dev[i].offset = osh->dev[i].offset;
}
+
nsh->hash_lock_index = hash;
free_stripe(conf->slab_cache, osh);
cnt++;
@@ -2543,17 +2553,42 @@ static int resize_stripes(struct r5conf *conf, int newsize)

/* Step 4, return new stripes to service */
while(!list_empty(&newstripes)) {
+ struct page *p;
+ unsigned int offset;
nsh = list_entry(newstripes.next, struct stripe_head, lru);
list_del_init(&nsh->lru);

- for (i=conf->raid_disks; i < newsize; i++)
- if (nsh->dev[i].page == NULL) {
- struct page *p = alloc_page(GFP_NOIO);
- nsh->dev[i].page = p;
- nsh->dev[i].orig_page = p;
+ /*
+ * If we use r5pages, means, pages.size is not zero,
+ * allocate pages it needed for new stripe_head.
+ */
+ for (i = 0; i < nsh->pages.size; i++) {
+ if (nsh->pages.page[i] == NULL) {
+ p = alloc_page(GFP_NOIO);
+ if (!p)
+ err = -ENOMEM;
+ nsh->pages.page[i] = p;
+ }
+ }
+
+ for (i = conf->raid_disks; i < newsize; i++) {
+ if (nsh->dev[i].page)
+ continue;
+
+ if (raid5_stripe_pages_shared(conf)) {
+ p = raid5_get_dev_page(nsh, i);
+ offset = raid5_get_page_offset(nsh, i);
+ } else {
+ p = alloc_page(GFP_NOIO);
if (!p)
err = -ENOMEM;
+ offset = 0;
}
+
+ nsh->dev[i].page = p;
+ nsh->dev[i].orig_page = p;
+ nsh->dev[i].offset = offset;
+ }
raid5_release_stripe(nsh);
}
/* critical section pass, GFP_NOIO no longer needed */
@@ -4476,7 +4511,9 @@ static void handle_stripe_expansion(struct r5conf *conf, struct stripe_head *sh)
/* place all the copies on one channel */
init_async_submit(&submit, 0, tx, NULL, NULL, NULL);
tx = async_memcpy(sh2->dev[dd_idx].page,
- sh->dev[i].page, 0, 0,
+ sh->dev[i].page,
+ sh2->dev[dd_idx].offset,
+ sh->dev[i].offset,
conf->stripe_size,
&submit);

--
2.25.4