mirror of
https://github.com/openvswitch/ovs
synced 2025-10-29 15:28:56 +00:00
lib/ovs-atomic: Add ovs_refcount_unref_relaxed(), ovs_refcount_try_ref_rcu().
When a reference counted object is also RCU protected the deletion of the object's memory is always postponed. This allows memory_order_relaxed to be used also for unreferencing, as RCU quiescing provides a full memory barrier (it has to, or otherwise there could be lingering accesses to objects after they are recycled). Also, when access to the reference counted object is protected via a mutex or a lock, the locking primitives provide the required memory barrier functionality. Also, add ovs_refcount_try_ref_rcu(), which takes a reference only if the refcount is non-zero and returns true if a reference was taken, false otherwise. This can be used in combined RCU/refcount scenarios where we have an RCU protected reference to an refcounted object, but which may be unref'ed at any time. If ovs_refcount_try_ref_rcu() fails, the object may still be safely used until the current thread quiesces. Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com> Acked-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
@@ -400,4 +400,78 @@ ovs_refcount_read(const struct ovs_refcount *refcount_)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Increments 'refcount', but only if it is non-zero.
|
||||||
|
*
|
||||||
|
* This may only be called for an object which is RCU protected during
|
||||||
|
* this call. This implies that its possible destruction is postponed
|
||||||
|
* until all current RCU threads quiesce.
|
||||||
|
*
|
||||||
|
* Returns false if the refcount was zero. In this case the object may
|
||||||
|
* be safely accessed until the current thread quiesces, but no additional
|
||||||
|
* references to the object may be taken.
|
||||||
|
*
|
||||||
|
* Does not provide a memory barrier, as the calling thread must have
|
||||||
|
* RCU protected access to the object already.
|
||||||
|
*
|
||||||
|
* It is critical that we never increment a zero refcount to a
|
||||||
|
* non-zero value, as whenever a refcount reaches the zero value, the
|
||||||
|
* protected object may be irrevocably scheduled for deletion. */
|
||||||
|
static inline bool
|
||||||
|
ovs_refcount_try_ref_rcu(struct ovs_refcount *refcount)
|
||||||
|
{
|
||||||
|
unsigned int count;
|
||||||
|
|
||||||
|
atomic_read_explicit(&refcount->count, &count, memory_order_relaxed);
|
||||||
|
do {
|
||||||
|
if (count == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} while (!atomic_compare_exchange_weak_explicit(&refcount->count, &count,
|
||||||
|
count + 1,
|
||||||
|
memory_order_relaxed,
|
||||||
|
memory_order_relaxed));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decrements 'refcount' and returns the previous reference count. To
|
||||||
|
* be used only when a memory barrier is already provided for the
|
||||||
|
* protected object independently.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
*
|
||||||
|
* if (ovs_refcount_unref_relaxed(&object->ref_cnt) == 1) {
|
||||||
|
* // Schedule uninitialization and freeing of the object:
|
||||||
|
* ovsrcu_postpone(destructor_function, object);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Here RCU quiescing already provides a full memory barrier. No additional
|
||||||
|
* barriers are needed here.
|
||||||
|
*
|
||||||
|
* Or:
|
||||||
|
*
|
||||||
|
* if (stp && ovs_refcount_unref_relaxed(&stp->ref_cnt) == 1) {
|
||||||
|
* ovs_mutex_lock(&mutex);
|
||||||
|
* list_remove(&stp->node);
|
||||||
|
* ovs_mutex_unlock(&mutex);
|
||||||
|
* free(stp->name);
|
||||||
|
* free(stp);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Here a mutex is used to guard access to all of 'stp' apart from
|
||||||
|
* 'ref_cnt'. Hence all changes to 'stp' by other threads must be
|
||||||
|
* visible when we get the mutex, and no access after the unlock can
|
||||||
|
* be reordered to happen prior the lock operation. No additional
|
||||||
|
* barriers are needed here.
|
||||||
|
*/
|
||||||
|
static inline unsigned int
|
||||||
|
ovs_refcount_unref_relaxed(struct ovs_refcount *refcount)
|
||||||
|
{
|
||||||
|
unsigned int old_refcount;
|
||||||
|
|
||||||
|
atomic_sub_explicit(&refcount->count, 1, &old_refcount,
|
||||||
|
memory_order_relaxed);
|
||||||
|
ovs_assert(old_refcount > 0);
|
||||||
|
return old_refcount;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* ovs-atomic.h */
|
#endif /* ovs-atomic.h */
|
||||||
|
|||||||
@@ -132,7 +132,10 @@ ovsrcu_quiesce_start(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Indicates a momentary quiescent state. See "Details" near the top of
|
/* Indicates a momentary quiescent state. See "Details" near the top of
|
||||||
* ovs-rcu.h. */
|
* ovs-rcu.h.
|
||||||
|
*
|
||||||
|
* Provides a full memory barrier via seq_change().
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
ovsrcu_quiesce(void)
|
ovsrcu_quiesce(void)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user