Thanks, applied.

On Tue, Aug 27, 2013 at 05:33:59PM -0700, Ethan Jackson wrote:
> Acked-by: Ethan Jackson <et...@nicira.com>
> 
> Thanks looks good.
> 
> On Tue, Aug 27, 2013 at 5:16 PM, Ben Pfaff <b...@nicira.com> wrote:
> > We have a call chain like this:
> >
> >     iface_configure_qos() calls
> >         netdev_dump_queues(), which calls
> >             netdev_linux_dump_queues(), which calls back through 'cb' to
> >                 qos_unixctl_show_cb(), which calls
> >                     netdev_delete_queue(), which calls
> >                         netdev_linux_delete_queue().
> >
> > Both netdev_dump_queues() and netdev_linux_delete_queue() take the same
> > mutex in the same netdev, which deadlocks.
> >
> > This commit fixes the problem by getting rid of the callback.
> >
> > netdev_linux_dump_queue_stats() would benefit from the same treatment but
> > it's less urgent because I don't see any callbacks from that function that
> > call back into a netdev function.
> >
> > Bug #19319.
> > Reported-by: Scott Hendricks <shendri...@vmware.com>
> > Signed-off-by: Ben Pfaff <b...@nicira.com>
> > ---
> >  lib/netdev-dummy.c    |    4 ++-
> >  lib/netdev-linux.c    |   79 +++++++++++++++++++++++++++++++++------------
> >  lib/netdev-provider.h |   49 +++++++++++++++++++---------
> >  lib/netdev-vport.c    |    4 ++-
> >  lib/netdev.c          |   86 
> > ++++++++++++++++++++++++++++++++++++++-----------
> >  lib/netdev.h          |   41 ++++++++++++++++++++---
> >  vswitchd/bridge.c     |   62 +++++++++++++++--------------------
> >  7 files changed, 226 insertions(+), 99 deletions(-)
> >
> > diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
> > index ac26564..16a6b0b 100644
> > --- a/lib/netdev-dummy.c
> > +++ b/lib/netdev-dummy.c
> > @@ -708,7 +708,9 @@ static const struct netdev_class dummy_class = {
> >      NULL,                       /* set_queue */
> >      NULL,                       /* delete_queue */
> >      NULL,                       /* get_queue_stats */
> > -    NULL,                       /* dump_queues */
> > +    NULL,                       /* queue_dump_start */
> > +    NULL,                       /* queue_dump_next */
> > +    NULL,                       /* queue_dump_done */
> >      NULL,                       /* dump_queue_stats */
> >
> >      NULL,                       /* get_in4 */
> > diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> > index 80fa39b..26fb32d 100644
> > --- a/lib/netdev-linux.c
> > +++ b/lib/netdev-linux.c
> > @@ -2100,35 +2100,35 @@ start_queue_dump(const struct netdev *netdev, 
> > struct nl_dump *dump)
> >      return true;
> >  }
> >
> > +struct netdev_linux_queue_state {
> > +    unsigned int *queues;
> > +    size_t cur_queue;
> > +    size_t n_queues;
> > +};
> > +
> >  static int
> > -netdev_linux_dump_queues(const struct netdev *netdev_,
> > -                         netdev_dump_queues_cb *cb, void *aux)
> > +netdev_linux_queue_dump_start(const struct netdev *netdev_, void **statep)
> >  {
> > -    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
> > +    const struct netdev_linux *netdev = netdev_linux_cast(netdev_);
> >      int error;
> >
> >      ovs_mutex_lock(&netdev->mutex);
> >      error = tc_query_qdisc(netdev_);
> >      if (!error) {
> >          if (netdev->tc->ops->class_get) {
> > -            struct tc_queue *queue, *next_queue;
> > -            struct smap details;
> > -
> > -            smap_init(&details);
> > -            HMAP_FOR_EACH_SAFE (queue, next_queue, hmap_node,
> > -                                &netdev->tc->queues) {
> > -                int retval;
> > -
> > -                smap_clear(&details);
> > -
> > -                retval = netdev->tc->ops->class_get(netdev_, queue, 
> > &details);
> > -                if (!retval) {
> > -                    (*cb)(queue->queue_id, &details, aux);
> > -                } else {
> > -                    error = retval;
> > -                }
> > +            struct netdev_linux_queue_state *state;
> > +            struct tc_queue *queue;
> > +            size_t i;
> > +
> > +            *statep = state = xmalloc(sizeof *state);
> > +            state->n_queues = hmap_count(&netdev->tc->queues);
> > +            state->cur_queue = 0;
> > +            state->queues = xmalloc(state->n_queues * sizeof 
> > *state->queues);
> > +
> > +            i = 0;
> > +            HMAP_FOR_EACH (queue, hmap_node, &netdev->tc->queues) {
> > +                state->queues[i++] = queue->queue_id;
> >              }
> > -            smap_destroy(&details);
> >          } else {
> >              error = EOPNOTSUPP;
> >          }
> > @@ -2139,6 +2139,41 @@ netdev_linux_dump_queues(const struct netdev 
> > *netdev_,
> >  }
> >
> >  static int
> > +netdev_linux_queue_dump_next(const struct netdev *netdev_, void *state_,
> > +                             unsigned int *queue_idp, struct smap *details)
> > +{
> > +    const struct netdev_linux *netdev = netdev_linux_cast(netdev_);
> > +    struct netdev_linux_queue_state *state = state_;
> > +    int error = EOF;
> > +
> > +    ovs_mutex_lock(&netdev->mutex);
> > +    while (state->cur_queue < state->n_queues) {
> > +        unsigned int queue_id = state->queues[state->cur_queue++];
> > +        struct tc_queue *queue = tc_find_queue(netdev_, queue_id);
> > +
> > +        if (queue) {
> > +            *queue_idp = queue_id;
> > +            error = netdev->tc->ops->class_get(netdev_, queue, details);
> > +            break;
> > +        }
> > +    }
> > +    ovs_mutex_unlock(&netdev->mutex);
> > +
> > +    return error;
> > +}
> > +
> > +static int
> > +netdev_linux_queue_dump_done(const struct netdev *netdev OVS_UNUSED,
> > +                             void *state_)
> > +{
> > +    struct netdev_linux_queue_state *state = state_;
> > +
> > +    free(state->queues);
> > +    free(state);
> > +    return 0;
> > +}
> > +
> > +static int
> >  netdev_linux_dump_queue_stats(const struct netdev *netdev_,
> >                                netdev_dump_queue_stats_cb *cb, void *aux)
> >  {
> > @@ -2580,7 +2615,9 @@ netdev_linux_change_seq(const struct netdev *netdev_)
> >      netdev_linux_set_queue,                                     \
> >      netdev_linux_delete_queue,                                  \
> >      netdev_linux_get_queue_stats,                               \
> > -    netdev_linux_dump_queues,                                   \
> > +    netdev_linux_queue_dump_start,                              \
> > +    netdev_linux_queue_dump_next,                               \
> > +    netdev_linux_queue_dump_done,                               \
> >      netdev_linux_dump_queue_stats,                              \
> >                                                                  \
> >      netdev_linux_get_in4,                                       \
> > diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
> > index 70a5188..9ab58fb 100644
> > --- a/lib/netdev-provider.h
> > +++ b/lib/netdev-provider.h
> > @@ -478,22 +478,39 @@ struct netdev_class {
> >      int (*get_queue_stats)(const struct netdev *netdev, unsigned int 
> > queue_id,
> >                             struct netdev_queue_stats *stats);
> >
> > -    /* Iterates over all of 'netdev''s queues, calling 'cb' with the 
> > queue's
> > -     * ID, its configuration, and the 'aux' specified by the caller.  The 
> > order
> > -     * of iteration is unspecified, but (when successful) each queue is 
> > visited
> > -     * exactly once.
> > -     *
> > -     * 'cb' will not modify or free the 'details' argument passed in.  It 
> > may
> > -     * delete or modify the queue passed in as its 'queue_id' argument.  
> > It may
> > -     * modify but will not delete any other queue within 'netdev'.  If 'cb'
> > -     * adds new queues, then ->dump_queues is allowed to visit some queues
> > -     * twice or not at all.
> > -     */
> > -    int (*dump_queues)(const struct netdev *netdev,
> > -                       void (*cb)(unsigned int queue_id,
> > -                                  const struct smap *details,
> > -                                  void *aux),
> > -                       void *aux);
> > +    /* Attempts to begin dumping the queues in 'netdev'.  On success, 
> > returns 0
> > +     * and initializes '*statep' with any data needed for iteration.  On
> > +     * failure, returns a positive errno value.
> > +     *
> > +     * May be NULL if 'netdev' does not support QoS at all. */
> > +    int (*queue_dump_start)(const struct netdev *netdev, void **statep);
> > +
> > +    /* Attempts to retrieve another queue from 'netdev' for 'state', which 
> > was
> > +     * initialized by a successful call to the 'queue_dump_start' function 
> > for
> > +     * 'netdev'.  On success, stores a queue ID into '*queue_id' and fills
> > +     * 'details' with the configuration of the queue with that ID.  
> > Returns EOF
> > +     * if the last queue has been dumped, or a positive errno value on 
> > error.
> > +     * This function will not be called again once it returns nonzero once 
> > for
> > +     * a given iteration (but the 'queue_dump_done' function will be called
> > +     * afterward).
> > +     *
> > +     * The caller initializes and clears 'details' before calling this
> > +     * function.  The caller takes ownership of the string key-values pairs
> > +     * added to 'details'.
> > +     *
> > +     * The returned contents of 'details' should be documented as valid 
> > for the
> > +     * given 'type' in the "other_config" column in the "Queue" table in
> > +     * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
> > +     *
> > +     * May be NULL if 'netdev' does not support QoS at all. */
> > +    int (*queue_dump_next)(const struct netdev *netdev, void *state,
> > +                           unsigned int *queue_id, struct smap *details);
> > +
> > +    /* Releases resources from 'netdev' for 'state', which was initialized 
> > by a
> > +     * successful call to the 'queue_dump_start' function for 'netdev'.
> > +     *
> > +     * May be NULL if 'netdev' does not support QoS at all. */
> > +    int (*queue_dump_done)(const struct netdev *netdev, void *state);
> >
> >      /* Iterates over all of 'netdev''s queues, calling 'cb' with the 
> > queue's
> >       * ID, its statistics, and the 'aux' specified by the caller.  The 
> > order of
> > diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
> > index 0f5dd7a..af50597 100644
> > --- a/lib/netdev-vport.c
> > +++ b/lib/netdev-vport.c
> > @@ -727,7 +727,9 @@ get_stats(const struct netdev *netdev, struct 
> > netdev_stats *stats)
> >      NULL,                       /* set_queue */             \
> >      NULL,                       /* delete_queue */          \
> >      NULL,                       /* get_queue_stats */       \
> > -    NULL,                       /* dump_queues */           \
> > +    NULL,                       /* queue_dump_start */      \
> > +    NULL,                       /* queue_dump_next */       \
> > +    NULL,                       /* queue_dump_done */       \
> >      NULL,                       /* dump_queue_stats */      \
> >                                                              \
> >      NULL,                       /* get_in4 */               \
> > diff --git a/lib/netdev.c b/lib/netdev.c
> > index bf942a0..5ed6062 100644
> > --- a/lib/netdev.c
> > +++ b/lib/netdev.c
> > @@ -1398,30 +1398,78 @@ netdev_get_queue_stats(const struct netdev *netdev, 
> > unsigned int queue_id,
> >      return retval;
> >  }
> >
> > -/* Iterates over all of 'netdev''s queues, calling 'cb' with the queue's 
> > ID,
> > - * its configuration, and the 'aux' specified by the caller.  The order of
> > - * iteration is unspecified, but (when successful) each queue is visited
> > - * exactly once.
> > +/* Initializes 'dump' to begin dumping the queues in a netdev.
> > + *
> > + * This function provides no status indication.  An error status for the 
> > entire
> > + * dump operation is provided when it is completed by calling
> > + * netdev_queue_dump_done().
> > + */
> > +void
> > +netdev_queue_dump_start(struct netdev_queue_dump *dump,
> > +                        const struct netdev *netdev)
> > +{
> > +    dump->netdev = netdev_ref(netdev);
> > +    if (netdev->netdev_class->queue_dump_start) {
> > +        dump->error = netdev->netdev_class->queue_dump_start(netdev,
> > +                                                             &dump->state);
> > +    } else {
> > +        dump->error = EOPNOTSUPP;
> > +    }
> > +}
> > +
> > +/* Attempts to retrieve another queue from 'dump', which must have been
> > + * initialized with netdev_queue_dump_start().  On success, stores a new 
> > queue
> > + * ID into '*queue_id', fills 'details' with configuration details for the
> > + * queue, and returns true.  On failure, returns false.
> >   *
> > - * Calling this function may be more efficient than calling 
> > netdev_get_queue()
> > - * for every queue.
> > + * Queues are not necessarily dumped in increasing order of queue ID (or 
> > any
> > + * other predictable order).
> >   *
> > - * 'cb' must not modify or free the 'details' argument passed in.  It may
> > - * delete or modify the queue passed in as its 'queue_id' argument.  It may
> > - * modify but must not delete any other queue within 'netdev'.  'cb' 
> > should not
> > - * add new queues because this may cause some queues to be visited twice 
> > or not
> > - * at all.
> > + * Failure might indicate an actual error or merely that the last queue has
> > + * been dumped.  An error status for the entire dump operation is provided 
> > when
> > + * it is completed by calling netdev_queue_dump_done().
> >   *
> > - * Returns 0 if successful, otherwise a positive errno value.  On error, 
> > some
> > - * configured queues may not have been included in the iteration. */
> > + * The returned contents of 'details' should be documented as valid for the
> > + * given 'type' in the "other_config" column in the "Queue" table in
> > + * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
> > + *
> > + * The caller must initialize 'details' (e.g. with smap_init()) before 
> > calling
> > + * this function.  This function will clear and replace its contents.  The
> > + * caller must free 'details' when it is no longer needed (e.g. with
> > + * smap_destroy()). */
> > +bool
> > +netdev_queue_dump_next(struct netdev_queue_dump *dump,
> > +                       unsigned int *queue_id, struct smap *details)
> > +{
> > +    const struct netdev *netdev = dump->netdev;
> > +
> > +    if (dump->error) {
> > +        return false;
> > +    }
> > +
> > +    dump->error = netdev->netdev_class->queue_dump_next(netdev, 
> > dump->state,
> > +                                                        queue_id, details);
> > +
> > +    if (dump->error) {
> > +        netdev->netdev_class->queue_dump_done(netdev, dump->state);
> > +        return false;
> > +    }
> > +    return true;
> > +}
> > +
> > +/* Completes queue table dump operation 'dump', which must have been
> > + * initialized with netdev_queue_dump_start().  Returns 0 if the dump 
> > operation
> > + * was error-free, otherwise a positive errno value describing the 
> > problem. */
> >  int
> > -netdev_dump_queues(const struct netdev *netdev,
> > -                   netdev_dump_queues_cb *cb, void *aux)
> > +netdev_queue_dump_done(struct netdev_queue_dump *dump)
> >  {
> > -    const struct netdev_class *class = netdev->netdev_class;
> > -    return (class->dump_queues
> > -            ? class->dump_queues(netdev, cb, aux)
> > -            : EOPNOTSUPP);
> > +    const struct netdev *netdev = dump->netdev;
> > +    if (!dump->error && netdev->netdev_class->queue_dump_done) {
> > +        dump->error = netdev->netdev_class->queue_dump_done(netdev,
> > +                                                            dump->state);
> > +    }
> > +    netdev_close(dump->netdev);
> > +    return dump->error == EOF ? 0 : dump->error;
> >  }
> >
> >  /* Iterates over all of 'netdev''s queues, calling 'cb' with the queue's 
> > ID,
> > diff --git a/lib/netdev.h b/lib/netdev.h
> > index 287f6cc..bafa50e 100644
> > --- a/lib/netdev.h
> > +++ b/lib/netdev.h
> > @@ -47,7 +47,17 @@ extern "C" {
> >   *      These functions are conditionally thread-safe: they may be called 
> > from
> >   *      different threads only on different netdev_rx objects.  (The 
> > client may
> >   *      create multiple netdev_rx objects for a single netdev and access 
> > each
> > - *      of those from a different thread.) */
> > + *      of those from a different thread.)
> > + *
> > + *    NETDEV_FOR_EACH_QUEUE
> > + *    netdev_queue_dump_next()
> > + *    netdev_queue_dump_done()
> > + *
> > + *      These functions are conditionally thread-safe: they may be called 
> > from
> > + *      different threads only on different netdev_queue_dump objects.  
> > (The
> > + *      client may create multiple netdev_queue_dump objects for a single
> > + *      netdev and access each of those from a different thread.)
> > + */
> >
> >  struct netdev;
> >  struct netdev_class;
> > @@ -271,10 +281,31 @@ int netdev_delete_queue(struct netdev *, unsigned int 
> > queue_id);
> >  int netdev_get_queue_stats(const struct netdev *, unsigned int queue_id,
> >                             struct netdev_queue_stats *);
> >
> > -typedef void netdev_dump_queues_cb(unsigned int queue_id,
> > -                                   const struct smap *details, void *aux);
> > -int netdev_dump_queues(const struct netdev *,
> > -                       netdev_dump_queues_cb *, void *aux);
> > +struct netdev_queue_dump {
> > +    struct netdev *netdev;
> > +    int error;
> > +    void *state;
> > +};
> > +void netdev_queue_dump_start(struct netdev_queue_dump *,
> > +                             const struct netdev *);
> > +bool netdev_queue_dump_next(struct netdev_queue_dump *,
> > +                            unsigned int *queue_id, struct smap *details);
> > +int netdev_queue_dump_done(struct netdev_queue_dump *);
> > +
> > +/* Iterates through each queue in NETDEV, using DUMP as state.  Fills 
> > QUEUE_ID
> > + * and DETAILS with information about queues.  The client must initialize 
> > and
> > + * destroy DETAILS.
> > + *
> > + * Arguments all have pointer type.
> > + *
> > + * If you break out of the loop, then you need to free the dump structure 
> > by
> > + * hand using netdev_queue_dump_done(). */
> > +#define NETDEV_QUEUE_FOR_EACH(QUEUE_ID, DETAILS, DUMP, NETDEV)  \
> > +    for (netdev_queue_dump_start(DUMP, NETDEV);                 \
> > +         (netdev_queue_dump_next(DUMP, QUEUE_ID, DETAILS)       \
> > +          ? true                                                \
> > +          : (netdev_queue_dump_done(DUMP), false));             \
> > +        )
> >
> >  typedef void netdev_dump_queue_stats_cb(unsigned int queue_id,
> >                                          struct netdev_queue_stats *,
> > diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
> > index a11e646..f993ba1 100644
> > --- a/vswitchd/bridge.c
> > +++ b/vswitchd/bridge.c
> > @@ -2557,13 +2557,11 @@ struct qos_unixctl_show_cbdata {
> >  };
> >
> >  static void
> > -qos_unixctl_show_cb(unsigned int queue_id,
> > -                    const struct smap *details,
> > -                    void *aux)
> > +qos_unixctl_show_queue(unsigned int queue_id,
> > +                       const struct smap *details,
> > +                       struct iface *iface,
> > +                       struct ds *ds)
> >  {
> > -    struct qos_unixctl_show_cbdata *data = aux;
> > -    struct ds *ds = data->ds;
> > -    struct iface *iface = data->iface;
> >      struct netdev_queue_stats stats;
> >      struct smap_node *node;
> >      int error;
> > @@ -2607,8 +2605,6 @@ qos_unixctl_show(struct unixctl_conn *conn, int argc 
> > OVS_UNUSED,
> >      struct iface *iface;
> >      const char *type;
> >      struct smap_node *node;
> > -    struct qos_unixctl_show_cbdata data;
> > -    int error;
> >
> >      iface = iface_find(argv[1]);
> >      if (!iface) {
> > @@ -2619,20 +2615,22 @@ qos_unixctl_show(struct unixctl_conn *conn, int 
> > argc OVS_UNUSED,
> >      netdev_get_qos(iface->netdev, &type, &smap);
> >
> >      if (*type != '\0') {
> > +        struct netdev_queue_dump dump;
> > +        struct smap details;
> > +        unsigned int queue_id;
> > +
> >          ds_put_format(&ds, "QoS: %s %s\n", iface->name, type);
> >
> >          SMAP_FOR_EACH (node, &smap) {
> >              ds_put_format(&ds, "%s: %s\n", node->key, node->value);
> >          }
> >
> > -        data.ds = &ds;
> > -        data.iface = iface;
> > -        error = netdev_dump_queues(iface->netdev, qos_unixctl_show_cb, 
> > &data);
> > -
> > -        if (error) {
> > -            ds_put_format(&ds, "failed to dump queues: %s",
> > -                          ovs_strerror(error));
> > +        smap_init(&details);
> > +        NETDEV_QUEUE_FOR_EACH (&queue_id, &details, &dump, iface->netdev) {
> > +            qos_unixctl_show_queue(queue_id, &details, iface, &ds);
> >          }
> > +        smap_destroy(&details);
> > +
> >          unixctl_command_reply(conn, ds_cstr(&ds));
> >      } else {
> >          ds_put_format(&ds, "QoS not configured on %s\n", iface->name);
> > @@ -3605,11 +3603,6 @@ iface_clear_db_record(const struct ovsrec_interface 
> > *if_cfg)
> >      }
> >  }
> >
> > -struct iface_delete_queues_cbdata {
> > -    struct netdev *netdev;
> > -    const struct ovsdb_datum *queues;
> > -};
> > -
> >  static bool
> >  queue_ids_include(const struct ovsdb_datum *queues, int64_t target)
> >  {
> > @@ -3620,17 +3613,6 @@ queue_ids_include(const struct ovsdb_datum *queues, 
> > int64_t target)
> >  }
> >
> >  static void
> > -iface_delete_queues(unsigned int queue_id,
> > -                    const struct smap *details OVS_UNUSED, void *cbdata_)
> > -{
> > -    struct iface_delete_queues_cbdata *cbdata = cbdata_;
> > -
> > -    if (!queue_ids_include(cbdata->queues, queue_id)) {
> > -        netdev_delete_queue(cbdata->netdev, queue_id);
> > -    }
> > -}
> > -
> > -static void
> >  iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
> >  {
> >      struct ofpbuf queues_buf;
> > @@ -3640,7 +3622,10 @@ iface_configure_qos(struct iface *iface, const 
> > struct ovsrec_qos *qos)
> >      if (!qos || qos->type[0] == '\0' || qos->n_queues < 1) {
> >          netdev_set_qos(iface->netdev, NULL, NULL);
> >      } else {
> > -        struct iface_delete_queues_cbdata cbdata;
> > +        const struct ovsdb_datum *queues;
> > +        struct netdev_queue_dump dump;
> > +        unsigned int queue_id;
> > +        struct smap details;
> >          bool queue_zero;
> >          size_t i;
> >
> > @@ -3648,10 +3633,15 @@ iface_configure_qos(struct iface *iface, const 
> > struct ovsrec_qos *qos)
> >          netdev_set_qos(iface->netdev, qos->type, &qos->other_config);
> >
> >          /* Deconfigure queues that were deleted. */
> > -        cbdata.netdev = iface->netdev;
> > -        cbdata.queues = ovsrec_qos_get_queues(qos, OVSDB_TYPE_INTEGER,
> > -                                              OVSDB_TYPE_UUID);
> > -        netdev_dump_queues(iface->netdev, iface_delete_queues, &cbdata);
> > +        queues = ovsrec_qos_get_queues(qos, OVSDB_TYPE_INTEGER,
> > +                                       OVSDB_TYPE_UUID);
> > +        smap_init(&details);
> > +        NETDEV_QUEUE_FOR_EACH (&queue_id, &details, &dump, iface->netdev) {
> > +            if (!queue_ids_include(queues, queue_id)) {
> > +                netdev_delete_queue(iface->netdev, queue_id);
> > +            }
> > +        }
> > +        smap_destroy(&details);
> >
> >          /* Configure queues for 'iface'. */
> >          queue_zero = false;
> > --
> > 1.7.10.4
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > http://openvswitch.org/mailman/listinfo/dev
_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to