x86, mm: dont use non-temporal stores in pagecache accesses
authorIngo Molnar <mingo@elte.hu>
Mon, 2 Mar 2009 10:00:57 +0000 (11:00 +0100)
committerIngo Molnar <mingo@elte.hu>
Mon, 2 Mar 2009 10:06:49 +0000 (11:06 +0100)
Impact: standardize IO on cached ops

On modern CPUs it is almost always a bad idea to use non-temporal stores,
as the regression in this commit has shown it:

  30d697f: x86: fix performance regression in write() syscall

The kernel simply has no good information about whether using non-temporal
stores is a good idea or not - and trying to add heuristics only increases
complexity and inserts fragility.

The regression on cached write()s took very long to be found - over two
years. So dont take any chances and let the hardware decide how it makes
use of its caches.

The only exception is drivers/gpu/drm/i915/i915_gem.c: there were we are
absolutely sure that another entity (the GPU) will pick up the dirty
data immediately and that the CPU will not touch that data before the
GPU will.

Also, keep the _nocache() primitives to make it easier for people to
experiment with these details. There may be more clear-cut cases where
non-cached copies can be used, outside of filemap.c.

Cc: Salman Qazi <sqazi@google.com>
Cc: Nick Piggin <npiggin@suse.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/include/asm/uaccess_32.h
arch/x86/include/asm/uaccess_64.h
drivers/gpu/drm/i915/i915_gem.c
include/linux/uaccess.h
mm/filemap.c
mm/filemap_xip.c

index a0ba61386972d573d0b6c8e975833072d1a2664e..5e06259e90e5a736539948ae22e1de25444ba485 100644 (file)
@@ -157,7 +157,7 @@ __copy_from_user(void *to, const void __user *from, unsigned long n)
 }
 
 static __always_inline unsigned long __copy_from_user_nocache(void *to,
-               const void __user *from, unsigned long n, unsigned long total)
+                               const void __user *from, unsigned long n)
 {
        might_fault();
        if (__builtin_constant_p(n)) {
@@ -180,7 +180,7 @@ static __always_inline unsigned long __copy_from_user_nocache(void *to,
 
 static __always_inline unsigned long
 __copy_from_user_inatomic_nocache(void *to, const void __user *from,
-                                 unsigned long n, unsigned long total)
+                                 unsigned long n)
 {
        return __copy_from_user_ll_nocache_nozero(to, from, n);
 }
index dcaa0404cf7be6a3ae709e47a15fe027a512af01..8cc687326eb80f20726852c9ba66c68837e8176a 100644 (file)
@@ -188,29 +188,18 @@ __copy_to_user_inatomic(void __user *dst, const void *src, unsigned size)
 extern long __copy_user_nocache(void *dst, const void __user *src,
                                unsigned size, int zerorest);
 
-static inline int __copy_from_user_nocache(void *dst, const void __user *src,
-                                  unsigned size, unsigned long total)
+static inline int
+__copy_from_user_nocache(void *dst, const void __user *src, unsigned size)
 {
        might_sleep();
-       /*
-        * In practice this limit means that large file write()s
-        * which get chunked to 4K copies get handled via
-        * non-temporal stores here. Smaller writes get handled
-        * via regular __copy_from_user():
-        */
-       if (likely(total >= PAGE_SIZE))
-               return __copy_user_nocache(dst, src, size, 1);
-       else
-               return __copy_from_user(dst, src, size);
+       return __copy_user_nocache(dst, src, size, 1);
 }
 
-static inline int __copy_from_user_inatomic_nocache(void *dst,
-           const void __user *src, unsigned size, unsigned total)
+static inline int
+__copy_from_user_inatomic_nocache(void *dst, const void __user *src,
+                                 unsigned size)
 {
-       if (likely(total >= PAGE_SIZE))
-               return __copy_user_nocache(dst, src, size, 0);
-       else
-               return __copy_from_user_inatomic(dst, src, size);
+       return __copy_user_nocache(dst, src, size, 0);
 }
 
 unsigned long
index 6b209db8370dbe4bcccb33a4e35fa2b51947785a..818576654092b341746ebb40a846e15ebfb4d5fc 100644 (file)
@@ -215,7 +215,7 @@ fast_user_write(struct io_mapping *mapping,
 
        vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base);
        unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + page_offset,
-                                                     user_data, length, length);
+                                                     user_data, length);
        io_mapping_unmap_atomic(vaddr_atomic);
        if (unwritten)
                return -EFAULT;
index 6f3c603b0d6714acbb55e5ef1d107ea66ee1d02a..6b58367d145e8735eaebbf16bfec0733406389fb 100644 (file)
@@ -41,13 +41,13 @@ static inline void pagefault_enable(void)
 #ifndef ARCH_HAS_NOCACHE_UACCESS
 
 static inline unsigned long __copy_from_user_inatomic_nocache(void *to,
-               const void __user *from, unsigned long n, unsigned long total)
+                               const void __user *from, unsigned long n)
 {
        return __copy_from_user_inatomic(to, from, n);
 }
 
 static inline unsigned long __copy_from_user_nocache(void *to,
-               const void __user *from, unsigned long n, unsigned long total)
+                               const void __user *from, unsigned long n)
 {
        return __copy_from_user(to, from, n);
 }
index 60fd56772cc68435c5646e0def07365f53b6f4f6..126d3973b3d1f3be7a3f5e4f42630725c796f31f 100644 (file)
@@ -1816,14 +1816,14 @@ EXPORT_SYMBOL(file_remove_suid);
 static size_t __iovec_copy_from_user_inatomic(char *vaddr,
                        const struct iovec *iov, size_t base, size_t bytes)
 {
-       size_t copied = 0, left = 0, total = bytes;
+       size_t copied = 0, left = 0;
 
        while (bytes) {
                char __user *buf = iov->iov_base + base;
                int copy = min(bytes, iov->iov_len - base);
 
                base = 0;
-               left = __copy_from_user_inatomic_nocache(vaddr, buf, copy, total);
+               left = __copy_from_user_inatomic(vaddr, buf, copy);
                copied += copy;
                bytes -= copy;
                vaddr += copy;
@@ -1851,9 +1851,7 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
        if (likely(i->nr_segs == 1)) {
                int left;
                char __user *buf = i->iov->iov_base + i->iov_offset;
-
-               left = __copy_from_user_inatomic_nocache(kaddr + offset,
-                                                       buf, bytes, bytes);
+               left = __copy_from_user_inatomic(kaddr + offset, buf, bytes);
                copied = bytes - left;
        } else {
                copied = __iovec_copy_from_user_inatomic(kaddr + offset,
@@ -1881,8 +1879,7 @@ size_t iov_iter_copy_from_user(struct page *page,
        if (likely(i->nr_segs == 1)) {
                int left;
                char __user *buf = i->iov->iov_base + i->iov_offset;
-
-               left = __copy_from_user_nocache(kaddr + offset, buf, bytes, bytes);
+               left = __copy_from_user(kaddr + offset, buf, bytes);
                copied = bytes - left;
        } else {
                copied = __iovec_copy_from_user_inatomic(kaddr + offset,
index bf54f8a2cf1df63172706477d666f8bbea6b0268..0c04615651b7e8b412e85cdeee26365ddee7515c 100644 (file)
@@ -354,7 +354,7 @@ __xip_file_write(struct file *filp, const char __user *buf,
                        break;
 
                copied = bytes -
-                       __copy_from_user_nocache(xip_mem + offset, buf, bytes, bytes);
+                       __copy_from_user_nocache(xip_mem + offset, buf, bytes);
 
                if (likely(copied > 0)) {
                        status = copied;