| From d70cef0d46729808dc53f145372c02b145c92604 Mon Sep 17 00:00:00 2001 |
| From: Ira Weiny <ira.weiny@intel.com> |
| Date: Wed, 27 Jan 2021 22:15:03 -0800 |
| Subject: btrfs: fix raid6 qstripe kmap |
| |
| From: Ira Weiny <ira.weiny@intel.com> |
| |
| commit d70cef0d46729808dc53f145372c02b145c92604 upstream. |
| |
| When a qstripe is required an extra page is allocated and mapped. There |
| were 3 problems: |
| |
| 1) There is no corresponding call of kunmap() for the qstripe page. |
| 2) There is no reason to map the qstripe page more than once if the |
| number of bits set in rbio->dbitmap is greater than one. |
| 3) There is no reason to map the parity page and unmap it each time |
| through the loop. |
| |
| The page memory can continue to be reused with a single mapping on each |
| iteration by raid6_call.gen_syndrome() without remapping. So map the |
| page for the duration of the loop. |
| |
| Similarly, improve the algorithm by mapping the parity page just 1 time. |
| |
| Fixes: 5a6ac9eacb49 ("Btrfs, raid56: support parity scrub on raid56") |
| CC: stable@vger.kernel.org # 4.4.x: c17af96554a8: btrfs: raid56: simplify tracking of Q stripe presence |
| CC: stable@vger.kernel.org # 4.4.x |
| Signed-off-by: Ira Weiny <ira.weiny@intel.com> |
| Reviewed-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/btrfs/raid56.c | 21 ++++++++++----------- |
| 1 file changed, 10 insertions(+), 11 deletions(-) |
| |
| --- a/fs/btrfs/raid56.c |
| +++ b/fs/btrfs/raid56.c |
| @@ -2360,16 +2360,21 @@ static noinline void finish_parity_scrub |
| SetPageUptodate(p_page); |
| |
| if (has_qstripe) { |
| + /* RAID6, allocate and map temp space for the Q stripe */ |
| q_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); |
| if (!q_page) { |
| __free_page(p_page); |
| goto cleanup; |
| } |
| SetPageUptodate(q_page); |
| + pointers[rbio->real_stripes - 1] = kmap(q_page); |
| } |
| |
| atomic_set(&rbio->error, 0); |
| |
| + /* Map the parity stripe just once */ |
| + pointers[nr_data] = kmap(p_page); |
| + |
| for_each_set_bit(pagenr, rbio->dbitmap, rbio->stripe_npages) { |
| struct page *p; |
| void *parity; |
| @@ -2379,16 +2384,8 @@ static noinline void finish_parity_scrub |
| pointers[stripe] = kmap(p); |
| } |
| |
| - /* then add the parity stripe */ |
| - pointers[stripe++] = kmap(p_page); |
| - |
| if (has_qstripe) { |
| - /* |
| - * raid6, add the qstripe and call the |
| - * library function to fill in our p/q |
| - */ |
| - pointers[stripe++] = kmap(q_page); |
| - |
| + /* RAID6, call the library function to fill in our P/Q */ |
| raid6_call.gen_syndrome(rbio->real_stripes, PAGE_SIZE, |
| pointers); |
| } else { |
| @@ -2409,12 +2406,14 @@ static noinline void finish_parity_scrub |
| |
| for (stripe = 0; stripe < nr_data; stripe++) |
| kunmap(page_in_rbio(rbio, stripe, pagenr, 0)); |
| - kunmap(p_page); |
| } |
| |
| + kunmap(p_page); |
| __free_page(p_page); |
| - if (q_page) |
| + if (q_page) { |
| + kunmap(q_page); |
| __free_page(q_page); |
| + } |
| |
| writeback: |
| /* |