diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-16 16:41:02 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-16 16:41:02 -0400 |
commit | 3469d261eac65912927dca13ee8f77c744ad7aa2 (patch) | |
tree | 09b25f80c065d52ee6e158c189ba44bb28bb76fc | |
parent | 1c19b68a279c58d6da4379bf8b6d679a300a1daf (diff) | |
parent | 4544ba8c6b1743499cabb682897a469911845f15 (diff) |
Merge branch 'locking-rwsem-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull support for killable rwsems from Ingo Molnar:
"This, by Michal Hocko, implements down_write_killable().
The main usecase will be to update mm_sem usage sites to use this new
API, to allow the mm-reaper introduced in commit aac453635549 ("mm,
oom: introduce oom reaper") to tear down oom victim address spaces
asynchronously with minimum latencies and without deadlock worries"
[ The vfs will want it too as the inode lock is changed from a mutex to
a rwsem due to the parallel lookup and readdir updates ]
* 'locking-rwsem-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
locking/rwsem: Fix comment on register clobbering
locking/rwsem: Fix down_write_killable()
locking/rwsem, x86: Add frame annotation for call_rwsem_down_write_failed_killable()
locking/rwsem: Provide down_write_killable()
locking/rwsem, x86: Provide __down_write_killable()
locking/rwsem, s390: Provide __down_write_killable()
locking/rwsem, ia64: Provide __down_write_killable()
locking/rwsem, alpha: Provide __down_write_killable()
locking/rwsem: Introduce basis for down_write_killable()
locking/rwsem, sparc: Drop superfluous arch specific implementation
locking/rwsem, sh: Drop superfluous arch specific implementation
locking/rwsem, xtensa: Drop superfluous arch specific implementation
locking/rwsem: Drop explicit memory barriers
locking/rwsem: Get rid of __down_write_nested()
-rw-r--r-- | arch/alpha/include/asm/rwsem.h | 18 | ||||
-rw-r--r-- | arch/ia64/include/asm/rwsem.h | 22 | ||||
-rw-r--r-- | arch/s390/include/asm/rwsem.h | 18 | ||||
-rw-r--r-- | arch/sh/include/asm/Kbuild | 1 | ||||
-rw-r--r-- | arch/sh/include/asm/rwsem.h | 132 | ||||
-rw-r--r-- | arch/sparc/include/asm/Kbuild | 1 | ||||
-rw-r--r-- | arch/sparc/include/asm/rwsem.h | 124 | ||||
-rw-r--r-- | arch/x86/include/asm/rwsem.h | 42 | ||||
-rw-r--r-- | arch/x86/lib/rwsem.S | 16 | ||||
-rw-r--r-- | arch/xtensa/include/asm/Kbuild | 1 | ||||
-rw-r--r-- | arch/xtensa/include/asm/rwsem.h | 131 | ||||
-rw-r--r-- | include/asm-generic/rwsem.h | 13 | ||||
-rw-r--r-- | include/linux/lockdep.h | 15 | ||||
-rw-r--r-- | include/linux/rwsem-spinlock.h | 2 | ||||
-rw-r--r-- | include/linux/rwsem.h | 3 | ||||
-rw-r--r-- | kernel/locking/rwsem-spinlock.c | 19 | ||||
-rw-r--r-- | kernel/locking/rwsem-xadd.c | 38 | ||||
-rw-r--r-- | kernel/locking/rwsem.c | 19 |
18 files changed, 189 insertions, 426 deletions
diff --git a/arch/alpha/include/asm/rwsem.h b/arch/alpha/include/asm/rwsem.h index a83bbea62c67..0131a7058778 100644 --- a/arch/alpha/include/asm/rwsem.h +++ b/arch/alpha/include/asm/rwsem.h | |||
@@ -63,7 +63,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem) | |||
63 | return res >= 0 ? 1 : 0; | 63 | return res >= 0 ? 1 : 0; |
64 | } | 64 | } |
65 | 65 | ||
66 | static inline void __down_write(struct rw_semaphore *sem) | 66 | static inline long ___down_write(struct rw_semaphore *sem) |
67 | { | 67 | { |
68 | long oldcount; | 68 | long oldcount; |
69 | #ifndef CONFIG_SMP | 69 | #ifndef CONFIG_SMP |
@@ -83,10 +83,24 @@ static inline void __down_write(struct rw_semaphore *sem) | |||
83 | :"=&r" (oldcount), "=m" (sem->count), "=&r" (temp) | 83 | :"=&r" (oldcount), "=m" (sem->count), "=&r" (temp) |
84 | :"Ir" (RWSEM_ACTIVE_WRITE_BIAS), "m" (sem->count) : "memory"); | 84 | :"Ir" (RWSEM_ACTIVE_WRITE_BIAS), "m" (sem->count) : "memory"); |
85 | #endif | 85 | #endif |
86 | if (unlikely(oldcount)) | 86 | return oldcount; |
87 | } | ||
88 | |||
89 | static inline void __down_write(struct rw_semaphore *sem) | ||
90 | { | ||
91 | if (unlikely(___down_write(sem))) | ||
87 | rwsem_down_write_failed(sem); | 92 | rwsem_down_write_failed(sem); |
88 | } | 93 | } |
89 | 94 | ||
95 | static inline int __down_write_killable(struct rw_semaphore *sem) | ||
96 | { | ||
97 | if (unlikely(___down_write(sem))) | ||
98 | if (IS_ERR(rwsem_down_write_failed_killable(sem))) | ||
99 | return -EINTR; | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
90 | /* | 104 | /* |
91 | * trylock for writing -- returns 1 if successful, 0 if contention | 105 | * trylock for writing -- returns 1 if successful, 0 if contention |
92 | */ | 106 | */ |
diff --git a/arch/ia64/include/asm/rwsem.h b/arch/ia64/include/asm/rwsem.h index ce112472bdd6..8b23e070b844 100644 --- a/arch/ia64/include/asm/rwsem.h +++ b/arch/ia64/include/asm/rwsem.h | |||
@@ -49,8 +49,8 @@ __down_read (struct rw_semaphore *sem) | |||
49 | /* | 49 | /* |
50 | * lock for writing | 50 | * lock for writing |
51 | */ | 51 | */ |
52 | static inline void | 52 | static inline long |
53 | __down_write (struct rw_semaphore *sem) | 53 | ___down_write (struct rw_semaphore *sem) |
54 | { | 54 | { |
55 | long old, new; | 55 | long old, new; |
56 | 56 | ||
@@ -59,10 +59,26 @@ __down_write (struct rw_semaphore *sem) | |||
59 | new = old + RWSEM_ACTIVE_WRITE_BIAS; | 59 | new = old + RWSEM_ACTIVE_WRITE_BIAS; |
60 | } while (cmpxchg_acq(&sem->count, old, new) != old); | 60 | } while (cmpxchg_acq(&sem->count, old, new) != old); |
61 | 61 | ||
62 | if (old != 0) | 62 | return old; |
63 | } | ||
64 | |||
65 | static inline void | ||
66 | __down_write (struct rw_semaphore *sem) | ||
67 | { | ||
68 | if (___down_write(sem)) | ||
63 | rwsem_down_write_failed(sem); | 69 | rwsem_down_write_failed(sem); |
64 | } | 70 | } |
65 | 71 | ||
72 | static inline int | ||
73 | __down_write_killable (struct rw_semaphore *sem) | ||
74 | { | ||
75 | if (___down_write(sem)) | ||
76 | if (IS_ERR(rwsem_down_write_failed_killable(sem))) | ||
77 | return -EINTR; | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
66 | /* | 82 | /* |
67 | * unlock after reading | 83 | * unlock after reading |
68 | */ | 84 | */ |
diff --git a/arch/s390/include/asm/rwsem.h b/arch/s390/include/asm/rwsem.h index fead491dfc28..c75e4471e618 100644 --- a/arch/s390/include/asm/rwsem.h +++ b/arch/s390/include/asm/rwsem.h | |||
@@ -90,7 +90,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem) | |||
90 | /* | 90 | /* |
91 | * lock for writing | 91 | * lock for writing |
92 | */ | 92 | */ |
93 | static inline void __down_write_nested(struct rw_semaphore *sem, int subclass) | 93 | static inline long ___down_write(struct rw_semaphore *sem) |
94 | { | 94 | { |
95 | signed long old, new, tmp; | 95 | signed long old, new, tmp; |
96 | 96 | ||
@@ -104,13 +104,23 @@ static inline void __down_write_nested(struct rw_semaphore *sem, int subclass) | |||
104 | : "=&d" (old), "=&d" (new), "=Q" (sem->count) | 104 | : "=&d" (old), "=&d" (new), "=Q" (sem->count) |
105 | : "Q" (sem->count), "m" (tmp) | 105 | : "Q" (sem->count), "m" (tmp) |
106 | : "cc", "memory"); | 106 | : "cc", "memory"); |
107 | if (old != 0) | 107 | |
108 | rwsem_down_write_failed(sem); | 108 | return old; |
109 | } | 109 | } |
110 | 110 | ||
111 | static inline void __down_write(struct rw_semaphore *sem) | 111 | static inline void __down_write(struct rw_semaphore *sem) |
112 | { | 112 | { |
113 | __down_write_nested(sem, 0); | 113 | if (___down_write(sem)) |
114 | rwsem_down_write_failed(sem); | ||
115 | } | ||
116 | |||
117 | static inline int __down_write_killable(struct rw_semaphore *sem) | ||
118 | { | ||
119 | if (___down_write(sem)) | ||
120 | if (IS_ERR(rwsem_down_write_failed_killable(sem))) | ||
121 | return -EINTR; | ||
122 | |||
123 | return 0; | ||
114 | } | 124 | } |
115 | 125 | ||
116 | /* | 126 | /* |
diff --git a/arch/sh/include/asm/Kbuild b/arch/sh/include/asm/Kbuild index a319745a7b63..751c3373a92c 100644 --- a/arch/sh/include/asm/Kbuild +++ b/arch/sh/include/asm/Kbuild | |||
@@ -26,6 +26,7 @@ generic-y += percpu.h | |||
26 | generic-y += poll.h | 26 | generic-y += poll.h |
27 | generic-y += preempt.h | 27 | generic-y += preempt.h |
28 | generic-y += resource.h | 28 | generic-y += resource.h |
29 | generic-y += rwsem.h | ||
29 | generic-y += sembuf.h | 30 | generic-y += sembuf.h |
30 | generic-y += serial.h | 31 | generic-y += serial.h |
31 | generic-y += shmbuf.h | 32 | generic-y += shmbuf.h |
diff --git a/arch/sh/include/asm/rwsem.h b/arch/sh/include/asm/rwsem.h deleted file mode 100644 index edab57265293..000000000000 --- a/arch/sh/include/asm/rwsem.h +++ /dev/null | |||
@@ -1,132 +0,0 @@ | |||
1 | /* | ||
2 | * include/asm-sh/rwsem.h: R/W semaphores for SH using the stuff | ||
3 | * in lib/rwsem.c. | ||
4 | */ | ||
5 | |||
6 | #ifndef _ASM_SH_RWSEM_H | ||
7 | #define _ASM_SH_RWSEM_H | ||
8 | |||
9 | #ifndef _LINUX_RWSEM_H | ||
10 | #error "please don't include asm/rwsem.h directly, use linux/rwsem.h instead" | ||
11 | #endif | ||
12 | |||
13 | #ifdef __KERNEL__ | ||
14 | |||
15 | #define RWSEM_UNLOCKED_VALUE 0x00000000 | ||
16 | #define RWSEM_ACTIVE_BIAS 0x00000001 | ||
17 | #define RWSEM_ACTIVE_MASK 0x0000ffff | ||
18 | #define RWSEM_WAITING_BIAS (-0x00010000) | ||
19 | #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS | ||
20 | #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) | ||
21 | |||
22 | /* | ||
23 | * lock for reading | ||
24 | */ | ||
25 | static inline void __down_read(struct rw_semaphore *sem) | ||
26 | { | ||
27 | if (atomic_inc_return((atomic_t *)(&sem->count)) > 0) | ||
28 | smp_wmb(); | ||
29 | else | ||
30 | rwsem_down_read_failed(sem); | ||
31 | } | ||
32 | |||
33 | static inline int __down_read_trylock(struct rw_semaphore *sem) | ||
34 | { | ||
35 | int tmp; | ||
36 | |||
37 | while ((tmp = sem->count) >= 0) { | ||
38 | if (tmp == cmpxchg(&sem->count, tmp, | ||
39 | tmp + RWSEM_ACTIVE_READ_BIAS)) { | ||
40 | smp_wmb(); | ||
41 | return 1; | ||
42 | } | ||
43 | } | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * lock for writing | ||
49 | */ | ||
50 | static inline void __down_write(struct rw_semaphore *sem) | ||
51 | { | ||
52 | int tmp; | ||
53 | |||
54 | tmp = atomic_add_return(RWSEM_ACTIVE_WRITE_BIAS, | ||
55 | (atomic_t *)(&sem->count)); | ||
56 | if (tmp == RWSEM_ACTIVE_WRITE_BIAS) | ||
57 | smp_wmb(); | ||
58 | else | ||
59 | rwsem_down_write_failed(sem); | ||
60 | } | ||
61 | |||
62 | static inline int __down_write_trylock(struct rw_semaphore *sem) | ||
63 | { | ||
64 | int tmp; | ||
65 | |||
66 | tmp = cmpxchg(&sem->count, RWSEM_UNLOCKED_VALUE, | ||
67 | RWSEM_ACTIVE_WRITE_BIAS); | ||
68 | smp_wmb(); | ||
69 | return tmp == RWSEM_UNLOCKED_VALUE; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * unlock after reading | ||
74 | */ | ||
75 | static inline void __up_read(struct rw_semaphore *sem) | ||
76 | { | ||
77 | int tmp; | ||
78 | |||
79 | smp_wmb(); | ||
80 | tmp = atomic_dec_return((atomic_t *)(&sem->count)); | ||
81 | if (tmp < -1 && (tmp & RWSEM_ACTIVE_MASK) == 0) | ||
82 | rwsem_wake(sem); | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * unlock after writing | ||
87 | */ | ||
88 | static inline void __up_write(struct rw_semaphore *sem) | ||
89 | { | ||
90 | smp_wmb(); | ||
91 | if (atomic_sub_return(RWSEM_ACTIVE_WRITE_BIAS, | ||
92 | (atomic_t *)(&sem->count)) < 0) | ||
93 | rwsem_wake(sem); | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * implement atomic add functionality | ||
98 | */ | ||
99 | static inline void rwsem_atomic_add(int delta, struct rw_semaphore *sem) | ||
100 | { | ||
101 | atomic_add(delta, (atomic_t *)(&sem->count)); | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * downgrade write lock to read lock | ||
106 | */ | ||
107 | static inline void __downgrade_write(struct rw_semaphore *sem) | ||
108 | { | ||
109 | int tmp; | ||
110 | |||
111 | smp_wmb(); | ||
112 | tmp = atomic_add_return(-RWSEM_WAITING_BIAS, (atomic_t *)(&sem->count)); | ||
113 | if (tmp < 0) | ||
114 | rwsem_downgrade_wake(sem); | ||
115 | } | ||
116 | |||
117 | static inline void __down_write_nested(struct rw_semaphore *sem, int subclass) | ||
118 | { | ||
119 | __down_write(sem); | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * implement exchange and add functionality | ||
124 | */ | ||
125 | static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem) | ||
126 | { | ||
127 | smp_mb(); | ||
128 | return atomic_add_return(delta, (atomic_t *)(&sem->count)); | ||
129 | } | ||
130 | |||
131 | #endif /* __KERNEL__ */ | ||
132 | #endif /* _ASM_SH_RWSEM_H */ | ||
diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild index e928618838bc..6024c26c0585 100644 --- a/arch/sparc/include/asm/Kbuild +++ b/arch/sparc/include/asm/Kbuild | |||
@@ -16,6 +16,7 @@ generic-y += mm-arch-hooks.h | |||
16 | generic-y += module.h | 16 | generic-y += module.h |
17 | generic-y += mutex.h | 17 | generic-y += mutex.h |
18 | generic-y += preempt.h | 18 | generic-y += preempt.h |
19 | generic-y += rwsem.h | ||
19 | generic-y += serial.h | 20 | generic-y += serial.h |
20 | generic-y += trace_clock.h | 21 | generic-y += trace_clock.h |
21 | generic-y += types.h | 22 | generic-y += types.h |
diff --git a/arch/sparc/include/asm/rwsem.h b/arch/sparc/include/asm/rwsem.h deleted file mode 100644 index 069bf4d663a1..000000000000 --- a/arch/sparc/include/asm/rwsem.h +++ /dev/null | |||
@@ -1,124 +0,0 @@ | |||
1 | /* | ||
2 | * rwsem.h: R/W semaphores implemented using CAS | ||
3 | * | ||
4 | * Written by David S. Miller (davem@redhat.com), 2001. | ||
5 | * Derived from asm-i386/rwsem.h | ||
6 | */ | ||
7 | #ifndef _SPARC64_RWSEM_H | ||
8 | #define _SPARC64_RWSEM_H | ||
9 | |||
10 | #ifndef _LINUX_RWSEM_H | ||
11 | #error "please don't include asm/rwsem.h directly, use linux/rwsem.h instead" | ||
12 | #endif | ||
13 | |||
14 | #ifdef __KERNEL__ | ||
15 | |||
16 | #define RWSEM_UNLOCKED_VALUE 0x00000000L | ||
17 | #define RWSEM_ACTIVE_BIAS 0x00000001L | ||
18 | #define RWSEM_ACTIVE_MASK 0xffffffffL | ||
19 | #define RWSEM_WAITING_BIAS (-RWSEM_ACTIVE_MASK-1) | ||
20 | #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS | ||
21 | #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) | ||
22 | |||
23 | /* | ||
24 | * lock for reading | ||
25 | */ | ||
26 | static inline void __down_read(struct rw_semaphore *sem) | ||
27 | { | ||
28 | if (unlikely(atomic64_inc_return((atomic64_t *)(&sem->count)) <= 0L)) | ||
29 | rwsem_down_read_failed(sem); | ||
30 | } | ||
31 | |||
32 | static inline int __down_read_trylock(struct rw_semaphore *sem) | ||
33 | { | ||
34 | long tmp; | ||
35 | |||
36 | while ((tmp = sem->count) >= 0L) { | ||
37 | if (tmp == cmpxchg(&sem->count, tmp, | ||
38 | tmp + RWSEM_ACTIVE_READ_BIAS)) { | ||
39 | return 1; | ||
40 | } | ||
41 | } | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | /* | ||
46 | * lock for writing | ||
47 | */ | ||
48 | static inline void __down_write_nested(struct rw_semaphore *sem, int subclass) | ||
49 | { | ||
50 | long tmp; | ||
51 | |||
52 | tmp = atomic64_add_return(RWSEM_ACTIVE_WRITE_BIAS, | ||
53 | (atomic64_t *)(&sem->count)); | ||
54 | if (unlikely(tmp != RWSEM_ACTIVE_WRITE_BIAS)) | ||
55 | rwsem_down_write_failed(sem); | ||
56 | } | ||
57 | |||
58 | static inline void __down_write(struct rw_semaphore *sem) | ||
59 | { | ||
60 | __down_write_nested(sem, 0); | ||
61 | } | ||
62 | |||
63 | static inline int __down_write_trylock(struct rw_semaphore *sem) | ||
64 | { | ||
65 | long tmp; | ||
66 | |||
67 | tmp = cmpxchg(&sem->count, RWSEM_UNLOCKED_VALUE, | ||
68 | RWSEM_ACTIVE_WRITE_BIAS); | ||
69 | return tmp == RWSEM_UNLOCKED_VALUE; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * unlock after reading | ||
74 | */ | ||
75 | static inline void __up_read(struct rw_semaphore *sem) | ||
76 | { | ||
77 | long tmp; | ||
78 | |||
79 | tmp = atomic64_dec_return((atomic64_t *)(&sem->count)); | ||
80 | if (unlikely(tmp < -1L && (tmp & RWSEM_ACTIVE_MASK) == 0L)) | ||
81 | rwsem_wake(sem); | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * unlock after writing | ||
86 | */ | ||
87 | static inline void __up_write(struct rw_semaphore *sem) | ||
88 | { | ||
89 | if (unlikely(atomic64_sub_return(RWSEM_ACTIVE_WRITE_BIAS, | ||
90 | (atomic64_t *)(&sem->count)) < 0L)) | ||
91 | rwsem_wake(sem); | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * implement atomic add functionality | ||
96 | */ | ||
97 | static inline void rwsem_atomic_add(long delta, struct rw_semaphore *sem) | ||
98 | { | ||
99 | atomic64_add(delta, (atomic64_t *)(&sem->count)); | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * downgrade write lock to read lock | ||
104 | */ | ||
105 | static inline void __downgrade_write(struct rw_semaphore *sem) | ||
106 | { | ||
107 | long tmp; | ||
108 | |||
109 | tmp = atomic64_add_return(-RWSEM_WAITING_BIAS, (atomic64_t *)(&sem->count)); | ||
110 | if (tmp < 0L) | ||
111 | rwsem_downgrade_wake(sem); | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * implement exchange and add functionality | ||
116 | */ | ||
117 | static inline long rwsem_atomic_update(long delta, struct rw_semaphore *sem) | ||
118 | { | ||
119 | return atomic64_add_return(delta, (atomic64_t *)(&sem->count)); | ||
120 | } | ||
121 | |||
122 | #endif /* __KERNEL__ */ | ||
123 | |||
124 | #endif /* _SPARC64_RWSEM_H */ | ||
diff --git a/arch/x86/include/asm/rwsem.h b/arch/x86/include/asm/rwsem.h index ceec86eb68e9..453744c1d347 100644 --- a/arch/x86/include/asm/rwsem.h +++ b/arch/x86/include/asm/rwsem.h | |||
@@ -99,26 +99,36 @@ static inline int __down_read_trylock(struct rw_semaphore *sem) | |||
99 | /* | 99 | /* |
100 | * lock for writing | 100 | * lock for writing |
101 | */ | 101 | */ |
102 | static inline void __down_write_nested(struct rw_semaphore *sem, int subclass) | 102 | #define ____down_write(sem, slow_path) \ |
103 | ({ \ | ||
104 | long tmp; \ | ||
105 | struct rw_semaphore* ret; \ | ||
106 | asm volatile("# beginning down_write\n\t" \ | ||
107 | LOCK_PREFIX " xadd %1,(%3)\n\t" \ | ||
108 | /* adds 0xffff0001, returns the old value */ \ | ||
109 | " test " __ASM_SEL(%w1,%k1) "," __ASM_SEL(%w1,%k1) "\n\t" \ | ||
110 | /* was the active mask 0 before? */\ | ||
111 | " jz 1f\n" \ | ||
112 | " call " slow_path "\n" \ | ||
113 | "1:\n" \ | ||
114 | "# ending down_write" \ | ||
115 | : "+m" (sem->count), "=d" (tmp), "=a" (ret) \ | ||
116 | : "a" (sem), "1" (RWSEM_ACTIVE_WRITE_BIAS) \ | ||
117 | : "memory", "cc"); \ | ||
118 | ret; \ | ||
119 | }) | ||
120 | |||
121 | static inline void __down_write(struct rw_semaphore *sem) | ||
103 | { | 122 | { |
104 | long tmp; | 123 | ____down_write(sem, "call_rwsem_down_write_failed"); |
105 | asm volatile("# beginning down_write\n\t" | ||
106 | LOCK_PREFIX " xadd %1,(%2)\n\t" | ||
107 | /* adds 0xffff0001, returns the old value */ | ||
108 | " test " __ASM_SEL(%w1,%k1) "," __ASM_SEL(%w1,%k1) "\n\t" | ||
109 | /* was the active mask 0 before? */ | ||
110 | " jz 1f\n" | ||
111 | " call call_rwsem_down_write_failed\n" | ||
112 | "1:\n" | ||
113 | "# ending down_write" | ||
114 | : "+m" (sem->count), "=d" (tmp) | ||
115 | : "a" (sem), "1" (RWSEM_ACTIVE_WRITE_BIAS) | ||
116 | : "memory", "cc"); | ||
117 | } | 124 | } |
118 | 125 | ||
119 | static inline void __down_write(struct rw_semaphore *sem) | 126 | static inline int __down_write_killable(struct rw_semaphore *sem) |
120 | { | 127 | { |
121 | __down_write_nested(sem, 0); | 128 | if (IS_ERR(____down_write(sem, "call_rwsem_down_write_failed_killable"))) |
129 | return -EINTR; | ||
130 | |||
131 | return 0; | ||
122 | } | 132 | } |
123 | 133 | ||
124 | /* | 134 | /* |
diff --git a/arch/x86/lib/rwsem.S b/arch/x86/lib/rwsem.S index be110efa0096..bf2c6074efd2 100644 --- a/arch/x86/lib/rwsem.S +++ b/arch/x86/lib/rwsem.S | |||
@@ -29,8 +29,10 @@ | |||
29 | * there is contention on the semaphore. | 29 | * there is contention on the semaphore. |
30 | * | 30 | * |
31 | * %eax contains the semaphore pointer on entry. Save the C-clobbered | 31 | * %eax contains the semaphore pointer on entry. Save the C-clobbered |
32 | * registers (%eax, %edx and %ecx) except %eax whish is either a return | 32 | * registers (%eax, %edx and %ecx) except %eax which is either a return |
33 | * value or just clobbered.. | 33 | * value or just gets clobbered. Same is true for %edx so make sure GCC |
34 | * reloads it after the slow path, by making it hold a temporary, for | ||
35 | * example see ____down_write(). | ||
34 | */ | 36 | */ |
35 | 37 | ||
36 | #define save_common_regs \ | 38 | #define save_common_regs \ |
@@ -106,6 +108,16 @@ ENTRY(call_rwsem_down_write_failed) | |||
106 | ret | 108 | ret |
107 | ENDPROC(call_rwsem_down_write_failed) | 109 | ENDPROC(call_rwsem_down_write_failed) |
108 | 110 | ||
111 | ENTRY(call_rwsem_down_write_failed_killable) | ||
112 | FRAME_BEGIN | ||
113 | save_common_regs | ||
114 | movq %rax,%rdi | ||
115 | call rwsem_down_write_failed_killable | ||
116 | restore_common_regs | ||
117 | FRAME_END | ||
118 | ret | ||
119 | ENDPROC(call_rwsem_down_write_failed_killable) | ||
120 | |||
109 | ENTRY(call_rwsem_wake) | 121 | ENTRY(call_rwsem_wake) |
110 | FRAME_BEGIN | 122 | FRAME_BEGIN |
111 | /* do nothing if still outstanding active readers */ | 123 | /* do nothing if still outstanding active readers */ |
diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild index b56855a1382a..28cf4c5d65ef 100644 --- a/arch/xtensa/include/asm/Kbuild +++ b/arch/xtensa/include/asm/Kbuild | |||
@@ -22,6 +22,7 @@ generic-y += mm-arch-hooks.h | |||
22 | generic-y += percpu.h | 22 | generic-y += percpu.h |
23 | generic-y += preempt.h | 23 | generic-y += preempt.h |
24 | generic-y += resource.h | 24 | generic-y += resource.h |
25 | generic-y += rwsem.h | ||
25 | generic-y += sections.h | 26 | generic-y += sections.h |
26 | generic-y += siginfo.h | 27 | generic-y += siginfo.h |
27 | generic-y += statfs.h | 28 | generic-y += statfs.h |
diff --git a/arch/xtensa/include/asm/rwsem.h b/arch/xtensa/include/asm/rwsem.h deleted file mode 100644 index 249619e7e7f2..000000000000 --- a/arch/xtensa/include/asm/rwsem.h +++ /dev/null | |||
@@ -1,131 +0,0 @@ | |||
1 | /* | ||
2 | * include/asm-xtensa/rwsem.h | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * Largely copied from include/asm-ppc/rwsem.h | ||
9 | * | ||
10 | * Copyright (C) 2001 - 2005 Tensilica Inc. | ||
11 | */ | ||
12 | |||
13 | #ifndef _XTENSA_RWSEM_H | ||
14 | #define _XTENSA_RWSEM_H | ||
15 | |||
16 | #ifndef _LINUX_RWSEM_H | ||
17 | #error "Please don't include <asm/rwsem.h> directly, use <linux/rwsem.h> instead." | ||
18 | #endif | ||
19 | |||
20 | #define RWSEM_UNLOCKED_VALUE 0x00000000 | ||
21 | #define RWSEM_ACTIVE_BIAS 0x00000001 | ||
22 | #define RWSEM_ACTIVE_MASK 0x0000ffff | ||
23 | #define RWSEM_WAITING_BIAS (-0x00010000) | ||
24 | #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS | ||
25 | #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) | ||
26 | |||
27 | /* | ||
28 | * lock for reading | ||
29 | */ | ||
30 | static inline void __down_read(struct rw_semaphore *sem) | ||
31 | { | ||
32 | if (atomic_add_return(1,(atomic_t *)(&sem->count)) > 0) | ||
33 | smp_wmb(); | ||
34 | else | ||
35 | rwsem_down_read_failed(sem); | ||
36 | } | ||
37 | |||
38 | static inline int __down_read_trylock(struct rw_semaphore *sem) | ||
39 | { | ||
40 | int tmp; | ||
41 | |||
42 | while ((tmp = sem->count) >= 0) { | ||
43 | if (tmp == cmpxchg(&sem->count, tmp, | ||
44 | tmp + RWSEM_ACTIVE_READ_BIAS)) { | ||
45 | smp_wmb(); | ||
46 | return 1; | ||
47 | } | ||
48 | } | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * lock for writing | ||
54 | */ | ||
55 | static inline void __down_write(struct rw_semaphore *sem) | ||
56 | { | ||
57 | int tmp; | ||
58 | |||
59 | tmp = atomic_add_return(RWSEM_ACTIVE_WRITE_BIAS, | ||
60 | (atomic_t *)(&sem->count)); | ||
61 | if (tmp == RWSEM_ACTIVE_WRITE_BIAS) | ||
62 | smp_wmb(); | ||
63 | else | ||
64 | rwsem_down_write_failed(sem); | ||
65 | } | ||
66 | |||
67 | static inline int __down_write_trylock(struct rw_semaphore *sem) | ||
68 | { | ||
69 | int tmp; | ||
70 | |||
71 | tmp = cmpxchg(&sem->count, RWSEM_UNLOCKED_VALUE, | ||
72 | RWSEM_ACTIVE_WRITE_BIAS); | ||
73 | smp_wmb(); | ||
74 | return tmp == RWSEM_UNLOCKED_VALUE; | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * unlock after reading | ||
79 | */ | ||
80 | static inline void __up_read(struct rw_semaphore *sem) | ||
81 | { | ||
82 | int tmp; | ||
83 | |||
84 | smp_wmb(); | ||
85 | tmp = atomic_sub_return(1,(atomic_t *)(&sem->count)); | ||
86 | if (tmp < -1 && (tmp & RWSEM_ACTIVE_MASK) == 0) | ||
87 | rwsem_wake(sem); | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * unlock after writing | ||
92 | */ | ||
93 | static inline void __up_write(struct rw_semaphore *sem) | ||
94 | { | ||
95 | smp_wmb(); | ||
96 | if (atomic_sub_return(RWSEM_ACTIVE_WRITE_BIAS, | ||
97 | (atomic_t *)(&sem->count)) < 0) | ||
98 | rwsem_wake(sem); | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * implement atomic add functionality | ||
103 | */ | ||
104 | static inline void rwsem_atomic_add(int delta, struct rw_semaphore *sem) | ||
105 | { | ||
106 | atomic_add(delta, (atomic_t *)(&sem->count)); | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * downgrade write lock to read lock | ||
111 | */ | ||
112 | static inline void __downgrade_write(struct rw_semaphore *sem) | ||
113 | { | ||
114 | int tmp; | ||
115 | |||
116 | smp_wmb(); | ||
117 | tmp = atomic_add_return(-RWSEM_WAITING_BIAS, (atomic_t *)(&sem->count)); | ||
118 | if (tmp < 0) | ||
119 | rwsem_downgrade_wake(sem); | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * implement exchange and add functionality | ||
124 | */ | ||
125 | static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem) | ||
126 | { | ||
127 | smp_mb(); | ||
128 | return atomic_add_return(delta, (atomic_t *)(&sem->count)); | ||
129 | } | ||
130 | |||
131 | #endif /* _XTENSA_RWSEM_H */ | ||
diff --git a/include/asm-generic/rwsem.h b/include/asm-generic/rwsem.h index d6d5dc98d7da..3fc94a046bf5 100644 --- a/include/asm-generic/rwsem.h +++ b/include/asm-generic/rwsem.h | |||
@@ -53,7 +53,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem) | |||
53 | /* | 53 | /* |
54 | * lock for writing | 54 | * lock for writing |
55 | */ | 55 | */ |
56 | static inline void __down_write_nested(struct rw_semaphore *sem, int subclass) | 56 | static inline void __down_write(struct rw_semaphore *sem) |
57 | { | 57 | { |
58 | long tmp; | 58 | long tmp; |
59 | 59 | ||
@@ -63,9 +63,16 @@ static inline void __down_write_nested(struct rw_semaphore *sem, int subclass) | |||
63 | rwsem_down_write_failed(sem); | 63 | rwsem_down_write_failed(sem); |
64 | } | 64 | } |
65 | 65 | ||
66 | static inline void __down_write(struct rw_semaphore *sem) | 66 | static inline int __down_write_killable(struct rw_semaphore *sem) |
67 | { | 67 | { |
68 | __down_write_nested(sem, 0); | 68 | long tmp; |
69 | |||
70 | tmp = atomic_long_add_return_acquire(RWSEM_ACTIVE_WRITE_BIAS, | ||
71 | (atomic_long_t *)&sem->count); | ||
72 | if (unlikely(tmp != RWSEM_ACTIVE_WRITE_BIAS)) | ||
73 | if (IS_ERR(rwsem_down_write_failed_killable(sem))) | ||
74 | return -EINTR; | ||
75 | return 0; | ||
69 | } | 76 | } |
70 | 77 | ||
71 | static inline int __down_write_trylock(struct rw_semaphore *sem) | 78 | static inline int __down_write_trylock(struct rw_semaphore *sem) |
diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index d10ef06971b5..f75222ea7f16 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h | |||
@@ -446,6 +446,18 @@ do { \ | |||
446 | lock_acquired(&(_lock)->dep_map, _RET_IP_); \ | 446 | lock_acquired(&(_lock)->dep_map, _RET_IP_); \ |
447 | } while (0) | 447 | } while (0) |
448 | 448 | ||
449 | #define LOCK_CONTENDED_RETURN(_lock, try, lock) \ | ||
450 | ({ \ | ||
451 | int ____err = 0; \ | ||
452 | if (!try(_lock)) { \ | ||
453 | lock_contended(&(_lock)->dep_map, _RET_IP_); \ | ||
454 | ____err = lock(_lock); \ | ||
455 | } \ | ||
456 | if (!____err) \ | ||
457 | lock_acquired(&(_lock)->dep_map, _RET_IP_); \ | ||
458 | ____err; \ | ||
459 | }) | ||
460 | |||
449 | #else /* CONFIG_LOCK_STAT */ | 461 | #else /* CONFIG_LOCK_STAT */ |
450 | 462 | ||
451 | #define lock_contended(lockdep_map, ip) do {} while (0) | 463 | #define lock_contended(lockdep_map, ip) do {} while (0) |
@@ -454,6 +466,9 @@ do { \ | |||
454 | #define LOCK_CONTENDED(_lock, try, lock) \ | 466 | #define LOCK_CONTENDED(_lock, try, lock) \ |
455 | lock(_lock) | 467 | lock(_lock) |
456 | 468 | ||
469 | #define LOCK_CONTENDED_RETURN(_lock, try, lock) \ | ||
470 | lock(_lock) | ||
471 | |||
457 | #endif /* CONFIG_LOCK_STAT */ | 472 | #endif /* CONFIG_LOCK_STAT */ |
458 | 473 | ||
459 | #ifdef CONFIG_LOCKDEP | 474 | #ifdef CONFIG_LOCKDEP |
diff --git a/include/linux/rwsem-spinlock.h b/include/linux/rwsem-spinlock.h index 561e8615528d..ae0528b834cd 100644 --- a/include/linux/rwsem-spinlock.h +++ b/include/linux/rwsem-spinlock.h | |||
@@ -34,7 +34,7 @@ struct rw_semaphore { | |||
34 | extern void __down_read(struct rw_semaphore *sem); | 34 | extern void __down_read(struct rw_semaphore *sem); |
35 | extern int __down_read_trylock(struct rw_semaphore *sem); | 35 | extern int __down_read_trylock(struct rw_semaphore *sem); |
36 | extern void __down_write(struct rw_semaphore *sem); | 36 | extern void __down_write(struct rw_semaphore *sem); |
37 | extern void __down_write_nested(struct rw_semaphore *sem, int subclass); | 37 | extern int __must_check __down_write_killable(struct rw_semaphore *sem); |
38 | extern int __down_write_trylock(struct rw_semaphore *sem); | 38 | extern int __down_write_trylock(struct rw_semaphore *sem); |
39 | extern void __up_read(struct rw_semaphore *sem); | 39 | extern void __up_read(struct rw_semaphore *sem); |
40 | extern void __up_write(struct rw_semaphore *sem); | 40 | extern void __up_write(struct rw_semaphore *sem); |
diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 8f498cdde280..d1c12d160ace 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/list.h> | 14 | #include <linux/list.h> |
15 | #include <linux/spinlock.h> | 15 | #include <linux/spinlock.h> |
16 | #include <linux/atomic.h> | 16 | #include <linux/atomic.h> |
17 | #include <linux/err.h> | ||
17 | #ifdef CONFIG_RWSEM_SPIN_ON_OWNER | 18 | #ifdef CONFIG_RWSEM_SPIN_ON_OWNER |
18 | #include <linux/osq_lock.h> | 19 | #include <linux/osq_lock.h> |
19 | #endif | 20 | #endif |
@@ -43,6 +44,7 @@ struct rw_semaphore { | |||
43 | 44 | ||
44 | extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); | 45 | extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); |
45 | extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); | 46 | extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); |
47 | extern struct rw_semaphore *rwsem_down_write_failed_killable(struct rw_semaphore *sem); | ||
46 | extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *); | 48 | extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *); |
47 | extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); | 49 | extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); |
48 | 50 | ||
@@ -116,6 +118,7 @@ extern int down_read_trylock(struct rw_semaphore *sem); | |||
116 | * lock for writing | 118 | * lock for writing |
117 | */ | 119 | */ |
118 | extern void down_write(struct rw_semaphore *sem); | 120 | extern void down_write(struct rw_semaphore *sem); |
121 | extern int __must_check down_write_killable(struct rw_semaphore *sem); | ||
119 | 122 | ||
120 | /* | 123 | /* |
121 | * trylock for writing -- returns 1 if successful, 0 if contention | 124 | * trylock for writing -- returns 1 if successful, 0 if contention |
diff --git a/kernel/locking/rwsem-spinlock.c b/kernel/locking/rwsem-spinlock.c index 3a5048572065..1591f6b3539f 100644 --- a/kernel/locking/rwsem-spinlock.c +++ b/kernel/locking/rwsem-spinlock.c | |||
@@ -191,11 +191,12 @@ int __down_read_trylock(struct rw_semaphore *sem) | |||
191 | /* | 191 | /* |
192 | * get a write lock on the semaphore | 192 | * get a write lock on the semaphore |
193 | */ | 193 | */ |
194 | void __sched __down_write_nested(struct rw_semaphore *sem, int subclass) | 194 | int __sched __down_write_common(struct rw_semaphore *sem, int state) |
195 | { | 195 | { |
196 | struct rwsem_waiter waiter; | 196 | struct rwsem_waiter waiter; |
197 | struct task_struct *tsk; | 197 | struct task_struct *tsk; |
198 | unsigned long flags; | 198 | unsigned long flags; |
199 | int ret = 0; | ||
199 | 200 | ||
200 | raw_spin_lock_irqsave(&sem->wait_lock, flags); | 201 | raw_spin_lock_irqsave(&sem->wait_lock, flags); |
201 | 202 | ||
@@ -215,21 +216,33 @@ void __sched __down_write_nested(struct rw_semaphore *sem, int subclass) | |||
215 | */ | 216 | */ |
216 | if (sem->count == 0) | 217 | if (sem->count == 0) |
217 | break; | 218 | break; |
218 | set_task_state(tsk, TASK_UNINTERRUPTIBLE); | 219 | if (signal_pending_state(state, current)) { |
220 | ret = -EINTR; | ||
221 | goto out; | ||
222 | } | ||
223 | set_task_state(tsk, state); | ||
219 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | 224 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); |
220 | schedule(); | 225 | schedule(); |
221 | raw_spin_lock_irqsave(&sem->wait_lock, flags); | 226 | raw_spin_lock_irqsave(&sem->wait_lock, flags); |
222 | } | 227 | } |
223 | /* got the lock */ | 228 | /* got the lock */ |
224 | sem->count = -1; | 229 | sem->count = -1; |
230 | out: | ||
225 | list_del(&waiter.list); | 231 | list_del(&waiter.list); |
226 | 232 | ||
227 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | 233 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); |
234 | |||
235 | return ret; | ||
228 | } | 236 | } |
229 | 237 | ||
230 | void __sched __down_write(struct rw_semaphore *sem) | 238 | void __sched __down_write(struct rw_semaphore *sem) |
231 | { | 239 | { |
232 | __down_write_nested(sem, 0); | 240 | __down_write_common(sem, TASK_UNINTERRUPTIBLE); |
241 | } | ||
242 | |||
243 | int __sched __down_write_killable(struct rw_semaphore *sem) | ||
244 | { | ||
245 | return __down_write_common(sem, TASK_KILLABLE); | ||
233 | } | 246 | } |
234 | 247 | ||
235 | /* | 248 | /* |
diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c index a4d4de05b2d1..09e30c6225e5 100644 --- a/kernel/locking/rwsem-xadd.c +++ b/kernel/locking/rwsem-xadd.c | |||
@@ -433,12 +433,13 @@ static inline bool rwsem_has_spinner(struct rw_semaphore *sem) | |||
433 | /* | 433 | /* |
434 | * Wait until we successfully acquire the write lock | 434 | * Wait until we successfully acquire the write lock |
435 | */ | 435 | */ |
436 | __visible | 436 | static inline struct rw_semaphore * |
437 | struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem) | 437 | __rwsem_down_write_failed_common(struct rw_semaphore *sem, int state) |
438 | { | 438 | { |
439 | long count; | 439 | long count; |
440 | bool waiting = true; /* any queued threads before us */ | 440 | bool waiting = true; /* any queued threads before us */ |
441 | struct rwsem_waiter waiter; | 441 | struct rwsem_waiter waiter; |
442 | struct rw_semaphore *ret = sem; | ||
442 | 443 | ||
443 | /* undo write bias from down_write operation, stop active locking */ | 444 | /* undo write bias from down_write operation, stop active locking */ |
444 | count = rwsem_atomic_update(-RWSEM_ACTIVE_WRITE_BIAS, sem); | 445 | count = rwsem_atomic_update(-RWSEM_ACTIVE_WRITE_BIAS, sem); |
@@ -478,7 +479,7 @@ struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem) | |||
478 | count = rwsem_atomic_update(RWSEM_WAITING_BIAS, sem); | 479 | count = rwsem_atomic_update(RWSEM_WAITING_BIAS, sem); |
479 | 480 | ||
480 | /* wait until we successfully acquire the lock */ | 481 | /* wait until we successfully acquire the lock */ |
481 | set_current_state(TASK_UNINTERRUPTIBLE); | 482 | set_current_state(state); |
482 | while (true) { | 483 | while (true) { |
483 | if (rwsem_try_write_lock(count, sem)) | 484 | if (rwsem_try_write_lock(count, sem)) |
484 | break; | 485 | break; |
@@ -486,21 +487,48 @@ struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem) | |||
486 | 487 | ||
487 | /* Block until there are no active lockers. */ | 488 | /* Block until there are no active lockers. */ |
488 | do { | 489 | do { |
490 | if (signal_pending_state(state, current)) | ||
491 | goto out_nolock; | ||
492 | |||
489 | schedule(); | 493 | schedule(); |
490 | set_current_state(TASK_UNINTERRUPTIBLE); | 494 | set_current_state(state); |
491 | } while ((count = sem->count) & RWSEM_ACTIVE_MASK); | 495 | } while ((count = sem->count) & RWSEM_ACTIVE_MASK); |
492 | 496 | ||
493 | raw_spin_lock_irq(&sem->wait_lock); | 497 | raw_spin_lock_irq(&sem->wait_lock); |
494 | } | 498 | } |
495 | __set_current_state(TASK_RUNNING); | 499 | __set_current_state(TASK_RUNNING); |
500 | list_del(&waiter.list); | ||
501 | raw_spin_unlock_irq(&sem->wait_lock); | ||
496 | 502 | ||
503 | return ret; | ||
504 | |||
505 | out_nolock: | ||
506 | __set_current_state(TASK_RUNNING); | ||
507 | raw_spin_lock_irq(&sem->wait_lock); | ||
497 | list_del(&waiter.list); | 508 | list_del(&waiter.list); |
509 | if (list_empty(&sem->wait_list)) | ||
510 | rwsem_atomic_update(-RWSEM_WAITING_BIAS, sem); | ||
511 | else | ||
512 | __rwsem_do_wake(sem, RWSEM_WAKE_ANY); | ||
498 | raw_spin_unlock_irq(&sem->wait_lock); | 513 | raw_spin_unlock_irq(&sem->wait_lock); |
499 | 514 | ||
500 | return sem; | 515 | return ERR_PTR(-EINTR); |
516 | } | ||
517 | |||
518 | __visible struct rw_semaphore * __sched | ||
519 | rwsem_down_write_failed(struct rw_semaphore *sem) | ||
520 | { | ||
521 | return __rwsem_down_write_failed_common(sem, TASK_UNINTERRUPTIBLE); | ||
501 | } | 522 | } |
502 | EXPORT_SYMBOL(rwsem_down_write_failed); | 523 | EXPORT_SYMBOL(rwsem_down_write_failed); |
503 | 524 | ||
525 | __visible struct rw_semaphore * __sched | ||
526 | rwsem_down_write_failed_killable(struct rw_semaphore *sem) | ||
527 | { | ||
528 | return __rwsem_down_write_failed_common(sem, TASK_KILLABLE); | ||
529 | } | ||
530 | EXPORT_SYMBOL(rwsem_down_write_failed_killable); | ||
531 | |||
504 | /* | 532 | /* |
505 | * handle waking up a waiter on the semaphore | 533 | * handle waking up a waiter on the semaphore |
506 | * - up_read/up_write has decremented the active part of count if we come here | 534 | * - up_read/up_write has decremented the active part of count if we come here |
diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index 205be0ce34de..c817216c1615 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c | |||
@@ -55,6 +55,25 @@ void __sched down_write(struct rw_semaphore *sem) | |||
55 | EXPORT_SYMBOL(down_write); | 55 | EXPORT_SYMBOL(down_write); |
56 | 56 | ||
57 | /* | 57 | /* |
58 | * lock for writing | ||
59 | */ | ||
60 | int __sched down_write_killable(struct rw_semaphore *sem) | ||
61 | { | ||
62 | might_sleep(); | ||
63 | rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_); | ||
64 | |||
65 | if (LOCK_CONTENDED_RETURN(sem, __down_write_trylock, __down_write_killable)) { | ||
66 | rwsem_release(&sem->dep_map, 1, _RET_IP_); | ||
67 | return -EINTR; | ||
68 | } | ||
69 | |||
70 | rwsem_set_owner(sem); | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | EXPORT_SYMBOL(down_write_killable); | ||
75 | |||
76 | /* | ||
58 | * trylock for writing -- returns 1 if successful, 0 if contention | 77 | * trylock for writing -- returns 1 if successful, 0 if contention |
59 | */ | 78 | */ |
60 | int down_write_trylock(struct rw_semaphore *sem) | 79 | int down_write_trylock(struct rw_semaphore *sem) |