netfilter: flowtable: Add API for registering to flow table events
authorPaul Blakey <paulb@mellanox.com>
Thu, 12 Mar 2020 10:23:05 +0000 (12:23 +0200)
committerDavid S. Miller <davem@davemloft.net>
Thu, 12 Mar 2020 22:00:38 +0000 (15:00 -0700)
Let drivers to add their cb allowing them to receive flow offload events
of type TC_SETUP_CLSFLOWER (REPLACE/DEL/STATS) for flows managed by the
flow table.

Signed-off-by: Paul Blakey <paulb@mellanox.com>
Reviewed-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/netfilter/nf_flow_table.h
net/netfilter/nf_flow_table_core.c
net/netfilter/nf_flow_table_offload.c

index e0f709d9d5475fcb44b8fad32861b19337a64d38..d9d0945b696ed5356631fa5324b0b46fff37bb76 100644 (file)
@@ -44,6 +44,7 @@ struct nf_flowtable {
        struct delayed_work             gc_work;
        unsigned int                    flags;
        struct flow_block               flow_block;
+       struct mutex                    flow_block_lock; /* Guards flow_block */
        possible_net_t                  net;
 };
 
@@ -129,6 +130,11 @@ struct nf_flow_route {
 struct flow_offload *flow_offload_alloc(struct nf_conn *ct);
 void flow_offload_free(struct flow_offload *flow);
 
+int nf_flow_table_offload_add_cb(struct nf_flowtable *flow_table,
+                                flow_setup_cb_t *cb, void *cb_priv);
+void nf_flow_table_offload_del_cb(struct nf_flowtable *flow_table,
+                                 flow_setup_cb_t *cb, void *cb_priv);
+
 int flow_offload_route_init(struct flow_offload *flow,
                            const struct nf_flow_route *route);
 
index 8af28e10b4e6268bb95e0eca87941b8bc498ae2b..4af0327992cfa0d2922a080e4015677373637d94 100644 (file)
@@ -372,6 +372,50 @@ static void nf_flow_offload_work_gc(struct work_struct *work)
        queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ);
 }
 
+int nf_flow_table_offload_add_cb(struct nf_flowtable *flow_table,
+                                flow_setup_cb_t *cb, void *cb_priv)
+{
+       struct flow_block *block = &flow_table->flow_block;
+       struct flow_block_cb *block_cb;
+       int err = 0;
+
+       mutex_lock(&flow_table->flow_block_lock);
+       block_cb = flow_block_cb_lookup(block, cb, cb_priv);
+       if (block_cb) {
+               err = -EEXIST;
+               goto unlock;
+       }
+
+       block_cb = flow_block_cb_alloc(cb, cb_priv, cb_priv, NULL);
+       if (IS_ERR(block_cb)) {
+               err = PTR_ERR(block_cb);
+               goto unlock;
+       }
+
+       list_add_tail(&block_cb->list, &block->cb_list);
+
+unlock:
+       mutex_unlock(&flow_table->flow_block_lock);
+       return err;
+}
+EXPORT_SYMBOL_GPL(nf_flow_table_offload_add_cb);
+
+void nf_flow_table_offload_del_cb(struct nf_flowtable *flow_table,
+                                 flow_setup_cb_t *cb, void *cb_priv)
+{
+       struct flow_block *block = &flow_table->flow_block;
+       struct flow_block_cb *block_cb;
+
+       mutex_lock(&flow_table->flow_block_lock);
+       block_cb = flow_block_cb_lookup(block, cb, cb_priv);
+       if (block_cb)
+               list_del(&block_cb->list);
+       else
+               WARN_ON(true);
+       mutex_unlock(&flow_table->flow_block_lock);
+}
+EXPORT_SYMBOL_GPL(nf_flow_table_offload_del_cb);
+
 static int nf_flow_nat_port_tcp(struct sk_buff *skb, unsigned int thoff,
                                __be16 port, __be16 new_port)
 {
@@ -494,6 +538,7 @@ int nf_flow_table_init(struct nf_flowtable *flowtable)
 
        INIT_DEFERRABLE_WORK(&flowtable->gc_work, nf_flow_offload_work_gc);
        flow_block_init(&flowtable->flow_block);
+       mutex_init(&flowtable->flow_block_lock);
 
        err = rhashtable_init(&flowtable->rhashtable,
                              &nf_flow_offload_rhash_params);
@@ -550,11 +595,13 @@ void nf_flow_table_free(struct nf_flowtable *flow_table)
        mutex_lock(&flowtable_lock);
        list_del(&flow_table->list);
        mutex_unlock(&flowtable_lock);
+
        cancel_delayed_work_sync(&flow_table->gc_work);
        nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL);
        nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, flow_table);
        nf_flow_table_offload_flush(flow_table);
        rhashtable_destroy(&flow_table->rhashtable);
+       mutex_destroy(&flow_table->flow_block_lock);
 }
 EXPORT_SYMBOL_GPL(nf_flow_table_free);
 
index 06f00cdc389100fb197abba44e7fd208d4da23b8..f5afdf023bb9d2061d747d1432f363ca91e8c6a4 100644 (file)
@@ -610,6 +610,7 @@ static int nf_flow_offload_tuple(struct nf_flowtable *flowtable,
        if (cmd == FLOW_CLS_REPLACE)
                cls_flow.rule = flow_rule->rule;
 
+       mutex_lock(&flowtable->flow_block_lock);
        list_for_each_entry(block_cb, block_cb_list, list) {
                err = block_cb->cb(TC_SETUP_CLSFLOWER, &cls_flow,
                                   block_cb->cb_priv);
@@ -618,6 +619,7 @@ static int nf_flow_offload_tuple(struct nf_flowtable *flowtable,
 
                i++;
        }
+       mutex_unlock(&flowtable->flow_block_lock);
 
        return i;
 }
@@ -692,8 +694,10 @@ static void flow_offload_tuple_stats(struct flow_offload_work *offload,
                             FLOW_CLS_STATS,
                             &offload->flow->tuplehash[dir].tuple, &extack);
 
+       mutex_lock(&flowtable->flow_block_lock);
        list_for_each_entry(block_cb, &flowtable->flow_block.cb_list, list)
                block_cb->cb(TC_SETUP_CLSFLOWER, &cls_flow, block_cb->cb_priv);
+       mutex_unlock(&flowtable->flow_block_lock);
        memcpy(stats, &cls_flow.stats, sizeof(*stats));
 }