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: 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. _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev