mips,s390,sh,sparc: gup: Work around the "COW can break either way" issue
authorBen Hutchings <ben@decadent.org.uk>
Mon, 24 Jan 2022 15:11:18 +0000 (16:11 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 27 Jan 2022 08:01:01 +0000 (09:01 +0100)
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>
arch/mips/mm/gup.c
arch/s390/mm/gup.c
arch/sh/mm/gup.c
arch/sparc/mm/gup.c

index 1e4658eee13fb5bdd0c4b7df0e32a486f8c0be75..5a535220d7cd8ee93fb3da7208ab2ac275fecea4 100644 (file)
@@ -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();
index 9bce54eac0b079e09bc0e5319005735804262a9f..f26ca7fbd44ef00535d2a6f83adb2d6ed100ba19 100644 (file)
@@ -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;
 
index 8045b5bb7075e90951dfc8f250301bc6e644dcbd..4abfc37dd7fe81d332229b9ef699997fbb62178f 100644 (file)
@@ -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();
index 5335ba3c850ed3acdc074ffe639d3ddac101f2ad..50593ca0214495302d64d4c323e548f71f3a1436 100644 (file)
@@ -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);