diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-03-31 14:35:30 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-03-31 14:35:30 -0700 |
commit | 1f8c538ed6a3323b06c2459e9ca36e0ae8bb0ebc (patch) | |
tree | 852cfd52103939d2ddcba38f4f1c34b9da258d97 /arch/s390/lib/uaccess_mvcos.c | |
parent | 190f918660a69d1c56fd05dc8c6cbb8336a8a0af (diff) | |
parent | 233faec97a1dfef1f4bc271f9e5d33f2ba4845ca (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 updates from Martin Schwidefsky:
"There are two memory management related changes, the CMMA support for
KVM to avoid swap-in of freed pages and the split page table lock for
the PMD level. These two come with common code changes in mm/.
A fix for the long standing theoretical TLB flush problem, this one
comes with a common code change in kernel/sched/.
Another set of changes is Heikos uaccess work, included is the initial
set of patches with more to come.
And fixes and cleanups as usual"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (36 commits)
s390/con3270: optionally disable auto update
s390/mm: remove unecessary parameter from pgste_ipte_notify
s390/mm: remove unnecessary parameter from gmap_do_ipte_notify
s390/mm: fixing comment so that parameter name match
s390/smp: limit number of cpus in possible cpu mask
hypfs: Add clarification for "weight_min" attribute
s390: update defconfigs
s390/ptrace: add support for PTRACE_SINGLEBLOCK
s390/perf: make print_debug_cf() static
s390/topology: Remove call to update_cpu_masks()
s390/compat: remove compat exec domain
s390: select CONFIG_TTY for use of tty in unconditional keyboard driver
s390/appldata_os: fix cpu array size calculation
s390/checksum: remove memset() within csum_partial_copy_from_user()
s390/uaccess: remove copy_from_user_real()
s390/sclp_early: Return correct HSA block count also for zero
s390: add some drivers/subsystems to the MAINTAINERS file
s390: improve debug feature usage
s390/airq: add support for irq ranges
s390/mm: enable split page table lock for PMD level
...
Diffstat (limited to 'arch/s390/lib/uaccess_mvcos.c')
-rw-r--r-- | arch/s390/lib/uaccess_mvcos.c | 116 |
1 files changed, 91 insertions, 25 deletions
diff --git a/arch/s390/lib/uaccess_mvcos.c b/arch/s390/lib/uaccess_mvcos.c index 4b7993bf69b9..ae97b8df11aa 100644 --- a/arch/s390/lib/uaccess_mvcos.c +++ b/arch/s390/lib/uaccess_mvcos.c @@ -6,8 +6,11 @@ * Gerald Schaefer (gerald.schaefer@de.ibm.com) */ +#include <linux/jump_label.h> #include <linux/errno.h> +#include <linux/init.h> #include <linux/mm.h> +#include <asm/facility.h> #include <asm/uaccess.h> #include <asm/futex.h> #include "uaccess.h" @@ -26,7 +29,10 @@ #define SLR "slgr" #endif -static size_t copy_from_user_mvcos(size_t size, const void __user *ptr, void *x) +static struct static_key have_mvcos = STATIC_KEY_INIT_TRUE; + +static inline unsigned long copy_from_user_mvcos(void *x, const void __user *ptr, + unsigned long size) { register unsigned long reg0 asm("0") = 0x81UL; unsigned long tmp1, tmp2; @@ -65,7 +71,16 @@ static size_t copy_from_user_mvcos(size_t size, const void __user *ptr, void *x) return size; } -static size_t copy_to_user_mvcos(size_t size, void __user *ptr, const void *x) +unsigned long __copy_from_user(void *to, const void __user *from, unsigned long n) +{ + if (static_key_true(&have_mvcos)) + return copy_from_user_mvcos(to, from, n); + return copy_from_user_pt(to, from, n); +} +EXPORT_SYMBOL(__copy_from_user); + +static inline unsigned long copy_to_user_mvcos(void __user *ptr, const void *x, + unsigned long size) { register unsigned long reg0 asm("0") = 0x810000UL; unsigned long tmp1, tmp2; @@ -94,8 +109,16 @@ static size_t copy_to_user_mvcos(size_t size, void __user *ptr, const void *x) return size; } -static size_t copy_in_user_mvcos(size_t size, void __user *to, - const void __user *from) +unsigned long __copy_to_user(void __user *to, const void *from, unsigned long n) +{ + if (static_key_true(&have_mvcos)) + return copy_to_user_mvcos(to, from, n); + return copy_to_user_pt(to, from, n); +} +EXPORT_SYMBOL(__copy_to_user); + +static inline unsigned long copy_in_user_mvcos(void __user *to, const void __user *from, + unsigned long size) { register unsigned long reg0 asm("0") = 0x810081UL; unsigned long tmp1, tmp2; @@ -117,7 +140,15 @@ static size_t copy_in_user_mvcos(size_t size, void __user *to, return size; } -static size_t clear_user_mvcos(size_t size, void __user *to) +unsigned long __copy_in_user(void __user *to, const void __user *from, unsigned long n) +{ + if (static_key_true(&have_mvcos)) + return copy_in_user_mvcos(to, from, n); + return copy_in_user_pt(to, from, n); +} +EXPORT_SYMBOL(__copy_in_user); + +static inline unsigned long clear_user_mvcos(void __user *to, unsigned long size) { register unsigned long reg0 asm("0") = 0x810000UL; unsigned long tmp1, tmp2; @@ -145,17 +176,26 @@ static size_t clear_user_mvcos(size_t size, void __user *to) return size; } -static size_t strnlen_user_mvcos(size_t count, const char __user *src) +unsigned long __clear_user(void __user *to, unsigned long size) { - size_t done, len, offset, len_str; + if (static_key_true(&have_mvcos)) + return clear_user_mvcos(to, size); + return clear_user_pt(to, size); +} +EXPORT_SYMBOL(__clear_user); + +static inline unsigned long strnlen_user_mvcos(const char __user *src, + unsigned long count) +{ + unsigned long done, len, offset, len_str; char buf[256]; done = 0; do { - offset = (size_t)src & ~PAGE_MASK; + offset = (unsigned long)src & ~PAGE_MASK; len = min(256UL, PAGE_SIZE - offset); len = min(count - done, len); - if (copy_from_user_mvcos(len, src, buf)) + if (copy_from_user_mvcos(buf, src, len)) return 0; len_str = strnlen(buf, len); done += len_str; @@ -164,18 +204,26 @@ static size_t strnlen_user_mvcos(size_t count, const char __user *src) return done + 1; } -static size_t strncpy_from_user_mvcos(size_t count, const char __user *src, - char *dst) +unsigned long __strnlen_user(const char __user *src, unsigned long count) { - size_t done, len, offset, len_str; + if (static_key_true(&have_mvcos)) + return strnlen_user_mvcos(src, count); + return strnlen_user_pt(src, count); +} +EXPORT_SYMBOL(__strnlen_user); - if (unlikely(!count)) +static inline long strncpy_from_user_mvcos(char *dst, const char __user *src, + long count) +{ + unsigned long done, len, offset, len_str; + + if (unlikely(count <= 0)) return 0; done = 0; do { - offset = (size_t)src & ~PAGE_MASK; + offset = (unsigned long)src & ~PAGE_MASK; len = min(count - done, PAGE_SIZE - offset); - if (copy_from_user_mvcos(len, src, dst)) + if (copy_from_user_mvcos(dst, src, len)) return -EFAULT; len_str = strnlen(dst, len); done += len_str; @@ -185,13 +233,31 @@ static size_t strncpy_from_user_mvcos(size_t count, const char __user *src, return done; } -struct uaccess_ops uaccess_mvcos = { - .copy_from_user = copy_from_user_mvcos, - .copy_to_user = copy_to_user_mvcos, - .copy_in_user = copy_in_user_mvcos, - .clear_user = clear_user_mvcos, - .strnlen_user = strnlen_user_mvcos, - .strncpy_from_user = strncpy_from_user_mvcos, - .futex_atomic_op = futex_atomic_op_pt, - .futex_atomic_cmpxchg = futex_atomic_cmpxchg_pt, -}; +long __strncpy_from_user(char *dst, const char __user *src, long count) +{ + if (static_key_true(&have_mvcos)) + return strncpy_from_user_mvcos(dst, src, count); + return strncpy_from_user_pt(dst, src, count); +} +EXPORT_SYMBOL(__strncpy_from_user); + +/* + * The uaccess page tabe walk variant can be enforced with the "uaccesspt" + * kernel parameter. This is mainly for debugging purposes. + */ +static int force_uaccess_pt __initdata; + +static int __init parse_uaccess_pt(char *__unused) +{ + force_uaccess_pt = 1; + return 0; +} +early_param("uaccesspt", parse_uaccess_pt); + +static int __init uaccess_init(void) +{ + if (IS_ENABLED(CONFIG_32BIT) || force_uaccess_pt || !test_facility(27)) + static_key_slow_dec(&have_mvcos); + return 0; +} +early_initcall(uaccess_init); |