+static void log_flow_put_message(struct dpif *, const struct dpif_flow_put *,
+ int error);
+static void log_flow_del_message(struct dpif *, const struct dpif_flow_del *,
+ int error);
+static void log_execute_message(struct dpif *, const struct dpif_execute *,
+ int error);
+
+static void
+dp_initialize(void)
+{
+ static int status = -1;
+
+ if (status < 0) {
+ int i;
+
+ status = 0;
+ for (i = 0; i < ARRAY_SIZE(base_dpif_classes); i++) {
+ dp_register_provider(base_dpif_classes[i]);
+ }
+ }
+}
+
+/* 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 (sset_contains(&dpif_blacklist, new_class->type)) {
+ VLOG_DBG("attempted to register blacklisted provider: %s",
+ new_class->type);
+ return EINVAL;
+ }
+
+ 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);
+ registered_class->dpif_class = new_class;
+ registered_class->refcount = 0;
+
+ 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;
+}
+
+/* Blacklists a provider. Causes future calls of dp_register_provider() with
+ * a dpif_class which implements 'type' to fail. */
+void
+dp_blacklist_provider(const char *type)
+{
+ sset_add(&dpif_blacklist, type);
+}
+
+/* Clears 'types' and enumerates the types of all currently registered datapath
+ * providers into it. The caller must first initialize the sset. */
+void
+dp_enumerate_types(struct sset *types)
+{
+ struct shash_node *node;
+
+ dp_initialize();
+ sset_clear(types);
+
+ SHASH_FOR_EACH(node, &dpif_classes) {
+ const struct registered_dpif_class *registered_class = node->data;
+ sset_add(types, registered_class->dpif_class->type);
+ }
+}
+
+/* Clears 'names' and enumerates the names of all known created datapaths with
+ * the given 'type'. The caller must first initialize the sset. Returns 0 if
+ * successful, otherwise a positive errno value.
+ *
+ * Some kinds of datapaths might not be practically enumerable. This is not
+ * considered an error. */
+int
+dp_enumerate_names(const char *type, struct sset *names)
+{
+ const struct registered_dpif_class *registered_class;
+ const struct dpif_class *dpif_class;
+ int error;
+
+ dp_initialize();
+ sset_clear(names);
+
+ registered_class = shash_find_data(&dpif_classes, type);
+ if (!registered_class) {
+ VLOG_WARN("could not enumerate unknown type: %s", type);
+ return EAFNOSUPPORT;
+ }
+
+ 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", dpif_class->type,
+ strerror(error));
+ }
+
+ return error;
+}
+
+/* Parses 'datapath_name_', which is of the form [type@]name into its
+ * component pieces. 'name' and 'type' must be freed by the caller.
+ *
+ * The returned 'type' is normalized, as if by dpif_normalize_type(). */
+void
+dp_parse_name(const char *datapath_name_, char **name, char **type)
+{
+ char *datapath_name = xstrdup(datapath_name_);
+ char *separator;
+
+ separator = strchr(datapath_name, '@');
+ if (separator) {
+ *separator = '\0';
+ *type = datapath_name;
+ *name = xstrdup(dpif_normalize_type(separator + 1));
+ } else {
+ *name = datapath_name;
+ *type = xstrdup(dpif_normalize_type(NULL));
+ }
+}