123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- #include <linux/atomic.h>
- #include <linux/rwsem.h>
- #include <linux/percpu.h>
- #include <linux/lockdep.h>
- #include <linux/percpu-rwsem.h>
- #include <linux/rcupdate.h>
- #include <linux/sched.h>
- #include <linux/errno.h>
- int __percpu_init_rwsem(struct percpu_rw_semaphore *sem,
- const char *name, struct lock_class_key *rwsem_key)
- {
- sem->read_count = alloc_percpu(int);
- if (unlikely(!sem->read_count))
- return -ENOMEM;
-
- rcu_sync_init(&sem->rss, RCU_SCHED_SYNC);
- __init_rwsem(&sem->rw_sem, name, rwsem_key);
- rcuwait_init(&sem->writer);
- sem->readers_block = 0;
- return 0;
- }
- EXPORT_SYMBOL_GPL(__percpu_init_rwsem);
- void percpu_free_rwsem(struct percpu_rw_semaphore *sem)
- {
-
- if (!sem->read_count)
- return;
- rcu_sync_dtor(&sem->rss);
- free_percpu(sem->read_count);
- sem->read_count = NULL;
- }
- EXPORT_SYMBOL_GPL(percpu_free_rwsem);
- int __percpu_down_read(struct percpu_rw_semaphore *sem, int try)
- {
-
- smp_mb();
-
- if (likely(!smp_load_acquire(&sem->readers_block)))
- return 1;
-
- __percpu_up_read(sem);
- if (try)
- return 0;
-
- preempt_enable_no_resched();
-
- __down_read(&sem->rw_sem);
- this_cpu_inc(*sem->read_count);
- __up_read(&sem->rw_sem);
- preempt_disable();
- return 1;
- }
- EXPORT_SYMBOL_GPL(__percpu_down_read);
- void __percpu_up_read(struct percpu_rw_semaphore *sem)
- {
- smp_mb();
-
- __this_cpu_dec(*sem->read_count);
-
- rcuwait_wake_up(&sem->writer);
- }
- EXPORT_SYMBOL_GPL(__percpu_up_read);
- #define per_cpu_sum(var) \
- ({ \
- typeof(var) __sum = 0; \
- int cpu; \
- compiletime_assert_atomic_type(__sum); \
- for_each_possible_cpu(cpu) \
- __sum += per_cpu(var, cpu); \
- __sum; \
- })
- static bool readers_active_check(struct percpu_rw_semaphore *sem)
- {
- if (per_cpu_sum(*sem->read_count) != 0)
- return false;
-
- smp_mb();
- return true;
- }
- void percpu_down_write(struct percpu_rw_semaphore *sem)
- {
-
- rcu_sync_enter(&sem->rss);
- down_write(&sem->rw_sem);
-
- WRITE_ONCE(sem->readers_block, 1);
- smp_mb();
-
-
- rcuwait_wait_event(&sem->writer, readers_active_check(sem));
- }
- EXPORT_SYMBOL_GPL(percpu_down_write);
- void percpu_up_write(struct percpu_rw_semaphore *sem)
- {
-
- smp_store_release(&sem->readers_block, 0);
-
- up_write(&sem->rw_sem);
-
- rcu_sync_exit(&sem->rss);
- }
- EXPORT_SYMBOL_GPL(percpu_up_write);
|