Catalli's threaded switch
[sliver-openvswitch.git] / lib / dpif.c
index 5947179..28b936b 100644 (file)
 #include "ofpbuf.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "shash.h"
 #include "svec.h"
 #include "util.h"
 #include "valgrind.h"
-
+#include "fatal-signal.h"
 #include "vlog.h"
-#define THIS_MODULE VLM_dpif
 
-static const struct dpif_class *dpif_classes[] = {
+VLOG_DEFINE_THIS_MODULE(dpif)
+
+static const struct dpif_class *base_dpif_classes[] = {
+#ifdef HAVE_NETLINK
     &dpif_linux_class,
+#endif
     &dpif_netdev_class,
 };
-enum { N_DPIF_CLASSES = ARRAY_SIZE(dpif_classes) };
+
+struct registered_dpif_class {
+    struct dpif_class dpif_class;
+    int refcount;
+};
+static struct shash dpif_classes = SHASH_INITIALIZER(&dpif_classes);
+
 
 /* Rate limit for individual messages going to or from the datapath, output at
  * DBG level.  This is very high because, if these are enabled, it is because
@@ -52,7 +62,7 @@ enum { N_DPIF_CLASSES = ARRAY_SIZE(dpif_classes) };
 static struct vlog_rate_limit dpmsg_rl = VLOG_RATE_LIMIT_INIT(600, 600);
 
 /* Not really much point in logging many dpif errors. */
-static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
+static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
 
 static void log_operation(const struct dpif *, const char *operation,
                           int error);
@@ -63,6 +73,37 @@ static void log_flow_put(struct dpif *, int error,
 static bool should_log_flow_message(int error);
 static void check_rw_odp_flow(struct odp_flow *);
 
+static void
+dp_initialize(void)
+{
+    static int status = -1;
+
+    if (status < 0) {
+        int i;
+#ifdef THREADED
+        struct shash_node *node;
+#endif
+
+        status = 0;
+        for (i = 0; i < ARRAY_SIZE(base_dpif_classes); i++) {
+            dp_register_provider(base_dpif_classes[i]);
+        }
+        
+#ifdef THREADED
+        /* register an exit handler for the registered classes */
+        SHASH_FOR_EACH(node, &dpif_classes) {
+            const struct registered_dpif_class *registered_class = node->data;
+            if (registered_class->dpif_class.exit_hook) {
+                fatal_signal_add_hook(registered_class->dpif_class.exit_hook,
+                        NULL, NULL, true);
+            }
+        }
+#endif
+    }
+}
+
+
+
 /* Performs periodic work needed by all the various kinds of dpifs.
  *
  * If your program opens any dpifs, it must call both this function and
@@ -70,11 +111,11 @@ static void check_rw_odp_flow(struct odp_flow *);
 void
 dp_run(void)
 {
-    int i;
-    for (i = 0; i < N_DPIF_CLASSES; i++) {
-        const struct dpif_class *class = dpif_classes[i];
-        if (class->run) {
-            class->run();
+    struct shash_node *node;
+    SHASH_FOR_EACH(node, &dpif_classes) {
+        const struct registered_dpif_class *registered_class = node->data;
+        if (registered_class->dpif_class.run) {
+            registered_class->dpif_class.run();
         }
     }
 }
@@ -86,27 +127,100 @@ dp_run(void)
 void
 dp_wait(void)
 {
-    int i;
-    for (i = 0; i < N_DPIF_CLASSES; i++) {
-        const struct dpif_class *class = dpif_classes[i];
-        if (class->wait) {
-            class->wait();
+    struct shash_node *node;
+    SHASH_FOR_EACH(node, &dpif_classes) {
+        const struct registered_dpif_class *registered_class = node->data;
+        if (registered_class->dpif_class.wait) {
+            registered_class->dpif_class.wait();
+        }
+    }
+}
+
+#ifdef THREADED
+/* Start the datapath management.
+ * 
+ * This function has been thought for a scenario in which the management of the
+ * datapath module and the ofproto module are performed in separate
+ * threads/processes module. */
+void
+dp_start(void)
+{
+    struct shash_node *node;
+
+    SHASH_FOR_EACH(node, &dpif_classes) {
+        const struct registered_dpif_class *registered_class = node->data;
+        if (registered_class->dpif_class.start) {
+            registered_class->dpif_class.start();
         }
     }
 }
+#endif
+
+
+/* Registers a new datapath provider.  After successful registration, new
+ * datapaths of that type can be opened using dpif_open(). */
+int
+dp_register_provider(const struct dpif_class *new_class)
+{
+    struct registered_dpif_class *registered_class;
+       
+    if (shash_find(&dpif_classes, new_class->type)) {
+        VLOG_WARN("attempted to register duplicate datapath provider: %s",
+                  new_class->type);
+        return EEXIST;
+    }
 
+    registered_class = xmalloc(sizeof *registered_class);
+    memcpy(&registered_class->dpif_class, new_class,
+           sizeof registered_class->dpif_class);
+    registered_class->refcount = 0;
 
-/* Clears 'types' and enumerates the types of all known datapath providers,
- * into it.  The caller must first initialize the svec. */
+    shash_add(&dpif_classes, new_class->type, registered_class);
+
+    return 0;
+}
+
+/* Unregisters a datapath provider.  'type' must have been previously
+ * registered and not currently be in use by any dpifs.  After unregistration
+ * new datapaths of that type cannot be opened using dpif_open(). */
+int
+dp_unregister_provider(const char *type)
+{
+    struct shash_node *node;
+    struct registered_dpif_class *registered_class;
+
+    node = shash_find(&dpif_classes, type);
+    if (!node) {
+        VLOG_WARN("attempted to unregister a datapath provider that is not "
+                  "registered: %s", type);
+        return EAFNOSUPPORT;
+    }
+
+    registered_class = node->data;
+    if (registered_class->refcount) {
+        VLOG_WARN("attempted to unregister in use datapath provider: %s", type);
+        return EBUSY;
+    }
+
+    shash_delete(&dpif_classes, node);
+    free(registered_class);
+
+    return 0;
+}
+
+/* Clears 'types' and enumerates the types of all currently registered datapath
+ * providers into it.  The caller must first initialize the svec. */
 void
 dp_enumerate_types(struct svec *types)
 {
-    int i;
+    struct shash_node *node;
 
+    dp_initialize();
     svec_clear(types);
 
-    for (i = 0; i < N_DPIF_CLASSES; i++) {
-        svec_add(types, dpif_classes[i]->type);
+    SHASH_FOR_EACH(node, &dpif_classes) {
+        const struct registered_dpif_class *registered_class = node->data;
+        svec_add(types, registered_class->dpif_class.type);
     }
 }
 
@@ -119,26 +233,28 @@ dp_enumerate_types(struct svec *types)
 int
 dp_enumerate_names(const char *type, struct svec *names)
 {
-    int i;
+    const struct registered_dpif_class *registered_class;
+    const struct dpif_class *dpif_class;
+    int error;
 
+    dp_initialize();
     svec_clear(names);
 
-    for (i = 0; i < N_DPIF_CLASSES; i++) {
-        const struct dpif_class *class = dpif_classes[i];
+    registered_class = shash_find_data(&dpif_classes, type);
+    if (!registered_class) {
+        VLOG_WARN("could not enumerate unknown type: %s", type);
+        return EAFNOSUPPORT;
+    }
 
-        if (!strcmp(type, class->type)) {
-            int error = class->enumerate ? class->enumerate(names) : 0;
+    dpif_class = &registered_class->dpif_class;
+    error = dpif_class->enumerate ? dpif_class->enumerate(names) : 0;
 
-            if (error) {
-                VLOG_WARN("failed to enumerate %s datapaths: %s",
-                          class->type, strerror(error));
-            }
-
-            return error;
-        }
+    if (error) {
+        VLOG_WARN("failed to enumerate %s datapaths: %s", dpif_class->type,
+                   strerror(error));
     }
 
-    return EAFNOSUPPORT;
+    return error;
 }
 
 /* Parses 'datapath name', which is of the form type@name into its
@@ -165,20 +281,26 @@ do_open(const char *name, const char *type, bool create, struct dpif **dpifp)
 {
     struct dpif *dpif = NULL;
     int error;
-    int i;
+    struct registered_dpif_class *registered_class;
+
+    dp_initialize();
 
     if (!type || *type == '\0') {
         type = "system";
     }
 
-    for (i = 0; i < N_DPIF_CLASSES; i++) {
-        const struct dpif_class *class = dpif_classes[i];
-        if (!strcmp(type, class->type)) {
-            error = class->open(name, type, create, &dpif);
-            goto exit;
-        }
+    registered_class = shash_find_data(&dpif_classes, type);
+    if (!registered_class) {
+        VLOG_WARN("could not create datapath %s of unknown type %s", name,
+                  type);
+        error = EAFNOSUPPORT;
+        goto exit;
+    }
+
+    error = registered_class->dpif_class.open(name, type, create, &dpif);
+    if (!error) {
+        registered_class->refcount++;
     }
-    error = EAFNOSUPPORT;
 
 exit:
     *dpifp = error ? NULL : dpif;
@@ -236,11 +358,15 @@ void
 dpif_close(struct dpif *dpif)
 {
     if (dpif) {
-        char *base_name = dpif->base_name;
-        char *full_name = dpif->full_name;
-        dpif->class->close(dpif);
-        free(base_name);
-        free(full_name);
+        struct registered_dpif_class *registered_class;
+
+        registered_class = shash_find_data(&dpif_classes,
+                dpif->dpif_class->type);
+        assert(registered_class);
+        assert(registered_class->refcount);
+
+        registered_class->refcount--;
+        dpif_uninit(dpif, true);
     }
 }
 
@@ -272,8 +398,8 @@ dpif_base_name(const struct dpif *dpif)
 int
 dpif_get_all_names(const struct dpif *dpif, struct svec *all_names)
 {
-    if (dpif->class->get_all_names) {
-        int error = dpif->class->get_all_names(dpif, all_names);
+    if (dpif->dpif_class->get_all_names) {
+        int error = dpif->dpif_class->get_all_names(dpif, all_names);
         if (error) {
             VLOG_WARN_RL(&error_rl,
                          "failed to retrieve names for datpath %s: %s",
@@ -296,7 +422,7 @@ dpif_delete(struct dpif *dpif)
 
     COVERAGE_INC(dpif_destroy);
 
-    error = dpif->class->delete(dpif);
+    error = dpif->dpif_class->destroy(dpif);
     log_operation(dpif, "delete", error);
     return error;
 }
@@ -306,7 +432,7 @@ dpif_delete(struct dpif *dpif)
 int
 dpif_get_dp_stats(const struct dpif *dpif, struct odp_stats *stats)
 {
-    int error = dpif->class->get_stats(dpif, stats);
+    int error = dpif->dpif_class->get_stats(dpif, stats);
     if (error) {
         memset(stats, 0, sizeof *stats);
     }
@@ -322,7 +448,7 @@ dpif_get_dp_stats(const struct dpif *dpif, struct odp_stats *stats)
 int
 dpif_get_drop_frags(const struct dpif *dpif, bool *drop_frags)
 {
-    int error = dpif->class->get_drop_frags(dpif, drop_frags);
+    int error = dpif->dpif_class->get_drop_frags(dpif, drop_frags);
     if (error) {
         *drop_frags = false;
     }
@@ -336,7 +462,7 @@ dpif_get_drop_frags(const struct dpif *dpif, bool *drop_frags)
 int
 dpif_set_drop_frags(struct dpif *dpif, bool drop_frags)
 {
-    int error = dpif->class->set_drop_frags(dpif, drop_frags);
+    int error = dpif->dpif_class->set_drop_frags(dpif, drop_frags);
     log_operation(dpif, "set_drop_frags", error);
     return error;
 }
@@ -355,7 +481,7 @@ dpif_port_add(struct dpif *dpif, const char *devname, uint16_t flags,
 
     COVERAGE_INC(dpif_port_add);
 
-    error = dpif->class->port_add(dpif, devname, flags, &port_no);
+    error = dpif->dpif_class->port_add(dpif, devname, flags, &port_no);
     if (!error) {
         VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu16,
                     dpif_name(dpif), devname, port_no);
@@ -379,7 +505,7 @@ dpif_port_del(struct dpif *dpif, uint16_t port_no)
 
     COVERAGE_INC(dpif_port_del);
 
-    error = dpif->class->port_del(dpif, port_no);
+    error = dpif->dpif_class->port_del(dpif, port_no);
     log_operation(dpif, "port_del", error);
     return error;
 }
@@ -391,7 +517,7 @@ int
 dpif_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
                           struct odp_port *port)
 {
-    int error = dpif->class->port_query_by_number(dpif, port_no, port);
+    int error = dpif->dpif_class->port_query_by_number(dpif, port_no, port);
     if (!error) {
         VLOG_DBG_RL(&dpmsg_rl, "%s: port %"PRIu16" is device %s",
                     dpif_name(dpif), port_no, port->devname);
@@ -410,7 +536,7 @@ int
 dpif_port_query_by_name(const struct dpif *dpif, const char *devname,
                         struct odp_port *port)
 {
-    int error = dpif->class->port_query_by_name(dpif, devname, port);
+    int error = dpif->dpif_class->port_query_by_name(dpif, devname, port);
     if (!error) {
         VLOG_DBG_RL(&dpmsg_rl, "%s: device %s is on port %"PRIu16,
                     dpif_name(dpif), devname, port->port);
@@ -475,7 +601,7 @@ dpif_port_list(const struct dpif *dpif,
         }
 
         ports = xcalloc(stats.n_ports, sizeof *ports);
-        retval = dpif->class->port_list(dpif, ports, stats.n_ports);
+        retval = dpif->dpif_class->port_list(dpif, ports, stats.n_ports);
         if (retval < 0) {
             /* Hard error. */
             error = -retval;
@@ -523,7 +649,7 @@ exit:
 int
 dpif_port_poll(const struct dpif *dpif, char **devnamep)
 {
-    int error = dpif->class->port_poll(dpif, devnamep);
+    int error = dpif->dpif_class->port_poll(dpif, devnamep);
     if (error) {
         *devnamep = NULL;
     }
@@ -535,7 +661,7 @@ dpif_port_poll(const struct dpif *dpif, char **devnamep)
 void
 dpif_port_poll_wait(const struct dpif *dpif)
 {
-    dpif->class->port_poll_wait(dpif);
+    dpif->dpif_class->port_poll_wait(dpif);
 }
 
 /* Retrieves a list of the port numbers in port group 'group' in 'dpif'.
@@ -556,8 +682,8 @@ dpif_port_group_get(const struct dpif *dpif, uint16_t group,
     *ports = NULL;
     *n_ports = 0;
     for (;;) {
-        int retval = dpif->class->port_group_get(dpif, group,
-                                                 *ports, *n_ports);
+        int retval = dpif->dpif_class->port_group_get(dpif, group,
+                                                      *ports, *n_ports);
         if (retval < 0) {
             /* Hard error. */
             error = -retval;
@@ -595,7 +721,7 @@ dpif_port_group_set(struct dpif *dpif, uint16_t group,
 
     COVERAGE_INC(dpif_port_group_set);
 
-    error = dpif->class->port_group_set(dpif, group, ports, n_ports);
+    error = dpif->dpif_class->port_group_set(dpif, group, ports, n_ports);
     log_operation(dpif, "port_group_set", error);
     return error;
 }
@@ -609,7 +735,7 @@ dpif_flow_flush(struct dpif *dpif)
 
     COVERAGE_INC(dpif_flow_flush);
 
-    error = dpif->class->flow_flush(dpif);
+    error = dpif->dpif_class->flow_flush(dpif);
     log_operation(dpif, "flow_flush", error);
     return error;
 }
@@ -635,10 +761,15 @@ dpif_flow_get(const struct dpif *dpif, struct odp_flow *flow)
     COVERAGE_INC(dpif_flow_get);
 
     check_rw_odp_flow(flow);
-    error = dpif->class->flow_get(dpif, flow, 1);
+    error = dpif->dpif_class->flow_get(dpif, flow, 1);
     if (!error) {
         error = flow->stats.error;
     }
+    if (error) {
+        /* Make the results predictable on error. */
+        memset(&flow->stats, 0, sizeof flow->stats);
+        flow->n_actions = 0;
+    }
     if (should_log_flow_message(error)) {
         log_flow_operation(dpif, "flow_get", error, flow);
     }
@@ -685,7 +816,7 @@ dpif_flow_get_multiple(const struct dpif *dpif,
         check_rw_odp_flow(&flows[i]);
     }
 
-    error = dpif->class->flow_get(dpif, flows, n);
+    error = dpif->dpif_class->flow_get(dpif, flows, n);
     log_operation(dpif, "flow_get_multiple", error);
     return error;
 }
@@ -713,7 +844,7 @@ dpif_flow_put(struct dpif *dpif, struct odp_flow_put *put)
 
     COVERAGE_INC(dpif_flow_put);
 
-    error = dpif->class->flow_put(dpif, put);
+    error = dpif->dpif_class->flow_put(dpif, put);
     if (should_log_flow_message(error)) {
         log_flow_put(dpif, error, put);
     }
@@ -735,7 +866,7 @@ dpif_flow_del(struct dpif *dpif, struct odp_flow *flow)
     check_rw_odp_flow(flow);
     memset(&flow->stats, 0, sizeof flow->stats);
 
-    error = dpif->class->flow_del(dpif, flow);
+    error = dpif->dpif_class->flow_del(dpif, flow);
     if (should_log_flow_message(error)) {
         log_flow_operation(dpif, "delete flow", error, flow);
     }
@@ -764,7 +895,7 @@ dpif_flow_list(const struct dpif *dpif, struct odp_flow flows[], size_t n,
             flows[i].n_actions = 0;
         }
     }
-    retval = dpif->class->flow_list(dpif, flows, n);
+    retval = dpif->dpif_class->flow_list(dpif, flows, n);
     if (retval < 0) {
         *n_out = 0;
         VLOG_WARN_RL(&error_rl, "%s: flow list failed (%s)",
@@ -841,7 +972,8 @@ dpif_execute(struct dpif *dpif, uint16_t in_port,
 
     COVERAGE_INC(dpif_execute);
     if (n_actions > 0) {
-        error = dpif->class->execute(dpif, in_port, actions, n_actions, buf);
+        error = dpif->dpif_class->execute(dpif, in_port, actions,
+                                          n_actions, buf);
     } else {
         error = 0;
     }
@@ -868,7 +1000,7 @@ dpif_execute(struct dpif *dpif, uint16_t in_port,
 int
 dpif_recv_get_mask(const struct dpif *dpif, int *listen_mask)
 {
-    int error = dpif->class->recv_get_mask(dpif, listen_mask);
+    int error = dpif->dpif_class->recv_get_mask(dpif, listen_mask);
     if (error) {
         *listen_mask = 0;
     }
@@ -882,14 +1014,50 @@ dpif_recv_get_mask(const struct dpif *dpif, int *listen_mask)
 int
 dpif_recv_set_mask(struct dpif *dpif, int listen_mask)
 {
-    int error = dpif->class->recv_set_mask(dpif, listen_mask);
+    int error = dpif->dpif_class->recv_set_mask(dpif, listen_mask);
     log_operation(dpif, "recv_set_mask", error);
     return error;
 }
 
+/* Retrieve the sFlow sampling probability.  '*probability' is expressed as the
+ * number of packets out of UINT_MAX to sample, e.g. probability/UINT_MAX is
+ * the probability of sampling a given packet.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.  EOPNOTSUPP
+ * indicates that 'dpif' does not support sFlow sampling. */
+int
+dpif_get_sflow_probability(const struct dpif *dpif, uint32_t *probability)
+{
+    int error = (dpif->dpif_class->get_sflow_probability
+                 ? dpif->dpif_class->get_sflow_probability(dpif, probability)
+                 : EOPNOTSUPP);
+    if (error) {
+        *probability = 0;
+    }
+    log_operation(dpif, "get_sflow_probability", error);
+    return error;
+}
+
+/* Set the sFlow sampling probability.  'probability' is expressed as the
+ * number of packets out of UINT_MAX to sample, e.g. probability/UINT_MAX is
+ * the probability of sampling a given packet.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.  EOPNOTSUPP
+ * indicates that 'dpif' does not support sFlow sampling. */
+int
+dpif_set_sflow_probability(struct dpif *dpif, uint32_t probability)
+{
+    int error = (dpif->dpif_class->set_sflow_probability
+                 ? dpif->dpif_class->set_sflow_probability(dpif, probability)
+                 : EOPNOTSUPP);
+    log_operation(dpif, "set_sflow_probability", error);
+    return error;
+}
+
 /* Attempts to receive a message from 'dpif'.  If successful, stores the
  * message into '*packetp'.  The message, if one is received, will begin with
- * 'struct odp_msg' as a header.  Only messages of the types selected with
+ * 'struct odp_msg' as a header, and will have at least DPIF_RECV_MSG_PADDING
+ * bytes of headroom.  Only messages of the types selected with
  * dpif_set_listen_mask() will ordinarily be received (but if a message type is
  * enabled and then later disabled, some stragglers might pop up).
  *
@@ -898,10 +1066,12 @@ dpif_recv_set_mask(struct dpif *dpif, int listen_mask)
 int
 dpif_recv(struct dpif *dpif, struct ofpbuf **packetp)
 {
-    int error = dpif->class->recv(dpif, packetp);
+    int error = dpif->dpif_class->recv(dpif, packetp);
     if (!error) {
+        struct ofpbuf *buf = *packetp;
+
+        assert(ofpbuf_headroom(buf) >= DPIF_RECV_MSG_PADDING);
         if (VLOG_IS_DBG_ENABLED()) {
-            struct ofpbuf *buf = *packetp;
             struct odp_msg *msg = buf->data;
             void *payload = msg + 1;
             size_t payload_len = buf->size - sizeof *msg;
@@ -910,6 +1080,7 @@ dpif_recv(struct dpif *dpif, struct ofpbuf **packetp)
                         "%zu on port %"PRIu16": %s", dpif_name(dpif),
                         (msg->type == _ODPL_MISS_NR ? "miss"
                          : msg->type == _ODPL_ACTION_NR ? "action"
+                         : msg->type == _ODPL_SFLOW_NR ? "sFlow"
                          : "<unknown>"),
                         payload_len, msg->port, s);
             free(s);
@@ -936,7 +1107,7 @@ dpif_recv_purge(struct dpif *dpif)
         return error;
     }
 
-    for (i = 0; i < stats.max_miss_queue + stats.max_action_queue; i++) {
+    for (i = 0; i < stats.max_miss_queue + stats.max_action_queue + stats.max_sflow_queue; i++) {
         struct ofpbuf *buf;
         error = dpif_recv(dpif, &buf);
         if (error) {
@@ -952,7 +1123,7 @@ dpif_recv_purge(struct dpif *dpif)
 void
 dpif_recv_wait(struct dpif *dpif)
 {
-    dpif->class->recv_wait(dpif);
+    dpif->dpif_class->recv_wait(dpif);
 }
 
 /* Obtains the NetFlow engine type and engine ID for 'dpif' into '*engine_type'
@@ -964,17 +1135,57 @@ dpif_get_netflow_ids(const struct dpif *dpif,
     *engine_type = dpif->netflow_engine_type;
     *engine_id = dpif->netflow_engine_id;
 }
+
+/* Translates OpenFlow queue ID 'queue_id' (in host byte order) into a priority
+ * value for use in the ODPAT_SET_PRIORITY action.  On success, returns 0 and
+ * stores the priority into '*priority'.  On failure, returns a positive errno
+ * value and stores 0 into '*priority'. */
+int
+dpif_queue_to_priority(const struct dpif *dpif, uint32_t queue_id,
+                       uint32_t *priority)
+{
+    int error = (dpif->dpif_class->queue_to_priority
+                 ? dpif->dpif_class->queue_to_priority(dpif, queue_id,
+                                                       priority)
+                 : EOPNOTSUPP);
+    if (error) {
+        *priority = 0;
+    }
+    log_operation(dpif, "queue_to_priority", error);
+    return error;
+}
 \f
 void
-dpif_init(struct dpif *dpif, const struct dpif_class *class, const char *name,
+dpif_init(struct dpif *dpif, const struct dpif_class *dpif_class,
+          const char *name,
           uint8_t netflow_engine_type, uint8_t netflow_engine_id)
 {
-    dpif->class = class;
+    dpif->dpif_class = dpif_class;
     dpif->base_name = xstrdup(name);
-    dpif->full_name = xasprintf("%s@%s", class->type, name);
+    dpif->full_name = xasprintf("%s@%s", dpif_class->type, name);
     dpif->netflow_engine_type = netflow_engine_type;
     dpif->netflow_engine_id = netflow_engine_id;
 }
+
+/* Undoes the results of initialization.
+ *
+ * Normally this function only needs to be called from dpif_close().
+ * However, it may be called by providers due to an error on opening
+ * that occurs after initialization.  It this case dpif_close() would
+ * never be called. */
+void
+dpif_uninit(struct dpif *dpif, bool close)
+{
+    char *base_name = dpif->base_name;
+    char *full_name = dpif->full_name;
+
+    if (close) {
+        dpif->dpif_class->close(dpif);
+    }
+
+    free(base_name);
+    free(full_name);
+}
 \f
 static void
 log_operation(const struct dpif *dpif, const char *operation, int error)