diff options
| author | Ben Hutchings <ben@decadent.org.uk> | 2022-01-24 18:11:18 +0300 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2022-01-27 11:01:01 +0300 |
| commit | 70b5928f5cd289b2ccf34384ca83b1d9ee7a0fad (patch) | |
| tree | fc9bbfca141e609e383784bea4c0defa47f4c156 | |
| parent | 04d1dbd1e8e21fc115a839ef39bf66a26542c98c (diff) | |
| download | linux-70b5928f5cd289b2ccf34384ca83b1d9ee7a0fad.tar.xz | |
mips,s390,sh,sparc: gup: Work around the "COW can break either way" issue
In Linux 4.14 and 4.19 these architectures still have their own
implementations of get_user_pages_fast(). These also need to force
the write flag on when taking the fast path.
Fixes: 407faed92b4a ("gup: document and work around "COW can break either way" issue")
Fixes: 5e24029791e8 ("gup: document and work around "COW can break either way" issue")
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
| -rw-r--r-- | arch/mips/mm/gup.c | 9 | ||||
| -rw-r--r-- | arch/s390/mm/gup.c | 9 | ||||
| -rw-r--r-- | arch/sh/mm/gup.c | 9 | ||||
| -rw-r--r-- | arch/sparc/mm/gup.c | 9 |
4 files changed, 32 insertions, 4 deletions
diff --git a/arch/mips/mm/gup.c b/arch/mips/mm/gup.c index 1e4658eee13f..5a535220d7cd 100644 --- a/arch/mips/mm/gup.c +++ b/arch/mips/mm/gup.c @@ -272,7 +272,14 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write, next = pgd_addr_end(addr, end); if (pgd_none(pgd)) goto slow; - if (!gup_pud_range(pgd, addr, next, write, pages, &nr)) + /* + * The FAST_GUP case requires FOLL_WRITE even for pure reads, + * because get_user_pages() may need to cause an early COW in + * order to avoid confusing the normal COW routines. So only + * targets that are already writable are safe to do by just + * looking at the page tables. + */ + if (!gup_pud_range(pgd, addr, next, 1, pages, &nr)) goto slow; } while (pgdp++, addr = next, addr != end); local_irq_enable(); diff --git a/arch/s390/mm/gup.c b/arch/s390/mm/gup.c index 9bce54eac0b0..f26ca7fbd44e 100644 --- a/arch/s390/mm/gup.c +++ b/arch/s390/mm/gup.c @@ -285,7 +285,14 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write, might_sleep(); start &= PAGE_MASK; - nr = __get_user_pages_fast(start, nr_pages, write, pages); + /* + * The FAST_GUP case requires FOLL_WRITE even for pure reads, + * because get_user_pages() may need to cause an early COW in + * order to avoid confusing the normal COW routines. So only + * targets that are already writable are safe to do by just + * looking at the page tables. + */ + nr = __get_user_pages_fast(start, nr_pages, 1, pages); if (nr == nr_pages) return nr; diff --git a/arch/sh/mm/gup.c b/arch/sh/mm/gup.c index 8045b5bb7075..4abfc37dd7fe 100644 --- a/arch/sh/mm/gup.c +++ b/arch/sh/mm/gup.c @@ -240,7 +240,14 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write, next = pgd_addr_end(addr, end); if (pgd_none(pgd)) goto slow; - if (!gup_pud_range(pgd, addr, next, write, pages, &nr)) + /* + * The FAST_GUP case requires FOLL_WRITE even for pure reads, + * because get_user_pages() may need to cause an early COW in + * order to avoid confusing the normal COW routines. So only + * targets that are already writable are safe to do by just + * looking at the page tables. + */ + if (!gup_pud_range(pgd, addr, next, 1, pages, &nr)) goto slow; } while (pgdp++, addr = next, addr != end); local_irq_enable(); diff --git a/arch/sparc/mm/gup.c b/arch/sparc/mm/gup.c index 5335ba3c850e..50593ca02144 100644 --- a/arch/sparc/mm/gup.c +++ b/arch/sparc/mm/gup.c @@ -262,7 +262,14 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write, next = pgd_addr_end(addr, end); if (pgd_none(pgd)) goto slow; - if (!gup_pud_range(pgd, addr, next, write, pages, &nr)) + /* + * The FAST_GUP case requires FOLL_WRITE even for pure reads, + * because get_user_pages() may need to cause an early COW in + * order to avoid confusing the normal COW routines. So only + * targets that are already writable are safe to do by just + * looking at the page tables. + */ + if (!gup_pud_range(pgd, addr, next, 1, pages, &nr)) goto slow; } while (pgdp++, addr = next, addr != end); |
