[dpdk-dev] [PATCH v8 1/4] librte_flow_classify: add flow classify library

Singh, Jasvinder jasvinder.singh at intel.com
Thu Oct 19 16:22:15 CEST 2017


<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 other types of rules, not only ipv4_5tuples.
Making this structure modular will help in extending it for other rule types 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 = 0;
> +	uint32_t i = 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 != RTE_FLOW_ITEM_TYPE_END) {
> +		if ((pattern + i)->type != RTE_FLOW_ITEM_TYPE_VOID)
> +			item_num++;
> +		i++;
> +	}
> +	item_num++;
> +
> +	items = 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 = 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 = parse_filter(attr, items, actions, &ntuple_filter, error);
> +	free(items);
> +	return ret;
> +}

This function mainly parses the flow pattern, actions etc and fill the entries 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 = (unsigned char)(ip >> 24 & 0xff);\
> +		*b = (unsigned char)(ip >> 16 & 0xff);\
> +		*c = (unsigned char)(ip >> 8 & 0xff);\
> +		*d = (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? 

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 == NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: Incorrect value for parameter params\n",
> __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* name */
> +	if (params->name == NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: Incorrect value for parameter name\n",
> __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* socket */
> +	if ((params->socket_id < 0) ||
> +	    (params->socket_id >= 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 = rte_flow_classifier_check_params(params);
> +	if (ret != 0) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: flow classifier params check failed (%d)\n",
> +			__func__, ret);
> +		return NULL;
> +	}
> +
> +	/* Allocate memory for the flow classifier */
> +	cls = rte_zmalloc_socket("FLOW_CLASSIFIER",
> +			sizeof(struct rte_flow_classifier),
> +			RTE_CACHE_LINE_SIZE, params->socket_id);
> +
> +	if (cls == 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 = params->socket_id;
> +	cls->type = params->type;
> +
> +	/* Initialize flow classifier internal data structure */
> +	cls->num_tables = 0;
> +
> +	return cls;
> +}
> +
> +static void
> +rte_flow_classify_table_free(struct rte_table *table)
> +{
> +	if (table->ops.f_free != 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 classifier 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 == NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: rte_flow_classifier parameter is NULL\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +
> +	/* Free tables */
> +	for (i = 0; i < cls->num_tables; i++) {
> +		struct rte_table *table = &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 == NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: flow classifier parameter is NULL\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +	if (params == NULL) {
> +		RTE_LOG(ERR, CLASSIFY, "%s: params parameter is NULL\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +	if (table_id == NULL) {
> +		RTE_LOG(ERR, CLASSIFY, "%s: table_id parameter is NULL\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +
> +	/* ops */
> +	if (params->ops == NULL) {
> +		RTE_LOG(ERR, CLASSIFY, "%s: params->ops is NULL\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +
> +	if (params->ops->f_create == NULL) {
> +		RTE_LOG(ERR, CLASSIFY,
> +			"%s: f_create function pointer is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (params->ops->f_lookup == 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 == 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 = rte_table_check_params(cls, params, table_id);
> +	if (ret != 0)
> +		return ret;
> +
> +	id = cls->num_tables;
> +	table = &cls->tables[id];
> +
> +	/* Allocate space for the default table entry */
> +	entry_size = sizeof(struct rte_flow_classify_table_entry) +
> +			params->table_metadata_size;
> +	default_entry =
> +		(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 == 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 the library?

> +	/* Create the table */
> +	h_table = params->ops->f_create(params->arg_create, cls-
> >socket_id,
> +		entry_size);
> +	if (h_table == 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 = id;
> +
> +	/* Save input parameters */
> +	memcpy(&table->ops, params->ops, sizeof(struct rte_table_ops));
> +
> +	table->entry_size = entry_size;
> +	table->default_entry = default_entry;
> +
> +	/* Initialize table internal data structure */
> +	table->h_table = h_table;
> +
> +	return 0;
> +}
> +
> +static struct rte_flow_classify_rule *
> +allocate_ipv4_5tuple_rule(void)
> +{
> +	struct rte_flow_classify_rule *rule;
> +
> +	rule = malloc(sizeof(struct rte_flow_classify_rule));
> +	if (!rule)
> +		return rule;
> +
> +	memset(rule, 0, sizeof(struct rte_flow_classify_rule));
> +	rule->id = unique_id++;
> +	rule->rule_type = 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 = ntuple_filter.priority;
> +	rule-
> >u.key.key_add.field_value[PROTO_FIELD_IPV4].mask_range.u8 =
> +			ntuple_filter.proto_mask;
> +	rule->u.key.key_add.field_value[PROTO_FIELD_IPV4].value.u8 =
> +			ntuple_filter.proto;
> +	rule->ipv4_5tuple.proto = ntuple_filter.proto;
> +	rule->ipv4_5tuple.proto_mask = ntuple_filter.proto_mask;
> +
> +	rule->u.key.key_add.field_value[SRC_FIELD_IPV4].mask_range.u32
> =
> +			ntuple_filter.src_ip_mask;
> +	rule->u.key.key_add.field_value[SRC_FIELD_IPV4].value.u32 =
> +			ntuple_filter.src_ip;
> +	rule->ipv4_5tuple.src_ip_mask = ntuple_filter.src_ip_mask;
> +	rule->ipv4_5tuple.src_ip = ntuple_filter.src_ip;
> +
> +	rule->u.key.key_add.field_value[DST_FIELD_IPV4].mask_range.u32
> =
> +			ntuple_filter.dst_ip_mask;
> +	rule->u.key.key_add.field_value[DST_FIELD_IPV4].value.u32 =
> +			ntuple_filter.dst_ip;
> +	rule->ipv4_5tuple.dst_ip_mask = ntuple_filter.dst_ip_mask;
> +	rule->ipv4_5tuple.dst_ip = ntuple_filter.dst_ip;
> +
> +	rule-
> >u.key.key_add.field_value[SRCP_FIELD_IPV4].mask_range.u16 =
> +			ntuple_filter.src_port_mask;
> +	rule->u.key.key_add.field_value[SRCP_FIELD_IPV4].value.u16 =
> +			ntuple_filter.src_port;
> +	rule->ipv4_5tuple.src_port_mask = ntuple_filter.src_port_mask;
> +	rule->ipv4_5tuple.src_port = ntuple_filter.src_port;
> +
> +	rule-
> >u.key.key_add.field_value[DSTP_FIELD_IPV4].mask_range.u16 =
> +			ntuple_filter.dst_port_mask;
> +	rule->u.key.key_add.field_value[DSTP_FIELD_IPV4].value.u16 =
> +			ntuple_filter.dst_port;
> +	rule->ipv4_5tuple.dst_port_mask = ntuple_filter.dst_port_mask;
> +	rule->ipv4_5tuple.dst_port = 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 >= 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 = rte_flow_classify_validate(attr, pattern, actions, error);
> +	if (ret < 0)
> +		return NULL;
> +
> +	switch (cls->type) {
> +	case RTE_FLOW_CLASSIFY_TABLE_TYPE_ACL:
> +		rule = allocate_ipv4_5tuple_rule();
> +		if (!rule)
> +			return NULL;
> +		break;
> +	default:
> +		return NULL;
> +	}
> +
> +	rule->entry = malloc(sizeof(struct rte_flow_classify_table_entry));
> +	if (!rule->entry) {
> +		free(rule);
> +		rule = NULL;
> +		return NULL;
> +	}
> +
> +	table_entry = rule->entry;
> +	table_entry->rule_id = rule->id;
> +
> +	ret = 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 = 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 = -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 >= 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 = 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 = -EINVAL;
> +	uint64_t pkts_mask;
> +	uint64_t lookup_hit_mask;
> +
> +	if (!error)
> +		return ret;
> +
> +	if (!cls || !pkts || nb_pkts == 0) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				NULL, "invalid input");
> +		return ret;
> +	}
> +
> +	if (table_id >= cls->num_tables) {
> +		rte_flow_error_set(error, EINVAL,
> +				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				NULL, "invalid table_id.");
> +		return ret;
> +	}
> +
> +	pkts_mask = RTE_LEN2MASK(nb_pkts, uint64_t);
> +	ret = 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 = nb_pkts;
> +	else
> +		cls->nb_pkts = 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 = 0;
> +	int i;
> +	int ret = -ENODATA;
> +
> +	switch (rule->action.type) {
> +	case RTE_FLOW_ACTION_TYPE_COUNT:
> +		for (i = 0; i < cls->nb_pkts; i++) {
> +			if (rule->id == cls->entries[i]->rule_id)
> +				count++;
> +		}
> +		if (count) {
> +			ret = 0;
> +			ntuple_stats =
> +				(struct rte_flow_classify_ipv4_5tuple_stats
> *)
> +				stats->stats;
> +			ntuple_stats->counter1 = count;
> +			ntuple_stats->ipv4_5tuple = rule->ipv4_5tuple;
> +		}
> +		break;
> +	default:
> +		ret = -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 = -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 = action_apply(cls, rule, stats);
> +	return ret;
> +}

The rte_flow_classify_run and rte_flow_classify_query API  should be invoked 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 copyright
> + *       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 information
> 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 transmitting
> them.
> + * Application should provide the flow type interested in, measurement to
> 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 to
> + *    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.


More information about the dev mailing list