I’ll see if we can make the iteration both safe and lockless, so there is no 
need to review this patch at this moment.

  Jarno

On Oct 16, 2014, at 4:24 PM, Jarno Rajahalme <jrajaha...@nicira.com> wrote:

> This patch changes the classifier internal mutex to a recursive type.
> This allows simplification of locking during iteration.
> 
> Accurate iteration requires classifier_inserts to be exluded during
> iteration.  To this end we take the internal mutex at the start of the
> iteration.  To allow rule removal while iterating, we have supported a
> 'SAFE' variant of the iterator, that used to release the mutex for the
> body of the iterator loop.  For a classifier user without additional
> locks this would have opened a possibility for another thread to issue
> classifier_insert()s during the iteration, potentially making the
> iteration to skip or duplicate entries due to cmap internal
> rearrangements.
> 
> This patch fixes the above keeping the writers excluded also in the
> safe mode, which is possible with a recursive mutex.  We also remove
> the distinction between 'SAFE' and normal iteration, as the only
> remaining difference was in the iterator for statement, and we can use
> the 'SAFE' variant in all cases without additional overhead.
> 
> Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com>
> ---
> lib/classifier.c        |   16 ++++++----------
> lib/classifier.h        |   33 ++++++++++++++-------------------
> ofproto/ofproto-dpif.c  |    2 +-
> ofproto/ofproto.c       |    2 +-
> tests/test-classifier.c |    5 ++---
> utilities/ovs-ofctl.c   |    2 +-
> 6 files changed, 25 insertions(+), 35 deletions(-)
> 
> diff --git a/lib/classifier.c b/lib/classifier.c
> index f30ba95..19a3435 100644
> --- a/lib/classifier.c
> +++ b/lib/classifier.c
> @@ -448,7 +448,7 @@ void
> classifier_init(struct classifier *cls, const uint8_t *flow_segments)
>     OVS_EXCLUDED(cls->mutex)
> {
> -    ovs_mutex_init(&cls->mutex);
> +    ovs_mutex_init_recursive(&cls->mutex);
>     ovs_mutex_lock(&cls->mutex);
>     cls->n_rules = 0;
>     cmap_init(&cls->subtables_map);
> @@ -1255,14 +1255,12 @@ search_subtable(const struct cls_subtable *subtable,
>  *
>  * Ignores target->priority. */
> struct cls_cursor cls_cursor_start(const struct classifier *cls,
> -                                   const struct cls_rule *target,
> -                                   bool safe)
> +                                   const struct cls_rule *target)
>     OVS_NO_THREAD_SAFETY_ANALYSIS
> {
>     struct cls_cursor cursor;
>     struct cls_subtable *subtable;
> 
> -    cursor.safe = safe;
>     cursor.cls = cls;
>     cursor.target = target && !cls_rule_is_catchall(target) ? target : NULL;
>     cursor.rule = NULL;
> @@ -1280,8 +1278,8 @@ struct cls_cursor cls_cursor_start(const struct 
> classifier *cls,
>         }
>     }
> 
> -    /* Leave locked if requested and have a rule. */
> -    if (safe || !cursor.rule) {
> +    /* Release the lock if iteration ended already. */
> +    if (!cursor.rule) {
>         ovs_mutex_unlock(&cursor.cls->mutex);
>     }
>     return cursor;
> @@ -1328,11 +1326,9 @@ void
> cls_cursor_advance(struct cls_cursor *cursor)
>     OVS_NO_THREAD_SAFETY_ANALYSIS
> {
> -    if (cursor->safe) {
> -        ovs_mutex_lock(&cursor->cls->mutex);
> -    }
>     cursor->rule = cls_cursor_next(cursor);
> -    if (cursor->safe || !cursor->rule) {
> +    /* Release the lock if iteration ended. */
> +    if (!cursor->rule) {
>         ovs_mutex_unlock(&cursor->cls->mutex);
>     }
> }
> diff --git a/lib/classifier.h b/lib/classifier.h
> index d1f4e86..0d6ee2c 100644
> --- a/lib/classifier.h
> +++ b/lib/classifier.h
> @@ -319,39 +319,34 @@ struct cls_cursor {
>     struct cmap_cursor subtables;
>     struct cmap_cursor rules;
>     struct cls_rule *rule;
> -    bool safe;
> };
> 
> -/* Iteration requires mutual exclusion of writers.  We do this by taking
> - * a mutex for the duration of the iteration, except for the
> - * 'SAFE' variant, where we release the mutex for the body of the loop. */
> +/* Accurate iteration requires mutual exclusion of writers.  We do this by
> + * taking a classifier internal mutex for the duration of the iteration.  
> This
> + * implies that the iteration MUST NOT be terminated by jumping out of the 
> loop
> + * with break or return statements, lest the classifier be left locked, and
> + * other threads deadlock.
> + *
> + * The iterating thread may modify the classifier itself.  However,
> + * classifier_insert()s can rearrange the internal structures so that the
> + * iteration can go through same rules twice or miss some rules altogether.
> + * classifier_replace() and classifier_remove() do not cause similar
> + * problems. */
> struct cls_cursor cls_cursor_start(const struct classifier *cls,
> -                                   const struct cls_rule *target,
> -                                   bool safe);
> +                                   const struct cls_rule *target);
> 
> void cls_cursor_advance(struct cls_cursor *);
> 
> #define CLS_FOR_EACH(RULE, MEMBER, CLS) \
>     CLS_FOR_EACH_TARGET(RULE, MEMBER, CLS, NULL)
> #define CLS_FOR_EACH_TARGET(RULE, MEMBER, CLS, TARGET)                  \
> -    for (struct cls_cursor cursor__ = cls_cursor_start(CLS, TARGET, false); \
> -         (cursor__.rule                                                 \
> -          ? (INIT_CONTAINER(RULE, cursor__.rule, MEMBER),               \
> -             true)                                                      \
> -          : false);                                                     \
> -         cls_cursor_advance(&cursor__))
> -
> -/* These forms allows classifier_remove() to be called within the loop. */
> -#define CLS_FOR_EACH_SAFE(RULE, MEMBER, CLS) \
> -    CLS_FOR_EACH_TARGET_SAFE(RULE, MEMBER, CLS, NULL)
> -#define CLS_FOR_EACH_TARGET_SAFE(RULE, MEMBER, CLS, TARGET)             \
> -    for (struct cls_cursor cursor__ = cls_cursor_start(CLS, TARGET, true); \
> +    for (struct cls_cursor cursor__ = cls_cursor_start(CLS, TARGET);    \
>          (cursor__.rule                                                 \
>           ? (INIT_CONTAINER(RULE, cursor__.rule, MEMBER),               \
>              cls_cursor_advance(&cursor__),                             \
>              true)                                                      \
>           : false);                                                     \
> -        )                                                               \
> +        )
> 
> #ifdef __cplusplus
> }
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index d965d38..1dfae96 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -1375,7 +1375,7 @@ destruct(struct ofproto *ofproto_)
>     hmap_remove(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node);
> 
>     OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
> -        CLS_FOR_EACH_SAFE (rule, up.cr, &table->cls) {
> +        CLS_FOR_EACH (rule, up.cr, &table->cls) {
>             ofproto_rule_delete(&ofproto->up, &rule->up);
>         }
>     }
> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
> index 2cb93b0..34958ca 100644
> --- a/ofproto/ofproto.c
> +++ b/ofproto/ofproto.c
> @@ -1369,7 +1369,7 @@ ofproto_flush__(struct ofproto *ofproto)
>             continue;
>         }
> 
> -        CLS_FOR_EACH_SAFE (rule, cr, &table->cls) {
> +        CLS_FOR_EACH (rule, cr, &table->cls) {
>             ofproto_rule_delete__(rule, OFPRR_DELETE);
>         }
>     }
> diff --git a/tests/test-classifier.c b/tests/test-classifier.c
> index 0dfa910..74bfac5 100644
> --- a/tests/test-classifier.c
> +++ b/tests/test-classifier.c
> @@ -454,7 +454,7 @@ destroy_classifier(struct classifier *cls)
> {
>     struct test_rule *rule;
> 
> -    CLS_FOR_EACH_SAFE (rule, cls_rule, cls) {
> +    CLS_FOR_EACH (rule, cls_rule, cls) {
>         classifier_remove(cls, &rule->cls_rule);
>         free_rule(rule);
>     }
> @@ -1074,8 +1074,7 @@ test_many_rules_in_n_tables(int n_tables)
> 
>             target = clone_rule(tcls.rules[random_range(tcls.n_rules)]);
> 
> -            CLS_FOR_EACH_TARGET_SAFE (rule, cls_rule, &cls,
> -                                      &target->cls_rule) {
> +            CLS_FOR_EACH_TARGET (rule, cls_rule, &cls, &target->cls_rule) {
>                 classifier_remove(&cls, &rule->cls_rule);
>                 free_rule(rule);
>             }
> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> index ae8d59d..87b5406 100644
> --- a/utilities/ovs-ofctl.c
> +++ b/utilities/ovs-ofctl.c
> @@ -2330,7 +2330,7 @@ fte_free_all(struct classifier *cls)
> {
>     struct fte *fte;
> 
> -    CLS_FOR_EACH_SAFE (fte, rule, cls) {
> +    CLS_FOR_EACH (fte, rule, cls) {
>         classifier_remove(cls, &fte->rule);
>         fte_free(fte);
>     }
> -- 
> 1.7.10.4
> 

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to