Merge tag 'wireless-drivers-2021-09-07' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / net / core / flow_offload.c
index 715b67f..6beaea1 100644 (file)
@@ -321,13 +321,13 @@ EXPORT_SYMBOL(flow_block_cb_setup_simple);
 static DEFINE_MUTEX(flow_indr_block_lock);
 static LIST_HEAD(flow_block_indr_list);
 static LIST_HEAD(flow_block_indr_dev_list);
+static LIST_HEAD(flow_indir_dev_list);
 
 struct flow_indr_dev {
        struct list_head                list;
        flow_indr_block_bind_cb_t       *cb;
        void                            *cb_priv;
        refcount_t                      refcnt;
-       struct rcu_head                 rcu;
 };
 
 static struct flow_indr_dev *flow_indr_dev_alloc(flow_indr_block_bind_cb_t *cb,
@@ -346,6 +346,33 @@ static struct flow_indr_dev *flow_indr_dev_alloc(flow_indr_block_bind_cb_t *cb,
        return indr_dev;
 }
 
+struct flow_indir_dev_info {
+       void *data;
+       struct net_device *dev;
+       struct Qdisc *sch;
+       enum tc_setup_type type;
+       void (*cleanup)(struct flow_block_cb *block_cb);
+       struct list_head list;
+       enum flow_block_command command;
+       enum flow_block_binder_type binder_type;
+       struct list_head *cb_list;
+};
+
+static void existing_qdiscs_register(flow_indr_block_bind_cb_t *cb, void *cb_priv)
+{
+       struct flow_block_offload bo;
+       struct flow_indir_dev_info *cur;
+
+       list_for_each_entry(cur, &flow_indir_dev_list, list) {
+               memset(&bo, 0, sizeof(bo));
+               bo.command = cur->command;
+               bo.binder_type = cur->binder_type;
+               INIT_LIST_HEAD(&bo.cb_list);
+               cb(cur->dev, cur->sch, cb_priv, cur->type, &bo, cur->data, cur->cleanup);
+               list_splice(&bo.cb_list, cur->cb_list);
+       }
+}
+
 int flow_indr_dev_register(flow_indr_block_bind_cb_t *cb, void *cb_priv)
 {
        struct flow_indr_dev *indr_dev;
@@ -367,6 +394,7 @@ int flow_indr_dev_register(flow_indr_block_bind_cb_t *cb, void *cb_priv)
        }
 
        list_add(&indr_dev->list, &flow_block_indr_dev_list);
+       existing_qdiscs_register(cb, cb_priv);
        mutex_unlock(&flow_indr_block_lock);
 
        return 0;
@@ -463,7 +491,59 @@ out:
 }
 EXPORT_SYMBOL(flow_indr_block_cb_alloc);
 
-int flow_indr_dev_setup_offload(struct net_device *dev, struct Qdisc *sch,
+static struct flow_indir_dev_info *find_indir_dev(void *data)
+{
+       struct flow_indir_dev_info *cur;
+
+       list_for_each_entry(cur, &flow_indir_dev_list, list) {
+               if (cur->data == data)
+                       return cur;
+       }
+       return NULL;
+}
+
+static int indir_dev_add(void *data, struct net_device *dev, struct Qdisc *sch,
+                        enum tc_setup_type type, void (*cleanup)(struct flow_block_cb *block_cb),
+                        struct flow_block_offload *bo)
+{
+       struct flow_indir_dev_info *info;
+
+       info = find_indir_dev(data);
+       if (info)
+               return -EEXIST;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->data = data;
+       info->dev = dev;
+       info->sch = sch;
+       info->type = type;
+       info->cleanup = cleanup;
+       info->command = bo->command;
+       info->binder_type = bo->binder_type;
+       info->cb_list = bo->cb_list_head;
+
+       list_add(&info->list, &flow_indir_dev_list);
+       return 0;
+}
+
+static int indir_dev_remove(void *data)
+{
+       struct flow_indir_dev_info *info;
+
+       info = find_indir_dev(data);
+       if (!info)
+               return -ENOENT;
+
+       list_del(&info->list);
+
+       kfree(info);
+       return 0;
+}
+
+int flow_indr_dev_setup_offload(struct net_device *dev,        struct Qdisc *sch,
                                enum tc_setup_type type, void *data,
                                struct flow_block_offload *bo,
                                void (*cleanup)(struct flow_block_cb *block_cb))
@@ -471,6 +551,12 @@ int flow_indr_dev_setup_offload(struct net_device *dev, struct Qdisc *sch,
        struct flow_indr_dev *this;
 
        mutex_lock(&flow_indr_block_lock);
+
+       if (bo->command == FLOW_BLOCK_BIND)
+               indir_dev_add(data, dev, sch, type, cleanup, bo);
+       else if (bo->command == FLOW_BLOCK_UNBIND)
+               indir_dev_remove(data);
+
        list_for_each_entry(this, &flow_block_indr_dev_list, list)
                this->cb(dev, sch, this->cb_priv, type, bo, data, cleanup);