2008-10-23 13:26:29 +08:00
|
|
|
#ifndef _ASM_X86_UACCESS_64_H
|
|
|
|
#define _ASM_X86_UACCESS_64_H
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* User space memory access functions
|
|
|
|
*/
|
|
|
|
#include <linux/compiler.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/prefetch.h>
|
2008-10-03 05:50:12 +08:00
|
|
|
#include <linux/lockdep.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/page.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy To/From Userspace
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Handles exceptions in both to and from, but doesn't do access_ok */
|
2006-09-26 16:52:39 +08:00
|
|
|
__must_check unsigned long
|
|
|
|
copy_user_generic(void *to, const void *from, unsigned len);
|
|
|
|
|
|
|
|
__must_check unsigned long
|
|
|
|
copy_to_user(void __user *to, const void *from, unsigned len);
|
|
|
|
__must_check unsigned long
|
|
|
|
copy_from_user(void *to, const void __user *from, unsigned len);
|
|
|
|
__must_check unsigned long
|
|
|
|
copy_in_user(void __user *to, const void __user *from, unsigned len);
|
|
|
|
|
|
|
|
static __always_inline __must_check
|
|
|
|
int __copy_from_user(void *dst, const void __user *src, unsigned size)
|
2008-03-23 16:03:49 +08:00
|
|
|
{
|
2006-09-26 16:52:40 +08:00
|
|
|
int ret = 0;
|
2008-09-10 19:37:17 +08:00
|
|
|
|
2008-09-10 19:37:17 +08:00
|
|
|
might_fault();
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!__builtin_constant_p(size))
|
2008-03-23 16:03:49 +08:00
|
|
|
return copy_user_generic(dst, (__force void *)src, size);
|
|
|
|
switch (size) {
|
|
|
|
case 1:__get_user_asm(*(u8 *)dst, (u8 __user *)src,
|
|
|
|
ret, "b", "b", "=q", 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2008-03-23 16:03:49 +08:00
|
|
|
case 2:__get_user_asm(*(u16 *)dst, (u16 __user *)src,
|
|
|
|
ret, "w", "w", "=r", 2);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2008-03-23 16:03:49 +08:00
|
|
|
case 4:__get_user_asm(*(u32 *)dst, (u32 __user *)src,
|
|
|
|
ret, "l", "k", "=r", 4);
|
|
|
|
return ret;
|
|
|
|
case 8:__get_user_asm(*(u64 *)dst, (u64 __user *)src,
|
|
|
|
ret, "q", "", "=r", 8);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
case 10:
|
2008-03-23 16:03:49 +08:00
|
|
|
__get_user_asm(*(u64 *)dst, (u64 __user *)src,
|
2008-11-14 10:06:04 +08:00
|
|
|
ret, "q", "", "=r", 10);
|
2008-03-23 16:03:49 +08:00
|
|
|
if (unlikely(ret))
|
|
|
|
return ret;
|
|
|
|
__get_user_asm(*(u16 *)(8 + (char *)dst),
|
|
|
|
(u16 __user *)(8 + (char __user *)src),
|
|
|
|
ret, "w", "w", "=r", 2);
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
case 16:
|
2008-03-23 16:03:49 +08:00
|
|
|
__get_user_asm(*(u64 *)dst, (u64 __user *)src,
|
|
|
|
ret, "q", "", "=r", 16);
|
|
|
|
if (unlikely(ret))
|
|
|
|
return ret;
|
|
|
|
__get_user_asm(*(u64 *)(8 + (char *)dst),
|
|
|
|
(u64 __user *)(8 + (char __user *)src),
|
|
|
|
ret, "q", "", "=r", 8);
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
2008-03-23 16:03:49 +08:00
|
|
|
return copy_user_generic(dst, (__force void *)src, size);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-03-23 16:03:49 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-09-26 16:52:39 +08:00
|
|
|
static __always_inline __must_check
|
|
|
|
int __copy_to_user(void __user *dst, const void *src, unsigned size)
|
2008-03-23 16:03:49 +08:00
|
|
|
{
|
2006-09-26 16:52:40 +08:00
|
|
|
int ret = 0;
|
2008-09-10 19:37:17 +08:00
|
|
|
|
2008-09-10 19:37:17 +08:00
|
|
|
might_fault();
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!__builtin_constant_p(size))
|
2008-03-23 16:03:49 +08:00
|
|
|
return copy_user_generic((__force void *)dst, src, size);
|
|
|
|
switch (size) {
|
|
|
|
case 1:__put_user_asm(*(u8 *)src, (u8 __user *)dst,
|
|
|
|
ret, "b", "b", "iq", 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2008-03-23 16:03:49 +08:00
|
|
|
case 2:__put_user_asm(*(u16 *)src, (u16 __user *)dst,
|
|
|
|
ret, "w", "w", "ir", 2);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2008-03-23 16:03:49 +08:00
|
|
|
case 4:__put_user_asm(*(u32 *)src, (u32 __user *)dst,
|
|
|
|
ret, "l", "k", "ir", 4);
|
|
|
|
return ret;
|
|
|
|
case 8:__put_user_asm(*(u64 *)src, (u64 __user *)dst,
|
|
|
|
ret, "q", "", "ir", 8);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
case 10:
|
2008-03-23 16:03:49 +08:00
|
|
|
__put_user_asm(*(u64 *)src, (u64 __user *)dst,
|
|
|
|
ret, "q", "", "ir", 10);
|
|
|
|
if (unlikely(ret))
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
asm("":::"memory");
|
2008-03-23 16:03:49 +08:00
|
|
|
__put_user_asm(4[(u16 *)src], 4 + (u16 __user *)dst,
|
|
|
|
ret, "w", "w", "ir", 2);
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
case 16:
|
2008-03-23 16:03:49 +08:00
|
|
|
__put_user_asm(*(u64 *)src, (u64 __user *)dst,
|
|
|
|
ret, "q", "", "ir", 16);
|
|
|
|
if (unlikely(ret))
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
asm("":::"memory");
|
2008-03-23 16:03:49 +08:00
|
|
|
__put_user_asm(1[(u64 *)src], 1 + (u64 __user *)dst,
|
|
|
|
ret, "q", "", "ir", 8);
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
2008-03-23 16:03:49 +08:00
|
|
|
return copy_user_generic((__force void *)dst, src, size);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-03-23 16:03:49 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-09-26 16:52:39 +08:00
|
|
|
static __always_inline __must_check
|
|
|
|
int __copy_in_user(void __user *dst, const void __user *src, unsigned size)
|
2008-03-23 16:03:49 +08:00
|
|
|
{
|
2006-09-26 16:52:40 +08:00
|
|
|
int ret = 0;
|
2008-09-10 19:37:17 +08:00
|
|
|
|
2008-09-10 19:37:17 +08:00
|
|
|
might_fault();
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!__builtin_constant_p(size))
|
2008-03-23 16:03:49 +08:00
|
|
|
return copy_user_generic((__force void *)dst,
|
|
|
|
(__force void *)src, size);
|
|
|
|
switch (size) {
|
|
|
|
case 1: {
|
2005-04-17 06:20:36 +08:00
|
|
|
u8 tmp;
|
2008-03-23 16:03:49 +08:00
|
|
|
__get_user_asm(tmp, (u8 __user *)src,
|
|
|
|
ret, "b", "b", "=q", 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (likely(!ret))
|
2008-03-23 16:03:49 +08:00
|
|
|
__put_user_asm(tmp, (u8 __user *)dst,
|
|
|
|
ret, "b", "b", "iq", 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2008-03-23 16:03:49 +08:00
|
|
|
case 2: {
|
2005-04-17 06:20:36 +08:00
|
|
|
u16 tmp;
|
2008-03-23 16:03:49 +08:00
|
|
|
__get_user_asm(tmp, (u16 __user *)src,
|
|
|
|
ret, "w", "w", "=r", 2);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (likely(!ret))
|
2008-03-23 16:03:49 +08:00
|
|
|
__put_user_asm(tmp, (u16 __user *)dst,
|
|
|
|
ret, "w", "w", "ir", 2);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-03-23 16:03:49 +08:00
|
|
|
case 4: {
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 tmp;
|
2008-03-23 16:03:49 +08:00
|
|
|
__get_user_asm(tmp, (u32 __user *)src,
|
|
|
|
ret, "l", "k", "=r", 4);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (likely(!ret))
|
2008-03-23 16:03:49 +08:00
|
|
|
__put_user_asm(tmp, (u32 __user *)dst,
|
|
|
|
ret, "l", "k", "ir", 4);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2008-03-23 16:03:49 +08:00
|
|
|
case 8: {
|
2005-04-17 06:20:36 +08:00
|
|
|
u64 tmp;
|
2008-03-23 16:03:49 +08:00
|
|
|
__get_user_asm(tmp, (u64 __user *)src,
|
|
|
|
ret, "q", "", "=r", 8);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (likely(!ret))
|
2008-03-23 16:03:49 +08:00
|
|
|
__put_user_asm(tmp, (u64 __user *)dst,
|
|
|
|
ret, "q", "", "ir", 8);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
default:
|
2008-03-23 16:03:49 +08:00
|
|
|
return copy_user_generic((__force void *)dst,
|
|
|
|
(__force void *)src, size);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-03-23 16:03:49 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-03-23 16:03:49 +08:00
|
|
|
__must_check long
|
2006-09-26 16:52:39 +08:00
|
|
|
strncpy_from_user(char *dst, const char __user *src, long count);
|
2008-03-23 16:03:49 +08:00
|
|
|
__must_check long
|
2006-09-26 16:52:39 +08:00
|
|
|
__strncpy_from_user(char *dst, const char __user *src, long count);
|
|
|
|
__must_check long strnlen_user(const char __user *str, long n);
|
|
|
|
__must_check long __strnlen_user(const char __user *str, long n);
|
|
|
|
__must_check long strlen_user(const char __user *str);
|
|
|
|
__must_check unsigned long clear_user(void __user *mem, unsigned long len);
|
|
|
|
__must_check unsigned long __clear_user(void __user *mem, unsigned long len);
|
|
|
|
|
2008-03-23 16:03:49 +08:00
|
|
|
__must_check long __copy_from_user_inatomic(void *dst, const void __user *src,
|
|
|
|
unsigned size);
|
2006-09-30 07:47:55 +08:00
|
|
|
|
|
|
|
static __must_check __always_inline int
|
|
|
|
__copy_to_user_inatomic(void __user *dst, const void *src, unsigned size)
|
|
|
|
{
|
|
|
|
return copy_user_generic((__force void *)dst, src, size);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-03-23 16:03:49 +08:00
|
|
|
extern long __copy_user_nocache(void *dst, const void __user *src,
|
|
|
|
unsigned size, int zerorest);
|
2007-02-13 20:26:19 +08:00
|
|
|
|
2008-03-23 16:03:49 +08:00
|
|
|
static inline int __copy_from_user_nocache(void *dst, const void __user *src,
|
2009-02-25 15:21:52 +08:00
|
|
|
unsigned size, unsigned long total)
|
2007-02-13 20:26:19 +08:00
|
|
|
{
|
|
|
|
might_sleep();
|
x86: fix performance regression in write() syscall
While the introduction of __copy_from_user_nocache (see commit:
0812a579c92fefa57506821fa08e90f47cb6dbdd) may have been an improvement
for sufficiently large writes, there is evidence to show that it is
deterimental for small writes. Unixbench's fstime test gives the
following results for 256 byte writes with MAX_BLOCK of 2000:
2.6.29-rc6 ( 5 samples, each in KB/sec ):
283750, 295200, 294500, 293000, 293300
2.6.29-rc6 + this patch (5 samples, each in KB/sec):
313050, 3106750, 293350, 306300, 307900
2.6.18
395700, 342000, 399100, 366050, 359850
See w_test() in src/fstime.c in unixbench version 4.1.0. Basically, the above test
consists of counting how much we can write in this manner:
alarm(10);
while (!sigalarm) {
for (f_blocks = 0; f_blocks < 2000; ++f_blocks) {
write(f, buf, 256);
}
lseek(f, 0L, 0);
}
Note, there are other components to the write syscall regression
that are not addressed here.
Signed-off-by: Salman Qazi <sqazi@google.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-02-24 10:03:04 +08:00
|
|
|
/*
|
|
|
|
* 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():
|
|
|
|
*/
|
2009-02-25 15:22:20 +08:00
|
|
|
if (likely(total >= PAGE_SIZE))
|
x86: fix performance regression in write() syscall
While the introduction of __copy_from_user_nocache (see commit:
0812a579c92fefa57506821fa08e90f47cb6dbdd) may have been an improvement
for sufficiently large writes, there is evidence to show that it is
deterimental for small writes. Unixbench's fstime test gives the
following results for 256 byte writes with MAX_BLOCK of 2000:
2.6.29-rc6 ( 5 samples, each in KB/sec ):
283750, 295200, 294500, 293000, 293300
2.6.29-rc6 + this patch (5 samples, each in KB/sec):
313050, 3106750, 293350, 306300, 307900
2.6.18
395700, 342000, 399100, 366050, 359850
See w_test() in src/fstime.c in unixbench version 4.1.0. Basically, the above test
consists of counting how much we can write in this manner:
alarm(10);
while (!sigalarm) {
for (f_blocks = 0; f_blocks < 2000; ++f_blocks) {
write(f, buf, 256);
}
lseek(f, 0L, 0);
}
Note, there are other components to the write syscall regression
that are not addressed here.
Signed-off-by: Salman Qazi <sqazi@google.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-02-24 10:03:04 +08:00
|
|
|
return __copy_user_nocache(dst, src, size, 1);
|
|
|
|
else
|
|
|
|
return __copy_from_user(dst, src, size);
|
2007-02-13 20:26:19 +08:00
|
|
|
}
|
|
|
|
|
2008-03-23 16:03:49 +08:00
|
|
|
static inline int __copy_from_user_inatomic_nocache(void *dst,
|
2009-02-25 15:21:52 +08:00
|
|
|
const void __user *src, unsigned size, unsigned total)
|
2007-02-13 20:26:19 +08:00
|
|
|
{
|
2009-02-25 15:22:20 +08:00
|
|
|
if (likely(total >= PAGE_SIZE))
|
x86: fix performance regression in write() syscall
While the introduction of __copy_from_user_nocache (see commit:
0812a579c92fefa57506821fa08e90f47cb6dbdd) may have been an improvement
for sufficiently large writes, there is evidence to show that it is
deterimental for small writes. Unixbench's fstime test gives the
following results for 256 byte writes with MAX_BLOCK of 2000:
2.6.29-rc6 ( 5 samples, each in KB/sec ):
283750, 295200, 294500, 293000, 293300
2.6.29-rc6 + this patch (5 samples, each in KB/sec):
313050, 3106750, 293350, 306300, 307900
2.6.18
395700, 342000, 399100, 366050, 359850
See w_test() in src/fstime.c in unixbench version 4.1.0. Basically, the above test
consists of counting how much we can write in this manner:
alarm(10);
while (!sigalarm) {
for (f_blocks = 0; f_blocks < 2000; ++f_blocks) {
write(f, buf, 256);
}
lseek(f, 0L, 0);
}
Note, there are other components to the write syscall regression
that are not addressed here.
Signed-off-by: Salman Qazi <sqazi@google.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-02-24 10:03:04 +08:00
|
|
|
return __copy_user_nocache(dst, src, size, 0);
|
|
|
|
else
|
|
|
|
return __copy_from_user_inatomic(dst, src, size);
|
2007-02-13 20:26:19 +08:00
|
|
|
}
|
|
|
|
|
2008-07-02 21:48:21 +08:00
|
|
|
unsigned long
|
|
|
|
copy_user_handle_tail(char *to, char *from, unsigned len, unsigned zerorest);
|
|
|
|
|
2008-10-23 13:26:29 +08:00
|
|
|
#endif /* _ASM_X86_UACCESS_64_H */
|