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

Tal Bar talb at mellanox.com
Tue Jul 24 21:55:39 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 element info:
					entry prio: 12599424
					entry prio: 7

				 neigh element 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 element info:
					entry prio: 549470208
					entry prio: 5

					entry prio: 2696953856
					entry prio: 10

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

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

diff --git a/include/linux/prune.h b/include/linux/prune.h
index 7c21d5c..e177fc2 100644
--- a/include/linux/prune.h
+++ b/include/linux/prune.h
@@ -58,6 +58,8 @@ struct prune_ops {
 				  struct prune_table *table, bool pruned);
 };
 
+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 77b3785..f9728d4 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;
 
 /*
  * This structure holds the item configuration and its prune vector
@@ -43,6 +53,7 @@ struct prune_intersec_table_list_elem {
 struct prune_intersec_table {
 	struct rhashtable entry_ht;
 	unsigned long *mask;
+	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)*/
 };
@@ -62,6 +73,7 @@ struct prune_intersec_table_entry {
 	struct prune_intersec_table *intersec_table;
 	struct list_head intersec_table_elem_list;
 	struct list_head neigh_table_elem_list;
+	struct list_head list; /* Node in items_list (in struct prune_intersec_table) */
 };
 
 /* This structure contains table entries of prune_table_entry and the
@@ -91,6 +103,190 @@ struct prune {
 	unsigned int mask_len;		/* Bits */
 };
 
+static void prune_vector_items_dump(struct seq_file *seq,
+				    struct prune_table_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->table->priv,
+			   vector_item->pruned ? "Yes" : "No");
+	}
+}
+
+static void prune_table_items_dump(struct seq_file *seq,
+				   struct prune_table *table)
+{
+	struct prune_table_entry *entry;
+
+	list_for_each_entry_rcu(entry, &table->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: %u\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_entry *intersec_entry;
+	struct prune_intersec_table_list_elem *intersec_item;
+	struct prune_intersec_table_list_elem *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);
+		seq_puts(seq, "\t\t\t\t intersec element info:\n");
+		list_for_each_entry_rcu(intersec_item,
+					&intersec_entry->intersec_table_elem_list,
+					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: %u\n\n",
+				   intersec_item->entry->item.priority);
+		}
+		seq_puts(seq, "\t\t\t\t neigh element info:\n");
+		list_for_each_entry_rcu(neigh_item,
+					&intersec_entry->neigh_table_elem_list,
+					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: %u\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;
+	unsigned int table_cnt = 0;
+
+	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");
+		table_cnt++;
+	}
+	seq_printf(seq, "Number of tables in prune object: %d\n", table_cnt);
+}
+
+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);
+	/* 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 unsigned int
 prune_intersec_table_entry_key_len(const struct prune_intersec_table_entry
 				   *intersec_entry)
@@ -135,7 +331,8 @@ static void prune_table_prune_vector_fini(struct prune_table_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);
 	}
 }
@@ -146,7 +343,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);
@@ -156,7 +353,7 @@ static int prune_table_prune_vector_init(struct prune *prune,
 		}
 		vector_item->pruned = true;
 		vector_item->table = table;
-		list_add_tail(&vector_item->list,
+		list_add_tail_rcu(&vector_item->list,
 				  &entry->table_prune_vector);
 	}
 	return 0;
@@ -192,7 +389,8 @@ prune_item_entry_create(struct prune *prune, unsigned long *key,
 static void prune_entry_destroy(struct prune_table_entry *entry)
 {
 	prune_table_prune_vector_fini(entry);
-	list_del(&entry->list);
+	list_del_rcu(&entry->list);
+	synchronize_rcu();
 	kfree(entry);
 }
 
@@ -533,7 +731,9 @@ 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->entry_list);
+
+	list_add_tail_rcu(&entry->list, &table->entry_list);
+
 	entry->item.table = table;
 
 	return entry;
@@ -571,8 +771,8 @@ static struct prune_intersec_table_entry
 	bitmap_and(intersec_entry->ht_key, intersec_table_mask, item_key,
 		   mask_len);
 
-	INIT_LIST_HEAD(&intersec_entry->intersec_table_elem_list);
-	INIT_LIST_HEAD(&intersec_entry->neigh_table_elem_list);
+	INIT_LIST_HEAD_RCU(&intersec_entry->intersec_table_elem_list);
+	INIT_LIST_HEAD_RCU(&intersec_entry->neigh_table_elem_list);
 
 	return intersec_entry;
 err_key_alloc:
@@ -586,6 +786,7 @@ static int prune_intersec_item_add(struct prune_intersec_table *intersec_table,
 				   bool neigh)
 {
 	struct prune_intersec_table_list_elem *intersec_item = NULL;
+	unsigned long *mask;
 	unsigned int mask_len = entry->item.table->prune->mask_len;
 	struct prune_intersec_table_list_elem *neigh_item = NULL;
 	struct prune_intersec_table_entry *intersec_entry;
@@ -598,6 +799,7 @@ static int prune_intersec_item_add(struct prune_intersec_table *intersec_table,
 	if (!ht_key)
 		return -ENOMEM;
 
+	mask = intersec_table->mask;
 	bitmap_and(ht_key, intersec_table->mask, entry->item.key, mask_len);
 
 	rht_params = prune_intersec_table_entry_rht_params(intersec_table);
@@ -608,7 +810,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,
 							     intersec_table,
 							     mask_len);
@@ -625,16 +827,17 @@ 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_elem_list);
+		list_add_tail_rcu(&intersec_item->list,
+				  &intersec_entry->intersec_table_elem_list);
 	} 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->neigh_table_elem_list);
+
+		list_add_tail_rcu(&neigh_item->list,
+				  &intersec_entry->neigh_table_elem_list);
 	}
 	if (!found) {
 		err = rhashtable_insert_fast(&intersec_table->entry_ht,
@@ -642,15 +845,19 @@ static int prune_intersec_item_add(struct prune_intersec_table *intersec_table,
 					     rht_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:
@@ -694,7 +901,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;
 			}
@@ -704,7 +912,8 @@ prune_intersec_item_remove(struct prune_intersec_table *intersec_table,
 				   &intersec_entry->neigh_table_elem_list) {
 			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;
 			}
@@ -715,6 +924,8 @@ prune_intersec_item_remove(struct prune_intersec_table *intersec_table,
 		rhashtable_remove_fast(&intersec_table->entry_ht,
 				       &intersec_entry->ht_node,
 				       rht_params);
+		list_del_rcu(&intersec_entry->list);
+		synchronize_rcu();
 		prune_intersec_entry_destroy(intersec_entry);
 	}
 	kfree(ht_key);
@@ -735,6 +946,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:
@@ -752,9 +965,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 neigh_table_elem_list
  */
-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_entry *entry;
 
@@ -799,11 +1013,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;
 
@@ -845,14 +1061,18 @@ static void prune_intersec_rht_free(void *ptr, void *arg)
 
 	list_for_each_safe(pos, tmp, &rht_node->intersec_table_elem_list) {
 		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->neigh_table_elem_list) {
 		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);
 	kfree(rht_node);
 }
@@ -879,7 +1099,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();
 			entry_ht = &curr_intersec_table->entry_ht;
 			rhashtable_free_and_destroy(entry_ht,
 						    prune_intersec_rht_free,
@@ -996,7 +1217,8 @@ static void prune_table_item_vector_remove(struct prune_table *table,
 			vector_item = list_entry(pos, typeof(*vector_item),
 						 list);
 			if (vector_item->table == del_table) {
-				list_del(&vector_item->list);
+				list_del_rcu(&vector_item->list);
+				synchronize_rcu();
 				kfree(vector_item);
 				break;
 			}
@@ -1018,7 +1240,7 @@ static int prune_table_item_vector_add(struct prune_table *table,
 		}
 		vector_item->pruned = true;
 		vector_item->table = new_table;
-		list_add_tail(&vector_item->list,
+		list_add_tail_rcu(&vector_item->list,
 				  &entry->table_prune_vector);
 	}
 	return 0;
@@ -1098,10 +1320,11 @@ struct prune *prune_create(unsigned int mask_len, const struct prune_ops *ops,
 
 	prune->priv = priv;
 	prune->mask_len = mask_len;
-	INIT_LIST_HEAD(&prune->table_list);
+	INIT_LIST_HEAD_RCU(&prune->table_list);
 	prune->ops = ops;
 	prune_intersec_tables_ht_params_init(prune, mask_len);
 
+	list_add_tail_rcu(&prune->list, &prune_object_list);
 	return prune;
 }
 EXPORT_SYMBOL(prune_create);
@@ -1114,6 +1337,8 @@ void prune_destroy(struct prune *prune)
 {
 	WARN_ON(!list_empty(&prune->table_list));
 
+	list_del_rcu(&prune->list);
+	synchronize_rcu();
 	kfree(prune);
 }
 EXPORT_SYMBOL(prune_destroy);
@@ -1130,8 +1355,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,
@@ -1155,8 +1378,9 @@ struct prune_table *prune_table_create(struct prune *prune, unsigned long *mask,
 	bitmap_copy(table->mask, mask, prune->mask_len);
 	table->priv = priv;
 	table->prune = prune;
-	INIT_LIST_HEAD(&table->entry_list);
-	INIT_LIST_HEAD(&table->intersec_table_list);
+	INIT_LIST_HEAD_RCU(&table->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);
@@ -1167,7 +1391,7 @@ struct prune_table *prune_table_create(struct prune *prune, unsigned long *mask,
 	if (err)
 		goto err_table_build;
 
-	list_add_tail(&table->list, &prune->table_list);
+	list_add_tail_rcu(&table->list, &prune->table_list);
 
 	return table;
 
@@ -1189,8 +1413,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)
 {
@@ -1208,7 +1430,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);
 
@@ -1332,6 +1555,42 @@ struct list_head *prune_item_prune_vector(struct prune_item *item)
 }
 EXPORT_SYMBOL(prune_item_prune_vector);
 
+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 7e2d81e..bbad14b 100644
--- a/lib/test_prune.c
+++ b/lib/test_prune.c
@@ -1200,11 +1200,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