summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorBen Hutchings <ben@decadent.org.uk>2022-01-24 16:11:18 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-01-27 09:04:34 +0100
commit294c7a9fb608c29a9e49010b515228e20ccbec8f (patch)
tree72323c946bf09b443edf16556f728c84504e92fd /arch
parent1550a97e4a5d8bf29071bd6c17355ca173e90f73 (diff)
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>
Diffstat (limited to 'arch')
-rw-r--r--arch/mips/mm/gup.c9
-rw-r--r--arch/s390/mm/gup.c9
-rw-r--r--arch/sh/mm/gup.c9
-rw-r--r--arch/sparc/mm/gup.c9
4 files changed, 32 insertions, 4 deletions
diff --git a/arch/mips/mm/gup.c b/arch/mips/mm/gup.c
index 5a4875cac1ec..2e7a0d201c09 100644
--- a/arch/mips/mm/gup.c
+++ b/arch/mips/mm/gup.c
@@ -274,7 +274,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 9b5b866d8adf..5389bf5bc828 100644
--- a/arch/s390/mm/gup.c
+++ b/arch/s390/mm/gup.c
@@ -287,7 +287,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 56c86ca98ecf..23fa2fc8aabc 100644
--- a/arch/sh/mm/gup.c
+++ b/arch/sh/mm/gup.c
@@ -242,7 +242,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 aee6dba83d0e..f291d34a1cd5 100644
--- a/arch/sparc/mm/gup.c
+++ b/arch/sparc/mm/gup.c
@@ -303,7 +303,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);