[RFC net-next mlxsw v8 2/2] lib: Add debugfs utility to prune library

Tal Bar talb at mellanox.com
Sun Jul 8 22:26:42 AEST 2018


This patch provides a debugfs /sys/kernel/debug/prune_debug to represent
the prune object state so admin can investigate why for some entries there
is latency due to needed lookups.
In order to check the current status user should:
"cat /sys/kernel/debug/prune_debug"

An example output:
prune
-----
prune priv:           (null)
number of table in prune object: 2
	table info
	----------
	table id: 0000000021cafe9f
	table priv:           (null)
	table mask: 16777215
		item info
		---------
		item key: 12599424
		item prio: 7
		item priv:           (null)
		item prune vector:
			table:           (null)	 is pruned: Yes
			table: 00000000db28e393	 is pruned: No

		intersec table info
		-------------------
		table mask: 16776960
		niegh table priv: 00000000db28e393
			intersec entry info
			-------------------
				 mask: 12599296
				 intersec item info:
					entry prio: 12599424
					entry prio: 7

				 neigh item info:
					entry key: 549470208
					entry prio: 5

					entry key: 2696953856
					entry prio: 10

	table info
	----------
	table id: 0000000041acfd40
	table priv: 00000000db28e393
	table mask: 4294967040
		item info
		---------
		item key: 549470208
		item prio: 5
		item priv:           (null)
		item prune vector:
			table:           (null)	 is pruned: Yes
			table: 00000000db28e393	 is pruned: Yes
		item info
		---------
		item key: 2696953856
		item prio: 10
		item priv:           (null)
		item prune vector:
			table:           (null)	 is pruned: No
			table: 00000000db28e393	 is pruned: Yes

		intersec table info
		-------------------
		table mask: 16776960
		niegh table priv:           (null)
			intersec entry info
			-------------------
				 mask: 12599296
				 intersec item info:
					entry prio: 549470208
					entry prio: 5

					entry prio: 2696953856
					entry prio: 10

				 neigh item info:
					entry key: 12599424
					entry prio: 7

Signed-off-by: Tal Bar <talb at mellanox.com>
---
 include/linux/prune.h |   2 +
 lib/prune.c           | 336 ++++++++++++++++++++++++++++++++++++++++++++------
 lib/test_prune.c      |   4 +-
 3 files changed, 301 insertions(+), 41 deletions(-)

diff --git a/include/linux/prune.h b/include/linux/prune.h
index 356ed4f..752fddc 100644
--- a/include/linux/prune.h
+++ b/include/linux/prune.h
@@ -59,6 +59,8 @@ struct prune_ops {
 				  struct prune_vector_item_data *prune_data);
 };
 
+int prune_init(void);
+void prune_fini(void);
 struct prune *prune_create(unsigned int mask_len, const struct prune_ops *ops,
 			   void *priv);
 void prune_destroy(struct prune *prune);
diff --git a/lib/prune.c b/lib/prune.c
index f2959a3..70422f2 100644
--- a/lib/prune.c
+++ b/lib/prune.c
@@ -10,10 +10,20 @@
 #include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/list.h>
+#include <linux/rculist.h>
 #include <linux/rhashtable.h>
 #include <linux/prune.h>
 #include <linux/jhash.h>
 #include <linux/bitmap.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+/*dentry for debugsfs purpose */
+static struct dentry *dentry;
+/* the list of all allocated prune objects */
+static struct list_head prune_object_list;
 
 struct prune_item_notification {
 	/* list of prune_item_notify */
@@ -47,11 +57,15 @@ struct prune_intersec_table_item_entry {
 	unsigned int key_len;
 	struct list_head intersec_table_items;
 	struct list_head neighbor_table_items; /* Can be enhanced to rbtree */
+	struct list_head list; /* Node in items_list in
+				* struct prune_intersec_table
+				*/
 };
 
 struct prune_intersec_table {
 	unsigned long *mask;
 	struct rhashtable items_ht;
+	struct list_head items_list; /* A walker for items_ht */
 	struct prune_table *neigh_table;
 	struct list_head list; /* Node of intersec_table_list (in prune_table)*/
 };
@@ -73,9 +87,192 @@ struct prune {
 	unsigned int mask_len;
 	struct list_head table_list;	/* A list of struct prune_table */
 	unsigned int table_cnt;		/* Number of tables in the list */
+	struct list_head list;		/* A node in prune pbject list */
 	void *priv;
 };
 
+static void prune_vector_items_dump(struct seq_file *seq,
+				    struct prune_table_item_entry *entry)
+{
+	struct prune_vector_item *vector_item;
+
+	seq_puts(seq, "\t\titem prune vector:\n");
+	list_for_each_entry_rcu(vector_item, &entry->table_prune_vector, list) {
+		seq_printf(seq, "\t\t\ttable: %p\t is pruned: %s\n",
+			   vector_item->prune_data.table->priv,
+			   vector_item->prune_data.pruned ? "Yes" : "No");
+	}
+}
+
+static void prune_table_items_dump(struct seq_file *seq,
+				   struct prune_table *table)
+{
+	struct prune_table_item_entry *entry;
+
+	list_for_each_entry_rcu(entry, &table->item_entry_list, list) {
+		seq_puts(seq, "\t\titem info\n");
+		seq_puts(seq, "\t\t---------\n");
+		seq_printf(seq, "\t\titem key: %lu\n", *entry->item.key);
+		seq_printf(seq, "\t\titem prio: %lu\n", entry->item.priority);
+		seq_printf(seq, "\t\titem priv: %p\n", entry->item.priv);
+		prune_vector_items_dump(seq, entry);
+	}
+}
+
+void prune_intersec_item_list_dump(struct seq_file *seq,
+				   struct prune_intersec_table *intersec_table)
+{
+	struct prune_intersec_table_item_entry *intersec_entry;
+	struct prune_intersec_table_item *intersec_item;
+	struct prune_neighbor_table_items *neigh_item;
+
+	list_for_each_entry_rcu(intersec_entry, &intersec_table->items_list,
+				list) {
+		seq_puts(seq, "\t\t\tintersec entry info\n");
+		seq_puts(seq, "\t\t\t-------------------\n");
+		seq_printf(seq, "\t\t\t\t mask: %lu\n",
+			   *intersec_entry->ht_key.key);
+		seq_puts(seq, "\t\t\t\t intersec item info:\n");
+		list_for_each_entry_rcu(intersec_item,
+					&intersec_entry->intersec_table_items,
+					list) {
+			seq_printf(seq, "\t\t\t\t\tentry prio: %lu\n",
+				   *intersec_item->entry->item.key);
+			seq_printf(seq, "\t\t\t\t\tentry prio: %lu\n\n",
+				   intersec_item->entry->item.priority);
+		}
+		seq_puts(seq, "\t\t\t\t neigh item info:\n");
+		list_for_each_entry_rcu(neigh_item,
+					&intersec_entry->neighbor_table_items,
+					list) {
+			seq_printf(seq, "\t\t\t\t\tentry key: %lu\n",
+				   *neigh_item->entry->item.key);
+			seq_printf(seq, "\t\t\t\t\tentry prio: %lu\n\n",
+				   neigh_item->entry->item.priority);
+		}
+	}
+}
+
+void prune_intersec_tables_dump(struct seq_file *seq, struct prune_table *table)
+{
+	struct prune_intersec_table *intersec_table;
+
+	list_for_each_entry_rcu(intersec_table, &table->intersec_table_list,
+				list) {
+		seq_puts(seq, "\t\tintersec table info\n");
+		seq_puts(seq, "\t\t-------------------\n");
+		seq_printf(seq, "\t\ttable mask: %lu\n", *intersec_table->mask);
+		seq_printf(seq, "\t\tniegh table priv: %p\n",
+			   intersec_table->neigh_table->priv);
+		prune_intersec_item_list_dump(seq, intersec_table);
+	}
+}
+
+static void prune_table_dump(struct seq_file *seq, struct prune *prune)
+{
+	struct prune_table *table;
+
+	list_for_each_entry_rcu(table, &prune->table_list, list) {
+		seq_puts(seq, "\ttable info\n");
+		seq_puts(seq, "\t----------\n");
+		seq_printf(seq, "\ttable id: %p\n", table);
+		seq_printf(seq, "\ttable priv: %p\n", table->priv);
+		seq_printf(seq, "\ttable mask: %lu\n", *table->mask);
+		/* dump table items */
+		prune_table_items_dump(seq, table);
+		seq_puts(seq, "\n");
+		prune_intersec_tables_dump(seq, table);
+		seq_puts(seq, "\n\n");
+	}
+}
+
+static void prune_obj_dump(struct seq_file *seq, struct prune *prune)
+{
+	/* print prune object info */
+	seq_puts(seq, "prune\n");
+	seq_puts(seq, "-----\n");
+	seq_printf(seq, "prune priv: %p\n", prune->priv);
+	seq_printf(seq, "number of table in prune object: %d\n",
+		   prune->table_cnt);
+	/* iterate over the prune tables */
+	prune_table_dump(seq, prune);
+	seq_puts(seq, "\n\n");
+}
+
+/* Iterate over the prune object_list and return the first valid object */
+static void *prune_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct prune *prune;
+	loff_t n = *pos;
+
+	rcu_read_lock();
+	if (!list_empty(&prune_object_list)) {
+		if (n-- == 0) {
+			prune = list_first_entry(&prune_object_list,
+						 typeof(*prune),
+						 list);
+			return prune;
+		}
+	}
+	return NULL;
+}
+
+/*
+ * Return the next object in the prune object_list.
+ */
+static void *prune_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct prune *prev_prune_obj = v;
+	struct prune *next_prune_obj = NULL;
+
+	++(*pos);
+
+	next_prune_obj = list_next_entry(prev_prune_obj, list);
+	if (&next_prune_obj->list == &prune_object_list)
+		return NULL;
+	else
+		return next_prune_obj;
+}
+
+/*
+ * frees needed resources initialized at start.
+ */
+static void prune_seq_stop(struct seq_file *seq, void *v)
+{
+	rcu_read_unlock(); /*unlock the rcu_lock taken at start */
+}
+
+/*
+ * Print the information for a prune object to the seq file.
+ */
+static int prune_seq_show(struct seq_file *seq, void *v)
+{
+	struct prune *prune = v;
+
+	prune_obj_dump(seq, prune);
+	return 0;
+}
+
+static const struct seq_operations prune_seq_ops = {
+	.start = prune_seq_start,
+	.next  = prune_seq_next,
+	.stop  = prune_seq_stop,
+	.show  = prune_seq_show,
+};
+
+static int prune_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &prune_seq_ops);
+}
+
+static const struct file_operations prune_fops = {
+	.owner	   = THIS_MODULE,
+	.open	    = prune_open,
+	.read	    = seq_read,
+	.llseek	  = seq_lseek,
+	.release = seq_release,
+};
+
 static inline size_t prune_mask_size(unsigned int nbits)
 {
 	return BITS_TO_LONGS(nbits) * sizeof(unsigned long);
@@ -114,7 +311,8 @@ static void prune_table_prune_vector_fini(struct prune_table_item_entry *entry)
 
 	list_for_each_safe(pos, tmp, &entry->table_prune_vector) {
 		vector_item = list_entry(pos, typeof(*vector_item), list);
-		list_del(&vector_item->list);
+		list_del_rcu(&vector_item->list);
+		synchronize_rcu();
 		kfree(vector_item);
 	}
 }
@@ -125,7 +323,7 @@ static int prune_table_prune_vector_init(struct prune *prune,
 	struct prune_vector_item *vector_item;
 	struct prune_table *table;
 
-	INIT_LIST_HEAD(&entry->table_prune_vector);
+	INIT_LIST_HEAD_RCU(&entry->table_prune_vector);
 
 	list_for_each_entry(table, &prune->table_list, list) {
 		vector_item = kzalloc(sizeof(*vector_item), GFP_KERNEL);
@@ -135,8 +333,8 @@ static int prune_table_prune_vector_init(struct prune *prune,
 		}
 		vector_item->prune_data.pruned = true;
 		vector_item->prune_data.table = table;
-		list_add_tail(&vector_item->list,
-			      &entry->table_prune_vector);
+		list_add_tail_rcu(&vector_item->list,
+				  &entry->table_prune_vector);
 	}
 	return 0;
 }
@@ -171,7 +369,8 @@ prune_item_entry_create(struct prune *prune, unsigned long *key,
 static void prune_entry_destroy(struct prune_table_item_entry *entry)
 {
 	prune_table_prune_vector_fini(entry);
-	list_del(&entry->list);
+	list_del_rcu(&entry->list);
+	synchronize_rcu();
 	kfree(entry);
 }
 
@@ -542,7 +741,7 @@ prune_table_entry_create(struct prune *prune, struct prune_table *table,
 		prune_entry_destroy(entry);
 		return ERR_PTR(err);
 	}
-	list_add_tail(&entry->list, &table->item_entry_list);
+	list_add_tail_rcu(&entry->list, &table->item_entry_list);
 	entry->item.table = table;
 
 	return entry;
@@ -579,8 +778,8 @@ static struct prune_intersec_table_item_entry
 	bitmap_and(intersec_entry->ht_key.key, intersec_table_mask, item_key,
 		   mask_len);
 
-	INIT_LIST_HEAD(&intersec_entry->intersec_table_items);
-	INIT_LIST_HEAD(&intersec_entry->neighbor_table_items);
+	INIT_LIST_HEAD_RCU(&intersec_entry->intersec_table_items);
+	INIT_LIST_HEAD_RCU(&intersec_entry->neighbor_table_items);
 
 	return intersec_entry;
 err_key_alloc:
@@ -597,6 +796,7 @@ static int prune_intersec_item_add(struct prune_intersec_table *intersec_table,
 	struct prune_intersec_table_item_entry *intersec_entry;
 	struct prune_intersec_table_item *intersec_item = NULL;
 	struct intersec_item ht_key;
+	unsigned long *mask;
 	unsigned int mask_len = entry->item.table->prune->mask_len;
 	bool found = true;
 	int err = 0;
@@ -605,7 +805,8 @@ static int prune_intersec_item_add(struct prune_intersec_table *intersec_table,
 	if (!ht_key.key)
 		return -ENOMEM;
 
-	bitmap_and(ht_key.key, intersec_table->mask, entry->item.key, mask_len);
+	mask = intersec_table->mask;
+	bitmap_and(ht_key.key, mask, entry->item.key, mask_len);
 
 	intersec_entry = rhashtable_lookup_fast(&intersec_table->items_ht,
 						&ht_key,
@@ -614,7 +815,7 @@ static int prune_intersec_item_add(struct prune_intersec_table *intersec_table,
 
 	if (!intersec_entry) {
 		/* create new intersec entry since there ins't already one */
-		intersec_entry = prune_intersec_entry_create(intersec_table->mask,
+		intersec_entry = prune_intersec_entry_create(mask,
 							     entry->item.key,
 							     mask_len);
 		if (IS_ERR(intersec_entry)) {
@@ -630,16 +831,16 @@ static int prune_intersec_item_add(struct prune_intersec_table *intersec_table,
 			goto err_list_item_alloc;
 
 		intersec_item->entry = entry;
-		list_add_tail(&intersec_item->list,
-			      &intersec_entry->intersec_table_items);
+		list_add_tail_rcu(&intersec_item->list,
+				  &intersec_entry->intersec_table_items);
 	} else {
 		neigh_item = kzalloc(sizeof(*neigh_item), GFP_KERNEL);
 		if (!neigh_item)
 			goto err_nigh_list_item_alloc;
 
 		neigh_item->entry = entry;
-		list_add_tail(&neigh_item->list,
-			      &intersec_entry->neighbor_table_items);
+		list_add_tail_rcu(&neigh_item->list,
+				  &intersec_entry->neighbor_table_items);
 	}
 	if (!found) {
 		err = rhashtable_insert_fast(&intersec_table->items_ht,
@@ -647,15 +848,19 @@ static int prune_intersec_item_add(struct prune_intersec_table *intersec_table,
 					     intersec_tables_ht_params);
 		if (err)
 			goto err_rhashtable_insert;
+		list_add_tail_rcu(&intersec_entry->list,
+				  &intersec_table->items_list);
 	}
 	return 0;
 
 err_rhashtable_insert:
 	if (!neigh) {
-		list_del(&intersec_item->list);
+		list_del_rcu(&intersec_item->list);
+		synchronize_rcu();
 		kfree(intersec_item);
 	} else {
-		list_del(&neigh_item->list);
+		list_del_rcu(&neigh_item->list);
+		synchronize_rcu();
 		kfree(neigh_item);
 	}
 err_nigh_list_item_alloc:
@@ -698,7 +903,8 @@ prune_intersec_item_remove(struct prune_intersec_table *intersec_table,
 			intersec_item = list_entry(pos, typeof(*intersec_item),
 						   list);
 			if (intersec_item->entry == remove_entry) {
-				list_del(&intersec_item->list);
+				list_del_rcu(&intersec_item->list);
+				synchronize_rcu();
 				kfree(intersec_item);
 				break;
 			}
@@ -708,7 +914,8 @@ prune_intersec_item_remove(struct prune_intersec_table *intersec_table,
 				   &intersec_entry->neighbor_table_items) {
 			neigh_item = list_entry(pos, typeof(*neigh_item), list);
 			if (remove_entry == neigh_item->entry) {
-				list_del(&neigh_item->list);
+				list_del_rcu(&neigh_item->list);
+				synchronize_rcu();
 				kfree(neigh_item);
 				break;
 			}
@@ -719,6 +926,8 @@ prune_intersec_item_remove(struct prune_intersec_table *intersec_table,
 		rhashtable_remove_fast(&intersec_table->items_ht,
 				       &intersec_entry->ht_node,
 				       intersec_tables_ht_params);
+		list_del_rcu(&intersec_entry->list);
+		synchronize_rcu();
 		prune_intersec_entry_destroy(intersec_entry);
 	}
 	kfree(ht_key.key);
@@ -739,6 +948,8 @@ prune_intrsc_table_create(unsigned int mask_len)
 	if (!intersec_table->mask)
 		goto err_intersec_table_mask_alloc;
 
+	INIT_LIST_HEAD_RCU(&intersec_table->items_list);
+
 	return intersec_table;
 
 err_intersec_table_mask_alloc:
@@ -756,9 +967,10 @@ prune_intrsc_table_create(unsigned int mask_len)
  * @intersec_table: The intersection table which needs to be updated
  * @add_to_neighbor_items: True if items need to be add to neighbor_table_items
  */
-static void prune_intersec_table_items_update(struct prune_table *curr_table,
-					      struct prune_intersec_table *intersec_table,
-					      bool add_to_neighbor_items)
+static void
+prune_intersec_table_items_update(struct prune_table *curr_table,
+				  struct prune_intersec_table *intersec_table,
+				  bool add_to_neighbor_items)
 {
 	struct prune_table_item_entry *entry;
 
@@ -794,7 +1006,6 @@ static int prune_intersec_table_add(struct prune *prune,
 			      &intersec_tables_ht_params);
 	if (err)
 		goto err_rhashtable_init_1;
-
 	err = rhashtable_init(&intersec_table2->items_ht,
 			      &intersec_tables_ht_params);
 	if (err)
@@ -803,11 +1014,13 @@ static int prune_intersec_table_add(struct prune *prune,
 	/* Add new intersection table to the old table */
 	intersec_table1->neigh_table = new_table;
 	prune_intersec_table_items_update(curr_table, intersec_table1, false);
-	list_add(&intersec_table1->list, &curr_table->intersec_table_list);
+	list_add_tail_rcu(&intersec_table1->list,
+			  &curr_table->intersec_table_list);
 	/* Add new intersection table to new table */
 	intersec_table2->neigh_table = curr_table;
 	prune_intersec_table_items_update(curr_table, intersec_table2, true);
-	list_add(&intersec_table2->list, &new_table->intersec_table_list);
+	list_add_tail_rcu(&intersec_table2->list,
+			  &new_table->intersec_table_list);
 
 	return 0;
 
@@ -849,14 +1062,18 @@ static void prune_intersec_rht_free(void *ptr, void *arg)
 
 	list_for_each_safe(pos, tmp, &rht_node->intersec_table_items) {
 		intersec_item = list_entry(pos, typeof(*intersec_item), list);
-		list_del(&intersec_item->list);
+		list_del_rcu(&intersec_item->list);
+		synchronize_rcu();
 		kfree(intersec_item);
 	}
 	list_for_each_safe(pos, tmp, &rht_node->neighbor_table_items) {
 		neigh_item = list_entry(pos, typeof(*neigh_item), list);
-		list_del(&neigh_item->list);
+		list_del_rcu(&neigh_item->list);
+		synchronize_rcu();
 		kfree(neigh_item);
 	}
+	list_del_rcu(&rht_node->list);
+	synchronize_rcu();
 	kfree(rht_node->ht_key.key);
 	kfree(rht_node);
 }
@@ -883,7 +1100,8 @@ static int prune_instersec_table_remove(unsigned int mask_len,
 						 list);
 		if (bitmap_equal(curr_mask, curr_intersec_table->mask,
 				 mask_len)) {
-			list_del(&curr_intersec_table->list);
+			list_del_rcu(&curr_intersec_table->list);
+			synchronize_rcu();
 			items_rht = &curr_intersec_table->items_ht;
 			rhashtable_free_and_destroy(items_rht,
 						    prune_intersec_rht_free,
@@ -963,7 +1181,8 @@ static void prune_table_item_vector_remove(struct prune_table *table,
 			vector_item = list_entry(pos, typeof(*vector_item),
 						 list);
 			if (vector_item->prune_data.table == del_table) {
-				list_del(&vector_item->list);
+				list_del_rcu(&vector_item->list);
+				synchronize_rcu();
 				kfree(vector_item);
 				break;
 			}
@@ -985,8 +1204,8 @@ static int prune_table_item_vector_add(struct prune_table *table,
 		}
 		vector_item->prune_data.pruned = true;
 		vector_item->prune_data.table = new_table;
-		list_add_tail(&vector_item->list,
-			      &entry->table_prune_vector);
+		list_add_tail_rcu(&vector_item->list,
+				  &entry->table_prune_vector);
 	}
 	return 0;
 }
@@ -1049,10 +1268,11 @@ struct prune *prune_create(unsigned int mask_len, const struct prune_ops *ops,
 	prune->table_cnt = 0;
 	prune->priv = priv;
 	prune->mask_len = mask_len;
-	INIT_LIST_HEAD(&prune->table_list);
+	INIT_LIST_HEAD_RCU(&prune->table_list);
 	prune->ops = ops;
 	intersec_tables_ht_params.key_len = mask_len;
 
+	list_add_tail_rcu(&prune->list, &prune_object_list);
 	return prune;
 }
 EXPORT_SYMBOL(prune_create);
@@ -1067,6 +1287,8 @@ void prune_destroy(struct prune *prune)
 {
 	WARN_ON(prune->table_cnt > 0 || !list_empty(&prune->table_list));
 
+	list_del_rcu(&prune->list);
+	synchronize_rcu();
 	kfree(prune);
 }
 EXPORT_SYMBOL(prune_destroy);
@@ -1083,8 +1305,6 @@ EXPORT_SYMBOL(prune_destroy);
  * otherwise it returns negative number to indicate an error.
  *
  * Note: It's the user responsibility to care for synchronization.
- * This function may sleep so you must not call it from interrupt
- * context or with spin locks held.
  */
 
 struct prune_table *prune_table_create(struct prune *prune, unsigned long *mask,
@@ -1107,8 +1327,8 @@ struct prune_table *prune_table_create(struct prune *prune, unsigned long *mask,
 	}
 	bitmap_copy(table->mask, mask, prune->mask_len);
 	table->priv = priv;
-	INIT_LIST_HEAD(&table->item_entry_list);
-	INIT_LIST_HEAD(&table->intersec_table_list);
+	INIT_LIST_HEAD_RCU(&table->item_entry_list);
+	INIT_LIST_HEAD_RCU(&table->intersec_table_list);
 
 	/* Enlarge the prune vector of all item with the new table */
 	err = prune_item_prune_table_add(prune, table);
@@ -1119,7 +1339,7 @@ struct prune_table *prune_table_create(struct prune *prune, unsigned long *mask,
 	if (err)
 		goto err_free_mask;
 
-	list_add_tail(&table->list, &prune->table_list);
+	list_add_tail_rcu(&table->list, &prune->table_list);
 	table->prune = prune;
 	prune->table_cnt++;
 
@@ -1141,8 +1361,6 @@ EXPORT_SYMBOL(prune_table_create);
  * Returns 0 in case of success, negative number to indicate an error.
  *
  * Note: It's the user responsibility to care for synchronization.
- * This function may sleep so you must not call it from interrupt
- * context or with spin locks held.
  */
 int prune_table_destroy(struct prune_table *table)
 {
@@ -1177,11 +1395,12 @@ int prune_table_destroy(struct prune_table *table)
 					list_entry(pos,
 						   typeof(*curr_intersec_table),
 						   list);
-			list_del(&curr_intersec_table->list);
 			items_rht = &curr_intersec_table->items_ht;
 			rhashtable_free_and_destroy(items_rht,
 						    prune_intersec_rht_free,
 						    NULL);
+			list_del_rcu(&curr_intersec_table->list);
+			synchronize_rcu();
 			kfree(curr_intersec_table->mask);
 			kfree(curr_intersec_table);
 		}
@@ -1190,7 +1409,8 @@ int prune_table_destroy(struct prune_table *table)
 
 	prune_item_prune_table_remove(prune, table);
 
-	list_del(&table->list);
+	list_del_rcu(&table->list);
+	synchronize_rcu();
 	kfree(table->mask);
 	kfree(table);
 
@@ -1372,6 +1592,42 @@ void *prune_table_priv_get(struct prune_table *table)
 }
 EXPORT_SYMBOL(prune_table_priv_get);
 
+static struct dentry *prune_debugfs_create(void)
+{
+	dentry = debugfs_create_file("prune_debug", 0644, NULL, NULL,
+				     &prune_fops);
+	if (!dentry)
+		pr_warn("Failed to create the debugfs prune file\n");
+
+	return dentry;
+}
+
+static void prune_debugfs_destroy(struct dentry *dentry)
+{
+	debugfs_remove(dentry);
+}
+
+int prune_init(void)
+{
+	INIT_LIST_HEAD_RCU(&prune_object_list);
+#ifdef CONFIG_DEBUG_FS
+	dentry = prune_debugfs_create();
+	if (!dentry)
+		return -EINVAL;
+
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(prune_init);
+
+void prune_fini(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	prune_debugfs_destroy(dentry);
+#endif
+}
+EXPORT_SYMBOL(prune_fini);
+
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Tal Bar<talb at mellanox.com>");
 MODULE_DESCRIPTION("Prune lookup table manager");
diff --git a/lib/test_prune.c b/lib/test_prune.c
index b581d4c..310f7af 100644
--- a/lib/test_prune.c
+++ b/lib/test_prune.c
@@ -1181,11 +1181,13 @@ static int test_prune(void)
 
 static int __init test_prune_init(void)
 {
-		return test_prune();
+	prune_init();
+	return test_prune();
 }
 
 static void __exit test_prune_exit(void)
 {
+	 prune_fini();
 }
 
 module_init(test_prune_init);
-- 
2.8.0



More information about the Linux-mlxsw mailing list