[ovs-dev] [threaded-learning v2 11/25] ofproto: Add a ref_count to "struct rule" to protect it from being freed.

Ethan Jackson ethan at nicira.com
Fri Sep 13 00:40:45 UTC 2013


Acked-by: Ethan Jackson <ethan at nicira.com>


On Thu, Sep 12, 2013 at 12:39 AM, Ben Pfaff <blp at nicira.com> wrote:
> Taking a read-lock on the 'rwlock' member of struct rule prevents members
> of the rule from changing.  This is a short-term use of the 'rwlock': one
> takes the lock, reads some members, and releases the lock.
>
> Taking a read-lock on the 'rwlock' also prevents the rule from being freed.
> This is often a relatively long-term need.  For example, until now flow
> translation has held the rwlock in xlate_table_action() across the entire
> recursive translation, which can call into a great deal of different code
> across multiple files.
>
> This commit switches to using a reference count for this second purpose
> of struct rule's rwlock.  This means that all the code that previously
> held a read-lock on the rwlock across deep stacks of functions can now
> simply keep a reference.
>
> Signed-off-by: Ben Pfaff <blp at nicira.com>
> ---
>  ofproto/ofproto-dpif-upcall.c |    2 +-
>  ofproto/ofproto-dpif-xlate.c  |   17 +++++++----------
>  ofproto/ofproto-dpif.c        |   37 +++++++++++++++++++------------------
>  ofproto/ofproto-dpif.h        |   12 +++++-------
>  ofproto/ofproto-provider.h    |   23 +++++++++++++++--------
>  ofproto/ofproto.c             |   32 +++++++++++++++++++++++++-------
>  6 files changed, 72 insertions(+), 51 deletions(-)
>
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index 54f441b..ae856a4 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -725,7 +725,7 @@ execute_flow_miss(struct flow_miss *miss, struct dpif_op *ops, size_t *n_ops)
>              xlate_actions_for_side_effects(&xin);
>          }
>      }
> -    rule_dpif_release(rule);
> +    rule_dpif_unref(rule);
>
>      if (miss->xout.odp_actions.size) {
>          LIST_FOR_EACH (packet, list_node, &miss->packets) {
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index eb6a1f9..cb09123 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -1669,7 +1669,6 @@ compose_output_action(struct xlate_ctx *ctx, ofp_port_t ofp_port)
>
>  static void
>  xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule)
> -    OVS_RELEASES(rule)
>  {
>      struct rule_dpif *old_rule = ctx->rule;
>      struct rule_actions *actions;
> @@ -1685,8 +1684,6 @@ xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule)
>      rule_actions_unref(actions);
>      ctx->rule = old_rule;
>      ctx->recurse--;
> -
> -    rule_dpif_release(rule);
>  }
>
>  static void
> @@ -1697,7 +1694,6 @@ xlate_table_action(struct xlate_ctx *ctx,
>          struct rule_dpif *rule;
>          ofp_port_t old_in_port = ctx->xin->flow.in_port.ofp_port;
>          uint8_t old_table_id = ctx->table_id;
> -        bool got_rule;
>
>          ctx->table_id = table_id;
>
> @@ -1705,18 +1701,16 @@ xlate_table_action(struct xlate_ctx *ctx,
>           * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
>           * have surprising behavior). */
>          ctx->xin->flow.in_port.ofp_port = in_port;
> -        got_rule = rule_dpif_lookup_in_table(ctx->xbridge->ofproto,
> -                                             &ctx->xin->flow, &ctx->xout->wc,
> -                                             table_id, &rule);
> +        rule_dpif_lookup_in_table(ctx->xbridge->ofproto,
> +                                  &ctx->xin->flow, &ctx->xout->wc,
> +                                  table_id, &rule);
>          ctx->xin->flow.in_port.ofp_port = old_in_port;
>
>          if (ctx->xin->resubmit_hook) {
>              ctx->xin->resubmit_hook(ctx->xin, rule, ctx->recurse);
>          }
>
> -        if (got_rule) {
> -            xlate_recursively(ctx, rule);
> -        } else if (may_packet_in) {
> +        if (!rule && may_packet_in) {
>              struct xport *xport;
>
>              /* XXX
> @@ -1729,7 +1723,10 @@ xlate_table_action(struct xlate_ctx *ctx,
>              choose_miss_rule(xport ? xport->config : 0,
>                               ctx->xbridge->miss_rule,
>                               ctx->xbridge->no_packet_in_rule, &rule);
> +        }
> +        if (rule) {
>              xlate_recursively(ctx, rule);
> +            rule_dpif_unref(rule);
>          }
>
>          ctx->table_id = old_table_id;
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index d16422b..6f1a4e5 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -1371,7 +1371,7 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id,
>
>      if (rule_dpif_lookup_in_table(ofproto, &fm.match.flow, NULL, TBL_INTERNAL,
>                                    rulep)) {
> -        rule_dpif_release(*rulep);
> +        rule_dpif_unref(*rulep);
>      } else {
>          NOT_REACHED();
>      }
> @@ -4171,7 +4171,7 @@ facet_is_controller_flow(struct facet *facet)
>          is_controller = ofpacts_len > 0
>              && ofpacts->type == OFPACT_CONTROLLER
>              && ofpact_next(ofpacts) >= ofpact_end(ofpacts, ofpacts_len);
> -        rule_dpif_release(rule);
> +        rule_dpif_unref(rule);
>
>          return is_controller;
>      }
> @@ -4266,7 +4266,7 @@ facet_check_consistency(struct facet *facet)
>      rule_dpif_lookup(facet->ofproto, &facet->flow, NULL, &rule);
>      xlate_in_init(&xin, facet->ofproto, &facet->flow, rule, 0, NULL);
>      xlate_actions(&xin, &xout);
> -    rule_dpif_release(rule);
> +    rule_dpif_unref(rule);
>
>      ok = ofpbuf_equal(&facet->xout.odp_actions, &xout.odp_actions)
>          && facet->xout.slow == xout.slow;
> @@ -4364,7 +4364,7 @@ facet_revalidate(struct facet *facet)
>          || memcmp(&facet->xout.wc, &xout.wc, sizeof xout.wc)) {
>          facet_remove(facet);
>          xlate_out_uninit(&xout);
> -        rule_dpif_release(new_rule);
> +        rule_dpif_unref(new_rule);
>          return false;
>      }
>
> @@ -4396,7 +4396,7 @@ facet_revalidate(struct facet *facet)
>      facet->used = MAX(facet->used, new_rule->up.created);
>
>      xlate_out_uninit(&xout);
> -    rule_dpif_release(new_rule);
> +    rule_dpif_unref(new_rule);
>      return true;
>  }
>
> @@ -4429,7 +4429,7 @@ flow_push_stats(struct ofproto_dpif *ofproto, struct flow *flow,
>      xin.resubmit_stats = stats;
>      xin.may_learn = may_learn;
>      xlate_actions_for_side_effects(&xin);
> -    rule_dpif_release(rule);
> +    rule_dpif_unref(rule);
>  }
>
>  static void
> @@ -4815,7 +4815,6 @@ bool
>  rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto,
>                            const struct flow *flow, struct flow_wildcards *wc,
>                            uint8_t table_id, struct rule_dpif **rule)
> -    OVS_TRY_RDLOCK(true, (*rule)->up.rwlock)
>  {
>      struct cls_rule *cls_rule;
>      struct classifier *cls;
> @@ -4850,11 +4849,7 @@ rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto,
>      }
>
>      *rule = rule_dpif_cast(rule_from_cls_rule(cls_rule));
> -    if (*rule && ovs_rwlock_tryrdlock(&(*rule)->up.rwlock)) {
> -        /* The rule is in the process of being removed.  Best we can do is
> -         * pretend it isn't there. */
> -        *rule = NULL;
> -    }
> +    rule_dpif_ref(*rule);
>      ovs_rwlock_unlock(&cls->rwlock);
>
>      return *rule != NULL;
> @@ -4866,18 +4861,24 @@ rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto,
>  void
>  choose_miss_rule(enum ofputil_port_config config, struct rule_dpif *miss_rule,
>                   struct rule_dpif *no_packet_in_rule, struct rule_dpif **rule)
> -    OVS_NO_THREAD_SAFETY_ANALYSIS
>  {
>      *rule = config & OFPUTIL_PC_NO_PACKET_IN ? no_packet_in_rule : miss_rule;
> -    ovs_rwlock_rdlock(&(*rule)->up.rwlock);
> +    rule_dpif_ref(*rule);
> +}
> +
> +void
> +rule_dpif_ref(struct rule_dpif *rule)
> +{
> +    if (rule) {
> +        ofproto_rule_ref(&rule->up);
> +    }
>  }
>
>  void
> -rule_dpif_release(struct rule_dpif *rule)
> -    OVS_NO_THREAD_SAFETY_ANALYSIS
> +rule_dpif_unref(struct rule_dpif *rule)
>  {
>      if (rule) {
> -        ovs_rwlock_unlock(&rule->up.rwlock);
> +        ofproto_rule_unref(&rule->up);
>      }
>  }
>
> @@ -5593,7 +5594,7 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
>          xlate_out_uninit(&trace.xout);
>      }
>
> -    rule_dpif_release(rule);
> +    rule_dpif_unref(rule);
>  }
>
>  /* Runs a self-check of flow translations in 'ofproto'.  Appends a message to
> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
> index befd458..14a9669 100644
> --- a/ofproto/ofproto-dpif.h
> +++ b/ofproto/ofproto-dpif.h
> @@ -61,15 +61,14 @@ struct OVS_LOCKABLE rule_dpif;
>   *   actions into datapath actions. */
>
>  void rule_dpif_lookup(struct ofproto_dpif *, const struct flow *,
> -                      struct flow_wildcards *, struct rule_dpif **rule)
> -    OVS_ACQ_RDLOCK(*rule);
> +                      struct flow_wildcards *, struct rule_dpif **rule);
>
>  bool rule_dpif_lookup_in_table(struct ofproto_dpif *, const struct flow *,
>                                 struct flow_wildcards *, uint8_t table_id,
> -                               struct rule_dpif **rule)
> -    OVS_TRY_RDLOCK(true, *rule);
> +                               struct rule_dpif **rule);
>
> -void rule_dpif_release(struct rule_dpif *rule) OVS_RELEASES(rule);
> +void rule_dpif_ref(struct rule_dpif *);
> +void rule_dpif_unref(struct rule_dpif *);
>
>  void rule_dpif_credit_stats(struct rule_dpif *rule ,
>                              const struct dpif_flow_stats *);
> @@ -86,8 +85,7 @@ void rule_dpif_reduce_timeouts(struct rule_dpif *rule, uint16_t idle_timeout,
>  void choose_miss_rule(enum ofputil_port_config,
>                        struct rule_dpif *miss_rule,
>                        struct rule_dpif *no_packet_in_rule,
> -                      struct rule_dpif **rule)
> -    OVS_ACQ_RDLOCK(*rule);
> +                      struct rule_dpif **rule);
>
>  bool ofproto_has_vlan_splinters(const struct ofproto_dpif *);
>  ofp_port_t vsp_realdev_to_vlandev(const struct ofproto_dpif *,
> diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
> index f681991..571f881 100644
> --- a/ofproto/ofproto-provider.h
> +++ b/ofproto/ofproto-provider.h
> @@ -27,6 +27,7 @@
>  #include "ofp-errors.h"
>  #include "ofp-util.h"
>  #include "ofproto/ofproto.h"
> +#include "ovs-atomic.h"
>  #include "ovs-thread.h"
>  #include "shash.h"
>  #include "simap.h"
> @@ -219,6 +220,7 @@ struct oftable {
>  struct rule {
>      struct ofproto *ofproto;     /* The ofproto that contains this rule. */
>      struct cls_rule cr;          /* In owning ofproto's classifier. */
> +    atomic_uint ref_count;
>
>      struct ofoperation *pending; /* Operation now in progress, if nonnull. */
>
> @@ -241,14 +243,16 @@ struct rule {
>      struct eviction_group *eviction_group; /* NULL if not in any group. */
>
>      /* The rwlock is used to protect those elements in struct rule which are
> -     * accessed by multiple threads.  While maintaining a pointer to struct
> -     * rule, threads are required to hold a readlock.  The main ofproto code is
> -     * guaranteed not to evict the rule, or change any of the elements "Guarded
> -     * by rwlock" without holding the writelock.
> -     *
> -     * A rule will not be evicted unless both its own and its classifier's
> -     * write locks are held.  Therefore, while holding a classifier readlock,
> -     * one can be assured that write locked rules are safe to reference. */
> +     * accessed by multiple threads.  The main ofproto code is guaranteed not
> +     * to change any of the elements "Guarded by rwlock" without holding the
> +     * writelock.
> +     *
> +     * While maintaining a pointer to struct rule, threads are required to hold
> +     * a readlock on the classifier that holds the rule or increment the rule's
> +     * ref_count.
> +     *
> +     * A rule will not be evicted unless its classifier's write lock is
> +     * held. */
>      struct ovs_rwlock rwlock;
>
>      /* Guarded by rwlock. */
> @@ -266,6 +270,9 @@ struct rule {
>                                   * is expirable, otherwise empty. */
>  };
>
> +void ofproto_rule_ref(struct rule *);
> +void ofproto_rule_unref(struct rule *);
> +
>  /* A set of actions within a "struct rule".
>   *
>   *
> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
> index 9d073ed..a68a33a 100644
> --- a/ofproto/ofproto.c
> +++ b/ofproto/ofproto.c
> @@ -229,7 +229,6 @@ static int init_ports(struct ofproto *);
>  static void reinit_ports(struct ofproto *);
>
>  /* rule. */
> -static void ofproto_rule_destroy(struct rule *);
>  static void ofproto_rule_destroy__(struct rule *);
>  static void ofproto_rule_send_removed(struct rule *, uint8_t reason);
>  static bool rule_is_modifiable(const struct rule *);
> @@ -2327,12 +2326,30 @@ update_mtu(struct ofproto *p, struct ofport *port)
>      }
>  }
>
> -static void
> -ofproto_rule_destroy(struct rule *rule)
> +void
> +ofproto_rule_ref(struct rule *rule)
>  {
>      if (rule) {
> -        rule->ofproto->ofproto_class->rule_destruct(rule);
> -        ofproto_rule_destroy__(rule);
> +        unsigned int orig;
> +
> +        atomic_add(&rule->ref_count, 1, &orig);
> +        ovs_assert(orig != 0);
> +    }
> +}
> +
> +void
> +ofproto_rule_unref(struct rule *rule)
> +{
> +    if (rule) {
> +        unsigned int orig;
> +
> +        atomic_sub(&rule->ref_count, 1, &orig);
> +        if (orig == 1) {
> +            rule->ofproto->ofproto_class->rule_destruct(rule);
> +            ofproto_rule_destroy__(rule);
> +        } else {
> +            ovs_assert(orig != 0);
> +        }
>      }
>  }
>
> @@ -3690,6 +3707,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
>      /* Initialize base state. */
>      rule->ofproto = ofproto;
>      cls_rule_move(&rule->cr, &cr);
> +    atomic_init(&rule->ref_count, 1);
>      rule->pending = NULL;
>      rule->flow_cookie = fm->new_cookie;
>      rule->created = rule->modified = rule->used = time_msec();
> @@ -5667,13 +5685,13 @@ ofopgroup_complete(struct ofopgroup *group)
>              } else {
>                  ovs_rwlock_wrlock(&rule->rwlock);
>                  oftable_remove_rule(rule);
> -                ofproto_rule_destroy(rule);
> +                ofproto_rule_unref(rule);
>              }
>              break;
>
>          case OFOPERATION_DELETE:
>              ovs_assert(!op->error);
> -            ofproto_rule_destroy(rule);
> +            ofproto_rule_unref(rule);
>              op->rule = NULL;
>              break;
>
> --
> 1.7.10.4
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev



More information about the dev mailing list