| #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; | |
| }; | |
| #define light_mb() barrier() | |
| #define heavy_mb() synchronize_sched() | |
| static inline void percpu_down_read(struct percpu_rw_semaphore *p) | |
| { | |
| rcu_read_lock_sched(); | |
| if (unlikely(p->locked)) { | |
| rcu_read_unlock_sched(); | |
| mutex_lock(&p->mtx); | |
| this_cpu_inc(*p->counters); | |
| mutex_unlock(&p->mtx); | |
| return; | |
| } | |
| this_cpu_inc(*p->counters); | |
| rcu_read_unlock_sched(); | |
| light_mb(); /* A, between read of p->locked and read of data, paired with D */ | |
| } | |
| static inline void percpu_up_read(struct percpu_rw_semaphore *p) | |
| { | |
| light_mb(); /* B, between read of the data and write to p->counter, paired with C */ | |
| 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_sched(); /* make sure that all readers exit the rcu_read_lock_sched region */ | |
| while (__percpu_count(p->counters)) | |
| msleep(1); | |
| heavy_mb(); /* C, between read of p->counter and write to data, paired with B */ | |
| } | |
| static inline void percpu_up_write(struct percpu_rw_semaphore *p) | |
| { | |
| heavy_mb(); /* D, between write to data and write to p->locked, paired with A */ | |
| 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 |