void percpu_free_rwsem(struct percpu_rw_semaphore *sem)
{ /* * XXX: temporary kludge. The error path in alloc_super() * assumes that percpu_free_rwsem() is safe after kzalloc().
*/ if (!sem->read_count) return;
rcu_sync_dtor(&sem->rss);
free_percpu(sem->read_count);
sem->read_count = NULL; /* catch use after free bugs */
}
EXPORT_SYMBOL_GPL(percpu_free_rwsem);
/* * Due to having preemption disabled the decrement happens on * the same CPU as the increment, avoiding the * increment-on-one-CPU-and-decrement-on-another problem. * * If the reader misses the writer's assignment of sem->block, then the * writer is guaranteed to see the reader's increment. * * Conversely, any readers that increment their sem->read_count after * the writer looks are guaranteed to see the sem->block value, which * in turn means that they are guaranteed to immediately decrement * their sem->read_count, so that it doesn't matter that the writer * missed them.
*/
smp_mb(); /* A matches D */
/* * If !sem->block the critical section starts here, matched by the * release in percpu_up_write().
*/ if (likely(!atomic_read_acquire(&sem->block))) returntrue;
this_cpu_dec(*sem->read_count);
/* Prod writer to re-evaluate readers_active_check() */
rcuwait_wake_up(&sem->writer);
returnfalse;
}
staticinlinebool __percpu_down_write_trylock(struct percpu_rw_semaphore *sem)
{ if (atomic_read(&sem->block)) returnfalse;
/* * The return value of wait_queue_entry::func means: * * <0 - error, wakeup is terminated and the error is returned * 0 - no wakeup, a next waiter is tried * >0 - woken, if EXCLUSIVE, counted towards @nr_exclusive. * * We use EXCLUSIVE for both readers and writers to preserve FIFO order, * and play games with the return value to allow waking multiple readers. * * Specifically, we wake readers until we've woken a single writer, or until a * trylock fails.
*/ staticint percpu_rwsem_wake_function(struct wait_queue_entry *wq_entry, unsignedint mode, int wake_flags, void *key)
{ bool reader = wq_entry->flags & WQ_FLAG_CUSTOM; struct percpu_rw_semaphore *sem = key; struct task_struct *p;
/* concurrent against percpu_down_write(), can get stolen */ if (!__percpu_rwsem_trylock(sem, reader)) return 1;
p = get_task_struct(wq_entry->private);
list_del_init(&wq_entry->entry);
smp_store_release(&wq_entry->private, NULL);
wake_up_process(p);
put_task_struct(p);
return !reader; /* wake (readers until) 1 writer */
}
spin_lock_irq(&sem->waiters.lock); /* * Serialize against the wakeup in percpu_up_write(), if we fail * the trylock, the wakeup must see us on the list.
*/
wait = !__percpu_rwsem_trylock(sem, reader); if (wait) {
wq_entry.flags |= WQ_FLAG_EXCLUSIVE | reader * WQ_FLAG_CUSTOM;
__add_wait_queue_entry_tail(&sem->waiters, &wq_entry);
}
spin_unlock_irq(&sem->waiters.lock);
while (wait) {
set_current_state(TASK_UNINTERRUPTIBLE |
(freeze ? TASK_FREEZABLE : 0)); if (!smp_load_acquire(&wq_entry.private)) break;
schedule();
}
__set_current_state(TASK_RUNNING);
}
/* * Return true if the modular sum of the sem->read_count per-CPU variable is * zero. If this sum is zero, then it is stable due to the fact that if any * newly arriving readers increment a given counter, they will immediately * decrement that same counter. * * Assumes sem->block is set.
*/ staticbool readers_active_check(struct percpu_rw_semaphore *sem)
{ if (per_cpu_sum(*sem->read_count) != 0) returnfalse;
/* * If we observed the decrement; ensure we see the entire critical * section.
*/
/* Notify readers to take the slow path. */
rcu_sync_enter(&sem->rss);
/* * Try set sem->block; this provides writer-writer exclusion. * Having sem->block set makes new readers block.
*/ if (!__percpu_down_write_trylock(sem)) {
trace_contention_begin(sem, LCB_F_PERCPU | LCB_F_WRITE);
percpu_rwsem_wait(sem, /* .reader = */ false, false);
contended = true;
}
/* smp_mb() implied by __percpu_down_write_trylock() on success -- D matches A */
/* * If they don't see our store of sem->block, then we are guaranteed to * see their sem->read_count increment, and therefore will wait for * them.
*/
/* Wait for all active readers to complete. */
rcuwait_wait_event(&sem->writer, readers_active_check(sem), TASK_UNINTERRUPTIBLE); if (contended)
trace_contention_end(sem, 0);
}
EXPORT_SYMBOL_GPL(percpu_down_write);
/* * Signal the writer is done, no fast path yet. * * One reason that we cannot just immediately flip to readers_fast is * that new readers might fail to see the results of this writer's * critical section. * * Therefore we force it through the slow path which guarantees an * acquire and thereby guarantees the critical section's consistency.
*/
atomic_set_release(&sem->block, 0);
/* * Prod any pending reader/writer to make progress.
*/
__wake_up(&sem->waiters, TASK_NORMAL, 1, sem);
/* * Once this completes (at least one RCU-sched grace period hence) the * reader fast path will be available again. Safe to use outside the * exclusive write lock because its counting.
*/
rcu_sync_exit(&sem->rss);
}
EXPORT_SYMBOL_GPL(percpu_up_write);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.