diff options
-rw-r--r-- | include/linux/percpu-rwsem.h | 10 | ||||
-rw-r--r-- | lib/percpu-rwsem.c | 21 |
2 files changed, 26 insertions, 5 deletions
diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index d2146a4f833e..3e88c9a7d57f 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <linux/rwsem.h> | 5 | #include <linux/rwsem.h> |
6 | #include <linux/percpu.h> | 6 | #include <linux/percpu.h> |
7 | #include <linux/wait.h> | 7 | #include <linux/wait.h> |
8 | #include <linux/lockdep.h> | ||
8 | 9 | ||
9 | struct percpu_rw_semaphore { | 10 | struct percpu_rw_semaphore { |
10 | unsigned int __percpu *fast_read_ctr; | 11 | unsigned int __percpu *fast_read_ctr; |
@@ -20,7 +21,14 @@ extern void percpu_up_read(struct percpu_rw_semaphore *); | |||
20 | extern void percpu_down_write(struct percpu_rw_semaphore *); | 21 | extern void percpu_down_write(struct percpu_rw_semaphore *); |
21 | extern void percpu_up_write(struct percpu_rw_semaphore *); | 22 | extern void percpu_up_write(struct percpu_rw_semaphore *); |
22 | 23 | ||
23 | extern int percpu_init_rwsem(struct percpu_rw_semaphore *); | 24 | extern int __percpu_init_rwsem(struct percpu_rw_semaphore *, |
25 | const char *, struct lock_class_key *); | ||
24 | extern void percpu_free_rwsem(struct percpu_rw_semaphore *); | 26 | extern void percpu_free_rwsem(struct percpu_rw_semaphore *); |
25 | 27 | ||
28 | #define percpu_init_rwsem(brw) \ | ||
29 | ({ \ | ||
30 | static struct lock_class_key rwsem_key; \ | ||
31 | __percpu_init_rwsem(brw, #brw, &rwsem_key); \ | ||
32 | }) | ||
33 | |||
26 | #endif | 34 | #endif |
diff --git a/lib/percpu-rwsem.c b/lib/percpu-rwsem.c index ce92ab563a08..652a8ee8efe9 100644 --- a/lib/percpu-rwsem.c +++ b/lib/percpu-rwsem.c | |||
@@ -2,18 +2,21 @@ | |||
2 | #include <linux/rwsem.h> | 2 | #include <linux/rwsem.h> |
3 | #include <linux/percpu.h> | 3 | #include <linux/percpu.h> |
4 | #include <linux/wait.h> | 4 | #include <linux/wait.h> |
5 | #include <linux/lockdep.h> | ||
5 | #include <linux/percpu-rwsem.h> | 6 | #include <linux/percpu-rwsem.h> |
6 | #include <linux/rcupdate.h> | 7 | #include <linux/rcupdate.h> |
7 | #include <linux/sched.h> | 8 | #include <linux/sched.h> |
8 | #include <linux/errno.h> | 9 | #include <linux/errno.h> |
9 | 10 | ||
10 | int percpu_init_rwsem(struct percpu_rw_semaphore *brw) | 11 | int __percpu_init_rwsem(struct percpu_rw_semaphore *brw, |
12 | const char *name, struct lock_class_key *rwsem_key) | ||
11 | { | 13 | { |
12 | brw->fast_read_ctr = alloc_percpu(int); | 14 | brw->fast_read_ctr = alloc_percpu(int); |
13 | if (unlikely(!brw->fast_read_ctr)) | 15 | if (unlikely(!brw->fast_read_ctr)) |
14 | return -ENOMEM; | 16 | return -ENOMEM; |
15 | 17 | ||
16 | init_rwsem(&brw->rw_sem); | 18 | /* ->rw_sem represents the whole percpu_rw_semaphore for lockdep */ |
19 | __init_rwsem(&brw->rw_sem, name, rwsem_key); | ||
17 | atomic_set(&brw->write_ctr, 0); | 20 | atomic_set(&brw->write_ctr, 0); |
18 | atomic_set(&brw->slow_read_ctr, 0); | 21 | atomic_set(&brw->slow_read_ctr, 0); |
19 | init_waitqueue_head(&brw->write_waitq); | 22 | init_waitqueue_head(&brw->write_waitq); |
@@ -66,19 +69,29 @@ static bool update_fast_ctr(struct percpu_rw_semaphore *brw, unsigned int val) | |||
66 | /* | 69 | /* |
67 | * Like the normal down_read() this is not recursive, the writer can | 70 | * Like the normal down_read() this is not recursive, the writer can |
68 | * come after the first percpu_down_read() and create the deadlock. | 71 | * come after the first percpu_down_read() and create the deadlock. |
72 | * | ||
73 | * Note: returns with lock_is_held(brw->rw_sem) == T for lockdep, | ||
74 | * percpu_up_read() does rwsem_release(). This pairs with the usage | ||
75 | * of ->rw_sem in percpu_down/up_write(). | ||
69 | */ | 76 | */ |
70 | void percpu_down_read(struct percpu_rw_semaphore *brw) | 77 | void percpu_down_read(struct percpu_rw_semaphore *brw) |
71 | { | 78 | { |
72 | if (likely(update_fast_ctr(brw, +1))) | 79 | might_sleep(); |
80 | if (likely(update_fast_ctr(brw, +1))) { | ||
81 | rwsem_acquire_read(&brw->rw_sem.dep_map, 0, 0, _RET_IP_); | ||
73 | return; | 82 | return; |
83 | } | ||
74 | 84 | ||
75 | down_read(&brw->rw_sem); | 85 | down_read(&brw->rw_sem); |
76 | atomic_inc(&brw->slow_read_ctr); | 86 | atomic_inc(&brw->slow_read_ctr); |
77 | up_read(&brw->rw_sem); | 87 | /* avoid up_read()->rwsem_release() */ |
88 | __up_read(&brw->rw_sem); | ||
78 | } | 89 | } |
79 | 90 | ||
80 | void percpu_up_read(struct percpu_rw_semaphore *brw) | 91 | void percpu_up_read(struct percpu_rw_semaphore *brw) |
81 | { | 92 | { |
93 | rwsem_release(&brw->rw_sem.dep_map, 1, _RET_IP_); | ||
94 | |||
82 | if (likely(update_fast_ctr(brw, -1))) | 95 | if (likely(update_fast_ctr(brw, -1))) |
83 | return; | 96 | return; |
84 | 97 | ||