diff --git a/Documentation/prune.txt b/Documentation/prune.txt
new file mode 100644
index 0000000..3707508
--- /dev/null
+++ b/Documentation/prune.txt
@@ -0,0 +1,56 @@
+Prune
+=====
+
+Prune is a library which solves how to select which table needs to be searched.
+User can create tables with specific masks. The tables contains masked items
+with priority. Since similar item can be inserted to different tables, there is
+a need to prune between the tables.
+Pruning saves time complexity so that there won't be any need to search in
+other tables.
+
+Requirements
+============
+        * Each item has its own prune-vector.
+	* Prune-vector can be effected by add/remove an item.
+	* User has the ability to determine if items can be compared for pruning.
+
+Database
+========
+Each prune object has a list of tables with mask.
+Each table consist:
+	* hash table for items
+	* list of the corresponding intersection tables (with other tables)
+Each intersection table consists of:
+	* hash table for intersects items
+		* each item contain two lists:
+			1. items of the table owner
+			2. items of other tables (neighbor) in the prune object.
+
+
+Flows
+=====
+Direction of prune calculation:
+-------------------------------
+A*->A: Calculate my own prune vector
+A->A*: Calculate other items prune vector in the prune object
+
+Insert item 'R'
+---------------
+	* A*->A: Insert an item 'R' to a table, calculate its own prune-vector.
+	* A->A*: 'R' was inserted to a table, and we have to calculate the
+		 prune-vectors of other compatible items (in different tables).
+
+Remove item 'R'
+---------------
+	* A->*A: 'R' was removed from a table, this might impact the prune-vectors of
+		 other compatible items (in different tables).
+
+Add table 'T'
+-------------
+	By adding table 'T' to the prune object, prune vector item should be added to the
+	prune-vector of all the items in the prune object.
+
+Remove table 'T'
+----------------
+	By removing table 'T' from the prune object, prune vector item should be removed
+	from the prune-vector of all the items in the prune object.
diff --git a/MAINTAINERS b/MAINTAINERS
index 4713860..9763378 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11204,6 +11204,14 @@ F:	include/linux/sysctl.h
 F:	kernel/sysctl.c
 F:	tools/testing/selftests/sysctl/
 
+PRUNE
+M:	Tal Bar <talb@mellanox.com>
+L:	netdev@vger.kernel.org
+S:	Supported
+F:	lib/prune.c
+F:	lib/test_prune.c
+F:	include/linux/prune.h
+
 PS3 NETWORK SUPPORT
 M:	Geoff Levand <geoff@infradead.org>
 L:	netdev@vger.kernel.org
diff --git a/include/linux/prune.h b/include/linux/prune.h
new file mode 100644
index 0000000..3fbb18a
--- /dev/null
+++ b/include/linux/prune.h
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3
+/*
+ * include/linux/prune.h - Manager for priority base pruning lookups
+ * between tables.
+ * Copyright (c) 2018 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2018 Tal Bar <talb@mellanox.com>
+ */
+
+#ifndef _PRUNE_H
+#define _PRUNE_H
+
+struct prune_item_notification;
+struct prune_table;
+struct prune;
+
+struct prune_item {
+	unsigned long priority; /* Higher number means higher priority */
+	unsigned long *mask;
+	unsigned int mask_len; /* Number of bits */
+	void *priv;
+};
+
+struct prune_vector_item {
+	struct prune_table *table;
+	bool is_pruned; /* True if the item is pruning this table */
+	struct list_head list;
+	void *priv; /* Equal to priv field in struct prune_table_attr */
+};
+
+struct prune_item_notify {
+	struct prune_table *table; /* The table which the item belongs to */
+	struct prune_item item;
+	/* List of prune vector item */
+	const struct list_head *table_prune_list;
+	struct list_head list; /* Node of prune_item_notify */
+};
+
+/**
+ * This structure defines the management hooks for prune object.
+ * The following hooks can be defined; unless noted otherwise, they are
+ * optional and can be filled with a null pointer.
+ *
+ * prune_item_notify:
+ *	Called if there is a change in a the prune vector of an item or items,
+ *	the notification can be sent on one item or more.
+ *
+ * prune_mask_comparable:
+ *	Called when adding new items to a table.
+ */
+struct prune_ops {
+	void (*prune_item_notify)(struct prune_item_notification *notification);
+	int (*prune_mask_comparable)(unsigned long *mask_a,
+				     unsigned long *mask_b,
+				     unsigned int len);
+};
+
+struct prune_table_attr {
+	unsigned long *mask;
+	unsigned int mask_len; /* Number of bits */
+	/* Used by the priv field in strcut prune_table */
+	void *priv;
+};
+
+struct prune *prune_create(unsigned int mask_len, const struct prune_ops *ops,
+			   void *priv);
+void prune_destroy(struct prune *prune);
+struct prune_table *prune_table_create(struct prune *prune,
+				       struct prune_table_attr *table_attr);
+int prune_table_destroy(struct prune *prune, struct prune_table *table);
+int prune_item_add(struct prune *prune, struct prune_table *table,
+		   struct prune_item *item,
+		   struct prune_item_notify *item_notify,
+		   bool *notify);
+int prune_item_remove(struct prune *prune, struct prune_table *table,
+		      struct prune_item *item);
+struct prune_vector_item
+*prune_vector_item_get_next(struct prune_item_notify *item_notify,
+			   struct prune_vector_item *vector_item_prev);
+struct prune_item_notify
+*prune_item_notify_get_next(struct prune_item_notification *notification,
+			    struct prune_item_notify *item_notify_prev);
+
+static inline size_t prune_mask_size(unsigned int nbits)
+{
+	return BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+}
+#endif
diff --git a/lib/Kconfig b/lib/Kconfig
index e960894..c9ed533 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -583,6 +583,12 @@ config PARMAN
 config PRIME_NUMBERS
 	tristate
 
+config PRUNE
+	tristate "prune"
+	help
+	  This option enables the use of prune manager library for priority
+	  base pruning lookups between tables.
+
 config STRING_SELFTEST
 	tristate "Test string functions"
 
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 64155e3..d1b0add 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1803,6 +1803,16 @@ config TEST_PARMAN
 
 	  If unsure, say N.
 
+config TEST_PRUNE
+	tristate "Perform selftest on pruning tables manager"
+        default n
+        depends on PRUNE
+        help
+          Enable this option to test pruning tables manager
+          (or module load).
+
+          If unsure, say N.
+
 config TEST_LKM
 	tristate "Test module loading with 'hello world' module"
 	default n
diff --git a/lib/Makefile b/lib/Makefile
index a90d4fc..e26e0c9 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_TEST_UUID) += test_uuid.o
 obj-$(CONFIG_TEST_PARMAN) += test_parman.o
 obj-$(CONFIG_TEST_KMOD) += test_kmod.o
 obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o
+obj-$(CONFIG_TEST_PRUNE) += test_prune.o
 
 ifeq ($(CONFIG_DEBUG_KOBJECT),y)
 CFLAGS_kobject.o += -DDEBUG
@@ -252,6 +253,8 @@ obj-$(CONFIG_SBITMAP) += sbitmap.o
 
 obj-$(CONFIG_PARMAN) += parman.o
 
+obj-$(CONFIG_PRUNE) += prune.o
+
 # GCC library routines
 obj-$(CONFIG_GENERIC_ASHLDI3) += ashldi3.o
 obj-$(CONFIG_GENERIC_ASHRDI3) += ashrdi3.o
diff --git a/lib/prune.c b/lib/prune.c
new file mode 100644
index 0000000..56e786b
--- /dev/null
+++ b/lib/prune.c
@@ -0,0 +1,1433 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3
+/*
+ * lib/prune.c - Manager for priority base pruning lookups between tables.
+ * Copyright (c) 2018 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2018 Tal Bar <talb@mellanox.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/rhashtable.h>
+#include <linux/prune.h>
+#include <linux/jhash.h>
+#include <linux/bitmap.h>
+
+struct prune_item_notification {
+	/* list of prune_item_notify */
+	struct list_head item_notify_list;
+};
+
+struct prune_table_item_key {
+	struct prune_item *item;
+};
+
+struct prune_table_item_entry {
+	struct rhash_head ht_node; /* Member of tablset HT */
+	struct prune_table_item_key ht_key;
+	/* List of struct prune_vector_item */
+	struct list_head table_prune_vector;
+};
+
+struct prune_neighbor_table_items {
+	const struct prune_item *item;
+	struct list_head list;
+};
+
+struct prune_intersec_table_item {
+	const struct prune_table_item_entry *entry;
+	struct list_head list;
+};
+
+struct prune_intersec_table_item_key {
+	unsigned long *mask;
+};
+
+struct prune_intersec_table_item_entry {
+	struct rhash_head ht_node; /* Member of tablset HT */
+	struct prune_intersec_table_item_key ht_key;
+	unsigned int mask_len;
+	struct list_head intersec_table_items;
+	struct list_head neighbor_table_items; /* Can be enhanced to rbtree */
+};
+
+struct prune_intersec_table {
+	unsigned long *mask;
+	struct rhashtable items_ht;
+	struct prune_table *neigh_table;
+	struct list_head list;
+};
+
+struct prune_table {
+	unsigned long *mask;
+	struct rhashtable items_ht;
+	struct rhashtable_iter iter;
+	/* A list of table structs that contains intersection of the current
+	 * table with the other tables in the prune object.
+	 */
+	struct list_head intersec_table_list;
+	struct list_head list; /* Node of table_list in struct prune */
+	const struct prune_ops *ops;
+	void *priv;
+};
+
+struct prune {
+	const struct prune_ops *ops;
+	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 */
+	void *priv;
+};
+
+static u32 prune_table_ht_hash(const void *data, u32 len, u32 seed)
+{
+	const struct prune_table_item_key *item_key = data;
+	u32 jhash_len = prune_mask_size(len);
+	unsigned int mask_hash, prio_hash;
+	unsigned long long tmp;
+
+	mask_hash = jhash(item_key->item->mask, jhash_len, seed);
+	prio_hash = jhash(&item_key->item->priority, sizeof(unsigned long),
+			  seed);
+
+	tmp = (unsigned long long)(mask_hash) << 32 | prio_hash;
+	return jhash(&tmp, sizeof(unsigned long long), seed);
+}
+
+static int prune_table_ht_cmp(struct rhashtable_compare_arg *arg,
+			      const void *obj)
+{
+	const struct prune_table_item_key *item_key = arg->key;
+	const struct prune_table_item_entry *entry = obj;
+	int bit_equal;
+	bool prio_equal;
+
+	bit_equal = bitmap_equal(item_key->item->mask,
+				 entry->ht_key.item->mask,
+				 entry->ht_key.item->mask_len);
+	prio_equal = entry->ht_key.item->priority == item_key->item->priority;
+
+	return !prio_equal && !bit_equal;
+}
+
+static u32 prune_intersec_ht_hash(const void *data, u32 len, u32 seed)
+{
+	u32 jash_len = prune_mask_size(len);
+	const struct prune_intersec_table_item_key * const ht_key = data;
+
+	return jhash(ht_key->mask, jash_len, seed);
+}
+
+static int prune_intersec_ht_cmp(struct rhashtable_compare_arg *arg,
+				 const void *obj)
+{
+	const struct prune_intersec_table_item_entry *intersec_entry = obj;
+	const struct prune_intersec_table_item_key *ht_key = arg->key;
+
+	return !(bitmap_equal(ht_key->mask, intersec_entry->ht_key.mask,
+			      intersec_entry->mask_len));
+}
+
+static struct rhashtable_params tables_ht_params = {
+	.key_offset = offsetof(struct prune_table_item_entry, ht_key),
+	.head_offset = offsetof(struct prune_table_item_entry, ht_node),
+	.obj_cmpfn = prune_table_ht_cmp,
+	.hashfn = prune_table_ht_hash,
+	.automatic_shrinking = true,
+};
+
+static struct rhashtable_params intersec_tables_ht_params = {
+	.key_offset = offsetof(struct prune_intersec_table_item_entry, ht_key),
+	.head_offset = offsetof(struct prune_intersec_table_item_entry,
+				ht_node),
+	.obj_cmpfn = prune_intersec_ht_cmp,
+	.hashfn = prune_intersec_ht_hash,
+	.automatic_shrinking = true,
+};
+
+static void prune_table_prune_list_fini(struct prune_table_item_entry *entry)
+{
+	struct prune_vector_item *vector_item;
+	struct list_head *pos, *tmp;
+
+	list_for_each_safe(pos, tmp, &entry->table_prune_vector) {
+		vector_item = list_entry(pos, typeof(*vector_item), list);
+		list_del(&vector_item->list);
+		kfree(vector_item);
+	}
+}
+
+static int prune_table_prune_list_init(struct prune *prune,
+				       struct prune_table_item_entry *entry)
+{
+	struct prune_vector_item *vector_item;
+	struct prune_table *table;
+
+	INIT_LIST_HEAD(&entry->table_prune_vector);
+
+	list_for_each_entry(table, &prune->table_list, list) {
+		vector_item = kzalloc(sizeof(*vector_item), GFP_KERNEL);
+		if (!vector_item) {
+			prune_table_prune_list_fini(entry);
+			return -ENOMEM;
+		}
+		vector_item->is_pruned = true;
+		vector_item->priv = table->priv;
+		vector_item->table = table;
+		list_add_tail(&vector_item->list,
+			      &entry->table_prune_vector);
+	}
+	return 0;
+}
+
+static struct prune_table_item_entry *
+prune_item_entry_create(struct prune *prune,
+			struct prune_item *item)
+{
+	struct prune_table_item_entry *entry;
+	int err;
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		goto err_entry_alloc;
+
+	entry->ht_key.item = item;
+
+	err = prune_table_prune_list_init(prune, entry);
+	if (err)
+		goto err_list_init;
+
+	return entry;
+
+err_list_init:
+	kfree(entry);
+err_entry_alloc:
+	return NULL;
+}
+
+static void prune_entry_destroy(struct prune_table_item_entry *entry)
+{
+	prune_table_prune_list_fini(entry);
+	kfree(entry);
+}
+
+static bool prune_items_comparable(struct prune_table *table,
+				   unsigned long *mask_a, unsigned long *mask_b,
+				   unsigned int mask_len)
+{
+	if (table->ops->prune_mask_comparable)
+		return table->ops->prune_mask_comparable(mask_a,
+							 mask_b,
+							 mask_len);
+
+	return true;
+}
+
+static bool prune_vector_item_update(const struct list_head *table_prune_list,
+				     struct prune_table *corresponding_table,
+				     bool is_prune)
+{
+	struct prune_vector_item *vector_item;
+
+	list_for_each_entry(vector_item, table_prune_list, list) {
+		/* Find the corresponding prune item and set it */
+		if (vector_item->table == corresponding_table &&
+		    vector_item->is_pruned != is_prune) {
+			vector_item->is_pruned = is_prune;
+			return true;
+		}
+	}
+	return false;
+}
+
+static bool prune_vector_set(struct prune_table *table,
+			     struct prune_table *neigh_table,
+			     const struct prune_table_item_entry *entry,
+			     struct prune_neighbor_table_items *neigh_item,
+			     struct prune_intersec_table_item *intersec_item,
+			     bool neigh_items)
+{
+	const struct list_head *prune_list;
+	unsigned long *mask;
+	bool not_prune;
+
+	if (neigh_items)
+		/* Should not continue pruning in case neighbor priority is
+		 * higher then the new item
+		 */
+		not_prune = neigh_item->item->priority > entry->ht_key.item->priority;
+	else
+		/* Should not continue pruning in case my priority is lower
+		 * then the new item
+		 */
+		not_prune = intersec_item->entry->ht_key.item->priority < entry->ht_key.item->priority;
+
+	mask = neigh_items ? neigh_item->item->mask : intersec_item->entry->ht_key.item->mask;
+	prune_list = neigh_items ? &entry->table_prune_vector : &intersec_item->entry->table_prune_vector;
+	if (not_prune)
+		if (prune_items_comparable(table, entry->ht_key.item->mask,
+					   mask, entry->ht_key.item->mask_len))
+			return prune_vector_item_update(prune_list, neigh_table,
+						 false);
+
+	return false;
+}
+
+static void
+prune_item_entry_to_item_notify(struct prune_item_notify *item_notify,
+				struct prune_intersec_table_item *intersec_item)
+{
+	const struct prune_table_item_entry *entry = intersec_item->entry;
+
+	item_notify->item.priority = entry->ht_key.item->priority;
+	item_notify->item.mask = entry->ht_key.item->mask;
+	item_notify->item.mask_len = entry->ht_key.item->mask_len;
+	item_notify->table_prune_list = &entry->table_prune_vector;
+}
+
+static void
+prune_item_notify_list_fini(struct prune_item_notification *notify_list)
+{
+	struct prune_item_notify *item_notify;
+	struct list_head *pos, *tmp;
+
+	list_for_each_safe(pos, tmp, &notify_list->item_notify_list) {
+		item_notify = list_entry(pos, typeof(*item_notify), list);
+		list_del(&item_notify->list);
+		kfree(item_notify);
+	}
+}
+
+static int
+prune_item_notify_list_add(struct prune_table *table, /* intersec item table owner */
+			   struct prune_intersec_table_item *intersec_item,
+			   struct prune_item_notification *notify_list)
+{
+	struct prune_item_notify *item_notify;
+
+	item_notify = kzalloc(sizeof(*item_notify), GFP_KERNEL);
+	if (!item_notify) {
+		if (!list_empty(&notify_list->item_notify_list))
+			prune_item_notify_list_fini(notify_list);
+	}
+	item_notify->table = table;
+	prune_item_entry_to_item_notify(item_notify, intersec_item);
+	list_add_tail(&item_notify->list, &notify_list->item_notify_list);
+
+	return 0;
+}
+
+static bool
+prune_item_add_prune_vector_calc(struct prune_table *table,
+				 struct prune_table *curr_table, /* intersec item table owner */
+				 struct prune_intersec_table *intersec_table,
+				 struct prune_intersec_table_item_key *ht_key,
+				 const struct prune_table_item_entry *entry,
+				 bool neigh,
+				 struct prune_item_notification *notify_list)
+{
+	struct prune_neighbor_table_items *neigh_item;
+	struct prune_intersec_table_item_entry *intersec_entry;
+	struct prune_intersec_table_item *intersec_item;
+	bool prune_vector_chng = false;
+
+	bitmap_clear(ht_key->mask, 0, entry->ht_key.item->mask_len);
+	bitmap_and(ht_key->mask, entry->ht_key.item->mask, intersec_table->mask,
+		   entry->ht_key.item->mask_len);
+
+	intersec_entry = rhashtable_lookup_fast(&intersec_table->items_ht,
+						ht_key,
+						intersec_tables_ht_params);
+	if (!intersec_entry)
+		return false;
+
+	if (neigh) {
+		/* Iterate on neighbor_table_items. In case there is a entry
+		 * with higher priority we should not prune the neighbor table.
+		 */
+		list_for_each_entry(neigh_item,
+				    &intersec_entry->neighbor_table_items,
+				    list) {
+			prune_vector_chng |= prune_vector_set(table,
+					     intersec_table->neigh_table,
+					     entry,
+					     neigh_item,
+					     NULL,
+					     neigh);
+		}
+	} else {
+		/* For my items need to update to prune vector
+		 * corresponding to the new item
+		 */
+		list_for_each_entry(intersec_item,
+				    &intersec_entry->intersec_table_items,
+				    list) {
+			if (prune_vector_set(table,
+					     intersec_table->neigh_table,
+					     entry,
+					     NULL,
+					     intersec_item,
+					     neigh)) {
+				prune_item_notify_list_add(curr_table,
+							   intersec_item,
+							   notify_list);
+			}
+		}
+	}
+	return prune_vector_chng;
+}
+
+static bool
+prune_neigh_item_prio_higher(struct prune_table *table,
+			     const struct prune_item *neigh_item,
+			     const struct prune_table_item_entry *entry)
+{
+	if (prune_items_comparable(table, neigh_item->mask,
+				   entry->ht_key.item->mask,
+				   neigh_item->mask_len)) {
+		if (neigh_item->priority > entry->ht_key.item->priority)
+			return true;
+		else
+			return false;
+	}
+	return false;
+}
+
+static bool prune_neighbor_list_item_higher_prio(struct prune_table *table,
+						 struct prune_intersec_table_item *intersec_item,
+						 struct prune_intersec_table_item_entry *intersec_entry)
+{
+	struct prune_neighbor_table_items *neigh_item;
+
+	list_for_each_entry(neigh_item, &intersec_entry->neighbor_table_items,
+			    list) {
+		if (prune_neigh_item_prio_higher(table, neigh_item->item,
+						 intersec_item->entry))
+			return true;
+	}
+	return false;
+}
+
+static void prune_item_del_prune_vector_calc(struct prune_table *table,
+					     struct prune_table *curr_table, /* intersec item table owner */
+					     struct prune_item *deleted_item,
+					     struct prune_intersec_table *intersec_table,
+					     struct prune_intersec_table_item_key *ht_key,
+					     struct prune_item_notification *notify_list)
+{
+	struct prune_intersec_table_item_entry *intersec_entry;
+	struct prune_intersec_table_item *intersec_item;
+	const struct list_head *prune_list;
+	bool to_prune, notify;
+
+	bitmap_clear(ht_key->mask, 0, deleted_item->mask_len);
+	bitmap_and(ht_key->mask, deleted_item->mask, intersec_table->mask,
+		   deleted_item->mask_len);
+
+	intersec_entry = rhashtable_lookup_fast(&intersec_table->items_ht,
+						ht_key,
+						intersec_tables_ht_params);
+	if (!intersec_entry)
+		return;
+
+	/* Check if one of the items in the neighbor list have higher prio if so
+	 * don't prune
+	 */
+	list_for_each_entry(intersec_item,
+			    &intersec_entry->intersec_table_items, list) {
+		/* Check if there is an item in my neighbor list with higher
+		 * priority and it's allowed to compare between them if so -->
+		 * don't prune
+		 */
+		to_prune = true;
+
+		notify = false;
+		if (!prune_neighbor_list_item_higher_prio(table,
+							  intersec_item,
+							  intersec_entry)) {
+			/* Update intersec_item prune vector for the
+			 * corresponding table
+			 */
+			prune_list = &intersec_item->entry->table_prune_vector;
+			notify = prune_vector_item_update(prune_list, table,
+							  true);
+		}
+		if (notify) {
+			prune_item_notify_list_add(curr_table,
+						   intersec_item,
+						   notify_list);
+		}
+	}
+}
+
+static int
+prune_item_add_prune_vector_set(struct prune_table *table,
+				struct prune_item *item,
+				struct prune_table_item_entry *entry,
+				struct prune_item_notify *item_notify,
+				bool *notify)
+{
+	struct prune_intersec_table *intersec_table;
+	struct prune_intersec_table_item_key ht_key;
+	unsigned int nbits = entry->ht_key.item->mask_len;
+
+	ht_key.mask = kcalloc(1, prune_mask_size(nbits), GFP_ATOMIC);
+	if (!ht_key.mask)
+		return -ENOMEM;
+
+	list_for_each_entry(intersec_table, &table->intersec_table_list, list) {
+		*notify |= prune_item_add_prune_vector_calc(table,
+							   NULL,
+							   intersec_table,
+							   &ht_key, entry,
+							   true,
+							   NULL);
+	}
+	if (*notify) {
+		item_notify->table = table;
+		item_notify->item = *item;
+		item_notify->table_prune_list = &entry->table_prune_vector;
+	}
+	kfree(ht_key.mask);
+	return 0;
+}
+
+static void
+prune_neigh_prune_vector_calc(struct prune_table *table,
+			      struct prune_table *curr_table,
+			      struct prune_item *item,
+			      struct prune_intersec_table_item_key *ht_key,
+			      const struct prune_table_item_entry *entry,
+			      bool add_item,
+			      struct prune_item_notification *notify_list)
+{
+	struct prune_intersec_table *intersec_table;
+
+	/* Search the corresponding intersection table with
+	 * this table and update the prune vector of items in
+	 * the intersection table if there is no higher
+	 * priority items in their neighbor table.
+	 */
+	list_for_each_entry(intersec_table, &curr_table->intersec_table_list,
+			    list) {
+		if (intersec_table->neigh_table == table) {
+			/* This is the corresponding table */
+			if (add_item)
+				prune_item_add_prune_vector_calc(table,
+								 curr_table,
+								 intersec_table,
+								 ht_key,
+								 entry,
+								 false,
+								 notify_list);
+			else
+				prune_item_del_prune_vector_calc(table,
+								 curr_table,
+								 item,
+								 intersec_table,
+								 ht_key,
+								 notify_list);
+		}
+	}
+}
+
+static int
+prune_neigh_table_prune_vector_set(struct prune *prune,
+				   struct prune_table *table,
+				   struct prune_item *item,
+				   const struct prune_table_item_entry *entry,
+				   bool add_item)
+{
+	struct prune_intersec_table_item_key ht_key;
+	struct prune_table *curr_table;
+	struct prune_item_notification notification_list;
+
+	ht_key.mask = kcalloc(1, prune_mask_size(item->mask_len), GFP_ATOMIC);
+	if (!ht_key.mask)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&notification_list.item_notify_list);
+
+	list_for_each_entry(curr_table, &prune->table_list, list) {
+		if (curr_table != table)
+			prune_neigh_prune_vector_calc(table, curr_table, item,
+						      &ht_key, entry, add_item,
+						      &notification_list);
+	}
+	if (!list_empty(&notification_list.item_notify_list)) {
+		prune->ops->prune_item_notify(&notification_list);
+		prune_item_notify_list_fini(&notification_list);
+	}
+
+	kfree(ht_key.mask);
+	return 0;
+}
+
+static struct prune_table_item_entry *
+prune_table_entry_create(struct prune *prune,
+			 struct prune_table *table,
+			 struct prune_item *item,
+			 struct prune_item_notify *item_notify,
+			 bool *notify)
+{
+	struct prune_table_item_entry *entry;
+	int err;
+
+	if (bitmap_subset(item->mask, table->mask, item->mask_len) != 1)
+		return ERR_PTR(-EPERM);
+
+	entry = prune_item_entry_create(prune, item);
+	if (!entry)
+		return ERR_PTR(-ENOMEM);
+
+	err = prune_item_add_prune_vector_set(table, item, entry, item_notify,
+					      notify);
+	if (err) {
+		prune_entry_destroy(entry);
+		return ERR_PTR(err);
+	}
+	err = rhashtable_insert_fast(&table->items_ht,
+				     &entry->ht_node, tables_ht_params);
+	if (err) {
+		prune_entry_destroy(entry);
+		return ERR_PTR(err);
+	}
+	return entry;
+}
+
+static int prune_table_entry_destroy(struct prune_table *table,
+				     struct prune_item *item)
+{
+	struct prune_table_item_entry *entry;
+	int err;
+
+	entry = rhashtable_lookup_fast(&table->items_ht, &item,
+				       tables_ht_params);
+	if (!entry)
+		return -EPERM;
+
+	err = rhashtable_remove_fast(&table->items_ht, &entry->ht_node,
+				     tables_ht_params);
+	if (err)
+		return err;
+
+	prune_entry_destroy(entry);
+
+	return 0;
+}
+
+static int prune_intersec_item_add(struct prune_intersec_table *intersec_table,
+				   struct prune_item *item,
+				   const struct prune_table_item_entry *entry,
+				   bool neigh)
+{
+	struct prune_neighbor_table_items *neigh_item = NULL;
+	struct prune_intersec_table_item_entry *intersec_entry;
+	struct prune_intersec_table_item *list_item = NULL;
+	struct prune_intersec_table_item_key ht_key;
+	unsigned long *mask;
+	bool found = true;
+	int err = 0;
+
+	ht_key.mask = kcalloc(1, prune_mask_size(item->mask_len), GFP_ATOMIC);
+	if (!ht_key.mask)
+		return -ENOMEM;
+
+	bitmap_and(ht_key.mask, intersec_table->mask, item->mask,
+		   item->mask_len);
+
+	intersec_entry = rhashtable_lookup_fast(&intersec_table->items_ht,
+						&ht_key,
+						intersec_tables_ht_params);
+	if (!intersec_entry) {
+		intersec_entry = kzalloc(sizeof(*intersec_entry), GFP_ATOMIC);
+		if (!intersec_entry)
+			goto err_entry_alloc;
+
+		mask = kcalloc(1, prune_mask_size(item->mask_len), GFP_ATOMIC);
+		intersec_entry->ht_key.mask = mask;
+		if (!intersec_entry->ht_key.mask)
+			goto err_mask_alloc;
+
+		intersec_entry->mask_len = item->mask_len;
+
+		bitmap_and(intersec_entry->ht_key.mask, intersec_table->mask,
+			   item->mask, item->mask_len);
+
+		INIT_LIST_HEAD(&intersec_entry->intersec_table_items);
+		INIT_LIST_HEAD(&intersec_entry->neighbor_table_items);
+		found = false;
+	}
+
+	if (!neigh) {
+		list_item = kzalloc(sizeof(*list_item), GFP_KERNEL);
+		if (!list_item)
+			goto err_list_item_alloc;
+
+		list_item->entry = entry;
+		list_add_tail(&list_item->list,
+			      &intersec_entry->intersec_table_items);
+	} else {
+		neigh_item = kzalloc(sizeof(*neigh_item), GFP_ATOMIC);
+		if (!neigh_item)
+			goto err_nigh_list_item_alloc;
+
+		neigh_item->item = item;
+		list_add_tail(&neigh_item->list,
+			      &intersec_entry->neighbor_table_items);
+	}
+	if (!found) {
+		err = rhashtable_insert_fast(&intersec_table->items_ht,
+					     &intersec_entry->ht_node,
+					     intersec_tables_ht_params);
+		if (err)
+			goto err_rhashtable_insert;
+	}
+
+	kfree(ht_key.mask);
+	return 0;
+
+err_rhashtable_insert:
+	if (!neigh) {
+		list_del(&list_item->list);
+		kfree(list_item);
+	} else {
+		list_del(&neigh_item->list);
+		kfree(neigh_item);
+	}
+err_nigh_list_item_alloc:
+err_list_item_alloc:
+	kfree(intersec_entry->ht_key.mask);
+err_mask_alloc:
+	kfree(intersec_entry);
+err_entry_alloc:
+	kfree(ht_key.mask);
+	return err ? err : -ENOMEM;
+}
+
+static int
+prune_intersec_item_remove(struct prune_intersec_table *intersec_table,
+			   struct prune_item *remove_item, bool neigh)
+{
+	struct prune_intersec_table_item_entry *intersec_entry;
+	struct prune_neighbor_table_items *neigh_item;
+	struct prune_intersec_table_item *list_item;
+	struct prune_intersec_table_item_key ht_key;
+	struct list_head *pos, *tmp;
+
+	ht_key.mask = kcalloc(1, prune_mask_size(remove_item->mask_len),
+			      GFP_ATOMIC);
+	if (!ht_key.mask)
+		return -ENOMEM;
+
+	bitmap_and(ht_key.mask, intersec_table->mask, remove_item->mask,
+		   remove_item->mask_len);
+
+	intersec_entry = rhashtable_lookup_fast(&intersec_table->items_ht,
+						&ht_key,
+						intersec_tables_ht_params);
+	if (!intersec_entry) {
+		kfree(ht_key.mask);
+		return -EPERM;
+	}
+	if (!neigh) {
+		list_for_each_safe(pos, tmp,
+				   &intersec_entry->intersec_table_items) {
+			list_item = list_entry(pos, typeof(*list_item), list);
+			if (list_item->entry->ht_key.item == remove_item) {
+				list_del(&list_item->list);
+				kfree(list_item);
+				break;
+			}
+		}
+	} else {
+		list_for_each_safe(pos, tmp,
+				   &intersec_entry->neighbor_table_items) {
+			neigh_item = list_entry(pos, typeof(*neigh_item), list);
+			if (remove_item == neigh_item->item) {
+				list_del(&neigh_item->list);
+				kfree(neigh_item);
+				break;
+			}
+		}
+	}
+	if (list_empty(&intersec_entry->intersec_table_items) &&
+	    list_empty(&intersec_entry->neighbor_table_items)) {
+		rhashtable_remove_fast(&intersec_table->items_ht,
+				       &intersec_entry->ht_node,
+				       intersec_tables_ht_params);
+		kfree(intersec_entry->ht_key.mask);
+		kfree(intersec_entry);
+	}
+	kfree(ht_key.mask);
+	return 0;
+}
+
+static struct prune_intersec_table *
+prune_intrsc_table_create(unsigned int mask_len)
+{
+	struct prune_intersec_table *intersec_table;
+
+	intersec_table = kzalloc(sizeof(*intersec_table), GFP_KERNEL);
+	if (!intersec_table)
+		goto err_intersec_table_alloc;
+
+	intersec_table->mask = kcalloc(1, prune_mask_size(mask_len),
+				       GFP_ATOMIC);
+	if (!intersec_table->mask)
+		goto err_intersec_table_mask_alloc;
+
+	return intersec_table;
+
+err_intersec_table_mask_alloc:
+	kfree(intersec_table);
+err_intersec_table_alloc:
+	return NULL;
+}
+
+/* Update the item of the current table to the nighbor item in the
+ * intersection table
+ */
+static void prune_intersec_table_neigh_items_update(struct prune_table *curr_table,
+						    struct prune_intersec_table *intersec_table)
+{
+	struct prune_table_item_entry *entry;
+
+	if (!atomic_read(&curr_table->items_ht.nelems))
+		return;
+
+	rhashtable_walk_enter(&curr_table->items_ht, &curr_table->iter);
+	rhashtable_walk_start(&curr_table->iter);
+
+	while ((entry = rhashtable_walk_next(&curr_table->iter))) {
+		prune_intersec_item_add(intersec_table, entry->ht_key.item,
+					entry, true);
+	}
+
+	rhashtable_walk_stop(&curr_table->iter);
+	rhashtable_walk_exit(&curr_table->iter);
+}
+
+static int prune_intersec_table_add(struct prune *prune,
+				    struct prune_table *new_table,
+				    struct prune_table *curr_table)
+{
+	struct prune_intersec_table *intersec_table1;
+	struct prune_intersec_table *intersec_table2;
+	int err;
+
+	intersec_table1 = prune_intrsc_table_create(prune->mask_len);
+	if (!intersec_table1)
+		goto err_allocate_intersec_table1;
+
+	intersec_table2 = prune_intrsc_table_create(prune->mask_len);
+	if (!intersec_table2)
+		goto err_allocate_intersec_table2;
+
+	bitmap_and(intersec_table1->mask, new_table->mask, curr_table->mask,
+		   prune->mask_len);
+
+	bitmap_and(intersec_table2->mask, new_table->mask, curr_table->mask,
+		   prune->mask_len);
+
+	/* TODO: maybe need to verify if there is another intersec table with
+	 * same mask and set error ?!.
+	 */
+	err = rhashtable_init(&intersec_table1->items_ht,
+			      &intersec_tables_ht_params);
+	if (err)
+		goto err_rhashtable_init_1;
+
+	err = rhashtable_init(&intersec_table2->items_ht,
+			      &intersec_tables_ht_params);
+	if (err)
+		goto err_rhashtable_init_2;
+
+	/* Add new intersection table to the old table */
+	intersec_table1->neigh_table = new_table;
+	prune_intersec_table_neigh_items_update(curr_table, intersec_table1);
+	list_add(&intersec_table1->list, &curr_table->intersec_table_list);
+	/* Add new intersection table to new table */
+	intersec_table2->neigh_table = curr_table;
+	prune_intersec_table_neigh_items_update(curr_table, intersec_table2);
+	list_add(&intersec_table2->list, &new_table->intersec_table_list);
+
+	return 0;
+
+err_rhashtable_init_2:
+	rhashtable_destroy(&intersec_table2->items_ht);
+err_rhashtable_init_1:
+err_allocate_intersec_table2:
+	kfree(intersec_table1->mask);
+	kfree(intersec_table1);
+err_allocate_intersec_table1:
+	return -ENOMEM;
+}
+
+/* Add new intersection table to all tables */
+static int prune_intersec_table_build(struct prune *prune,
+				      struct prune_table *new_table)
+{
+	struct prune_table *curr_table;
+	int err;
+
+	list_for_each_entry(curr_table, &prune->table_list, list) {
+		err = prune_intersec_table_add(prune, new_table, curr_table);
+		if (err) {
+			/* TODO: Need to free memory of new intersec tables,
+			 * that have been created.
+			 */
+			return err;
+		}
+	}
+	return 0;
+}
+
+static void prune_intersec_rht_free(void *ptr, void *arg)
+{
+	struct prune_intersec_table_item_entry *rht_node = ptr;
+	struct prune_intersec_table_item *intersec_item;
+	struct prune_neighbor_table_items *neigh_item;
+	struct list_head *pos, *tmp;
+
+	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);
+		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);
+		kfree(neigh_item);
+	}
+	kfree(rht_node);
+}
+
+static int prune_instersec_table_remove(unsigned int mask_len,
+					struct prune_table *table,
+					struct prune_table *table_removed)
+{
+	struct prune_intersec_table *curr_intersec_table;
+	struct list_head *intersec_pos, *tmp;
+	struct rhashtable *items_rht;
+	unsigned long *curr_mask;
+	int err;
+
+	curr_mask = kcalloc(1, prune_mask_size(mask_len), GFP_ATOMIC);
+	if (!curr_mask)
+		return -ENOMEM;
+
+	bitmap_and(curr_mask, table->mask, table_removed->mask, mask_len);
+
+	list_for_each_safe(intersec_pos, tmp, &table->intersec_table_list) {
+		curr_intersec_table = list_entry(intersec_pos,
+						 typeof(*curr_intersec_table),
+						 list);
+		if (bitmap_equal(curr_mask, curr_intersec_table->mask,
+				 mask_len)) {
+			list_del(&curr_intersec_table->list);
+			items_rht = &curr_intersec_table->items_ht;
+			rhashtable_free_and_destroy(items_rht,
+						    prune_intersec_rht_free,
+						    NULL);
+			kfree(curr_intersec_table->mask);
+			kfree(curr_intersec_table);
+			err = 0;
+			goto end;
+		}
+	}
+	goto err_flow;
+
+err_flow:
+	err = -EPERM;
+	WARN_ON(1); /* Didn't found the wanted intersection table */
+end:
+	kfree(curr_mask);
+	return err;
+}
+
+static int
+prune_intersec_list_item_add(struct prune_table *item_added_table,
+			     struct prune_table *table,
+			     struct prune_item *item,
+			     const struct prune_table_item_entry *entry,
+			     bool neigh_item)
+{
+	struct prune_intersec_table *curr_intersec_table;
+	int err;
+
+	list_for_each_entry(curr_intersec_table, &table->intersec_table_list,
+			    list) {
+		/* Add the item only to the corresponding tables */
+		if (!neigh_item ||
+		    curr_intersec_table->neigh_table == item_added_table) {
+			err = prune_intersec_item_add(curr_intersec_table, item,
+						      entry,
+						      neigh_item);
+			if (err)
+				return err;
+		}
+	}
+	return 0;
+}
+
+static int
+prune_intersec_list_item_remove(struct prune_table *remove_item_table,
+				struct prune_table *table,
+				struct prune_item *remove_item,
+				bool neigh_item)
+{
+	struct prune_intersec_table *curr_intersec_table;
+	int err;
+
+	list_for_each_entry(curr_intersec_table, &table->intersec_table_list,
+			    list) {
+		if (curr_intersec_table->neigh_table == remove_item_table ||
+		    !neigh_item) {
+			err = prune_intersec_item_remove(curr_intersec_table,
+							 remove_item,
+							 neigh_item);
+			if (err)
+				return err;
+		}
+	}
+	return 0;
+}
+
+static int prune_table_item_list_add(struct prune_table *table,
+				     struct prune_table *new_table)
+{
+	struct prune_vector_item *vector_item;
+	struct prune_table_item_entry *entry;
+
+	if (!atomic_read(&table->items_ht.nelems))
+		return 0;
+
+	rhashtable_walk_enter(&table->items_ht, &table->iter);
+	rhashtable_walk_start(&table->iter);
+
+	while ((entry = rhashtable_walk_next(&table->iter))) {
+		vector_item = kzalloc(sizeof(*vector_item), GFP_ATOMIC);
+		if (!vector_item)
+			return -ENOMEM;
+		/* TODO: Remove all the items from entry in case of error*/
+
+		vector_item->is_pruned = true;
+		vector_item->priv = new_table->priv;
+		vector_item->table = new_table;
+		list_add_tail(&vector_item->list,
+			      &entry->table_prune_vector);
+	}
+	rhashtable_walk_stop(&table->iter);
+	rhashtable_walk_exit(&table->iter);
+
+	return 0;
+}
+
+static int prune_table_item_list_remove(struct prune_table *table,
+					struct prune_table *del_table)
+{
+	struct prune_vector_item *vector_item;
+	struct prune_table_item_entry *entry;
+	struct list_head *pos, *tmp;
+
+	if (!atomic_read(&table->items_ht.nelems))
+		return 0;
+
+	rhashtable_walk_enter(&table->items_ht, &table->iter);
+	rhashtable_walk_start(&table->iter);
+
+	while ((entry = rhashtable_walk_next(&table->iter))) {
+		list_for_each_safe(pos, tmp, &entry->table_prune_vector) {
+			vector_item = list_entry(pos, typeof(*vector_item),
+						 list);
+			if (vector_item->table == del_table) {
+				list_del(&vector_item->list);
+				kfree(vector_item);
+				break;
+			}
+		}
+	}
+	rhashtable_walk_stop(&table->iter);
+	rhashtable_walk_exit(&table->iter);
+
+	return 0;
+}
+
+/* Add new prune table item to each item in each table */
+static void prune_item_prune_table_add(struct prune *prune,
+				       struct prune_table *new_table)
+{
+	struct prune_table *table;
+
+	list_for_each_entry(table, &prune->table_list, list) {
+		prune_table_item_list_add(table, new_table);
+	}
+}
+
+static void prune_item_prune_table_remove(struct prune *prune,
+					  struct prune_table *del_table)
+{
+	struct prune_table *table;
+
+	list_for_each_entry(table, &prune->table_list, list) {
+		if (del_table != table)
+			prune_table_item_list_remove(table, del_table);
+	}
+}
+
+/**
+ * prune_create - Creates a prune object which manage the pruning vector
+ *		  between tables
+ * @mask_len:	Sets the mask len (nbits) for all tables in the prune
+ *		object.
+ * @ops:	caller-specific callbacks
+ * @priv:	Pointer to a private user data.
+ *
+ * Returns a pointer to newly created prune instance in case of success,
+ * otherwise it returns NULL.
+ *
+ * Note: It's the user responsibility to care for synchronization.
+ */
+struct prune *prune_create(unsigned int mask_len, const struct prune_ops *ops,
+			   void *priv)
+{
+	struct prune *prune;
+
+	if (!ops)
+		return NULL;
+
+	prune = kzalloc(sizeof(*prune), GFP_KERNEL);
+	if (!prune)
+		return NULL;
+
+	prune->table_cnt = 0;
+	prune->priv = priv;
+	prune->mask_len = mask_len;
+	INIT_LIST_HEAD(&prune->table_list);
+	prune->ops = ops;
+	tables_ht_params.key_len = mask_len;
+	intersec_tables_ht_params.key_len = mask_len;
+
+	return prune;
+}
+EXPORT_SYMBOL(prune_create);
+
+/**
+ * prune_destroy - Destroys the prune object
+ * @prune: Prune object
+ *
+ * Note: It's the user responsibility to care for synchronization.
+ */
+void prune_destroy(struct prune *prune)
+{
+	WARN_ON(prune->table_cnt > 0 || !list_empty(&prune->table_list));
+
+	kfree(prune);
+}
+EXPORT_SYMBOL(prune_destroy);
+
+/**
+ * prune_table_create - Creates new table with specific mask and adds it to the
+ *			prune object
+ * @prune:	Prune object
+ * @table_attr:	Attributes for this table
+ *
+ * Returns a pointer to newly created prune table instance in case of success,
+ * 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,
+				       struct prune_table_attr *table_attr)
+{
+	int err;
+	struct prune_table *table;
+
+	if (prune->mask_len != table_attr->mask_len)
+		return ERR_PTR(-EPERM);
+
+	table = kzalloc(sizeof(*table), GFP_KERNEL);
+	if (!table)
+		return ERR_PTR(-ENOMEM);
+
+	table->mask = kcalloc(1, prune_mask_size(prune->mask_len), GFP_ATOMIC);
+	if (!table->mask) {
+		err = -ENOMEM;
+		goto err_mask_alloc_fail;
+	}
+	bitmap_copy(table->mask, table_attr->mask, table_attr->mask_len);
+	table->priv = table_attr->priv;
+	table->ops = prune->ops;
+	INIT_LIST_HEAD(&table->intersec_table_list);
+
+	err = rhashtable_init(&table->items_ht, &tables_ht_params);
+	if (err)
+		goto err_rhashtable_init;
+
+	err = prune_intersec_table_build(prune, table);
+	if (err)
+		goto err_intersec_table_build;
+
+	/* Enlarge the prune vector of all item with the new table */
+	prune_item_prune_table_add(prune, table);
+
+	list_add_tail(&table->list, &prune->table_list);
+
+	prune->table_cnt++;
+
+	return table;
+
+err_intersec_table_build:
+	rhashtable_destroy(&table->items_ht);
+err_rhashtable_init:
+	kfree(table->mask);
+err_mask_alloc_fail:
+	kfree(table);
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL(prune_table_create);
+
+/**
+ * prune_table_destroy - Removes a table from the prune object and destroys it.
+ * @prune:	Prune object
+ * @table:	Table to be removed.
+ *
+ * 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 *prune, struct prune_table *table)
+{
+	struct prune_intersec_table *curr_intersec_table;
+	struct prune_table *curr_table;
+	struct rhashtable *items_rht;
+	struct list_head *pos, *tmp;
+	int err;
+
+	if (prune->table_cnt == 0 ||
+	    atomic_read(&table->items_ht.nelems) != 0) {
+		WARN_ON(1);
+		return -EPERM;
+	}
+
+	else if (prune->table_cnt > 1) {
+		/* Remove the intersection table from neighbor tables */
+		list_for_each_entry(curr_table, &prune->table_list, list) {
+			if (curr_table != table) {
+				err = prune_instersec_table_remove
+						(prune->mask_len,
+						 curr_table,
+						 table);
+				if (err)
+					return err;
+			}
+		}
+		/* Remove all intersection tables from this table */
+		list_for_each_safe(pos, tmp, &table->intersec_table_list) {
+			curr_intersec_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);
+			kfree(curr_intersec_table->mask);
+			kfree(curr_intersec_table);
+		}
+	}
+	WARN_ON_ONCE(!list_empty(&table->intersec_table_list));
+
+	prune_item_prune_table_remove(prune, table);
+
+	list_del(&table->list);
+	rhashtable_destroy(&table->items_ht);
+	kfree(table->mask);
+	kfree(table);
+
+	prune->table_cnt--;
+
+	return 0;
+}
+EXPORT_SYMBOL(prune_table_destroy);
+
+/**
+ * prune_item_add - Adds an item to a table.
+ * @prune:		Prune object
+ * @table:		Add the item to this table.
+ * @item:		The item to add.
+ * @item_notify:	In case of success returns the item prune vector
+ *			valid only if notify == true
+ * @notify:		True if there is a change in the prune vector
+ *			i,e not all tables are pruned (default)
+ *
+ * Returns 0 in case of success, negative number to indicate an error.
+ *
+ * Note: It's the user responsibility to care for synchronization.
+ */
+int prune_item_add(struct prune *prune, struct prune_table *table,
+		   struct prune_item *item,
+		   struct prune_item_notify *item_notify,
+		   bool *notify)
+{
+	const struct prune_table_item_entry *entry;
+	struct prune_table *curr_table;
+	int err;
+
+	if (prune->mask_len != item->mask_len)
+		return -EPERM;
+
+	*notify = false;
+	entry = prune_table_entry_create(prune, table, item, item_notify,
+					 notify);
+	if (IS_ERR(entry))
+		return PTR_ERR(entry);
+
+	/* Add the item to the intersection tables of all tables*/
+	list_for_each_entry(curr_table, &prune->table_list, list) {
+		err = prune_intersec_list_item_add(table, curr_table, item,
+						   entry,
+						   curr_table != table ?
+								  true : false);
+		if (err) {
+			if (prune_table_entry_destroy(table, item))
+				WARN_ON(1);
+
+			*notify = false;
+			return err;
+		}
+	}
+	/* Update prune vector of the corresponding items of the neighbor
+	 * tables
+	 */
+	err = prune_neigh_table_prune_vector_set(prune, table, item, entry,
+						 true);
+	if (err) {
+		if (prune_table_entry_destroy(table, item))
+			WARN_ON(1);
+
+		prune_item_remove(prune, table, item);
+		*notify = false;
+		return err;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(prune_item_add);
+
+/**
+ * prune_item_remove - removes an item from a table
+ * @prune:	prune object
+ * @table:	Remove the item from this table.
+ * @item:	the item to remove.
+ *
+ * Returns 0 in case of success, negative number to indicate an error.
+ *
+ * Note: It's the user responsibility to care for synchronization.
+ */
+int prune_item_remove(struct prune *prune, struct prune_table *table,
+		      struct prune_item *item)
+{
+	struct prune_table *curr_table;
+	int err;
+
+	if (prune->mask_len != item->mask_len)
+		return -EPERM;
+
+	/* Remove the item from tables in the intersect tables */
+	list_for_each_entry(curr_table, &prune->table_list, list) {
+		err = prune_intersec_list_item_remove(table, curr_table, item,
+						      curr_table != table ?
+								  true : false);
+		if (err)
+			return err;
+	}
+
+	/* Update prune vector of the corresponding items of the neighbor
+	 * prune_table
+	 */
+	err = prune_neigh_table_prune_vector_set(prune, table, item, NULL,
+						 false);
+	if (err)
+		return err;
+
+	err = prune_table_entry_destroy(table, item);
+	if (err)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(prune_item_remove);
+
+/**
+ * prune_vector_item_get_next - gets the next vector item
+ * @item_notify:	item_notify object
+ * @vector_item_prev:	NULL for the first item otherwise the next one.
+ *
+ * Returns a pointer to a vector item in case of success,
+ * NULL if there is no more  items
+ *
+ * Note: It's the user responsibility to care for synchronization.
+ */
+struct prune_vector_item
+*prune_vector_item_get_next(struct prune_item_notify *item_notify,
+			    struct prune_vector_item *vector_item_prev)
+{
+	struct prune_vector_item *vector_item;
+
+	if (!vector_item_prev) {
+		return list_first_entry(item_notify->table_prune_list,
+					     typeof(*vector_item), list);
+	} else {
+		vector_item = list_next_entry(vector_item_prev, list);
+		if (&vector_item->list == item_notify->table_prune_list)
+			return NULL;
+		else
+			return vector_item;
+	}
+	WARN_ON(1);
+	return NULL;
+}
+EXPORT_SYMBOL(prune_vector_item_get_next);
+
+/**
+ * prune_item_notify_get_next - gets the next item notify
+ * @notification:	notification object
+ * @item_notify_prev:	NULL for the first item otherwise the next one.
+ *
+ * Returns a pointer to an item notify in case of success,
+ * NULL if there is no more items
+ *
+ * Note: It's the user responsibility to care for synchronization.
+ */
+struct prune_item_notify
+*prune_item_notify_get_next(struct prune_item_notification *notification,
+			    struct prune_item_notify *item_notify_prev)
+{
+	struct prune_item_notify *item_notify;
+
+	if (!item_notify_prev) {
+		return list_first_entry(&notification->item_notify_list,
+					typeof(*item_notify), list);
+	} else {
+		item_notify = list_next_entry(item_notify_prev, list);
+		if (&item_notify->list == &notification->item_notify_list)
+			return NULL;
+		else
+			return item_notify;
+	}
+	WARN_ON(1);
+	return NULL;
+}
+EXPORT_SYMBOL(prune_item_notify_get_next);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Tal Bar<talb@mellanox.com>");
+MODULE_DESCRIPTION("Prune lookup table manager");
diff --git a/lib/test_prune.c b/lib/test_prune.c
new file mode 100644
index 0000000..dec866b
--- /dev/null
+++ b/lib/test_prune.c
@@ -0,0 +1,1095 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3
+/*
+ * lib/test_prune.c - Test module for prune
+ * Copyright (c) 2018 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2018 Tal Bar <talb@mellanox.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/prune.h>
+#include <linux/bitmap.h>
+#include <linux/slab.h>
+
+static void test_prune_notification_print(struct prune_item_notify *item_notify,
+					  bool print)
+{
+	struct prune_vector_item *vector_item = NULL;
+	int i;
+
+	if (print) {
+		i = 0;
+		pr_info("item mask: %lu\n", *item_notify->item.mask);
+		pr_info("item prio: %lu\n", item_notify->item.priority);
+		pr_info("item from table *: %p\n", item_notify->table);
+
+		while ((vector_item =
+			prune_vector_item_get_next(item_notify, vector_item))) {
+			pr_info("i = [%d], table priv: %lu\t is prune: %s\t pruned table *: %p\n",
+				i,
+				(uintptr_t)vector_item->priv,
+				vector_item->is_pruned ? "Yes" : "No",
+				vector_item->table);
+			i++;
+		}
+	}
+}
+
+static bool test_prune_check_case(struct prune_item_notify *item_notify,
+				  unsigned int prio,
+				  bool *prune_val_arr,
+				  int arr_len,
+				  int test_case)
+{
+	struct prune_vector_item *vector_item = NULL;
+	bool case_passed;
+	int table_id;
+
+	if (item_notify->item.priority != prio) {
+		WARN(1, "test case [%d] didn't pass. Incorrect priority [%lu]",
+		     test_case,
+		     item_notify->item.priority);
+		return false;
+	}
+	table_id = 0;
+
+	while ((vector_item =
+		prune_vector_item_get_next(item_notify, vector_item))) {
+		if (table_id == arr_len)
+			return false;
+
+		switch (table_id) {
+		case 0:
+			case_passed =
+				(uintptr_t)vector_item->priv == table_id &&
+				vector_item->is_pruned == prune_val_arr[0];
+			break;
+		case 1:
+			case_passed =
+				(uintptr_t)vector_item->priv == table_id &&
+				vector_item->is_pruned == prune_val_arr[1];
+			break;
+		case 2:
+			case_passed =
+				(uintptr_t)vector_item->priv == table_id &&
+				vector_item->is_pruned == prune_val_arr[2];
+			break;
+		case 3:
+			case_passed =
+				(uintptr_t)vector_item->priv == table_id &&
+				vector_item->is_pruned == prune_val_arr[3];
+			break;
+		case 4:
+			case_passed =
+				(uintptr_t)vector_item->priv == table_id &&
+				vector_item->is_pruned == prune_val_arr[4];
+			break;
+		case 5:
+			case_passed =
+				(uintptr_t)vector_item->priv == table_id &&
+				vector_item->is_pruned == prune_val_arr[5];
+			break;
+		default:
+			case_passed = false;
+			break;
+		}
+		table_id++;
+
+		if (!case_passed) {
+			WARN(1, "test case [%d] didn't pass for table [%d]",
+			     test_case,
+			     table_id);
+			return case_passed;
+		}
+	}
+	return case_passed;
+}
+
+static bool
+test_prune_notify_return_check(bool notify,
+			       bool expected_notfiy,
+			       struct prune_item_notify *item_notify,
+			       unsigned int prio,
+			       bool *prune_val_arr,
+			       int arr_len)
+{
+	if (expected_notfiy) {
+		if (notify) {
+			test_prune_notification_print(item_notify, false);
+			return test_prune_check_case(item_notify, prio,
+						     prune_val_arr, arr_len, 0);
+		} else {
+			pr_err("Should have got notification while adding an item\n");
+			return false;
+		}
+	} else {
+		if (notify) {
+			test_prune_notification_print(item_notify, false);
+			pr_err("Prune vector should be default i.e notify is false\n");
+			return false;
+		} else {
+			return true;
+		}
+	}
+	return false;
+}
+
+static void
+test_prune_subsequent_notify(struct prune_item_notification *notification)
+{
+	bool prune_value_case0[2] = {true, false};
+	bool prune_value_case1[3] = {true, true, true};
+	struct prune_item_notify *item_notify = NULL;
+	static int test_case;
+
+	while ((item_notify =
+		prune_item_notify_get_next(notification, item_notify))) {
+		test_prune_notification_print(item_notify, false);
+
+		switch (test_case) {
+		case 0:
+			test_prune_check_case(item_notify, 1, prune_value_case0,
+					      ARRAY_SIZE(prune_value_case0),
+					     test_case);
+			break;
+		case 1:
+			test_prune_check_case(item_notify, 2, prune_value_case1,
+					      ARRAY_SIZE(prune_value_case1),
+					      test_case);
+			break;
+		default:
+			WARN(1, "unexpected test case %d", test_case);
+			break;
+		}
+		test_case++;
+	}
+}
+
+static void
+test_prune_flows_1_notify(struct prune_item_notification *notification)
+{
+	bool prune_value_arr_case0[2] = {false, true};
+	bool prune_value_arr_case1[2] = {true, true};
+	bool prune_value_arr_case2[2] = {true, true};
+	struct prune_item_notify *item_notify = NULL;
+	static int test_case;
+
+	while ((item_notify =
+		prune_item_notify_get_next(notification, item_notify))) {
+		test_prune_notification_print(item_notify, false);
+
+		switch (test_case) {
+		case 0:
+			test_prune_check_case(item_notify, 5,
+					      prune_value_arr_case0,
+					      ARRAY_SIZE(prune_value_arr_case0),
+					      test_case);
+			break;
+		case 1:
+			test_prune_check_case(item_notify, 7,
+					      prune_value_arr_case1,
+					      ARRAY_SIZE(prune_value_arr_case1),
+					      test_case);
+			break;
+		case 2:
+			test_prune_check_case(item_notify, 5,
+					      prune_value_arr_case2,
+					      ARRAY_SIZE(prune_value_arr_case2),
+					      test_case);
+			break;
+		default:
+			WARN(1, "unexpected test case %d", test_case);
+			break;
+		}
+		test_case++;
+	}
+}
+
+static void
+test_prune_flows_2_notify(struct prune_item_notification *notification)
+{
+	struct prune_item_notify *item_notify = NULL;
+
+	while ((item_notify =
+		prune_item_notify_get_next(notification, item_notify))) {
+		pr_err("The prune vector should be changed\n");
+		test_prune_notification_print(item_notify, false);
+	}
+}
+
+static void
+test_prune_basic_notify(struct prune_item_notification *notification)
+{
+	bool prune_value_arr_case0[3] = {true, false, true};
+	bool prune_value_arr_case1[3] = {true, false, false};
+	bool prune_value_arr_case2[3] = {true, true, false};
+	bool prune_value_arr_case3[3] = {true, false, true};
+	bool prune_value_arr_case4[3] = {true, true, true};
+	struct prune_item_notify *item_notify = NULL;
+	static int test_case;
+
+	while ((item_notify =
+		prune_item_notify_get_next(notification, item_notify))) {
+		test_prune_notification_print(item_notify, false);
+
+		switch (test_case) {
+		case 0:
+			test_prune_check_case(item_notify, 1,
+					      prune_value_arr_case0,
+					      ARRAY_SIZE(prune_value_arr_case0),
+					      test_case);
+			break;
+		case 1:
+			test_prune_check_case(item_notify, 1,
+					      prune_value_arr_case1,
+					      ARRAY_SIZE(prune_value_arr_case1),
+					      test_case);
+			break;
+		case 2:
+			test_prune_check_case(item_notify, 2,
+					      prune_value_arr_case2,
+					      ARRAY_SIZE(prune_value_arr_case2),
+					      test_case);
+			break;
+		case 3:
+			test_prune_check_case(item_notify, 1,
+					      prune_value_arr_case3,
+					      ARRAY_SIZE(prune_value_arr_case3),
+					      test_case);
+			break;
+		case 4:
+			test_prune_check_case(item_notify, 2,
+					      prune_value_arr_case4,
+					      ARRAY_SIZE(prune_value_arr_case4),
+					      test_case);
+			break;
+		default:
+			WARN(1, "unexpected test case %d", test_case);
+			break;
+		}
+		test_case++;
+	}
+}
+
+static const struct prune_ops test_prune_add_new_table_subsequent_ops = {
+	.prune_item_notify = test_prune_subsequent_notify
+};
+
+static const struct prune_ops test_prune_flows_add_delete_items_1_ops = {
+	.prune_item_notify = test_prune_flows_1_notify
+};
+
+static const struct prune_ops test_prune_flows_add_delete_items_2_ops = {
+	.prune_item_notify = test_prune_flows_2_notify
+};
+
+static const struct prune_ops test_prune_basic_ops = {
+	.prune_item_notify = test_prune_basic_notify
+};
+
+static int table_attr_create(struct prune_table_attr *table_attr,
+			     unsigned int mask_len,
+			     unsigned int table_cnt)
+{
+	unsigned long i, j;
+
+	for (i = 0; i < table_cnt; i++) {
+		table_attr[i].mask = kcalloc(1, prune_mask_size(mask_len),
+					     GFP_KERNEL);
+		if (!table_attr[i].mask) {
+			if (i > 0) {
+				for (j = i - 1; j > -1; j--)
+					kfree(table_attr[j].mask);
+			}
+			return -ENOMEM;
+		}
+		table_attr[i].mask_len = mask_len;
+		table_attr[i].priv = (int *)i;
+	}
+	return 0;
+}
+
+static void table_attr_destroy(struct prune_table_attr *table_attr,
+			       unsigned int table_cnt)
+{
+	int i;
+
+	for (i = 0; i < table_cnt; i++)
+		kfree(table_attr[i].mask);
+}
+
+static void prune_item_arr_destroy(struct prune_item *pi_arr,
+				   unsigned int pi_cnt)
+{
+	int i;
+
+	for (i = 0; i < pi_cnt; i++)
+		kfree(pi_arr[i].mask);
+}
+
+static int prune_item_arr_create(struct prune_item *pi_arr, unsigned int pi_cnt,
+				 unsigned int pi_nbits)
+{
+	int i, j = 0;
+
+	for (i = 0; i < pi_cnt; i++) {
+		pi_arr[i].mask = kcalloc(1, prune_mask_size(pi_nbits),
+					 GFP_KERNEL);
+		if (!pi_arr[i].mask) {
+			if (i > 0)
+				for (j = i - 1; j > -1; j--)
+					kfree(pi_arr[j].mask);
+			return -ENOMEM;
+		}
+		pi_arr[i].mask_len = pi_nbits;
+		bitmap_clear(pi_arr[i].mask, 0, pi_arr[i].mask_len);
+	}
+	return 0;
+}
+
+/*
+ * Basic test: test add / remove table and item without checking their prune
+ * vector
+ */
+static int test_prune_basic(void)
+{
+	unsigned int item_cnt = 3;
+	unsigned int tbl_cnt = 3;
+	struct prune_table_attr table_attr[tbl_cnt];
+	struct prune_table *table_arr[tbl_cnt];
+	struct prune_item pi_arr[item_cnt];
+	struct prune_item_notify item_notify;
+	unsigned int nbits = 40;
+	struct prune *prune;
+	bool notify;
+	int err;
+
+	prune = prune_create(nbits, &test_prune_basic_ops, NULL);
+	if (IS_ERR(prune))
+		return PTR_ERR(prune);
+
+	err = table_attr_create(table_attr, nbits, tbl_cnt);
+	if (err)
+		return err;
+
+	err = prune_item_arr_create(pi_arr, item_cnt, nbits);
+	if (err)
+		return err;
+
+	bitmap_clear(table_attr[0].mask, 0, table_attr[0].mask_len);
+	bitmap_set(table_attr[0].mask, 0, 2);
+	table_arr[0] = prune_table_create(prune, &table_attr[0]);
+	if (IS_ERR(table_arr[0]))
+		return PTR_ERR(table_arr[0]);
+
+	bitmap_clear(table_attr[1].mask, 0, table_attr[1].mask_len);
+	bitmap_set(table_attr[1].mask, 1, 2);
+	table_arr[1]  = prune_table_create(prune, &table_attr[1]);
+	if (IS_ERR(table_arr[1]))
+		return PTR_ERR(table_arr[1]);
+
+	bitmap_clear(table_attr[2].mask, 0, table_attr[2].mask_len);
+	bitmap_set(table_attr[2].mask, 2, 2);
+	table_arr[2] = prune_table_create(prune, &table_attr[2]);
+	if (IS_ERR(table_arr[2]))
+		return PTR_ERR(table_arr[2]);
+
+	pi_arr[0].priority = 1;
+	pi_arr[0].priv = NULL;
+	bitmap_set(pi_arr[0].mask, 0, 2);
+	err = prune_item_add(prune, table_arr[0], &pi_arr[0], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	pi_arr[1].priority = 2;
+	pi_arr[1].priv = NULL;
+	bitmap_set(pi_arr[1].mask, 1, 2);
+	err = prune_item_add(prune, table_arr[1], &pi_arr[1], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0)) {
+		return -EBADE;
+	}
+
+	pi_arr[2].priority = 3;
+	pi_arr[2].priv = NULL;
+	bitmap_set(pi_arr[2].mask, 2, 2);
+	/* Negative test */
+	err = prune_item_remove(prune, table_arr[2], &pi_arr[2]);
+	if (err != -EPERM)
+		return err;
+
+	err = prune_item_add(prune, table_arr[2], &pi_arr[2], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	err = prune_item_remove(prune, table_arr[2], &pi_arr[2]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[0], &pi_arr[0]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[1], &pi_arr[1]);
+	if (err)
+		return err;
+
+	prune_table_destroy(prune, table_arr[0]);
+
+	prune_table_destroy(prune, table_arr[1]);
+
+	prune_table_destroy(prune, table_arr[2]);
+
+	prune_destroy(prune);
+
+	prune_item_arr_destroy(pi_arr, item_cnt);
+	table_attr_destroy(table_attr, tbl_cnt);
+
+	return 0;
+}
+
+static int test_prune_flows_add_delete_items_1(void)
+{
+	unsigned int item_cnt = 3;
+	unsigned int tbl_cnt = 2;
+	struct prune_table_attr table_attr[tbl_cnt];
+	struct prune_table *table_arr[tbl_cnt];
+	struct prune_item pi_arr[item_cnt];
+	struct prune_item_notify item_notify;
+	unsigned int nbits = 40;
+	bool prune_val_arr_2[2] =  {true, false};
+	struct prune *prune;
+	bool notify;
+	int err;
+
+	prune = prune_create(nbits, &test_prune_flows_add_delete_items_1_ops,
+			     NULL);
+
+	if (IS_ERR(prune))
+		return PTR_ERR(prune);
+
+	err = table_attr_create(table_attr, nbits, tbl_cnt);
+	if (err)
+		return err;
+
+	err = prune_item_arr_create(pi_arr, item_cnt, nbits);
+	if (err)
+		return err;
+
+	/* Create table with mask u.u.u.x.x - table 0*/
+	bitmap_clear(table_attr[0].mask, 0, table_attr[0].mask_len);
+	bitmap_set(table_attr[0].mask, 0, 24); /* Set care on #1-3 byte*/
+	table_arr[0] = prune_table_create(prune, &table_attr[0]);
+	if (IS_ERR(table_arr[0]))
+		return PTR_ERR(table_arr[0]);
+
+	/* Create table with mask x.u.u.u.x */
+	bitmap_clear(table_attr[1].mask, 0, table_attr[1].mask_len);
+	bitmap_set(table_attr[1].mask, 8, 24); /* Set care on #2-4 byte*/
+	table_arr[1] = prune_table_create(prune, &table_attr[1]);
+	if (IS_ERR(table_arr[0]))
+		return PTR_ERR(table_arr[0]);
+
+	/* Create item0 x.2.3.4.x prio 10 */
+	pi_arr[0].priority = 10;
+	pi_arr[0].priv = NULL;
+	bitmap_set(pi_arr[0].mask, 14, 1); /* Set 2 on #2 byte*/
+	bitmap_set(pi_arr[0].mask, 22, 2); /* Set 3 on #3 byte*/
+	bitmap_set(pi_arr[0].mask, 29, 1); /* Set 4 on #4 byte*/
+
+	/* Create item1 x.2.3.5.x prio 5*/
+	pi_arr[1].priority = 5;
+	pi_arr[1].priv = NULL;
+	bitmap_set(pi_arr[1].mask, 14, 1); /* Set 2 on #2 byte*/
+	bitmap_set(pi_arr[1].mask, 22, 2); /* Set 3 on #3 byte*/
+	bitmap_set(pi_arr[1].mask, 29, 1); /* Set 5 on #4 byte*/
+	bitmap_set(pi_arr[1].mask, 31, 1); /* Set 5 on #4 byte*/
+
+	/* Add item0: x.2.3.4.x to table 1 */
+	err = prune_item_add(prune, table_arr[1], &pi_arr[0], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	/* Expected prune vector for item1 and item2
+	 * table 0 prune
+	 * table 1 prune
+	 * --> Shoudn't not get notification
+	 */
+
+	/* Add item1: x.2.3.5.x to table 1 */
+	err = prune_item_add(prune, table_arr[1], &pi_arr[1], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	/* Expected prune vector for both item
+	 * table 0 prune
+	 * table 1 prune
+	 * --> Shoudn't not get notification
+	 */
+
+	/* Create item2 1.2.3.x.x prio 7 */
+	pi_arr[2].priority = 7;
+	pi_arr[2].priv = NULL;
+	bitmap_set(pi_arr[2].mask, 7, 1); /* Set 1 on #1 byte*/
+	bitmap_set(pi_arr[2].mask, 14, 1); /* Set 2 on #2 byte*/
+	bitmap_set(pi_arr[2].mask, 22, 2); /* Set 3 on #3 byte*/
+
+	/* Add item2: 1.2.3.x.x prio 7 to table 0 */
+	err = prune_item_add(prune, table_arr[0], &pi_arr[2], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, true, &item_notify,
+					    pi_arr[2].priority, prune_val_arr_2,
+					    2))
+		return -EBADE;
+
+	/* Expected prune vector per item
+	 * item0 in table 1 (x.2.3.4.x prio 10):
+	 *	table 0 prune (no change)
+	 *	table 1 prune (no change)
+	 * --> shoudn't not get notification
+	 * item1 in table 1 (x.2.3.5.x prio 5):
+	 *	table 0 don't prune (changed)
+	 *	table 1 prune (changed)
+	 * --> Should get notification
+	 * item2 in table 0 (1.2.3.x.x prio 7):
+	 *	table 0 prune (no change)
+	 *	table 1 don't prune (no change)
+	 * --> Should get notification
+	 */
+
+	/* Delete item: 0: (x.2.3.4.x prio 10 from table 1 */
+	err = prune_item_remove(prune, table_arr[1], &pi_arr[0]);
+	if (err)
+		return err;
+
+	/* Expected prune vector
+	 * item0: item is deleted
+	 * item1 table 1 (x.2.3.5.x prio 5):
+	 *	table 0 don't prune
+	 *	table 1 prune
+	 * item2 table 0 (1.2.3.x.x prio 7):
+	 *	table 0 prune
+	 *	table 1 prune (changed)
+	 */
+
+	/* Delete item: 2: (1.2.3.x.x prio 7) from table 0 */
+	err = prune_item_remove(prune, table_arr[0], &pi_arr[2]);
+	if (err)
+		return err;
+
+	/* Expected prune vector
+	 * item0: item is deleted
+	 * item1 table 1 (x.2.3.5.x prio 5):
+	 *	table 0 prune (changed)
+	 *	table 1 prune
+	 * item2 item is deleted
+	 *
+	 */
+
+	/* Delete item: 1: (x.2.3.5.x prio 5) from table 1 */
+	err = prune_item_remove(prune, table_arr[1], &pi_arr[1]);
+	if (err)
+		return err;
+
+	err = prune_table_destroy(prune, table_arr[0]);
+	if (err)
+		return err;
+
+	err = prune_table_destroy(prune, table_arr[1]);
+	if (err)
+		return err;
+
+	prune_destroy(prune);
+
+	prune_item_arr_destroy(pi_arr, item_cnt);
+	table_attr_destroy(table_attr, tbl_cnt);
+
+	return 0;
+}
+
+static int test_prune_flows_add_delete_item_2(void)
+{
+	unsigned int item_cnt = 6;
+	unsigned int tbl_cnt = 3;
+	bool prune_val_arr_3[3] = {false, true, true};
+	bool prune_val_arr_4[3] = {true, true, false};
+	struct prune_table_attr table_attr[tbl_cnt];
+	struct prune_table *table_arr[tbl_cnt];
+	struct prune_item pi_arr[item_cnt];
+	struct prune_item_notify item_notify;
+	unsigned int nbits = 40;
+	struct prune *prune;
+	bool notify;
+	int err;
+
+	prune = prune_create(nbits, &test_prune_flows_add_delete_items_2_ops,
+			     NULL);
+	if (IS_ERR(prune))
+		return PTR_ERR(prune);
+
+	err = table_attr_create(table_attr, nbits, tbl_cnt);
+	if (err)
+		return err;
+
+	err = prune_item_arr_create(pi_arr, item_cnt, nbits);
+	if (err)
+		return err;
+
+	/* Tables:
+	 *	table 0: x.x.u.u.u
+	 *	table 1: u.u.u.x.x
+	 *	table 2: x.u.x.x.x
+	 */
+
+	/* Create table with mask x.x.u.u.u - table 0 */
+	bitmap_clear(table_attr[0].mask, 0, table_attr[0].mask_len);
+	bitmap_set(table_attr[0].mask, 16, 24); /* Set care on #3-5 byte*/
+
+	/* Create table with mask u.u.u.x.x - table 1 */
+	bitmap_clear(table_attr[1].mask, 0, table_attr[1].mask_len);
+	bitmap_set(table_attr[1].mask, 0, 24); /* Set care on #1-3 byte*/
+
+	/* Create table with mask x.u.x.x.x - table 2 */
+	bitmap_clear(table_attr[2].mask, 2, table_attr[2].mask_len);
+	bitmap_set(table_attr[2].mask, 8, 8); /* Set care on #2 byte*/
+
+	/* Create rules */
+	/* Create item0 x.x.3.5.5 prio 5 */
+	pi_arr[0].priority = 5;
+	pi_arr[0].priv = NULL;
+	bitmap_set(pi_arr[0].mask, 22, 2); /* Set 3 on #3 byte */
+	bitmap_set(pi_arr[0].mask, 29, 1); /* Set 5 on #4 byte */
+	bitmap_set(pi_arr[0].mask, 31, 1); /* Set 5 on #4 byte */
+	bitmap_set(pi_arr[0].mask, 37, 1); /* Set 5 on #5 byte */
+	bitmap_set(pi_arr[0].mask, 39, 1); /* Set 5 on #5 byte */
+
+	/* Create item1 1.2.3.x.x  prio 5 */
+	pi_arr[1].priority = 5;
+	pi_arr[1].priv = NULL;
+	bitmap_set(pi_arr[1].mask, 7, 1); /* Set 1 on #1 byte */
+	bitmap_set(pi_arr[1].mask, 14, 1); /* Set 2 on #2 byte */
+	bitmap_set(pi_arr[1].mask, 22, 2); /* Set 3 on #3 byte */
+
+	/* Create item2 x.x.3.5.x prio 10 */
+	pi_arr[2].priority = 10;
+	pi_arr[2].priv = NULL;
+	bitmap_set(pi_arr[2].mask, 23, 1); /* Set 3 on #3 byte */
+	bitmap_set(pi_arr[2].mask, 29, 1); /* Set 5 on #4 byte */
+	bitmap_set(pi_arr[2].mask, 31, 1); /* Set 5 on #4 byte */
+
+	/* Create item3 x.2.x.x.x prio 3 */
+	pi_arr[3].priority = 3;
+	pi_arr[3].priv = NULL;
+	bitmap_set(pi_arr[3].mask, 14, 2); /* Set 2 on #2 byte */
+
+	/* Create item4 x.x.x.x.6 prio 5 */
+	pi_arr[4].priority = 5;
+	pi_arr[4].priv = NULL;
+	bitmap_set(pi_arr[4].mask, 37, 2); /* Set 6 on #5 byte */
+
+	/* Create item5 2.2.3.x.x prio 7 */
+	pi_arr[5].priority = 7;
+	pi_arr[5].priv = NULL;
+	bitmap_set(pi_arr[5].mask, 7, 1); /* Set 2 on #1 byte */
+	bitmap_set(pi_arr[5].mask, 14, 1); /* Set 2 on #2 byte */
+	bitmap_set(pi_arr[5].mask, 22, 2); /* Set 3 on #3 byte */
+
+	table_arr[0] = prune_table_create(prune, &table_attr[0]);
+	if (IS_ERR(table_arr[0]))
+		return PTR_ERR(table_arr[0]);
+
+	err = prune_item_add(prune, table_arr[0], &pi_arr[0], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	table_arr[1] = prune_table_create(prune, &table_attr[1]);
+	if (IS_ERR(table_arr[1]))
+		return PTR_ERR(table_arr[1]);
+
+	err = prune_item_add(prune, table_arr[1], &pi_arr[1], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	err = prune_item_add(prune, table_arr[0], &pi_arr[2], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	table_arr[2] = prune_table_create(prune, &table_attr[2]);
+	if (IS_ERR(table_arr[2]))
+		return PTR_ERR(table_arr[2]);
+
+	err = prune_item_add(prune, table_arr[2], &pi_arr[3], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, true, &item_notify,
+					    pi_arr[3].priority, prune_val_arr_3,
+					    3))
+		return -EBADE;
+
+	err = prune_item_add(prune, table_arr[0], &pi_arr[4], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, true, &item_notify,
+					    pi_arr[4].priority, prune_val_arr_4,
+					    3))
+		return -EBADE;
+
+	err = prune_item_add(prune, table_arr[1], &pi_arr[5], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	/* Free resources */
+	err = prune_item_remove(prune, table_arr[0], &pi_arr[0]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[1], &pi_arr[1]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[0], &pi_arr[2]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[2], &pi_arr[3]);
+	if (err)
+		return err;
+
+	err = prune_table_destroy(prune, table_arr[2]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[0], &pi_arr[4]);
+	if (err)
+		return err;
+
+	err = prune_table_destroy(prune, table_arr[0]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[1], &pi_arr[5]);
+	if (err)
+		return err;
+
+	err = prune_table_destroy(prune, table_arr[1]);
+	if (err)
+		return err;
+
+	prune_destroy(prune);
+
+	prune_item_arr_destroy(pi_arr, item_cnt);
+	table_attr_destroy(table_attr, tbl_cnt);
+
+	return 0;
+}
+
+static int test_prune_add_new_table_with_item_subsequent_to_curr_tables(void)
+{
+	unsigned int item_cnt = 8;
+	unsigned int tbl_cnt = 4;
+	struct prune_table_attr table_attr[tbl_cnt];
+	struct prune_table *table_arr[tbl_cnt];
+	struct prune_item pi_arr[item_cnt];
+	bool prune_val_arr_5[2] = {false, true};
+	struct prune_item_notify item_notify;
+	unsigned int nbits = 40;
+	struct prune *prune;
+	bool notify;
+	int err;
+
+	prune = prune_create(nbits, &test_prune_add_new_table_subsequent_ops,
+			     NULL);
+	if (IS_ERR(prune))
+		return PTR_ERR(prune);
+
+	err = table_attr_create(table_attr, nbits, tbl_cnt);
+	if (err)
+		return err;
+
+	err = prune_item_arr_create(pi_arr, item_cnt, nbits);
+	if (err)
+		return err;
+
+	/* Tables:
+	 *      table 0: u.u.u.x.x
+	 *      table 1: x.u.u.u.x
+	 *      table 2: x.x.u.u.u
+	 */
+
+	/* Create table with mask u.u.u.x.x - table 0 */
+	bitmap_clear(table_attr[0].mask, 0, table_attr[0].mask_len);
+	bitmap_set(table_attr[0].mask, 0, 24); /* Set care on #1-3 byte*/
+	table_arr[0] = prune_table_create(prune, &table_attr[0]);
+	if (IS_ERR(table_arr[0]))
+		return PTR_ERR(table_arr[0]);
+
+	/* Create table with mask x.u.u.u.x table 1 */
+	bitmap_clear(table_attr[1].mask, 0, table_attr[1].mask_len);
+	bitmap_set(table_attr[1].mask, 8, 24); /* Set care on #2-4 byte*/
+	table_arr[1] = prune_table_create(prune, &table_attr[1]);
+	if (IS_ERR(table_arr[1]))
+		return PTR_ERR(table_arr[1]);
+
+	/* Create rules */
+	/* Create item1 1.2.3.x.x prio 1 */
+	pi_arr[0].priority = 1;
+	pi_arr[0].priv = NULL;
+	bitmap_set(pi_arr[0].mask, 7, 1); /* Set 1 on #1 byte */
+	bitmap_set(pi_arr[0].mask, 14, 1); /* Set 2 on #2 byte */
+	bitmap_set(pi_arr[0].mask, 22, 2); /* Set 3 on #3 byte */
+
+	/* Create item2 x.2.3.4.x prio 5 */
+	pi_arr[1].priority = 5;
+	pi_arr[1].priv = NULL;
+	bitmap_set(pi_arr[1].mask, 14, 1); /* Set 2 on #2 byte */
+	bitmap_set(pi_arr[1].mask, 22, 2); /* Set 3 on #3 byte */
+	bitmap_set(pi_arr[1].mask, 29, 1); /* Set 4 on #4 byte */
+
+	/* Create item3 x.x.3.9.9 prio 10 */
+	pi_arr[2].priority = 10;
+	pi_arr[2].priv = NULL;
+	bitmap_set(pi_arr[2].mask, 22, 2); /* Set 3 on #3 byte */
+	bitmap_set(pi_arr[2].mask, 28, 1); /* Set 9 on #4 byte */
+	bitmap_set(pi_arr[2].mask, 31, 1); /* Set 9 on #4 byte */
+	bitmap_set(pi_arr[2].mask, 36, 1); /* Set 9 on #5 byte */
+	bitmap_set(pi_arr[2].mask, 39, 1); /* Set 9 on #5 byte */
+
+	/* Create item4 2.2.4.x.x prio 3 */
+	pi_arr[3].priority = 3;
+	pi_arr[3].priv = NULL;
+	bitmap_set(pi_arr[3].mask, 6, 1); /* Set 2 on #1 byte */
+	bitmap_set(pi_arr[3].mask, 14, 1); /* Set 2 on #2 byte */
+	bitmap_set(pi_arr[3].mask, 21, 1); /* Set 4 on #3 byte */
+
+	/* Create item5 x.x.4.5.x prio 8 */
+	pi_arr[4].priority = 8;
+	pi_arr[4].priv = NULL;
+	bitmap_set(pi_arr[4].mask, 21, 1); /* Set 4 on #3 byte */
+	bitmap_set(pi_arr[4].mask, 29, 1); /* Set 5 on #4 byte */
+	bitmap_set(pi_arr[4].mask, 31, 1); /* Set 5 on #4 byte */
+
+	/* Create item6 x.2.4.7.x prio 2 */
+	pi_arr[5].priority = 2;
+	pi_arr[5].priv = NULL;
+	bitmap_set(pi_arr[5].mask, 14, 1); /* Set 2 on #2 byte */
+	bitmap_set(pi_arr[5].mask, 21, 1); /* Set 4 on #3 byte */
+	bitmap_set(pi_arr[5].mask, 29, 3); /* Set 7 on #4 byte */
+
+	/* Create item7 x.2.x.9.x prio 2 */
+	pi_arr[6].priority = 2;
+	pi_arr[6].priv = NULL;
+	bitmap_set(pi_arr[6].mask, 14, 1); /* Set 2 on #2 byte */
+	bitmap_set(pi_arr[6].mask, 28, 1); /* Set 9 on #4 byte */
+	bitmap_set(pi_arr[6].mask, 31, 1); /* Set 9 on #4 byte */
+
+	err = prune_item_add(prune, table_arr[0], &pi_arr[0], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	err = prune_item_add(prune, table_arr[1], &pi_arr[1], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	err = prune_item_add(prune, table_arr[0], &pi_arr[3], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	err = prune_item_add(prune, table_arr[1], &pi_arr[5], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, true, &item_notify,
+					    pi_arr[5].priority, prune_val_arr_5,
+					    2))
+		return -EBADE;
+
+	err = prune_item_add(prune, table_arr[1], &pi_arr[6], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	/* Create table with mask x.x.u.u.u table 2 */
+	bitmap_clear(table_attr[2].mask, 0, table_attr[2].mask_len);
+	bitmap_set(table_attr[2].mask, 16, 24); /* Set care on #2-5 byte*/
+	table_arr[2] = prune_table_create(prune, &table_attr[2]);
+	if (IS_ERR(table_arr[2]))
+		return PTR_ERR(table_arr[2]);
+
+	err = prune_item_add(prune, table_arr[2], &pi_arr[2], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	err = prune_item_add(prune, table_arr[2], &pi_arr[4], &item_notify,
+			     &notify);
+	if (err)
+		return err;
+
+	if (!test_prune_notify_return_check(notify, false, &item_notify, 0,
+					    NULL, 0))
+		return -EBADE;
+
+	err = prune_item_remove(prune, table_arr[0], &pi_arr[0]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[1], &pi_arr[1]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[2], &pi_arr[2]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[0], &pi_arr[3]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[2], &pi_arr[4]);
+	if (err)
+		return err;
+
+	err = prune_table_destroy(prune, table_arr[2]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[1], &pi_arr[5]);
+	if (err)
+		return err;
+
+	err = prune_item_remove(prune, table_arr[1], &pi_arr[6]);
+	if (err)
+		return err;
+
+	err = prune_table_destroy(prune, table_arr[0]);
+	if (err)
+		return err;
+
+	err = prune_table_destroy(prune, table_arr[1]);
+	if (err)
+		return err;
+
+	prune_destroy(prune);
+
+	prune_item_arr_destroy(pi_arr, item_cnt);
+	table_attr_destroy(table_attr, tbl_cnt);
+
+	return 0;
+}
+
+static int test_prune(void)
+{
+	int err;
+
+	err = test_prune_basic();
+	if (err)
+		return err;
+
+	err = test_prune_flows_add_delete_items_1();
+	if (err)
+		return err;
+
+	err = test_prune_add_new_table_with_item_subsequent_to_curr_tables();
+	if (err)
+		return err;
+
+	err = test_prune_flows_add_delete_item_2();
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int __init test_prune_init(void)
+{
+		return test_prune();
+}
+
+static void __exit test_prune_exit(void)
+{
+}
+
+module_init(test_prune_init);
+module_exit(test_prune_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Tal Bar <talb@mellanox.com>");
+MODULE_DESCRIPTION("Test module for prune");
