With RCU in Open vSwitch it's very easy to protect objects accessed by a pointer, but sometimes a pointer is not available.
One example is the vhost id for DPDK 16.07. Until DPDK 16.04 a pointer was used to access a vhost device with RCU semantics. From DPDK 16.07 an integer id (which is an array index) is used to access a vhost device. Ideally, we want the exact same RCU semantics that we had for the pointer, on the integer (atomicity, memory barriers, behaviour around quiescent states) This commit implements a new type in ovs-rcu: ovsrcu_index. The newly implemented ovsrcu_index_*() functions should be used to access the type. Even though we say "Do not, in general, declare a typedef for a struct, union, or enum.", I think we're not in the "general" case. Signed-off-by: Daniele Di Proietto <diproiet...@vmware.com> --- lib/ovs-rcu.h | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/lib/ovs-rcu.h b/lib/ovs-rcu.h index dc75749..2887bb8 100644 --- a/lib/ovs-rcu.h +++ b/lib/ovs-rcu.h @@ -125,6 +125,36 @@ * ovs_mutex_unlock(&mutex); * } * + * In some rare cases an object may not be addressable with a pointer, but only + * through an array index (e.g. because it's provided by another library). It + * is still possible to have RCU semantics by using the ovsrcu_index type. + * + * static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; + * + * ovsrcu_index port_id; + * + * void tx() + * { + * int id = ovsrcu_index_get(&port_id); + * if (id == -1) { + * return; + * } + * port_tx(id); + * } + * + * void delete() + * { + * int id; + * + * ovs_mutex_lock(&mutex); + * id = ovsrcu_index_get_protected(&port_id); + * ovsrcu_index_set(&port_id, -1); + * ovs_mutex_unlock(&mutex); + * + * ovsrcu_synchronize(); + * port_delete(id); + * } + * */ #include "compiler.h" @@ -213,6 +243,60 @@ void ovsrcu_postpone__(void (*function)(void *aux), void *aux); (void) sizeof(*(ARG)), \ ovsrcu_postpone__((void (*)(void *))(FUNCTION), ARG)) +/* An array index protected by RCU semantics. This is an easier alternative to + * an RCU protected pointer to a malloc'd int. */ +typedef struct { atomic_int v; } ovsrcu_index; + +static inline int ovsrcu_index_get__(const ovsrcu_index *i, memory_order order) +{ + int ret; + atomic_read_explicit(CONST_CAST(atomic_int *, &i->v), &ret, order); + return ret; +} + +/* Returns the index contained in 'i'. The returned value can be used until + * the next grace period. */ +static inline int ovsrcu_index_get(const ovsrcu_index *i) +{ + return ovsrcu_index_get__(i, memory_order_consume); +} + +/* Returns the index contained in 'i'. This is an alternative to + * ovsrcu_index_get() that can be used when there's no possible concurrent + * writer. */ +static inline int ovsrcu_index_get_protected(const ovsrcu_index *i) +{ + return ovsrcu_index_get__(i, memory_order_relaxed); +} + +static inline void ovsrcu_index_set__(ovsrcu_index *i, int value, + memory_order order) +{ + atomic_store_explicit(&i->v, value, order); +} + +/* Writes the index 'value' in 'i'. The previous value of 'i' may still be + * used by readers until the next grace period. */ +static inline void ovsrcu_index_set(ovsrcu_index *i, int value) +{ + ovsrcu_index_set__(i, value, memory_order_release); +} + +/* Writes the index 'value' in 'i'. This is an alternative to + * ovsrcu_index_set() that can be used when there's no possible concurrent + * reader. */ +static inline void ovsrcu_index_set_hidden(ovsrcu_index *i, int value) +{ + ovsrcu_index_set__(i, value, memory_order_relaxed); +} + +/* Initializes 'i' with 'value'. This is safe to call as long as there are no + * concurrent readers. */ +static inline void ovsrcu_index_init(ovsrcu_index *i, int value) +{ + atomic_init(&i->v, value); +} + /* Quiescent states. */ void ovsrcu_quiesce_start(void); void ovsrcu_quiesce_end(void); -- 2.8.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev