-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Mikulas Patocka
authored and
Jens Axboe
committed
Sep 26, 2012
1 parent
5bbc549
commit 3970163
Showing
5 changed files
with
136 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: b87570f5d349661814b262dd5fc40787700f80d6 | ||
refs/heads/master: 62ac665ff9fc07497ca524bd20d6a96893d11071 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
Percpu rw semaphores | ||
-------------------- | ||
|
||
Percpu rw semaphores is a new read-write semaphore design that is | ||
optimized for locking for reading. | ||
|
||
The problem with traditional read-write semaphores is that when multiple | ||
cores take the lock for reading, the cache line containing the semaphore | ||
is bouncing between L1 caches of the cores, causing performance | ||
degradation. | ||
|
||
Locking for reading it very fast, it uses RCU and it avoids any atomic | ||
instruction in the lock and unlock path. On the other hand, locking for | ||
writing is very expensive, it calls synchronize_rcu() that can take | ||
hundreds of microseconds. | ||
|
||
The lock is declared with "struct percpu_rw_semaphore" type. | ||
The lock is initialized percpu_init_rwsem, it returns 0 on success and | ||
-ENOMEM on allocation failure. | ||
The lock must be freed with percpu_free_rwsem to avoid memory leak. | ||
|
||
The lock is locked for read with percpu_down_read, percpu_up_read and | ||
for write with percpu_down_write, percpu_up_write. | ||
|
||
The idea of using RCU for optimized rw-lock was introduced by | ||
Eric Dumazet <eric.dumazet@gmail.com>. | ||
The code was written by Mikulas Patocka <mpatocka@redhat.com> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
#ifndef _LINUX_PERCPU_RWSEM_H | ||
#define _LINUX_PERCPU_RWSEM_H | ||
|
||
#include <linux/mutex.h> | ||
#include <linux/percpu.h> | ||
#include <linux/rcupdate.h> | ||
#include <linux/delay.h> | ||
|
||
struct percpu_rw_semaphore { | ||
unsigned __percpu *counters; | ||
bool locked; | ||
struct mutex mtx; | ||
}; | ||
|
||
static inline void percpu_down_read(struct percpu_rw_semaphore *p) | ||
{ | ||
rcu_read_lock(); | ||
if (unlikely(p->locked)) { | ||
rcu_read_unlock(); | ||
mutex_lock(&p->mtx); | ||
this_cpu_inc(*p->counters); | ||
mutex_unlock(&p->mtx); | ||
return; | ||
} | ||
this_cpu_inc(*p->counters); | ||
rcu_read_unlock(); | ||
} | ||
|
||
static inline void percpu_up_read(struct percpu_rw_semaphore *p) | ||
{ | ||
/* | ||
* On X86, write operation in this_cpu_dec serves as a memory unlock | ||
* barrier (i.e. memory accesses may be moved before the write, but | ||
* no memory accesses are moved past the write). | ||
* On other architectures this may not be the case, so we need smp_mb() | ||
* there. | ||
*/ | ||
#if defined(CONFIG_X86) && (!defined(CONFIG_X86_PPRO_FENCE) && !defined(CONFIG_X86_OOSTORE)) | ||
barrier(); | ||
#else | ||
smp_mb(); | ||
#endif | ||
this_cpu_dec(*p->counters); | ||
} | ||
|
||
static inline unsigned __percpu_count(unsigned __percpu *counters) | ||
{ | ||
unsigned total = 0; | ||
int cpu; | ||
|
||
for_each_possible_cpu(cpu) | ||
total += ACCESS_ONCE(*per_cpu_ptr(counters, cpu)); | ||
|
||
return total; | ||
} | ||
|
||
static inline void percpu_down_write(struct percpu_rw_semaphore *p) | ||
{ | ||
mutex_lock(&p->mtx); | ||
p->locked = true; | ||
synchronize_rcu(); | ||
while (__percpu_count(p->counters)) | ||
msleep(1); | ||
smp_rmb(); /* paired with smp_mb() in percpu_sem_up_read() */ | ||
} | ||
|
||
static inline void percpu_up_write(struct percpu_rw_semaphore *p) | ||
{ | ||
p->locked = false; | ||
mutex_unlock(&p->mtx); | ||
} | ||
|
||
static inline int percpu_init_rwsem(struct percpu_rw_semaphore *p) | ||
{ | ||
p->counters = alloc_percpu(unsigned); | ||
if (unlikely(!p->counters)) | ||
return -ENOMEM; | ||
p->locked = false; | ||
mutex_init(&p->mtx); | ||
return 0; | ||
} | ||
|
||
static inline void percpu_free_rwsem(struct percpu_rw_semaphore *p) | ||
{ | ||
free_percpu(p->counters); | ||
p->counters = NULL; /* catch use after free bugs */ | ||
} | ||
|
||
#endif |