convert mmap_sem to a scalable rw_mutex
Subsystems (47)
cover convert mmap_sem to a scalable rw_mutex
I was toying with a scalable rw_mutex and found that it gives ~10% reduction in system time on ebizzy runs (without the MADV_FREE patch).
2-way x86_64 pentium D box:
2.6.21
/usr/bin/time ./ebizzy -m -P
60.10user 137.72system 1:49.59elapsed 180%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swaps
/usr/bin/time ./ebizzy -m -P
59.73user 139.50system 1:50.28elapsed 180%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555878minor)pagefaults 0swaps
/usr/bin/time ./ebizzy -m -P
59.49user 137.74system 1:49.22elapsed 180%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swaps
2.6.21-rw_mutex
/usr/bin/time ./ebizzy -m -P
57.85user 124.30system 1:42.99elapsed 176%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swaps
/usr/bin/time ./ebizzy -m -P
58.09user 124.11system 1:43.18elapsed 176%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555876minor)pagefaults 0swaps
/usr/bin/time ./ebizzy -m -P
57.36user 124.92system 1:43.52elapsed 176%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swaps
Signature · 5 lines
-- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 1/2 scalable rw_mutex
Scalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
--- include/linux/rwmutex.h | 103 +++++++++++++++++++++++++++++++++++++ kernel/Makefile | 3 - kernel/rwmutex.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+), 1 deletion(-)
include/linux/rwmutex.h new file +103 -0
@@ -0,0 +1,103 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* This file contains the public data structure and API definitions. +*/ +#ifndef _LINUX_RWMUTEX_H +#define _LINUX_RWMUTEX_H + +#include <linux/preempt.h> +#include <linux/wait.h> +#include <linux/percpu_counter.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> +#include <asm/atomic.h> + +struct rw_mutex { +/* Read mostly global */ +struct percpu_counter readers; +unsigned int status; + +/* The following variables are only for the slowpath */ +struct mutex read_mutex;/* r -> w waiting */ +struct mutex write_mutex;/* w -> w waiting */ +wait_queue_head_t wait_queue;/* w -> r waiting */ +atomic_t read_waiters; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +struct lockdep_map dep_map; +#endif +}; + +extern void __rw_mutex_init (struct rw_mutex *rw_mutex,const char * name, +struct lock_class_key *key); +extern void rw_mutex_destroy (struct rw_mutex *rw_mutex); + +#define rw_mutex_init (rw_mutex) \ + do { \ + static struct lock_class_key __key; \ + __rw_mutex_init((rw_mutex), #rw_mutex, &__key); \ + } while (0) + +extern void __rw_mutex_read_lock (struct rw_mutex *rw_mutex); + +extern void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass); +extern void rw_mutex_write_unlock (struct rw_mutex *rw_mutex); + +static inline unsigned int __rw_mutex_reader_slow (struct rw_mutex *rw_mutex) +{ +unsigned int ret; + +smp_rmb (); + ret = rw_mutex->status; + +return ret; +} + +static inline int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +preempt_disable (); +if (likely (!__rw_mutex_reader_slow (rw_mutex))) { +percpu_counter_mod (&rw_mutex->readers,1 ); +preempt_enable (); +return 1 ; + } +preempt_enable (); +return 0 ; +} + +static inline int rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +int ret =__rw_mutex_read_trylock (rw_mutex); +if (ret) +rwsem_acquire_read (&rw_mutex->dep_map,0 ,1 , _RET_IP_); +return ret; +} + +static inline void rw_mutex_read_lock (struct rw_mutex *rw_mutex) +{ +int ret; + +might_sleep (); +rwsem_acquire_read (&rw_mutex->dep_map,0 ,0 , _RET_IP_); + + ret =__rw_mutex_read_trylock (rw_mutex); +if (!ret) +__rw_mutex_read_lock (rw_mutex); +} + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_is_locked (struct rw_mutex *rw_mutex) +{ +return mutex_is_locked (&rw_mutex->write_mutex); +} + +static inline void rw_mutex_write_lock (struct rw_mutex *rw_mutex) +{ +rw_mutex_write_lock_nested (rw_mutex,0 ); +} + +#endif /* _LINUX_RWMUTEX_H */
kernel/rwmutex.c new file +132 -0
132 lines not on this page · raw message
kernel/Makefile +2 -1
@@ -8,7 +8,8 @@ obj-y = sched.o fork.o exec_domain.o signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o + hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o \ + rwmutex.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/
Signature · 5 lines
-- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 6 lines · Peter Zijlstra
Scalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
While this implementation looks pretty nice I really hate growing more and more locking primitives. Do we have any rwsem user that absolutley needs FIFO semantics or could we convert all user over (in which case the objection above is of course completely moot)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerRe: [PATCH 1/2] scalable rw_mutex
In reply to Peter ZijlstraQuoted 6 lines · On Fri, 11 May 2007 15:15:42 +0200 Peter Zijlstra <[email protected]>…
Scalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
It looks .... surprisingly sane, given the history of these things ;)
Quoted 34 lines
--- include/linux/rwmutex.h | 103 +++++++++++++++++++++++++++++++++++++ kernel/Makefile | 3 - kernel/rwmutex.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+), 1 deletion(-)Index:
linux-2.6/include/linux/rwmutex.h
===================================================================include/linux/rwmutex.h new file +22
-0@@ -0,0 +1,103 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* This file contains the public data structure and API definitions. +*/ +#ifndef _LINUX_RWMUTEX_H +#define _LINUX_RWMUTEX_H + +#include <linux/preempt.h> +#include <linux/wait.h> +#include <linux/percpu_counter.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> +#include <asm/atomic.h> + +struct rw_mutex { + /* Read mostly global */ + struct percpu_counter readers; + unsigned int status; +
include/linux/rwmutex.h line 32
Quoted 10 lines
include/linux/rwmutex.h new file +10
-0+/* The following variables are only for the slowpath */ +struct mutex read_mutex;/* r -> w waiting */ +struct mutex write_mutex;/* w -> w waiting */ +wait_queue_head_t wait_queue;/* w -> r waiting */ +atomic_t read_waiters; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +struct lockdep_map dep_map; +#endif +};
A nice comment describing the overall design and the runtime dynamics and the lock's characteristics would be useful. It should include a prominent description of the lock's storage requirements, which are considerable.
include/linux/rwmutex.h line 36
Quoted 3 lines
include/linux/rwmutex.h new file +3
-0+extern void __rw_mutex_init (struct rw_mutex *rw_mutex,const char * name, +struct lock_class_key *key); +extern void rw_mutex_destroy (struct rw_mutex *rw_mutex);
Sometimes you use `extern'.
Quoted 10 lines
include/linux/rwmutex.h new file +10
-0+#define rw_mutex_init (rw_mutex) \ + do { \ + static struct lock_class_key __key; \ + __rw_mutex_init((rw_mutex), #rw_mutex, &__key); \ + } while (0) + +extern void __rw_mutex_read_lock (struct rw_mutex *rw_mutex); + +extern void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass); +extern void rw_mutex_write_unlock (struct rw_mutex *rw_mutex);
include/linux/rwmutex.h line 57
Quoted 10 lines
include/linux/rwmutex.h new file +10
-0+ +static inline unsigned int __rw_mutex_reader_slow (struct rw_mutex *rw_mutex) +{ +unsigned int ret; + +smp_rmb (); + ret = rw_mutex->status; + +return ret; +}
An undocumented barrier!
include/linux/rwmutex.h line 69
Quoted 11 lines
include/linux/rwmutex.h new file +11
-0+static inline int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +preempt_disable (); +if (likely (!__rw_mutex_reader_slow (rw_mutex))) { +percpu_counter_mod (&rw_mutex->readers,1 ); +preempt_enable (); +return 1 ; + } +preempt_enable (); +return 0 ; +}
What does the preempt_disable() do?
Quoted 11 lines
include/linux/rwmutex.h new file +11
-0+static inline int rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +int ret =__rw_mutex_read_trylock (rw_mutex); +if (ret) +rwsem_acquire_read (&rw_mutex->dep_map,0 ,1 , _RET_IP_); +return ret; +} + +static inline void rw_mutex_read_lock (struct rw_mutex *rw_mutex) +{ +int ret;
include/linux/rwmutex.h line 91
Quoted 10 lines
include/linux/rwmutex.h new file +10
-0+ +might_sleep (); +rwsem_acquire_read (&rw_mutex->dep_map,0 ,0 , _RET_IP_); + + ret =__rw_mutex_read_trylock (rw_mutex); +if (!ret) +__rw_mutex_read_lock (rw_mutex); +} + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex);
and other times you don't use extern. I think it's pretty pointless personally.
include/linux/rwmutex.h line 88
Quoted 1 line
include/linux/rwmutex.h new file +1
-0+EXPORT_SYMBOL_GPL (__rw_mutex_init);
down_foo(mmap_sem) was previously accessible to non-gpl modules, so the GPL export might be a problem.
Quoted 22 lines
include/linux/rwmutex.h new file +22
-0+ +void rw_mutex_destroy (struct rw_mutex *rw_mutex) +{ +percpu_counter_destroy (&rw_mutex->readers); +mutex_destroy (&rw_mutex->read_mutex); +mutex_destroy (&rw_mutex->write_mutex); +} +EXPORT_SYMBOL_GPL (rw_mutex_destroy); + +void __rw_mutex_read_lock (struct rw_mutex *rw_mutex) +{ +/* +* read lock slow path; +* count the number of readers waiting on the read_mutex +*/ +atomic_inc (&rw_mutex->read_waiters); +mutex_lock (&rw_mutex->read_mutex); +/* +* rw_mutex->state is only set while the read_mutex is held +* so by serialising on this lock, we're sure its free. +*/ +BUG_ON (rw_mutex->status);
include/linux/rwmutex.h
Quoted 10 lines
include/linux/rwmutex.h new file +10
-0+/* +* take the read reference, and drop the read_waiters count +* and nudge all those waiting on the read_waiters count. +*/ +percpu_counter_mod (&rw_mutex->readers,1 ); +atomic_dec (&rw_mutex->read_waiters); +wake_up_all (&rw_mutex->wait_queue); +mutex_unlock (&rw_mutex->read_mutex); +} +EXPORT_SYMBOL_GPL (__rw_mutex_read_lock);
hm, I'm surprised that any foo_lock() would ever wake anyone up.
include/linux/rwmutex.h
Quoted 5 lines
include/linux/rwmutex.h new file +5
-0+void rw_mutex_read_unlock (struct rw_mutex *rw_mutex) +{ +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +percpu_counter_mod (&rw_mutex->readers,-1 );
percpu_counter_dec()?
include/linux/rwmutex.h
Quoted 5 lines
include/linux/rwmutex.h new file +5
-0+if (unlikely (__rw_mutex_reader_slow (rw_mutex)) && +percpu_counter_sum (&rw_mutex->readers) ==0 ) +wake_up_all (&rw_mutex->wait_queue); +} +EXPORT_SYMBOL_GPL (rw_mutex_read_unlock);
yipes. percpu_counter_sum() is expensive.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 1 line · Andrew Morton
yipes.
percpu_counter_sum()is expensive.
Capable of triggering NMI watchdog on 4096+ processors?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerRe: [PATCH 1/2] scalable rw_mutex
Quoted 5 lines · On Fri, 11 May 2007 10:07:17 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 1 line · Andrew Morton
yipes.
percpu_counter_sum()is expensive.Capable of triggering NMI watchdog on 4096+ processors?
Well. That would be a millisecond per cpu which sounds improbable. And we'd need to be calling it under local_irq_save() which we presently don't. And nobody has reported any problems against the existing callsites.
But it's no speed demon, that's for sure.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 7 lines · Andrew Morton
Quoted 4 lines · On Fri, 11 May 2007 10:07:17 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 1 line · Andrew Morton
yipes.
percpu_counter_sum()is expensive.
Quoted 7 lines
Quoted 1 line
Capable of triggering NMI watchdog on 4096+ processors?
Well. That would be a millisecond per cpu which sounds improbable. And we'd need to be calling it under
local_irq_save()which we presently don't. And nobody has reported any problems against the existing callsites.But it's no speed demon, that's for sure.
There is one possible optimization for this I did some time ago. You don't really need to sum all over the possible map, but only all CPUs that were ever online. But this only helps on systems where the possible map is bigger than online map in the common case. But that shouldn't be the case anymore on x86 -- it just used to be. If it's true on some other architectures it might be still worth it.
Old patches with an network example for reference appended.
-Andi
include/linux/cpumask.h +4 -0
@@ -380,6 +380,7 @@ static inline void __cpus_remap(cpumask_extern cpumask_t cpu_possible_map;extern cpumask_t cpu_online_map;extern cpumask_t cpu_present_map; +extern cpumask_t cpu_everonline_map;#if NR_CPUS >1 #define num_online_cpus () cpus_weight(cpu_online_map)
@@ -388,6 +389,7 @@ extern cpumask_t cpu_present_map;#define cpu_online (cpu) cpu_isset((cpu), cpu_online_map)#define cpu_possible (cpu) cpu_isset((cpu), cpu_possible_map)#define cpu_present (cpu) cpu_isset((cpu), cpu_present_map) +#define cpu_ever_online (cpu) cpu_isset((cpu), cpu_everonline_map)#else #define num_online_cpus () 1#define num_possible_cpus () 1
@@ -395,6 +397,7 @@ extern cpumask_t cpu_present_map;#define cpu_online (cpu) ((cpu) == 0)#define cpu_possible (cpu) ((cpu) == 0)#define cpu_present (cpu) ((cpu) == 0) +#define cpu_ever_online (cpu) ((cpu) == 0)#endif #ifdef CONFIG_SMP
@@ -409,5 +412,6 @@ int __any_online_cpu(const cpumask_t *ma#define for_each_possible_cpu (cpu) for_each_cpu_mask((cpu), cpu_possible_map)#define for_each_online_cpu (cpu) for_each_cpu_mask((cpu), cpu_online_map)#define for_each_present_cpu (cpu) for_each_cpu_mask((cpu), cpu_present_map) +#define for_each_everonline_cpu (cpu) for_each_cpu_mask((cpu), cpu_everonline_map)#endif /* __LINUX_CPUMASK_H */
kernel/cpu.c +6 -0
@@ -26,6 +26,10 @@ static __cpuinitdata RAW_NOTIFIER_HEAD(c*/ static int cpu_hotplug_disabled; +/* Contains any CPUs that were ever online at some point. +No guarantee they were fully initialized though */ +cpumask_t cpu_everonline_map; +#ifdef CONFIG_HOTPLUG_CPU /* Crappy recursive lock-takers in cpufreq! Complain loudly about idiots */
@@ -212,6 +216,8 @@ static int __cpuinit _cpu_up(unsigned inif (cpu_online (cpu) || !cpu_present (cpu))return -EINVAL ; +cpu_set (cpu, cpu_everonline_map); + ret =raw_notifier_call_chain (&cpu_chain,CPU_UP_PREPARE , hcpu);if (ret ==NOTIFY_BAD ) { printk("%s: attempt to bring up CPU %u failed\n",
net/ipv4/proc.c +1 -1
@@ -50,7 +50,7 @@ static int fold_prot_inuse(struct protoint res =0 ;int cpu; -for_each_possible_cpu (cpu) +for_each_everonline_cpu (cpu) res += proto->stats[cpu].inuse;return res;
Andrew Morton
maintainerRe: [PATCH 1/2] scalable rw_mutex
Quoted 15 lines · On 12 May 2007 20:55:28 +0200 Andi Kleen <[email protected]> wrote:
Quoted 13 lines · Andrew Morton
Quoted 5 lines · On Fri, 11 May 2007 10:07:17 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 1 line · Andrew Morton
yipes.
percpu_counter_sum()is expensive.Capable of triggering NMI watchdog on 4096+ processors?
Well. That would be a millisecond per cpu which sounds improbable. And we'd need to be calling it under
local_irq_save()which we presently don't. And nobody has reported any problems against the existing callsites.
Quoted 9 lines
Quoted 1 line
But it's no speed demon, that's for sure.
There is one possible optimization for this I did some time ago. You don't really need to sum all over the possible map, but only all CPUs that were ever online. But this only helps on systems where the possible map is bigger than online map in the common case. But that shouldn't be the case anymore on x86 -- it just used to be. If it's true on some other architectures it might be still worth it.
hm, yeah.
We could put a cpumask in percpu_counter, initialise it to cpu_possible_map. Then, those callsites which have hotplug notifiers can call into new percpu_counter functions which clear and set bits in that cpumask and which drain percpu_counter.counts[cpu] into percpu_counter.count.
And percpu_counter_sum() gets taught to do for_each_cpu_mask(fbc->cpumask).
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerRe: [PATCH 1/2] scalable rw_mutex
Quoted 7 lines · On Sat, 12 May 2007 11:06:24 -0700 Andrew Morton <[email protected]>…
We could put a cpumask in
percpu_counter, initialise it tocpu_possible_map. Then, those callsites which have hotplug notifiers can call into newpercpu_counterfunctions which clear and set bits in that cpumask and which drain percpu_counter.counts[cpu] into percpu_counter.count.And
percpu_counter_sum()gets taught to dofor_each_cpu_mask(fbc->cpumask).
Perhaps we could have a single cpu hotplug notifier in the percpu_counter library. Add register/deregister functions to the percpu_counter API so that all percpu_counters in the machine can be linked together.
One _could_ just register and deregister the counters in percpu_counter_init() and percpu_counter_destroy(), but perhaps that wouldn't suit all callers, dunno.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerRe: [PATCH 1/2] scalable rw_mutex
In reply to Andrew MortonQuoted 29 lines · On Sat, 12 May 2007 11:06:24 -0700 Andrew Morton <[email protected]>…
Quoted 15 lines · On 12 May 2007 20:55:28 +0200 Andi Kleen <[email protected]> wrote:
Quoted 13 lines · Andrew Morton
Quoted 5 lines · On Fri, 11 May 2007 10:07:17 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 1 line · Andrew Morton
yipes.
percpu_counter_sum()is expensive.Capable of triggering NMI watchdog on 4096+ processors?
Well. That would be a millisecond per cpu which sounds improbable. And we'd need to be calling it under
local_irq_save()which we presently don't. And nobody has reported any problems against the existing callsites.Quoted 9 lines
Quoted 1 line
But it's no speed demon, that's for sure.
There is one possible optimization for this I did some time ago. You don't really need to sum all over the possible map, but only all CPUs that were ever online. But this only helps on systems where the possible map is bigger than online map in the common case. But that shouldn't be the case anymore on x86 -- it just used to be. If it's true on some other architectures it might be still worth it.
hm, yeah.
Quoted 7 lines
We could put a cpumask in
percpu_counter, initialise it tocpu_possible_map. Then, those callsites which have hotplug notifiers can call into newpercpu_counterfunctions which clear and set bits in that cpumask and which drain percpu_counter.counts[cpu] into percpu_counter.count.And
percpu_counter_sum()gets taught to dofor_each_cpu_mask(fbc->cpumask).
Like this:
From: Andrew Morton <[email protected]>
per-cpu counters presently must iterate over all possible CPUs in the exhaustive percpu_counter_sum().
But it can be much better to only iterate over the presently-online CPUs. To do this, we must arrange for an offlined CPU's count to be spilled into the counter's central count.
We can do this for all percpu_counters in the machine by linking them into a single global list and walking that list at CPU_DEAD time.
(I hope. Might have race windows in which the percpu_counter_sum() count is inaccurate?)
---
include/linux/percpu_counter.h | 18 ++------ lib/percpu_counter.c | 66 +++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 12 deletions(-)
lib/percpu_counter.c renamed from lib/percpu_counter.c~percpu_counters-use-cpu-notifiers +66 -0
@@ -3,8 +3,17 @@*/ #include <linux/percpu_counter.h> +#include <linux/notifier.h> +#include <linux/mutex.h> +#include <linux/init.h> +#include <linux/cpu.h> #include <linux/module.h> +#ifdef CONFIG_HOTPLUG_CPU +static LIST_HEAD (percpu_counters); +static DEFINE_MUTEX (percpu_counters_lock); +#endif +void percpu_counter_mod (struct percpu_counter *fbc,s32 amount) {long count;
@@ -44,3 +53,60 @@ s64 percpu_counter_sum(struct percpu_coureturn ret <0 ?0 : ret; }EXPORT_SYMBOL (percpu_counter_sum); + +void percpu_counter_init (struct percpu_counter *fbc,s64 amount) +{ +spin_lock_init (&fbc->lock); + fbc->count = amount; + fbc->counters =alloc_percpu (s32); +#ifdef CONFIG_HOTPLUG_CPU +mutex_lock (&percpu_counters_lock); +list_add (&fbc->list, &percpu_counters); +mutex_unlock (&percpu_counters_lock); +#endif +} +EXPORT_SYMBOL (percpu_counter_init); + +void percpu_counter_destroy (struct percpu_counter *fbc) +{ +free_percpu (fbc->counters); +#ifdef CONFIG_HOTPLUG_CPU +mutex_lock (&percpu_counters_lock); +list_del (&fbc->list); +mutex_unlock (&percpu_counters_lock); +#endif +} +EXPORT_SYMBOL (percpu_counter_destroy); + +#ifdef CONFIG_HOTPLUG_CPU +static int __cpuinit percpu_counter_hotcpu_callback (struct notifier_block *nb, +unsigned long action,void *hcpu) +{ +unsigned int cpu; +struct percpu_counter *fbc; + +if (action !=CPU_DEAD ) +return NOTIFY_OK ; + + cpu = (unsigned long )hcpu; +mutex_lock (&percpu_counters_lock); +list_for_each_entry (fbc, &percpu_counters, list) { +s32 *pcount; + +spin_lock (&fbc->lock); + pcount =per_cpu_ptr (fbc->counters, cpu); + fbc->count += *pcount; + *pcount =0 ; +spin_unlock (&fbc->lock); + } +mutex_unlock (&percpu_counters_lock); +return NOTIFY_OK ; +} + +static int __init percpu_counter_startup (void ) +{ +hotcpu_notifier (percpu_counter_hotcpu_callback,0 ); +return 0 ; +} +module_init (percpu_counter_startup); +#endif
diff -puN include/linux/percpu.h~percpu_counters-use-cpu-notifiers include/linux/percpu.h
include/linux/percpu_counter.h renamed from include/linux/percpu_counter.h~percpu_counters-use-cpu-notifiers +6 -12
@@ -8,6 +8,7 @@#include <linux/spinlock.h> #include <linux/smp.h> +#include <linux/list.h> #include <linux/threads.h> #include <linux/percpu.h> #include <linux/types.h>
@@ -17,6 +18,9 @@struct percpu_counter {spinlock_t lock;s64 count; +#ifdef CONFIG_HOTPLUG_CPU +struct list_head list;/* All percpu_counters are on a list */ +#endif s32 *counters; };
@@ -26,18 +30,8 @@ struct percpu_counter {#define FBC_BATCH (NR_CPUS*4)#endif -static inline void percpu_counter_init (struct percpu_counter *fbc,s64 amount) -{ -spin_lock_init (&fbc->lock); - fbc->count = amount; - fbc->counters =alloc_percpu (s32); -} - -static inline void percpu_counter_destroy (struct percpu_counter *fbc) -{ -free_percpu (fbc->counters); -} - +void percpu_counter_init (struct percpu_counter *fbc,s64 amount); +void percpu_counter_destroy (struct percpu_counter *fbc);void percpu_counter_mod (struct percpu_counter *fbc,s32 amount);s64 percpu_counter_sum (struct percpu_counter *fbc);
_
and then this:
From: Andrew Morton <[email protected]>
Now that we have implemented hotunplug-time counter spilling, percpu_counter_sum() only needs to look at online CPus.
---
lib/percpu_counter.c | 2 +- 1 files changed, 1 insertion(+), 1 deletion(-)
lib/percpu_counter.c renamed from lib/percpu_counter.c~percpu_counters-use-for_each_online_cpu +1 -1
@@ -45,7 +45,7 @@ s64 percpu_counter_sum(struct percpu_couspin_lock (&fbc->lock); ret = fbc->count; -for_each_possible_cpu (cpu) { +for_each_online_cpu (cpu) {s32 *pcount =per_cpu_ptr (fbc->counters, cpu); ret += *pcount; }
_
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 2 lines · Andrew Morton
(I hope. Might have race windows in which the
percpu_counter_sum()count is inaccurate?)
The question is how do these race windows affect the locking scheme?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerRe: [PATCH 1/2] scalable rw_mutex
Quoted 6 lines · On Wed, 16 May 2007 16:40:59 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 2 lines · Andrew Morton
(I hope. Might have race windows in which the
percpu_counter_sum()count is inaccurate?)The question is how do these race windows affect the locking scheme?
The race to which I refer here is if another CPU is running percpu_counter_sum() in the window between the clearing of the bit in cpu_online_map and the CPU_DEAD callout. Maybe that's too small to care about in the short-term, dunno.
Officially we should fix that by taking lock_cpu_hotplug() in percpu_counter_sum(), but I hate that thing.
I was thinking of putting a cpumask into the counter. If we do that then there's no race at all: everything happens under fbc->lock. This would be a preferable fix, if we need to fix it.
But I'd prefer that freezer-based cpu-hotplug comes along and saves us again.
umm, actually, we can fix the race by using CPU_DOWN_PREPARE instead of CPU_DEAD. Because it's OK if percpu_counter_sum() looks at a gone-away CPU's slot.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
In reply to Andi KleenQuoted 15 lines · Andi Kleen
kernel/cpu.c +4
-0@@ -26,6 +26,10 @@ static __cpuinitdata RAW_NOTIFIER_HEAD(c*/ static int cpu_hotplug_disabled; +/* Contains any CPUs that were ever online at some point. +No guarantee they were fully initialized though */ +cpumask_t cpu_everonline_map; +#ifdef CONFIG_HOTPLUG_CPU /* Crappy recursive lock-takers in cpufreq! Complain loudly about idiots */
kernel/cpu.c static int __cpuinit _cpu_up(unsigned in · line 220
Quoted 5 lines
kernel/cpu.c +2
-0@@ -212,6 +216,8 @@ static int __cpuinit _cpu_up(unsigned inif (cpu_online (cpu) || !cpu_present (cpu))return -EINVAL ; +cpu_set (cpu, cpu_everonline_map); +
This also allows us to de-uglify workqueue.c a little bit, it uses a home-grown cpu_populated_map.
Oleg.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 2 lines
This also allows us to de-uglify
workqueue.ca little bit, it uses a home-growncpu_populated_map.
It might be obsolete iff more and more architecture don't use NR_CPUS filled possible_map anymore (haven't checked them all to know if it's true or not)
If not there are a couple of more optimizations that can be done, e.g. in networking by converting more code to hotplug notifier.
-Andi
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 9 lines · Andi Kleen
Quoted 3 lines
This also allows us to de-uglify
workqueue.ca little bit, it uses a home-growncpu_populated_map.It might be obsolete iff more and more architecture don't use
NR_CPUSfilledpossible_mapanymore (haven't checked them all to know if it's true or not)If not there are a couple of more optimizations that can be done, e.g. in networking by converting more code to hotplug notifier.
As for workqueue.c, it is not an optimization. It is a documentation. For example, if CPU-hotplug use freezer, we can just do
s/cpu_populated_map/cpu_online_map/
workqueue.c has a hotplug notifier, but we can't migrate work_structs currently in a race-free manner.
So I vote for your patch in any case.
Oleg.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
In reply to Andrew MortonQuoted 10 lines · Andrew Morton
Quoted 6 lines · On Fri, 11 May 2007 15:15:42 +0200 Peter Zijlstra <[email protected]>…
Scalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
It looks .... surprisingly sane, given the history of these things ;)
Thanks!
Quoted 40 lines
Quoted 40 lines
--- include/linux/rwmutex.h | 103 +++++++++++++++++++++++++++++++++++++ kernel/Makefile | 3 - kernel/rwmutex.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+), 1 deletion(-)Index:
linux-2.6/include/linux/rwmutex.h
===================================================================include/linux/rwmutex.h new file +29
-0@@ -0,0 +1,103 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* This file contains the public data structure and API definitions. +*/ +#ifndef _LINUX_RWMUTEX_H +#define _LINUX_RWMUTEX_H + +#include <linux/preempt.h> +#include <linux/wait.h> +#include <linux/percpu_counter.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> +#include <asm/atomic.h> + +struct rw_mutex { + /* Read mostly global */ + struct percpu_counter readers; + unsigned int status; + + /* The following variables are only for the slowpath */ + struct mutex read_mutex; /* r -> w waiting */ + struct mutex write_mutex; /* w -> w waiting */ + wait_queue_head_t wait_queue; /* w -> r waiting */ + atomic_t read_waiters; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC
Quoted 8 lines
include/linux/rwmutex.h line 32
Quoted 4 lines
include/linux/rwmutex.h new file +3
-0+struct lockdep_map dep_map; +#endif +};A nice comment describing the overall design and the runtime dynamics and the lock's characteristics would be useful. It should include a prominent description of the lock's storage requirements, which are considerable.
Yes, storage-wise it is a tad heavy.
I'll try to write up a coherent description.
Quoted 5 lines
include/linux/rwmutex.h line 36
Quoted 3 lines
include/linux/rwmutex.h new file +3
-0+extern void __rw_mutex_init (struct rw_mutex *rw_mutex,const char * name, +struct lock_class_key *key); +extern void rw_mutex_destroy (struct rw_mutex *rw_mutex);Sometimes you use `extern'.
/me does 's/extern //'
Quoted 14 lines
Quoted 14 lines
include/linux/rwmutex.h new file +14
-0+#define rw_mutex_init (rw_mutex) \ + do { \ + static struct lock_class_key __key; \ + __rw_mutex_init((rw_mutex), #rw_mutex, &__key); \ + } while (0) + +extern void __rw_mutex_read_lock (struct rw_mutex *rw_mutex); + +extern void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass); +extern void rw_mutex_write_unlock (struct rw_mutex *rw_mutex); + +static inline unsigned int __rw_mutex_reader_slow (struct rw_mutex *rw_mutex) +{ +unsigned int ret;
Quoted 8 lines
include/linux/rwmutex.h line 57
Quoted 6 lines
include/linux/rwmutex.h new file +6
-0+ +smp_rmb (); + ret = rw_mutex->status; + +return ret; +}An undocumented barrier!
/me adds documentation pointing to the smp_wmb() in __rw_mutex_status_set() and expands the comment there.
Quoted 5 lines
Quoted 5 lines
include/linux/rwmutex.h new file +5
-0+static inline int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +preempt_disable (); +if (likely (!__rw_mutex_reader_slow (rw_mutex))) { +percpu_counter_mod (&rw_mutex->readers,1 );
Quoted 8 lines
include/linux/rwmutex.h line 69
Quoted 6 lines
include/linux/rwmutex.h new file +6
-0+preempt_enable (); +return 1 ; + } +preempt_enable (); +return 0 ; +}What does the
preempt_disable()do?
Good question; and while writing up the answer I had for myself I found it wrong. So this might very well be a race where a read lock succeeds concurrently with a writer - bad!
I seem to need some rest to untangle my brains here. :-(
Quoted 4 lines
include/linux/rwmutex.h line 67
Quoted 1 line
include/linux/rwmutex.h new file +1
-0+EXPORT_SYMBOL_GPL (__rw_mutex_init);
down_foo(mmap_sem)was previously accessible to non-gpl modules, so the GPL export might be a problem.
Right, always breaks my heart to remove _GPL. But I guess
Quoted 25 lines
Quoted 25 lines
include/linux/rwmutex.h new file +25
-0+void rw_mutex_destroy (struct rw_mutex *rw_mutex) +{ +percpu_counter_destroy (&rw_mutex->readers); +mutex_destroy (&rw_mutex->read_mutex); +mutex_destroy (&rw_mutex->write_mutex); +} +EXPORT_SYMBOL_GPL (rw_mutex_destroy); + +void __rw_mutex_read_lock (struct rw_mutex *rw_mutex) +{ +/* +* read lock slow path; +* count the number of readers waiting on the read_mutex +*/ +atomic_inc (&rw_mutex->read_waiters); +mutex_lock (&rw_mutex->read_mutex); +/* +* rw_mutex->state is only set while the read_mutex is held +* so by serialising on this lock, we're sure its free. +*/ +BUG_ON (rw_mutex->status); +/* +* take the read reference, and drop the read_waiters count +* and nudge all those waiting on the read_waiters count. +*/
Quoted 8 lines
include/linux/rwmutex.h line 98
Quoted 6 lines
include/linux/rwmutex.h new file +6
-0+percpu_counter_mod (&rw_mutex->readers,1 ); +atomic_dec (&rw_mutex->read_waiters); +wake_up_all (&rw_mutex->wait_queue); +mutex_unlock (&rw_mutex->read_mutex); +} +EXPORT_SYMBOL_GPL (__rw_mutex_read_lock);hm, I'm surprised that any
foo_lock()would ever wake anyone up.
Yeah, this could use some more documentation; it wakes _write_unlock() which can wait holding off new writers until at least a single reader has had a chance.
Quoted 7 lines
include/linux/rwmutex.h line 103
Quoted 5 lines
include/linux/rwmutex.h new file +5
-0+void rw_mutex_read_unlock (struct rw_mutex *rw_mutex) +{ +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +percpu_counter_mod (&rw_mutex->readers,-1 );
percpu_counter_dec()?
Where was my brain... :-)
Quoted 7 lines
include/linux/rwmutex.h
Quoted 5 lines
include/linux/rwmutex.h new file +5
-0+if (unlikely (__rw_mutex_reader_slow (rw_mutex)) && +percpu_counter_sum (&rw_mutex->readers) ==0 ) +wake_up_all (&rw_mutex->wait_queue); +} +EXPORT_SYMBOL_GPL (rw_mutex_read_unlock);yipes.
percpu_counter_sum()is expensive.
Right, and this instance is not strictly needed for correctness.
It might be possible to remove the other from the wait_event() loop, if that makes any difference. If we fold the counter when switching to the slow path, and use the shared counter there so it doesn't diverge again.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
In reply to Peter Zijlstrainclude/linux/rwmutex.h line 62
Quoted 5 lines · Peter Zijlstra
include/linux/rwmutex.h new file +4
-0+static inline int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +preempt_disable (); +if (likely (!__rw_mutex_reader_slow (rw_mutex))) {
--- WINDOW ---
Quoted 14 lines
kernel/rwmutex.c new file +10
-0+percpu_counter_mod (&rw_mutex->readers,1 ); +preempt_enable (); +return 1 ; + } +preempt_enable (); +return 0 ; +} +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass) +{ +
kernel/rwmutex.c line 105
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+/* +* block new readers +*/ +__rw_mutex_status_set (rw_mutex,RW_MUTEX_READER_SLOW ); +/* +* wait for all readers to go away +*/ +wait_event (rw_mutex->wait_queue, + (percpu_counter_sum (&rw_mutex->readers) ==0 )); +}
This look a bit suspicious, can't mutex_write_lock() set RW_MUTEX_READER_SLOW and find percpu_counter_sum() == 0 in that WINDOW above?
kernel/rwmutex.c line 75
Quoted 9 lines
kernel/rwmutex.c new file +9
-0+void rw_mutex_read_unlock (struct rw_mutex *rw_mutex) +{ +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +percpu_counter_mod (&rw_mutex->readers,-1 ); +if (unlikely (__rw_mutex_reader_slow (rw_mutex)) && +percpu_counter_sum (&rw_mutex->readers) ==0 ) +wake_up_all (&rw_mutex->wait_queue); +}
The same. __rw_mutex_status_set()->wmb() in rw_mutex_write_lock below is not enough. percpu_counter_mod() doesn't take fbc->lock if < FBC_BATCH, so we don't have a proper serialization.
write_lock() sets RW_MUTEX_READER_SLOW, finds percpu_counter_sum() != 0, and sleeps. rw_mutex_read_unlock() decrements cpu-local var, does not see RW_MUTEX_READER_SLOW and skips wake_up_all().
Quoted 33 lines
kernel/rwmutex.c new file +33
-0+void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass) +{ +might_sleep (); +rwsem_acquire (&rw_mutex->dep_map, subclass,0 , _RET_IP_); + +mutex_lock_nested (&rw_mutex->write_mutex, subclass); +mutex_lock_nested (&rw_mutex->read_mutex, subclass); + +/* +* block new readers +*/ +__rw_mutex_status_set (rw_mutex,RW_MUTEX_READER_SLOW ); +/* +* wait for all readers to go away +*/ +wait_event (rw_mutex->wait_queue, + (percpu_counter_sum (&rw_mutex->readers) ==0 )); +} + +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex) +{ +int waiters; + +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +/* +* let the readers rip +*/ +__rw_mutex_status_set (rw_mutex,RW_MUTEX_READER_FAST ); + waiters =atomic_read (&rw_mutex->read_waiters); +mutex_unlock (&rw_mutex->read_mutex); +/* +* wait for at least 1 reader to get through
kernel/rwmutex.c line 131
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+*/ +if (waiters) { +wait_event (rw_mutex->wait_queue, + (atomic_read (&rw_mutex->read_waiters) < waiters)); + } +/* +* before we let the writers rip +*/ +mutex_unlock (&rw_mutex->write_mutex); +}
Looks like we can have only one task on rw_mutex->wait_queue, and it holds ->write_mutex. Can't we use just a "task_struct *write_waiter" instead of ->wait_queue ? This makes rw_mutex smaller.
Oleg.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 9 lines · Oleg Nesterov
include/linux/rwmutex.h line 62
Quoted 5 lines · Peter Zijlstra
include/linux/rwmutex.h new file +4
-0+static inline int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +preempt_disable (); +if (likely (!__rw_mutex_reader_slow (rw_mutex))) {--- WINDOW ---
Quoted 7 lines
include/linux/rwmutex.h line 69
Quoted 7 lines
include/linux/rwmutex.h new file +7
-0+percpu_counter_mod (&rw_mutex->readers,1 ); +preempt_enable (); +return 1 ; + } +preempt_enable (); +return 0 ; +}
Yeah, I found that one when Andrew asked me about that preempt_disable() thing.
How about:
int __rw_mutex_read_trylock(struct rw_mutex *rw_mutex)
{
percpu_counter_inc(&rw_mutex->readers);
if (unlikely(rw_mutex_reader_slow(rw_mutex))) {
percpu_counter_dec(&rw_mutex->readers);
/*
* possibly wake up a writer waiting for this reference to
* disappear
*/
wake_up(&rw_mutex->wait_queue);
return 0;
}
return 1;
}Quoted 11 lines
Quoted 11 lines
[...snip...]
kernel/rwmutex.c new file +8
-0+void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass) +{ + +/* +* block new readers +*/ +__rw_mutex_status_set (rw_mutex,RW_MUTEX_READER_SLOW ); +/*
Quoted 8 lines
kernel/rwmutex.c line 105
Quoted 5 lines
kernel/rwmutex.c new file +5
-0+* wait for all readers to go away +*/ +wait_event (rw_mutex->wait_queue, + (percpu_counter_sum (&rw_mutex->readers) ==0 )); +}This look a bit suspicious, can't
mutex_write_lock()setRW_MUTEX_READER_SLOWand findpercpu_counter_sum()== 0 in that WINDOW above?
Indeed; however with the above having the reverse sequence this has, it should be closed no?
Quoted 7 lines
kernel/rwmutex.c line 73
Quoted 7 lines
kernel/rwmutex.c new file +7
-0+void rw_mutex_read_unlock (struct rw_mutex *rw_mutex) +{ +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +percpu_counter_mod (&rw_mutex->readers,-1 ); +if (unlikely (__rw_mutex_reader_slow (rw_mutex)) && +percpu_counter_sum (&rw_mutex->readers) ==0 )
I took out the percpu_counter_sum()
Quoted 10 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ wake_up_all(&rw_mutex->wait_queue); +}The same. __rw_mutex_status_set()->wmb() in
rw_mutex_write_lockbelow is not enough.percpu_counter_mod()doesn't takefbc->lockif <FBC_BATCH, so we don't have a proper serialization.
write_lock()setsRW_MUTEX_READER_SLOW, findspercpu_counter_sum()!= 0, and sleeps.rw_mutex_read_unlock()decrements cpu-local var, does not seeRW_MUTEX_READER_SLOWand skipswake_up_all().
write lock read lock read unlock
a) state = slow 1) readers++ I) readers-- b) wait(readers == 0) 2) if (state == slow) II) if (state == slow)
That looks pretty safe to me; however are you suggesting the percpu_counter_inc() needs some sort of barrier in order to be reliably picked up by the percpu_counter_sum()?
something like this:
percpu_counter_{inc,dec}smp_wmb()
vs
smp_rmb()
percpu_counter_sum(()
Quoted 39 lines
Quoted 39 lines
kernel/rwmutex.c new file +39
-0+void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass) +{ +might_sleep (); +rwsem_acquire (&rw_mutex->dep_map, subclass,0 , _RET_IP_); + +mutex_lock_nested (&rw_mutex->write_mutex, subclass); +mutex_lock_nested (&rw_mutex->read_mutex, subclass); + +/* +* block new readers +*/ +__rw_mutex_status_set (rw_mutex,RW_MUTEX_READER_SLOW ); +/* +* wait for all readers to go away +*/ +wait_event (rw_mutex->wait_queue, + (percpu_counter_sum (&rw_mutex->readers) ==0 )); +} + +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex) +{ +int waiters; + +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +/* +* let the readers rip +*/ +__rw_mutex_status_set (rw_mutex,RW_MUTEX_READER_FAST ); + waiters =atomic_read (&rw_mutex->read_waiters); +mutex_unlock (&rw_mutex->read_mutex); +/* +* wait for at least 1 reader to get through +*/ +if (waiters) { +wait_event (rw_mutex->wait_queue, + (atomic_read (&rw_mutex->read_waiters) < waiters)); + } +/*
Quoted 8 lines
kernel/rwmutex.c line 131
Quoted 4 lines
kernel/rwmutex.c new file +4
-0+* before we let the writers rip +*/ +mutex_unlock (&rw_mutex->write_mutex); +}Looks like we can have only one task on
rw_mutex->wait_queue, and it holds ->write_mutex. Can't we use just a "task_struct*write_waiter" instead of ->wait_queue ? This makesrw_mutexsmaller.
Good point; I'll try and figure out how to sleep and wake a single task without the waitqueue.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
In reply to Oleg NesterovIt has grown a few undocumented barriers again; but I'd like some feedback on them. /me still hopes some can go.. but these things still mess my head up.
---
Scalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
--- include/linux/rwmutex.h | 83 +++++++++++++++ kernel/Makefile | 3 kernel/rwmutex.c | 254 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 339 insertions(+), 1 deletion(-)
include/linux/rwmutex.h new file +83 -0
@@ -0,0 +1,83 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* This file contains the public data structure and API definitions. +*/ +#ifndef _LINUX_RWMUTEX_H +#define _LINUX_RWMUTEX_H + +#include <linux/preempt.h> +#include <linux/wait.h> +#include <linux/percpu_counter.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> +#include <asm/atomic.h> + +struct rw_mutex { +/* Read mostly global */ +struct percpu_counter readers; +unsigned int status; + +/* The following variables are only for the slowpath */ +struct mutex read_mutex;/* r -> w waiting */ +struct mutex write_mutex;/* w -> w waiting */ +struct task_struct *waiter;/* w -> r waiting */ +atomic_t read_waiters; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +struct lockdep_map dep_map; +#endif +}; + +void __rw_mutex_init (struct rw_mutex *rw_mutex,const char * name, +struct lock_class_key *key); +void rw_mutex_destroy (struct rw_mutex *rw_mutex); + +#define rw_mutex_init (rw_mutex) \ + do { \ + static struct lock_class_key __key; \ + __rw_mutex_init((rw_mutex), #rw_mutex, &__key); \ + } while (0) + +void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex); + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass); +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +int ret =__rw_mutex_read_trylock (rw_mutex); +if (ret) +rwsem_acquire_read (&rw_mutex->dep_map,0 ,1 , _RET_IP_); +return ret; +} + +static inline void rw_mutex_read_lock (struct rw_mutex *rw_mutex) +{ +int ret; + +might_sleep (); +rwsem_acquire_read (&rw_mutex->dep_map,0 ,0 , _RET_IP_); + + ret =__rw_mutex_read_trylock (rw_mutex); +if (!ret) +rw_mutex_read_lock_slow (rw_mutex); +} + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_is_locked (struct rw_mutex *rw_mutex) +{ +return mutex_is_locked (&rw_mutex->write_mutex); +} + +static inline void rw_mutex_write_lock (struct rw_mutex *rw_mutex) +{ +rw_mutex_write_lock_nested (rw_mutex,0 ); +} + +#endif /* _LINUX_RWMUTEX_H */
kernel/rwmutex.c new file +254 -0
254 lines not on this page · raw message
kernel/Makefile +2 -1
@@ -8,7 +8,8 @@ obj-y = sched.o fork.o exec_domain.o signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o + hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o \ + rwmutex.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 22 lines · Peter Zijlstra
kernel/rwmutex.c new file +19
-0+static inline void rw_mutex_readers_dec (struct rw_mutex *rw_mutex) +{ +percpu_counter_dec (&rw_mutex->readers); +smp_wmb (); +} +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex) +{ +rw_mutex_readers_dec (rw_mutex); +/* +* on the slow path; +* nudge the writer waiting for the last reader to go away +*/ +if (unlikely (rw_mutex_reader_slow (rw_mutex))) +rw_mutex_writer_wake (rw_mutex); +} +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass) +{ +mutex_lock_nested (&rw_mutex->write_mutex, subclass); +
kernel/rwmutex.c line 226
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+/* +* block new readers +*/ +mutex_lock_nested (&rw_mutex->read_mutex, subclass); +rw_mutex_status_set (rw_mutex,RW_MUTEX_READER_SLOW ); +/* +* and wait for all current readers to go away +*/ +rw_mutex_writer_wait (rw_mutex, (rw_mutex_readers (rw_mutex) ==0 )); +}
I think this is still not right, but when it comes to barriers we need a true expert (Paul cc-ed).
this code roughly does (the only reader does unlock)
READER WRITER readers = 0; state = 1; wmb(); wmb(); CHECK(state != 0) CHECK(readers == 0)
We need to ensure that we can't miss both CHECKs. Either reader should see RW_MUTEX_READER_SLOW, o writer sees "readers == 0" and does not sleep.
In that case both barriers should be converted to smp_mb(). There was a _long_ discussion about STORE-MB-LOAD behaviour, and experts seem to believe everething is ok.
Another question. Isn't it possible to kill rw_mutex->status ?
I have a vague feeling you can change the code so that
rw_mutex_reader_slow() <=> "->waiter != NULL"
, but I am not sure.
Oleg.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 45 lines · Oleg Nesterov
Quoted 22 lines · Peter Zijlstra
kernel/rwmutex.c new file +19
-0+static inline void rw_mutex_readers_dec (struct rw_mutex *rw_mutex) +{ +percpu_counter_dec (&rw_mutex->readers); +smp_wmb (); +} +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex) +{ +rw_mutex_readers_dec (rw_mutex); +/* +* on the slow path; +* nudge the writer waiting for the last reader to go away +*/ +if (unlikely (rw_mutex_reader_slow (rw_mutex))) +rw_mutex_writer_wake (rw_mutex); +} +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass) +{ +mutex_lock_nested (&rw_mutex->write_mutex, subclass); +kernel/rwmutex.c line 226
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+/* +* block new readers +*/ +mutex_lock_nested (&rw_mutex->read_mutex, subclass); +rw_mutex_status_set (rw_mutex,RW_MUTEX_READER_SLOW ); +/* +* and wait for all current readers to go away +*/ +rw_mutex_writer_wait (rw_mutex, (rw_mutex_readers (rw_mutex) ==0 )); +}I think this is still not right, but when it comes to barriers we need a true expert (Paul cc-ed).
this code roughly does (the only reader does unlock)
READER WRITER readers = 0; state = 1; wmb(); wmb(); CHECK(state != 0) CHECK(readers == 0)
Quoted 7 lines
We need to ensure that we can't miss both CHECKs. Either reader should see
RW_MUTEX_READER_SLOW, o writer sees "readers == 0" and does not sleep.In that case both barriers should be converted to
smp_mb(). There was a _long_ discussion about STORE-MB-LOAD behaviour, and experts seem to believe everething is ok.
Ah, but note that both those CHECK()s have a rmb(), so that ends up being:
READER WRITER readers = 0; state = 1; wmb(); wmb(); rmb(); rmb(); if (state != 0) if (readers == 0)
and a wmb+rmb is a full mb, right?
Quoted 7 lines
Another question. Isn't it possible to kill
rw_mutex->status?I have a vague feeling you can change the code so that
rw_mutex_reader_slow() <=> "->waiter != NULL", but I am not sure.
If not now, it might be possible to make it so. Thanks for the suggestion.
Peter
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 30 lines · Peter Zijlstra
Quoted 9 lines · Oleg Nesterov
this code roughly does (the only reader does unlock)
READER WRITER readers = 0; state = 1; wmb(); wmb(); CHECK(state != 0) CHECK(readers == 0)Quoted 7 lines
We need to ensure that we can't miss both CHECKs. Either reader should see
RW_MUTEX_READER_SLOW, o writer sees "readers == 0" and does not sleep.In that case both barriers should be converted to
smp_mb(). There was a _long_ discussion about STORE-MB-LOAD behaviour, and experts seem to believe everething is ok.Ah, but note that both those CHECK()s have a
rmb(), so that ends up being:READER WRITER readers = 0; state = 1; wmb(); wmb(); rmb(); rmb(); if (state != 0) if (readers == 0)
Quoted 1 line
and a wmb+rmb is a full mb, right?
I used to think the same, but this is wrong: wmb+rmb != mb. wmb+rmb doesn't provide LOAD,STORE or STORE,LOAD ordering.
for example,
LOAD; rmb(); wmb(); STORE;
it is still possible that STORE comes before LOAD. At least this is my understanding.
Oleg.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Changes include:
- wmb+rmb != mb
- ->state folded into ->waiter
---
Subject: scalable rw_mutex
Scalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
--- include/linux/rwmutex.h | 82 ++++++++++++++++ kernel/Makefile | 3 kernel/rwmutex.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+), 1 deletion(-)
include/linux/rwmutex.h new file +82 -0
@@ -0,0 +1,82 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* This file contains the public data structure and API definitions. +*/ +#ifndef _LINUX_RWMUTEX_H +#define _LINUX_RWMUTEX_H + +#include <linux/preempt.h> +#include <linux/wait.h> +#include <linux/percpu_counter.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> +#include <asm/atomic.h> + +struct rw_mutex { +/* Read mostly global */ +struct percpu_counter readers; + +/* The following variables are only for the slowpath */ +struct task_struct *waiter;/* w -> r waiting */ +struct mutex read_mutex;/* r -> w waiting */ +struct mutex write_mutex;/* w -> w waiting */ +atomic_t read_waiters; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +struct lockdep_map dep_map; +#endif +}; + +void __rw_mutex_init (struct rw_mutex *rw_mutex,const char * name, +struct lock_class_key *key); +void rw_mutex_destroy (struct rw_mutex *rw_mutex); + +#define rw_mutex_init (rw_mutex) \ + do { \ + static struct lock_class_key __key; \ + __rw_mutex_init((rw_mutex), #rw_mutex, &__key); \ + } while (0) + +void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex); + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass); +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +int ret =__rw_mutex_read_trylock (rw_mutex); +if (ret) +rwsem_acquire_read (&rw_mutex->dep_map,0 ,1 , _RET_IP_); +return ret; +} + +static inline void rw_mutex_read_lock (struct rw_mutex *rw_mutex) +{ +int ret; + +might_sleep (); +rwsem_acquire_read (&rw_mutex->dep_map,0 ,0 , _RET_IP_); + + ret =__rw_mutex_read_trylock (rw_mutex); +if (!ret) +rw_mutex_read_lock_slow (rw_mutex); +} + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_is_locked (struct rw_mutex *rw_mutex) +{ +return mutex_is_locked (&rw_mutex->write_mutex); +} + +static inline void rw_mutex_write_lock (struct rw_mutex *rw_mutex) +{ +rw_mutex_write_lock_nested (rw_mutex,0 ); +} + +#endif /* _LINUX_RWMUTEX_H */
kernel/rwmutex.c new file +229 -0
229 lines not on this page · raw message
kernel/Makefile +2 -1
@@ -8,7 +8,8 @@ obj-y = sched.o fork.o exec_domain.o signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o + hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o \ + rwmutex.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 64 lines · Peter Zijlstra
Changes include:
- wmb+rmb != mb
- ->state folded into ->waiter---
Subject: scalablerw_mutexScalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
kernel/rwmutex.c new file +49
-0+#define rw_mutex_writer_wait (rw_mutex, condition) \ +do { \ + struct task_struct *tsk = (rw_mutex)->waiter; \ + BUG_ON(tsk != current); \ + \ + set_task_state(tsk, TASK_UNINTERRUPTIBLE); \ + while (!(condition)) { \ + schedule(); \ + set_task_state(tsk, TASK_UNINTERRUPTIBLE); \ + } \ + tsk->state = TASK_RUNNING; \ +} while (0) + +void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +/* +* read lock slow path; +* count the number of readers waiting on the read_mutex +*/ +atomic_inc (&rw_mutex->read_waiters); +mutex_lock (&rw_mutex->read_mutex); + +percpu_counter_inc (&rw_mutex->readers); + +/* +* wake up a possible write unlock; waiting for at least a single +* reader to pass before letting a new writer through. +*/ +atomic_dec (&rw_mutex->read_waiters); + tsk = rw_mutex->waiter; +if (tsk) +wake_up_process (tsk); +mutex_unlock (&rw_mutex->read_mutex); +} +EXPORT_SYMBOL (rw_mutex_read_lock_slow); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +percpu_counter_inc (&rw_mutex->readers); +/* +* ensure the ->readers store and the ->waiter load is properly +* sequenced +*/ +smp_mb (); + tsk = rw_mutex->waiter;
kernel/rwmutex.c line 143
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+if (unlikely (tsk)) { +percpu_counter_dec (&rw_mutex->readers); +/* +* ensure the ->readers store has taken place before we issue +* the wake_up +* +* XXX: or does this require an smp_wmb() and the waiter to do +* (smp_rmb(), percpu_counter(&rw_mutex->readers) == 0) +*/ +barrier ();
The store to percpu readers AFAIKS may not become visible until after the wakeup and therefore after the waiter checks for readers. So I think this needs a full smp_mb, doesn't it? (you seem to have the barrier in unlock, so I can't see what differs here).
kernel/rwmutex.c line 148
Quoted 5 lines
kernel/rwmutex.c new file +5
-0+/* +* possibly wake up a writer waiting for this reference to +* disappear +*/ +wake_up_process (tsk);
Pretty sure you need to be more careful here: the waiter might have left the locking code and have exitted by this time, no? (ditto for the rest of the wake_up_process calls)
Quoted 65 lines
kernel/rwmutex.c new file +65
-0+return 0 ; + } +return 1 ; +} +EXPORT_SYMBOL (__rw_mutex_read_trylock); + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +percpu_counter_dec (&rw_mutex->readers); +/* +* ensure the ->readers store and the ->waiter load is properly +* sequenced +*/ +smp_mb (); + tsk = rw_mutex->waiter; +if (unlikely (tsk)) { +/* +* on the slow path; nudge the writer waiting for the last +* reader to go away +*/ +wake_up_process (tsk); + } +} +EXPORT_SYMBOL (rw_mutex_read_unlock); + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass) +{ +might_sleep (); +rwsem_acquire (&rw_mutex->dep_map, subclass,0 , _RET_IP_); + +mutex_lock_nested (&rw_mutex->write_mutex, subclass); +BUG_ON (rw_mutex->waiter); + +/* +* block new readers +*/ +mutex_lock_nested (&rw_mutex->read_mutex, subclass); + rw_mutex->waiter = current; +/* +* full barrier to sequence the store of ->waiter +* and the load of ->readers +*/ +smp_mb (); +/* +* and wait for all current readers to go away +*/ +rw_mutex_writer_wait (rw_mutex, + (percpu_counter_sum (&rw_mutex->readers) ==0 )); +} +EXPORT_SYMBOL (rw_mutex_write_lock_nested); + +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex) +{ +int waiters; + +might_sleep (); +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +/* +* let the readers rip +*/
kernel/rwmutex.c line 223
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+ waiters =atomic_read (&rw_mutex->read_waiters); +mutex_unlock (&rw_mutex->read_mutex); +/* +* wait for at least 1 reader to get through +*/ +if (waiters) { +rw_mutex_writer_wait (rw_mutex, + (atomic_read (&rw_mutex->read_waiters) < waiters)); + } + rw_mutex->waiter =NULL ;
Hmm, if you have set rw_mutex->waiter to NULL _after_ waiting for read_waiters to be decremented below value X, don't you have a starvation problem?
What I believe you need to do is this:
set_task_state(task_uninterruptible);
rw_mutex->waiter = NULL;
smp_mb();
if (read_waiters >= waiters)
schedule();--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
In reply to Peter ZijlstraQuoted 8 lines · Peter Zijlstra
Changes include:
- wmb+rmb != mb
- ->state folded into ->waiter---
Subject: scalablerw_mutex
Quoted 6 lines
Scalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
Hmmm... brlock reincarnates, but as sleeplock. ;-)
I believe that there are a few severe problems in this code, search for "!!!" to quickly find the specific areas that concern me.
Thanx, Paul
Quoted 6 lines
Signed-off-by Peter Zijlstra <[email protected]>--- include/linux/rwmutex.h | 82 ++++++++++++++++ kernel/Makefile | 3 kernel/rwmutex.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+), 1 deletion(-)
List of races that must be resolved:
1. Read acquire vs. write acquire.
rw_mutex_read_lock() invokes __rw_mutex_read_trylock(), if
this fails, invokes rw_mutex_read_lock_slow().
__rw_mutex_read_trylock() increments the per-CPU counter,
does smp_mb(), picks up ->waiter:
if non-NULL decrements the per-CPU counter, does a barrier(), does wake_up_process() on the task fetched from ->waiter. Return failure.
Otherwise, return success.
rw_mutex_read_lock_slow() increments ->read_waiters,
acquires ->read_mutex, increments the ->readers counter, and decrements the ->read_waiters counter. It then fetches ->waiter, and, if non-NULL, wakes up the tasks.
Either way, releases ->read_mutex.
rw_mutex_write_lock_nested(): acquires ->write_mutex, which
prevents any writer-writer races. Acquires ->read_mutex, which does -not- prevent readers from continuing to acquire. Sets ->waiter to current, which -does- (eventually) stop readers. smp_mb(), then invokes rw_mutex_writer_wait() for the sum of the per-CPU counters to go to zero.
!In principle, this could be indefinitely postponed, but in practice would require an infinite number of reading tasks, so probably OK to ignore. ;-) This can occur because the readers unconditionally increment their per-CPU counters, and decrement it only later.
The smp_mb()s currently in the reader and the writer code forms a Dekker-algorithm-like barrier, preventing both the reader and writer from entering their critical section, as required.
2. Read acquire vs. write release (need to avoid reader sleeping
forever, even in the case where no one ever uses the lock again). rw_mutex_read_lock() invokes __rw_mutex_read_trylock(), if this fails, invokes rw_mutex_read_lock_slow(). __rw_mutex_read_trylock() increments the per-CPU counter, does smp_mb(), picks up ->waiter: if non-NULL decrements the per-CPU counter, does a barrier(), does wake_up_process() on the task fetched from ->waiter. Return failure. Otherwise, return success. rw_mutex_read_lock_slow() increments ->read_waiters, acquires ->read_mutex, increments the ->readers counter, and decrements the ->read_waiters counter. It then fetches ->waiter, and, if non-NULL, wakes up the tasks. Either way, releases ->read_mutex. rw_mutex_write_unlock(): pick up ->read_waiters, release ->read_mutex, if copy of ->read_waiters was non-NULL do slow path (but refetches ->read_waiters??? why??? [Ah -- refetched each time through the loop in the rw_mutex_writer_wait() macro), NULL out ->waiter, then release ->write_mutex. Slow path: Pick up ->waiter, make sure it is us, set state to uninterruptible, loop while ->read_waiters less than the value fetched earlier from ->read_waiters, scheduling each time through, set state back to running. (!!!This is subject to indefinite postponement by a flurry of readers, see the commentary for rw_mutex_write_unlock() interspersed below.) However, the fact that ->read_mutex is unconditionally released by the writer prevents the readers from being starved.
3. Read release vs. write acquire (similar to #2).
rw_mutex_read_unlock(): decrement the per-CPU counter, smb_mb(),
pick up ->waiter, if non-NULL, wake it up.
rw_mutex_write_lock_nested(): acquires ->write_mutex, which
prevents any writer-writer races. Acquires ->read_mutex, which does -not- prevent readers from continuing to acquire. Sets ->waiter to current, which -does- (eventually) stop readers. smp_mb(), then invokes rw_mutex_writer_wait() for the sum of the per-CPU counters to go to zero.
As written, the writer never really blocks, so omitting the wakeup is OK. (Failing to really block is -not- OK given realtime processes, but more on that later.)
4. Read release vs. write release -- presumably this one cannot
happen, but wouldn't want to fall prey to complacency. ;-)
Strangely enough, this -can- happen!!! When the readers
indefinitely postpone writer release, the readers will also be read-releasing... So the readers will be needlessly waking up the task that is trying to finish releasing the write lock. :-/ Not a big deal in this case, but just shows to go that when reviewing locking algorithms, you should -never- restrict yourself to considering only things that seem possible. ;-)
Quoted 20 lines
Index:
linux-2.6/include/linux/rwmutex.h
===================================================================include/linux/rwmutex.h new file +15
-0@@ -0,0 +1,82 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* This file contains the public data structure and API definitions. +*/ +#ifndef _LINUX_RWMUTEX_H +#define _LINUX_RWMUTEX_H + +#include <linux/preempt.h> +#include <linux/wait.h> +#include <linux/percpu_counter.h> +#include <linux/lockdep.h> +#include <linux/mutex.h>
include/linux/rwmutex.h line 25
Quoted 10 lines
include/linux/rwmutex.h new file +10
-0+#include <asm/atomic.h> + +struct rw_mutex { +/* Read mostly global */ +struct percpu_counter readers; + +/* The following variables are only for the slowpath */ +struct task_struct *waiter;/* w -> r waiting */ +struct mutex read_mutex;/* r -> w waiting */ +struct mutex write_mutex;/* w -> w waiting */
Priority-inheritance relationship? Seems like this would be tough to arrange while still avoiding deadlock...
Readers currently do boost the writer via ->read_mutex.
!!!
Writers currently do -not- boost readers. In fact, the identities of the readers are not tracked, so there is no way for the writer to tell what to boost. Admittedly, write-to-read priority boosting is quite the can of worms if you allow more than one reader, but something will be needed for realtime kernels.
A brlock-like implementation can allow boosting in both directions, but has other issues (such as write-side performance even in the case where there are no readers).
Quoted 144 lines
include/linux/rwmutex.h new file +57
-0+atomic_t read_waiters; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +struct lockdep_map dep_map; +#endif +}; + +void __rw_mutex_init (struct rw_mutex *rw_mutex,const char * name, +struct lock_class_key *key); +void rw_mutex_destroy (struct rw_mutex *rw_mutex); + +#define rw_mutex_init (rw_mutex) \ + do { \ + static struct lock_class_key __key; \ + __rw_mutex_init((rw_mutex), #rw_mutex, &__key); \ + } while (0) + +void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex); + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass); +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +int ret =__rw_mutex_read_trylock (rw_mutex); +if (ret) +rwsem_acquire_read (&rw_mutex->dep_map,0 ,1 , _RET_IP_); +return ret; +} + +static inline void rw_mutex_read_lock (struct rw_mutex *rw_mutex) +{ +int ret; + +might_sleep (); +rwsem_acquire_read (&rw_mutex->dep_map,0 ,0 , _RET_IP_); + + ret =__rw_mutex_read_trylock (rw_mutex); +if (!ret) +rw_mutex_read_lock_slow (rw_mutex); +} + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_is_locked (struct rw_mutex *rw_mutex) +{ +return mutex_is_locked (&rw_mutex->write_mutex); +} + +static inline void rw_mutex_write_lock (struct rw_mutex *rw_mutex) +{ +rw_mutex_write_lock_nested (rw_mutex,0 ); +} + +#endif /* _LINUX_RWMUTEX_H */ Index:
linux-2.6/kernel/rwmutex.c
===================================================================kernel/rwmutex.c new file +82
-0@@ -0,0 +1,229 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* Its scalable in that the read count is a percpu counter and the reader fast +* path does not write to a shared cache-line. +* +* Its not FIFO fair, but starvation proof by alternating readers and writers. +*/ +#include <linux/sched.h> +#include <linux/rwmutex.h> +#include <linux/debug_locks.h> +#include <linux/module.h> + +/* +* rw mutex - oxymoron when we take mutex to stand for 'MUTual EXlusion' +* +* However in this context we take mutex to mean a sleeping lock, with the +* property that it must be released by the same context that acquired it. +* +* design goals: +* +* A sleeping reader writer lock with a scalable read side, to avoid bouncing +* cache-lines. +* +* dynamics: +* +* The reader fast path is modification of a percpu_counter and a read of a +* shared cache-line. +* +* The write side is quite heavy; it takes two mutexes, a writer mutex and a +* readers mutex. The writer mutex is for w <-> w interaction, the read mutex +* for r -> w. The read side is forced into the slow path by setting the +* status bit. Then it waits for all current readers to disappear. +* +* The read lock slow path; taken when the status bit is set; takes the read +* mutex. Because the write side also takes this mutex, the new readers are +* blocked. The read unlock slow path tickles the writer every time a read +* lock is released. +* +* Write unlock clears the status bit, and drops the read mutex; allowing new +* readers. It then waits for at least one waiting reader to get a lock (if +* there were any readers waiting) before releasing the write mutex which will +* allow possible other writers to come in an stop new readers, thus avoiding +* starvation by alternating between readers and writers +* +* considerations: +* +* The lock's space footprint is quite large (on x86_64): +* +* 88 bytes [struct rw_mutex] +* 8 bytes per cpu NR_CPUS [void *] +* 32 bytes per cpu (nr_cpu_ids) [smallest slab] +* +* 408 bytes for x86_64 defconfig (NR_CPUS = 32) on a 2-way box. +* +* The write side is quite heavy; this lock is best suited for situations +* where the read side vastly dominates the write side. +*/ + +void __rw_mutex_init (struct rw_mutex *rw_mutex,const char *name, +struct lock_class_key *key) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC +debug_check_no_locks_freed ((void *)rw_mutex,sizeof (*rw_mutex)); +lockdep_init_map (&rw_mutex->dep_map, name, key,0 ); +#endif + +percpu_counter_init (&rw_mutex->readers,0 ); + rw_mutex->waiter =NULL ; +mutex_init (&rw_mutex->read_mutex); +mutex_init (&rw_mutex->write_mutex); +} +EXPORT_SYMBOL (__rw_mutex_init); + +void rw_mutex_destroy (struct rw_mutex *rw_mutex) +{ +percpu_counter_destroy (&rw_mutex->readers); +mutex_destroy (&rw_mutex->read_mutex); +mutex_destroy (&rw_mutex->write_mutex); +}
kernel/rwmutex.c line 92
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+EXPORT_SYMBOL (rw_mutex_destroy); + +#define rw_mutex_writer_wait (rw_mutex, condition) \ +do { \ + struct task_struct *tsk = (rw_mutex)->waiter; \ + BUG_ON(tsk != current); \ + \ + set_task_state(tsk, TASK_UNINTERRUPTIBLE); \ + while (!(condition)) { \ + schedule(); \
!!!
If there are at least as many of these scalable reader-writer locks as there are CPUs, and each such lock has a realtime-priority writer executing, you can have an infinite loop starving all the non-realtime readers, who are then unable to ever read-release the lock, preventing the writers from making progress. Or am I missing something subtle here?
kernel/rwmutex.c line 96
Quoted 4 lines
kernel/rwmutex.c new file +4
-0+set_task_state (tsk,TASK_UNINTERRUPTIBLE ); \ + } \ + tsk->state =TASK_RUNNING ; \ +}while (0 )
Can't something like __wait_event() be used here?
Quoted 34 lines
kernel/rwmutex.c new file +34
-0+void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +/* +* read lock slow path; +* count the number of readers waiting on the read_mutex +*/ +atomic_inc (&rw_mutex->read_waiters); +mutex_lock (&rw_mutex->read_mutex); + +percpu_counter_inc (&rw_mutex->readers); + +/* +* wake up a possible write unlock; waiting for at least a single +* reader to pass before letting a new writer through. +*/ +atomic_dec (&rw_mutex->read_waiters); + tsk = rw_mutex->waiter; +if (tsk) +wake_up_process (tsk); +mutex_unlock (&rw_mutex->read_mutex); +} +EXPORT_SYMBOL (rw_mutex_read_lock_slow); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +percpu_counter_inc (&rw_mutex->readers); +/* +* ensure the ->readers store and the ->waiter load is properly +* sequenced +*/
kernel/rwmutex.c line 141
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+smp_mb (); + tsk = rw_mutex->waiter; +if (unlikely (tsk)) { +percpu_counter_dec (&rw_mutex->readers); +/* +* ensure the ->readers store has taken place before we issue +* the wake_up +* +* XXX: or does this require an smp_wmb() and the waiter to do +* (smp_rmb(), percpu_counter(&rw_mutex->readers) == 0)
I don't think that -anything- is needed here as written. The "sleeping" writers don't really block, they instead simply loop on schedule(). Of course that in itself is a -really- bad idea if case of realtime-priority writers...
So the writers need to really block, which will make the wakeup code much more ticklish. In that case, it seems to me that smp_mb() will be needed here, but much will depend on the exact structure of the block/wakeup scheme chosen.
kernel/rwmutex.c line 147
Quoted 5 lines
kernel/rwmutex.c new file +5
-0+barrier (); +/* +* possibly wake up a writer waiting for this reference to +* disappear +*/
!!!
Suppose we get delayed here (preempted, interrupted, whatever) and the task referenced by tsk has exited and cleaned up before we get a chance to wake it up? We might well be "waking up" some entirely different data structure in that case, not?
Quoted 15 lines
kernel/rwmutex.c new file +15
-0+wake_up_process (tsk); +return 0 ; + } +return 1 ; +} +EXPORT_SYMBOL (__rw_mutex_read_trylock); + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +percpu_counter_dec (&rw_mutex->readers); +/*
kernel/rwmutex.c line 172
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+* ensure the ->readers store and the ->waiter load is properly +* sequenced +*/ +smp_mb (); + tsk = rw_mutex->waiter; +if (unlikely (tsk)) { +/* +* on the slow path; nudge the writer waiting for the last +* reader to go away +*/
!!!
What if we are delayed (interrupted, preempted, whatever) here, so that the task referenced by tsk has exited and been cleaned up before we can execute the following? We could end up "waking up" the freelist, not?
Quoted 40 lines
kernel/rwmutex.c new file +40
-0+wake_up_process (tsk); + } +} +EXPORT_SYMBOL (rw_mutex_read_unlock); + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass) +{ +might_sleep (); +rwsem_acquire (&rw_mutex->dep_map, subclass,0 , _RET_IP_); + +mutex_lock_nested (&rw_mutex->write_mutex, subclass); +BUG_ON (rw_mutex->waiter); + +/* +* block new readers +*/ +mutex_lock_nested (&rw_mutex->read_mutex, subclass); + rw_mutex->waiter = current; +/* +* full barrier to sequence the store of ->waiter +* and the load of ->readers +*/ +smp_mb (); +/* +* and wait for all current readers to go away +*/ +rw_mutex_writer_wait (rw_mutex, + (percpu_counter_sum (&rw_mutex->readers) ==0 )); +} +EXPORT_SYMBOL (rw_mutex_write_lock_nested); + +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex) +{ +int waiters; + +might_sleep (); +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +/* +* let the readers rip
kernel/rwmutex.c line 222
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+*/ + waiters =atomic_read (&rw_mutex->read_waiters); +mutex_unlock (&rw_mutex->read_mutex); +/* +* wait for at least 1 reader to get through +*/ +if (waiters) { +rw_mutex_writer_wait (rw_mutex, + (atomic_read (&rw_mutex->read_waiters) < waiters)); + }
!!!
Readers can indefinitely postpone the write-unlock at this point. Suppose that there is one reader waiting when we fetch ->read_waiters above. Suppose that as soon as the mutex is released, a large number of readers arrive. Because we have not yet NULLed ->waiter, all of these readers will take the slow path, incrementing the ->read_waiters counter, acquiring the ->read_mutex, and so on. The ->read_mutex lock acquisition is likely to be the bottleneck for large systems, which would result in the count remaining 2 or higher indefinitely. The readers would make progress (albeit quite inefficiently), but the writer would never get out of the loop in the rw_mutex_writer_wait() macro.
Consider instead using a generation number that just increments every time a reader successfully acquires the lock and is never decremented. That way, you can unambiguously determine when at least one reader has made progress. An additional counter required, but might be able to multiplex with something else. Or get rid of the current ->read_waiters in favor of the generation number? This latter seems like it should be possible, at least at first glance... Just change the name of the field, get rid of the decrement, and change the comparison to "!=", right?
Quoted 14 lines
kernel/rwmutex.c new file +7
-0+ rw_mutex->waiter =NULL ; +/* +* before we let the writers rip +*/ +mutex_unlock (&rw_mutex->write_mutex); +} +EXPORT_SYMBOL (rw_mutex_write_unlock);Index: linux-2.6/kernel/Makefile
===================================================================
kernel/Makefile obj-y = sched.o fork.o exec_domain.o · line 15
Quoted 9 lines
kernel/Makefile +2
-1@@ -8,7 +8,8 @@ obj-y = sched.o fork.o exec_domain.o signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o + hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o \ + rwmutex.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 9 lines · Paul E. McKenney
Quoted 8 lines · Peter Zijlstra
Changes include:
- wmb+rmb != mb
- ->state folded into ->waiter---
Subject: scalablerw_mutex
Quoted 8 lines
Quoted 6 lines
Scalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
Hmmm... brlock reincarnates, but as sleeplock. ;-)
/me googles... ooh, yeah, quite similar.
Quoted 2 lines
I believe that there are a few severe problems in this code, search for "!!!" to quickly find the specific areas that concern me.
Thanks for the feedback, replies at the !!! sites.
Quoted 166 lines
Thanx, Paul
Quoted 6 lines
Signed-off-by Peter Zijlstra <[email protected]>--- include/linux/rwmutex.h | 82 ++++++++++++++++ kernel/Makefile | 3 kernel/rwmutex.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+), 1 deletion(-)List of races that must be resolved:
1. Read acquire vs. write acquire.
rw_mutex_read_lock()invokes__rw_mutex_read_trylock(), if
this fails, invokesrw_mutex_read_lock_slow().
__rw_mutex_read_trylock()increments the per-CPU counter,
doessmp_mb(), picks up ->waiter:
if non-NULL decrements the per-CPU counter, does abarrier(), doeswake_up_process()on the task fetched from ->waiter. Return failure.Otherwise, return success.
rw_mutex_read_lock_slow()increments ->read_waiters,
acquires ->read_mutex, increments the ->readers counter, and decrements the ->read_waiters counter. It then fetches ->waiter, and, if non-NULL, wakes up the tasks.
Either way, releases ->read_mutex.
rw_mutex_write_lock_nested(): acquires ->write_mutex, which
prevents any writer-writer races. Acquires ->read_mutex, which does -not- prevent readers from continuing to acquire. Sets ->waiter to current, which -does- (eventually) stop readers.smp_mb(), then invokesrw_mutex_writer_wait()for the sum of the per-CPU counters to go to zero.!In principle, this could be indefinitely postponed, but in practice would require an infinite number of reading tasks, so probably OK to ignore. ;-) This can occur because the readers unconditionally increment their per-CPU counters, and decrement it only later.
The smp_mb()s currently in the reader and the writer code forms a Dekker-algorithm-like barrier, preventing both the reader and writer from entering their critical section, as required.
2. Read acquire vs. write release (need to avoid reader sleeping
forever, even in the case where no one ever uses the lock again). rw_mutex_read_lock() invokes __rw_mutex_read_trylock(), if this fails, invokes rw_mutex_read_lock_slow(). __rw_mutex_read_trylock() increments the per-CPU counter, does smp_mb(), picks up ->waiter: if non-NULL decrements the per-CPU counter, does a barrier(), does wake_up_process() on the task fetched from ->waiter. Return failure. Otherwise, return success. rw_mutex_read_lock_slow() increments ->read_waiters, acquires ->read_mutex, increments the ->readers counter, and decrements the ->read_waiters counter. It then fetches ->waiter, and, if non-NULL, wakes up the tasks. Either way, releases ->read_mutex. rw_mutex_write_unlock(): pick up ->read_waiters, release ->read_mutex, if copy of ->read_waiters was non-NULL do slow path (but refetches ->read_waiters??? why??? [Ah -- refetched each time through the loop in the rw_mutex_writer_wait() macro), NULL out ->waiter, then release ->write_mutex. Slow path: Pick up ->waiter, make sure it is us, set state to uninterruptible, loop while ->read_waiters less than the value fetched earlier from ->read_waiters, scheduling each time through, set state back to running. (!!!This is subject to indefinite postponement by a flurry of readers, see the commentary for rw_mutex_write_unlock() interspersed below.) However, the fact that ->read_mutex is unconditionally released by the writer prevents the readers from being starved.3. Read release vs. write acquire (similar to #2).
rw_mutex_read_unlock(): decrement the per-CPU counter,smb_mb(),
pick up ->waiter, if non-NULL, wake it up.
rw_mutex_write_lock_nested(): acquires ->write_mutex, which
prevents any writer-writer races. Acquires ->read_mutex, which does -not- prevent readers from continuing to acquire. Sets ->waiter to current, which -does- (eventually) stop readers.smp_mb(), then invokesrw_mutex_writer_wait()for the sum of the per-CPU counters to go to zero.As written, the writer never really blocks, so omitting the wakeup is OK. (Failing to really block is -not- OK given realtime processes, but more on that later.)
4. Read release vs. write release -- presumably this one cannot
happen, but wouldn't want to fall prey to complacency. ;-)Strangely enough, this -can- happen!!! When the readers
indefinitely postpone writer release, the readers will also be read-releasing... So the readers will be needlessly waking up the task that is trying to finish releasing the write lock. :-/ Not a big deal in this case, but just shows to go that when reviewing locking algorithms, you should -never- restrict yourself to considering only things that seem possible. ;-)Quoted 20 lines
Index:
linux-2.6/include/linux/rwmutex.h
===================================================================include/linux/rwmutex.h new file +15
-0@@ -0,0 +1,82 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* This file contains the public data structure and API definitions. +*/ +#ifndef _LINUX_RWMUTEX_H +#define _LINUX_RWMUTEX_H + +#include <linux/preempt.h> +#include <linux/wait.h> +#include <linux/percpu_counter.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> include/linux/rwmutex.h line 25
Quoted 10 lines
include/linux/rwmutex.h new file +10
-0+#include <asm/atomic.h> + +struct rw_mutex { +/* Read mostly global */ +struct percpu_counter readers; + +/* The following variables are only for the slowpath */ +struct task_struct *waiter;/* w -> r waiting */ +struct mutex read_mutex;/* r -> w waiting */ +struct mutex write_mutex;/* w -> w waiting */ Priority-inheritance relationship? Seems like this would be tough to arrange while still avoiding deadlock...
Readers currently do boost the writer via ->read_mutex.
!!!
Writers currently do -not- boost readers. In fact, the identities of the readers are not tracked, so there is no way for the writer to tell what to boost. Admittedly, write-to-read priority boosting is quite the can of worms if you allow more than one reader, but something will be needed for realtime kernels.
Quoted 3 lines
A brlock-like implementation can allow boosting in both directions, but has other issues (such as write-side performance even in the case where there are no readers).
You could short circuit the no readers case for a full brlock like affair. But yeah, the write side will be horribly heavy when you have to take nr_cpu_ids() locks.
Quoted 157 lines
Quoted 144 lines
include/linux/rwmutex.h new file +57
-0+atomic_t read_waiters; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +struct lockdep_map dep_map; +#endif +}; + +void __rw_mutex_init (struct rw_mutex *rw_mutex,const char * name, +struct lock_class_key *key); +void rw_mutex_destroy (struct rw_mutex *rw_mutex); + +#define rw_mutex_init (rw_mutex) \ + do { \ + static struct lock_class_key __key; \ + __rw_mutex_init((rw_mutex), #rw_mutex, &__key); \ + } while (0) + +void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex); + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass); +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +int ret =__rw_mutex_read_trylock (rw_mutex); +if (ret) +rwsem_acquire_read (&rw_mutex->dep_map,0 ,1 , _RET_IP_); +return ret; +} + +static inline void rw_mutex_read_lock (struct rw_mutex *rw_mutex) +{ +int ret; + +might_sleep (); +rwsem_acquire_read (&rw_mutex->dep_map,0 ,0 , _RET_IP_); + + ret =__rw_mutex_read_trylock (rw_mutex); +if (!ret) +rw_mutex_read_lock_slow (rw_mutex); +} + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_is_locked (struct rw_mutex *rw_mutex) +{ +return mutex_is_locked (&rw_mutex->write_mutex); +} + +static inline void rw_mutex_write_lock (struct rw_mutex *rw_mutex) +{ +rw_mutex_write_lock_nested (rw_mutex,0 ); +} + +#endif /* _LINUX_RWMUTEX_H */ Index:
linux-2.6/kernel/rwmutex.c
===================================================================kernel/rwmutex.c new file +82
-0@@ -0,0 +1,229 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* Its scalable in that the read count is a percpu counter and the reader fast +* path does not write to a shared cache-line. +* +* Its not FIFO fair, but starvation proof by alternating readers and writers. +*/ +#include <linux/sched.h> +#include <linux/rwmutex.h> +#include <linux/debug_locks.h> +#include <linux/module.h> + +/* +* rw mutex - oxymoron when we take mutex to stand for 'MUTual EXlusion' +* +* However in this context we take mutex to mean a sleeping lock, with the +* property that it must be released by the same context that acquired it. +* +* design goals: +* +* A sleeping reader writer lock with a scalable read side, to avoid bouncing +* cache-lines. +* +* dynamics: +* +* The reader fast path is modification of a percpu_counter and a read of a +* shared cache-line. +* +* The write side is quite heavy; it takes two mutexes, a writer mutex and a +* readers mutex. The writer mutex is for w <-> w interaction, the read mutex +* for r -> w. The read side is forced into the slow path by setting the +* status bit. Then it waits for all current readers to disappear. +* +* The read lock slow path; taken when the status bit is set; takes the read +* mutex. Because the write side also takes this mutex, the new readers are +* blocked. The read unlock slow path tickles the writer every time a read +* lock is released. +* +* Write unlock clears the status bit, and drops the read mutex; allowing new +* readers. It then waits for at least one waiting reader to get a lock (if +* there were any readers waiting) before releasing the write mutex which will +* allow possible other writers to come in an stop new readers, thus avoiding +* starvation by alternating between readers and writers +* +* considerations: +* +* The lock's space footprint is quite large (on x86_64): +* +* 88 bytes [struct rw_mutex] +* 8 bytes per cpu NR_CPUS [void *] +* 32 bytes per cpu (nr_cpu_ids) [smallest slab] +* +* 408 bytes for x86_64 defconfig (NR_CPUS = 32) on a 2-way box. +* +* The write side is quite heavy; this lock is best suited for situations +* where the read side vastly dominates the write side. +*/ + +void __rw_mutex_init (struct rw_mutex *rw_mutex,const char *name, +struct lock_class_key *key) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC +debug_check_no_locks_freed ((void *)rw_mutex,sizeof (*rw_mutex)); +lockdep_init_map (&rw_mutex->dep_map, name, key,0 ); +#endif + +percpu_counter_init (&rw_mutex->readers,0 ); + rw_mutex->waiter =NULL ; +mutex_init (&rw_mutex->read_mutex); +mutex_init (&rw_mutex->write_mutex); +} +EXPORT_SYMBOL (__rw_mutex_init); + +void rw_mutex_destroy (struct rw_mutex *rw_mutex) +{ +percpu_counter_destroy (&rw_mutex->readers); +mutex_destroy (&rw_mutex->read_mutex); +mutex_destroy (&rw_mutex->write_mutex); +}kernel/rwmutex.c line 92
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+EXPORT_SYMBOL (rw_mutex_destroy); + +#define rw_mutex_writer_wait (rw_mutex, condition) \ +do { \ + struct task_struct *tsk = (rw_mutex)->waiter; \ + BUG_ON(tsk != current); \ + \ + set_task_state(tsk, TASK_UNINTERRUPTIBLE); \ + while (!(condition)) { \ + schedule(); \!!!
Quoted 5 lines
If there are at least as many of these scalable reader-writer locks as there are CPUs, and each such lock has a realtime-priority writer executing, you can have an infinite loop starving all the non-realtime readers, who are then unable to ever read-release the lock, preventing the writers from making progress. Or am I missing something subtle here?
It will actually block the task. schedule() will make it sleep when state is uninterruptible.
Quoted 6 lines
kernel/rwmutex.c line 96
Quoted 4 lines
kernel/rwmutex.c new file +4
-0+set_task_state (tsk,TASK_UNINTERRUPTIBLE ); \ + } \ + tsk->state =TASK_RUNNING ; \ +}while (0 )Can't something like
__wait_event()be used here?
Blame Oleg for this :-)
He suggested I not use waitqueues since I only ever have a single waiter.
Quoted 50 lines
Quoted 34 lines
kernel/rwmutex.c new file +34
-0+void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +/* +* read lock slow path; +* count the number of readers waiting on the read_mutex +*/ +atomic_inc (&rw_mutex->read_waiters); +mutex_lock (&rw_mutex->read_mutex); + +percpu_counter_inc (&rw_mutex->readers); + +/* +* wake up a possible write unlock; waiting for at least a single +* reader to pass before letting a new writer through. +*/ +atomic_dec (&rw_mutex->read_waiters); + tsk = rw_mutex->waiter; +if (tsk) +wake_up_process (tsk); +mutex_unlock (&rw_mutex->read_mutex); +} +EXPORT_SYMBOL (rw_mutex_read_lock_slow); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +percpu_counter_inc (&rw_mutex->readers); +/* +* ensure the ->readers store and the ->waiter load is properly +* sequenced +*/ kernel/rwmutex.c line 141
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+smp_mb (); + tsk = rw_mutex->waiter; +if (unlikely (tsk)) { +percpu_counter_dec (&rw_mutex->readers); +/* +* ensure the ->readers store has taken place before we issue +* the wake_up +* +* XXX: or does this require an smp_wmb() and the waiter to do +* (smp_rmb(), percpu_counter(&rw_mutex->readers) == 0) I don't think that -anything- is needed here as written. The "sleeping" writers don't really block, they instead simply loop on
schedule(). Of course that in itself is a -really- bad idea if case of realtime-priority writers...
Quoted 4 lines
So the writers need to really block, which will make the wakeup code much more ticklish. In that case, it seems to me that
smp_mb()will be needed here, but much will depend on the exact structure of the block/wakeup scheme chosen.
It does really sleep. And Nick thinks the suggested wmb + rmb will suffice if I read his latest mail correctly.
Quoted 8 lines
kernel/rwmutex.c line 147
Quoted 5 lines
kernel/rwmutex.c new file +5
-0+barrier (); +/* +* possibly wake up a writer waiting for this reference to +* disappear +*/ !!!
Quoted 4 lines
Suppose we get delayed here (preempted, interrupted, whatever) and the task referenced by tsk has exited and cleaned up before we get a chance to wake it up? We might well be "waking up" some entirely different data structure in that case, not?
Yes, this is a real problem, I'll borrow a spinlock from one of the mutexes.
Quoted 28 lines
Quoted 15 lines
kernel/rwmutex.c new file +15
-0+wake_up_process (tsk); +return 0 ; + } +return 1 ; +} +EXPORT_SYMBOL (__rw_mutex_read_trylock); + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +percpu_counter_dec (&rw_mutex->readers); +/* kernel/rwmutex.c line 172
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+* ensure the ->readers store and the ->waiter load is properly +* sequenced +*/ +smp_mb (); + tsk = rw_mutex->waiter; +if (unlikely (tsk)) { +/* +* on the slow path; nudge the writer waiting for the last +* reader to go away +*/ !!!
Quoted 3 lines
What if we are delayed (interrupted, preempted, whatever) here, so that the task referenced by tsk has exited and been cleaned up before we can execute the following? We could end up "waking up" the freelist, not?
idem.
Quoted 53 lines
Quoted 40 lines
kernel/rwmutex.c new file +40
-0+wake_up_process (tsk); + } +} +EXPORT_SYMBOL (rw_mutex_read_unlock); + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass) +{ +might_sleep (); +rwsem_acquire (&rw_mutex->dep_map, subclass,0 , _RET_IP_); + +mutex_lock_nested (&rw_mutex->write_mutex, subclass); +BUG_ON (rw_mutex->waiter); + +/* +* block new readers +*/ +mutex_lock_nested (&rw_mutex->read_mutex, subclass); + rw_mutex->waiter = current; +/* +* full barrier to sequence the store of ->waiter +* and the load of ->readers +*/ +smp_mb (); +/* +* and wait for all current readers to go away +*/ +rw_mutex_writer_wait (rw_mutex, + (percpu_counter_sum (&rw_mutex->readers) ==0 )); +} +EXPORT_SYMBOL (rw_mutex_write_lock_nested); + +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex) +{ +int waiters; + +might_sleep (); +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +/* +* let the readers rip kernel/rwmutex.c line 222
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+*/ + waiters =atomic_read (&rw_mutex->read_waiters); +mutex_unlock (&rw_mutex->read_mutex); +/* +* wait for at least 1 reader to get through +*/ +if (waiters) { +rw_mutex_writer_wait (rw_mutex, + (atomic_read (&rw_mutex->read_waiters) < waiters)); + }!!!
Quoted 11 lines
Readers can indefinitely postpone the write-unlock at this point. Suppose that there is one reader waiting when we fetch ->read_waiters above. Suppose that as soon as the mutex is released, a large number of readers arrive. Because we have not yet NULLed ->waiter, all of these readers will take the slow path, incrementing the ->read_waiters counter, acquiring the ->read_mutex, and so on. The ->read_mutex lock acquisition is likely to be the bottleneck for large systems, which would result in the count remaining 2 or higher indefinitely. The readers would make progress (albeit quite inefficiently), but the writer would never get out of the loop in the
rw_mutex_writer_wait()macro.
Ah, yeah. Ouch!
I missed this detail when I got rid of ->state. I used to clear the reader slow path before unlocking the ->read_mutex.
Quoted 8 lines
Consider instead using a generation number that just increments every time a reader successfully acquires the lock and is never decremented. That way, you can unambiguously determine when at least one reader has made progress. An additional counter required, but might be able to multiplex with something else. Or get rid of the current ->read_waiters in favor of the generation number? This latter seems like it should be possible, at least at first glance... Just change the name of the field, get rid of the decrement, and change the comparison to "!=", right?
Yes, this seems like a very good suggestion, I'll give it a shot. Thanks!
Quoted 19 lines
Quoted 19 lines
kernel/rwmutex.c new file +7
-0+ rw_mutex->waiter =NULL ; +/* +* before we let the writers rip +*/ +mutex_unlock (&rw_mutex->write_mutex); +} +EXPORT_SYMBOL (rw_mutex_write_unlock);Index: linux-2.6/kernel/Makefile
===================================================================kernel/Makefile +2
-1@@ -8,7 +8,8 @@ obj-y = sched.o fork.o exec_domain.o signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o + hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o \ + rwmutex.o
Quoted 4 lines
kernel/Makefile obj-y = sched.o fork.o exec_domain.o · line 15
Quoted 4 lines
kernel/Makefile +0
-0obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 192 lines · Peter Zijlstra
Quoted 9 lines · Paul E. McKenney
Quoted 8 lines · Peter Zijlstra
Changes include:
- wmb+rmb != mb
- ->state folded into ->waiter---
Subject: scalablerw_mutexQuoted 8 lines
Quoted 6 lines
Scalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
Hmmm... brlock reincarnates, but as sleeplock. ;-)
/me googles... ooh, yeah, quite similar.
Quoted 2 lines
I believe that there are a few severe problems in this code, search for "!!!" to quickly find the specific areas that concern me.
Thanks for the feedback, replies at the !!! sites.
Quoted 166 lines
Thanx, Paul
Quoted 6 lines
Signed-off-by Peter Zijlstra <[email protected]>--- include/linux/rwmutex.h | 82 ++++++++++++++++ kernel/Makefile | 3 kernel/rwmutex.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+), 1 deletion(-)List of races that must be resolved:
1. Read acquire vs. write acquire.
rw_mutex_read_lock()invokes__rw_mutex_read_trylock(), if
this fails, invokesrw_mutex_read_lock_slow().
__rw_mutex_read_trylock()increments the per-CPU counter,
doessmp_mb(), picks up ->waiter:
if non-NULL decrements the per-CPU counter, does abarrier(), doeswake_up_process()on the task fetched from ->waiter. Return failure.Otherwise, return success.
rw_mutex_read_lock_slow()increments ->read_waiters,
acquires ->read_mutex, increments the ->readers counter, and decrements the ->read_waiters counter. It then fetches ->waiter, and, if non-NULL, wakes up the tasks.
Either way, releases ->read_mutex.
rw_mutex_write_lock_nested(): acquires ->write_mutex, which
prevents any writer-writer races. Acquires ->read_mutex, which does -not- prevent readers from continuing to acquire. Sets ->waiter to current, which -does- (eventually) stop readers.smp_mb(), then invokesrw_mutex_writer_wait()for the sum of the per-CPU counters to go to zero.!In principle, this could be indefinitely postponed, but in practice would require an infinite number of reading tasks, so probably OK to ignore. ;-) This can occur because the readers unconditionally increment their per-CPU counters, and decrement it only later.
The smp_mb()s currently in the reader and the writer code forms a Dekker-algorithm-like barrier, preventing both the reader and writer from entering their critical section, as required.
2. Read acquire vs. write release (need to avoid reader sleeping
forever, even in the case where no one ever uses the lock again). rw_mutex_read_lock() invokes __rw_mutex_read_trylock(), if this fails, invokes rw_mutex_read_lock_slow(). __rw_mutex_read_trylock() increments the per-CPU counter, does smp_mb(), picks up ->waiter: if non-NULL decrements the per-CPU counter, does a barrier(), does wake_up_process() on the task fetched from ->waiter. Return failure. Otherwise, return success. rw_mutex_read_lock_slow() increments ->read_waiters, acquires ->read_mutex, increments the ->readers counter, and decrements the ->read_waiters counter. It then fetches ->waiter, and, if non-NULL, wakes up the tasks. Either way, releases ->read_mutex. rw_mutex_write_unlock(): pick up ->read_waiters, release ->read_mutex, if copy of ->read_waiters was non-NULL do slow path (but refetches ->read_waiters??? why??? [Ah -- refetched each time through the loop in the rw_mutex_writer_wait() macro), NULL out ->waiter, then release ->write_mutex. Slow path: Pick up ->waiter, make sure it is us, set state to uninterruptible, loop while ->read_waiters less than the value fetched earlier from ->read_waiters, scheduling each time through, set state back to running. (!!!This is subject to indefinite postponement by a flurry of readers, see the commentary for rw_mutex_write_unlock() interspersed below.) However, the fact that ->read_mutex is unconditionally released by the writer prevents the readers from being starved.3. Read release vs. write acquire (similar to #2).
rw_mutex_read_unlock(): decrement the per-CPU counter,smb_mb(),
pick up ->waiter, if non-NULL, wake it up.
rw_mutex_write_lock_nested(): acquires ->write_mutex, which
prevents any writer-writer races. Acquires ->read_mutex, which does -not- prevent readers from continuing to acquire. Sets ->waiter to current, which -does- (eventually) stop readers.smp_mb(), then invokesrw_mutex_writer_wait()for the sum of the per-CPU counters to go to zero.As written, the writer never really blocks, so omitting the wakeup is OK. (Failing to really block is -not- OK given realtime processes, but more on that later.)
4. Read release vs. write release -- presumably this one cannot
happen, but wouldn't want to fall prey to complacency. ;-)Strangely enough, this -can- happen!!! When the readers
indefinitely postpone writer release, the readers will also be read-releasing... So the readers will be needlessly waking up the task that is trying to finish releasing the write lock. :-/ Not a big deal in this case, but just shows to go that when reviewing locking algorithms, you should -never- restrict yourself to considering only things that seem possible. ;-)Quoted 20 lines
Index:
linux-2.6/include/linux/rwmutex.h
===================================================================include/linux/rwmutex.h new file +15
-0@@ -0,0 +1,82 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* This file contains the public data structure and API definitions. +*/ +#ifndef _LINUX_RWMUTEX_H +#define _LINUX_RWMUTEX_H + +#include <linux/preempt.h> +#include <linux/wait.h> +#include <linux/percpu_counter.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> include/linux/rwmutex.h line 25
Quoted 10 lines
include/linux/rwmutex.h new file +10
-0+#include <asm/atomic.h> + +struct rw_mutex { +/* Read mostly global */ +struct percpu_counter readers; + +/* The following variables are only for the slowpath */ +struct task_struct *waiter;/* w -> r waiting */ +struct mutex read_mutex;/* r -> w waiting */ +struct mutex write_mutex;/* w -> w waiting */ Priority-inheritance relationship? Seems like this would be tough to arrange while still avoiding deadlock...
Readers currently do boost the writer via ->read_mutex.
!!!
Writers currently do -not- boost readers. In fact, the identities of the readers are not tracked, so there is no way for the writer to tell what to boost. Admittedly, write-to-read priority boosting is quite the can of worms if you allow more than one reader, but something will be needed for realtime kernels.
Quoted 7 lines
Quoted 3 lines
A brlock-like implementation can allow boosting in both directions, but has other issues (such as write-side performance even in the case where there are no readers).
You could short circuit the no readers case for a full brlock like affair. But yeah, the write side will be horribly heavy when you have to take
nr_cpu_ids()locks.
Yeah, I don't immediately see a good solution. :-/
Quoted 157 lines
Quoted 157 lines
Quoted 144 lines
include/linux/rwmutex.h new file +57
-0+atomic_t read_waiters; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +struct lockdep_map dep_map; +#endif +}; + +void __rw_mutex_init (struct rw_mutex *rw_mutex,const char * name, +struct lock_class_key *key); +void rw_mutex_destroy (struct rw_mutex *rw_mutex); + +#define rw_mutex_init (rw_mutex) \ + do { \ + static struct lock_class_key __key; \ + __rw_mutex_init((rw_mutex), #rw_mutex, &__key); \ + } while (0) + +void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex); + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass); +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +int ret =__rw_mutex_read_trylock (rw_mutex); +if (ret) +rwsem_acquire_read (&rw_mutex->dep_map,0 ,1 , _RET_IP_); +return ret; +} + +static inline void rw_mutex_read_lock (struct rw_mutex *rw_mutex) +{ +int ret; + +might_sleep (); +rwsem_acquire_read (&rw_mutex->dep_map,0 ,0 , _RET_IP_); + + ret =__rw_mutex_read_trylock (rw_mutex); +if (!ret) +rw_mutex_read_lock_slow (rw_mutex); +} + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_is_locked (struct rw_mutex *rw_mutex) +{ +return mutex_is_locked (&rw_mutex->write_mutex); +} + +static inline void rw_mutex_write_lock (struct rw_mutex *rw_mutex) +{ +rw_mutex_write_lock_nested (rw_mutex,0 ); +} + +#endif /* _LINUX_RWMUTEX_H */ Index:
linux-2.6/kernel/rwmutex.c
===================================================================kernel/rwmutex.c new file +82
-0@@ -0,0 +1,229 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* Its scalable in that the read count is a percpu counter and the reader fast +* path does not write to a shared cache-line. +* +* Its not FIFO fair, but starvation proof by alternating readers and writers. +*/ +#include <linux/sched.h> +#include <linux/rwmutex.h> +#include <linux/debug_locks.h> +#include <linux/module.h> + +/* +* rw mutex - oxymoron when we take mutex to stand for 'MUTual EXlusion' +* +* However in this context we take mutex to mean a sleeping lock, with the +* property that it must be released by the same context that acquired it. +* +* design goals: +* +* A sleeping reader writer lock with a scalable read side, to avoid bouncing +* cache-lines. +* +* dynamics: +* +* The reader fast path is modification of a percpu_counter and a read of a +* shared cache-line. +* +* The write side is quite heavy; it takes two mutexes, a writer mutex and a +* readers mutex. The writer mutex is for w <-> w interaction, the read mutex +* for r -> w. The read side is forced into the slow path by setting the +* status bit. Then it waits for all current readers to disappear. +* +* The read lock slow path; taken when the status bit is set; takes the read +* mutex. Because the write side also takes this mutex, the new readers are +* blocked. The read unlock slow path tickles the writer every time a read +* lock is released. +* +* Write unlock clears the status bit, and drops the read mutex; allowing new +* readers. It then waits for at least one waiting reader to get a lock (if +* there were any readers waiting) before releasing the write mutex which will +* allow possible other writers to come in an stop new readers, thus avoiding +* starvation by alternating between readers and writers +* +* considerations: +* +* The lock's space footprint is quite large (on x86_64): +* +* 88 bytes [struct rw_mutex] +* 8 bytes per cpu NR_CPUS [void *] +* 32 bytes per cpu (nr_cpu_ids) [smallest slab] +* +* 408 bytes for x86_64 defconfig (NR_CPUS = 32) on a 2-way box. +* +* The write side is quite heavy; this lock is best suited for situations +* where the read side vastly dominates the write side. +*/ + +void __rw_mutex_init (struct rw_mutex *rw_mutex,const char *name, +struct lock_class_key *key) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC +debug_check_no_locks_freed ((void *)rw_mutex,sizeof (*rw_mutex)); +lockdep_init_map (&rw_mutex->dep_map, name, key,0 ); +#endif + +percpu_counter_init (&rw_mutex->readers,0 ); + rw_mutex->waiter =NULL ; +mutex_init (&rw_mutex->read_mutex); +mutex_init (&rw_mutex->write_mutex); +} +EXPORT_SYMBOL (__rw_mutex_init); + +void rw_mutex_destroy (struct rw_mutex *rw_mutex) +{ +percpu_counter_destroy (&rw_mutex->readers); +mutex_destroy (&rw_mutex->read_mutex); +mutex_destroy (&rw_mutex->write_mutex); +}kernel/rwmutex.c line 92
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+EXPORT_SYMBOL (rw_mutex_destroy); + +#define rw_mutex_writer_wait (rw_mutex, condition) \ +do { \ + struct task_struct *tsk = (rw_mutex)->waiter; \ + BUG_ON(tsk != current); \ + \ + set_task_state(tsk, TASK_UNINTERRUPTIBLE); \ + while (!(condition)) { \ + schedule(); \!!!
Quoted 8 lines
Quoted 5 lines
If there are at least as many of these scalable reader-writer locks as there are CPUs, and each such lock has a realtime-priority writer executing, you can have an infinite loop starving all the non-realtime readers, who are then unable to ever read-release the lock, preventing the writers from making progress. Or am I missing something subtle here?
It will actually block the task.
schedule()will make it sleep when state is uninterruptible.
Color me confused... :-(
Sorry for the noise!!!
But there were a couple of places that I thought were correct only because I also thought that the schedule() wasn't really blocking...
Quoted 10 lines
Quoted 6 lines
kernel/rwmutex.c line 96
Quoted 4 lines
kernel/rwmutex.c new file +4
-0+set_task_state (tsk,TASK_UNINTERRUPTIBLE ); \ + } \ + tsk->state =TASK_RUNNING ; \ +}while (0 )Can't something like
__wait_event()be used here?Blame Oleg for this :-)
He suggested I not use waitqueues since I only ever have a single waiter.
I will defer to Oleg on this, although I could have sworn that the waitqueues were designed mostly for the single-waiter case.
Quoted 50 lines
Quoted 50 lines
Quoted 34 lines
kernel/rwmutex.c new file +34
-0+void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +/* +* read lock slow path; +* count the number of readers waiting on the read_mutex +*/ +atomic_inc (&rw_mutex->read_waiters); +mutex_lock (&rw_mutex->read_mutex); + +percpu_counter_inc (&rw_mutex->readers); + +/* +* wake up a possible write unlock; waiting for at least a single +* reader to pass before letting a new writer through. +*/ +atomic_dec (&rw_mutex->read_waiters); + tsk = rw_mutex->waiter; +if (tsk) +wake_up_process (tsk); +mutex_unlock (&rw_mutex->read_mutex); +} +EXPORT_SYMBOL (rw_mutex_read_lock_slow); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +percpu_counter_inc (&rw_mutex->readers); +/* +* ensure the ->readers store and the ->waiter load is properly +* sequenced +*/ kernel/rwmutex.c line 141
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+smp_mb (); + tsk = rw_mutex->waiter; +if (unlikely (tsk)) { +percpu_counter_dec (&rw_mutex->readers); +/* +* ensure the ->readers store has taken place before we issue +* the wake_up +* +* XXX: or does this require an smp_wmb() and the waiter to do +* (smp_rmb(), percpu_counter(&rw_mutex->readers) == 0) I don't think that -anything- is needed here as written. The "sleeping" writers don't really block, they instead simply loop on
schedule(). Of course that in itself is a -really- bad idea if case of realtime-priority writers...
Quoted 7 lines
Quoted 4 lines
So the writers need to really block, which will make the wakeup code much more ticklish. In that case, it seems to me that
smp_mb()will be needed here, but much will depend on the exact structure of the block/wakeup scheme chosen.It does really sleep. And Nick thinks the suggested wmb + rmb will suffice if I read his latest mail correctly.
My concern would be the possibility that someone else also woke the sleeper up just as we were preparing to do so. This might happen in the case where multiple readers executed this code simultaneously. Seems to me that this might be vulnerable to the following sequence of events:
1. Reader-wannabe #1 wakes up the writer.
2. The writer wakes up and sees that the sum of the per-cpu
counters is still non-zero, due to the presence of reader-wannabe #2.
3. Reader-wannabe #2 decrements its counter. Note that
reader-wannabe #2 could do all the memory barriers it wanted -- this vulnerability would still exist.
4. Reader-wannabe #2 wakes up the writer, which has no effect,
since the writer is already awake (but is perhaps preempted, interrupted, or something).
5. The writer goes to sleep, never to awaken again (unless some
other reader comes along, which might or might not happen).
Or am I missing something here?
Quoted 8 lines
Quoted 8 lines
kernel/rwmutex.c line 147
Quoted 5 lines
kernel/rwmutex.c new file +5
-0+barrier (); +/* +* possibly wake up a writer waiting for this reference to +* disappear +*/ !!!
Quoted 7 lines
Quoted 4 lines
Suppose we get delayed here (preempted, interrupted, whatever) and the task referenced by tsk has exited and cleaned up before we get a chance to wake it up? We might well be "waking up" some entirely different data structure in that case, not?
Yes, this is a real problem, I'll borrow a spinlock from one of the mutexes.
Not sure exactly what you are intending to do, so will await your next patch. ;-)
Quoted 28 lines
Quoted 28 lines
Quoted 15 lines
kernel/rwmutex.c new file +15
-0+wake_up_process (tsk); +return 0 ; + } +return 1 ; +} +EXPORT_SYMBOL (__rw_mutex_read_trylock); + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex) +{ +struct task_struct *tsk; + +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +percpu_counter_dec (&rw_mutex->readers); +/* kernel/rwmutex.c line 172
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+* ensure the ->readers store and the ->waiter load is properly +* sequenced +*/ +smp_mb (); + tsk = rw_mutex->waiter; +if (unlikely (tsk)) { +/* +* on the slow path; nudge the writer waiting for the last +* reader to go away +*/ !!!
Quoted 5 lines
Quoted 3 lines
What if we are delayed (interrupted, preempted, whatever) here, so that the task referenced by tsk has exited and been cleaned up before we can execute the following? We could end up "waking up" the freelist, not?
idem.
Likewise.
Quoted 79 lines
Quoted 53 lines
Quoted 40 lines
kernel/rwmutex.c new file +40
-0+wake_up_process (tsk); + } +} +EXPORT_SYMBOL (rw_mutex_read_unlock); + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass) +{ +might_sleep (); +rwsem_acquire (&rw_mutex->dep_map, subclass,0 , _RET_IP_); + +mutex_lock_nested (&rw_mutex->write_mutex, subclass); +BUG_ON (rw_mutex->waiter); + +/* +* block new readers +*/ +mutex_lock_nested (&rw_mutex->read_mutex, subclass); + rw_mutex->waiter = current; +/* +* full barrier to sequence the store of ->waiter +* and the load of ->readers +*/ +smp_mb (); +/* +* and wait for all current readers to go away +*/ +rw_mutex_writer_wait (rw_mutex, + (percpu_counter_sum (&rw_mutex->readers) ==0 )); +} +EXPORT_SYMBOL (rw_mutex_write_lock_nested); + +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex) +{ +int waiters; + +might_sleep (); +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +/* +* let the readers rip kernel/rwmutex.c line 222
Quoted 10 lines
kernel/rwmutex.c new file +10
-0+*/ + waiters =atomic_read (&rw_mutex->read_waiters); +mutex_unlock (&rw_mutex->read_mutex); +/* +* wait for at least 1 reader to get through +*/ +if (waiters) { +rw_mutex_writer_wait (rw_mutex, + (atomic_read (&rw_mutex->read_waiters) < waiters)); + }!!!
Quoted 11 lines
Readers can indefinitely postpone the write-unlock at this point. Suppose that there is one reader waiting when we fetch ->read_waiters above. Suppose that as soon as the mutex is released, a large number of readers arrive. Because we have not yet NULLed ->waiter, all of these readers will take the slow path, incrementing the ->read_waiters counter, acquiring the ->read_mutex, and so on. The ->read_mutex lock acquisition is likely to be the bottleneck for large systems, which would result in the count remaining 2 or higher indefinitely. The readers would make progress (albeit quite inefficiently), but the writer would never get out of the loop in the
rw_mutex_writer_wait()macro.Ah, yeah. Ouch!
I missed this detail when I got rid of ->state. I used to clear the reader slow path before unlocking the ->read_mutex.
Quoted 8 lines
Consider instead using a generation number that just increments every time a reader successfully acquires the lock and is never decremented. That way, you can unambiguously determine when at least one reader has made progress. An additional counter required, but might be able to multiplex with something else. Or get rid of the current ->read_waiters in favor of the generation number? This latter seems like it should be possible, at least at first glance... Just change the name of the field, get rid of the decrement, and change the comparison to "!=", right?
Quoted 2 lines
Yes, this seems like a very good suggestion, I'll give it a shot. Thanks!
NP! (Glad I got -something- right on this one!)
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
kernel/rwmutex.c new file +7
-0+ rw_mutex->waiter =NULL ; +/* +* before we let the writers rip +*/ +mutex_unlock (&rw_mutex->write_mutex); +} +EXPORT_SYMBOL (rw_mutex_write_unlock);Index: linux-2.6/kernel/Makefile
===================================================================kernel/Makefile +2
-1@@ -8,7 +8,8 @@ obj-y = sched.o fork.o exec_domain.o signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o + hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o \ + rwmutex.o
Quoted 5 lines
Quoted 4 lines
kernel/Makefile obj-y = sched.o fork.o exec_domain.o · line 15
Quoted 4 lines
kernel/Makefile +0
-0obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 16 lines · Paul E. McKenney
My concern would be the possibility that someone else also woke the sleeper up just as we were preparing to do so. This might happen in the case where multiple readers executed this code simultaneously. Seems to me that this might be vulnerable to the following sequence of events:
1. Reader-wannabe #1 wakes up the writer.
2. The writer wakes up and sees that the sum of the per-cpu
counters is still non-zero, due to the presence of reader-wannabe #2.3. Reader-wannabe #2 decrements its counter. Note that
reader-wannabe #2 could do all the memory barriers it wanted -- this vulnerability would still exist.
Quoted 8 lines
4. Reader-wannabe #2 wakes up the writer, which has no effect,
since the writer is already awake (but is perhaps preempted, interrupted, or something).5. The writer goes to sleep, never to awaken again (unless some
other reader comes along, which might or might not happen).Or am I missing something here?
Ugh, nasty. Will have to ponder this a bit. It looks like a spinlock is needed somewhere.
In the mean time, here is the latest code I have:
----
Subject: scalable rw_mutex
Scalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
--- include/linux/rwmutex.h | 82 ++++++++++++++++ kernel/Makefile | 3 kernel/rwmutex.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+), 1 deletion(-)
include/linux/rwmutex.h new file +82 -0
@@ -0,0 +1,82 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* This file contains the public data structure and API definitions. +*/ +#ifndef _LINUX_RWMUTEX_H +#define _LINUX_RWMUTEX_H + +#include <linux/preempt.h> +#include <linux/wait.h> +#include <linux/percpu_counter.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> +#include <asm/atomic.h> + +struct rw_mutex { +/* Read mostly global */ +struct percpu_counter readers; + +/* The following variables are only for the slowpath */ +struct task_struct *waiter;/* w -> r waiting */ +struct mutex read_mutex;/* r -> w waiting */ +struct mutex write_mutex;/* w -> w waiting */ +atomic_t reader_seq; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +struct lockdep_map dep_map; +#endif +}; + +void __rw_mutex_init (struct rw_mutex *rw_mutex,const char * name, +struct lock_class_key *key); +void rw_mutex_destroy (struct rw_mutex *rw_mutex); + +#define rw_mutex_init (rw_mutex) \ + do { \ + static struct lock_class_key __key; \ + __rw_mutex_init((rw_mutex), #rw_mutex, &__key); \ + } while (0) + +void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex); + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass); +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +int ret =__rw_mutex_read_trylock (rw_mutex); +if (ret) +rwsem_acquire_read (&rw_mutex->dep_map,0 ,1 , _RET_IP_); +return ret; +} + +static inline void rw_mutex_read_lock (struct rw_mutex *rw_mutex) +{ +int ret; + +might_sleep (); +rwsem_acquire_read (&rw_mutex->dep_map,0 ,0 , _RET_IP_); + + ret =__rw_mutex_read_trylock (rw_mutex); +if (!ret) +rw_mutex_read_lock_slow (rw_mutex); +} + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_is_locked (struct rw_mutex *rw_mutex) +{ +return mutex_is_locked (&rw_mutex->write_mutex); +} + +static inline void rw_mutex_write_lock (struct rw_mutex *rw_mutex) +{ +rw_mutex_write_lock_nested (rw_mutex,0 ); +} + +#endif /* _LINUX_RWMUTEX_H */
kernel/rwmutex.c new file +240 -0
240 lines not on this page · raw message
kernel/Makefile +2 -1
@@ -8,7 +8,8 @@ obj-y = sched.o fork.o exec_domain.o signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o + hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o \ + rwmutex.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/2] scalable rw_mutex
Quoted 22 lines · Peter Zijlstra
Quoted 20 lines · Paul E. McKenney
My concern would be the possibility that someone else also woke the sleeper up just as we were preparing to do so. This might happen in the case where multiple readers executed this code simultaneously. Seems to me that this might be vulnerable to the following sequence of events:
1. Reader-wannabe #1 wakes up the writer.
2. The writer wakes up and sees that the sum of the per-cpu
counters is still non-zero, due to the presence of reader-wannabe #2.3. Reader-wannabe #2 decrements its counter. Note that
reader-wannabe #2 could do all the memory barriers it wanted -- this vulnerability would still exist.4. Reader-wannabe #2 wakes up the writer, which has no effect,
since the writer is already awake (but is perhaps preempted, interrupted, or something).
Quoted 7 lines
Quoted 4 lines
5. The writer goes to sleep, never to awaken again (unless some
other reader comes along, which might or might not happen).Or am I missing something here?
Ugh, nasty. Will have to ponder this a bit. It looks like a spinlock is needed somewhere.
Ah -- the usual approach is for the writer to hold a spinlock across the initial check, the prepare_to_wait(), and last-minute re-checks to see if blocking is still warranted. Then drop the lock, call schedule(), and call finish_wait().
The reader would hold this same spinlock across both the change that would prevent the writer from sleeping and the wake-up call.
Then if the reader shows up just after the writer drops the lock, the reader's wakeup is guaranteed to do something useful. If two readers show up, they cannot run concurrently, so the above situation cannot occur.
There are a number of variations on this theme. Easy to get wrong, though -- I have proven that to myself more times than I care to admit...
Thanx, Paul
Quoted 358 lines
In the mean time, here is the latest code I have:
----
Subject: scalablerw_mutexScalable reader/writer lock.
Its scalable in that the read count is a percpu counter and the reader fast path does not write to a shared cache-line.
Its not FIFO fair, but starvation proof by alternating readers and writers.
Signed-off-by Peter Zijlstra <[email protected]>--- include/linux/rwmutex.h | 82 ++++++++++++++++ kernel/Makefile | 3 kernel/rwmutex.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+), 1 deletion(-)Index:
linux-2.6/include/linux/rwmutex.h
===================================================================include/linux/rwmutex.h new file +82
-0@@ -0,0 +1,82 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* This file contains the public data structure and API definitions. +*/ +#ifndef _LINUX_RWMUTEX_H +#define _LINUX_RWMUTEX_H + +#include <linux/preempt.h> +#include <linux/wait.h> +#include <linux/percpu_counter.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> +#include <asm/atomic.h> + +struct rw_mutex { +/* Read mostly global */ +struct percpu_counter readers; + +/* The following variables are only for the slowpath */ +struct task_struct *waiter;/* w -> r waiting */ +struct mutex read_mutex;/* r -> w waiting */ +struct mutex write_mutex;/* w -> w waiting */ +atomic_t reader_seq; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +struct lockdep_map dep_map; +#endif +}; + +void __rw_mutex_init (struct rw_mutex *rw_mutex,const char * name, +struct lock_class_key *key); +void rw_mutex_destroy (struct rw_mutex *rw_mutex); + +#define rw_mutex_init (rw_mutex) \ + do { \ + static struct lock_class_key __key; \ + __rw_mutex_init((rw_mutex), #rw_mutex, &__key); \ + } while (0) + +void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex); + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass); +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +int ret =__rw_mutex_read_trylock (rw_mutex); +if (ret) +rwsem_acquire_read (&rw_mutex->dep_map,0 ,1 , _RET_IP_); +return ret; +} + +static inline void rw_mutex_read_lock (struct rw_mutex *rw_mutex) +{ +int ret; + +might_sleep (); +rwsem_acquire_read (&rw_mutex->dep_map,0 ,0 , _RET_IP_); + + ret =__rw_mutex_read_trylock (rw_mutex); +if (!ret) +rw_mutex_read_lock_slow (rw_mutex); +} + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex); + +static inline int rw_mutex_is_locked (struct rw_mutex *rw_mutex) +{ +return mutex_is_locked (&rw_mutex->write_mutex); +} + +static inline void rw_mutex_write_lock (struct rw_mutex *rw_mutex) +{ +rw_mutex_write_lock_nested (rw_mutex,0 ); +} + +#endif /* _LINUX_RWMUTEX_H */ Index:
linux-2.6/kernel/rwmutex.c
===================================================================kernel/rwmutex.c new file +240
-0@@ -0,0 +1,240 @@ +/* +* Scalable reader/writer lock. +* +* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <[email protected]> +* +* Its scalable in that the read count is a percpu counter and the reader fast +* path does not write to a shared cache-line. +* +* Its not FIFO fair, but starvation proof by alternating readers and writers. +*/ +#include <linux/sched.h> +#include <linux/rwmutex.h> +#include <linux/debug_locks.h> +#include <linux/module.h> + +/* +* rw mutex - oxymoron when we take mutex to stand for 'MUTual EXlusion' +* +* However in this context we take mutex to mean a sleeping lock, with the +* property that it must be released by the same context that acquired it. +* +* design goals: +* +* A sleeping reader writer lock with a scalable read side, to avoid bouncing +* cache-lines. +* +* dynamics: +* +* The reader fast path is modification of a percpu_counter and a read of a +* shared cache-line. +* +* The write side is quite heavy; it takes two mutexes, a writer mutex and a +* readers mutex. The writer mutex is for w <-> w interaction, the read mutex +* for r -> w. The read side is forced into the slow path by setting the +* status bit. Then it waits for all current readers to disappear. +* +* The read lock slow path; taken when the status bit is set; takes the read +* mutex. Because the write side also takes this mutex, the new readers are +* blocked. The read unlock slow path tickles the writer every time a read +* lock is released. +* +* Write unlock clears the status bit, and drops the read mutex; allowing new +* readers. It then waits for at least one waiting reader to get a lock (if +* there were any readers waiting) before releasing the write mutex which will +* allow possible other writers to come in an stop new readers, thus avoiding +* starvation by alternating between readers and writers +* +* considerations: +* +* The lock's space footprint is quite large (on x86_64): +* +* 88 bytes [struct rw_mutex] +* 8 bytes per cpu NR_CPUS [void *] +* 32 bytes per cpu (nr_cpu_ids) [smallest slab] +* +* 408 bytes for x86_64 defconfig (NR_CPUS = 32) on a 2-way box. +* +* The write side is quite heavy; this lock is best suited for situations +* where the read side vastly dominates the write side. +*/ + +void __rw_mutex_init (struct rw_mutex *rw_mutex,const char *name, +struct lock_class_key *key) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC +debug_check_no_locks_freed ((void *)rw_mutex,sizeof (*rw_mutex)); +lockdep_init_map (&rw_mutex->dep_map, name, key,0 ); +#endif + +percpu_counter_init (&rw_mutex->readers,0 ); + rw_mutex->waiter =NULL ; +mutex_init (&rw_mutex->read_mutex); +mutex_init (&rw_mutex->write_mutex); +} +EXPORT_SYMBOL (__rw_mutex_init); + +void rw_mutex_destroy (struct rw_mutex *rw_mutex) +{ +percpu_counter_destroy (&rw_mutex->readers); +mutex_destroy (&rw_mutex->read_mutex); +mutex_destroy (&rw_mutex->write_mutex); +} +EXPORT_SYMBOL (rw_mutex_destroy); + +static inline void rw_mutex_waiter_set (struct rw_mutex *rw_mutex, +struct task_struct *tsk) +{ +spin_lock (&rw_mutex->write_mutex.wait_lock); + rw_mutex->waiter = tsk; +spin_unlock (&rw_mutex->write_mutex.wait_lock); +} + +#define rw_mutex_waiter_wait (rw_mutex, condition) \ +do { \ + struct task_struct *tsk = (rw_mutex)->waiter; \ + BUG_ON(tsk != current); \ + \ + set_task_state(tsk, TASK_UNINTERRUPTIBLE); \ + while (!(condition)) { \ + schedule(); \ + set_task_state(tsk, TASK_UNINTERRUPTIBLE); \ + } \ + tsk->state = TASK_RUNNING; \ +} while (0) + +static inline void rw_mutex_waiter_wake (struct rw_mutex *rw_mutex) +{ +spin_lock (&rw_mutex->write_mutex.wait_lock); +if (rw_mutex->waiter) +wake_up_process (rw_mutex->waiter); +spin_unlock (&rw_mutex->write_mutex.wait_lock); +} + +void rw_mutex_read_lock_slow (struct rw_mutex *rw_mutex) +{ +mutex_lock (&rw_mutex->read_mutex); +percpu_counter_inc (&rw_mutex->readers); +/* +* wake up a possible write unlock; waiting for at least a single +* reader to pass before letting a new writer through. +*/ +atomic_inc (&rw_mutex->reader_seq); +rw_mutex_waiter_wake (rw_mutex); +mutex_unlock (&rw_mutex->read_mutex); +} +EXPORT_SYMBOL (rw_mutex_read_lock_slow); + +int __rw_mutex_read_trylock (struct rw_mutex *rw_mutex) +{ +percpu_counter_inc (&rw_mutex->readers); +/* +* ensure the ->readers store and the ->waiter load is properly +* sequenced +*/ +smp_mb (); +if (unlikely (rw_mutex->waiter)) { +percpu_counter_dec (&rw_mutex->readers); +/* +* ensure the ->readers store has taken place before we issue +* the wake_up +*/ +smp_wmb (); +/* +* possibly wake up a writer waiting for this reference to +* disappear +*/ +rw_mutex_waiter_wake (rw_mutex); +return 0 ; + } +return 1 ; +} +EXPORT_SYMBOL (__rw_mutex_read_trylock); + +void rw_mutex_read_unlock (struct rw_mutex *rw_mutex) +{ +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +percpu_counter_dec (&rw_mutex->readers); +/* +* ensure the ->readers store and the ->waiter load is properly +* sequenced +*/ +smp_mb (); +if (unlikely (rw_mutex->waiter)) { +/* +* on the slow path; nudge the writer waiting for the last +* reader to go away +*/ +rw_mutex_waiter_wake (rw_mutex); + } +} +EXPORT_SYMBOL (rw_mutex_read_unlock); + +static inline bool rw_mutex_no_readers (struct rw_mutex *rw_mutex) +{ +/* +* match the wmb in __rw_mutex_read_trylock() +* sequence the store of ->readers vs this read. +*/ +smp_rmb (); +return percpu_counter_sum (&rw_mutex->readers) ==0 ; +} + +void rw_mutex_write_lock_nested (struct rw_mutex *rw_mutex,int subclass) +{ +might_sleep (); +rwsem_acquire (&rw_mutex->dep_map, subclass,0 , _RET_IP_); + +mutex_lock_nested (&rw_mutex->write_mutex, subclass); +BUG_ON (rw_mutex->waiter); + +/* +* block new readers +*/ +mutex_lock_nested (&rw_mutex->read_mutex, subclass); + rw_mutex->waiter = current; +/* +* full barrier to sequence the store of ->waiter +* and the load of ->readers +*/ +smp_mb (); +/* +* and wait for all current readers to go away +*/ +rw_mutex_waiter_wait (rw_mutex,rw_mutex_no_readers (rw_mutex)); +} +EXPORT_SYMBOL (rw_mutex_write_lock_nested); + +void rw_mutex_write_unlock (struct rw_mutex *rw_mutex) +{ +int seq, wait; + +might_sleep (); +rwsem_release (&rw_mutex->dep_map,1 , _RET_IP_); + +/* +* let the readers rip +* - snapshot the reader_seq +* - peek inside the read_mutex to see if anyone is waiting +* +* if so, we'll wait for the reader_seq to change after releasing the +* read_mutex +*/ + seq =atomic_read (&rw_mutex->reader_seq); + wait = (atomic_read (&rw_mutex->read_mutex.count) <0 ); +mutex_unlock (&rw_mutex->read_mutex); +/* +* wait for at least 1 reader to get through +*/ +if (wait) { +rw_mutex_waiter_wait (rw_mutex, + (atomic_read (&rw_mutex->reader_seq) != seq)); + } +rw_mutex_waiter_set (rw_mutex,NULL ); +/* +* before we let the writers rip +*/ +mutex_unlock (&rw_mutex->write_mutex); +} +EXPORT_SYMBOL (rw_mutex_write_unlock);Index: linux-2.6/kernel/Makefile
===================================================================
kernel/Makefile obj-y = sched.o fork.o exec_domain.o · line 15
Quoted 9 lines
kernel/Makefile +2
-1@@ -8,7 +8,8 @@ obj-y = sched.o fork.o exec_domain.o signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o + hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o \ + rwmutex.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 2/2 mm: change mmap_sem over to the scalable rw_mutex
In reply to Peter ZijlstraChange the mmap_sem over to the new rw_mtuex.
--- arch/alpha/kernel/osf_sys.c | 4 arch/alpha/kernel/traps.c | 4 arch/alpha/mm/fault.c | 8 - arch/arm/kernel/sys_arm.c | 8 - arch/arm/kernel/traps.c | 6 - arch/arm/mm/fault.c | 10 - arch/arm26/kernel/sys_arm.c | 8 - arch/arm26/mm/fault.c | 4 arch/avr32/kernel/sys_avr32.c | 4 arch/avr32/mm/fault.c | 12 +- arch/blackfin/kernel/sys_bfin.c | 4 arch/cris/arch-v32/drivers/cryptocop.c | 8 - arch/cris/kernel/sys_cris.c | 4 arch/cris/mm/fault.c | 12 +- arch/frv/kernel/sys_frv.c | 8 - arch/frv/mm/fault.c | 10 - arch/h8300/kernel/sys_h8300.c | 8 - arch/i386/kernel/sys_i386.c | 4 arch/i386/kernel/sysenter.c | 4 arch/i386/lib/usercopy.c | 8 - arch/i386/mm/fault.c | 16 +-- arch/ia64/ia32/binfmt_elf32.c | 24 ++-- arch/ia64/ia32/sys_ia32.c | 28 ++--- arch/ia64/kernel/perfmon.c | 12 +- arch/ia64/kernel/sys_ia64.c | 14 +- arch/ia64/mm/fault.c | 16 +-- arch/ia64/mm/init.c | 12 +- arch/m32r/kernel/sys_m32r.c | 4 arch/m32r/mm/fault.c | 16 +-- arch/m68k/kernel/sys_m68k.c | 8 - arch/m68k/mm/fault.c | 10 - arch/m68knommu/kernel/sys_m68k.c | 4 arch/mips/kernel/irixelf.c | 36 +++---- arch/mips/kernel/linux32.c | 4 arch/mips/kernel/syscall.c | 4 arch/mips/kernel/sysirix.c | 16 +-- arch/mips/mm/fault.c | 12 +- arch/parisc/kernel/sys_parisc.c | 4 arch/parisc/kernel/traps.c | 6 - arch/parisc/mm/fault.c | 8 - arch/powerpc/kernel/syscalls.c | 4 arch/powerpc/kernel/vdso.c | 6 - arch/powerpc/mm/fault.c | 18 +-- arch/powerpc/mm/tlb_32.c | 2 arch/powerpc/platforms/cell/spufs/fault.c | 6 - arch/ppc/mm/fault.c | 14 +- arch/s390/kernel/compat_linux.c | 4 arch/s390/kernel/sys_s390.c | 4 arch/s390/lib/uaccess_pt.c | 10 - arch/s390/mm/fault.c | 16 +-- arch/sh/kernel/sys_sh.c | 4 arch/sh/kernel/vsyscall/vsyscall.c | 4 arch/sh/mm/cache-sh4.c | 2 arch/sh/mm/fault.c | 12 +- arch/sh64/kernel/sys_sh64.c | 4 arch/sh64/mm/fault.c | 12 +- arch/sparc/kernel/sys_sparc.c | 8 - arch/sparc/kernel/sys_sunos.c | 8 - arch/sparc/mm/fault.c | 18 +-- arch/sparc64/kernel/binfmt_aout32.c | 36 +++---- arch/sparc64/kernel/sys_sparc.c | 12 +- arch/sparc64/kernel/sys_sparc32.c | 4 arch/sparc64/kernel/sys_sunos32.c | 8 - arch/sparc64/mm/fault.c | 14 +- arch/sparc64/solaris/misc.c | 4 arch/um/kernel/syscall.c | 4 arch/um/kernel/trap.c | 8 - arch/v850/kernel/syscalls.c | 4 arch/x86_64/ia32/ia32_aout.c | 32 +++--- arch/x86_64/ia32/ia32_binfmt.c | 6 - arch/x86_64/ia32/sys_ia32.c | 8 - arch/x86_64/ia32/syscall32.c | 4 arch/x86_64/kernel/sys_x86_64.c | 4 arch/x86_64/mm/fault.c | 16 +-- arch/x86_64/mm/pageattr.c | 10 - arch/xtensa/kernel/syscall.c | 4 arch/xtensa/mm/fault.c | 12 +- drivers/char/drm/drm_bufs.c | 8 - drivers/char/drm/i810_dma.c | 8 - drivers/char/drm/i830_dma.c | 8 - drivers/char/drm/via_dmablit.c | 4 drivers/char/mem.c | 6 - drivers/dma/iovlock.c | 4 drivers/infiniband/core/umem.c | 18 +-- drivers/infiniband/hw/ipath/ipath_user_pages.c | 18 +-- drivers/media/video/cafe_ccic.c | 2 drivers/media/video/ivtv/ivtv-udma.c | 4 drivers/media/video/ivtv/ivtv-yuv.c | 4 drivers/media/video/video-buf.c | 4 drivers/oprofile/buffer_sync.c | 12 +- drivers/scsi/sg.c | 4 drivers/scsi/st.c | 4 drivers/video/pvr2fb.c | 4 fs/aio.c | 10 - fs/binfmt_aout.c | 40 +++---- fs/binfmt_elf.c | 32 +++--- fs/binfmt_elf_fdpic.c | 18 +-- fs/binfmt_flat.c | 12 +- fs/binfmt_som.c | 12 +- fs/bio.c | 4 fs/block_dev.c | 4 fs/direct-io.c | 4 fs/exec.c | 22 ++-- fs/fuse/dev.c | 4 fs/fuse/file.c | 4 fs/hugetlbfs/inode.c | 2 fs/nfs/direct.c | 8 - fs/proc/task_mmu.c | 14 +- fs/proc/task_nommu.c | 20 +-- fs/splice.c | 6 - include/linux/futex.h | 2 include/linux/init_task.h | 1 include/linux/mempolicy.h | 2 include/linux/sched.h | 3 include/linux/uaccess.h | 4 ipc/shm.c | 8 - kernel/acct.c | 6 - kernel/auditsc.c | 4 kernel/cpuset.c | 6 - kernel/exit.c | 14 +- kernel/fork.c | 19 ++- kernel/futex.c | 128 ++++++++++++------------- kernel/relay.c | 2 mm/filemap.c | 6 - mm/fremap.c | 12 +- mm/madvise.c | 21 +--- mm/memory.c | 32 +++--- mm/mempolicy.c | 24 ++-- mm/migrate.c | 12 +- mm/mincore.c | 4 mm/mlock.c | 18 +-- mm/mmap.c | 26 ++--- mm/mprotect.c | 6 - mm/mremap.c | 6 - mm/msync.c | 8 - mm/nommu.c | 18 +-- mm/prio_tree.c | 6 - mm/rmap.c | 6 - mm/swapfile.c | 6 - 139 files changed, 740 insertions(+), 736 deletions(-)
arch/alpha/kernel/osf_sys.c +2 -2
@@ -193,9 +193,9 @@ osf_mmap(unsigned long addr, unsigned lo goto out; } flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); ret =do_mmap (file, addr, len, prot, flags, off); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file); out:
arch/alpha/kernel/traps.c +2 -2
@@ -1048,12 +1048,12 @@ give_sigsegv: info.si_code =SEGV_ACCERR ; else {struct mm_struct *mm = current->mm; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock);if (find_vma (mm, (unsigned long )va)) info.si_code =SEGV_ACCERR ;else info.si_code =SEGV_MAPERR ; -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); } info.si_addr = va;send_sig_info (SIGSEGV , &info, current);
arch/alpha/mm/fault.c +4 -4
@@ -115,7 +115,7 @@ do_page_fault(unsigned long address, uns goto vmalloc_fault;#endif -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);if (!vma) goto bad_area;
@@ -147,7 +147,7 @@ do_page_fault(unsigned long address, unsmake sure we exit gracefully rather than endlessly redo the fault. */ fault =handle_mm_fault (mm, vma, address, cause >0 ); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);switch (fault) {case VM_FAULT_MINOR :
@@ -168,7 +168,7 @@ do_page_fault(unsigned long address, uns/* Something tried to access memory that isn't in our memory map. Fix it, but check if it's kernel or user first. */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (user_mode (regs)) goto do_sigsegv;
@@ -194,7 +194,7 @@ do_page_fault(unsigned long address, uns out_of_memory:if (is_init (current)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; } printk(KERN_ALERT "VM: killing process %s(%d)\n",
arch/arm/kernel/sys_arm.c +4 -4
@@ -72,9 +72,9 @@ inline long do_mmap2( goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
@@ -118,9 +118,9 @@ sys_arm_mremap(unsigned long addr, unsigif (flags &MREMAP_FIXED && new_addr <FIRST_USER_ADDRESS ) goto out; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); ret =do_mremap (addr, old_len, new_len, flags, new_addr); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); out:return ret;
arch/arm/kernel/traps.c +3 -3
@@ -504,7 +504,7 @@ asmlinkage int arm_syscall(int no, strucspinlock_t *ptl; regs->ARM_cpsr &= ~PSR_C_BIT ; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); pgd =pgd_offset (mm, addr);if (!pgd_present (*pgd)) goto bad_access;
@@ -523,11 +523,11 @@ asmlinkage int arm_syscall(int no, struc regs->ARM_cpsr |=PSR_C_BIT ; }pte_unmap_unlock (pte, ptl); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return val; bad_access: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* simulate a write access fault */ do_DataAbort (addr,15 + (1 <<11 ), regs);return -1 ;
arch/arm/mm/fault.c +5 -5
@@ -203,9 +203,9 @@ survive:/* * If we are out of memory for pid1, sleep for a while and retry */ -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; check_stack:
@@ -237,14 +237,14 @@ do_page_fault(unsigned long addr, unsign* validly references user space from well defined areas of the code, * we can bug out early if this is from code which shouldn't. */ -if (!down_read_trylock (&mm->mmap_sem)) { +if (!down_read_trylock (&mm->mmap_lock)) {if (!user_mode (regs) && !search_exception_tables (regs->ARM_pc)) goto no_context; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); } fault =__do_page_fault (mm, addr, fsr, tsk); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* * Handle the "normal" case first - VM_FAULT_MAJOR / VM_FAULT_MINOR
arch/arm26/kernel/sys_arm.c +4 -4
@@ -77,9 +77,9 @@ inline long do_mmap2( goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
@@ -127,9 +127,9 @@ sys_arm_mremap(unsigned long addr, unsigif (flags &MREMAP_FIXED && new_addr <FIRST_USER_ADDRESS ) goto out; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); ret =do_mremap (addr, old_len, new_len, flags, new_addr); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); out:return ret;
arch/arm26/mm/fault.c +2 -2
@@ -218,9 +218,9 @@ int do_page_fault(unsigned long addr, unif (in_atomic () || !mm) goto no_context; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); fault =__do_page_fault (mm, addr, fsr, tsk); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* * Handle the "normal" case first
arch/avr32/kernel/sys_avr32.c +2 -2
@@ -41,9 +41,9 @@ asmlinkage long sys_mmap2(unsigned longreturn error; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, offset); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/avr32/mm/fault.c +6 -6
@@ -97,7 +97,7 @@ asmlinkage void do_page_fault(unsigned llocal_irq_enable (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);if (!vma)
@@ -159,7 +159,7 @@ survive:BUG (); } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ;/*
@@ -167,7 +167,7 @@ survive:* map. Fix it, but check if it's kernel or user first... */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (user_mode (regs)) {if (exception_trace)
@@ -221,10 +221,10 @@ no_context:* that made us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (current)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }printk ("VM: Killing process %s\n" , tsk->comm);
@@ -233,7 +233,7 @@ out_of_memory: goto no_context; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* Kernel mode? Handle exceptions or die */ signr =SIGBUS ;
arch/cris/arch-v32/drivers/cryptocop.c +4 -4
@@ -2715,7 +2715,7 @@ static int cryptocop_ioctl_process(struc }/* Acquire the mm page semaphore. */ -down_read (¤t->mm->mmap_sem); +rw_mutex_read_lock (¤t->mm->mmap_lock); err = get_user_pages(current, current->mm,
@@ -2727,7 +2727,7 @@ static int cryptocop_ioctl_process(struc NULL);if (err <0 ) { -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock); nooutpages = noinpages =0 ;DEBUG_API (printk ("cryptocop_ioctl_process: get_user_pages indata\n" )); goto error_cleanup;
@@ -2742,7 +2742,7 @@ static int cryptocop_ioctl_process(struc0 ,/* no force */ outpages, NULL); -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock);if (err <0 ) { nooutpages =0 ;DEBUG_API (printk ("cryptocop_ioctl_process: get_user_pages outdata\n" ));
@@ -2750,7 +2750,7 @@ static int cryptocop_ioctl_process(struc } nooutpages = err; }else { -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock); }/* Add 6 to nooutpages to make room for possibly inserted buffers for storing digest and
arch/cris/kernel/sys_cris.c +2 -2
@@ -60,9 +60,9 @@ do_mmap2(unsigned long addr, unsigned lo goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/cris/mm/fault.c +6 -6
@@ -104,7 +104,7 @@* than >= VMALLOC_START. * * Revision 1.12 2001/04/04 10:51:14 bjornw -* mmap_sem is grabbed for reading +* mmap_lock is grabbed for reading * * Revision 1.11 2001/03/23 07:36:07 starvik * Corrected according to review remarks
@@ -235,7 +235,7 @@ do_page_fault(unsigned long address, strif (in_atomic () || !mm) goto no_context; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);if (!vma) goto bad_area;
@@ -296,7 +296,7 @@ do_page_fault(unsigned long address, str goto out_of_memory; } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ;/*
@@ -305,7 +305,7 @@ do_page_fault(unsigned long address, str*/ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); bad_area_nosemaphore:DPG (show_registers (regs));
@@ -356,14 +356,14 @@ do_page_fault(unsigned long address, str*/ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);printk ("VM: killing process %s\n" , tsk->comm);if (user_mode (regs))do_exit (SIGKILL ); goto no_context; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* * Send a sigbus, regardless of whether we were in kernel
arch/frv/kernel/sys_frv.c +4 -4
@@ -68,9 +68,9 @@ asmlinkage long sys_mmap2(unsigned long pgoff >>= (PAGE_SHIFT -12 ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
@@ -113,9 +113,9 @@ asmlinkage long sys_mmap64(struct mmap_a } a.flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, a.addr, a.len, a.prot, a.flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file); out:
arch/frv/mm/fault.c +5 -5
@@ -81,7 +81,7 @@ asmlinkage void do_page_fault(int datammif (in_atomic () || !mm) goto no_context; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, ear0);if (!vma)
@@ -175,7 +175,7 @@ asmlinkage void do_page_fault(int datamm goto out_of_memory; } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ;/*
@@ -183,7 +183,7 @@ asmlinkage void do_page_fault(int datamm* Fix it, but check if it's kernel or user first.. */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* User mode accesses just cause a SIGSEGV */ if (user_mode (__frame)) {
@@ -255,14 +255,14 @@ asmlinkage void do_page_fault(int datamm* us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);printk ("VM: killing process %s\n" , current->comm);if (user_mode (__frame))do_exit (SIGKILL ); goto no_context; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* * Send a sigbus, regardless of whether we were in kernel
arch/h8300/kernel/sys_h8300.c +4 -4
@@ -59,9 +59,9 @@ static inline long do_mmap2( goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
@@ -146,9 +146,9 @@ asmlinkage long sys_mmap64(struct mmap_a } a.flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, a.addr, a.len, a.prot, a.flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file); out:
arch/i386/kernel/sys_i386.c +2 -2
@@ -55,9 +55,9 @@ asmlinkage long sys_mmap2(unsigned long goto out; } -down_write (&mm->mmap_sem); +rw_mutex_write_lock (&mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);if (file)fput (file);
arch/i386/kernel/sysenter.c +2 -2
@@ -271,7 +271,7 @@ int arch_setup_additional_pages(struct lint ret =0 ;bool compat; -down_write (&mm->mmap_sem); +rw_mutex_write_lock (&mm->mmap_lock);/* Test compat mode once here, in case someone changes it via sysctl */
@@ -312,7 +312,7 @@ int arch_setup_additional_pages(struct l (void *)VDSO_SYM (&SYSENTER_RETURN ); up_fail: -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);return ret; }
arch/i386/lib/usercopy.c +4 -4
@@ -744,18 +744,18 @@ unsigned long __copy_to_user_ll(void __u len = n; survive: -down_read (¤t->mm->mmap_sem); +rw_mutex_read_lock (¤t->mm->mmap_lock); retval =get_user_pages (current, current->mm, (unsigned long )to,1 ,1 ,0 , &pg,NULL );if (retval == -ENOMEM &&is_init (current)) { -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock);congestion_wait (WRITE ,HZ /50 ); goto survive; }if (retval !=1 ) { -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock);break ; }
@@ -764,7 +764,7 @@ survive:kunmap_atomic (maddr,KM_USER0 );set_page_dirty_lock (pg);put_page (pg); -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock); from += len; to += len;
arch/i386/mm/fault.c +8 -8
@@ -356,7 +356,7 @@ fastcall void __kprobes do_page_fault(st/* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the * kernel and should generate an OOPS. Unfortunatly, in the case of an -* erroneous fault occurring in a code path which already holds mmap_sem +* erroneous fault occurring in a code path which already holds mmap_lock * we will deadlock attempting to validate the fault against the * address space. Luckily the kernel only validly references user * space from well defined areas of code, which are listed in the
@@ -368,11 +368,11 @@ fastcall void __kprobes do_page_fault(st* source. If this is invalid we can skip the address space check, * thus avoiding the deadlock. */ -if (!down_read_trylock (&mm->mmap_sem)) { +if (!rw_mutex_read_trylock (&mm->mmap_lock)) {if ((error_code &4 ) ==0 && !search_exception_tables (regs->eip)) goto bad_area_nosemaphore; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); } vma =find_vma (mm, address);
@@ -445,7 +445,7 @@ good_area:if (bit <32 ) tsk->thread.screen_bitmap |=1 << bit; } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ;/*
@@ -453,7 +453,7 @@ good_area:* Fix it, but check if it's kernel or user first.. */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); bad_area_nosemaphore:/* User mode accesses just cause a SIGSEGV */
@@ -575,10 +575,10 @@ no_context:* us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (tsk)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }printk ("VM: killing process %s\n" , tsk->comm);
@@ -587,7 +587,7 @@ out_of_memory: goto no_context; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* Kernel mode? Handle exceptions or die */ if (!(error_code &4 ))
arch/ia64/ia32/binfmt_elf32.c +12 -12
@@ -99,15 +99,15 @@ ia64_elf32_init (struct pt_regs *regs) vma->vm_page_prot =PAGE_SHARED ; vma->vm_flags =VM_READ |VM_MAYREAD |VM_RESERVED ; vma->vm_ops = &ia32_shared_page_vm_ops; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); {if (insert_vm_struct (current->mm, vma)) {kmem_cache_free (vm_area_cachep, vma); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);BUG (); } } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }/*
@@ -125,15 +125,15 @@ ia64_elf32_init (struct pt_regs *regs) vma->vm_flags =VM_READ |VM_MAYREAD |VM_EXEC |VM_MAYEXEC |VM_RESERVED ; vma->vm_ops = &ia32_gate_page_vm_ops; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); {if (insert_vm_struct (current->mm, vma)) {kmem_cache_free (vm_area_cachep, vma); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);BUG (); } } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }/*
@@ -147,15 +147,15 @@ ia64_elf32_init (struct pt_regs *regs) vma->vm_end = vma->vm_start +PAGE_ALIGN (IA32_LDT_ENTRIES *IA32_LDT_ENTRY_SIZE ); vma->vm_page_prot =PAGE_SHARED ; vma->vm_flags =VM_READ |VM_WRITE |VM_MAYREAD |VM_MAYWRITE ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); {if (insert_vm_struct (current->mm, vma)) {kmem_cache_free (vm_area_cachep, vma); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);BUG (); } } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }ia64_psr (regs)->ac =0 ;/* turn off alignment checking */
@@ -215,7 +215,7 @@ ia32_setup_arg_pages (struct linux_binprif (!mpnt)return -ENOMEM ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); { mpnt->vm_mm = current->mm; mpnt->vm_start =PAGE_MASK & (unsigned long ) bprm->p;
@@ -229,7 +229,7 @@ ia32_setup_arg_pages (struct linux_binpr mpnt->vm_page_prot = (mpnt->vm_flags &VM_EXEC )?PAGE_COPY_EXEC :PAGE_COPY ;if ((ret =insert_vm_struct (current->mm, mpnt))) { -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);kmem_cache_free (vm_area_cachep, mpnt);return ret; }
@@ -244,7 +244,7 @@ ia32_setup_arg_pages (struct linux_binpr } stack_base +=PAGE_SIZE ; } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);/* Can't do it in ia64_elf32_init(). Needs to be done before calls to elf32_map() */
arch/ia64/ia32/sys_ia32.c +14 -14
@@ -213,12 +213,12 @@ mmap_subpage (struct file *file, unsigneif (old_prot)copy_from_user (page, (void __user *)PAGE_START (start),PAGE_SIZE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); { ret =do_mmap (NULL ,PAGE_START (start),PAGE_SIZE , prot |PROT_WRITE , flags |MAP_FIXED |MAP_ANONYMOUS ,0 ); } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (IS_ERR ((void *) ret)) goto out;
@@ -471,7 +471,7 @@ __ia32_set_pp(unsigned int start, unsignstatic void ia32_set_pp (unsigned int start,unsigned int end,int flags) { -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);if (flags &MAP_FIXED ) {/* * MAP_FIXED may lead to overlapping mmap. When this happens,
@@ -489,7 +489,7 @@ ia32_set_pp(unsigned int start, unsignedif (offset_in_page (end))__ia32_set_pp (PAGE_START (end), end, flags); } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }/*
@@ -559,7 +559,7 @@ ia32_unset_pp(unsigned int *startp, unsiunsigned int start = *startp, end = *endp;int ret =0 ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);__ia32_delete_pp_range (PAGE_ALIGN (start),PAGE_START (end));
@@ -594,7 +594,7 @@ ia32_unset_pp(unsigned int *startp, unsi } out: -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);return ret; }
@@ -652,7 +652,7 @@ ia32_compare_pp(unsigned int *startp, ununsigned int start = *startp, end = *endp;int retval =0 ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);if (end <PAGE_ALIGN (start)) { retval =__ia32_compare_pp (start, end);
@@ -677,7 +677,7 @@ ia32_compare_pp(unsigned int *startp, un } out: -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);return retval; }
@@ -745,11 +745,11 @@ ia32_copy_partial_page_list(struct task_ p->thread.ppl =ia32_init_pp_list ();if (!p->thread.ppl)return -ENOMEM ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); { retval =__ia32_copy_pp_list (p->thread.ppl); } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }return retval;
@@ -821,7 +821,7 @@ emulate_mmap (struct file *file, unsigneDBG ("mmap_body: mapping [0x%lx-0x%lx) %s with poff 0x%llx\n" , pstart, pend, is_congruent ?"congruent" :"not congruent" , poff); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); {if (!(flags &MAP_ANONYMOUS ) && is_congruent) ret =do_mmap (file, pstart, pend - pstart, prot, flags |MAP_FIXED , poff);
@@ -830,7 +830,7 @@ emulate_mmap (struct file *file, unsigne prot | ((flags &MAP_ANONYMOUS ) ?0 :PROT_WRITE ), flags | MAP_FIXED | MAP_ANONYMOUS, 0); } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (IS_ERR ((void *) ret))return ret;
@@ -904,11 +904,11 @@ ia32_do_mmap (struct file *file, unsigne }mutex_unlock (&ia32_mmap_mutex);#else -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); { addr =do_mmap (file, addr, len, prot, flags, offset); } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);#endif DBG ("ia32_do_mmap: returning 0x%lx\n" , addr);return addr;
arch/ia64/kernel/perfmon.c +6 -6
@@ -1451,13 +1451,13 @@ pfm_remove_smpl_mapping(struct task_stru/* * does the actual unmapping */ -down_write (&task->mm->mmap_sem); +rw_mutex_write_lock (&task->mm->mmap_lock);DPRINT (("down_write done smpl_vaddr=%p size=%lu\n" , vaddr, size)); r =pfm_do_munmap (task->mm, (unsigned long )vaddr, size,0 ); -up_write (&task->mm->mmap_sem); +rw_mutex_write_unlock (&task->mm->mmap_lock);if (r !=0 ) {printk (KERN_ERR "perfmon: [%d] unable to unmap sampling buffer @%p size=%lu\n" , task->pid, vaddr, size); }
@@ -2366,13 +2366,13 @@ pfm_smpl_buffer_alloc(struct task_struct* now we atomically find some area in the address space and * remap the buffer in it. */ -down_write (&task->mm->mmap_sem); +rw_mutex_write_lock (&task->mm->mmap_lock);/* find some free area in address space, must have mmap sem held */ vma->vm_start =pfm_get_unmapped_area (NULL ,0 , size,0 ,MAP_PRIVATE |MAP_ANONYMOUS ,0 );if (vma->vm_start ==0UL ) {DPRINT (("Cannot find unmapped area for size %ld\n" , size)); -up_write (&task->mm->mmap_sem); +rw_mutex_write_unlock (&task->mm->mmap_lock); goto error; } vma->vm_end = vma->vm_start + size;
@@ -2383,7 +2383,7 @@ pfm_smpl_buffer_alloc(struct task_struct/* can only be applied to current task, need to have the mm semaphore held when called */ if (pfm_remap_buffer (vma, (unsigned long )smpl_buf, vma->vm_start, size)) {DPRINT (("Can't remap buffer\n" )); -up_write (&task->mm->mmap_sem); +rw_mutex_write_unlock (&task->mm->mmap_lock); goto error; }
@@ -2398,7 +2398,7 @@ pfm_smpl_buffer_alloc(struct task_struct mm->total_vm += size >>PAGE_SHIFT ;vm_stat_account (vma->vm_mm, vma->vm_flags, vma->vm_file,vma_pages (vma)); -up_write (&task->mm->mmap_sem); +rw_mutex_write_unlock (&task->mm->mmap_lock);/* * keep track of user level virtual address
arch/ia64/kernel/sys_ia64.c +7 -7
@@ -106,9 +106,9 @@ ia64_brk (unsigned long brk)/* * Most of this replicates the code in sys_brk() except for an additional safety * check and the clearing of r8. However, we can't call sys_brk() because we need -* to acquire the mmap_sem before we can do the test... +* to acquire the mmap_lock before we can do the test... */ -down_write (&mm->mmap_sem); +rw_mutex_write_lock (&mm->mmap_lock);if (brk < mm->end_code) goto out;
@@ -144,7 +144,7 @@ set_brk: mm->brk = brk; out: retval = mm->brk; -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);force_successful_syscall_return ();return retval; }
@@ -209,9 +209,9 @@ do_mmap2 (unsigned long addr, unsigned l goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); addr =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); out:if (file)fput (file);
@@ -254,11 +254,11 @@ ia64_mremap (unsigned long addr, unsigneunsigned long flags, unsigned long new_addr); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); { addr =do_mremap (addr, old_len, new_len, flags, new_addr); } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (IS_ERR ((void *) addr))return addr;
arch/ia64/mm/fault.c +8 -8
@@ -93,8 +93,8 @@ ia64_do_page_fault (unsigned long addresstruct siginfo si;unsigned long mask; -/* mmap_sem is performance critical.... */ -prefetchw (&mm->mmap_sem); +/* mmap_lock is performance critical.... */ +prefetchw (&mm->mmap_lock);/* * If we're in an interrupt or have no user context, we must not take the fault..
@@ -105,7 +105,7 @@ ia64_do_page_fault (unsigned long addres#ifdef CONFIG_VIRTUAL_MEM_MAP /* * If fault is in region 5 and we are in the kernel, we may already -* have the mmap_sem (pfn_valid macro is called during mmap). There +* have the mmap_lock (pfn_valid macro is called during mmap). There * is no vma for region 5 addr's anyway, so skip getting the semaphore * and go directly to the exception handling code. */
@@ -121,7 +121,7 @@ ia64_do_page_fault (unsigned long addres SIGSEGV) == NOTIFY_STOP)return ; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma_prev (mm, address, &prev_vma);if (!vma)
@@ -180,7 +180,7 @@ ia64_do_page_fault (unsigned long addresdefault :BUG (); } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ; check_expansion:
@@ -209,7 +209,7 @@ ia64_do_page_fault (unsigned long addres goto good_area; bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);#ifdef CONFIG_VIRTUAL_MEM_MAP bad_area_no_up:#endif
@@ -278,10 +278,10 @@ ia64_do_page_fault (unsigned long addresreturn ; out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (current)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }printk (KERN_CRIT "VM: killing process %s\n" , current->comm);
arch/ia64/mm/init.c +6 -6
@@ -183,13 +183,13 @@ ia64_init_addr_space (void) vma->vm_end = vma->vm_start +PAGE_SIZE ; vma->vm_page_prot = protection_map[VM_DATA_DEFAULT_FLAGS &0x7 ]; vma->vm_flags =VM_DATA_DEFAULT_FLAGS |VM_GROWSUP |VM_ACCOUNT ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);if (insert_vm_struct (current->mm, vma)) { -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);kmem_cache_free (vm_area_cachep, vma);return ; } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }/* map NaT-page at address zero to speed up speculative dereferencing of NULL: */
@@ -200,13 +200,13 @@ ia64_init_addr_space (void) vma->vm_end =PAGE_SIZE ; vma->vm_page_prot =__pgprot (pgprot_val (PAGE_READONLY ) | _PAGE_MA_NAT); vma->vm_flags =VM_READ |VM_MAYREAD |VM_IO |VM_RESERVED ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);if (insert_vm_struct (current->mm, vma)) { -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);kmem_cache_free (vm_area_cachep, vma);return ; } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); } } }
arch/m32r/kernel/sys_m32r.c +2 -2
@@ -109,9 +109,9 @@ asmlinkage long sys_mmap2(unsigned long goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/m32r/mm/fault.c +8 -8
@@ -120,7 +120,7 @@ asmlinkage void do_page_fault(struct pt_/* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the * kernel and should generate an OOPS. Unfortunatly, in the case of an -* erroneous fault occurring in a code path which already holds mmap_sem +* erroneous fault occurring in a code path which already holds mmap_lock * we will deadlock attempting to validate the fault against the * address space. Luckily the kernel only validly references user * space from well defined areas of code, which are listed in the
@@ -132,11 +132,11 @@ asmlinkage void do_page_fault(struct pt_* source. If this is invalid we can skip the address space check, * thus avoiding the deadlock. */ -if (!down_read_trylock (&mm->mmap_sem)) { +if (!down_read_trylock (&mm->mmap_lock)) {if ((error_code &ACE_USERMODE ) ==0 && !search_exception_tables (regs->psw)) goto bad_area_nosemaphore; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); } vma =find_vma (mm, address);
@@ -210,7 +210,7 @@ survive:BUG (); }set_thread_fault_code (0 ); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ;/*
@@ -218,7 +218,7 @@ survive:* Fix it, but check if it's kernel or user first.. */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); bad_area_nosemaphore:/* User mode accesses just cause a SIGSEGV */
@@ -271,10 +271,10 @@ no_context:* us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (tsk)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }printk ("VM: killing process %s\n" , tsk->comm);
@@ -283,7 +283,7 @@ out_of_memory: goto no_context; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* Kernel mode? Handle exception or die */ if (!(error_code &ACE_USERMODE ))
arch/m68k/kernel/sys_m68k.c +4 -4
@@ -62,9 +62,9 @@ static inline long do_mmap2( goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
@@ -149,9 +149,9 @@ asmlinkage long sys_mmap64(struct mmap_a } a.flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, a.addr, a.len, a.prot, a.flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file); out:
arch/m68k/mm/fault.c +5 -5
@@ -102,7 +102,7 @@ int do_page_fault(struct pt_regs *regs,if (in_atomic () || !mm) goto no_context; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);if (!vma)
@@ -172,7 +172,7 @@ good_area: goto out_of_memory; } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return 0 ;/*
@@ -180,10 +180,10 @@ good_area:* us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (current)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }
@@ -214,6 +214,6 @@ acc_err: current->thread.faddr = address; send_sig: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return send_fault_sig (regs); }
arch/m68knommu/kernel/sys_m68k.c +2 -2
@@ -60,9 +60,9 @@ static inline long do_mmap2( goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/mips/kernel/irixelf.c +18 -18
@@ -154,9 +154,9 @@ static void set_brk(unsigned long start, end =PAGE_ALIGN (end);if (end <= start)return ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (start, end - start); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }
@@ -339,12 +339,12 @@ static unsigned int load_irix_interp(str (unsigned long ) (eppnt->p_offset & 0xfffff000)); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (interpreter, vaddr, eppnt->p_filesz + (eppnt->p_vaddr &0xfff ), elf_prot, elf_type, eppnt->p_offset &0xfffff000 ); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (error <0 && error >-1024 ) { printk("Aieee IRIX interp mmap error=%d\n",
@@ -396,9 +396,9 @@ static unsigned int load_irix_interp(str/* Map the last of the bss segment */ if (last_bss > len) { -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (len, (last_bss - len)); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }kfree (elf_phdata);
@@ -511,12 +511,12 @@ static inline void map_executable(struct prot = (epp->p_flags &PF_R ) ?PROT_READ :0 ; prot |= (epp->p_flags &PF_W ) ?PROT_WRITE :0 ; prot |= (epp->p_flags &PF_X ) ?PROT_EXEC :0 ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); (void )do_mmap (fp, (epp->p_vaddr &0xfffff000 ), (epp->p_filesz + (epp->p_vaddr &0xfff )), prot,EXEC_MAP_FLAGS , (epp->p_offset &0xfffff000 )); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);/* Fixup location tracking vars. */ if ((epp->p_vaddr &0xfffff000 ) < *estack)
@@ -580,9 +580,9 @@ static void irix_map_prda_page(void)unsigned long v;struct prda *pp; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); v =do_brk (PRDA_ADDRESS ,PAGE_SIZE ); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (v <0 )return ;
@@ -795,10 +795,10 @@ static int load_irix_binary(struct linux* Since we do not have the power to recompile these, we * emulate the SVr4 behavior. Sigh. */ -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); (void )do_mmap (NULL ,0 ,4096 ,PROT_READ |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE ,0 ); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);#endif start_thread (regs, elf_entry, bprm->p);
@@ -868,14 +868,14 @@ static int load_irix_library(struct filewhile (elf_phdata->p_type !=PT_LOAD ) elf_phdata++;/* Now use mmap to map the library into memory. */ -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (file, elf_phdata->p_vaddr &0xfffff000 , elf_phdata->p_filesz + (elf_phdata->p_vaddr &0xfff ),PROT_READ |PROT_WRITE |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE , elf_phdata->p_offset &0xfffff000 ); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); k = elf_phdata->p_vaddr + elf_phdata->p_filesz;if (k > elf_bss) elf_bss = k;
@@ -890,9 +890,9 @@ static int load_irix_library(struct file len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+0xfff ) &0xfffff000 ; bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;if (bss > len) { -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (len, bss-len); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }kfree (elf_phdata);return 0 ;
@@ -956,12 +956,12 @@ unsigned long irix_mapelf(int fd, struct prot |= (flags &PF_W ) ?PROT_WRITE :0 ; prot |= (flags &PF_X ) ?PROT_EXEC :0 ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); retval =do_mmap (filp, (vaddr &0xfffff000 ), (filesz + (vaddr &0xfff )), prot, (MAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE ), (offset &0xfffff000 )); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (retval != (vaddr &0xfffff000 )) {printk ("irix_mapelf: do_mmap fails with %d!\n" , retval);
arch/mips/kernel/linux32.c +2 -2
@@ -119,9 +119,9 @@ sys32_mmap2(unsigned long addr, unsigned } flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/mips/kernel/syscall.c +2 -2
@@ -130,9 +130,9 @@ do_mmap2(unsigned long addr, unsigned lo goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/mips/kernel/sysirix.c +8 -8
@@ -460,7 +460,7 @@ asmlinkage int irix_syssgi(struct pt_regpmd_t *pmdp;pte_t *ptep; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); pgdp =pgd_offset (mm, addr); pudp =pud_offset (pgdp, addr); pmdp =pmd_offset (pudp, addr);
@@ -475,7 +475,7 @@ asmlinkage int irix_syssgi(struct pt_reg PAGE_SHIFT, pageno); } } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);break ; }
@@ -523,7 +523,7 @@ asmlinkage int irix_brk(unsigned long brstruct mm_struct *mm = current->mm;int ret; -down_write (&mm->mmap_sem); +rw_mutex_write_lock (&mm->mmap_lock);if (brk < mm->end_code) { ret = -ENOMEM ; goto out;
@@ -576,7 +576,7 @@ asmlinkage int irix_brk(unsigned long br ret =0 ; out: -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);return ret; }
@@ -1051,9 +1051,9 @@ asmlinkage unsigned long irix_mmap32(uns flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); retval =do_mmap (file, addr, len, prot, flags, offset); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
@@ -1536,9 +1536,9 @@ asmlinkage int irix_mmap64(struct pt_reg flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/mips/mm/fault.c +6 -6
@@ -71,7 +71,7 @@ asmlinkage void do_page_fault(struct pt_if (in_atomic () || !mm) goto bad_area_nosemaphore; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);if (!vma) goto bad_area;
@@ -117,7 +117,7 @@ survive:BUG (); } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ;/*
@@ -125,7 +125,7 @@ survive:* Fix it, but check if it's kernel or user first.. */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); bad_area_nosemaphore:/* User mode accesses just cause a SIGSEGV */
@@ -173,10 +173,10 @@ no_context:* us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (tsk)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }printk ("VM: killing process %s\n" , tsk->comm);
@@ -185,7 +185,7 @@ out_of_memory: goto no_context; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* Kernel mode? Handle exceptions or die */ if (!user_mode (regs))
arch/parisc/kernel/sys_parisc.c +2 -2
@@ -137,9 +137,9 @@ static unsigned long do_mmap2(unsigned l flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file !=NULL )fput (file);
arch/parisc/kernel/traps.c +3 -3
@@ -710,7 +710,7 @@ void handle_interruption(int code, strucif (user_mode (regs)) {struct vm_area_struct *vma; -down_read (¤t->mm->mmap_sem); +rw_mutex_read_lock (¤t->mm->mmap_lock); vma =find_vma (current->mm,regs->iaoq[0 ]);if (vma && (regs->iaoq[0 ] >= vma->vm_start) && (vma->vm_flags &VM_EXEC )) {
@@ -718,10 +718,10 @@ void handle_interruption(int code, struc fault_address = regs->iaoq[0 ]; fault_space = regs->iasq[0 ]; -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock);break ;/* call do_page_fault() */ } -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock); }/* Fall Through */ case 27 :
arch/parisc/mm/fault.c +4 -4
@@ -151,7 +151,7 @@ void do_page_fault(struct pt_regs *regs,if (in_atomic () || !mm) goto no_context; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma_prev (mm, address, &prev_vma);if (!vma || address < vma->vm_start) goto check_expansion;
@@ -190,7 +190,7 @@ good_area:default : goto out_of_memory; } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ; check_expansion:
@@ -202,7 +202,7 @@ check_expansion:* Something tried to access memory that isn't in our memory map.. */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (user_mode (regs)) {struct siginfo si;
@@ -259,7 +259,7 @@ no_context:parisc_terminate ("Bad Address (null pointer deref?)" , regs, code, address); out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);printk (KERN_CRIT "VM: killing process %s\n" , current->comm);if (user_mode (regs))do_exit (SIGKILL );
arch/powerpc/kernel/syscalls.c +2 -2
@@ -175,9 +175,9 @@ static inline unsigned long do_mmap2(uns flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); ret =do_mmap_pgoff (file, addr, len, prot, flags, off); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file); out:
arch/powerpc/kernel/vdso.c +3 -3
@@ -214,7 +214,7 @@ int arch_setup_additional_pages(struct l* at vdso_base which is the "natural" base for it, but we might fail * and end up putting it elsewhere. */ -down_write (&mm->mmap_sem); +rw_mutex_write_lock (&mm->mmap_lock); vdso_base =get_unmapped_area (NULL , vdso_base, vdso_pages <<PAGE_SHIFT ,0 ,0 );if (IS_ERR_VALUE (vdso_base)) {
@@ -248,11 +248,11 @@ int arch_setup_additional_pages(struct l/* Put vDSO base into mm struct */ current->mm->context.vdso_base = vdso_base; -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);return 0 ; fail_mmapsem: -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);return rc; }
arch/powerpc/mm/fault.c +9 -9
@@ -199,7 +199,7 @@ int __kprobes do_page_fault(struct pt_re/* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the * kernel and should generate an OOPS. Unfortunately, in the case of an -* erroneous fault occurring in a code path which already holds mmap_sem +* erroneous fault occurring in a code path which already holds mmap_lock * we will deadlock attempting to validate the fault against the * address space. Luckily the kernel only validly references user * space from well defined areas of code, which are listed in the
@@ -211,11 +211,11 @@ int __kprobes do_page_fault(struct pt_re* source. If this is invalid we can skip the address space check, * thus avoiding the deadlock. */ -if (!down_read_trylock (&mm->mmap_sem)) { +if (!down_read_trylock (&mm->mmap_lock)) {if (!user_mode (regs) && !search_exception_tables (regs->nip)) goto bad_area_nosemaphore; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); } vma =find_vma (mm, address);
@@ -306,7 +306,7 @@ good_area:pte_update (ptep,0 , _PAGE_HWEXEC);_tlbie (address);pte_unmap_unlock (ptep, ptl); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return 0 ; }pte_unmap_unlock (ptep, ptl);
@@ -347,11 +347,11 @@ good_area:BUG (); } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return 0 ; bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); bad_area_nosemaphore:/* User mode accesses cause a SIGSEGV */
@@ -373,10 +373,10 @@ bad_area_nosemaphore:* us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (current)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }printk ("VM: killing process %s\n" , current->comm);
@@ -385,7 +385,7 @@ out_of_memory:return SIGKILL ; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (user_mode (regs)) { info.si_signo =SIGBUS ; info.si_errno =0 ;
arch/powerpc/mm/tlb_32.c +1 -1
@@ -150,7 +150,7 @@ void flush_tlb_mm(struct mm_struct *mm)/* * It is safe to go down the mm's list of vmas when called -* from dup_mmap, holding mmap_sem. It would also be safe from +* from dup_mmap, holding mmap_lock. It would also be safe from * unmap_region or exit_mmap, but not from vmtruncate on SMP - * but it seems dup_mmap is the only SMP case which gets here. */
arch/ppc/mm/fault.c +7 -7
@@ -130,7 +130,7 @@ int do_page_fault(struct pt_regs *regs,if (in_atomic () || mm ==NULL )return SIGSEGV ; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);if (!vma) goto bad_area;
@@ -228,7 +228,7 @@ good_area:pte_update (ptep,0 , _PAGE_HWEXEC);_tlbie (address);pte_unmap_unlock (ptep, ptl); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return 0 ; }pte_unmap_unlock (ptep, ptl);
@@ -264,7 +264,7 @@ good_area:BUG (); } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* * keep track of tlb+htab misses that are good addrs but * just need pte's created via handle_mm_fault()
@@ -274,7 +274,7 @@ good_area:return 0 ; bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); pte_errors++;/* User mode accesses cause a SIGSEGV */
@@ -290,10 +290,10 @@ bad_area:* us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (current)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }printk ("VM: killing process %s\n" , current->comm);
@@ -302,7 +302,7 @@ out_of_memory:return SIGKILL ; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); info.si_signo =SIGBUS ; info.si_errno =0 ; info.si_code =BUS_ADRERR ;
arch/s390/kernel/compat_linux.c +2 -2
@@ -860,14 +860,14 @@ static inline long do_mmap2( goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff);if (!IS_ERR ((void *) error) && error + len >=0x80000000ULL ) {/* Result is out of bounds. */ do_munmap (current->mm, addr, len); error = -ENOMEM ; } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/s390/kernel/sys_s390.c +2 -2
@@ -64,9 +64,9 @@ static inline long do_mmap2( goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/s390/lib/uaccess_pt.c +5 -5
@@ -23,7 +23,7 @@ static int __handle_fault(struct mm_struif (in_atomic ())return ret; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);if (unlikely (!vma)) goto out;
@@ -60,21 +60,21 @@ survive: } ret =0 ; out: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ret; out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (current)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }printk ("VM: killing process %s\n" , current->comm);return ret; out_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); current->thread.prot_addr = address; current->thread.trap_no =0x11 ;force_sig (SIGBUS , current);
arch/s390/mm/fault.c +8 -8
@@ -210,10 +210,10 @@ static int do_out_of_memory(struct pt_restruct task_struct *tsk = current;struct mm_struct *mm = tsk->mm; -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (tsk)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock);return 1 ; }printk ("VM: killing process %s\n" , tsk->comm);
@@ -229,7 +229,7 @@ static void do_sigbus(struct pt_regs *restruct task_struct *tsk = current;struct mm_struct *mm = tsk->mm; -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* * Send a sigbus, regardless of whether we were in kernel * or user mode.
@@ -261,7 +261,7 @@ static int signal_return(struct mm_strucif (rc)return -EFAULT ; -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);clear_tsk_thread_flag (current,TIF_SINGLE_STEP );#ifdef CONFIG_COMPAT compat =test_tsk_thread_flag (current,TIF_31BIT );
@@ -330,7 +330,7 @@ do_exception(struct pt_regs *regs, unsig*/ local_irq_enable (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); si_code =SEGV_MAPERR ; vma =find_vma (mm, address);
@@ -341,7 +341,7 @@ do_exception(struct pt_regs *regs, unsigif (unlikely ((space ==2 ) && !(vma->vm_flags &VM_EXEC )))if (!signal_return (mm, regs, address, error_code))/* -* signal_return() has done an up_read(&mm->mmap_sem) +* signal_return() has done an rw_mutex_read_unlock(&mm->mmap_lock) * if it returns 0. */ return ;
@@ -392,7 +392,7 @@ survive:BUG (); } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* * The instruction that caused the program check will * be repeated. Don't signal single step via SIGTRAP.
@@ -405,7 +405,7 @@ survive:* Fix it, but check if it's kernel or user first.. */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* User mode accesses just cause a SIGSEGV */ if (regs->psw.mask &PSW_MASK_PSTATE ) {
arch/sh/kernel/sys_sh.c +2 -2
@@ -152,9 +152,9 @@ do_mmap2(unsigned long addr, unsigned lo goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/sh/kernel/vsyscall/vsyscall.c +2 -2
@@ -64,7 +64,7 @@ int arch_setup_additional_pages(struct lunsigned long addr;int ret; -down_write (&mm->mmap_sem); +rw_mutex_write_lock (&mm->mmap_lock); addr =get_unmapped_area (NULL ,0 ,PAGE_SIZE ,0 ,0 );if (IS_ERR_VALUE (addr)) { ret = addr;
@@ -82,7 +82,7 @@ int arch_setup_additional_pages(struct l current->mm->context.vdso = (void *)addr; up_fail: -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);return ret; }
arch/sh/mm/cache-sh4.c +1 -1
@@ -372,7 +372,7 @@ loop_exit:* need to flush the I-cache, since aliases don't matter for that. We * should try that. * -* Caller takes mm->mmap_sem. +* Caller takes mm->mmap_lock. */ void flush_cache_mm (struct mm_struct *mm) {
arch/sh/mm/fault.c +6 -6
@@ -131,7 +131,7 @@ asmlinkage void __kprobes do_page_fault(if (in_atomic () || !mm) goto no_context; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);if (!vma)
@@ -177,7 +177,7 @@ survive:BUG (); } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ;/*
@@ -185,7 +185,7 @@ survive:* Fix it, but check if it's kernel or user first.. */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); bad_area_nosemaphore:if (user_mode (regs)) {
@@ -232,10 +232,10 @@ no_context:* us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (current)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }printk ("VM: killing process %s\n" , tsk->comm);
@@ -244,7 +244,7 @@ out_of_memory: goto no_context; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* * Send a sigbus, regardless of whether we were in kernel
arch/sh64/kernel/sys_sh64.c +2 -2
@@ -147,9 +147,9 @@ static inline long do_mmap2( goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/sh64/mm/fault.c +6 -6
@@ -157,7 +157,7 @@ asmlinkage void do_page_fault(struct pt_ goto no_context;/* TLB misses upon some cache flushes get done under cli() */ -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);
@@ -249,7 +249,7 @@ survive: no_pte: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ;/*
@@ -260,7 +260,7 @@ bad_area:#ifdef DEBUG_FAULT printk ("fault:bad area\n" );#endif -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (user_mode (regs)) {static int count=0 ;
@@ -324,10 +324,10 @@ out_of_memory: goto survive; }printk ("fault:Out of memory\n" ); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (current)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }printk ("VM: killing process %s\n" , tsk->comm);
@@ -337,7 +337,7 @@ out_of_memory: do_sigbus:printk ("fault:Do sigbus\n" ); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* * Send a sigbus, regardless of whether we were in kernel
arch/sparc/kernel/sys_sparc.c +4 -4
@@ -252,9 +252,9 @@ static unsigned long do_mmap2(unsigned l len =PAGE_ALIGN (len); flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); retval =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
@@ -308,7 +308,7 @@ asmlinkage unsigned long sparc_mremap(unif (old_len >TASK_SIZE -PAGE_SIZE || new_len >TASK_SIZE -PAGE_SIZE ) goto out; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);if (flags &MREMAP_FIXED ) { if (ARCH_SUN4C_SUN4 && new_addr < 0xe0000000 &&
@@ -343,7 +343,7 @@ asmlinkage unsigned long sparc_mremap(un } ret =do_mremap (addr, old_len, new_len, flags, new_addr); out_sem: -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); out:return ret; }
arch/sparc/kernel/sys_sunos.c +4 -4
@@ -120,9 +120,9 @@ asmlinkage unsigned long sunos_mmap(unsi } flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); retval =do_mmap (file, addr, len, prot, flags, off); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (!ret_type) retval = ((retval <PAGE_OFFSET ) ?0 : retval);
@@ -149,7 +149,7 @@ asmlinkage int sunos_brk(unsigned long bunsigned long rlim;unsigned long newbrk, oldbrk; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);if (ARCH_SUN4C_SUN4 ) {if (brk >=0x20000000 && brk <0xe0000000 ) { goto out;
@@ -211,7 +211,7 @@ asmlinkage int sunos_brk(unsigned long bdo_brk (oldbrk, newbrk-oldbrk); retval =0 ; out: -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);return retval; }
arch/sparc/mm/fault.c +9 -9
@@ -251,7 +251,7 @@ asmlinkage void do_sparc_fault(struct ptif (in_atomic () || !mm) goto no_context; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock);/* * The kernel referencing a bad kernel pointer can lock up
@@ -302,7 +302,7 @@ good_area: current->min_flt++;break ; } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ;/*
@@ -310,7 +310,7 @@ good_area:* Fix it, but check if it's kernel or user first.. */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); bad_area_nosemaphore:/* User mode accesses just cause a SIGSEGV */
@@ -366,14 +366,14 @@ no_context:* us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);printk ("VM: killing process %s\n" , tsk->comm);if (from_user)do_exit (SIGKILL ); goto no_context; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); info.si_signo =SIGBUS ; info.si_errno =0 ; info.si_code =BUS_ADRERR ;
@@ -513,7 +513,7 @@ inline void force_user_fault(unsigned loprintk ("wf<pid=%d,wr=%d,addr=%08lx>\n" , tsk->pid, write, address);#endif -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);if (!vma) goto bad_area;
@@ -537,10 +537,10 @@ good_area:case VM_FAULT_OOM : goto do_sigbus; } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ; bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);#if 0 printk ("Window whee %s [%d]: segfaults at %08lx\n" , tsk->comm, tsk->pid, address);
@@ -555,7 +555,7 @@ bad_area:return ; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); info.si_signo =SIGBUS ; info.si_errno =0 ; info.si_code =BUS_ADRERR ;
arch/sparc64/kernel/binfmt_aout32.c +18 -18
@@ -48,9 +48,9 @@ static void set_brk(unsigned long start, end =PAGE_ALIGN (end);if (end <= start)return ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (start, end - start); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }/*
@@ -248,14 +248,14 @@ static int load_aout32_binary(struct linif (N_MAGIC (ex) ==NMAGIC ) {loff_t pos = fd_offset;/* Fuck me plenty... */ -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_brk (N_TXTADDR (ex), ex.a_text); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); bprm->file->f_op->read (bprm->file, (char __user *)N_TXTADDR (ex), ex.a_text, &pos); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_brk (N_DATADDR (ex), ex.a_data); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); bprm->file->f_op->read (bprm->file, (char __user *)N_DATADDR (ex), ex.a_data, &pos); goto beyond_if;
@@ -263,10 +263,10 @@ static int load_aout32_binary(struct linif (N_MAGIC (ex) ==OMAGIC ) {loff_t pos = fd_offset; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (N_TXTADDR (ex) &PAGE_MASK , ex.a_text+ex.a_data +PAGE_SIZE -1 ); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); bprm->file->f_op->read (bprm->file, (char __user *)N_TXTADDR (ex), ex.a_text+ex.a_data, &pos); }else {
@@ -280,33 +280,33 @@ static int load_aout32_binary(struct linif (!bprm->file->f_op->mmap) {loff_t pos = fd_offset; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (0 , ex.a_text+ex.a_data); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); bprm->file->f_op->read (bprm->file, (char __user *)N_TXTADDR (ex), ex.a_text+ex.a_data, &pos); goto beyond_if; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (bprm->file,N_TXTADDR (ex), ex.a_text,PROT_READ |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE |MAP_EXECUTABLE , fd_offset); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (error !=N_TXTADDR (ex)) {send_sig (SIGKILL , current,0 );return error; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (bprm->file,N_DATADDR (ex), ex.a_data,PROT_READ |PROT_WRITE |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE |MAP_EXECUTABLE , fd_offset + ex.a_text); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (error !=N_DATADDR (ex)) {send_sig (SIGKILL , current,0 );return error;
@@ -378,12 +378,12 @@ static int load_aout32_library(struct fi start_addr = ex.a_entry &0xfffff000 ;/* Now use mmap to map the library into memory. */ -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (file, start_addr, ex.a_text + ex.a_data,PROT_READ |PROT_WRITE |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE ,N_TXTOFF (ex)); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); retval = error;if (error != start_addr) goto out;
@@ -391,9 +391,9 @@ static int load_aout32_library(struct fi len =PAGE_ALIGN (ex.a_text + ex.a_data); bss = ex.a_text + ex.a_data + ex.a_bss;if (bss > len) { -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_brk (start_addr + len, bss - len); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); retval = error;if (error != start_addr + len) goto out;
arch/sparc64/kernel/sys_sparc.c +6 -6
@@ -584,9 +584,9 @@ asmlinkage unsigned long sys_mmap(unsign flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); len =PAGE_ALIGN (len); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); retval =do_mmap (file, addr, len, prot, flags, off); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
@@ -601,9 +601,9 @@ asmlinkage long sys64_munmap(unsigned loif (invalid_64bit_range (addr, len))return -EINVAL ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); ret =do_munmap (current->mm, addr, len); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);return ret; }
@@ -625,7 +625,7 @@ asmlinkage unsigned long sys64_mremap(unif (unlikely (invalid_64bit_range (addr, old_len))) goto out; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);if (flags &MREMAP_FIXED ) {if (invalid_64bit_range (new_addr, new_len)) goto out_sem;
@@ -655,7 +655,7 @@ asmlinkage unsigned long sys64_mremap(un } ret =do_mremap (addr, old_len, new_len, flags, new_addr); out_sem: -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); out:return ret; }
arch/sparc64/kernel/sys_sparc32.c +2 -2
@@ -921,7 +921,7 @@ asmlinkage unsigned long sys32_mremap(un goto out;if (addr >STACK_TOP32 - old_len) goto out; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);if (flags &MREMAP_FIXED ) {if (new_addr >STACK_TOP32 - new_len) goto out_sem;
@@ -951,7 +951,7 @@ asmlinkage unsigned long sys32_mremap(un } ret =do_mremap (addr, old_len, new_len, flags, new_addr); out_sem: -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); out:return ret; }
arch/sparc64/kernel/sys_sunos32.c +4 -4
@@ -100,12 +100,12 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 flags &= ~_MAP_NEW; flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); retval =do_mmap (file, (unsigned long ) addr, (unsigned long ) len, (unsigned long ) prot, (unsigned long ) flags, (unsigned long ) off); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (!ret_type) retval = ((retval <0xf0000000 ) ?0 : retval); out_putf:
@@ -126,7 +126,7 @@ asmlinkage int sunos_brk(u32 baddr)unsigned long rlim;unsigned long newbrk, oldbrk, brk = (unsigned long ) baddr; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);if (brk < current->mm->end_code) goto out; newbrk =PAGE_ALIGN (brk);
@@ -169,7 +169,7 @@ asmlinkage int sunos_brk(u32 baddr)do_brk (oldbrk, newbrk-oldbrk); retval =0 ; out: -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);return retval; }
arch/sparc64/mm/fault.c +7 -7
@@ -125,7 +125,7 @@ static void bad_kernel_pc(struct pt_regs }/* -* We now make sure that mmap_sem is held in all paths that call +* We now make sure that mmap_lock is held in all paths that call * this. Additionally, to prevent kswapd from ripping ptes from * under us, raise interrupts around the time that we look at the * pte, kswapd will have to wait to get his smp ipi response from
@@ -319,13 +319,13 @@ asmlinkage void __kprobes do_sparc64_fau address &=0xffffffff ; } -if (!down_read_trylock (&mm->mmap_sem)) { +if (!down_read_trylock (&mm->mmap_lock)) {if ((regs->tstate &TSTATE_PRIV ) && !search_exception_tables (regs->tpc)) { insn =get_fault_insn (regs, insn); goto handle_kernel_fault; } -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); } vma =find_vma (mm, address);
@@ -430,7 +430,7 @@ good_area:BUG (); } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); mm_rss =get_mm_rss (mm);#ifdef CONFIG_HUGETLB_PAGE
@@ -453,7 +453,7 @@ good_area:*/ bad_area: insn =get_fault_insn (regs, insn); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); handle_kernel_fault:do_kernel_fault (regs, si_code, fault_code, insn, address);
@@ -465,7 +465,7 @@ handle_kernel_fault:*/ out_of_memory: insn =get_fault_insn (regs, insn); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);printk ("VM: killing process %s\n" , current->comm);if (!(regs->tstate &TSTATE_PRIV ))do_exit (SIGKILL );
@@ -477,7 +477,7 @@ intr_or_no_mm: do_sigbus: insn =get_fault_insn (regs, insn); -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* * Send a sigbus, regardless of whether we were in kernel
arch/sparc64/solaris/misc.c +2 -2
@@ -95,12 +95,12 @@ static u32 do_solaris_mmap(u32 addr, u32 ret_type = flags & _MAP_NEW; flags &= ~_MAP_NEW; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); flags &= ~(MAP_EXECUTABLE |MAP_DENYWRITE ); retval =do_mmap (file, (unsigned long ) addr, (unsigned long ) len, (unsigned long ) prot, (unsigned long ) flags, off); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (!ret_type) retval = ((retval <STACK_TOP32 ) ?0 : retval);
arch/um/kernel/syscall.c +2 -2
@@ -63,9 +63,9 @@ long sys_mmap2(unsigned long addr, unsig goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/um/kernel/trap.c +4 -4
@@ -53,7 +53,7 @@ int handle_page_fault(unsigned long addrif (in_atomic ()) goto out_nosemaphore; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);if (!vma) goto out;
@@ -111,7 +111,7 @@ survive:#endif flush_tlb_page (vma, address); out: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); out_nosemaphore:return (err);
@@ -121,9 +121,9 @@ out_nosemaphore:*/ out_of_memory:if (is_init (current)) { -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; } goto out;
arch/v850/kernel/syscalls.c +2 -2
@@ -165,9 +165,9 @@ do_mmap2 (unsigned long addr, size_t len goto out; } -down_write (¤t->mm->mmap_sem); +down_write (¤t->mm->mmap_lock); ret =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +up_write (¤t->mm->mmap_lock);if (file)fput (file); out:
arch/x86_64/ia32/ia32_aout.c +16 -16
@@ -112,9 +112,9 @@ static void set_brk(unsigned long start, end =PAGE_ALIGN (end);if (end <= start)return ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (start, end - start); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }#ifdef CORE_DUMP
@@ -324,9 +324,9 @@ static int load_aout_binary(struct linux pos =32 ; map_size = ex.a_text+ex.a_data; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_brk (text_addr &PAGE_MASK , map_size); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (error != (text_addr &PAGE_MASK )) {send_sig (SIGKILL , current,0 );
@@ -364,9 +364,9 @@ static int load_aout_binary(struct linuxif (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK ) !=0 )) {loff_t pos = fd_offset; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (N_TXTADDR (ex), ex.a_text+ex.a_data); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); bprm->file->f_op->read (bprm->file, (char __user *)N_TXTADDR (ex), ex.a_text+ex.a_data, &pos);
@@ -376,24 +376,24 @@ static int load_aout_binary(struct linux goto beyond_if; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (bprm->file,N_TXTADDR (ex), ex.a_text,PROT_READ |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE |MAP_EXECUTABLE |MAP_32BIT , fd_offset); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (error !=N_TXTADDR (ex)) {send_sig (SIGKILL , current,0 );return error; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (bprm->file,N_DATADDR (ex), ex.a_data,PROT_READ |PROT_WRITE |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE |MAP_EXECUTABLE |MAP_32BIT , fd_offset + ex.a_text); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (error !=N_DATADDR (ex)) {send_sig (SIGKILL , current,0 );return error;
@@ -475,9 +475,9 @@ static int load_aout_library(struct file error_time = jiffies; }#endif -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (start_addr, ex.a_text + ex.a_data + ex.a_bss); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); file->f_op->read (file, (char __user *)start_addr, ex.a_text + ex.a_data, &pos);
@@ -488,12 +488,12 @@ static int load_aout_library(struct file goto out; }/* Now use mmap to map the library into memory. */ -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (file, start_addr, ex.a_text + ex.a_data,PROT_READ |PROT_WRITE |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE |MAP_32BIT ,N_TXTOFF (ex)); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); retval = error;if (error != start_addr) goto out;
@@ -501,9 +501,9 @@ static int load_aout_library(struct file len =PAGE_ALIGN (ex.a_text + ex.a_data); bss = ex.a_text + ex.a_data + ex.a_bss;if (bss > len) { -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_brk (start_addr + len, bss - len); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); retval = error;if (error != start_addr + len) goto out;
arch/x86_64/ia32/ia32_binfmt.c +3 -3
@@ -306,7 +306,7 @@ int ia32_setup_arg_pages(struct linux_biif (!mpnt)return -ENOMEM ; -down_write (&mm->mmap_sem); +rw_mutex_write_lock (&mm->mmap_lock); { mpnt->vm_mm = mm; mpnt->vm_start =PAGE_MASK & (unsigned long ) bprm->p;
@@ -320,7 +320,7 @@ int ia32_setup_arg_pages(struct linux_bi mpnt->vm_page_prot = (mpnt->vm_flags &VM_EXEC ) ?PAGE_COPY_EXEC :PAGE_COPY ;if ((ret =insert_vm_struct (mm, mpnt))) { -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);kmem_cache_free (vm_area_cachep, mpnt);return ret; }
@@ -335,7 +335,7 @@ int ia32_setup_arg_pages(struct linux_bi } stack_base +=PAGE_SIZE ; } -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);return 0 ; }
arch/x86_64/ia32/sys_ia32.c +4 -4
@@ -242,12 +242,12 @@ sys32_mmap(struct mmap_arg_struct __user } mm = current->mm; -down_write (&mm->mmap_sem); +rw_mutex_write_lock (&mm->mmap_lock); retval =do_mmap_pgoff (file, a.addr, a.len, a.prot, a.flags, a.offset>>PAGE_SHIFT );if (file)fput (file); -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);return retval; }
@@ -708,9 +708,9 @@ asmlinkage long sys32_mmap2(unsigned lonreturn -EBADF ; } -down_write (&mm->mmap_sem); +rw_mutex_write_lock (&mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);if (file)fput (file);
arch/x86_64/ia32/syscall32.c +2 -2
@@ -30,7 +30,7 @@ int syscall32_setup_pages(struct linux_bstruct mm_struct *mm = current->mm;int ret; -down_write (&mm->mmap_sem); +rw_mutex_write_lock (&mm->mmap_lock);/* * MAYWRITE to allow gdb to COW and set breakpoints *
@@ -45,7 +45,7 @@ int syscall32_setup_pages(struct linux_bVM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC |VM_ALWAYSDUMP , syscall32_pages); -up_write (&mm->mmap_sem); +rw_mutex_write_unlock (&mm->mmap_lock);return ret; }
arch/x86_64/kernel/sys_x86_64.c +2 -2
@@ -54,9 +54,9 @@ asmlinkage long sys_mmap(unsigned long aif (!file) goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, off >>PAGE_SHIFT ); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/x86_64/mm/fault.c +8 -8
@@ -323,7 +323,7 @@ asmlinkage void __kprobes do_page_fault( tsk = current; mm = tsk->mm; -prefetchw (&mm->mmap_sem); +prefetchw (&mm->mmap_lock);/* get the address */ __asm__("movq %%cr2,%0" :"=r" (address));
@@ -388,7 +388,7 @@ asmlinkage void __kprobes do_page_fault(/* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the * kernel and should generate an OOPS. Unfortunatly, in the case of an -* erroneous fault occurring in a code path which already holds mmap_sem +* erroneous fault occurring in a code path which already holds mmap_lock * we will deadlock attempting to validate the fault against the * address space. Luckily the kernel only validly references user * space from well defined areas of code, which are listed in the
@@ -400,11 +400,11 @@ asmlinkage void __kprobes do_page_fault(* source. If this is invalid we can skip the address space check, * thus avoiding the deadlock. */ -if (!down_read_trylock (&mm->mmap_sem)) { +if (!rw_mutex_read_trylock (&mm->mmap_lock)) {if ((error_code &PF_USER ) ==0 && !search_exception_tables (regs->rip)) goto bad_area_nosemaphore; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); } vma =find_vma (mm, address);
@@ -463,7 +463,7 @@ good_area: goto out_of_memory; } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ;/*
@@ -471,7 +471,7 @@ good_area:* Fix it, but check if it's kernel or user first.. */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock); bad_area_nosemaphore:/* User mode accesses just cause a SIGSEGV */
@@ -556,7 +556,7 @@ no_context:* us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (current)) {yield (); goto again;
@@ -567,7 +567,7 @@ out_of_memory: goto no_context; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* Kernel mode? Handle exceptions or die */ if (!(error_code &PF_USER ))
arch/x86_64/mm/pageattr.c +5 -5
@@ -90,7 +90,7 @@ static inline void flush_map(struct liston_each_cpu (flush_kernel_map, l,1 ,1 ); } -static LIST_HEAD (deferred_pages);/* protected by init_mm.mmap_sem */ +static LIST_HEAD (deferred_pages);/* protected by init_mm.mmap_lock */ static inline void save_page (struct page *fpage) {
@@ -189,7 +189,7 @@ int change_page_attr_addr(unsigned long kernel_map =1 ; } -down_write (&init_mm.mmap_sem); +rw_mutex_write_lock (&init_mm.mmap_lock);for (i =0 ; i < numpages; i++, address +=PAGE_SIZE ) {unsigned long pfn =__pa (address) >>PAGE_SHIFT ;
@@ -210,7 +210,7 @@ int change_page_attr_addr(unsigned long PAGE_KERNEL_EXEC); } } -up_write (&init_mm.mmap_sem); +rw_mutex_write_unlock (&init_mm.mmap_lock);return err; }
@@ -226,9 +226,9 @@ void global_flush_tlb(void)struct page *pg, *next;struct list_head l; -down_read (&init_mm.mmap_sem); +rw_mutex_read_lock (&init_mm.mmap_lock);list_replace_init (&deferred_pages, &l); -up_read (&init_mm.mmap_sem); +rw_mutex_read_unlock (&init_mm.mmap_lock);flush_map (&l);
arch/xtensa/kernel/syscall.c +2 -2
@@ -72,9 +72,9 @@ asmlinkage long xtensa_mmap2(unsigned lo goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap_pgoff (file, addr, len, prot, flags, pgoff); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (file)fput (file);
arch/xtensa/mm/fault.c +6 -6
@@ -68,7 +68,7 @@ void do_page_fault(struct pt_regs *regs) address, exccause, regs->pc, is_write? "w":"", is_exec? "x":"");#endif -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); vma =find_vma (mm, address);if (!vma)
@@ -117,14 +117,14 @@ survive:BUG (); } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return ;/* Something tried to access memory that isn't in our memory map.. * Fix it, but check if it's kernel or user first.. */ bad_area: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (user_mode (regs)) { current->thread.bad_vaddr = address; current->thread.error_code = is_write;
@@ -143,10 +143,10 @@ bad_area:* us unable to handle the page fault gracefully. */ out_of_memory: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);if (is_init (current)) {yield (); -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); goto survive; }printk ("VM: killing process %s\n" , current->comm);
@@ -156,7 +156,7 @@ out_of_memory:return ; do_sigbus: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* Send a sigbus, regardless of whether we were in kernel * or user mode.
drivers/char/drm/drm_bufs.c +4 -4
@@ -1582,17 +1582,17 @@ int drm_mapbufs(struct inode *inode, str goto done; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); virtual =do_mmap (filp,0 , map->size,PROT_READ |PROT_WRITE ,MAP_SHARED , token); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }else { -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); virtual =do_mmap (filp,0 , dma->byte_count,PROT_READ |PROT_WRITE ,MAP_SHARED ,0 ); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }if (virtual >-1024UL ) {/* Real error */
drivers/char/drm/i810_dma.c +4 -4
@@ -132,7 +132,7 @@ static int i810_map_buffer(drm_buf_t * bif (buf_priv->currently_mapped ==I810_BUF_MAPPED )return -EINVAL ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); old_fops = filp->f_op; filp->f_op = &i810_buffer_fops; dev_priv->mmap_buffer = buf;
@@ -147,7 +147,7 @@ static int i810_map_buffer(drm_buf_t * b retcode =PTR_ERR (buf_priv->virtual); buf_priv->virtual =NULL ; } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);return retcode; }
@@ -160,11 +160,11 @@ static int i810_unmap_buffer(drm_buf_t *if (buf_priv->currently_mapped !=I810_BUF_MAPPED )return -EINVAL ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); retcode =do_munmap (current->mm, (unsigned long )buf_priv->virtual, (size_t ) buf->total); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); buf_priv->currently_mapped =I810_BUF_UNMAPPED ; buf_priv->virtual =NULL ;
drivers/char/drm/i830_dma.c +4 -4
@@ -135,7 +135,7 @@ static int i830_map_buffer(drm_buf_t * bif (buf_priv->currently_mapped ==I830_BUF_MAPPED )return -EINVAL ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); old_fops = filp->f_op; filp->f_op = &i830_buffer_fops; dev_priv->mmap_buffer = buf;
@@ -151,7 +151,7 @@ static int i830_map_buffer(drm_buf_t * b }else { buf_priv->virtual = (void __user *)virtual; } -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);return retcode; }
@@ -164,11 +164,11 @@ static int i830_unmap_buffer(drm_buf_t *if (buf_priv->currently_mapped !=I830_BUF_MAPPED )return -EINVAL ; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); retcode =do_munmap (current->mm, (unsigned long )buf_priv->virtual, (size_t ) buf->total); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); buf_priv->currently_mapped =I830_BUF_UNMAPPED ; buf_priv->virtual =NULL ;
drivers/char/drm/via_dmablit.c +2 -2
@@ -239,14 +239,14 @@ via_lock_all_dma_pages(drm_via_sg_info_tif (NULL == (vsg->pages =vmalloc (sizeof (struct page *) * vsg->num_pages)))return DRM_ERR (ENOMEM );memset (vsg->pages,0 ,sizeof (struct page *) * vsg->num_pages); -down_read (¤t->mm->mmap_sem); +rw_mutex_read_lock (¤t->mm->mmap_lock); ret =get_user_pages (current, current->mm, (unsigned long )xfer->mem_addr, vsg->num_pages, (vsg->direction ==DMA_FROM_DEVICE ),0 , vsg->pages,NULL ); -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock);if (ret != vsg->num_pages) {if (ret <0 )return ret;
drivers/char/mem.c +3 -3
@@ -630,7 +630,7 @@ static inline size_t read_zero_pagealign mm = current->mm;/* Oops, this was forgotten before. -ben */ -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock);/* For private mappings, just map in zero pages. */ for (vma =find_vma (mm, addr); vma; vma = vma->vm_next) {
@@ -655,7 +655,7 @@ static inline size_t read_zero_pagealign goto out_up; } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* The shared case is hard. Let's do the conventional zeroing. */ do {
@@ -669,7 +669,7 @@ static inline size_t read_zero_pagealignreturn size; out_up: -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return size; }
drivers/dma/iovlock.c +2 -2
@@ -97,7 +97,7 @@ struct dma_pinned_list *dma_pin_iovec_pa pages += page_list->nr_pages;/* pin pages down */ -down_read (¤t->mm->mmap_sem); +rw_mutex_read_lock (¤t->mm->mmap_lock); ret = get_user_pages( current, current->mm,
@@ -107,7 +107,7 @@ struct dma_pinned_list *dma_pin_iovec_pa0 ,/* force */ page_list->pages, NULL); -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock);if (ret != page_list->nr_pages) { err = -ENOMEM ;
drivers/infiniband/hw/ipath/ipath_user_pages.c +9 -9
@@ -50,7 +50,7 @@ static void __ipath_release_user_pages(s } } -/* call with current->mm->mmap_sem held */ +/* call with current->mm->mmap_lock held */ static int __get_user_pages (unsigned long start_page,size_t num_pages,struct page **p,struct vm_area_struct **vma) {
@@ -162,11 +162,11 @@ int ipath_get_user_pages(unsigned long s {int ret; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); ret =__get_user_pages (start_page, num_pages, p,NULL ); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);return ret; }
@@ -188,24 +188,24 @@ int ipath_get_user_pages_nocopy(unsignedstruct vm_area_struct *vma;int ret; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); ret =__get_user_pages (page,1 , p, &vma); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);return ret; }void ipath_release_user_pages (struct page **p,size_t num_pages) { -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);__ipath_release_user_pages (p, num_pages,1 ); current->mm->locked_vm -= num_pages; -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }struct ipath_user_pages_work {
@@ -219,9 +219,9 @@ static void user_pages_account(struct wostruct ipath_user_pages_work *work = container_of(_work, struct ipath_user_pages_work, work); -down_write (&work->mm->mmap_sem); +rw_mutex_write_lock (&work->mm->mmap_lock); work->mm->locked_vm -= work->num_pages; -up_write (&work->mm->mmap_sem); +rw_mutex_write_unlock (&work->mm->mmap_lock);mmput (work->mm);kfree (work); }
drivers/media/video/cafe_ccic.c +1 -1
@@ -1375,7 +1375,7 @@ static void cafe_v4l_vm_open(struct vm_a {struct cafe_sio_buffer *sbuf = vma->vm_private_data;/* -* Locking: done under mmap_sem, so we don't need to +* Locking: done under mmap_lock, so we don't need to * go back to the camera lock here. */ sbuf->mapcount++;
drivers/media/video/video-buf.c +2 -2
@@ -150,12 +150,12 @@ int videobuf_dma_init_user(struct videob dma->varea = (void *) data; -down_read (¤t->mm->mmap_sem); +rw_mutex_read_lock (¤t->mm->mmap_lock); err =get_user_pages (current,current->mm, data &PAGE_MASK , dma->nr_pages, rw ==READ ,1 ,/* force */ dma->pages,NULL ); -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock);if (err != dma->nr_pages) { dma->nr_pages = (err >=0 ) ? err :0 ;dprintk (1 ,"get_user_pages: err=%d [%d]\n" ,err,dma->nr_pages);
drivers/oprofile/buffer_sync.c +6 -6
@@ -81,11 +81,11 @@ static int munmap_notify(struct notifierstruct mm_struct * mm = current->mm;struct vm_area_struct * mpnt; -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock); mpnt =find_vma (mm, addr);if (mpnt && mpnt->vm_file && (mpnt->vm_flags &VM_EXEC )) { -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);/* To avoid latency problems, we only process the current CPU, * hoping that most samples for the task are on this CPU */
@@ -93,7 +93,7 @@ static int munmap_notify(struct notifierreturn 0 ; } -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);return 0 ; }
@@ -366,7 +366,7 @@ static void release_mm(struct mm_struct {if (!mm)return ; -up_read (&mm->mmap_sem); +rw_mutex_read_unlock (&mm->mmap_lock);mmput (mm); }
@@ -375,7 +375,7 @@ static struct mm_struct * take_tasks_mm( {struct mm_struct * mm =get_task_mm (task);if (mm) -down_read (&mm->mmap_sem); +rw_mutex_read_lock (&mm->mmap_lock);return mm; }
@@ -486,7 +486,7 @@ typedef enum {/* Sync one of the CPU's buffers into the global event buffer. * Here we need to go through each batch of samples punctuated -* by context switch notes, taking the task's mmap_sem and doing +* by context switch notes, taking the task's mmap_lock and doing * lookup in task->mm->mmap to convert EIP into dcookie/offset * value. */
drivers/scsi/sg.c +2 -2
@@ -1716,7 +1716,7 @@ st_map_user_pages(struct scatterlist *sgreturn -ENOMEM ;/* Try to fault in all of the necessary pages */ -down_read (¤t->mm->mmap_sem); +rw_mutex_read_lock (¤t->mm->mmap_lock);/* rw==READ means read from drive, write into memory area */ res = get_user_pages( current,
@@ -1727,7 +1727,7 @@ st_map_user_pages(struct scatterlist *sg0 ,/* don't force */ pages, NULL); -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock);/* Errors and no page mapped should return here */ if (res < nr_pages)
drivers/scsi/st.c +2 -2
@@ -4420,7 +4420,7 @@ static int sgl_map_user_pages(struct scareturn -ENOMEM ;/* Try to fault in all of the necessary pages */ -down_read (¤t->mm->mmap_sem); +rw_mutex_read_lock (¤t->mm->mmap_lock);/* rw==READ means read from drive, write into memory area */ res = get_user_pages( current,
@@ -4431,7 +4431,7 @@ static int sgl_map_user_pages(struct sca0 ,/* don't force */ pages, NULL); -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock);/* Errors and no page mapped should return here */ if (res < nr_pages)
drivers/video/pvr2fb.c +2 -2
@@ -688,10 +688,10 @@ static ssize_t pvr2fb_write(struct fb_inif (!pages)return -ENOMEM ; -down_read (¤t->mm->mmap_sem); +rw_mutex_read_lock (¤t->mm->mmap_lock); ret =get_user_pages (current, current->mm, (unsigned long )buf, nr_pages,WRITE ,0 , pages,NULL ); -up_read (¤t->mm->mmap_sem); +rw_mutex_read_unlock (¤t->mm->mmap_lock);if (ret < nr_pages) { nr_pages = ret;
fs/aio.c +5 -5
@@ -87,9 +87,9 @@ static void aio_free_ring(struct kioctxput_page (info->ring_pages[i]);if (info->mmap_size) { -down_write (&ctx->mm->mmap_sem); +rw_mutex_write_lock (&ctx->mm->mmap_lock);do_munmap (ctx->mm, info->mmap_base, info->mmap_size); -up_write (&ctx->mm->mmap_sem); +rw_mutex_write_unlock (&ctx->mm->mmap_lock); }if (info->ring_pages && info->ring_pages != info->internal_pages)
@@ -128,12 +128,12 @@ static int aio_setup_ring(struct kioctx info->mmap_size = nr_pages *PAGE_SIZE ;dprintk ("attempting mmap of %lu bytes\n" , info->mmap_size); -down_write (&ctx->mm->mmap_sem); +rw_mutex_write_lock (&ctx->mm->mmap_lock); info->mmap_base =do_mmap (NULL ,0 , info->mmap_size,PROT_READ |PROT_WRITE ,MAP_ANONYMOUS |MAP_PRIVATE ,0 );if (IS_ERR ((void *)info->mmap_base)) { -up_write (&ctx->mm->mmap_sem); +rw_mutex_write_unlock (&ctx->mm->mmap_lock); info->mmap_size =0 ;aio_free_ring (ctx);return -EAGAIN ;
@@ -143,7 +143,7 @@ static int aio_setup_ring(struct kioctx info->nr_pages =get_user_pages (current, ctx->mm, info->mmap_base, nr_pages,1 ,0 , info->ring_pages,NULL ); -up_write (&ctx->mm->mmap_sem); +rw_mutex_write_unlock (&ctx->mm->mmap_lock);if (unlikely (info->nr_pages != nr_pages)) {aio_free_ring (ctx);
fs/binfmt_aout.c +20 -20
@@ -49,9 +49,9 @@ static int set_brk(unsigned long start, end =PAGE_ALIGN (end);if (end > start) {unsigned long addr; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); addr =do_brk (start, end - start); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (BAD_ADDR (addr))return addr; }
@@ -331,14 +331,14 @@ static int load_aout_binary(struct linuxloff_t pos = fd_offset;/* Fuck me plenty... */ /* <AOL></AOL> */ -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_brk (N_TXTADDR (ex), ex.a_text); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); bprm->file->f_op->read (bprm->file, (char *)N_TXTADDR (ex), ex.a_text, &pos); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_brk (N_DATADDR (ex), ex.a_data); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); bprm->file->f_op->read (bprm->file, (char *)N_DATADDR (ex), ex.a_data, &pos); goto beyond_if;
@@ -358,9 +358,9 @@ static int load_aout_binary(struct linux pos =32 ; map_size = ex.a_text+ex.a_data;#endif -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_brk (text_addr &PAGE_MASK , map_size); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (error != (text_addr &PAGE_MASK )) {send_sig (SIGKILL , current,0 );return error;
@@ -395,9 +395,9 @@ static int load_aout_binary(struct linuxif (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK ) !=0 )) {loff_t pos = fd_offset; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (N_TXTADDR (ex), ex.a_text+ex.a_data); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); bprm->file->f_op->read (bprm->file, (char __user *)N_TXTADDR (ex), ex.a_text+ex.a_data, &pos);
@@ -407,24 +407,24 @@ static int load_aout_binary(struct linux goto beyond_if; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (bprm->file,N_TXTADDR (ex), ex.a_text,PROT_READ |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE |MAP_EXECUTABLE , fd_offset); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (error !=N_TXTADDR (ex)) {send_sig (SIGKILL , current,0 );return error; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (bprm->file,N_DATADDR (ex), ex.a_data,PROT_READ |PROT_WRITE |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE |MAP_EXECUTABLE , fd_offset + ex.a_text); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (error !=N_DATADDR (ex)) {send_sig (SIGKILL , current,0 );return error;
@@ -509,9 +509,9 @@ static int load_aout_library(struct file file->f_path.dentry->d_name.name); error_time = jiffies; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (start_addr, ex.a_text + ex.a_data + ex.a_bss); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); file->f_op->read (file, (char __user *)start_addr, ex.a_text + ex.a_data, &pos);
@@ -522,12 +522,12 @@ static int load_aout_library(struct file goto out; }/* Now use mmap to map the library into memory. */ -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (file, start_addr, ex.a_text + ex.a_data,PROT_READ |PROT_WRITE |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE ,N_TXTOFF (ex)); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); retval = error;if (error != start_addr) goto out;
@@ -535,9 +535,9 @@ static int load_aout_library(struct file len =PAGE_ALIGN (ex.a_text + ex.a_data); bss = ex.a_text + ex.a_data + ex.a_bss;if (bss > len) { -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_brk (start_addr + len, bss - len); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); retval = error;if (error != start_addr + len) goto out;
fs/binfmt_elf.c +16 -16
@@ -88,9 +88,9 @@ static int set_brk(unsigned long start, end =ELF_PAGEALIGN (end);if (end > start) {unsigned long addr; -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); addr =do_brk (start, end - start); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (BAD_ADDR (addr))return addr; }
@@ -290,7 +290,7 @@ static unsigned long elf_map(struct fileunsigned long map_addr;unsigned long pageoffset =ELF_PAGEOFFSET (eppnt->p_vaddr); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);/* mmap() will return -EINVAL if given a zero size, but a * segment with zero filesize is perfectly valid */ if (eppnt->p_filesz + pageoffset)
@@ -299,7 +299,7 @@ static unsigned long elf_map(struct file eppnt->p_offset - pageoffset); else map_addr =ELF_PAGESTART (addr); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);return (map_addr); }
@@ -435,9 +435,9 @@ static unsigned long load_elf_interp(str/* Map the last of the bss segment */ if (last_bss > elf_bss) { -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_brk (elf_bss, last_bss - elf_bss); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (BAD_ADDR (error)) goto out_close; }
@@ -477,9 +477,9 @@ static unsigned long load_aout_interp(st goto out; } -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (0 , text_data); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (!interpreter->f_op || !interpreter->f_op->read) goto out;if (interpreter->f_op->read (interpreter, addr, text_data, &offset) <0 )
@@ -487,10 +487,10 @@ static unsigned long load_aout_interp(stflush_icache_range ((unsigned long )addr, (unsigned long )addr + text_data); -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock);do_brk (ELF_PAGESTART (text_data +ELF_MIN_ALIGN -1 ), interp_ex->a_bss); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); elf_entry = interp_ex->a_entry; out:
@@ -1005,10 +1005,10 @@ static int load_elf_binary(struct linux_and some applications "depend" upon this behavior. Since we do not have the power to recompile these, we emulate the SVr4 behavior. Sigh. */ -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error =do_mmap (NULL ,0 ,PAGE_SIZE ,PROT_READ |PROT_EXEC ,MAP_FIXED |MAP_PRIVATE ,0 ); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock); }#ifdef ELF_PLAT_INIT
@@ -1104,7 +1104,7 @@ static int load_elf_library(struct file eppnt++;/* Now use mmap to map the library into memory. */ -down_write (¤t->mm->mmap_sem); +rw_mutex_write_lock (¤t->mm->mmap_lock); error = do_mmap(file, ELF_PAGESTART(eppnt->p_vaddr), (eppnt->p_filesz +
@@ -1113,7 +1113,7 @@ static int load_elf_library(struct fileMAP_FIXED |MAP_PRIVATE |MAP_DENYWRITE , (eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr))); -up_write (¤t->mm->mmap_sem); +rw_mutex_write_unlock (¤t->mm->mmap_lock);if (error !=ELF_PAGESTART (eppnt->p_vaddr)) goto out_free_ph;
@@ -1127,9 +1127,9 @@ static int load_elf_library(struct file ELF_MIN_ALIGN - 1); bss = eppnt->p_memsz + eppnt->p_vaddr;
Lines of this file are not shown. View the raw message.
kernel/futex.c +64 -64
128 lines not on this page · raw message
arch/powerpc/platforms/cell/spufs/fault.c +3 -3
Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Signature · 5 lines
-- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Part of the diff is not shown here. View the raw message.
Andrew Morton
maintainerRe: [PATCH 2/2] mm: change mmap_sem over to the scalable rw_mutex
Review
Quoted 2 lines · On Fri, 11 May 2007 15:15:43 +0200 Peter Zijlstra <[email protected]>…
(quoted diff) +1
-1- down_write(¤t->mm->mmap_sem); + rw_mutex_write_lock(¤t->mm->mmap_lock);
y'know, this is such an important lock and people have had such problems with it and so many different schemes and ideas have popped up that I'm kinda thinking that we should wrap it:
write_lock_mm(struct mm_struct *mm); write_unlock_mm(struct mm_struct *mm); read_lock_mm(struct mm_struct *mm); read_unlock_mm(struct mm_struct *mm);
so that further experimentations become easier?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 2/2] mm: change mmap_sem over to the scalable rw_mutex
(now with reply-all)
Quoted 9 lines · Andrew Morton
Review
Quoted 2 lines · On Fri, 11 May 2007 15:15:43 +0200 Peter Zijlstra <[email protected]>…
(quoted diff) +1
-1- down_write(¤t->mm->mmap_sem); + rw_mutex_write_lock(¤t->mm->mmap_lock);y'know, this is such an important lock and people have had such problems with it and so many different schemes and ideas have popped up that I'm kinda thinking that we should wrap it:
Quoted 6 lines
write_lock_mm(struct mm_struct *mm); write_unlock_mm(struct mm_struct *mm); read_lock_mm(struct mm_struct *mm); read_unlock_mm(struct mm_struct *mm);so that further experimentations become easier?
Sure, can do; it'd require a few more functions than these, but its not too many. However, what is the best way to go about such massive rename actions? Just push them through quickly, and make everybody cope?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerRe: [PATCH 2/2] mm: change mmap_sem over to the scalable rw_mutex
Quoted 17 lines · On Fri, 11 May 2007 19:12:16 +0200 Peter Zijlstra <[email protected]>…
(now with reply-all)
Quoted 14 lines · Andrew Morton
Review
Quoted 2 lines · On Fri, 11 May 2007 15:15:43 +0200 Peter Zijlstra <[email protected]>…
(quoted diff) +1
-1- down_write(¤t->mm->mmap_sem); + rw_mutex_write_lock(¤t->mm->mmap_lock);y'know, this is such an important lock and people have had such problems with it and so many different schemes and ideas have popped up that I'm kinda thinking that we should wrap it:
write_lock_mm(struct mm_struct *mm); write_unlock_mm(struct mm_struct *mm); read_lock_mm(struct mm_struct *mm); read_unlock_mm(struct mm_struct *mm);
Quoted 5 lines
Quoted 1 line
so that further experimentations become easier?
Sure, can do; it'd require a few more functions than these, but its not too many. However, what is the best way to go about such massive rename actions? Just push them through quickly, and make everybody cope?
Well, if we _do_ decide to do this (is anyone howling?) then we can do
static inline void write_lock_mm(struct mm_struct *mm)
{
down_write(&mm->mmap_sem);
}and then let the conversions trickle into the tree in an orderly fashion.
Once we think all the conversions have landed, we rename mmap_sem to _mmap_sem to avoid any backpedalling.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 2/2] mm: change mmap_sem over to the scalable rw_mutex
Quoted 26 lines · Andrew Morton
Quoted 17 lines · On Fri, 11 May 2007 19:12:16 +0200 Peter Zijlstra <[email protected]>…
(now with reply-all)
Quoted 14 lines · Andrew Morton
Review
Quoted 2 lines · On Fri, 11 May 2007 15:15:43 +0200 Peter Zijlstra <[email protected]>…
(quoted diff) +1
-1- down_write(¤t->mm->mmap_sem); + rw_mutex_write_lock(¤t->mm->mmap_lock);y'know, this is such an important lock and people have had such problems with it and so many different schemes and ideas have popped up that I'm kinda thinking that we should wrap it:
write_lock_mm(struct mm_struct *mm); write_unlock_mm(struct mm_struct *mm); read_lock_mm(struct mm_struct *mm); read_unlock_mm(struct mm_struct *mm);Quoted 5 lines
Quoted 1 line
so that further experimentations become easier?
Sure, can do; it'd require a few more functions than these, but its not too many. However, what is the best way to go about such massive rename actions? Just push them through quickly, and make everybody cope?
Quoted 6 lines
Well, if we _do_ decide to do this (is anyone howling?) then we can do
static inline void
write_lock_mm(struct mm_struct *mm){ down_write(&mm->mmap_sem); }
I think that would be fine to do.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 11 lines · Peter Zijlstra
I was toying with a scalable
rw_mutexand found that it gives ~10% reduction in system time on ebizzy runs (without theMADV_FREEpatch).2-way
x86_64pentium D box:2.6.21
/usr/bin/time./ebizzy -m -P
59.49user 137.74system 1:49.22elapsed 180%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swaps
Quoted 5 lines
2.6.21-rw_mutex
/usr/bin/time./ebizzy -m -P
57.85user 124.30system 1:42.99elapsed 176%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swaps
nice! This 6% runtime reduction on a 2-way box will i suspect get exponentially better on systems with more CPUs/cores.
i also like the design, alot: instead of doing a full new lock type (with per-arch changes, extra lockdep support, etc. etc) you layered the new abstraction ontop of mutexes. This makes this hard locking abstraction look really, really simple, while the percpu_counter trick makes it scale _perfectly_ for the reader case. Congratulations!
given how nice this looks already, have you considered completely replacing rwsems with this? I suspect you could test the correctness of that without doing a mass API changeover, by embedding struct rw_mutex in struct rwsem and implementing kernel/rwsem.c's API that way. (the real patch would just flip it all over to rw-mutexes)
Ingo
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Ingo Molnar a ecrit :
Quoted 12 lines · Peter Zijlstra
Quoted 11 lines
I was toying with a scalable
rw_mutexand found that it gives ~10% reduction in system time on ebizzy runs (without theMADV_FREEpatch).2-way
x86_64pentium D box:2.6.21
/usr/bin/time./ebizzy -m -P
59.49user 137.74system 1:49.22elapsed 180%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swaps
Quoted 7 lines
Quoted 5 lines
2.6.21-rw_mutex
/usr/bin/time./ebizzy -m -P
57.85user 124.30system 1:42.99elapsed 176%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swapsnice! This 6% runtime reduction on a 2-way box will i suspect get exponentially better on systems with more CPUs/cores.
As long you only have readers, yes.
But I personally find this new rw_mutex not scalable at all if you have some writers around.
percpu_counter_sum is just a L1 cache eater, and O(NR_CPUS)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 23 lines · Eric Dumazet
Ingo Molnar a A(C)crit :
Quoted 13 lines · Peter Zijlstra
Quoted 12 lines
I was toying with a scalable
rw_mutexand found that it gives ~10% reduction in system time on ebizzy runs (without theMADV_FREEpatch).2-way
x86_64pentium D box:2.6.21
/usr/bin/time./ebizzy -m -P
59.49user 137.74system 1:49.22elapsed 180%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swapsQuoted 8 lines
Quoted 5 lines
2.6.21-rw_mutex
/usr/bin/time./ebizzy -m -P
57.85user 124.30system 1:42.99elapsed 176%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swapsnice! This 6% runtime reduction on a 2-way box will i suspect get exponentially better on systems with more CPUs/cores.
Quoted 6 lines
As long you only have readers, yes.
But I personally find this new
rw_mutexnot scalable at all if you have some writers around.
percpu_counter_sumis just a L1 cache eater, andO(NR_CPUS)
Yeah, that is true; there are two occurences, the one in rw_mutex_read_unlock() is not strictly needed for correctness.
Write locks are indeed quite expensive. But given the ratio of reader:writer locks on mmap_sem (I'm not all that familiar with other rwsem users) this trade-off seems workable.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 5 lines · Peter Zijlstra
Quoted 4 lines · Eric Dumazet
But I personally find this new
rw_mutexnot scalable at all if you have some writers around.
Quoted 8 lines
Quoted 1 line
percpu_counter_sumis just a L1 cache eater, andO(NR_CPUS)Yeah, that is true; there are two occurences, the one in
rw_mutex_read_unlock()is not strictly needed for correctness.Write locks are indeed quite expensive. But given the ratio of reader:writer locks on
mmap_sem(I'm not all that familiar with other rwsem users) this trade-off seems workable.
I guess the problem with that logic is assuming the mmap_sem read side always needs to be scalable. Given the ratio of threaded:unthreaded apps, maybe the trade-off swings away from favour?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 11 lines · Nick Piggin
Quoted 10 lines · Peter Zijlstra
Quoted 5 lines · Eric Dumazet
But I personally find this new
rw_mutexnot scalable at all if you have some writers around.
percpu_counter_sumis just a L1 cache eater, andO(NR_CPUS)Yeah, that is true; there are two occurences, the one in
rw_mutex_read_unlock()is not strictly needed for correctness.
Quoted 7 lines
Quoted 3 lines
Write locks are indeed quite expensive. But given the ratio of reader:writer locks on
mmap_sem(I'm not all that familiar with other rwsem users) this trade-off seems workable.I guess the problem with that logic is assuming the
mmap_semread side always needs to be scalable. Given the ratio of threaded:unthreaded apps, maybe the trade-off swings away from favour?
Could be; I've been bashing my head against the wall trying to find a scalable write side solution. But so far only got a massive dent in my brain from the effort.
Perhaps I can do a similar optimistic locking for my rcu-btree as I did for the radix tree. That way most of the trouble would be endowed upon the vmas instead of the mm itself. And then it would be up to user-space to ensure it has in the order of nr_cpu_ids arenas to work in.
Also, as Hugh pointed out in an earlier thread; mmap_sem's write side also protects the page tables, so we'd need to fix that up too; assumedly the write side equivalent of the vma lock would then protect all underlying page tables....
/me drifting away, rambling incoherently,..
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 5 lines · Ingo Molnar
given how nice this looks already, have you considered completely replacing rwsems with this? I suspect you could test the correctness of that without doing a mass API changeover, by embedding struct
rw_mutexin struct rwsem and implementing kernel/rwsem.c's API that way. (the real patch would just flip it all over to rw-mutexes)
Ummmm... How much memory would that cost on large systems?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 14 lines · Ingo Molnar
Quoted 11 lines · Peter Zijlstra
I was toying with a scalable
rw_mutexand found that it gives ~10% reduction in system time on ebizzy runs (without theMADV_FREEpatch).2-way
x86_64pentium D box:2.6.21
/usr/bin/time./ebizzy -m -P
59.49user 137.74system 1:49.22elapsed 180%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swaps
Quoted 8 lines
Quoted 5 lines
2.6.21-rw_mutex
/usr/bin/time./ebizzy -m -P
57.85user 124.30system 1:42.99elapsed 176%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swapsnice! This 6% runtime reduction on a 2-way box will i suspect get exponentially better on systems with more CPUs/cores.
Is this with the MADV_DONTNEED kernel and glibc work?
Quoted 8 lines
i also like the design, alot: instead of doing a full new lock type (with per-arch changes, extra lockdep support, etc. etc) you layered the new abstraction ontop of mutexes. This makes this hard locking abstraction look really, really simple, while the
percpu_countertrick makes it scale _perfectly_ for the reader case. Congratulations!given how nice this looks already, have you considered completely replacing rwsems with this? I suspect you could test the correctness of
Not to take anything away from this lock type (because it can have its uses), but have you considered the size of this lock and its write side performance?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 17 lines · Nick Piggin
Quoted 16 lines · Ingo Molnar
Quoted 13 lines · Peter Zijlstra
I was toying with a scalable
rw_mutexand found that it gives ~10% reduction in system time on ebizzy runs (without theMADV_FREEpatch).2-way
x86_64pentium D box:2.6.21
/usr/bin/time./ebizzy -m -P
59.49user 137.74system 1:49.22elapsed 180%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swaps2.6.21-rw_mutex
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
/usr/bin/time./ebizzy -m -P
57.85user 124.30system 1:42.99elapsed 176%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+33555877minor)pagefaults 0swapsnice! This 6% runtime reduction on a 2-way box will i suspect get exponentially better on systems with more CPUs/cores.
Is this with the
MADV_DONTNEEDkernel and glibc work?
No, just the old stuff.
Quoted 7 lines
Quoted 7 lines
i also like the design, alot: instead of doing a full new lock type (with per-arch changes, extra lockdep support, etc. etc) you layered the new abstraction ontop of mutexes. This makes this hard locking abstraction look really, really simple, while the
percpu_countertrick makes it scale _perfectly_ for the reader case. Congratulations!
Quoted 6 lines
Quoted 2 lines
given how nice this looks already, have you considered completely replacing rwsems with this? I suspect you could test the correctness of
Not to take anything away from this lock type (because it can have its uses), but have you considered the size of this lock and its write side performance?
Yeah, I did, I was initially just toying about with the concept, to see if it was doable at all. I only posted because I got these fairly good numbers.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 4 lines · Peter Zijlstra
I was toying with a scalable
rw_mutexand found that it gives ~10% reduction in system time on ebizzy runs (without theMADV_FREEpatch).
You break priority enheritance on user space futexes! :-(
The problems is that the futex waiter have to take the mmap_sem. And as your rw_mutex isn't PI enabled you get priority inversions :-(
Esben
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 11 lines · Esben Nielsen
Quoted 4 lines · Peter Zijlstra
I was toying with a scalable
rw_mutexand found that it gives ~10% reduction in system time on ebizzy runs (without theMADV_FREEpatch).You break priority enheritance on user space futexes! :-( The problems is that the futex waiter have to take the
mmap_sem. And as yourrw_mutexisn't PI enabled you get priority inversions :-(
Do note that rwsems have no PI either.
PI is not a concern for mainline - yet, I do have ideas here though.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 9 lines · Peter Zijlstra
Quoted 8 lines · Esben Nielsen
Quoted 4 lines · Peter Zijlstra
I was toying with a scalable
rw_mutexand found that it gives ~10% reduction in system time on ebizzy runs (without theMADV_FREEpatch).
Quoted 8 lines
Quoted 3 lines
You break priority enheritance on user space futexes! :-( The problems is that the futex waiter have to take the
mmap_sem. And as yourrw_mutexisn't PI enabled you get priority inversions :-(Do note that rwsems have no PI either.
PI is not a concern for mainline - yet, I do have ideas here though.
If PI wasn't a concern for mainline, why is PI futexes merged into the mainline?
I notice that the rwsems used now isn't priority inversion safe (thus destroyingthe perpose of having PI futexes). We thus already have a bug in the mainline.
I suggest making a rw_mutex which does read side PI: A reader boosts the writer, but a writer can't boost the readers, since there can be a large amount of those.
I don't have time to make such a rw_mutex but I have a simple idea for one, where the rt_mutex can be reused.
struct pi_rw_mutex {
int count; /* 0 -> unoccupied,
>0 -> the number of current readers,
Second highest bit: there are a waiting writer
-1 -> A writer have it. */
struct rt_mutex mutex;}
Use atomic exchange on count.
When locking:
A writer checks if count <= 0. If so it sets the value to -1 and takes
the mutex. When it gets the mutex it rechecks the count and proceeds.
If count > 0 the writer sets the second highest bit and add itself to
the wait-list in the mutex and sleeps. (The mutex will now be in a state where owner==NULL but there are waiters. It must be cheched if the rt_mutex code can handle this.)
A reader checks if count >= 0. If so it does count++ and proceeds.
If count < 0 it takes the rtmutex. When it gets the mutex it sets the count to 1, unlocks the mutex and proceeds.
When unlocking:
The writer sets count to 0 or 0x8000000 (second highest bit) depending on how many waiters the mutex have and unlocks the mutex.
The reader checks if count is 0x80000001. If so it sets count to 0 and wakes up the first waiter on the mutex (if there are any). Otherwise it just do count--.
Esben
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 3 lines · Esben Nielsen
I notice that the rwsems used now isn't priority inversion safe (thus destroying the perpose of having PI futexes). We thus already have a bug in the mainline.
you see everything in black and white, ignoring all the grey scales! Upstream PI futexes are perfectly fine as long as the mm semaphore is not write-locked (by anyone) while the critical path is running. Given that real-time tasks often use mlockall and other practices to simplify their workload so this is not all that hard to achieve.
Quoted 3 lines
I suggest making a
rw_mutexwhich does read side PI: A reader boosts the writer, but a writer can't boost the readers, since there can be a large amount of those.
this happens automatically when you use Peter's stuff on -rt. But mainline should not be bothered with this.
Quoted 2 lines
I don't have time to make such a
rw_mutexbut I have a simple idea for one, where thert_mutexcan be reused.
Peter's stuff does this already if you remap all the mutex ops to rt_mutex ops. Which is also what happens on -rt automatically. Ok?
[ for mainline it is totally pointless and unnecessary to slow down all
MM ops via an rt_mutex, because there are so many other, much larger effects that make execution time unbound. (interrupts for example) ]
Ingo
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 7 lines · Ingo Molnar
Quoted 3 lines · Esben Nielsen
I notice that the rwsems used now isn't priority inversion safe (thus destroying the perpose of having PI futexes). We thus already have a bug in the mainline.
Quoted 6 lines
you see everything in black and white, ignoring all the grey scales! Upstream PI futexes are perfectly fine as long as the mm semaphore is not write-locked (by anyone) while the critical path is running. Given that real-time tasks often use mlockall and other practices to simplify their workload so this is not all that hard to achieve.
Yeah, after sending that mail I realized I accepted this fact way back...
But I disagree in that it is easy to avoid not write-lcling the mm semaphore: A simple malloc() might lead to a mmap() call creating trouble. Am I right?
Quoted 5 lines
Quoted 3 lines
I suggest making a
rw_mutexwhich does read side PI: A reader boosts the writer, but a writer can't boost the readers, since there can be a large amount of those.this happens automatically when you use Peter's stuff on -rt.
Because the rw_mutex is translated into a rt_mutex - as you say below.
Quoted 3 lines
But
mainline should not be bothered with this.
I disagree. You lay a large burdon on the users of PI futexes to avoid write locking the mm semaphore. PI boosting those writers would be a good idea even in the mainline.
Quoted 6 lines
Quoted 2 lines
I don't have time to make such a
rw_mutexbut I have a simple idea for one, where thert_mutexcan be reused.Peter's stuff does this already if you remap all the mutex ops to
rt_mutexops. Which is also what happens on -rt automatically. Ok?
That is how -rt works and should work. No disagrement there.
Quoted 4 lines
[ for mainline it is totally pointless and unnecessary to slow down all
MM ops via anrt_mutex, because there are so many other, much larger effects that make execution time unbound. (interrupts for example) ]
1) How much slower would the pi_rw_mutex I suggested really be? As far as I see there is only an overhead when there is congestion. I can not see that that overhead is much larger than a non-PI boosting implementation.
2) I know that execution time isn't bounded in the main-line - that is why -rt is needed. But it is _that_ bad? How low can you get your latencies with preemption on on a really busy machine?
Basically, I think PI futexes should provide the same kind of latencies as the basic thread latency. That is what the user would expect. Priority invertions should be removed, but ofcourse you can't do better than the OS does otherwise.
Quoted 2 lines
Ingo
Esben
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 4 lines · Esben Nielsen
Yeah, after sending that mail I realized I accepted this fact way back... But I disagree in that it is easy to avoid not write-lcling the mm semaphore: A simple
malloc()might lead to ammap()call creating trouble. Am I right?
yeah - that's why "hard RT" apps generally either preallocate all memory in advance, or use special, deterministic allocators. And for "soft RT" it's all a matter of degree.
Quoted 5 lines
Quoted 1 line
But mainline should not be bothered with this.
I disagree. You lay a large burdon on the users of PI futexes to avoid write locking the mm semaphore. PI boosting those writers would be a good idea even in the mainline.
only if it can be done without slowing down all the much more important uses of the MM semaphore.
Quoted 4 lines
1) How much slower would the
pi_rw_mutexI suggested really be? As far as I see there is only an overhead when there is congestion. I can not see that that overhead is much larger than a non-PI boosting implementation.
it could be measured, but it's certainly not going to be zero.
Quoted 3 lines
2) I know that execution time isn't bounded in the main-line - that is why -rt is needed. But it is _that_ bad? How low can you get your latencies with preemption on on a really busy machine?
on mainline? It can get arbitrarily large (read: seconds) in essence.
Ingo
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Esben Nielsen a ecrit :
Quoted 17 lines · Peter Zijlstra
Quoted 14 lines · Esben Nielsen
Quoted 11 lines · Peter Zijlstra
Quoted 5 lines
I was toying with a scalable
rw_mutexand found that it gives ~10% reduction in system time on ebizzy runs (without theMADV_FREEpatch).You break priority enheritance on user space futexes! :-( The problems is that the futex waiter have to take the
mmap_sem. And as yourrw_mutexisn't PI enabled you get priority inversions :-(
Quoted 5 lines
Quoted 4 lines
Do note that rwsems have no PI either.
PI is not a concern for mainline - yet, I do have ideas here though.If PI wasn't a concern for mainline, why is PI futexes merged into the mainline?
If you really care about futexes and mmap_sem, just use private futexes, since they dont use mmap_sem at all.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 20 lines · Eric Dumazet
Quoted 19 lines · Esben Nielsen
Quoted 15 lines · Peter Zijlstra
Quoted 13 lines · Esben Nielsen
Quoted 5 lines · Peter Zijlstra
I was toying with a scalable
rw_mutexand found that it gives ~10% reduction in
system time on ebizzy runs (without theMADV_FREEpatch).You break priority enheritance on user space futexes! :-( The problems is that the futex waiter have to take the
mmap_sem. And as
yourrw_mutexisn't PI enabled you get priority inversions :-(
Quoted 9 lines
Quoted 6 lines
Quoted 4 lines
Do note that rwsems have no PI either.
PI is not a concern for mainline - yet, I do have ideas here though.If PI wasn't a concern for mainline, why is PI futexes merged into the mainline?
If you really care about futexes and
mmap_sem, just use private futexes, since they dont usemmap_semat all.
futex_wait_pi() takes mmap_sem. So does futex_fd(). I can't see a code path into the PI futexes which doesn't use mmap_sem.
There is another way to avoid problems with mmap_sem:
Use shared memory for data you need to share with high priority tasks and
normal low priority tasks there. The high priority task(s) run(s) in an isolated high priority process having its own mmap_sem. This high priority process is mlock'ed and doesn't do any operations write locking mmap_sem.
But it would be nice if you can avoid such a cumbersome workaround...
Esben