+static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
+
+static void log_flow_message(const struct dpif *dpif, int error,
+ const char *operation,
+ const struct nlattr *key, size_t key_len,
+ const struct dpif_flow_stats *stats,
+ const struct nlattr *actions, size_t actions_len);
+static void log_operation(const struct dpif *, const char *operation,
+ int error);
+static bool should_log_flow_message(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 (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;
+}
+
+/* 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);