- * [dpdk-dev] [PATCH v4 1/5] table: added bulk add/delete functions for table
  2015-10-20 13:01 [dpdk-dev] [PATCH v4 0/5] pipeline: add bulk add/delete functions for table Michal Jastrzebski
@ 2015-10-20 13:01 ` Michal Jastrzebski
  2015-10-20 13:01 ` [dpdk-dev] [PATCH v4 2/5] pipeline: " Michal Jastrzebski
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Michal Jastrzebski @ 2015-10-20 13:01 UTC (permalink / raw)
  To: dev
From: Marcin Kerlin <marcinx.kerlin@intel.com>
New functions prototypes for bulk add/delete added to table API. New
functions allows adding/deleting multiple records with single function
call. For now those functions are implemented only for ACL table. For
other tables these function pointers are set to NULL.
Signed-off-by: Maciej Gajdzica <maciejx.t.gajdzica@intel.com>
Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 lib/librte_table/rte_table.h            |  85 ++++++++-
 lib/librte_table/rte_table_acl.c        | 309 ++++++++++++++++++++++++++++++++
 lib/librte_table/rte_table_array.c      |   2 +
 lib/librte_table/rte_table_hash_ext.c   |   4 +
 lib/librte_table/rte_table_hash_key16.c |   4 +
 lib/librte_table/rte_table_hash_key32.c |   4 +
 lib/librte_table/rte_table_hash_key8.c  |   8 +
 lib/librte_table/rte_table_hash_lru.c   |   4 +
 lib/librte_table/rte_table_lpm.c        |   2 +
 lib/librte_table/rte_table_lpm_ipv6.c   |   2 +
 lib/librte_table/rte_table_stub.c       |   2 +
 11 files changed, 420 insertions(+), 6 deletions(-)
diff --git a/lib/librte_table/rte_table.h b/lib/librte_table/rte_table.h
index c13d40d..720514e 100644
--- a/lib/librte_table/rte_table.h
+++ b/lib/librte_table/rte_table.h
@@ -154,6 +154,77 @@ typedef int (*rte_table_op_entry_delete)(
 	void *entry);
 
 /**
+ * Lookup table entry add bulk
+ *
+ * @param table
+ *   Handle to lookup table instance
+ * @param key
+ *   Array containing lookup keys
+ * @param entries
+ *   Array containing data to be associated with each key. Every item in the
+ *   array has to point to a valid memory buffer where the first entry_size
+ *   bytes (table create parameter) are populated with the data.
+ * @param n_keys
+ *   Number of keys to add
+ * @param key_found
+ *   After successful invocation, key_found for every item in the array is set
+ *   to a value different than 0 if the current key is already present in the
+ *   table and to 0 if not. This pointer has to be set to a valid memory
+ *   location before the table entry add function is called.
+ * @param entries_ptr
+ *   After successful invocation, array *entries_ptr stores the handle to the
+ *   table entry containing the data associated with every key. This handle can
+ *   be used to perform further read-write accesses to this entry. This handle
+ *   is valid until the key is deleted from the table or the same key is
+ *   re-added to the table, typically to associate it with different data. This
+ *   pointer has to be set to a valid memory location before the function is
+ *   called.
+ * @return
+ *   0 on success, error code otherwise
+ */
+typedef int (*rte_table_op_entry_add_bulk)(
+	void *table,
+	void **keys,
+	void **entries,
+	uint32_t n_keys,
+	int *key_found,
+	void **entries_ptr);
+
+/**
+ * Lookup table entry delete bulk
+ *
+ * @param table
+ *   Handle to lookup table instance
+ * @param key
+ *   Array containing lookup keys
+ * @param n_keys
+ *   Number of keys to delete
+ * @param key_found
+ *   After successful invocation, key_found for every item in the array is set
+ *   to a value different than 0if the current key was present in the table
+ *   before the delete operation was performed and to 0 if not. This pointer
+ *   has to be set to a valid memory location before the table entry delete
+ *   function is called.
+ * @param entries
+ *   If entries pointer is NULL, this pointer is ignored for every entry found.
+ *   Else, after successful invocation, if specific key is found in the table
+ *   (key_found is different than 0 for this item after function call is
+ *   completed) and item of entry array points to a valid buffer (entry is set
+ *   to a value different than NULL before the function is called), then the
+ *   first entry_size bytes (table create parameter) in *entry store a copy of
+ *   table entry that contained the data associated with the current key before
+ *   the key was deleted.
+ * @return
+ *   0 on success, error code otherwise
+ */
+typedef int (*rte_table_op_entry_delete_bulk)(
+	void *table,
+	void **keys,
+	uint32_t n_keys,
+	int *key_found,
+	void **entries);
+
+/**
  * Lookup table lookup
  *
  * @param table
@@ -213,12 +284,14 @@ typedef int (*rte_table_op_stats_read)(
 
 /** Lookup table interface defining the lookup table operation */
 struct rte_table_ops {
-	rte_table_op_create f_create;       /**< Create */
-	rte_table_op_free f_free;           /**< Free */
-	rte_table_op_entry_add f_add;       /**< Entry add */
-	rte_table_op_entry_delete f_delete; /**< Entry delete */
-	rte_table_op_lookup f_lookup;       /**< Lookup */
-	rte_table_op_stats_read f_stats;	/**< Stats */
+	rte_table_op_create f_create;                 /**< Create */
+	rte_table_op_free f_free;                     /**< Free */
+	rte_table_op_entry_add f_add;                 /**< Entry add */
+	rte_table_op_entry_delete f_delete;           /**< Entry delete */
+	rte_table_op_entry_add_bulk f_add_bulk;       /**< Add entry bulk */
+	rte_table_op_entry_delete_bulk f_delete_bulk; /**< Delete entry bulk */
+	rte_table_op_lookup f_lookup;                 /**< Lookup */
+	rte_table_op_stats_read f_stats;              /**< Stats */
 };
 
 #ifdef __cplusplus
diff --git a/lib/librte_table/rte_table_acl.c b/lib/librte_table/rte_table_acl.c
index f02de3e..c1eb848 100644
--- a/lib/librte_table/rte_table_acl.c
+++ b/lib/librte_table/rte_table_acl.c
@@ -444,6 +444,313 @@ rte_table_acl_entry_delete(
 }
 
 static int
+rte_table_acl_entry_add_bulk(
+	void *table,
+	void **keys,
+	void **entries,
+	uint32_t n_keys,
+	int *key_found,
+	void **entries_ptr)
+{
+	struct rte_table_acl *acl = (struct rte_table_acl *) table;
+	struct rte_acl_ctx *ctx;
+	uint32_t rule_pos[n_keys];
+	uint32_t i;
+	int err = 0, build = 0;
+	int status;
+
+	/* Check input parameters */
+	if (table == NULL) {
+		RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (keys == NULL) {
+		RTE_LOG(ERR, TABLE, "%s: keys parameter is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (entries == NULL) {
+		RTE_LOG(ERR, TABLE, "%s: entries parameter is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (n_keys == 0) {
+		RTE_LOG(ERR, TABLE, "%s: 0 rules to add\n", __func__);
+		return -EINVAL;
+	}
+	if (key_found == NULL) {
+		RTE_LOG(ERR, TABLE, "%s: key_found parameter is NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+	if (entries_ptr == NULL) {
+		RTE_LOG(ERR, TABLE, "%s: entries_ptr parameter is NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	/* Check input parameters in arrays */
+	for (i = 0; i < n_keys; i++) {
+		struct rte_table_acl_rule_add_params *rule;
+
+		if (keys[i] == NULL) {
+			RTE_LOG(ERR, TABLE, "%s: keys[%" PRIu32 "] parameter is NULL\n",
+					__func__, i);
+			return -EINVAL;
+		}
+
+		if (entries[i] == NULL) {
+			RTE_LOG(ERR, TABLE, "%s: entries[%" PRIu32 "] parameter is NULL\n",
+					__func__, i);
+			return -EINVAL;
+		}
+
+		if (entries_ptr[i] == NULL) {
+			RTE_LOG(ERR, TABLE, "%s: entries_ptr[%" PRIu32 "] parameter is NULL\n",
+					__func__, i);
+			return -EINVAL;
+		}
+
+		rule = (struct rte_table_acl_rule_add_params *) keys[i];
+		if (rule->priority > RTE_ACL_MAX_PRIORITY) {
+			RTE_LOG(ERR, TABLE, "%s: Priority is too high\n", __func__);
+			return -EINVAL;
+		}
+	}
+
+	memset(rule_pos, 0, n_keys * sizeof(uint32_t));
+	memset(key_found, 0, n_keys * sizeof(int));
+	for (i = 0; i < n_keys; i++) {
+		struct rte_table_acl_rule_add_params *rule =
+				(struct rte_table_acl_rule_add_params *) keys[i];
+		struct rte_pipeline_acl_rule acl_rule;
+		struct rte_acl_rule *rule_location;
+		uint32_t free_pos, free_pos_valid, j;
+
+		/* Setup rule data structure */
+		memset(&acl_rule, 0, sizeof(acl_rule));
+		acl_rule.data.category_mask = 1;
+		acl_rule.data.priority = RTE_ACL_MAX_PRIORITY - rule->priority;
+		acl_rule.data.userdata = 0; /* To be set up later */
+		memcpy(&acl_rule.field[0],
+			&rule->field_value[0],
+			acl->cfg.num_fields * sizeof(struct rte_acl_field));
+
+		/* Look to see if the rule exists already in the table */
+		free_pos = 0;
+		free_pos_valid = 0;
+		for (j = 1; j < acl->n_rules; j++) {
+			if (acl->acl_rule_list[j] == NULL) {
+				if (free_pos_valid == 0) {
+					free_pos = j;
+					free_pos_valid = 1;
+				}
+
+				continue;
+			}
+
+			/* Compare the key fields */
+			status = memcmp(&acl->acl_rule_list[j]->field[0],
+				&rule->field_value[0],
+				acl->cfg.num_fields * sizeof(struct rte_acl_field));
+
+			/* Rule found: update data associated with the rule */
+			if (status == 0) {
+				key_found[i] = 1;
+				entries_ptr[i] = &acl->memory[j * acl->entry_size];
+				memcpy(entries_ptr[i], entries[i], acl->entry_size);
+
+				break;
+			}
+		}
+
+		/* Key already in the table */
+		if (key_found[i] != 0)
+			continue;
+
+		/* Maximum number of rules reached */
+		if (free_pos_valid == 0) {
+			err = 1;
+			break;
+		}
+
+		/* Add the new rule to the rule set */
+		acl_rule.data.userdata = free_pos;
+		rule_location = (struct rte_acl_rule *)
+			&acl->acl_rule_memory[free_pos * acl->acl_params.rule_size];
+		memcpy(rule_location, &acl_rule, acl->acl_params.rule_size);
+		acl->acl_rule_list[free_pos] = rule_location;
+		rule_pos[i] = free_pos;
+		build = 1;
+	}
+
+	if (err != 0) {
+		for (i = 0; i < n_keys; i++) {
+			if (rule_pos[i] == 0)
+				continue;
+
+			acl->acl_rule_list[rule_pos[i]] = NULL;
+		}
+
+		return -ENOSPC;
+	}
+
+	if (build == 0)
+		return 0;
+
+	/* Build low level ACL table */
+	acl->name_id ^= 1;
+	acl->acl_params.name = acl->name[acl->name_id];
+	status = rte_table_acl_build(acl, &ctx);
+	if (status != 0) {
+		/* Roll back changes */
+		for (i = 0; i < n_keys; i++) {
+			if (rule_pos[i] == 0)
+				continue;
+
+			acl->acl_rule_list[rule_pos[i]] = NULL;
+		}
+		acl->name_id ^= 1;
+
+		return -EINVAL;
+	}
+
+	/* Commit changes */
+	if (acl->ctx != NULL)
+		rte_acl_free(acl->ctx);
+	acl->ctx = ctx;
+
+	for (i = 0; i < n_keys; i++) {
+		if (rule_pos[i] == 0)
+			continue;
+
+		key_found[i] = 0;
+		entries_ptr[i] = &acl->memory[rule_pos[i] * acl->entry_size];
+		memcpy(entries_ptr[i], entries[i], acl->entry_size);
+	}
+
+	return 0;
+}
+
+static int
+rte_table_acl_entry_delete_bulk(
+	void *table,
+	void **keys,
+	uint32_t n_keys,
+	int *key_found,
+	void **entries)
+{
+	struct rte_table_acl *acl = (struct rte_table_acl *) table;
+	struct rte_acl_rule *deleted_rules[n_keys];
+	uint32_t rule_pos[n_keys];
+	struct rte_acl_ctx *ctx;
+	uint32_t i;
+	int status;
+	int build = 0;
+
+	/* Check input parameters */
+	if (table == NULL) {
+		RTE_LOG(ERR, TABLE, "%s: table parameter is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (keys == NULL) {
+		RTE_LOG(ERR, TABLE, "%s: key parameter is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (n_keys == 0) {
+		RTE_LOG(ERR, TABLE, "%s: 0 rules to delete\n", __func__);
+		return -EINVAL;
+	}
+	if (key_found == NULL) {
+		RTE_LOG(ERR, TABLE, "%s: key_found parameter is NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < n_keys; i++) {
+		if (keys[i] == NULL) {
+			RTE_LOG(ERR, TABLE, "%s: keys[%" PRIu32 "] parameter is NULL\n",
+					__func__, i);
+			return -EINVAL;
+		}
+	}
+
+	memset(deleted_rules, 0, n_keys * sizeof(struct rte_acl_rule *));
+	memset(rule_pos, 0, n_keys * sizeof(uint32_t));
+	for (i = 0; i < n_keys; i++) {
+		struct rte_table_acl_rule_delete_params *rule =
+			(struct rte_table_acl_rule_delete_params *) keys[i];
+		uint32_t pos_valid, j;
+
+		/* Look for the rule in the table */
+		pos_valid = 0;
+		for (j = 1; j < acl->n_rules; j++) {
+			if (acl->acl_rule_list[j] == NULL)
+				continue;
+
+			/* Compare the key fields */
+			status = memcmp(&acl->acl_rule_list[j]->field[0],
+					&rule->field_value[0],
+					acl->cfg.num_fields * sizeof(struct rte_acl_field));
+
+			/* Rule found: remove from table */
+			if (status == 0) {
+				pos_valid = 1;
+
+				deleted_rules[i] = acl->acl_rule_list[j];
+				acl->acl_rule_list[j] = NULL;
+				rule_pos[i] = j;
+
+				build = 1;
+			}
+		}
+
+		if (pos_valid == 0) {
+			key_found[i] = 0;
+			continue;
+		}
+	}
+
+	/* Return if no changes to acl table */
+	if (build == 0) {
+		return 0;
+	}
+
+	/* Build low level ACL table */
+	acl->name_id ^= 1;
+	acl->acl_params.name = acl->name[acl->name_id];
+	status = rte_table_acl_build(acl, &ctx);
+	if (status != 0) {
+		/* Roll back changes */
+		for (i = 0; i < n_keys; i++) {
+			if (rule_pos[i] == 0)
+				continue;
+
+			acl->acl_rule_list[rule_pos[i]] = deleted_rules[i];
+		}
+
+		acl->name_id ^= 1;
+
+		return -EINVAL;
+	}
+
+	/* Commit changes */
+	if (acl->ctx != NULL)
+		rte_acl_free(acl->ctx);
+
+	acl->ctx = ctx;
+	for (i = 0; i < n_keys; i++) {
+		if (rule_pos[i] == 0)
+			continue;
+
+		key_found[i] = 1;
+		if (entries != NULL && entries[i] != NULL)
+			memcpy(entries[i], &acl->memory[rule_pos[i] * acl->entry_size],
+					acl->entry_size);
+	}
+
+	return 0;
+}
+
+static int
 rte_table_acl_lookup(
 	void *table,
 	struct rte_mbuf **pkts,
@@ -521,6 +828,8 @@ struct rte_table_ops rte_table_acl_ops = {
 	.f_free = rte_table_acl_free,
 	.f_add = rte_table_acl_entry_add,
 	.f_delete = rte_table_acl_entry_delete,
+	.f_add_bulk = rte_table_acl_entry_add_bulk,
+	.f_delete_bulk = rte_table_acl_entry_delete_bulk,
 	.f_lookup = rte_table_acl_lookup,
 	.f_stats = rte_table_acl_stats_read,
 };
diff --git a/lib/librte_table/rte_table_array.c b/lib/librte_table/rte_table_array.c
index 422f8c3..3bb68d1 100644
--- a/lib/librte_table/rte_table_array.c
+++ b/lib/librte_table/rte_table_array.c
@@ -230,6 +230,8 @@ struct rte_table_ops rte_table_array_ops = {
 	.f_free = rte_table_array_free,
 	.f_add = rte_table_array_entry_add,
 	.f_delete = NULL,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_array_lookup,
 	.f_stats = rte_table_array_stats_read,
 };
diff --git a/lib/librte_table/rte_table_hash_ext.c b/lib/librte_table/rte_table_hash_ext.c
index 1fa15c8..ebe952b 100644
--- a/lib/librte_table/rte_table_hash_ext.c
+++ b/lib/librte_table/rte_table_hash_ext.c
@@ -1139,6 +1139,8 @@ struct rte_table_ops rte_table_hash_ext_ops	 = {
 	.f_free = rte_table_hash_ext_free,
 	.f_add = rte_table_hash_ext_entry_add,
 	.f_delete = rte_table_hash_ext_entry_delete,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_ext_lookup,
 	.f_stats = rte_table_hash_ext_stats_read,
 };
@@ -1148,6 +1150,8 @@ struct rte_table_ops rte_table_hash_ext_dosig_ops  = {
 	.f_free = rte_table_hash_ext_free,
 	.f_add = rte_table_hash_ext_entry_add,
 	.f_delete = rte_table_hash_ext_entry_delete,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_ext_lookup_dosig,
 	.f_stats = rte_table_hash_ext_stats_read,
 };
diff --git a/lib/librte_table/rte_table_hash_key16.c b/lib/librte_table/rte_table_hash_key16.c
index f6a3306..35b7f10 100644
--- a/lib/librte_table/rte_table_hash_key16.c
+++ b/lib/librte_table/rte_table_hash_key16.c
@@ -1103,6 +1103,8 @@ struct rte_table_ops rte_table_hash_key16_lru_ops = {
 	.f_free = rte_table_hash_free_key16_lru,
 	.f_add = rte_table_hash_entry_add_key16_lru,
 	.f_delete = rte_table_hash_entry_delete_key16_lru,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_lookup_key16_lru,
 	.f_stats = rte_table_hash_key16_stats_read,
 };
@@ -1112,6 +1114,8 @@ struct rte_table_ops rte_table_hash_key16_ext_ops = {
 	.f_free = rte_table_hash_free_key16_ext,
 	.f_add = rte_table_hash_entry_add_key16_ext,
 	.f_delete = rte_table_hash_entry_delete_key16_ext,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_lookup_key16_ext,
 	.f_stats = rte_table_hash_key16_stats_read,
 };
diff --git a/lib/librte_table/rte_table_hash_key32.c b/lib/librte_table/rte_table_hash_key32.c
index 5fe4161..b3ae73e 100644
--- a/lib/librte_table/rte_table_hash_key32.c
+++ b/lib/librte_table/rte_table_hash_key32.c
@@ -1123,6 +1123,8 @@ struct rte_table_ops rte_table_hash_key32_lru_ops = {
 	.f_free = rte_table_hash_free_key32_lru,
 	.f_add = rte_table_hash_entry_add_key32_lru,
 	.f_delete = rte_table_hash_entry_delete_key32_lru,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_lookup_key32_lru,
 	.f_stats = rte_table_hash_key32_stats_read,
 };
@@ -1132,6 +1134,8 @@ struct rte_table_ops rte_table_hash_key32_ext_ops = {
 	.f_free = rte_table_hash_free_key32_ext,
 	.f_add = rte_table_hash_entry_add_key32_ext,
 	.f_delete = rte_table_hash_entry_delete_key32_ext,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_lookup_key32_ext,
 	.f_stats = rte_table_hash_key32_stats_read,
 };
diff --git a/lib/librte_table/rte_table_hash_key8.c b/lib/librte_table/rte_table_hash_key8.c
index b351a49..379748e 100644
--- a/lib/librte_table/rte_table_hash_key8.c
+++ b/lib/librte_table/rte_table_hash_key8.c
@@ -1394,6 +1394,8 @@ struct rte_table_ops rte_table_hash_key8_lru_ops = {
 	.f_free = rte_table_hash_free_key8_lru,
 	.f_add = rte_table_hash_entry_add_key8_lru,
 	.f_delete = rte_table_hash_entry_delete_key8_lru,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_lookup_key8_lru,
 	.f_stats = rte_table_hash_key8_stats_read,
 };
@@ -1403,6 +1405,8 @@ struct rte_table_ops rte_table_hash_key8_lru_dosig_ops = {
 	.f_free = rte_table_hash_free_key8_lru,
 	.f_add = rte_table_hash_entry_add_key8_lru,
 	.f_delete = rte_table_hash_entry_delete_key8_lru,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_lookup_key8_lru_dosig,
 	.f_stats = rte_table_hash_key8_stats_read,
 };
@@ -1412,6 +1416,8 @@ struct rte_table_ops rte_table_hash_key8_ext_ops = {
 	.f_free = rte_table_hash_free_key8_ext,
 	.f_add = rte_table_hash_entry_add_key8_ext,
 	.f_delete = rte_table_hash_entry_delete_key8_ext,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_lookup_key8_ext,
 	.f_stats = rte_table_hash_key8_stats_read,
 };
@@ -1421,6 +1427,8 @@ struct rte_table_ops rte_table_hash_key8_ext_dosig_ops = {
 	.f_free = rte_table_hash_free_key8_ext,
 	.f_add = rte_table_hash_entry_add_key8_ext,
 	.f_delete = rte_table_hash_entry_delete_key8_ext,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_lookup_key8_ext_dosig,
 	.f_stats = rte_table_hash_key8_stats_read,
 };
diff --git a/lib/librte_table/rte_table_hash_lru.c b/lib/librte_table/rte_table_hash_lru.c
index 1640dc9..14e9072 100644
--- a/lib/librte_table/rte_table_hash_lru.c
+++ b/lib/librte_table/rte_table_hash_lru.c
@@ -1082,6 +1082,8 @@ struct rte_table_ops rte_table_hash_lru_ops = {
 	.f_free = rte_table_hash_lru_free,
 	.f_add = rte_table_hash_lru_entry_add,
 	.f_delete = rte_table_hash_lru_entry_delete,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_lru_lookup,
 	.f_stats = rte_table_hash_lru_stats_read,
 };
@@ -1091,6 +1093,8 @@ struct rte_table_ops rte_table_hash_lru_dosig_ops = {
 	.f_free = rte_table_hash_lru_free,
 	.f_add = rte_table_hash_lru_entry_add,
 	.f_delete = rte_table_hash_lru_entry_delete,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_hash_lru_lookup_dosig,
 	.f_stats = rte_table_hash_lru_stats_read,
 };
diff --git a/lib/librte_table/rte_table_lpm.c b/lib/librte_table/rte_table_lpm.c
index 849d899..673f401 100644
--- a/lib/librte_table/rte_table_lpm.c
+++ b/lib/librte_table/rte_table_lpm.c
@@ -377,6 +377,8 @@ struct rte_table_ops rte_table_lpm_ops = {
 	.f_free = rte_table_lpm_free,
 	.f_add = rte_table_lpm_entry_add,
 	.f_delete = rte_table_lpm_entry_delete,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_lpm_lookup,
 	.f_stats = rte_table_lpm_stats_read,
 };
diff --git a/lib/librte_table/rte_table_lpm_ipv6.c b/lib/librte_table/rte_table_lpm_ipv6.c
index e9bc6a7..836f4cf 100644
--- a/lib/librte_table/rte_table_lpm_ipv6.c
+++ b/lib/librte_table/rte_table_lpm_ipv6.c
@@ -391,6 +391,8 @@ struct rte_table_ops rte_table_lpm_ipv6_ops = {
 	.f_free = rte_table_lpm_ipv6_free,
 	.f_add = rte_table_lpm_ipv6_entry_add,
 	.f_delete = rte_table_lpm_ipv6_entry_delete,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_lpm_ipv6_lookup,
 	.f_stats = rte_table_lpm_ipv6_stats_read,
 };
diff --git a/lib/librte_table/rte_table_stub.c b/lib/librte_table/rte_table_stub.c
index c1065ef..691d681 100644
--- a/lib/librte_table/rte_table_stub.c
+++ b/lib/librte_table/rte_table_stub.c
@@ -114,6 +114,8 @@ struct rte_table_ops rte_table_stub_ops = {
 	.f_free = NULL,
 	.f_add = NULL,
 	.f_delete = NULL,
+	.f_add_bulk = NULL,
+	.f_delete_bulk = NULL,
 	.f_lookup = rte_table_stub_lookup,
 	.f_stats = rte_table_stub_stats_read,
 };
-- 
1.9.1
^ permalink raw reply	[flat|nested] 7+ messages in thread
- * [dpdk-dev] [PATCH v4 2/5] pipeline: added bulk add/delete functions for table
  2015-10-20 13:01 [dpdk-dev] [PATCH v4 0/5] pipeline: add bulk add/delete functions for table Michal Jastrzebski
  2015-10-20 13:01 ` [dpdk-dev] [PATCH v4 1/5] table: added " Michal Jastrzebski
@ 2015-10-20 13:01 ` Michal Jastrzebski
  2015-10-20 13:01 ` [dpdk-dev] [PATCH v4 3/5] test_table: added check for bulk add/delete to acl table unit test Michal Jastrzebski
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Michal Jastrzebski @ 2015-10-20 13:01 UTC (permalink / raw)
  To: dev
From: Marcin Kerlin <marcinx.kerlin@intel.com>
Added functions for adding/deleting multiple records to table owned by
pipeline. The LIBABIVER number is incremented for table and pipeline
libraries.
Signed-off-by: Maciej Gajdzica <maciejx.t.gajdzica@intel.com>
Signed-off-by: Marcin Kerlin <marcinx.kerlin@intel.com>
Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 lib/librte_pipeline/Makefile                 |   2 +-
 lib/librte_pipeline/rte_pipeline.c           | 106 +++++++++++++++++++++++++++
 lib/librte_pipeline/rte_pipeline.h           |  64 ++++++++++++++++
 lib/librte_pipeline/rte_pipeline_version.map |   9 +++
 4 files changed, 180 insertions(+), 1 deletion(-)
diff --git a/lib/librte_pipeline/Makefile b/lib/librte_pipeline/Makefile
index 15e406b..1166d3c 100644
--- a/lib/librte_pipeline/Makefile
+++ b/lib/librte_pipeline/Makefile
@@ -41,7 +41,7 @@ CFLAGS += $(WERROR_FLAGS)
 
 EXPORT_MAP := rte_pipeline_version.map
 
-LIBABIVER := 1
+LIBABIVER := 2
 
 #
 # all source are stored in SRCS-y
diff --git a/lib/librte_pipeline/rte_pipeline.c b/lib/librte_pipeline/rte_pipeline.c
index bd700d2..56022f4 100644
--- a/lib/librte_pipeline/rte_pipeline.c
+++ b/lib/librte_pipeline/rte_pipeline.c
@@ -587,6 +587,112 @@ rte_pipeline_table_entry_delete(struct rte_pipeline *p,
 	return (table->ops.f_delete)(table->h_table, key, key_found, entry);
 }
 
+int rte_pipeline_table_entry_add_bulk(struct rte_pipeline *p,
+	uint32_t table_id,
+	void **keys,
+	struct rte_pipeline_table_entry **entries,
+	uint32_t n_keys,
+	int *key_found,
+	struct rte_pipeline_table_entry **entries_ptr)
+{
+	struct rte_table *table;
+	uint32_t i;
+
+	/* Check input arguments */
+	if (p == NULL) {
+		RTE_LOG(ERR, PIPELINE, "%s: pipeline parameter is NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (keys == NULL) {
+		RTE_LOG(ERR, PIPELINE, "%s: keys parameter is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (entries == NULL) {
+		RTE_LOG(ERR, PIPELINE, "%s: entries parameter is NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (table_id >= p->num_tables) {
+		RTE_LOG(ERR, PIPELINE,
+			"%s: table_id %d out of range\n", __func__, table_id);
+		return -EINVAL;
+	}
+
+	table = &p->tables[table_id];
+
+	if (table->ops.f_add_bulk == NULL) {
+		RTE_LOG(ERR, PIPELINE, "%s: f_add_bulk function pointer NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < n_keys; i++) {
+		if ((entries[i]->action == RTE_PIPELINE_ACTION_TABLE) &&
+			table->table_next_id_valid &&
+			(entries[i]->table_id != table->table_next_id)) {
+			RTE_LOG(ERR, PIPELINE,
+				"%s: Tree-like topologies not allowed\n", __func__);
+			return -EINVAL;
+		}
+	}
+
+	/* Add entry */
+	for (i = 0; i < n_keys; i++) {
+		if ((entries[i]->action == RTE_PIPELINE_ACTION_TABLE) &&
+			(table->table_next_id_valid == 0)) {
+			table->table_next_id = entries[i]->table_id;
+			table->table_next_id_valid = 1;
+		}
+	}
+
+	return (table->ops.f_add_bulk)(table->h_table, keys, (void **) entries,
+		n_keys, key_found, (void **) entries_ptr);
+}
+
+int rte_pipeline_table_entry_delete_bulk(struct rte_pipeline *p,
+	uint32_t table_id,
+	void **keys,
+	uint32_t n_keys,
+	int *key_found,
+	struct rte_pipeline_table_entry **entries)
+{
+	struct rte_table *table;
+
+	/* Check input arguments */
+	if (p == NULL) {
+		RTE_LOG(ERR, PIPELINE, "%s: pipeline parameter NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (keys == NULL) {
+		RTE_LOG(ERR, PIPELINE, "%s: key parameter is NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (table_id >= p->num_tables) {
+		RTE_LOG(ERR, PIPELINE,
+			"%s: table_id %d out of range\n", __func__, table_id);
+		return -EINVAL;
+	}
+
+	table = &p->tables[table_id];
+
+	if (table->ops.f_delete_bulk == NULL) {
+		RTE_LOG(ERR, PIPELINE,
+			"%s: f_delete function pointer NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	return (table->ops.f_delete_bulk)(table->h_table, keys, n_keys, key_found,
+			(void **) entries);
+}
+
 /*
  * Port
  *
diff --git a/lib/librte_pipeline/rte_pipeline.h b/lib/librte_pipeline/rte_pipeline.h
index 59e0710..5459324 100644
--- a/lib/librte_pipeline/rte_pipeline.h
+++ b/lib/librte_pipeline/rte_pipeline.h
@@ -466,6 +466,70 @@ int rte_pipeline_table_entry_delete(struct rte_pipeline *p,
 	struct rte_pipeline_table_entry *entry);
 
 /**
+ * Pipeline table entry add bulk
+ *
+ * @param p
+ *   Handle to pipeline instance
+ * @param table_id
+ *   Table ID (returned by previous invocation of pipeline table create)
+ * @param keys
+ *   Array containing table entry keys
+ * @param entries
+ *   Array containung new contents for every table entry identified by key
+ * @param n_keys
+ *   Number of keys to add
+ * @param key_found
+ *   On successful invocation, key_found for every item in the array is set to
+ *   TRUE (value different than 0) if key was already present in the table
+ *   before the add operation and to FALSE (value 0) if not
+ * @param entries_ptr
+ *   On successful invocation, array *entries_ptr stores pointer to every table
+ *   entry associated with key. This can be used for further read-write accesses
+ *   to this table entry and is valid until the key is deleted from the table or
+ *   re-added (usually for associating different actions and/or action meta-data
+ *   to the current key)
+ * @return
+ *   0 on success, error code otherwise
+ */
+int rte_pipeline_table_entry_add_bulk(struct rte_pipeline *p,
+	uint32_t table_id,
+	void **keys,
+	struct rte_pipeline_table_entry **entries,
+	uint32_t n_keys,
+	int *key_found,
+	struct rte_pipeline_table_entry **entries_ptr);
+
+/**
+ * Pipeline table entry delete bulk
+ *
+ * @param p
+ *   Handle to pipeline instance
+ * @param table_id
+ *   Table ID (returned by previous invocation of pipeline table create)
+ * @param keys
+ *   Array containing table entry keys
+ * @param n_keys
+ *   Number of keys to delete
+ * @param key_found
+ *   On successful invocation, key_found for every item in the array is set to
+ *   TRUE (value different than 0) if key was found in the table before the
+ *   delete operation and to FALSE (value 0) if not
+ * @param entries
+ *   If entries pointer is NULL, this pointer is ignored for every entry found.
+ *   Else, after successful invocation, if specific key is found in the table
+ *   and entry points to a valid buffer, the table entry contents (as it was
+ *   before the delete was performed) is copied to this buffer.
+ * @return
+ *   0 on success, error code otherwise
+ */
+int rte_pipeline_table_entry_delete_bulk(struct rte_pipeline *p,
+	uint32_t table_id,
+	void **keys,
+	uint32_t n_keys,
+	int *key_found,
+	struct rte_pipeline_table_entry **entries);
+
+/**
  * Read pipeline table stats.
  *
  * This function reads table statistics identified by *table_id* of given
diff --git a/lib/librte_pipeline/rte_pipeline_version.map b/lib/librte_pipeline/rte_pipeline_version.map
index 8f25d0f..1ff953e 100644
--- a/lib/librte_pipeline/rte_pipeline_version.map
+++ b/lib/librte_pipeline/rte_pipeline_version.map
@@ -29,3 +29,12 @@ DPDK_2.1 {
 	rte_pipeline_table_stats_read;
 
 } DPDK_2.0;
+
+
+DPDK_2.2 {
+	global:
+
+	rte_pipeline_table_entry_add_bulk;
+	rte_pipeline_table_entry_delete_bulk;
+
+} DPDK_2.1;
\ No newline at end of file
-- 
1.9.1
^ permalink raw reply	[flat|nested] 7+ messages in thread
- * [dpdk-dev] [PATCH v4 3/5] test_table: added check for bulk add/delete to acl table unit test
  2015-10-20 13:01 [dpdk-dev] [PATCH v4 0/5] pipeline: add bulk add/delete functions for table Michal Jastrzebski
  2015-10-20 13:01 ` [dpdk-dev] [PATCH v4 1/5] table: added " Michal Jastrzebski
  2015-10-20 13:01 ` [dpdk-dev] [PATCH v4 2/5] pipeline: " Michal Jastrzebski
@ 2015-10-20 13:01 ` Michal Jastrzebski
  2015-10-20 13:01 ` [dpdk-dev] [PATCH v4 4/5] ip_pipline: added cli commands for bulk add/delete to firewall pipeline Michal Jastrzebski
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Michal Jastrzebski @ 2015-10-20 13:01 UTC (permalink / raw)
  To: dev
From: Marcin Kerlin <marcinx.kerlin@intel.com>
Added to acl table unit test check for bulk add and bulk delete.
Signed-off-by: Maciej Gajdzica <maciejx.t.gajdzica@intel.com>
Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 app/test/test_table_acl.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 166 insertions(+)
diff --git a/app/test/test_table_acl.c b/app/test/test_table_acl.c
index e4e9b9c..fe8e545 100644
--- a/app/test/test_table_acl.c
+++ b/app/test/test_table_acl.c
@@ -253,6 +253,94 @@ parse_cb_ipv4_rule(char *str, struct rte_table_acl_rule_add_params *v)
 	return 0;
 }
 
+static int
+parse_cb_ipv4_rule_del(char *str, struct rte_table_acl_rule_delete_params *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_NUM];
+	static const char *dlm = " \t\n";
+
+	/*
+	** Skip leading '@'
+	*/
+	if (strchr(str, '@') != str)
+		return -EINVAL;
+
+	s = str + 1;
+
+	/*
+	* Populate the 'in' array with the location of each
+	* field in the string we're parsing
+	*/
+	for (i = 0; i != DIM(in); i++) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+		s = NULL;
+	}
+
+	/* Parse x.x.x.x/x */
+	rc = parse_ipv4_net(in[CB_FLD_SRC_ADDR],
+		&v->field_value[SRC_FIELD_IPV4].value.u32,
+		&v->field_value[SRC_FIELD_IPV4].mask_range.u32);
+	if (rc != 0) {
+		RTE_LOG(ERR, PIPELINE, "failed to read src address/mask: %s\n",
+			in[CB_FLD_SRC_ADDR]);
+		return rc;
+	}
+
+	printf("V=%u, mask=%u\n", v->field_value[SRC_FIELD_IPV4].value.u32,
+		v->field_value[SRC_FIELD_IPV4].mask_range.u32);
+
+	/* Parse x.x.x.x/x */
+	rc = parse_ipv4_net(in[CB_FLD_DST_ADDR],
+		&v->field_value[DST_FIELD_IPV4].value.u32,
+		&v->field_value[DST_FIELD_IPV4].mask_range.u32);
+	if (rc != 0) {
+		RTE_LOG(ERR, PIPELINE, "failed to read dest address/mask: %s\n",
+			in[CB_FLD_DST_ADDR]);
+		return rc;
+	}
+
+	printf("V=%u, mask=%u\n", v->field_value[DST_FIELD_IPV4].value.u32,
+	v->field_value[DST_FIELD_IPV4].mask_range.u32);
+	/* Parse n:n */
+	rc = parse_port_range(in[CB_FLD_SRC_PORT_RANGE],
+		&v->field_value[SRCP_FIELD_IPV4].value.u16,
+		&v->field_value[SRCP_FIELD_IPV4].mask_range.u16);
+	if (rc != 0) {
+		RTE_LOG(ERR, PIPELINE, "failed to read source port range: %s\n",
+			in[CB_FLD_SRC_PORT_RANGE]);
+		return rc;
+	}
+
+	printf("V=%u, mask=%u\n", v->field_value[SRCP_FIELD_IPV4].value.u16,
+		v->field_value[SRCP_FIELD_IPV4].mask_range.u16);
+	/* Parse n:n */
+	rc = parse_port_range(in[CB_FLD_DST_PORT_RANGE],
+		&v->field_value[DSTP_FIELD_IPV4].value.u16,
+		&v->field_value[DSTP_FIELD_IPV4].mask_range.u16);
+	if (rc != 0) {
+		RTE_LOG(ERR, PIPELINE, "failed to read dest port range: %s\n",
+			in[CB_FLD_DST_PORT_RANGE]);
+		return rc;
+	}
+
+	printf("V=%u, mask=%u\n", v->field_value[DSTP_FIELD_IPV4].value.u16,
+		v->field_value[DSTP_FIELD_IPV4].mask_range.u16);
+	/* parse 0/0xnn */
+	GET_CB_FIELD(in[CB_FLD_PROTO],
+		v->field_value[PROTO_FIELD_IPV4].value.u8,
+		0, UINT8_MAX, '/');
+	GET_CB_FIELD(in[CB_FLD_PROTO],
+		v->field_value[PROTO_FIELD_IPV4].mask_range.u8,
+		0, UINT8_MAX, 0);
+
+	printf("V=%u, mask=%u\n",
+		(unsigned int)v->field_value[PROTO_FIELD_IPV4].value.u8,
+		v->field_value[PROTO_FIELD_IPV4].mask_range.u8);
+	return 0;
+}
 
 /*
  * The format for these rules DO NOT need the port ranges to be
@@ -393,6 +481,84 @@ setup_acl_pipeline(void)
 		}
 	}
 
+	/* Add bulk entries to tables */
+	for (i = 0; i < N_PORTS; i++) {
+		struct rte_table_acl_rule_add_params keys[5];
+		struct rte_pipeline_table_entry entries[5];
+		struct rte_table_acl_rule_add_params *key_array[5];
+		struct rte_pipeline_table_entry *table_entries[5];
+		int key_found[5];
+		struct rte_pipeline_table_entry *table_entries_ptr[5];
+		struct rte_pipeline_table_entry entries_ptr[5];
+
+		parser = parse_cb_ipv4_rule;
+		for (n = 0; n < 5; n++) {
+			memset(&keys[n], 0, sizeof(struct rte_table_acl_rule_add_params));
+			key_array[n] = &keys[n];
+
+			snprintf(line, sizeof(line), "%s", lines[n]);
+			printf("PARSING [%s]\n", line);
+
+			ret = parser(line, &keys[n]);
+			if (ret != 0) {
+				RTE_LOG(ERR, PIPELINE,
+					"line %u: parse_cb_ipv4vlan_rule"
+					" failed, error code: %d (%s)\n",
+					n, ret, strerror(-ret));
+				return ret;
+			}
+
+			keys[n].priority = RTE_ACL_MAX_PRIORITY - n - 1;
+
+			entries[n].action = RTE_PIPELINE_ACTION_PORT;
+			entries[n].port_id = port_out_id[i^1];
+			table_entries[n] = &entries[n];
+			table_entries_ptr[n] = &entries_ptr[n];
+		}
+
+		ret = rte_pipeline_table_entry_add_bulk(p, table_id[i],
+				(void **)key_array, table_entries, 5, key_found, table_entries_ptr);
+		if (ret < 0) {
+			rte_panic("Add entry bulk to table %u failed (%d)\n",
+				table_id[i], ret);
+			goto fail;
+		}
+	}
+
+	/* Delete bulk entries from tables */
+	for (i = 0; i < N_PORTS; i++) {
+		struct rte_table_acl_rule_delete_params keys[5];
+		struct rte_table_acl_rule_delete_params *key_array[5];
+		struct rte_pipeline_table_entry *table_entries[5];
+		int key_found[5];
+
+		for (n = 0; n < 5; n++) {
+			memset(&keys[n], 0, sizeof(struct rte_table_acl_rule_delete_params));
+			key_array[n] = &keys[n];
+
+			snprintf(line, sizeof(line), "%s", lines[n]);
+			printf("PARSING [%s]\n", line);
+
+			ret = parse_cb_ipv4_rule_del(line, &keys[n]);
+			if (ret != 0) {
+				RTE_LOG(ERR, PIPELINE,
+					"line %u: parse_cb_ipv4vlan_rule"
+					" failed, error code: %d (%s)\n",
+					n, ret, strerror(-ret));
+				return ret;
+			}
+		}
+
+		ret = rte_pipeline_table_entry_delete_bulk(p, table_id[i],
+			(void **)key_array, 5, key_found, table_entries);
+		if (ret < 0) {
+			rte_panic("Delete bulk entries from table %u failed (%d)\n",
+				table_id[i], ret);
+			goto fail;
+		} else
+			printf("Bulk deleted rules.\n");
+	}
+
 	/* Add entries to tables */
 	for (i = 0; i < N_PORTS; i++) {
 		struct rte_pipeline_table_entry table_entry = {
-- 
1.9.1
^ permalink raw reply	[flat|nested] 7+ messages in thread
- * [dpdk-dev] [PATCH v4 4/5] ip_pipline: added cli commands for bulk add/delete to firewall pipeline
  2015-10-20 13:01 [dpdk-dev] [PATCH v4 0/5] pipeline: add bulk add/delete functions for table Michal Jastrzebski
                   ` (2 preceding siblings ...)
  2015-10-20 13:01 ` [dpdk-dev] [PATCH v4 3/5] test_table: added check for bulk add/delete to acl table unit test Michal Jastrzebski
@ 2015-10-20 13:01 ` Michal Jastrzebski
  2015-10-20 13:01 ` [dpdk-dev] [PATCH v4 5/5] doc: modify release notes and deprecation notice for table and pipeline Michal Jastrzebski
  2015-11-25 22:20 ` [dpdk-dev] [PATCH v4 0/5] pipeline: add bulk add/delete functions for table Thomas Monjalon
  5 siblings, 0 replies; 7+ messages in thread
From: Michal Jastrzebski @ 2015-10-20 13:01 UTC (permalink / raw)
  To: dev
From: Marcin Kerlin <marcinx.kerlin@intel.com>
Added two new cli commands to firewall pipeline. Commands bulk add and
bulk delete takes as argument a file with rules to add/delete. The file
is parsed, and then rules are passed to backend functions which
add/delete records from pipeline tables.
Signed-off-by: Maciej Gajdzica <maciejx.t.gajdzica@intel.com>
Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 examples/ip_pipeline/pipeline/pipeline_firewall.c  | 858 +++++++++++++++++++++
 examples/ip_pipeline/pipeline/pipeline_firewall.h  |  14 +
 .../ip_pipeline/pipeline/pipeline_firewall_be.c    | 157 ++++
 .../ip_pipeline/pipeline/pipeline_firewall_be.h    |  38 +
 4 files changed, 1067 insertions(+)
diff --git a/examples/ip_pipeline/pipeline/pipeline_firewall.c b/examples/ip_pipeline/pipeline/pipeline_firewall.c
index f6924ab..4137923 100644
--- a/examples/ip_pipeline/pipeline/pipeline_firewall.c
+++ b/examples/ip_pipeline/pipeline/pipeline_firewall.c
@@ -51,6 +51,8 @@
 #include "pipeline_common_fe.h"
 #include "pipeline_firewall.h"
 
+#define BUF_SIZE		1024
+
 struct app_pipeline_firewall_rule {
 	struct pipeline_firewall_key key;
 	int32_t priority;
@@ -73,6 +75,18 @@ struct app_pipeline_firewall {
 	void *default_rule_entry_ptr;
 };
 
+struct app_pipeline_add_bulk_params {
+	struct pipeline_firewall_key *keys;
+	uint32_t n_keys;
+	uint32_t *priorities;
+	uint32_t *port_ids;
+};
+
+struct app_pipeline_del_bulk_params {
+	struct pipeline_firewall_key *keys;
+	uint32_t n_keys;
+};
+
 static void
 print_firewall_ipv4_rule(struct app_pipeline_firewall_rule *rule)
 {
@@ -256,6 +270,358 @@ app_pipeline_firewall_key_check_and_normalize(struct pipeline_firewall_key *key)
 	}
 }
 
+static int
+app_pipeline_add_bulk_parse_file(char *filename,
+		struct app_pipeline_add_bulk_params *params)
+{
+	FILE *f;
+	char file_buf[BUF_SIZE];
+	uint32_t i;
+	int status = 0;
+
+	f = fopen(filename, "r");
+	if (f == NULL)
+		return -1;
+
+	params->n_keys = 0;
+	while (fgets(file_buf, BUF_SIZE, f) != NULL)
+		params->n_keys++;
+	rewind(f);
+
+	if (params->n_keys == 0) {
+		status = -1;
+		goto end;
+	}
+
+	params->keys = rte_malloc(NULL,
+			params->n_keys * sizeof(struct pipeline_firewall_key),
+			RTE_CACHE_LINE_SIZE);
+	if (params->keys == NULL) {
+		status = -1;
+		goto end;
+	}
+
+	params->priorities = rte_malloc(NULL,
+			params->n_keys * sizeof(uint32_t),
+			RTE_CACHE_LINE_SIZE);
+	if (params->priorities == NULL) {
+		status = -1;
+		goto end;
+	}
+
+	params->port_ids = rte_malloc(NULL,
+			params->n_keys * sizeof(uint32_t),
+			RTE_CACHE_LINE_SIZE);
+	if (params->port_ids == NULL) {
+		status = -1;
+		goto end;
+	}
+
+	i = 0;
+	while (fgets(file_buf, BUF_SIZE, f) != NULL) {
+		char *str;
+
+		str = strtok(file_buf, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->priorities[i] = atoi(str);
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_ip = atoi(str)<<24;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_ip |= atoi(str)<<16;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_ip |= atoi(str)<<8;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_ip |= atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_ip_mask = atoi(str);
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_ip = atoi(str)<<24;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_ip |= atoi(str)<<16;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_ip |= atoi(str)<<8;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_ip |= atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_ip_mask = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_port_from = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_port_to = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_port_from = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_port_to = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.proto = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		/* Need to add 2 to str to skip leading 0x */
+		params->keys[i].key.ipv4_5tuple.proto_mask = strtol(str+2, NULL, 16);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->port_ids[i] = atoi(str);
+		params->keys[i].type = PIPELINE_FIREWALL_IPV4_5TUPLE;
+
+		i++;
+	}
+
+end:
+	fclose(f);
+	return status;
+}
+
+static int
+app_pipeline_del_bulk_parse_file(char *filename,
+		struct app_pipeline_del_bulk_params *params)
+{
+	FILE *f;
+	char file_buf[BUF_SIZE];
+	uint32_t i;
+	int status = 0;
+
+	f = fopen(filename, "r");
+	if (f == NULL)
+		return -1;
+
+	params->n_keys = 0;
+	while (fgets(file_buf, BUF_SIZE, f) != NULL)
+		params->n_keys++;
+	rewind(f);
+
+	if (params->n_keys == 0) {
+		status = -1;
+		goto end;
+	}
+
+	params->keys = rte_malloc(NULL,
+			params->n_keys * sizeof(struct pipeline_firewall_key),
+			RTE_CACHE_LINE_SIZE);
+	if (params->keys == NULL) {
+		status = -1;
+		goto end;
+	}
+
+	i = 0;
+	while (fgets(file_buf, BUF_SIZE, f) != NULL) {
+		char *str;
+
+		str = strtok(file_buf, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_ip = atoi(str)<<24;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_ip |= atoi(str)<<16;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_ip |= atoi(str)<<8;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_ip |= atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_ip_mask = atoi(str);
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_ip = atoi(str)<<24;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_ip |= atoi(str)<<16;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_ip |= atoi(str)<<8;
+
+		str = strtok(NULL, " .");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_ip |= atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_ip_mask = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_port_from = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.src_port_to = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_port_from = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.dst_port_to = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		params->keys[i].key.ipv4_5tuple.proto = atoi(str);
+
+		str = strtok(NULL, " ");
+		if (str == NULL) {
+			status = -1;
+			goto end;
+		}
+		/* Need to add 2 to str to skip leading 0x */
+		params->keys[i].key.ipv4_5tuple.proto_mask = strtol(str+2, NULL, 16);
+
+		params->keys[i].type = PIPELINE_FIREWALL_IPV4_5TUPLE;
+
+		i++;
+	}
+
+	for (i = 0; i < params->n_keys; i++) {
+		if (app_pipeline_firewall_key_check_and_normalize(¶ms->keys[i]) != 0) {
+			status = -1;
+			goto end;
+		}
+	}
+
+end:
+	fclose(f);
+	return status;
+}
+
 int
 app_pipeline_firewall_add_rule(struct app_params *app,
 	uint32_t pipeline_id,
@@ -407,6 +773,332 @@ app_pipeline_firewall_delete_rule(struct app_params *app,
 }
 
 int
+app_pipeline_firewall_add_bulk(struct app_params *app,
+		uint32_t pipeline_id,
+		struct pipeline_firewall_key *keys,
+		uint32_t n_keys,
+		uint32_t *priorities,
+		uint32_t *port_ids)
+{
+	struct app_pipeline_firewall *p;
+	struct pipeline_firewall_add_bulk_msg_req *req;
+	struct pipeline_firewall_add_bulk_msg_rsp *rsp;
+
+	struct app_pipeline_firewall_rule **rules;
+	int *new_rules;
+
+	int *keys_found;
+	void **entries_ptr;
+
+	uint32_t i;
+	int status = 0;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	rules = rte_malloc(NULL,
+			n_keys * sizeof(struct app_pipeline_firewall_rule *),
+			RTE_CACHE_LINE_SIZE);
+	if (rules == NULL)
+		return -1;
+
+	new_rules = rte_malloc(NULL,
+			n_keys * sizeof(int),
+			RTE_CACHE_LINE_SIZE);
+	if (new_rules == NULL) {
+		rte_free(rules);
+		return -1;
+	}
+
+	/* check data integrity and add to rule list */
+	for (i = 0; i < n_keys; i++) {
+		if (port_ids[i]  >= p->n_ports_out) {
+			rte_free(rules);
+			rte_free(new_rules);
+			return -1;
+		}
+
+		if (app_pipeline_firewall_key_check_and_normalize(&keys[i]) != 0) {
+			rte_free(rules);
+			rte_free(new_rules);
+			return -1;
+		}
+
+		rules[i] = app_pipeline_firewall_rule_find(p, &keys[i]);
+		new_rules[i] = (rules[i] == NULL);
+		if (rules[i] == NULL) {
+			rules[i] = rte_malloc(NULL, sizeof(rules[i]),
+					RTE_CACHE_LINE_SIZE);
+
+			if (rules[i] == NULL) {
+				uint32_t j;
+
+				for (j = 0; j <= i; j++)
+					if (new_rules[j])
+						rte_free(rules[j]);
+
+				rte_free(rules);
+				rte_free(new_rules);
+				return -1;
+			}
+		}
+	}
+
+	keys_found = rte_malloc(NULL,
+			n_keys * sizeof(int),
+			RTE_CACHE_LINE_SIZE);
+	if (keys_found == NULL) {
+		uint32_t j;
+
+		for (j = 0; j < n_keys; j++)
+			if (new_rules[j])
+				rte_free(rules[j]);
+
+		rte_free(rules);
+		rte_free(new_rules);
+		return -1;
+	}
+
+	entries_ptr = rte_malloc(NULL,
+			n_keys * sizeof(struct rte_pipeline_table_entry *),
+			RTE_CACHE_LINE_SIZE);
+	if (entries_ptr == NULL) {
+		uint32_t j;
+
+		for (j = 0; j < n_keys; j++)
+			if (new_rules[j])
+				rte_free(rules[j]);
+
+		rte_free(rules);
+		rte_free(new_rules);
+		rte_free(keys_found);
+		return -1;
+	}
+	for (i = 0; i < n_keys; i++) {
+		entries_ptr[i] = rte_malloc(NULL,
+				sizeof(struct rte_pipeline_table_entry),
+				RTE_CACHE_LINE_SIZE);
+
+		if (entries_ptr[i] == NULL) {
+			uint32_t j;
+
+			for (j = 0; j < n_keys; j++)
+				if (new_rules[j])
+					rte_free(rules[j]);
+
+			for (j = 0; j <= i; j++)
+				rte_free(entries_ptr[j]);
+
+			rte_free(rules);
+			rte_free(new_rules);
+			rte_free(keys_found);
+			rte_free(entries_ptr);
+			return -1;
+		}
+	}
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL) {
+		uint32_t j;
+
+		for (j = 0; j < n_keys; j++)
+			if (new_rules[j])
+				rte_free(rules[j]);
+
+		for (j = 0; j < n_keys; j++)
+			rte_free(entries_ptr[j]);
+
+		rte_free(rules);
+		rte_free(new_rules);
+		rte_free(keys_found);
+		rte_free(entries_ptr);
+		return -1;
+	}
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FIREWALL_MSG_REQ_ADD_BULK;
+
+	req->keys = keys;
+	req->n_keys = n_keys;
+	req->port_ids = port_ids;
+	req->priorities = priorities;
+	req->keys_found = keys_found;
+	req->entries_ptr = entries_ptr;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL) {
+		uint32_t j;
+
+		for (j = 0; j < n_keys; j++)
+			if (new_rules[j])
+				rte_free(rules[j]);
+
+		for (j = 0; j < n_keys; j++)
+			rte_free(entries_ptr[j]);
+
+		rte_free(rules);
+		rte_free(new_rules);
+		rte_free(keys_found);
+		rte_free(entries_ptr);
+		return -1;
+	}
+
+	if (rsp->status) {
+		for (i = 0; i < n_keys; i++)
+			if (new_rules[i])
+				rte_free(rules[i]);
+
+		for (i = 0; i < n_keys; i++)
+			rte_free(entries_ptr[i]);
+
+		status = -1;
+		goto cleanup;
+	}
+
+	for (i = 0; i < n_keys; i++) {
+		if (entries_ptr[i] == NULL ||
+			((new_rules[i] == 0) && (keys_found[i] == 0)) ||
+			((new_rules[i] == 1) && (keys_found[i] == 1))) {
+			for (i = 0; i < n_keys; i++)
+				if (new_rules[i])
+					rte_free(rules[i]);
+
+			for (i = 0; i < n_keys; i++)
+				rte_free(entries_ptr[i]);
+
+			status = -1;
+			goto cleanup;
+		}
+	}
+
+	for (i = 0; i < n_keys; i++) {
+		memcpy(&rules[i]->key, &keys[i], sizeof(keys[i]));
+		rules[i]->priority = priorities[i];
+		rules[i]->port_id = port_ids[i];
+		rules[i]->entry_ptr = entries_ptr[i];
+
+		/* Commit rule */
+		if (new_rules[i]) {
+			TAILQ_INSERT_TAIL(&p->rules, rules[i], node);
+			p->n_rules++;
+		}
+
+		print_firewall_ipv4_rule(rules[i]);
+	}
+
+cleanup:
+	app_msg_free(app, rsp);
+	rte_free(rules);
+	rte_free(new_rules);
+	rte_free(keys_found);
+	rte_free(entries_ptr);
+
+	return status;
+}
+
+int
+app_pipeline_firewall_delete_bulk(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_firewall_key *keys,
+	uint32_t n_keys)
+{
+	struct app_pipeline_firewall *p;
+	struct pipeline_firewall_del_bulk_msg_req *req;
+	struct pipeline_firewall_del_bulk_msg_rsp *rsp;
+
+	struct app_pipeline_firewall_rule **rules;
+	int *keys_found;
+
+	uint32_t i;
+	int status = 0;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	rules = rte_malloc(NULL,
+			n_keys * sizeof(struct app_pipeline_firewall_rule *),
+			RTE_CACHE_LINE_SIZE);
+	if (rules == NULL)
+		return -1;
+
+	for (i = 0; i < n_keys; i++) {
+		if (app_pipeline_firewall_key_check_and_normalize(&keys[i]) != 0) {
+			return -1;
+		}
+
+		rules[i] = app_pipeline_firewall_rule_find(p, &keys[i]);
+	}
+
+	keys_found = rte_malloc(NULL,
+			n_keys * sizeof(int),
+			RTE_CACHE_LINE_SIZE);
+	if (keys_found == NULL) {
+		rte_free(rules);
+		return -1;
+	}
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL) {
+		rte_free(rules);
+		rte_free(keys_found);
+		return -1;
+	}
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FIREWALL_MSG_REQ_DEL_BULK;
+
+	req->keys = keys;
+	req->n_keys = n_keys;
+	req->keys_found = keys_found;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL) {
+		rte_free(rules);
+		rte_free(keys_found);
+		return -1;
+	}
+
+	if (rsp->status) {
+		status = -1;
+		goto cleanup;
+	}
+
+	for (i = 0; i < n_keys; i++) {
+		if (keys_found[i] == 0) {
+			status = -1;
+			goto cleanup;
+		}
+	}
+
+	for (i = 0; i < n_keys; i++) {
+		TAILQ_REMOVE(&p->rules, rules[i], node);
+		p->n_rules--;
+		rte_free(rules[i]);
+	}
+
+cleanup:
+	app_msg_free(app, rsp);
+	rte_free(rules);
+	rte_free(keys_found);
+
+	return status;
+}
+
+int
 app_pipeline_firewall_add_default_rule(struct app_params *app,
 	uint32_t pipeline_id,
 	uint32_t port_id)
@@ -795,6 +1487,170 @@ cmdline_parse_inst_t cmd_firewall_del_ipv4 = {
 };
 
 /*
+ * p firewall add bulk
+ */
+
+struct cmd_firewall_add_bulk_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t firewall_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t bulk_string;
+	cmdline_fixed_string_t file_path;
+};
+
+static void
+cmd_firewall_add_bulk_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	void *data)
+{
+	struct cmd_firewall_add_bulk_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	struct app_pipeline_add_bulk_params add_bulk_params;
+
+	status = app_pipeline_add_bulk_parse_file(params->file_path, &add_bulk_params);
+	if (status != 0) {
+		printf("Command failed\n");
+		goto end;
+	}
+
+	status = app_pipeline_firewall_add_bulk(app, params->pipeline_id, add_bulk_params.keys,
+			add_bulk_params.n_keys, add_bulk_params.priorities, add_bulk_params.port_ids);
+	if (status != 0) {
+		printf("Command failed\n");
+		goto end;
+	}
+
+end:
+	rte_free(add_bulk_params.keys);
+	rte_free(add_bulk_params.priorities);
+	rte_free(add_bulk_params.port_ids);
+}
+
+cmdline_parse_token_string_t cmd_firewall_add_bulk_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_bulk_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_firewall_add_bulk_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_bulk_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_firewall_add_bulk_firewall_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_bulk_result,
+		firewall_string, "firewall");
+
+cmdline_parse_token_string_t cmd_firewall_add_bulk_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_bulk_result,
+		add_string, "add");
+
+cmdline_parse_token_string_t cmd_firewall_add_bulk_bulk_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_bulk_result,
+		bulk_string, "bulk");
+
+cmdline_parse_token_string_t cmd_firewall_add_bulk_file_path_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_bulk_result,
+		file_path, NULL);
+
+cmdline_parse_inst_t cmd_firewall_add_bulk = {
+	.f = cmd_firewall_add_bulk_parsed,
+	.data = NULL,
+	.help_str = "Firewall rule add bulk",
+	.tokens = {
+		(void *) &cmd_firewall_add_bulk_p_string,
+		(void *) &cmd_firewall_add_bulk_pipeline_id,
+		(void *) &cmd_firewall_add_bulk_firewall_string,
+		(void *) &cmd_firewall_add_bulk_add_string,
+		(void *) &cmd_firewall_add_bulk_bulk_string,
+		(void *) &cmd_firewall_add_bulk_file_path_string,
+		NULL,
+	},
+};
+
+/*
+ * p firewall del bulk
+ */
+
+struct cmd_firewall_del_bulk_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t firewall_string;
+	cmdline_fixed_string_t del_string;
+	cmdline_fixed_string_t bulk_string;
+	cmdline_fixed_string_t file_path;
+};
+
+static void
+cmd_firewall_del_bulk_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	void *data)
+{
+	struct cmd_firewall_del_bulk_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	struct app_pipeline_del_bulk_params del_bulk_params;
+
+	status = app_pipeline_del_bulk_parse_file(params->file_path, &del_bulk_params);
+	if (status != 0) {
+		printf("Command failed\n");
+		goto end;
+	}
+
+	status = app_pipeline_firewall_delete_bulk(app, params->pipeline_id,
+			del_bulk_params.keys, del_bulk_params.n_keys);
+	if (status != 0) {
+		printf("Command failed\n");
+		goto end;
+	}
+
+end:
+	rte_free(del_bulk_params.keys);
+}
+
+cmdline_parse_token_string_t cmd_firewall_del_bulk_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_bulk_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_firewall_del_bulk_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_bulk_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_firewall_del_bulk_firewall_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_bulk_result,
+		firewall_string, "firewall");
+
+cmdline_parse_token_string_t cmd_firewall_del_bulk_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_bulk_result,
+		del_string, "del");
+
+cmdline_parse_token_string_t cmd_firewall_del_bulk_bulk_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_bulk_result,
+		bulk_string, "bulk");
+
+cmdline_parse_token_string_t cmd_firewall_del_bulk_file_path_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_bulk_result,
+		file_path, NULL);
+
+cmdline_parse_inst_t cmd_firewall_del_bulk = {
+	.f = cmd_firewall_del_bulk_parsed,
+	.data = NULL,
+	.help_str = "Firewall rule del bulk",
+	.tokens = {
+		(void *) &cmd_firewall_del_bulk_p_string,
+		(void *) &cmd_firewall_del_bulk_pipeline_id,
+		(void *) &cmd_firewall_del_bulk_firewall_string,
+		(void *) &cmd_firewall_del_bulk_add_string,
+		(void *) &cmd_firewall_del_bulk_bulk_string,
+		(void *) &cmd_firewall_del_bulk_file_path_string,
+		NULL,
+	},
+};
+
+/*
  * p firewall add default
  */
 struct cmd_firewall_add_default_result {
@@ -984,6 +1840,8 @@ cmdline_parse_inst_t cmd_firewall_ls = {
 static cmdline_parse_ctx_t pipeline_cmds[] = {
 	(cmdline_parse_inst_t *) &cmd_firewall_add_ipv4,
 	(cmdline_parse_inst_t *) &cmd_firewall_del_ipv4,
+	(cmdline_parse_inst_t *) &cmd_firewall_add_bulk,
+	(cmdline_parse_inst_t *) &cmd_firewall_del_bulk,
 	(cmdline_parse_inst_t *) &cmd_firewall_add_default,
 	(cmdline_parse_inst_t *) &cmd_firewall_del_default,
 	(cmdline_parse_inst_t *) &cmd_firewall_ls,
diff --git a/examples/ip_pipeline/pipeline/pipeline_firewall.h b/examples/ip_pipeline/pipeline/pipeline_firewall.h
index 82e905d..ccc4e64 100644
--- a/examples/ip_pipeline/pipeline/pipeline_firewall.h
+++ b/examples/ip_pipeline/pipeline/pipeline_firewall.h
@@ -50,6 +50,20 @@ app_pipeline_firewall_delete_rule(struct app_params *app,
 	struct pipeline_firewall_key *key);
 
 int
+app_pipeline_firewall_add_bulk(struct app_params *app,
+		uint32_t pipeline_id,
+		struct pipeline_firewall_key *keys,
+		uint32_t n_keys,
+		uint32_t *priorities,
+		uint32_t *port_ids);
+
+int
+app_pipeline_firewall_delete_bulk(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_firewall_key *keys,
+	uint32_t n_keys);
+
+int
 app_pipeline_firewall_add_default_rule(struct app_params *app,
 	uint32_t pipeline_id,
 	uint32_t port_id);
diff --git a/examples/ip_pipeline/pipeline/pipeline_firewall_be.c b/examples/ip_pipeline/pipeline/pipeline_firewall_be.c
index b6f305f..1c376f7 100644
--- a/examples/ip_pipeline/pipeline/pipeline_firewall_be.c
+++ b/examples/ip_pipeline/pipeline/pipeline_firewall_be.c
@@ -80,6 +80,12 @@ static void *
 pipeline_firewall_msg_req_del_handler(struct pipeline *p, void *msg);
 
 static void *
+pipeline_firewall_msg_req_add_bulk_handler(struct pipeline *p, void *msg);
+
+static void *
+pipeline_firewall_msg_req_del_bulk_handler(struct pipeline *p, void *msg);
+
+static void *
 pipeline_firewall_msg_req_add_default_handler(struct pipeline *p, void *msg);
 
 static void *
@@ -90,6 +96,10 @@ static pipeline_msg_req_handler custom_handlers[] = {
 		pipeline_firewall_msg_req_add_handler,
 	[PIPELINE_FIREWALL_MSG_REQ_DEL] =
 		pipeline_firewall_msg_req_del_handler,
+	[PIPELINE_FIREWALL_MSG_REQ_ADD_BULK] =
+		pipeline_firewall_msg_req_add_bulk_handler,
+	[PIPELINE_FIREWALL_MSG_REQ_DEL_BULK] =
+		pipeline_firewall_msg_req_del_bulk_handler,
 	[PIPELINE_FIREWALL_MSG_REQ_ADD_DEFAULT] =
 		pipeline_firewall_msg_req_add_default_handler,
 	[PIPELINE_FIREWALL_MSG_REQ_DEL_DEFAULT] =
@@ -698,6 +708,153 @@ pipeline_firewall_msg_req_del_handler(struct pipeline *p, void *msg)
 	return rsp;
 }
 
+static void *
+pipeline_firewall_msg_req_add_bulk_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_firewall_add_bulk_msg_req *req = msg;
+	struct pipeline_firewall_add_bulk_msg_rsp *rsp = msg;
+
+	struct rte_table_acl_rule_add_params *params[req->n_keys];
+	struct firewall_table_entry *entries[req->n_keys];
+
+	uint32_t i, n_keys;
+
+	n_keys = req->n_keys;
+
+	for (i = 0; i < n_keys; i++) {
+		entries[i] = rte_malloc(NULL,
+				sizeof(struct firewall_table_entry),
+				RTE_CACHE_LINE_SIZE);
+		if (entries[i] == NULL) {
+			rsp->status = -1;
+			return rsp;
+		}
+
+		params[i] = rte_malloc(NULL,
+				sizeof(struct rte_table_acl_rule_add_params),
+				RTE_CACHE_LINE_SIZE);
+		if (params[i] == NULL) {
+			rsp->status = -1;
+			return rsp;
+		}
+
+		entries[i]->head.action = RTE_PIPELINE_ACTION_PORT;
+		entries[i]->head.port_id = p->port_out_id[req->port_ids[i]];
+
+		switch (req->keys[i].type) {
+		case PIPELINE_FIREWALL_IPV4_5TUPLE:
+			params[i]->priority = req->priorities[i];
+			params[i]->field_value[0].value.u8 =
+				req->keys[i].key.ipv4_5tuple.proto;
+			params[i]->field_value[0].mask_range.u8 =
+				req->keys[i].key.ipv4_5tuple.proto_mask;
+			params[i]->field_value[1].value.u32 =
+				req->keys[i].key.ipv4_5tuple.src_ip;
+			params[i]->field_value[1].mask_range.u32 =
+				req->keys[i].key.ipv4_5tuple.src_ip_mask;
+			params[i]->field_value[2].value.u32 =
+				req->keys[i].key.ipv4_5tuple.dst_ip;
+			params[i]->field_value[2].mask_range.u32 =
+				req->keys[i].key.ipv4_5tuple.dst_ip_mask;
+			params[i]->field_value[3].value.u16 =
+				req->keys[i].key.ipv4_5tuple.src_port_from;
+			params[i]->field_value[3].mask_range.u16 =
+				req->keys[i].key.ipv4_5tuple.src_port_to;
+			params[i]->field_value[4].value.u16 =
+				req->keys[i].key.ipv4_5tuple.dst_port_from;
+			params[i]->field_value[4].mask_range.u16 =
+				req->keys[i].key.ipv4_5tuple.dst_port_to;
+			break;
+
+		default:
+			rsp->status = -1; /* Error */
+
+			for (i = 0; i < n_keys; i++) {
+				rte_free(entries[i]);
+				rte_free(params[i]);
+			}
+
+			return rsp;
+		}
+	}
+
+	rsp->status = rte_pipeline_table_entry_add_bulk(p->p, p->table_id[0],
+			(void *)params, (struct rte_pipeline_table_entry **)entries,
+			n_keys, req->keys_found,
+			(struct rte_pipeline_table_entry **)req->entries_ptr);
+
+	for (i = 0; i < n_keys; i++) {
+		rte_free(entries[i]);
+		rte_free(params[i]);
+	}
+
+	return rsp;
+}
+
+static void *
+pipeline_firewall_msg_req_del_bulk_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_firewall_del_bulk_msg_req *req = msg;
+	struct pipeline_firewall_del_bulk_msg_rsp *rsp = msg;
+
+	struct rte_table_acl_rule_delete_params *params[req->n_keys];
+
+	uint32_t i, n_keys;
+
+	n_keys = req->n_keys;
+
+	for (i = 0; i < n_keys; i++) {
+		params[i] = rte_malloc(NULL,
+				sizeof(struct rte_table_acl_rule_delete_params),
+				RTE_CACHE_LINE_SIZE);
+		if (params[i] == NULL) {
+			rsp->status = -1;
+			return rsp;
+		}
+
+		switch (req->keys[i].type) {
+		case PIPELINE_FIREWALL_IPV4_5TUPLE:
+			params[i]->field_value[0].value.u8 =
+				req->keys[i].key.ipv4_5tuple.proto;
+			params[i]->field_value[0].mask_range.u8 =
+				req->keys[i].key.ipv4_5tuple.proto_mask;
+			params[i]->field_value[1].value.u32 =
+				req->keys[i].key.ipv4_5tuple.src_ip;
+			params[i]->field_value[1].mask_range.u32 =
+				req->keys[i].key.ipv4_5tuple.src_ip_mask;
+			params[i]->field_value[2].value.u32 =
+				req->keys[i].key.ipv4_5tuple.dst_ip;
+			params[i]->field_value[2].mask_range.u32 =
+				req->keys[i].key.ipv4_5tuple.dst_ip_mask;
+			params[i]->field_value[3].value.u16 =
+				req->keys[i].key.ipv4_5tuple.src_port_from;
+			params[i]->field_value[3].mask_range.u16 =
+				req->keys[i].key.ipv4_5tuple.src_port_to;
+			params[i]->field_value[4].value.u16 =
+				req->keys[i].key.ipv4_5tuple.dst_port_from;
+			params[i]->field_value[4].mask_range.u16 =
+				req->keys[i].key.ipv4_5tuple.dst_port_to;
+			break;
+
+		default:
+			rsp->status = -1; /* Error */
+
+			for (i = 0; i < n_keys; i++)
+				rte_free(params[i]);
+
+			return rsp;
+		}
+	}
+
+	rsp->status = rte_pipeline_table_entry_delete_bulk(p->p, p->table_id[0],
+			(void **)¶ms, n_keys, req->keys_found, NULL);
+
+	for (i = 0; i < n_keys; i++)
+		rte_free(params[i]);
+
+	return rsp;
+}
+
 void *
 pipeline_firewall_msg_req_add_default_handler(struct pipeline *p, void *msg)
 {
diff --git a/examples/ip_pipeline/pipeline/pipeline_firewall_be.h b/examples/ip_pipeline/pipeline/pipeline_firewall_be.h
index 8e1fd69..f5b0522 100644
--- a/examples/ip_pipeline/pipeline/pipeline_firewall_be.h
+++ b/examples/ip_pipeline/pipeline/pipeline_firewall_be.h
@@ -63,6 +63,8 @@ struct pipeline_firewall_key {
 enum pipeline_firewall_msg_req_type {
 	PIPELINE_FIREWALL_MSG_REQ_ADD = 0,
 	PIPELINE_FIREWALL_MSG_REQ_DEL,
+	PIPELINE_FIREWALL_MSG_REQ_ADD_BULK,
+	PIPELINE_FIREWALL_MSG_REQ_DEL_BULK,
 	PIPELINE_FIREWALL_MSG_REQ_ADD_DEFAULT,
 	PIPELINE_FIREWALL_MSG_REQ_DEL_DEFAULT,
 	PIPELINE_FIREWALL_MSG_REQS
@@ -106,6 +108,42 @@ struct pipeline_firewall_del_msg_rsp {
 };
 
 /*
+ * MSG ADD BULK
+ */
+struct pipeline_firewall_add_bulk_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_firewall_msg_req_type subtype;
+
+	struct pipeline_firewall_key *keys;
+	uint32_t n_keys;
+
+	uint32_t *priorities;
+	uint32_t *port_ids;
+	int *keys_found;
+	void **entries_ptr;
+};
+struct pipeline_firewall_add_bulk_msg_rsp {
+	int status;
+};
+
+/*
+ * MSG DEL BULK
+ */
+struct pipeline_firewall_del_bulk_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_firewall_msg_req_type subtype;
+
+	/* key */
+	struct pipeline_firewall_key *keys;
+	uint32_t n_keys;
+	int *keys_found;
+};
+
+struct pipeline_firewall_del_bulk_msg_rsp {
+	int status;
+};
+
+/*
  * MSG ADD DEFAULT
  */
 struct pipeline_firewall_add_default_msg_req {
-- 
1.9.1
^ permalink raw reply	[flat|nested] 7+ messages in thread
- * [dpdk-dev] [PATCH v4 5/5] doc: modify release notes and deprecation notice for table and pipeline
  2015-10-20 13:01 [dpdk-dev] [PATCH v4 0/5] pipeline: add bulk add/delete functions for table Michal Jastrzebski
                   ` (3 preceding siblings ...)
  2015-10-20 13:01 ` [dpdk-dev] [PATCH v4 4/5] ip_pipline: added cli commands for bulk add/delete to firewall pipeline Michal Jastrzebski
@ 2015-10-20 13:01 ` Michal Jastrzebski
  2015-11-25 22:20 ` [dpdk-dev] [PATCH v4 0/5] pipeline: add bulk add/delete functions for table Thomas Monjalon
  5 siblings, 0 replies; 7+ messages in thread
From: Michal Jastrzebski @ 2015-10-20 13:01 UTC (permalink / raw)
  To: dev
From: Marcin Kerlin <marcinx.kerlin@intel.com>
The release notes is updated and the deprecation announce is removed.
Signed-off-by: Maciej Gajdzica <maciejx.t.gajdzica@intel.com>
Signed-off-by: Marcin Kerlin <marcinx.kerlin@intel.com>
Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
 doc/guides/rel_notes/deprecation.rst | 3 ---
 doc/guides/rel_notes/release_2_2.rst | 2 +-
 2 files changed, 1 insertion(+), 4 deletions(-)
diff --git a/doc/guides/rel_notes/deprecation.rst b/doc/guides/rel_notes/deprecation.rst
index 018a119..5be498a 100644
--- a/doc/guides/rel_notes/deprecation.rst
+++ b/doc/guides/rel_notes/deprecation.rst
@@ -45,9 +45,6 @@ Deprecation Notices
   the value of macros CFG_NAME_LEN and CFG_NAME_VAL will be increased.
   Most likely, the new values will be 64 and 256, respectively.
 
-* librte_table: New functions for table entry bulk add/delete will be added
-  to the table operations structure.
-
 * librte_table hash: Key mask parameter will be added to the hash table
   parameter structure for 8-byte key and 16-byte key extendible bucket and
   LRU tables.
diff --git a/doc/guides/rel_notes/release_2_2.rst b/doc/guides/rel_notes/release_2_2.rst
index 89cba4d..fa85c9f 100644
--- a/doc/guides/rel_notes/release_2_2.rst
+++ b/doc/guides/rel_notes/release_2_2.rst
@@ -128,7 +128,7 @@ The libraries prepended with a plus sign were incremented in this version.
    + librte_mbuf.so.2
      librte_mempool.so.1
      librte_meter.so.1
-     librte_pipeline.so.1
+   + librte_pipeline.so.2
      librte_pmd_bond.so.1
    + librte_pmd_ring.so.2
    + librte_port.so.2
-- 
1.9.1
^ permalink raw reply	[flat|nested] 7+ messages in thread
- * Re: [dpdk-dev] [PATCH v4 0/5] pipeline: add bulk add/delete functions for table
  2015-10-20 13:01 [dpdk-dev] [PATCH v4 0/5] pipeline: add bulk add/delete functions for table Michal Jastrzebski
                   ` (4 preceding siblings ...)
  2015-10-20 13:01 ` [dpdk-dev] [PATCH v4 5/5] doc: modify release notes and deprecation notice for table and pipeline Michal Jastrzebski
@ 2015-11-25 22:20 ` Thomas Monjalon
  5 siblings, 0 replies; 7+ messages in thread
From: Thomas Monjalon @ 2015-11-25 22:20 UTC (permalink / raw)
  To: Michal Jastrzebski; +Cc: dev
2015-10-20 15:01, Michal Jastrzebski:
> From: Marcin Kerlin <marcinx.kerlin@intel.com>
> 
> This patch adds bulk add/delete functions for tables used by pipelines. It
> allows for adding/deleting many rules to pipeline tables in one function call.
> It is particulary useful for firewall pipeline which is using ACL table. After
> every add or delete, table is rebuild which leads to very long times when
> trying to add/delete many entries.
> 
> v2:
> * Incremented the LIBABIVER number
> * Updated release notes
> * Removed deprecation announce
> 
> v3:
> * Updated a Doxygen comment
> 
> v4:
> * Moved .map file to the second patch
> 
> Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
> 
> Maciej Gajdzica (5):
>   table: added bulk add/delete functions for table
>   pipeline: added bulk add/delete functions for table
>   test_table: added check for bulk add/delete to acl table unit test
>   ip_pipline: added cli commands for bulk add/delete to firewall
>     pipeline
>   doc: modify release notes and deprecation notice for table and
>     pipeline
Applied, thanks
^ permalink raw reply	[flat|nested] 7+ messages in thread