On Fri, Mar 14, 2014 at 10:44 AM, Ben Pfaff <b...@nicira.com> wrote: > On Fri, Mar 14, 2014 at 01:36:15AM -0700, Andy Zhou wrote: > > Acked-by: Andy Zhou <az...@nicira.com> > > Thanks. > > > On Tue, Mar 11, 2014 at 1:56 PM, Ben Pfaff <b...@nicira.com> wrote: > > > > > RCU allows multiple threads to read objects in parallel without any > > > performance penalty. The following commit will introduce the first > use. > > > > > > Signed-off-by: Ben Pfaff <b...@nicira.com> > > > + * Use ovsrcu_set() to write an RCU-protected pointer and > > > ovsrcu_postpone() to > > > + * free the previous data. If more than one thread can write the > > > pointer, then > > > + * some form of external synchronization, e.g. a mutex, is needed to > > > prevent > > > + * writers from interfering with one another. For example, to write > the > > > + * pointer variable declared above while safely freeing the old value: > > > + * > > > + * static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; > > > + * > > > + * static void > > > + * free_flow(struct flow *flow) > > > + * { > > > + * free(flow); > > > + * } > > > + * > > > + * void > > > + * change_flow(struct flow *new_flow) > > > + * { > > > + * ovs_mutex_lock(&mutex); > > > + * ovsrcu_postpone(free_flow, ovsrcu_get(struct flow *, > &flowp)); > > > + * ovsrcu_set(&flowp, new_flow); > > > + * ovs_mutex_unlock(&mutex); > > > > > I assume the mutex lock and unlock should have embeded memory barrier > > operations to make sure all previous writes are visble. If true, then > > ovsrcu_get() and ovsrcu_set() does not seem necessary in this example. > They > > are correct though. > > They are correct, but not really necessary, as you say. How about the > following incremental: > > It is better. Thanks.
> diff --git a/lib/ovs-rcu.h b/lib/ovs-rcu.h > index 6721273..710870a 100644 > --- a/lib/ovs-rcu.h > +++ b/lib/ovs-rcu.h > @@ -98,7 +98,8 @@ > * change_flow(struct flow *new_flow) > * { > * ovs_mutex_lock(&mutex); > - * ovsrcu_postpone(free_flow, ovsrcu_get(struct flow *, &flowp)); > + * ovsrcu_postpone(free_flow, > + * ovsrcu_get_protected(struct flow *, &flowp)); > * ovsrcu_set(&flowp, new_flow); > * ovs_mutex_unlock(&mutex); > * } > @@ -118,33 +119,44 @@ > * > * struct flow *flow = ovsrcu_get(struct flow *, flowp); > * > + * If the pointer variable is currently protected against change (because > + * the current thread holds a mutex that protects it), > ovsrcu_get_protected() > + * may be used instead. Only on the Alpha architecture is this likely to > + * generate different code, but it may be useful documentation. > + * > * (With GNU C or Clang, you get a compiler error if TYPE is wrong; other > * compilers will merrily carry along accepting the wrong type.) > */ > #if __GNUC__ > #define OVSRCU_TYPE(TYPE) struct { ATOMIC(TYPE) p; } > -#define ovsrcu_get__(TYPE, VAR) \ > +#define ovsrcu_get__(TYPE, VAR, ORDER) \ > ({ \ > TYPE value__; \ > \ > atomic_read_explicit(CONST_CAST(ATOMIC(TYPE) *, &(VAR)->p), \ > - &value__, memory_order_consume); \ > + &value__, ORDER); \ > \ > value__; \ > }) > -#define ovsrcu_get(TYPE, VAR) CONST_CAST(TYPE, ovsrcu_get__(TYPE, VAR)) > +#define ovsrcu_get(TYPE, VAR) \ > + CONST_CAST(TYPE, ovsrcu_get__(TYPE, VAR, memory_order_consume)) > +#define ovsrcu_get_protected(TYPE, VAR) \ > + CONST_CAST(TYPE, ovsrcu_get__(TYPE, VAR, memory_order_relaxed)) > #else /* not GNU C */ > typedef struct ovsrcu_pointer { ATOMIC(void *) p; }; > #define OVSRCU_TYPE(TYPE) struct ovsrcu_pointer > static inline void * > -ovsrcu_get__(const struct ovsrcu_pointer *pointer) > +ovsrcu_get__(const struct ovsrcu_pointer *pointer, memory_order order) > { > void *value; > atomic_read_explicit(&CONST_CAST(struct ovsrcu_pointer *, pointer)->p, > - &value, memory_order_consume); > + &value, order); > return value; > } > -#define ovsrcu_get(TYPE, VAR) CONST_CAST(TYPE, ovsrcu_get__(VAR)) > +#define ovsrcu_get(TYPE, VAR) \ > + CONST_CAST(TYPE, ovsrcu_get__(VAR, memory_order_consume)) > +#define ovsrcu_get_protected(TYPE, VAR) \ > + CONST_CAST(TYPE, ovsrcu_get__(VAR, memory_order_relaxed)) > #endif > > /* Writes VALUE to the RCU-protected pointer whose address is VAR. > > > > > +/* Calls FUNCTION passing ARG as its pointer-type argument following > the > > > next > > > + * grace period. See "Usage" above for example. */ > > > +void ovsrcu_postpone__(void (*function)(void *aux), void *aux); > > > +#define ovsrcu_postpone(FUNCTION, ARG) \ > > > + ((void) sizeof((FUNCTION)(ARG), 1), > > > > I suppose the idea of using sizeof is for type checking. It is not > obvious > > to me why "1" required. > > It's because 'function' has to have a void return type, so > sizeof(function()) is sizeof(void), which yields a warning. > Make sense. Thanks for explaining.
_______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev