From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <jasvinder.singh@intel.com>
Received: from mga01.intel.com (mga01.intel.com [192.55.52.88])
 by dpdk.org (Postfix) with ESMTP id 170FD1B1FF
 for <dev@dpdk.org>; Thu, 19 Oct 2017 16:22:19 +0200 (CEST)
Received: from orsmga003.jf.intel.com ([10.7.209.27])
 by fmsmga101.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;
 19 Oct 2017 07:22:18 -0700
X-ExtLoop1: 1
X-IronPort-AV: E=Sophos;i="5.43,402,1503385200"; d="scan'208";a="1026937540"
Received: from irsmsx151.ger.corp.intel.com ([163.33.192.59])
 by orsmga003.jf.intel.com with ESMTP; 19 Oct 2017 07:22:16 -0700
Received: from irsmsx103.ger.corp.intel.com ([169.254.3.49]) by
 IRSMSX151.ger.corp.intel.com ([169.254.4.108]) with mapi id 14.03.0319.002;
 Thu, 19 Oct 2017 15:22:15 +0100
From: "Singh, Jasvinder" <jasvinder.singh@intel.com>
To: "Iremonger, Bernard" <bernard.iremonger@intel.com>, "dev@dpdk.org"
 <dev@dpdk.org>, "Yigit, Ferruh" <ferruh.yigit@intel.com>, "Ananyev,
 Konstantin" <konstantin.ananyev@intel.com>, "Dumitrescu, Cristian"
 <cristian.dumitrescu@intel.com>, "adrien.mazarguil@6wind.com"
 <adrien.mazarguil@6wind.com>
Thread-Topic: [PATCH v8 1/4] librte_flow_classify: add flow classify library
Thread-Index: AQHTR4Y4d3L9SJArtkCkooxZKoaWQqLrMunA
Date: Thu, 19 Oct 2017 14:22:15 +0000
Message-ID: <54CBAA185211B4429112C315DA58FF6D33275162@IRSMSX103.ger.corp.intel.com>
References: <1506936668-31197-1-git-send-email-bernard.iremonger@intel.com>
 <1508271970-2229-2-git-send-email-bernard.iremonger@intel.com>
In-Reply-To: <1508271970-2229-2-git-send-email-bernard.iremonger@intel.com>
Accept-Language: en-US
Content-Language: en-US
X-MS-Has-Attach: 
X-MS-TNEF-Correlator: 
x-titus-metadata-40: eyJDYXRlZ29yeUxhYmVscyI6IiIsIk1ldGFkYXRhIjp7Im5zIjoiaHR0cDpcL1wvd3d3LnRpdHVzLmNvbVwvbnNcL0ludGVsMyIsImlkIjoiMGYyMzY1YmItZjIxZi00MzAxLWJmNGMtNzA1YzkzYjgwYzIwIiwicHJvcHMiOlt7Im4iOiJDVFBDbGFzc2lmaWNhdGlvbiIsInZhbHMiOlt7InZhbHVlIjoiQ1RQX0lDIn1dfV19LCJTdWJqZWN0TGFiZWxzIjpbXSwiVE1DVmVyc2lvbiI6IjE2LjUuOS4zIiwiVHJ1c3RlZExhYmVsSGFzaCI6IkpGYzNVSVIrSHBVSWZqbndjR2trWEZMUzdTNDRXZThRZnBwV2p1bXdGbHM9In0=
x-ctpclassification: CTP_IC
dlp-product: dlpe-windows
dlp-version: 11.0.0.116
dlp-reaction: no-action
x-originating-ip: [163.33.239.181]
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0
Subject: Re: [dpdk-dev] [PATCH v8 1/4] librte_flow_classify: add flow
	classify library
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.15
Precedence: list
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <http://dpdk.org/ml/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://dpdk.org/ml/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <http://dpdk.org/ml/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
X-List-Received-Date: Thu, 19 Oct 2017 14:22:21 -0000


<snip>

> +
> +struct acl_keys {
> +	struct rte_table_acl_rule_add_params key_add; /**< add key */
> +	struct rte_table_acl_rule_delete_params	key_del; /**< delete
> key */
> +};
> +
> +struct rte_flow_classify_rule {
> +	uint32_t id; /**< unique ID of classify rule */
> +	enum rte_flow_classify_rule_type rule_type; /**< classify rule type
> */
> +	struct rte_flow_action action; /**< action when match found */
> +	struct rte_flow_classify_ipv4_5tuple ipv4_5tuple; /**< ipv4 5tuple */
> +	union {
> +		struct acl_keys key;
> +	} u;
> +	int key_found;   /**< rule key found in table */
> +	void *entry;     /**< pointer to buffer to hold rule meta data*/
> +	void *entry_ptr; /**< handle to the table entry for rule meta data*/
> +};

In my opnion, the  above struct should have the provision to accommodate ot=
her types of rules, not only ipv4_5tuples.
Making this structure modular will help in extending it for other rule type=
s in the future.

> +int
> +rte_flow_classify_validate(
> +		   const struct rte_flow_attr *attr,
> +		   const struct rte_flow_item pattern[],
> +		   const struct rte_flow_action actions[],
> +		   struct rte_flow_error *error)
> +{
> +	struct rte_flow_item *items;
> +	parse_filter_t parse_filter;
> +	uint32_t item_num =3D 0;
> +	uint32_t i =3D 0;
> +	int ret;
> +
> +	if (!error)
> +		return -EINVAL;
> +
> +	if (!pattern) {
> +		rte_flow_error_set(error, EINVAL,
> RTE_FLOW_ERROR_TYPE_ITEM_NUM,
> +				   NULL, "NULL pattern.");
> +		return -EINVAL;
> +	}
> +
> +	if (!actions) {
> +		rte_flow_error_set(error, EINVAL,
> +				   RTE_FLOW_ERROR_TYPE_ACTION_NUM,
> +				   NULL, "NULL action.");
> +		return -EINVAL;
> +	}
> +
> +	if (!attr) {
> +		rte_flow_error_set(error, EINVAL,
> +				   RTE_FLOW_ERROR_TYPE_ATTR,
> +				   NULL, "NULL attribute.");
> +		return -EINVAL;
> +	}
> +
> +	memset(&ntuple_filter, 0, sizeof(ntuple_filter));
> +
> +	/* Get the non-void item number of pattern */
> +	while ((pattern + i)->type !=3D RTE_FLOW_ITEM_TYPE_END) {
> +		if ((pattern + i)->type !=3D RTE_FLOW_ITEM_TYPE_VOID)
> +			item_num++;
> +		i++;
> +	}
> +	item_num++;
> +
> +	items =3D malloc(item_num * sizeof(struct rte_flow_item));
> +	if (!items) {
> +		rte_flow_error_set(error, ENOMEM,
> RTE_FLOW_ERROR_TYPE_ITEM_NUM,
> +				   NULL, "No memory for pattern items.");
> +		return -ENOMEM;
> +	}
> +
> +	memset(items, 0, item_num * sizeof(struct rte_flow_item));
> +	classify_pattern_skip_void_item(items, pattern);
> +
> +	parse_filter =3D classify_find_parse_filter_func(items);
> +	if (!parse_filter) {
> +		rte_flow_error_set(error, EINVAL,
> +				   RTE_FLOW_ERROR_TYPE_ITEM,
> +				   pattern, "Unsupported pattern");
> +		free(items);
> +		return -EINVAL;
> +	}
> +
> +	ret =3D parse_filter(attr, items, actions, &ntuple_filter, error);
> +	free(items);
> +	return ret;
> +}

This function mainly parses the flow pattern, actions etc and fill the entr=
ies in
internal ntuple_filter.It is invoked only in flow_entry_add(). Is there any=
 reason to
make this function available as public API?

> +#ifdef RTE_LIBRTE_CLASSIFY_DEBUG
> +#define uint32_t_to_char(ip, a, b, c, d) do {\
> +		*a =3D (unsigned char)(ip >> 24 & 0xff);\
> +		*b =3D (unsigned char)(ip >> 16 & 0xff);\
> +		*c =3D (unsigned char)(ip >> 8 & 0xff);\
> +		*d =3D (unsigned char)(ip & 0xff);\
> +	} while (0)
> +
> +static inline void
> +print_ipv4_key_add(struct rte_table_acl_rule_add_params *key)
> +{
> +	unsigned char a, b, c, d;
> +
> +	printf("ipv4_key_add: 0x%02hhx/0x%hhx ",
> +		key->field_value[PROTO_FIELD_IPV4].value.u8,
> +		key->field_value[PROTO_FIELD_IPV4].mask_range.u8);
> +
> +	uint32_t_to_char(key->field_value[SRC_FIELD_IPV4].value.u32,
> +			&a, &b, &c, &d);
> +	printf(" %hhu.%hhu.%hhu.%hhu/0x%x ", a, b, c, d,
> +			key-
> >field_value[SRC_FIELD_IPV4].mask_range.u32);
> +
> +	uint32_t_to_char(key->field_value[DST_FIELD_IPV4].value.u32,
> +			&a, &b, &c, &d);
> +	printf("%hhu.%hhu.%hhu.%hhu/0x%x ", a, b, c, d,
> +			key-
> >field_value[DST_FIELD_IPV4].mask_range.u32);
> +
> +	printf("%hu : 0x%x %hu : 0x%x",
> +		key->field_value[SRCP_FIELD_IPV4].value.u16,
> +		key->field_value[SRCP_FIELD_IPV4].mask_range.u16,
> +		key->field_value[DSTP_FIELD_IPV4].value.u16,
> +		key->field_value[DSTP_FIELD_IPV4].mask_range.u16);
> +
> +	printf(" priority: 0x%x\n", key->priority);
> +}

The above function is specific to printing acl table keys. How about making=
 this function little generic by
passing the parameters to distinguish the rule, table type, etc. and do the=
 printing?=20

Same comments for the print_ipv4_key_delete().


> +static inline void
> +print_ipv4_key_delete(struct rte_table_acl_rule_delete_params *key)
> +{
> +	unsigned char a, b, c, d;
> +
> +	printf("ipv4_key_del: 0x%02hhx/0x%hhx ",
> +		key->field_value[PROTO_FIELD_IPV4].value.u8,
> +		key->field_value[PROTO_FIELD_IPV4].mask_range.u8);
> +
> +	uint32_t_to_char(key->field_value[SRC_FIELD_IPV4].value.u32,
> +			&a, &b, &c, &d);
> +	printf(" %hhu.%hhu.%hhu.%hhu/0x%x ", a, b, c, d,
> +			key-
> >field_value[SRC_FIELD_IPV4].mask_range.u32);
> +
> +	uint32_t_to_char(key->field_value[DST_FIELD_IPV4].value.u32,
> +			&a, &b, &c, &d);
> +	printf("%hhu.%hhu.%hhu.%hhu/0x%x ", a, b, c, d,
> +			key-
> >field_value[DST_FIELD_IPV4].mask_range.u32);
> +
> +	printf("%hu : 0x%x %hu : 0x%x\n",
> +		key->field_value[SRCP_FIELD_IPV4].value.u16,
> +		key->field_value[SRCP_FIELD_IPV4].mask_range.u16,
> +		key->field_value[DSTP_FIELD_IPV4].value.u16,
> +		key->field_value[DSTP_FIELD_IPV4].mask_range.u16);
> +}
> +#endif
> +
> +static int
> +rte_flow_classifier_check_params(struct rte_flow_classifier_params
> *params)
> +{
> +	if (params =3D=3D NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: Incorrect value for parameter params\n",
> __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* name */
> +	if (params->name =3D=3D NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: Incorrect value for parameter name\n",
> __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* socket */
> +	if ((params->socket_id < 0) ||
> +	    (params->socket_id >=3D RTE_MAX_NUMA_NODES)) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: Incorrect value for parameter socket_id\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +struct rte_flow_classifier *
> +rte_flow_classifier_create(struct rte_flow_classifier_params *params)
> +{
> +	struct rte_flow_classifier *cls;
> +	int ret;
> +
> +	/* Check input parameters */
> +	ret =3D rte_flow_classifier_check_params(params);
> +	if (ret !=3D 0) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: flow classifier params check failed (%d)\n",
> +			__func__, ret);
> +		return NULL;
> +	}
> +
> +	/* Allocate memory for the flow classifier */
> +	cls =3D rte_zmalloc_socket("FLOW_CLASSIFIER",
> +			sizeof(struct rte_flow_classifier),
> +			RTE_CACHE_LINE_SIZE, params->socket_id);
> +
> +	if (cls =3D=3D NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: flow classifier memory allocation failed\n",
> +			__func__);
> +		return NULL;
> +	}
> +
> +	/* Save input parameters */
> +	snprintf(cls->name, RTE_FLOW_CLASSIFIER_MAX_NAME_SZ, "%s",
> +			params->name);
> +	cls->socket_id =3D params->socket_id;
> +	cls->type =3D params->type;
> +
> +	/* Initialize flow classifier internal data structure */
> +	cls->num_tables =3D 0;
> +
> +	return cls;
> +}
> +
> +static void
> +rte_flow_classify_table_free(struct rte_table *table)
> +{
> +	if (table->ops.f_free !=3D NULL)
> +		table->ops.f_free(table->h_table);
> +
> +	rte_free(table->default_entry);
> +}

This is internal function. There is an API for creating a table for classif=
ier instance but not for destroying the table. What
if application requires destroying the specific table of the classifier but=
 want to keep the classifier instance?

> +int
> +rte_flow_classifier_free(struct rte_flow_classifier *cls)
> +{
> +	uint32_t i;
> +
> +	/* Check input parameters */
> +	if (cls =3D=3D NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: rte_flow_classifier parameter is NULL\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +
> +	/* Free tables */
> +	for (i =3D 0; i < cls->num_tables; i++) {
> +		struct rte_table *table =3D &cls->tables[i];
> +
> +		rte_flow_classify_table_free(table);
> +	}
> +
> +	/* Free flow classifier memory */
> +	rte_free(cls);
> +
> +	return 0;
> +}
> +
> +static int
> +rte_table_check_params(struct rte_flow_classifier *cls,
> +		struct rte_flow_classify_table_params *params,
> +		uint32_t *table_id)
> +{
> +	if (cls =3D=3D NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: flow classifier parameter is NULL\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +	if (params =3D=3D NULL) {
> +		RTE_LOG(ERR, CLASSIFY, "%s: params parameter is NULL\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +	if (table_id =3D=3D NULL) {
> +		RTE_LOG(ERR, CLASSIFY, "%s: table_id parameter is NULL\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +
> +	/* ops */
> +	if (params->ops =3D=3D NULL) {
> +		RTE_LOG(ERR, CLASSIFY, "%s: params->ops is NULL\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +
> +	if (params->ops->f_create =3D=3D NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: f_create function pointer is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (params->ops->f_lookup =3D=3D NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: f_lookup function pointer is NULL\n",
> __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* De we have room for one more table? */
> +	if (cls->num_tables =3D=3D RTE_FLOW_CLASSIFY_TABLE_MAX) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: Incorrect value for num_tables parameter\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int
> +rte_flow_classify_table_create(struct rte_flow_classifier *cls,
> +	struct rte_flow_classify_table_params *params,
> +	uint32_t *table_id)
> +{
> +	struct rte_table *table;
> +	struct rte_flow_classify_table_entry *default_entry;
> +	void *h_table;
> +	uint32_t entry_size, id;
> +	int ret;
> +
> +	/* Check input arguments */
> +	ret =3D rte_table_check_params(cls, params, table_id);
> +	if (ret !=3D 0)
> +		return ret;
> +
> +	id =3D cls->num_tables;
> +	table =3D &cls->tables[id];
> +
> +	/* Allocate space for the default table entry */
> +	entry_size =3D sizeof(struct rte_flow_classify_table_entry) +
> +			params->table_metadata_size;
> +	default_entry =3D
> +		(struct rte_flow_classify_table_entry *) rte_zmalloc_socket(
> +		"Flow Classify default entry", entry_size,
> +		RTE_CACHE_LINE_SIZE, cls->socket_id);
> +	if (default_entry =3D=3D NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: Failed to allocate default entry\n", __func__);
> +		return -EINVAL;
> +	}

what is the purpose of default_entry as I don't see its usage anywhere in t=
he library?

> +	/* Create the table */
> +	h_table =3D params->ops->f_create(params->arg_create, cls-
> >socket_id,
> +		entry_size);
> +	if (h_table =3D=3D NULL) {
> +		rte_free(default_entry);
> +		RTE_LOG(ERR, CLASSIFY, "%s: Table creation failed\n",
> __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* Commit current table to the classifier */
> +	cls->num_tables++;
> +	*table_id =3D id;
> +
> +	/* Save input parameters */
> +	memcpy(&table->ops, params->ops, sizeof(struct rte_table_ops));
> +
> +	table->entry_size =3D entry_size;
> +	table->default_entry =3D default_entry;
> +
> +	/* Initialize table internal data structure */
> +	table->h_table =3D h_table;
> +
> +	return 0;
> +}
> +
> +static struct rte_flow_classify_rule *
> +allocate_ipv4_5tuple_rule(void)
> +{
> +	struct rte_flow_classify_rule *rule;
> +
> +	rule =3D malloc(sizeof(struct rte_flow_classify_rule));
> +	if (!rule)
> +		return rule;
> +
> +	memset(rule, 0, sizeof(struct rte_flow_classify_rule));
> +	rule->id =3D unique_id++;
> +	rule->rule_type =3D RTE_FLOW_CLASSIFY_RULE_TYPE_IPV4_5TUPLE;
> +
> +	memcpy(&rule->action, classify_get_flow_action(),
> +	       sizeof(struct rte_flow_action));
> +
> +	/* key add values */
> +	rule->u.key.key_add.priority =3D ntuple_filter.priority;
> +	rule-
> >u.key.key_add.field_value[PROTO_FIELD_IPV4].mask_range.u8 =3D
> +			ntuple_filter.proto_mask;
> +	rule->u.key.key_add.field_value[PROTO_FIELD_IPV4].value.u8 =3D
> +			ntuple_filter.proto;
> +	rule->ipv4_5tuple.proto =3D ntuple_filter.proto;
> +	rule->ipv4_5tuple.proto_mask =3D ntuple_filter.proto_mask;
> +
> +	rule->u.key.key_add.field_value[SRC_FIELD_IPV4].mask_range.u32
> =3D
> +			ntuple_filter.src_ip_mask;
> +	rule->u.key.key_add.field_value[SRC_FIELD_IPV4].value.u32 =3D
> +			ntuple_filter.src_ip;
> +	rule->ipv4_5tuple.src_ip_mask =3D ntuple_filter.src_ip_mask;
> +	rule->ipv4_5tuple.src_ip =3D ntuple_filter.src_ip;
> +
> +	rule->u.key.key_add.field_value[DST_FIELD_IPV4].mask_range.u32
> =3D
> +			ntuple_filter.dst_ip_mask;
> +	rule->u.key.key_add.field_value[DST_FIELD_IPV4].value.u32 =3D
> +			ntuple_filter.dst_ip;
> +	rule->ipv4_5tuple.dst_ip_mask =3D ntuple_filter.dst_ip_mask;
> +	rule->ipv4_5tuple.dst_ip =3D ntuple_filter.dst_ip;
> +
> +	rule-
> >u.key.key_add.field_value[SRCP_FIELD_IPV4].mask_range.u16 =3D
> +			ntuple_filter.src_port_mask;
> +	rule->u.key.key_add.field_value[SRCP_FIELD_IPV4].value.u16 =3D
> +			ntuple_filter.src_port;
> +	rule->ipv4_5tuple.src_port_mask =3D ntuple_filter.src_port_mask;
> +	rule->ipv4_5tuple.src_port =3D ntuple_filter.src_port;
> +
> +	rule-
> >u.key.key_add.field_value[DSTP_FIELD_IPV4].mask_range.u16 =3D
> +			ntuple_filter.dst_port_mask;
> +	rule->u.key.key_add.field_value[DSTP_FIELD_IPV4].value.u16 =3D
> +			ntuple_filter.dst_port;
> +	rule->ipv4_5tuple.dst_port_mask =3D ntuple_filter.dst_port_mask;
> +	rule->ipv4_5tuple.dst_port =3D ntuple_filter.dst_port;
> +
> +#ifdef RTE_LIBRTE_CLASSIFY_DEBUG
> +	print_ipv4_key_add(&rule->u.key.key_add);
> +#endif
> +
> +	/* key delete values */
> +	memcpy(&rule->u.key.key_del.field_value[PROTO_FIELD_IPV4],
> +	       &rule->u.key.key_add.field_value[PROTO_FIELD_IPV4],
> +	       NUM_FIELDS_IPV4 * sizeof(struct rte_acl_field));
> +
> +#ifdef RTE_LIBRTE_CLASSIFY_DEBUG
> +	print_ipv4_key_delete(&rule->u.key.key_del);
> +#endif
> +	return rule;
> +}
> +
> +struct rte_flow_classify_rule *
> +rte_flow_classify_table_entry_add(struct rte_flow_classifier *cls,
> +		uint32_t table_id,
> +		const struct rte_flow_attr *attr,
> +		const struct rte_flow_item pattern[],
> +		const struct rte_flow_action actions[],
> +		struct rte_flow_error *error)
> +{
> +	struct rte_flow_classify_rule *rule;
> +	struct rte_flow_classify_table_entry *table_entry;
> +	int ret;
> +
> +	if (!error)
> +		return NULL;
> +
> +	if (!cls) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				NULL, "NULL classifier.");
> +		return NULL;
> +	}
> +
> +	if (table_id >=3D cls->num_tables) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				NULL, "invalid table_id.");
> +		return NULL;
> +	}
> +
> +	if (!pattern) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_ITEM_NUM,
> +				NULL, "NULL pattern.");
> +		return NULL;
> +	}
> +
> +	if (!actions) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_ACTION_NUM,
> +				NULL, "NULL action.");
> +		return NULL;
> +	}
> +
> +	if (!attr) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_ATTR,
> +				NULL, "NULL attribute.");
> +		return NULL;
> +	}
> +
> +	/* parse attr, pattern and actions */
> +	ret =3D rte_flow_classify_validate(attr, pattern, actions, error);
> +	if (ret < 0)
> +		return NULL;
> +
> +	switch (cls->type) {
> +	case RTE_FLOW_CLASSIFY_TABLE_TYPE_ACL:
> +		rule =3D allocate_ipv4_5tuple_rule();
> +		if (!rule)
> +			return NULL;
> +		break;
> +	default:
> +		return NULL;
> +	}
> +
> +	rule->entry =3D malloc(sizeof(struct rte_flow_classify_table_entry));
> +	if (!rule->entry) {
> +		free(rule);
> +		rule =3D NULL;
> +		return NULL;
> +	}
> +
> +	table_entry =3D rule->entry;
> +	table_entry->rule_id =3D rule->id;
> +
> +	ret =3D cls->tables[table_id].ops.f_add(
> +			cls->tables[table_id].h_table,
> +			&rule->u.key.key_add,
> +			rule->entry,
> +			&rule->key_found,
> +			&rule->entry_ptr);
> +	if (ret) {
> +		free(rule->entry);
> +		free(rule);
> +		rule =3D NULL;
> +		return NULL;
> +	}
> +	return rule;
> +}

It is not clear if the pattern to be added already exists in the table? how=
 this information will be propagated
to the application?

> +int
> +rte_flow_classify_table_entry_delete(struct rte_flow_classifier *cls,
> +		uint32_t table_id,
> +		struct rte_flow_classify_rule *rule,
> +		struct rte_flow_error *error)
> +{
> +	int ret =3D -EINVAL;
> +
> +	if (!error)
> +		return ret;
> +
> +	if (!cls) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				NULL, "NULL classifier.");
> +		return ret;
> +	}
> +
> +	if (table_id >=3D cls->num_tables) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				NULL, "invalid table_id.");
> +		return ret;
> +	}
> +
> +	if (!rule) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				NULL, "NULL rule.");
> +		return ret;
> +	}
> +
> +	ret =3D cls->tables[table_id].ops.f_delete(
> +			cls->tables[table_id].h_table,
> +			&rule->u.key.key_del,
> +			&rule->key_found,
> +			&rule->entry);

Please introduce check for f_delete, shouldn't be NULL.

> +
> +int
> +rte_flow_classifier_run(struct rte_flow_classifier *cls,
> +		uint32_t table_id,
> +		struct rte_mbuf **pkts,
> +		const uint16_t nb_pkts,
> +		struct rte_flow_error *error)
> +{
> +	int ret =3D -EINVAL;
> +	uint64_t pkts_mask;
> +	uint64_t lookup_hit_mask;
> +
> +	if (!error)
> +		return ret;
> +
> +	if (!cls || !pkts || nb_pkts =3D=3D 0) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				NULL, "invalid input");
> +		return ret;
> +	}
> +
> +	if (table_id >=3D cls->num_tables) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				NULL, "invalid table_id.");
> +		return ret;
> +	}
> +
> +	pkts_mask =3D RTE_LEN2MASK(nb_pkts, uint64_t);
> +	ret =3D cls->tables[table_id].ops.f_lookup(
> +			cls->tables[table_id].h_table,
> +			pkts, pkts_mask, &lookup_hit_mask,
> +			(void **)cls->entries);
> +
> +	if (!ret && lookup_hit_mask)
> +		cls->nb_pkts =3D nb_pkts;
> +	else
> +		cls->nb_pkts =3D 0;
> +
> +	return ret;
> +}
> +
> +static int
> +action_apply(struct rte_flow_classifier *cls,
> +		struct rte_flow_classify_rule *rule,
> +		struct rte_flow_classify_stats *stats)
> +{
> +	struct rte_flow_classify_ipv4_5tuple_stats *ntuple_stats;
> +	uint64_t count =3D 0;
> +	int i;
> +	int ret =3D -ENODATA;
> +
> +	switch (rule->action.type) {
> +	case RTE_FLOW_ACTION_TYPE_COUNT:
> +		for (i =3D 0; i < cls->nb_pkts; i++) {
> +			if (rule->id =3D=3D cls->entries[i]->rule_id)
> +				count++;
> +		}
> +		if (count) {
> +			ret =3D 0;
> +			ntuple_stats =3D
> +				(struct rte_flow_classify_ipv4_5tuple_stats
> *)
> +				stats->stats;
> +			ntuple_stats->counter1 =3D count;
> +			ntuple_stats->ipv4_5tuple =3D rule->ipv4_5tuple;
> +		}
> +		break;
> +	default:
> +		ret =3D -ENOTSUP;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +int
> +rte_flow_classifier_query(struct rte_flow_classifier *cls,
> +		struct rte_flow_classify_rule *rule,
> +		struct rte_flow_classify_stats *stats,
> +		struct rte_flow_error *error)
> +{
> +	int ret =3D -EINVAL;
> +
> +	if (!error)
> +		return ret;
> +
> +	if (!cls || !rule || !stats) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				NULL, "invalid input");
> +		return ret;
> +	}
> +
> +	ret =3D action_apply(cls, rule, stats);
> +	return ret;
> +}

The rte_flow_classify_run and rte_flow_classify_query API  should be invoke=
d consecutively in the application, true?

> diff --git a/lib/librte_flow_classify/rte_flow_classify.h
> b/lib/librte_flow_classify/rte_flow_classify.h
> new file mode 100644
> index 0000000..9bd6cf4
> --- /dev/null
> +++ b/lib/librte_flow_classify/rte_flow_classify.h
> @@ -0,0 +1,321 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2017 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyrig=
ht
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> + */
> +
> +#ifndef _RTE_FLOW_CLASSIFY_H_
> +#define _RTE_FLOW_CLASSIFY_H_
> +
> +/**
> + * @file
> + *
> + * RTE Flow Classify Library
> + *
> + * This library provides flow record information with some measured
> properties.
> + *
> + * Application should define the flow and measurement criteria (action) =
for
> it.
> + *
> + * Library doesn't maintain any flow records itself, instead flow inform=
ation
> is
> + * returned to upper layer only for given packets.
> + *
> + * It is application's responsibility to call rte_flow_classify_query()
> + * for group of packets, just after receiving them or before transmittin=
g
> them.
> + * Application should provide the flow type interested in, measurement t=
o
> apply
> + * to that flow in rte_flow_classify_create() API, and should provide
> + * rte_flow_classify object and storage to put results in
> + * rte_flow_classify_query() API.
> + *
> + *  Usage:
> + *  - application calls rte_flow_classify_create() to create a rte_flow_=
classify
> + *    object.
> + *  - application calls rte_flow_classify_query() in a polling manner,
> + *    preferably after rte_eth_rx_burst(). This will cause the library t=
o
> + *    convert packet information to flow information with some
> measurements.
> + *  - rte_flow_classify object can be destroyed when they are no more
> needed
> + *    via rte_flow_classify_destroy()
> + */
> +
> +#include <rte_ethdev.h>
> +#include <rte_ether.h>
> +#include <rte_flow.h>
> +#include <rte_acl.h>
> +#include <rte_table_acl.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +
> +#define RTE_FLOW_CLASSIFY_TABLE_MAX  1
> +
> +/** Opaque data type for flow classifier */
> +struct rte_flow_classifier;
> +
> +/** Opaque data type for flow classify rule */
> +struct rte_flow_classify_rule;
> +
> +enum rte_flow_classify_rule_type {
> +	RTE_FLOW_CLASSIFY_RULE_TYPE_NONE,   /**< no type */
> +	RTE_FLOW_CLASSIFY_RULE_TYPE_IPV4_5TUPLE, /**< IPv4 5tuple
> type */
> +};
> +
> +enum rte_flow_classify_table_type {
> +	RTE_FLOW_CLASSIFY_TABLE_TYPE_NONE, /**< no type */
> +	RTE_FLOW_CLASSIFY_TABLE_TYPE_ACL,  /**< ACL type */
> +};
> +
> +/** Parameters for flow classifier creation */
> +struct rte_flow_classifier_params {
> +	/**< flow classifier name */
> +	const char *name;
> +
> +	/**< CPU socket ID where memory for the flow classifier and its */
> +	/**< elements (tables) should be allocated */
> +	int socket_id;
> +
> +	/**< Table type */
> +	enum rte_flow_classify_table_type type;
> +
> +	/**< Table id */
> +	uint32_t table_id;
> +};
> +
> +struct rte_flow_classify_table_params {
> +	/**<Table operations (specific to each table type) */
> +	struct rte_table_ops *ops;
> +
> +	/**< Opaque param to be passed to the table create operation */
> +	void *arg_create;
> +
> +	/**< Memory size to be reserved per classifier object entry for */
> +	/**< storing meta data */
> +	uint32_t table_metadata_size;
> +};
> +
> +struct rte_flow_classify_ipv4_5tuple {
> +	uint32_t dst_ip;         /**< Destination IP address in big endian. */
> +	uint32_t dst_ip_mask;    /**< Mask of destination IP address. */
> +	uint32_t src_ip;         /**< Source IP address in big endian. */
> +	uint32_t src_ip_mask;    /**< Mask of destination IP address. */
> +	uint16_t dst_port;       /**< Destination port in big endian. */
> +	uint16_t dst_port_mask;  /**< Mask of destination port. */
> +	uint16_t src_port;       /**< Source Port in big endian. */
> +	uint16_t src_port_mask;  /**< Mask of source port. */
> +	uint8_t proto;           /**< L4 protocol. */
> +	uint8_t proto_mask;      /**< Mask of L4 protocol. */
> +};
> +
> +struct rte_flow_classify_table_entry {
> +	/**< meta-data for classify rule */
> +	uint32_t rule_id;
> +
> +	/**< Start of table entry area for user defined meta data */
> +	__extension__ uint8_t meta_data[0];
> +};

The above structure is not used by any of the public API ?

> + * Flow stats
> + *
> + * For the count action, stats can be returned by the query API.
> + *
> + * Storage for stats is provided by application.
> + */
> +struct rte_flow_classify_stats {
> +	void *stats;
> +};
> +
> +struct rte_flow_classify_ipv4_5tuple_stats {
> +	/**< count of packets that match IPv4 5tuple pattern */
> +	uint64_t counter1;
> +	/**< IPv4 5tuple data */
> +	struct rte_flow_classify_ipv4_5tuple ipv4_5tuple;
> +};
> +
> +/**
> + * Flow classifier create
> + *
> + * @param params
> + *   Parameters for flow classifier creation
> + * @return
> + *   Handle to flow classifier instance on success or NULL otherwise
> + */
> +struct rte_flow_classifier *rte_flow_classifier_create(
> +		struct rte_flow_classifier_params *params);
> +
> +/**
> + * Flow classifier free
> + *
> + * @param cls
> + *   Handle to flow classifier instance
> + * @return
> + *   0 on success, error code otherwise
> + */
> +int rte_flow_classifier_free(struct rte_flow_classifier *cls);
> +
> +/**
> + * Flow classify table create
> + *
> + * @param cls
> + *   Handle to flow classifier instance
> + * @param params
> + *   Parameters for flow_classify table creation
> + * @param table_id
> + *   Table ID. Valid only within the scope of table IDs of the current
> + *   classifier. Only returned after a successful invocation.
> + * @return
> + *   0 on success, error code otherwise
> + */
> +int rte_flow_classify_table_create(struct rte_flow_classifier *cls,
> +	struct rte_flow_classify_table_params *params,
> +	uint32_t *table_id);
> +
> +/**
> + * Validate a flow classify rule.
> + *
> + * @param[in] attr
> + *   Flow rule attributes
> + * @param[in] pattern
> + *   Pattern specification (list terminated by the END pattern item).
> + * @param[in] actions
> + *   Associated actions (list terminated by the END pattern item).
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL. Structure
> + *   initialised in case of error only.
> + *
> + * @return
> + *   0 on success, error code otherwise.
> + */
> +int
> +rte_flow_classify_validate(
> +		const struct rte_flow_attr *attr,
> +		const struct rte_flow_item pattern[],
> +		const struct rte_flow_action actions[],
> +		struct rte_flow_error *error);
> +
> +/**
> + * Add a flow classify rule to the flow_classifer table.
> + *
> + * @param[in] cls
> + *   Flow classifier handle
> + * @param[in] table_id
> + *   id of table
> + * @param[in] attr
> + *   Flow rule attributes
> + * @param[in] pattern
> + *   Pattern specification (list terminated by the END pattern item).
> + * @param[in] actions
> + *   Associated actions (list terminated by the END pattern item).
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL. Structure
> + *   initialised in case of error only.
> + * @return
> + *   A valid handle in case of success, NULL otherwise.
> + */
> +struct rte_flow_classify_rule *
> +rte_flow_classify_table_entry_add(struct rte_flow_classifier *cls,
> +		uint32_t table_id,
> +		const struct rte_flow_attr *attr,
> +		const struct rte_flow_item pattern[],
> +		const struct rte_flow_action actions[],
> +		struct rte_flow_error *error);
> +
> +/**
> + * Delete a flow classify rule from the flow_classifer table.
> + *
> + * @param[in] cls
> + *   Flow classifier handle
> + * @param[in] table_id
> + *   id of table
> + * @param[in] rule
> + *   Flow classify rule
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL. Structure
> + *   initialised in case of error only.
> + * @return
> + *   0 on success, error code otherwise.
> + */
> +int
> +rte_flow_classify_table_entry_delete(struct rte_flow_classifier *cls,
> +		uint32_t table_id,
> +		struct rte_flow_classify_rule *rule,
> +		struct rte_flow_error *error);
> +
> +/**
> + * Run flow classifier for given packets.
> + *
> + * @param[in] cls
> + *   Flow classifier handle
> + * @param[in] table_id
> + *   id of table
> + * @param[in] pkts
> + *   Pointer to packets to process
> + * @param[in] nb_pkts
> + *   Number of packets to process
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL. Structure
> + *   initialised in case of error only.
> + *
> + * @return
> + *   0 on success, error code otherwise.
> + */
> +
> +int rte_flow_classifier_run(struct rte_flow_classifier *cls,
> +		uint32_t table_id,
> +		struct rte_mbuf **pkts,
> +		const uint16_t nb_pkts,
> +		struct rte_flow_error *error);
> +
> +/**
> + * Query flow classifier for given rule.
> + *
> + * @param[in] cls
> + *   Flow classifier handle
> + * @param[in] rule
> + *   Flow classify rule
> + * @param[in] stats
> + *   Flow classify stats
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL. Structure
> + *   initialised in case of error only.
> + *
> + * @return
> + *   0 on success, error code otherwise.
> + */
> +int rte_flow_classifier_query(struct rte_flow_classifier *cls,
> +		struct rte_flow_classify_rule *rule,
> +		struct rte_flow_classify_stats *stats,
> +		struct rte_flow_error *error);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* _RTE_FLOW_CLASSIFY_H_ */


There are doxygen rendering issues in this document. Please cross check the=
 header file with
"make doc-api-html" output.