Merge branch 'mainstream'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Sun, 4 May 2014 09:19:14 +0000 (11:19 +0200)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Sun, 4 May 2014 09:19:14 +0000 (11:19 +0200)
1  2 
Makefile.am
lib/automake.mk
lib/dpif-netdev.c
lib/dpif.c
lib/netdev-provider.h
lib/netdev.c

diff --combined Makefile.am
@@@ -79,6 -79,7 +79,7 @@@ EXTRA_DIST = 
        PORTING \
        README-lisp \
        REPORTING-BUGS \
+       TODO \
        WHY-OVS \
        boot.sh \
        build-aux/cccl \
@@@ -128,7 -129,6 +129,7 @@@ ro_shell = printf '\043 Generated autom
  
  SUFFIXES += .in
  .in:
 +      @mkdir -p $$(dirname $@)
        $(PERL) $(srcdir)/build-aux/soexpand.pl -I$(srcdir) < $< | \
            sed \
                -e 's,[@]PKIDIR[@],$(PKIDIR),g' \
@@@ -167,10 -167,10 +168,10 @@@ dist-hook-git: distfile
        @if test -e $(srcdir)/.git && (git --version) >/dev/null 2>&1; then \
          (cd datapath && $(MAKE) distfiles);                               \
          (cat distfiles; sed 's|^|datapath/|' datapath/distfiles) |        \
 -          LC_ALL=C sort -u > all-distfiles;                               \
 -        (cd $(srcdir) && git ls-files) | grep -v '\.gitignore$$' |        \
 -          LC_ALL=C sort -u > all-gitfiles;                                \
 -        LC_ALL=C comm -1 -3 all-distfiles all-gitfiles > missing-distfiles; \
 +          LC_ALL=C sort -u > all-distfiles;                                       \
 +        (cd $(srcdir) && git ls-files) | grep -vFf $(srcdir)/.non-distfiles |     \
 +          LC_ALL=C sort -u > all-gitfiles;                                        \
 +        LC_ALL=C comm -1 -3 all-distfiles all-gitfiles > missing-distfiles;       \
          if test -s missing-distfiles; then                                \
            echo "The distribution is missing the following files:";        \
            cat missing-distfiles;                                          \
@@@ -248,7 -248,7 +249,7 @@@ thread-safety-check
        @if test -e '$(srcdir)'/.git && (git --version) >/dev/null 2>&1 && \
           grep -n -f '$(srcdir)'/build-aux/thread-safety-blacklist \
               `git ls-files '$(srcdir)' | grep '\.[ch]$$' \
-                 | $(EGREP) -v '^datapath|^lib/sflow|^third-party'` \
+                 | $(EGREP) -v '^datapath|^lib/sflow|^third-party'` /dev/null \
               | $(EGREP) -v ':[        ]*/?\*'; \
        then \
            echo "See above for list of calls to functions that are"; \
@@@ -308,6 -308,5 +309,6 @@@ include rhel/automake.m
  include xenserver/automake.mk
  include python/automake.mk
  include python/compat/automake.mk
 +include planetlab/automake.mk
  include tutorial/automake.mk
  include vtep/automake.mk
diff --combined lib/automake.mk
@@@ -45,7 -45,9 +45,9 @@@ lib_libopenvswitch_la_SOURCES = 
        lib/crc32c.h \
        lib/csum.c \
        lib/csum.h \
+       lib/daemon.c \
        lib/daemon.h \
+       lib/daemon-private.h \
        lib/dhcp.h \
        lib/dummy.c \
        lib/dummy.h \
        lib/multipath.c \
        lib/multipath.h \
        lib/netdev-dummy.c \
 +      lib/netdev-tunnel.c \
 +      lib/netdev-pltap.c \
        lib/netdev-provider.h \
        lib/netdev-vport.c \
        lib/netdev-vport.h \
        lib/timeval.h \
        lib/token-bucket.c \
        lib/token-bucket.h \
 +      lib/tunalloc.c \
 +      lib/tunalloc.h \
        lib/type-props.h \
        lib/unaligned.h \
        lib/unicode.c \
@@@ -255,7 -253,7 +257,7 @@@ lib_libopenvswitch_la_SOURCES += 
        lib/stream-fd-windows.c
  else
  lib_libopenvswitch_la_SOURCES += \
-       lib/daemon.c \
+       lib/daemon-unix.c \
        lib/latch-unix.c \
        lib/signals.c \
        lib/signals.h \
diff --combined lib/dpif-netdev.c
@@@ -323,7 -323,6 +323,6 @@@ struct pmd_thread 
      pthread_t thread;
      int id;
      atomic_uint change_seq;
-     char *name;
  };
  
  /* Interface to netdev-based datapath. */
@@@ -353,10 -352,11 +352,11 @@@ static int dpif_netdev_open(const struc
                              bool create, struct dpif **);
  static int dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *,
                                        int queue_no, int type,
-                                       const struct flow *,
+                                       const struct miniflow *,
                                        const struct nlattr *userdata);
  static void dp_netdev_execute_actions(struct dp_netdev *dp,
-                                       const struct flow *, struct ofpbuf *, bool may_steal,
+                                       const struct miniflow *,
+                                       struct ofpbuf *, bool may_steal,
                                        struct pkt_metadata *,
                                        const struct nlattr *actions,
                                        size_t actions_len);
@@@ -398,17 -398,10 +398,17 @@@ dpif_netdev_class_is_dummy(const struc
      return class != &dpif_netdev_class;
  }
  
 +static bool
 +dpif_netdev_class_is_planetlab(const struct dpif_class *class)
 +{
 +    return class == &dpif_planetlab_class;
 +}
 +
  static const char *
  dpif_netdev_port_open_type(const struct dpif_class *class, const char *type)
  {
      return strcmp(type, "internal") ? type
 +                  : dpif_netdev_class_is_planetlab(class) ? "pltap"
                    : dpif_netdev_class_is_dummy(class) ? "dummy"
                    : "tap";
  }
@@@ -437,8 -430,7 +437,8 @@@ choose_port(struct dp_netdev *dp, cons
  {
      uint32_t port_no;
  
 -    if (dp->class != &dpif_netdev_class) {
 +    if (dp->class != &dpif_netdev_class && 
 +        dp->class != &dpif_planetlab_class) {
          const char *p;
          int start_no = 0;
  
@@@ -1071,13 -1063,15 +1071,15 @@@ dp_netdev_flow_cast(const struct cls_ru
  }
  
  static struct dp_netdev_flow *
- dp_netdev_lookup_flow(const struct dp_netdev *dp, const struct flow *flow)
+ dp_netdev_lookup_flow(const struct dp_netdev *dp, const struct miniflow *key)
      OVS_EXCLUDED(dp->cls.rwlock)
  {
      struct dp_netdev_flow *netdev_flow;
+     struct cls_rule *rule;
  
      fat_rwlock_rdlock(&dp->cls.rwlock);
-     netdev_flow = dp_netdev_flow_cast(classifier_lookup(&dp->cls, flow, NULL));
+     rule = classifier_lookup_miniflow_first(&dp->cls, key);
+     netdev_flow = dp_netdev_flow_cast(rule);
      fat_rwlock_unlock(&dp->cls.rwlock);
  
      return netdev_flow;
@@@ -1302,6 -1296,7 +1304,7 @@@ dpif_netdev_flow_put(struct dpif *dpif
      struct dp_netdev *dp = get_dp_netdev(dpif);
      struct dp_netdev_flow *netdev_flow;
      struct flow flow;
+     struct miniflow miniflow;
      struct flow_wildcards wc;
      int error;
  
      if (error) {
          return error;
      }
+     miniflow_init(&miniflow, &flow);
  
      ovs_mutex_lock(&dp->flow_mutex);
-     netdev_flow = dp_netdev_lookup_flow(dp, &flow);
+     netdev_flow = dp_netdev_lookup_flow(dp, &miniflow);
      if (!netdev_flow) {
          if (put->flags & DPIF_FP_CREATE) {
              if (hmap_count(&dp->flow_table) < MAX_FLOWS) {
@@@ -1450,6 -1446,7 +1454,7 @@@ dpif_netdev_flow_dump_next(const struc
      struct dp_netdev_flow_state *state = state_;
      struct dp_netdev *dp = get_dp_netdev(dpif);
      struct dp_netdev_flow *netdev_flow;
+     struct flow_wildcards wc;
      int error;
  
      ovs_mutex_lock(&iter->mutex);
          return error;
      }
  
+     minimask_expand(&netdev_flow->cr.match.mask, &wc);
      if (key) {
          struct ofpbuf buf;
  
          ofpbuf_use_stack(&buf, &state->keybuf, sizeof state->keybuf);
-         odp_flow_key_from_flow(&buf, &netdev_flow->flow,
+         odp_flow_key_from_flow(&buf, &netdev_flow->flow, &wc.masks,
                                 netdev_flow->flow.in_port.odp_port);
  
          *key = ofpbuf_data(&buf);
  
      if (key && mask) {
          struct ofpbuf buf;
-         struct flow_wildcards wc;
  
          ofpbuf_use_stack(&buf, &state->maskbuf, sizeof state->maskbuf);
-         minimask_expand(&netdev_flow->cr.match.mask, &wc);
          odp_flow_key_from_mask(&buf, &wc.masks, &netdev_flow->flow,
                                 odp_to_u32(wc.masks.in_port.odp_port),
                                 SIZE_MAX);
@@@ -1530,7 -1527,10 +1535,10 @@@ dpif_netdev_execute(struct dpif *dpif, 
  {
      struct dp_netdev *dp = get_dp_netdev(dpif);
      struct pkt_metadata *md = &execute->md;
-     struct flow key;
+     struct {
+         struct miniflow flow;
+         uint32_t buf[FLOW_U32S];
+     } key;
  
      if (ofpbuf_size(execute->packet) < ETH_HEADER_LEN ||
          ofpbuf_size(execute->packet) > UINT16_MAX) {
      }
  
      /* Extract flow key. */
-     flow_extract(execute->packet, md, &key);
+     miniflow_initialize(&key.flow, key.buf);
+     miniflow_extract(execute->packet, md, &key.flow);
  
      ovs_rwlock_rdlock(&dp->port_rwlock);
-     dp_netdev_execute_actions(dp, &key, execute->packet, false, md,
+     dp_netdev_execute_actions(dp, &key.flow, execute->packet, false, md,
                                execute->actions, execute->actions_len);
      ovs_rwlock_unlock(&dp->port_rwlock);
  
@@@ -1877,8 -1878,6 +1886,6 @@@ pmd_thread_main(void *f_
      int poll_cnt;
      int i;
  
-     f->name = xasprintf("pmd_%u", ovsthread_id_self());
-     set_subprogram_name("%s", f->name);
      poll_cnt = 0;
      poll_list = NULL;
  
@@@ -1918,7 -1917,6 +1925,6 @@@ reload
      }
  
      free(poll_list);
-     free(f->name);
      return NULL;
  }
  
@@@ -1955,7 -1953,7 +1961,7 @@@ dp_netdev_set_pmd_threads(struct dp_net
  
          /* Each thread will distribute all devices rx-queues among
           * themselves. */
-         xpthread_create(&f->thread, NULL, pmd_thread_main, f);
+         f->thread = ovs_thread_create("pmd", pmd_thread_main, f);
      }
  }
  
@@@ -1971,9 -1969,9 +1977,9 @@@ dp_netdev_flow_stats_new_cb(void
  static void
  dp_netdev_flow_used(struct dp_netdev_flow *netdev_flow,
                      const struct ofpbuf *packet,
-                     const struct flow *key)
+                     const struct miniflow *key)
  {
-     uint16_t tcp_flags = ntohs(key->tcp_flags);
+     uint16_t tcp_flags = miniflow_get_tcp_flags(key);
      long long int now = time_msec();
      struct dp_netdev_flow_stats *bucket;
  
@@@ -2013,28 -2011,34 +2019,34 @@@ dp_netdev_input(struct dp_netdev *dp, s
      OVS_REQ_RDLOCK(dp->port_rwlock)
  {
      struct dp_netdev_flow *netdev_flow;
-     struct flow key;
+     struct {
+         struct miniflow flow;
+         uint32_t buf[FLOW_U32S];
+     } key;
  
      if (ofpbuf_size(packet) < ETH_HEADER_LEN) {
          ofpbuf_delete(packet);
          return;
      }
-     flow_extract(packet, md, &key);
-     netdev_flow = dp_netdev_lookup_flow(dp, &key);
+     miniflow_initialize(&key.flow, key.buf);
+     miniflow_extract(packet, md, &key.flow);
+     netdev_flow = dp_netdev_lookup_flow(dp, &key.flow);
      if (netdev_flow) {
          struct dp_netdev_actions *actions;
  
-         dp_netdev_flow_used(netdev_flow, packet, &key);
+         dp_netdev_flow_used(netdev_flow, packet, &key.flow);
  
          actions = dp_netdev_flow_get_actions(netdev_flow);
-         dp_netdev_execute_actions(dp, &key, packet, true, md,
+         dp_netdev_execute_actions(dp, &key.flow, packet, true, md,
                                    actions->actions, actions->size);
          dp_netdev_count_packet(dp, DP_STAT_HIT);
      } else if (dp->handler_queues) {
          dp_netdev_count_packet(dp, DP_STAT_MISS);
          dp_netdev_output_userspace(dp, packet,
-                                    flow_hash_5tuple(&key, 0) % dp->n_handlers,
-                                    DPIF_UC_MISS, &key, NULL);
+                                    miniflow_hash_5tuple(&key.flow, 0)
+                                    % dp->n_handlers,
+                                    DPIF_UC_MISS, &key.flow, NULL);
          ofpbuf_delete(packet);
      }
  }
@@@ -2052,7 -2056,7 +2064,7 @@@ dp_netdev_port_input(struct dp_netdev *
  
  static int
  dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
-                            int queue_no, int type, const struct flow *flow,
+                            int queue_no, int type, const struct miniflow *key,
                             const struct nlattr *userdata)
  {
      struct dp_netdev_queue *q;
          struct dpif_upcall *upcall = &u->upcall;
          struct ofpbuf *buf = &u->buf;
          size_t buf_size;
+         struct flow flow;
  
          upcall->type = type;
  
          ofpbuf_init(buf, buf_size);
  
          /* Put ODP flow. */
-         odp_flow_key_from_flow(buf, flow, flow->in_port.odp_port);
+         miniflow_expand(key, &flow);
+         odp_flow_key_from_flow(buf, &flow, NULL, flow.in_port.odp_port);
          upcall->key = ofpbuf_data(buf);
          upcall->key_len = ofpbuf_size(buf);
  
  
  struct dp_netdev_execute_aux {
      struct dp_netdev *dp;
-     const struct flow *key;
+     const struct miniflow *key;
  };
  
  static void
@@@ -2135,7 -2141,7 +2149,7 @@@ dp_execute_cb(void *aux_, struct ofpbu
          userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
  
          dp_netdev_output_userspace(aux->dp, packet,
-                                    flow_hash_5tuple(aux->key, 0)
+                                    miniflow_hash_5tuple(aux->key, 0)
                                         % aux->dp->n_handlers,
                                     DPIF_UC_ACTION, aux->key,
                                     userdata);
          break;
      }
  
+     case OVS_ACTION_ATTR_HASH: {
+         const struct ovs_action_hash *hash_act;
+         uint32_t hash;
+         hash_act = nl_attr_get(a);
+         if (hash_act->hash_alg == OVS_HASH_ALG_L4) {
+             /* Hash need not be symmetric, nor does it need to include
+              * L2 fields. */
+             hash = miniflow_hash_5tuple(aux->key, hash_act->hash_basis);
+             if (!hash) {
+                 hash = 1; /* 0 is not valid */
+             }
+         } else {
+             VLOG_WARN("Unknown hash algorithm specified for the hash action.");
+             hash = 2;
+         }
+         md->dp_hash = hash;
+         break;
+     }
      case OVS_ACTION_ATTR_RECIRC:
          if (*depth < MAX_RECIRC_DEPTH) {
              struct pkt_metadata recirc_md = *md;
              struct ofpbuf *recirc_packet;
-             const struct ovs_action_recirc *act;
  
              recirc_packet = may_steal ? packet : ofpbuf_clone(packet);
-             act = nl_attr_get(a);
-             recirc_md.recirc_id = act->recirc_id;
-             recirc_md.dp_hash = 0;
-             if (act->hash_alg == OVS_RECIRC_HASH_ALG_L4) {
-                 recirc_md.dp_hash = flow_hash_symmetric_l4(aux->key,
-                                                            act->hash_bias);
-                 if (!recirc_md.dp_hash) {
-                     recirc_md.dp_hash = 1;  /* 0 is not valid */
-                 }
-             }
+             recirc_md.recirc_id = nl_attr_get_u32(a);
  
              (*depth)++;
              dp_netdev_input(aux->dp, recirc_packet, &recirc_md);
  }
  
  static void
- dp_netdev_execute_actions(struct dp_netdev *dp, const struct flow *key,
+ dp_netdev_execute_actions(struct dp_netdev *dp, const struct miniflow *key,
                            struct ofpbuf *packet, bool may_steal,
                            struct pkt_metadata *md,
                            const struct nlattr *actions, size_t actions_len)
                          actions, actions_len, dp_execute_cb);
  }
  
 +#define DPIF_NETDEV_CLASS_FUNCTIONS                   \
 +    dpif_netdev_enumerate,                            \
 +    dpif_netdev_port_open_type,                               \
 +    dpif_netdev_open,                                 \
 +    dpif_netdev_close,                                        \
 +    dpif_netdev_destroy,                              \
 +    dpif_netdev_run,                                  \
 +    dpif_netdev_wait,                                 \
 +    dpif_netdev_get_stats,                            \
 +    dpif_netdev_port_add,                             \
 +    dpif_netdev_port_del,                             \
 +    dpif_netdev_port_query_by_number,                 \
 +    dpif_netdev_port_query_by_name,                   \
 +    NULL,                       /* port_get_pid */    \
 +    dpif_netdev_port_dump_start,                      \
 +    dpif_netdev_port_dump_next,                               \
 +    dpif_netdev_port_dump_done,                               \
 +    dpif_netdev_port_poll,                            \
 +    dpif_netdev_port_poll_wait,                               \
 +    dpif_netdev_flow_get,                             \
 +    dpif_netdev_flow_put,                             \
 +    dpif_netdev_flow_del,                             \
 +    dpif_netdev_flow_flush,                           \
 +    dpif_netdev_flow_dump_state_init,   \
 +    dpif_netdev_flow_dump_start,                      \
 +    dpif_netdev_flow_dump_next,                               \
 +    NULL,                                   \
 +    dpif_netdev_flow_dump_done,                               \
 +    dpif_netdev_flow_dump_state_uninit,     \
 +    dpif_netdev_execute,                              \
 +    NULL,                       /* operate */         \
 +    dpif_netdev_recv_set,                             \
 +    dpif_netdev_handlers_set,           \
 +    dpif_netdev_queue_to_priority,                    \
 +    dpif_netdev_recv,                                 \
 +    dpif_netdev_recv_wait,                            \
 +    dpif_netdev_recv_purge,                           \
 +
  const struct dpif_class dpif_netdev_class = {
      "netdev",
 -    dpif_netdev_enumerate,
 -    dpif_netdev_port_open_type,
 -    dpif_netdev_open,
 -    dpif_netdev_close,
 -    dpif_netdev_destroy,
 -    dpif_netdev_run,
 -    dpif_netdev_wait,
 -    dpif_netdev_get_stats,
 -    dpif_netdev_port_add,
 -    dpif_netdev_port_del,
 -    dpif_netdev_port_query_by_number,
 -    dpif_netdev_port_query_by_name,
 -    NULL,                       /* port_get_pid */
 -    dpif_netdev_port_dump_start,
 -    dpif_netdev_port_dump_next,
 -    dpif_netdev_port_dump_done,
 -    dpif_netdev_port_poll,
 -    dpif_netdev_port_poll_wait,
 -    dpif_netdev_flow_get,
 -    dpif_netdev_flow_put,
 -    dpif_netdev_flow_del,
 -    dpif_netdev_flow_flush,
 -    dpif_netdev_flow_dump_state_init,
 -    dpif_netdev_flow_dump_start,
 -    dpif_netdev_flow_dump_next,
 -    NULL,
 -    dpif_netdev_flow_dump_done,
 -    dpif_netdev_flow_dump_state_uninit,
 -    dpif_netdev_execute,
 -    NULL,                       /* operate */
 -    dpif_netdev_recv_set,
 -    dpif_netdev_handlers_set,
 -    dpif_netdev_queue_to_priority,
 -    dpif_netdev_recv,
 -    dpif_netdev_recv_wait,
 -    dpif_netdev_recv_purge,
 +    DPIF_NETDEV_CLASS_FUNCTIONS
 +};
 +
 +const struct dpif_class dpif_planetlab_class = {
 +    "planetlab",
 +    DPIF_NETDEV_CLASS_FUNCTIONS
  };
  
  static void
@@@ -2326,4 -2334,3 +2350,4 @@@ dpif_dummy_register(bool override
                               "DP PORT NEW-NUMBER",
                               3, 3, dpif_dummy_change_port_number, NULL);
  }
 +
diff --combined lib/dpif.c
@@@ -61,7 -61,6 +61,7 @@@ static const struct dpif_class *base_dp
      &dpif_linux_class,
  #endif
      &dpif_netdev_class,
 +    &dpif_planetlab_class,
  };
  
  struct registered_dpif_class {
@@@ -1135,6 -1134,7 +1135,7 @@@ dpif_execute_helper_cb(void *aux_, stru
      case OVS_ACTION_ATTR_SAMPLE:
      case OVS_ACTION_ATTR_UNSPEC:
      case OVS_ACTION_ATTR_RECIRC:
+     case OVS_ACTION_ATTR_HASH:
      case __OVS_ACTION_ATTR_MAX:
          OVS_NOT_REACHED();
      }
diff --combined lib/netdev-provider.h
  
  /* Generic interface to network devices. */
  
+ #include "connectivity.h"
  #include "netdev.h"
  #include "list.h"
+ #include "seq.h"
  #include "shash.h"
  #include "smap.h"
  
@@@ -38,6 -40,14 +40,14 @@@ struct netdev 
      const struct netdev_class *netdev_class; /* Functions to control
                                                  this device. */
  
+     /* A sequence number which indicates changes in one of 'netdev''s
+      * properties.   It must be nonzero so that users have a value which
+      * they may use as a reset when tracking 'netdev'.
+      *
+      * Minimally, the sequence number is required to change whenever
+      * 'netdev''s flags, features, ethernet address, or carrier changes. */
+     uint64_t change_seq;
      /* The following are protected by 'netdev_mutex' (internal to netdev.c). */
      int n_rxq;
      int ref_cnt;                        /* Times this devices was opened. */
      struct list saved_flags_list; /* Contains "struct netdev_saved_flags". */
  };
  
+ static void
+ netdev_change_seq_changed(struct netdev *netdev)
+ {
+     seq_change(connectivity_seq_get());
+     netdev->change_seq++;
+     if (!netdev->change_seq) {
+         netdev->change_seq++;
+     }
+ }
  const char *netdev_get_type(const struct netdev *);
  const struct netdev_class *netdev_get_class(const struct netdev *);
  const char *netdev_get_name(const struct netdev *);
  struct netdev *netdev_from_name(const char *name);
  void netdev_get_devices(const struct netdev_class *,
                          struct shash *device_list);
+ struct netdev **netdev_get_vports(size_t *size);
  
  /* A data structure for capturing packets received by a network device.
   *
@@@ -671,9 -692,6 +692,9 @@@ extern const struct netdev_class netdev
  extern const struct netdev_class netdev_bsd_class;
  #endif
  
 +extern const struct netdev_class netdev_tunnel_class;
 +extern const struct netdev_class netdev_pltap_class;
 +
  #ifdef  __cplusplus
  }
  #endif
diff --combined lib/netdev.c
@@@ -24,7 -24,6 +24,6 @@@
  #include <string.h>
  #include <unistd.h>
  
- #include "connectivity.h"
  #include "coverage.h"
  #include "dpif.h"
  #include "dynamic-string.h"
@@@ -38,7 -37,6 +37,6 @@@
  #include "openflow/openflow.h"
  #include "packets.h"
  #include "poll-loop.h"
- #include "seq.h"
  #include "shash.h"
  #include "smap.h"
  #include "sset.h"
@@@ -104,13 -102,25 +102,25 @@@ netdev_is_pmd(const struct netdev *netd
  }
  
  static void
- netdev_initialize(void)
+ netdev_class_mutex_initialize(void)
      OVS_EXCLUDED(netdev_class_mutex, netdev_mutex)
  {
      static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
  
      if (ovsthread_once_start(&once)) {
          ovs_mutex_init_recursive(&netdev_class_mutex);
+         ovsthread_once_done(&once);
+     }
+ }
+ static void
+ netdev_initialize(void)
+     OVS_EXCLUDED(netdev_class_mutex, netdev_mutex)
+ {
+     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+     if (ovsthread_once_start(&once)) {
+         netdev_class_mutex_initialize();
  
          fatal_signal_add_hook(restore_all_flags, NULL, NULL, true);
          netdev_vport_patch_register();
          netdev_register_provider(&netdev_tap_class);
          netdev_register_provider(&netdev_bsd_class);
  #endif
 +        netdev_register_provider(&netdev_tunnel_class);
 +        netdev_register_provider(&netdev_pltap_class);
          netdev_dpdk_register();
  
          ovsthread_once_done(&once);
@@@ -143,6 -151,7 +153,7 @@@ netdev_run(void
  {
      struct netdev_registered_class *rc;
  
+     netdev_initialize();
      ovs_mutex_lock(&netdev_class_mutex);
      HMAP_FOR_EACH (rc, hmap_node, &netdev_classes) {
          if (rc->class->run) {
@@@ -194,6 -203,7 +205,7 @@@ netdev_register_provider(const struct n
  {
      int error;
  
+     netdev_class_mutex_initialize();
      ovs_mutex_lock(&netdev_class_mutex);
      if (netdev_lookup_class(new_class->type)) {
          VLOG_WARN("attempted to register duplicate netdev provider: %s",
@@@ -342,6 -352,7 +354,7 @@@ netdev_open(const char *name, const cha
                  memset(netdev, 0, sizeof *netdev);
                  netdev->netdev_class = rc->class;
                  netdev->name = xstrdup(name);
+                 netdev->change_seq = 1;
                  netdev->node = shash_add(&netdev_shash, name, netdev);
  
                  /* By default enable one rx queue per netdev. */
                      int old_ref_cnt;
  
                      atomic_add(&rc->ref_cnt, 1, &old_ref_cnt);
-                     seq_change(connectivity_seq_get());
+                     netdev_change_seq_changed(netdev);
                  } else {
                      free(netdev->name);
                      ovs_assert(list_is_empty(&netdev->saved_flags_list));
@@@ -1602,6 -1613,41 +1615,41 @@@ netdev_get_devices(const struct netdev_
      ovs_mutex_unlock(&netdev_mutex);
  }
  
+ /* Extracts pointers to all 'netdev-vports' into an array 'vports'
+  * and returns it.  Stores the size of the array into '*size'.
+  *
+  * The caller is responsible for freeing 'vports' and must close
+  * each 'netdev-vport' in the list. */
+ struct netdev **
+ netdev_get_vports(size_t *size)
+     OVS_EXCLUDED(netdev_mutex)
+ {
+     struct netdev **vports;
+     struct shash_node *node;
+     size_t n = 0;
+     if (!size) {
+         return NULL;
+     }
+     /* Explicitly allocates big enough chunk of memory. */
+     vports = xmalloc(shash_count(&netdev_shash) * sizeof *vports);
+     ovs_mutex_lock(&netdev_mutex);
+     SHASH_FOR_EACH (node, &netdev_shash) {
+         struct netdev *dev = node->data;
+         if (netdev_vport_is_vport_class(dev->netdev_class)) {
+             dev->ref_cnt++;
+             vports[n] = dev;
+             n++;
+         }
+     }
+     ovs_mutex_unlock(&netdev_mutex);
+     *size = n;
+     return vports;
+ }
  const char *
  netdev_get_type_from_name(const char *name)
  {
@@@ -1651,3 -1697,9 +1699,9 @@@ restore_all_flags(void *aux OVS_UNUSED
          }
      }
  }
+ uint64_t
+ netdev_get_change_seq(const struct netdev *netdev)
+ {
+     return netdev->change_seq;
+ }