From: Maor Gottlieb <ma...@mellanox.com>

Use kernel notifier block API in order to notifiy user when new rule
is added/deleted to/from namespace. Once a new listener is registered
we will fire add rule notification on all the existing rules.

Signed-off-by: Maor Gottlieb <ma...@mellanox.com>
Signed-off-by: Saeed Mahameed <sae...@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 215 +++++++++++++++++++++-
 drivers/net/ethernet/mellanox/mlx5/core/fs_core.h |  15 ++
 include/linux/mlx5/fs.h                           |  20 ++
 3 files changed, 247 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c 
b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
index 06f94bf..6ef7b99 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
@@ -164,10 +164,10 @@ static void tree_get_node(struct fs_node *node)
 }
 
 static void nested_lock_ref_node(struct fs_node *node,
-                                enum fs_i_mutex_lock_class class)
+                                int nesting)
 {
        if (node) {
-               mutex_lock_nested(&node->lock, class);
+               mutex_lock_nested(&node->lock, nesting);
                atomic_inc(&node->refcount);
        }
 }
@@ -363,6 +363,8 @@ static void del_flow_table(struct fs_node *node)
 
 static void del_rule(struct fs_node *node)
 {
+       struct rule_client_data *priv_data;
+       struct rule_client_data *tmp;
        struct mlx5_flow_rule *rule;
        struct mlx5_flow_table *ft;
        struct mlx5_flow_group *fg;
@@ -380,6 +382,12 @@ static void del_rule(struct fs_node *node)
        }
 
        fs_get_obj(rule, node);
+
+       list_for_each_entry_safe(priv_data, tmp, &rule->clients_data, list) {
+               list_del(&priv_data->list);
+               kfree(priv_data);
+       }
+
        fs_get_obj(fte, rule->node.parent);
        fs_get_obj(fg, fte->node.parent);
        memcpy(match_value, fte->val, sizeof(fte->val));
@@ -896,6 +904,8 @@ static struct mlx5_flow_rule *alloc_rule(struct 
mlx5_flow_destination *dest)
        INIT_LIST_HEAD(&rule->next_ft);
        atomic_set(&rule->refcount, 1);
        rule->node.type = FS_TYPE_FLOW_DEST;
+       INIT_LIST_HEAD(&rule->clients_data);
+       mutex_init(&rule->clients_lock);
        if (dest)
                memcpy(&rule->dest_attr, dest, sizeof(*dest));
 
@@ -1070,6 +1080,52 @@ static struct mlx5_flow_rule *find_flow_rule(struct 
fs_fte *fte,
        return NULL;
 }
 
+static struct mlx5_flow_namespace *get_ns(struct fs_node *node)
+{
+       struct mlx5_flow_namespace *ns = NULL;
+
+       while (node  && (node->type != FS_TYPE_NAMESPACE))
+               node = node->parent;
+
+       if (node)
+               fs_get_obj(ns, node);
+
+       return ns;
+}
+
+static void get_event_data(struct mlx5_flow_rule *rule, struct mlx5_event_data
+                          *data)
+{
+       struct mlx5_flow_group *fg;
+       struct mlx5_flow_table *ft;
+       struct fs_fte *fte;
+
+       data->rule = rule;
+
+       fs_get_obj(fte, rule->node.parent);
+       WARN_ON(!fte);
+       fs_get_obj(fg, fte->node.parent);
+       WARN_ON(!fg);
+       fs_get_obj(ft, fg->node.parent);
+       WARN_ON(!ft);
+       data->ft = ft;
+}
+
+static void notify_add_rule(struct mlx5_flow_rule *rule)
+{
+       struct mlx5_event_data evt_data;
+       struct mlx5_flow_namespace *ns;
+       struct fs_fte *fte;
+
+       fs_get_obj(fte, rule->node.parent);
+       ns = get_ns(&fte->node);
+       if (!ns)
+               return;
+
+       get_event_data(rule, &evt_data);
+       raw_notifier_call_chain(&ns->listeners, MLX5_RULE_EVENT_ADD, &evt_data);
+}
+
 static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg,
                                          u32 *match_value,
                                          u8 action,
@@ -1126,6 +1182,7 @@ static struct mlx5_flow_rule *add_rule_fg(struct 
mlx5_flow_group *fg,
        list_add(&fte->node.list, prev);
 add_rule:
        tree_add_node(&rule->node, &fte->node);
+       notify_add_rule(rule);
 unlock_fg:
        unlock_ref_node(&fg->node);
        return rule;
@@ -1238,6 +1295,7 @@ mlx5_add_flow_rule(struct mlx5_flow_table *ft,
        struct mlx5_flow_rule *rule = NULL;
        u32 sw_action = attr->action;
        struct fs_prio *prio;
+       struct mlx5_flow_namespace *ns;
 
        fs_get_obj(prio, ft->node.parent);
        if (attr->action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) {
@@ -1258,8 +1316,10 @@ mlx5_add_flow_rule(struct mlx5_flow_table *ft,
                }
        }
 
+       ns = get_ns(&ft->node);
+       if (ns)
+               down_read(&ns->ns_rw_sem);
        rule =  _mlx5_add_flow_rule(ft, attr);
-
        if (sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) {
                if (!IS_ERR_OR_NULL(rule) &&
                    (list_empty(&rule->next_ft))) {
@@ -1270,15 +1330,41 @@ mlx5_add_flow_rule(struct mlx5_flow_table *ft,
                }
                mutex_unlock(&root->chain_lock);
        }
+       if (ns)
+               up_read(&ns->ns_rw_sem);
+
        return rule;
 }
 EXPORT_SYMBOL(mlx5_add_flow_rule);
 
+static void notify_del_rule(struct mlx5_flow_rule *rule)
+{
+       struct mlx5_flow_namespace *ns;
+       struct mlx5_event_data evt_data;
+       struct fs_fte *fte;
+
+       fs_get_obj(fte, rule->node.parent);
+       ns = get_ns(&fte->node);
+       if (!ns)
+               return;
+
+       get_event_data(rule, &evt_data);
+       raw_notifier_call_chain(&ns->listeners, MLX5_RULE_EVENT_DEL, &evt_data);
+}
+
 void mlx5_del_flow_rule(struct mlx5_flow_rule *rule)
 {
+       struct mlx5_flow_namespace *ns;
+
        if (!atomic_dec_and_test(&rule->refcount))
                return;
+       ns = get_ns(&rule->node);
+       if (ns)
+               down_read(&ns->ns_rw_sem);
+       notify_del_rule(rule);
        tree_remove_node(&rule->node);
+       if (ns)
+               up_read(&ns->ns_rw_sem);
 }
 EXPORT_SYMBOL(mlx5_del_flow_rule);
 
@@ -1453,6 +1539,8 @@ static struct mlx5_flow_namespace 
*fs_init_namespace(struct mlx5_flow_namespace
 {
        ns->node.type = FS_TYPE_NAMESPACE;
 
+       init_rwsem(&ns->ns_rw_sem);
+
        return ns;
 }
 
@@ -1823,3 +1911,124 @@ void mlx5_put_flow_rule(struct mlx5_flow_rule *rule)
 {
        tree_put_node(&rule->node);
 }
+
+static void notify_existing_rules_recursive(struct fs_node *root,
+                                           struct notifier_block *nb,
+                                           int nesting)
+{
+       struct mlx5_event_data data;
+       struct fs_node *iter;
+
+       nested_lock_ref_node(root, nesting++);
+       if (root->type == FS_TYPE_FLOW_ENTRY) {
+               struct mlx5_flow_rule *rule;
+               int err = 0;
+
+               /* Iterate on destinations */
+               list_for_each_entry(iter, &root->children, list) {
+                       fs_get_obj(rule, iter);
+                       get_event_data(rule, &data);
+                       err = nb->notifier_call(nb, MLX5_RULE_EVENT_ADD, &data);
+                       if (err)
+                               break;
+               }
+       } else {
+               list_for_each_entry(iter, &root->children, list)
+                       notify_existing_rules_recursive(iter, nb, nesting);
+       }
+       unlock_ref_node(root);
+}
+
+static void mlx5_flow_notify_existing_rules(struct mlx5_flow_namespace *ns,
+                                           struct notifier_block *nb)
+{
+       notify_existing_rules_recursive(&ns->node, nb, 0);
+}
+
+int mlx5_register_rule_notifier(struct mlx5_flow_namespace *ns,
+                               struct notifier_block *nb)
+{
+       int err;
+
+       down_write(&ns->ns_rw_sem);
+       mlx5_flow_notify_existing_rules(ns, nb);
+       err = raw_notifier_chain_register(&ns->listeners, nb);
+       up_write(&ns->ns_rw_sem);
+
+       return err;
+}
+
+int mlx5_unregister_rule_notifier(struct mlx5_flow_namespace *ns,
+                                 struct notifier_block *nb)
+{
+       int err;
+
+       down_write(&ns->ns_rw_sem);
+       err = raw_notifier_chain_unregister(&ns->listeners, nb);
+       up_write(&ns->ns_rw_sem);
+
+       return err;
+}
+
+void *mlx5_get_rule_private_data(struct mlx5_flow_rule *rule,
+                                struct notifier_block *nb)
+{
+       struct rule_client_data *priv_data;
+       void *data = NULL;
+
+       mutex_lock(&rule->clients_lock);
+       list_for_each_entry(priv_data, &rule->clients_data, list) {
+               if (priv_data->nb == nb) {
+                       data = priv_data->client_data;
+                       break;
+               }
+       }
+       mutex_unlock(&rule->clients_lock);
+
+       return data;
+}
+
+void mlx5_release_rule_private_data(struct mlx5_flow_rule *rule,
+                                   struct notifier_block *nb)
+{
+       struct rule_client_data *priv_data;
+       struct rule_client_data *tmp;
+
+       mutex_lock(&rule->clients_lock);
+       list_for_each_entry_safe(priv_data, tmp, &rule->clients_data, list) {
+               if (priv_data->nb == nb) {
+                       list_del(&priv_data->list);
+                       break;
+               }
+       }
+       mutex_unlock(&rule->clients_lock);
+}
+
+int mlx5_set_rule_private_data(struct mlx5_flow_rule *rule,
+                              struct notifier_block *nb,
+                              void  *client_data)
+{
+       struct rule_client_data *priv_data;
+
+       mutex_lock(&rule->clients_lock);
+       list_for_each_entry(priv_data, &rule->clients_data, list) {
+               if (priv_data->nb == nb) {
+                       priv_data->client_data = client_data;
+                       goto unlock;
+               }
+       }
+       priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL);
+       if (!priv_data) {
+               mutex_unlock(&rule->clients_lock);
+               return -ENOMEM;
+       }
+
+       priv_data->client_data = client_data;
+       priv_data->nb = nb;
+       list_add(&priv_data->list, &rule->clients_data);
+
+unlock:
+       mutex_unlock(&rule->clients_lock);
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h 
b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
index 29dd9e0..dc08742 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
@@ -85,6 +85,9 @@ struct mlx5_flow_rule {
        struct list_head                        next_ft;
        u32                                     sw_action;
        atomic_t                                refcount;
+       struct list_head                        clients_data;
+       /* Protect clients data list */
+       struct mutex                            clients_lock;
 };
 
 /* Type of children is mlx5_flow_group */
@@ -153,6 +156,12 @@ struct fs_prio {
 struct mlx5_flow_namespace {
        /* parent == NULL => root ns */
        struct  fs_node                 node;
+       /* Listeners list for rule add/del operations */
+       struct raw_notifier_head        listeners;
+       /* We take write lock when we iterate on the
+        * namespace's rules.
+        */
+       struct  rw_semaphore            ns_rw_sem;
 };
 
 struct mlx5_flow_group_mask {
@@ -182,6 +191,12 @@ struct mlx5_flow_root_namespace {
 int mlx5_init_fc_stats(struct mlx5_core_dev *dev);
 void mlx5_cleanup_fc_stats(struct mlx5_core_dev *dev);
 
+struct rule_client_data {
+       struct notifier_block *nb;
+       struct list_head list;
+       void   *client_data;
+};
+
 int mlx5_init_fs(struct mlx5_core_dev *dev);
 void mlx5_cleanup_fs(struct mlx5_core_dev *dev);
 
diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h
index 37e13a1..5ac0e8f 100644
--- a/include/linux/mlx5/fs.h
+++ b/include/linux/mlx5/fs.h
@@ -152,4 +152,24 @@ void mlx5_fc_query_cached(struct mlx5_fc *counter,
 void mlx5_get_flow_rule(struct mlx5_flow_rule *rule);
 void mlx5_put_flow_rule(struct mlx5_flow_rule *rule);
 
+enum {
+       MLX5_RULE_EVENT_ADD,
+       MLX5_RULE_EVENT_DEL,
+};
+
+int mlx5_set_rule_private_data(struct mlx5_flow_rule *rule,
+                              struct notifier_block *nb, void *client_data);
+void *mlx5_get_rule_private_data(struct mlx5_flow_rule *rule,
+                                struct notifier_block *nb);
+void mlx5_release_rule_private_data(struct mlx5_flow_rule *rule,
+                                   struct notifier_block *nb);
+
+int mlx5_register_rule_notifier(struct mlx5_flow_namespace *ns,
+                               struct notifier_block *nb);
+int mlx5_unregister_rule_notifier(struct mlx5_flow_namespace *ns,
+                                 struct notifier_block *nb);
+struct mlx5_event_data {
+       struct mlx5_flow_table *ft;
+       struct mlx5_flow_rule *rule;
+};
 #endif
-- 
2.8.0

Reply via email to