On Fri, Mar 14, 2014 at 10:44 AM, Ben Pfaff <[email protected]> wrote:
> On Fri, Mar 14, 2014 at 01:36:15AM -0700, Andy Zhou wrote:
> > Acked-by: Andy Zhou <[email protected]>
>
> Thanks.
>
> > On Tue, Mar 11, 2014 at 1:56 PM, Ben Pfaff <[email protected]> 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 <[email protected]>
> > > + * 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
[email protected]
http://openvswitch.org/mailman/listinfo/dev