/** * srcu_readers_active - returns approximate number of readers. * @sp: which srcu_struct to count active readers (holding srcu_read_lock). * * Note that this is not an atomic primitive, and can therefore suffer * severe errors when invoked on an active srcu_struct. That said, it * can be useful as an error check at cleanup time. */ static int srcu_readers_active(struct srcu_struct *sp) { return srcu_readers_active_idx(sp, 0) + srcu_readers_active_idx(sp, 1); }
/* * Helper function for synchronize_srcu() and synchronize_srcu_expedited(). */ static void __synchronize_srcu(struct srcu_struct *sp, void (*sync_func)(void)) { int idx; idx = sp->completed; mutex_lock(&sp->mutex); /* * Check to see if someone else did the work for us while we were * waiting to acquire the lock. We need -two- advances of * the counter, not just one. If there was but one, we might have * shown up -after- our helper's first synchronize_sched(), thus * having failed to prevent CPU-reordering races with concurrent * srcu_read_unlock()s on other CPUs (see comment below). So we * either (1) wait for two or (2) supply the second ourselves. */ if ((sp->completed - idx) >= 2) { mutex_unlock(&sp->mutex); return; } sync_func(); /* Force memory barrier on all CPUs. */ /* * The preceding synchronize_sched() ensures that any CPU that * sees the new value of sp->completed will also see any preceding * changes to data structures made by this CPU. This prevents * some other CPU from reordering the accesses in its SRCU * read-side critical section to precede the corresponding * srcu_read_lock() -- ensuring that such references will in * fact be protected. * * So it is now safe to do the flip. */ idx = sp->completed & 0x1; sp->completed++; sync_func(); /* Force memory barrier on all CPUs. */ /* * At this point, because of the preceding synchronize_sched(), * all srcu_read_lock() calls using the old counters have completed. * Their corresponding critical sections might well be still * executing, but the srcu_read_lock() primitives themselves * will have finished executing. We initially give readers * an arbitrarily chosen 10 microseconds to get out of their * SRCU read-side critical sections, then loop waiting 1/HZ * seconds per iteration. The 10-microsecond value has done * very well in testing. */ if (srcu_readers_active_idx(sp, idx)) udelay(SYNCHRONIZE_SRCU_READER_DELAY); while (srcu_readers_active_idx(sp, idx)) schedule_timeout_interruptible(1); sync_func(); /* Force memory barrier on all CPUs. */ /* * The preceding synchronize_sched() forces all srcu_read_unlock() * primitives that were executing concurrently with the preceding * for_each_possible_cpu() loop to have completed by this point. * More importantly, it also forces the corresponding SRCU read-side * critical sections to have also completed, and the corresponding * references to SRCU-protected data items to be dropped. * * Note: * * Despite what you might think at first glance, the * preceding synchronize_sched() -must- be within the * critical section ended by the following mutex_unlock(). * Otherwise, a task taking the early exit can race * with a srcu_read_unlock(), which might have executed * just before the preceding srcu_readers_active() check, * and whose CPU might have reordered the srcu_read_unlock() * with the preceding critical section. In this case, there * is nothing preventing the synchronize_sched() task that is * taking the early exit from freeing a data structure that * is still being referenced (out of order) by the task * doing the srcu_read_unlock(). * * Alternatively, the comparison with "2" on the early exit * could be changed to "3", but this increases synchronize_srcu() * latency for bulk loads. So the current code is preferred. */ mutex_unlock(&sp->mutex); }
/* * Return true if the number of pre-existing readers is determined to * be stably zero. An example unstable zero can occur if the call * to srcu_readers_active_idx() misses an __srcu_read_lock() increment, * but due to task migration, sees the corresponding __srcu_read_unlock() * decrement. This can happen because srcu_readers_active_idx() takes * time to sum the array, and might in fact be interrupted or preempted * partway through the summation. */ static bool srcu_readers_active_idx_check(struct srcu_struct *sp, int idx) { unsigned long seq; seq = srcu_readers_seq_idx(sp, idx); /* * The following smp_mb() A pairs with the smp_mb() B located in * __srcu_read_lock(). This pairing ensures that if an * __srcu_read_lock() increments its counter after the summation * in srcu_readers_active_idx(), then the corresponding SRCU read-side * critical section will see any changes made prior to the start * of the current SRCU grace period. * * Also, if the above call to srcu_readers_seq_idx() saw the * increment of ->seq[], then the call to srcu_readers_active_idx() * must see the increment of ->c[]. */ smp_mb(); /* A */ /* * Note that srcu_readers_active_idx() can incorrectly return * zero even though there is a pre-existing reader throughout. * To see this, suppose that task A is in a very long SRCU * read-side critical section that started on CPU 0, and that * no other reader exists, so that the sum of the counters * is equal to one. Then suppose that task B starts executing * srcu_readers_active_idx(), summing up to CPU 1, and then that * task C starts reading on CPU 0, so that its increment is not * summed, but finishes reading on CPU 2, so that its decrement * -is- summed. Then when task B completes its sum, it will * incorrectly get zero, despite the fact that task A has been * in its SRCU read-side critical section the whole time. * * We therefore do a validation step should srcu_readers_active_idx() * return zero. */ if (srcu_readers_active_idx(sp, idx) != 0) return false; /* * The remainder of this function is the validation step. * The following smp_mb() D pairs with the smp_mb() C in * __srcu_read_unlock(). If the __srcu_read_unlock() was seen * by srcu_readers_active_idx() above, then any destructive * operation performed after the grace period will happen after * the corresponding SRCU read-side critical section. * * Note that there can be at most NR_CPUS worth of readers using * the old index, which is not enough to overflow even a 32-bit * integer. (Yes, this does mean that systems having more than * a billion or so CPUs need to be 64-bit systems.) Therefore, * the sum of the ->seq[] counters cannot possibly overflow. * Therefore, the only way that the return values of the two * calls to srcu_readers_seq_idx() can be equal is if there were * no increments of the corresponding rank of ->seq[] counts * in the interim. But the missed-increment scenario laid out * above includes an increment of the ->seq[] counter by * the corresponding __srcu_read_lock(). Therefore, if this * scenario occurs, the return values from the two calls to * srcu_readers_seq_idx() will differ, and thus the validation * step below suffices. */ smp_mb(); /* D */ return srcu_readers_seq_idx(sp, idx) == seq; }