diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2016-10-20 07:39:45 -0700 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2016-10-24 09:24:26 -0700 |
commit | 91e040a79df73d371f70792f30380d4e44805250 (patch) | |
tree | b2732eadb60fc5575c465a81cc637a899330c1b5 /arch/arc/kernel | |
parent | 1dec78585328db00e33fb18dc1a6deed0e2095a5 (diff) | |
download | linux-stable-91e040a79df73d371f70792f30380d4e44805250.tar.gz linux-stable-91e040a79df73d371f70792f30380d4e44805250.tar.bz2 linux-stable-91e040a79df73d371f70792f30380d4e44805250.zip |
ARC: syscall for userspace cmpxchg assist
Older ARC700 cores (ARC750 specifically) lack instructions to implement
atomic r-w-w. This is problematic for userspace libraries such as NPTL
which need atomic primitives. So enable them by providing kernel assist.
This is costly but really the only sane soluton (othern than tight
spinning using the otherwise availiable atomic exchange EX instruciton).
Good thing is there are only a few of these cores running Linux out in
the wild.
This only works on UP systems.
Reviewed-by: Colin Ian King <colin.king@canonical.com>
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
Diffstat (limited to 'arch/arc/kernel')
-rw-r--r-- | arch/arc/kernel/process.c | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/arch/arc/kernel/process.c b/arch/arc/kernel/process.c index be1972bd2729..59aa43cb146e 100644 --- a/arch/arc/kernel/process.c +++ b/arch/arc/kernel/process.c @@ -41,6 +41,39 @@ SYSCALL_DEFINE0(arc_gettls) return task_thread_info(current)->thr_ptr; } +SYSCALL_DEFINE3(arc_usr_cmpxchg, int *, uaddr, int, expected, int, new) +{ + int uval; + int ret; + + /* + * This is only for old cores lacking LLOCK/SCOND, which by defintion + * can't possibly be SMP. Thus doesn't need to be SMP safe. + * And this also helps reduce the overhead for serializing in + * the UP case + */ + WARN_ON_ONCE(IS_ENABLED(CONFIG_SMP)); + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + return -EFAULT; + + preempt_disable(); + + ret = __get_user(uval, uaddr); + if (ret) + goto done; + + if (uval != expected) + ret = -EAGAIN; + else + ret = __put_user(new, uaddr); + +done: + preempt_enable(); + + return ret; +} + void arch_cpu_idle(void) { /* sleep, but enable all interrupts before committing */ |