Add-on hardware tables initial implementation.
[sliver-openvswitch.git] / datapath / chain.c
index f44fbf0..2b1178b 100644 (file)
@@ -7,8 +7,14 @@
 #include "chain.h"
 #include "flow.h"
 #include "table.h"
+#include <linux/module.h>
 #include <linux/rcupdate.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
+
+static struct sw_table *(*create_hw_table_hook)(void);
+static struct module *hw_table_owner;
+static DEFINE_SPINLOCK(hook_lock);
 
 /* Attempts to append 'table' to the set of tables in 'chain'.  Returns 0 or
  * negative error.  If 'table' is null it is assumed that table creation failed
@@ -32,17 +38,25 @@ struct sw_chain *chain_create(struct datapath *dp)
 {
        struct sw_chain *chain = kzalloc(sizeof *chain, GFP_KERNEL);
        if (chain == NULL)
-               return NULL;
+               goto error;
        chain->dp = dp;
+       chain->owner = try_module_get(hw_table_owner) ? hw_table_owner : NULL;
+       if (chain->owner && create_hw_table_hook) {
+               struct sw_table *hwtable = create_hw_table_hook();
+               if (!hwtable || add_table(chain, hwtable))
+                       goto error;
+       }
 
        if (add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS,
                                                0x741B8CD7, TABLE_HASH_MAX_FLOWS))
-               || add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS))) {
-               chain_destroy(chain);
-               return NULL;
-       }
-
+           || add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS)))
+               goto error;
        return chain;
+
+error:
+       if (chain)
+               chain_destroy(chain);
+       return NULL;
 }
 
 /* Searches 'chain' for a flow matching 'key', which must not have any wildcard
@@ -141,6 +155,7 @@ void chain_destroy(struct sw_chain *chain)
                struct sw_table *t = chain->tables[i];
                t->destroy(t);
        }
+       module_put(chain->owner);
        kfree(chain);
 }
 
@@ -158,3 +173,28 @@ void chain_print_stats(struct sw_chain *chain)
                                        stats.name, stats.n_flows, stats.max_flows);
        }
 }
+
+
+int chain_set_hw_hook(struct sw_table *(*create_hw_table)(void),
+                     struct module *owner)
+{
+       int retval = -EBUSY;
+
+       spin_lock(&hook_lock);
+       if (!create_hw_table_hook) {
+               create_hw_table_hook = create_hw_table;
+               hw_table_owner = owner;
+               retval = 0;
+       }
+       spin_unlock(&hook_lock);
+
+       return retval;
+}
+EXPORT_SYMBOL(chain_set_hw_hook);
+
+void chain_clear_hw_hook(void)
+{
+       create_hw_table_hook = NULL;
+       hw_table_owner = NULL;
+}
+EXPORT_SYMBOL(chain_clear_hw_hook);