/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2010-2020 Intel Corporation */ #include "rte_malloc.h" #include "igc_logs.h" #include "igc_txrx.h" #include "igc_filter.h" #include "igc_flow.h" /******************************************************************************* * All Supported Rule Type * * Notes: * `para` or `(para)`, the para must been set * `[para]`, the para is optional * `([para1][para2]...)`, all paras is optional, but must one of them been set * `para1 | para2 | ...`, only one of the paras can be set * * ether-type filter * pattern: ETH(type)/END * action: QUEUE/END * attribute: * * n-tuple filter * pattern: [ETH/]([IPv4(protocol)|IPv6(protocol)/][UDP(dst_port)| * TCP([dst_port],[flags])|SCTP(dst_port)/])END * action: QUEUE/END * attribute: [priority(0-7)] * * SYN filter * pattern: [ETH/][IPv4|IPv6/]TCP(flags=SYN)/END * action: QUEUE/END * attribute: [priority(0,1)] * * RSS filter * pattern: * action: RSS/END * attribute: ******************************************************************************/ /* Structure to store all filters */ struct igc_all_filter { struct igc_ethertype_filter ethertype; struct igc_ntuple_filter ntuple; struct igc_syn_filter syn; struct igc_rss_filter rss; uint32_t mask; /* see IGC_FILTER_MASK_* definition */ }; #define IGC_FILTER_MASK_ETHER (1u << IGC_FILTER_TYPE_ETHERTYPE) #define IGC_FILTER_MASK_NTUPLE (1u << IGC_FILTER_TYPE_NTUPLE) #define IGC_FILTER_MASK_TCP_SYN (1u << IGC_FILTER_TYPE_SYN) #define IGC_FILTER_MASK_RSS (1u << IGC_FILTER_TYPE_HASH) #define IGC_FILTER_MASK_ALL (IGC_FILTER_MASK_ETHER | \ IGC_FILTER_MASK_NTUPLE | \ IGC_FILTER_MASK_TCP_SYN | \ IGC_FILTER_MASK_RSS) #define IGC_SET_FILTER_MASK(_filter, _mask_bits) \ ((_filter)->mask &= (_mask_bits)) #define IGC_IS_ALL_BITS_SET(_val) ((_val) == (typeof(_val))~0) #define IGC_NOT_ALL_BITS_SET(_val) ((_val) != (typeof(_val))~0) /* Parse rule attribute */ static int igc_parse_attribute(const struct rte_flow_attr *attr, struct igc_all_filter *filter, struct rte_flow_error *error) { if (!attr) return 0; if (attr->group) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_GROUP, attr, "Not support"); if (attr->egress) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_EGRESS, attr, "Not support"); if (attr->transfer) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER, attr, "Not support"); if (!attr->ingress) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_INGRESS, attr, "A rule must apply to ingress traffic"); if (attr->priority == 0) return 0; /* only n-tuple and SYN filter have priority level */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN); if (IGC_IS_ALL_BITS_SET(attr->priority)) { /* only SYN filter match this value */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_TCP_SYN); filter->syn.hig_pri = 1; return 0; } if (attr->priority > IGC_NTUPLE_MAX_PRI) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, attr, "Priority value is invalid."); if (attr->priority > 1) { /* only n-tuple filter match this value */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); /* get priority */ filter->ntuple.tuple_info.priority = (uint8_t)attr->priority; return 0; } /* get priority */ filter->ntuple.tuple_info.priority = (uint8_t)attr->priority; filter->syn.hig_pri = (uint8_t)attr->priority; return 0; } /* function type of parse pattern */ typedef int (*igc_pattern_parse)(const struct rte_flow_item *, struct igc_all_filter *, struct rte_flow_error *); static int igc_parse_pattern_void(__rte_unused const struct rte_flow_item *item, __rte_unused struct igc_all_filter *filter, __rte_unused struct rte_flow_error *error); static int igc_parse_pattern_ether(const struct rte_flow_item *item, struct igc_all_filter *filter, struct rte_flow_error *error); static int igc_parse_pattern_ip(const struct rte_flow_item *item, struct igc_all_filter *filter, struct rte_flow_error *error); static int igc_parse_pattern_ipv6(const struct rte_flow_item *item, struct igc_all_filter *filter, struct rte_flow_error *error); static int igc_parse_pattern_udp(const struct rte_flow_item *item, struct igc_all_filter *filter, struct rte_flow_error *error); static int igc_parse_pattern_tcp(const struct rte_flow_item *item, struct igc_all_filter *filter, struct rte_flow_error *error); static igc_pattern_parse pattern_parse_list[] = { [RTE_FLOW_ITEM_TYPE_VOID] = igc_parse_pattern_void, [RTE_FLOW_ITEM_TYPE_ETH] = igc_parse_pattern_ether, [RTE_FLOW_ITEM_TYPE_IPV4] = igc_parse_pattern_ip, [RTE_FLOW_ITEM_TYPE_IPV6] = igc_parse_pattern_ipv6, [RTE_FLOW_ITEM_TYPE_UDP] = igc_parse_pattern_udp, [RTE_FLOW_ITEM_TYPE_TCP] = igc_parse_pattern_tcp, }; /* Parse rule patterns */ static int igc_parse_patterns(const struct rte_flow_item patterns[], struct igc_all_filter *filter, struct rte_flow_error *error) { const struct rte_flow_item *item = patterns; if (item == NULL) { /* only RSS filter match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_RSS); return 0; } for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) { int ret; if (item->type >= RTE_DIM(pattern_parse_list)) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, item, "Not been supported"); if (item->last) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_LAST, item, "Range not been supported"); /* check pattern format is valid */ if (!!item->spec ^ !!item->mask) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, item, "Format error"); /* get the pattern type callback */ igc_pattern_parse parse_func = pattern_parse_list[item->type]; if (!parse_func) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, item, "Not been supported"); /* call the pattern type function */ ret = parse_func(item, filter, error); if (ret) return ret; /* if no filter match the pattern */ if (filter->mask == 0) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, item, "Not been supported"); } return 0; } static int igc_parse_action_queue(struct rte_eth_dev *dev, const struct rte_flow_action *act, struct igc_all_filter *filter, struct rte_flow_error *error); static int igc_parse_action_rss(struct rte_eth_dev *dev, const struct rte_flow_action *act, struct igc_all_filter *filter, struct rte_flow_error *error); /* Parse flow actions */ static int igc_parse_actions(struct rte_eth_dev *dev, const struct rte_flow_action actions[], struct igc_all_filter *filter, struct rte_flow_error *error) { const struct rte_flow_action *act = actions; int ret; if (act == NULL) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION_NUM, act, "Action is needed"); for (; act->type != RTE_FLOW_ACTION_TYPE_END; act++) { switch (act->type) { case RTE_FLOW_ACTION_TYPE_QUEUE: ret = igc_parse_action_queue(dev, act, filter, error); if (ret) return ret; break; case RTE_FLOW_ACTION_TYPE_RSS: ret = igc_parse_action_rss(dev, act, filter, error); if (ret) return ret; break; case RTE_FLOW_ACTION_TYPE_VOID: break; default: return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, act, "Not been supported"); } /* if no filter match the action */ if (filter->mask == 0) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, act, "Not been supported"); } return 0; } /* Parse a flow rule */ static int igc_parse_flow(struct rte_eth_dev *dev, const struct rte_flow_attr *attr, const struct rte_flow_item patterns[], const struct rte_flow_action actions[], struct rte_flow_error *error, struct igc_all_filter *filter) { int ret; /* clear all filters */ memset(filter, 0, sizeof(*filter)); /* set default filter mask */ filter->mask = IGC_FILTER_MASK_ALL; ret = igc_parse_attribute(attr, filter, error); if (ret) return ret; ret = igc_parse_patterns(patterns, filter, error); if (ret) return ret; ret = igc_parse_actions(dev, actions, filter, error); if (ret) return ret; /* if no or more than one filter matched this flow */ if (filter->mask == 0 || (filter->mask & (filter->mask - 1))) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, NULL, "Flow can't be recognized"); return 0; } /* Parse pattern type of void */ static int igc_parse_pattern_void(__rte_unused const struct rte_flow_item *item, __rte_unused struct igc_all_filter *filter, __rte_unused struct rte_flow_error *error) { return 0; } /* Parse pattern type of ethernet header */ static int igc_parse_pattern_ether(const struct rte_flow_item *item, struct igc_all_filter *filter, struct rte_flow_error *error) { const struct rte_flow_item_eth *spec = item->spec; const struct rte_flow_item_eth *mask = item->mask; struct igc_ethertype_filter *ether; if (mask == NULL) { /* only n-tuple and SYN filter match the pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN); return 0; } /* only ether-type filter match the pattern*/ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_ETHER); /* destination and source MAC address are not supported */ if (!rte_is_zero_ether_addr(&mask->src) || !rte_is_zero_ether_addr(&mask->dst)) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, "Only support ether-type"); /* ether-type mask bits must be all 1 */ if (IGC_NOT_ALL_BITS_SET(mask->type)) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, "Ethernet type mask bits must be all 1"); ether = &filter->ethertype; /* get ether-type */ ether->ether_type = rte_be_to_cpu_16(spec->type); /* ether-type should not be IPv4 and IPv6 */ if (ether->ether_type == RTE_ETHER_TYPE_IPV4 || ether->ether_type == RTE_ETHER_TYPE_IPV6 || ether->ether_type == 0) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, NULL, "IPv4/IPv6/0 not supported by ethertype filter"); return 0; } /* Parse pattern type of IP */ static int igc_parse_pattern_ip(const struct rte_flow_item *item, struct igc_all_filter *filter, struct rte_flow_error *error) { const struct rte_flow_item_ipv4 *spec = item->spec; const struct rte_flow_item_ipv4 *mask = item->mask; if (mask == NULL) { /* only n-tuple and SYN filter match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN); return 0; } /* only n-tuple filter match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); /* only protocol is used */ if (mask->hdr.version_ihl || mask->hdr.type_of_service || mask->hdr.total_length || mask->hdr.packet_id || mask->hdr.fragment_offset || mask->hdr.time_to_live || mask->hdr.hdr_checksum || mask->hdr.dst_addr || mask->hdr.src_addr) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, "IPv4 only support protocol"); if (mask->hdr.next_proto_id == 0) return 0; if (IGC_NOT_ALL_BITS_SET(mask->hdr.next_proto_id)) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, "IPv4 protocol mask bits must be all 0 or 1"); /* get protocol type */ filter->ntuple.tuple_info.proto_mask = 1; filter->ntuple.tuple_info.proto = spec->hdr.next_proto_id; return 0; } /* * Check ipv6 address is 0 * Return 1 if true, 0 for false. */ static inline bool igc_is_zero_ipv6_addr(const void *ipv6_addr) { const uint64_t *ddw = ipv6_addr; return ddw[0] == 0 && ddw[1] == 0; } /* Parse pattern type of IPv6 */ static int igc_parse_pattern_ipv6(const struct rte_flow_item *item, struct igc_all_filter *filter, struct rte_flow_error *error) { const struct rte_flow_item_ipv6 *spec = item->spec; const struct rte_flow_item_ipv6 *mask = item->mask; if (mask == NULL) { /* only n-tuple and syn filter match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN); return 0; } /* only n-tuple filter match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); /* only protocol is used */ if (mask->hdr.vtc_flow || mask->hdr.payload_len || mask->hdr.hop_limits || !igc_is_zero_ipv6_addr(mask->hdr.src_addr) || !igc_is_zero_ipv6_addr(mask->hdr.dst_addr)) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, item, "IPv6 only support protocol"); if (mask->hdr.proto == 0) return 0; if (IGC_NOT_ALL_BITS_SET(mask->hdr.proto)) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, "IPv6 protocol mask bits must be all 0 or 1"); /* get protocol type */ filter->ntuple.tuple_info.proto_mask = 1; filter->ntuple.tuple_info.proto = spec->hdr.proto; return 0; } /* Parse pattern type of UDP */ static int igc_parse_pattern_udp(const struct rte_flow_item *item, struct igc_all_filter *filter, struct rte_flow_error *error) { const struct rte_flow_item_udp *spec = item->spec; const struct rte_flow_item_udp *mask = item->mask; /* only n-tuple filter match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); if (mask == NULL) return 0; /* only destination port is used */ if (mask->hdr.dgram_len || mask->hdr.dgram_cksum || mask->hdr.src_port) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, "UDP only support destination port"); if (mask->hdr.dst_port == 0) return 0; if (IGC_NOT_ALL_BITS_SET(mask->hdr.dst_port)) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, "UDP port mask bits must be all 0 or 1"); /* get destination port info. */ filter->ntuple.tuple_info.dst_port_mask = 1; filter->ntuple.tuple_info.dst_port = spec->hdr.dst_port; return 0; } /* Parse pattern type of TCP */ static int igc_parse_pattern_tcp(const struct rte_flow_item *item, struct igc_all_filter *filter, struct rte_flow_error *error) { const struct rte_flow_item_tcp *spec = item->spec; const struct rte_flow_item_tcp *mask = item->mask; struct igc_ntuple_info *tuple_info = &filter->ntuple.tuple_info; if (mask == NULL) { /* only n-tuple filter match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); return 0; } /* only n-tuple and SYN filter match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN); /* only destination port and TCP flags are used */ if (mask->hdr.sent_seq || mask->hdr.recv_ack || mask->hdr.data_off || mask->hdr.rx_win || mask->hdr.cksum || mask->hdr.tcp_urp || mask->hdr.src_port) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, "TCP only support destination port and flags"); /* if destination port is used */ if (mask->hdr.dst_port) { /* only n-tuple match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); if (IGC_NOT_ALL_BITS_SET(mask->hdr.dst_port)) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, "TCP port mask bits must be all 1"); /* get destination port info. */ tuple_info->dst_port = spec->hdr.dst_port; tuple_info->dst_port_mask = 1; } /* if TCP flags are used */ if (mask->hdr.tcp_flags) { if (IGC_IS_ALL_BITS_SET(mask->hdr.tcp_flags)) { /* only n-tuple match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); /* get TCP flags */ tuple_info->tcp_flags = spec->hdr.tcp_flags; } else if (mask->hdr.tcp_flags == RTE_TCP_SYN_FLAG) { /* only TCP SYN filter match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_TCP_SYN); } else { /* no filter match this pattern */ return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, "TCP flags can't match"); } } else { /* only n-tuple match this pattern */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); } return 0; } static int igc_parse_action_queue(struct rte_eth_dev *dev, const struct rte_flow_action *act, struct igc_all_filter *filter, struct rte_flow_error *error) { uint16_t queue_idx; if (act->conf == NULL) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, "NULL pointer"); /* only ether-type, n-tuple, SYN filter match the action */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_ETHER | IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN); /* get queue index */ queue_idx = ((const struct rte_flow_action_queue *)act->conf)->index; /* check the queue index is valid */ if (queue_idx >= dev->data->nb_rx_queues) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, "Queue id is invalid"); /* get queue info. */ filter->ethertype.queue = queue_idx; filter->ntuple.queue = queue_idx; filter->syn.queue = queue_idx; return 0; } /* Parse action of RSS */ static int igc_parse_action_rss(struct rte_eth_dev *dev, const struct rte_flow_action *act, struct igc_all_filter *filter, struct rte_flow_error *error) { const struct rte_flow_action_rss *rss = act->conf; uint32_t i; if (act->conf == NULL) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, "NULL pointer"); /* only RSS match the action */ IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_RSS); /* RSS redirect table can't be zero and can't exceed 128 */ if (!rss || !rss->queue_num || rss->queue_num > IGC_RSS_RDT_SIZD) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, "No valid queues"); /* queue index can't exceed max queue index */ for (i = 0; i < rss->queue_num; i++) { if (rss->queue[i] >= dev->data->nb_rx_queues) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, "Queue id is invalid"); } /* only default RSS hash function is supported */ if (rss->func != RTE_ETH_HASH_FUNCTION_DEFAULT) return rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, "Only default RSS hash functions is supported"); if (rss->level) return rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, "Only 0 RSS encapsulation level is supported"); /* check key length is valid */ if (rss->key_len && rss->key_len != sizeof(filter->rss.key)) return rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, "RSS hash key must be exactly 40 bytes"); /* get RSS info. */ igc_rss_conf_set(&filter->rss, rss); return 0; } /** * Allocate a rte_flow from the heap * Return the pointer of the flow, or NULL for failed **/ static inline struct rte_flow * igc_alloc_flow(const void *filter, enum igc_filter_type type, size_t inbytes) { /* allocate memory, 8 bytes boundary aligned */ struct rte_flow *flow = rte_malloc("igc flow filter", sizeof(struct rte_flow) + inbytes, 8); if (flow == NULL) { PMD_DRV_LOG(ERR, "failed to allocate memory"); return NULL; } flow->filter_type = type; /* copy filter data */ memcpy(flow->filter, filter, inbytes); return flow; } /* Append a rte_flow to the list */ static inline void igc_append_flow(struct igc_flow_list *list, struct rte_flow *flow) { TAILQ_INSERT_TAIL(list, flow, node); } /** * Remove the flow and free the flow buffer * The caller should make sure the flow is really exist in the list **/ static inline void igc_remove_flow(struct igc_flow_list *list, struct rte_flow *flow) { TAILQ_REMOVE(list, flow, node); rte_free(flow); } /* Check whether the flow is really in the list or not */ static inline bool igc_is_flow_in_list(struct igc_flow_list *list, struct rte_flow *flow) { struct rte_flow *it; TAILQ_FOREACH(it, list, node) { if (it == flow) return true; } return false; } /** * Create a flow rule. * Theoretically one rule can match more than one filters. * We will let it use the filter which it hit first. * So, the sequence matters. **/ static struct rte_flow * igc_flow_create(struct rte_eth_dev *dev, const struct rte_flow_attr *attr, const struct rte_flow_item patterns[], const struct rte_flow_action actions[], struct rte_flow_error *error) { struct rte_flow *flow = NULL; struct igc_all_filter filter; int ret; ret = igc_parse_flow(dev, attr, patterns, actions, error, &filter); if (ret) return NULL; ret = -ENOMEM; switch (filter.mask) { case IGC_FILTER_MASK_ETHER: flow = igc_alloc_flow(&filter.ethertype, IGC_FILTER_TYPE_ETHERTYPE, sizeof(filter.ethertype)); if (flow) ret = igc_add_ethertype_filter(dev, &filter.ethertype); break; case IGC_FILTER_MASK_NTUPLE: /* Check n-tuple filter is valid */ if (filter.ntuple.tuple_info.dst_port_mask == 0 && filter.ntuple.tuple_info.proto_mask == 0) { rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_NONE, NULL, "Flow can't be recognized"); return NULL; } flow = igc_alloc_flow(&filter.ntuple, IGC_FILTER_TYPE_NTUPLE, sizeof(filter.ntuple)); if (flow) ret = igc_add_ntuple_filter(dev, &filter.ntuple); break; case IGC_FILTER_MASK_TCP_SYN: flow = igc_alloc_flow(&filter.syn, IGC_FILTER_TYPE_SYN, sizeof(filter.syn)); if (flow) ret = igc_set_syn_filter(dev, &filter.syn); break; case IGC_FILTER_MASK_RSS: flow = igc_alloc_flow(&filter.rss, IGC_FILTER_TYPE_HASH, sizeof(filter.rss)); if (flow) { struct igc_rss_filter *rss = (struct igc_rss_filter *)flow->filter; rss->conf.key = rss->key; rss->conf.queue = rss->queue; ret = igc_add_rss_filter(dev, &filter.rss); } break; default: rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_NONE, NULL, "Flow can't be recognized"); return NULL; } if (ret) { /* check and free the memory */ if (flow) rte_free(flow); rte_flow_error_set(error, -ret, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, "Failed to create flow."); return NULL; } /* append the flow to the tail of the list */ igc_append_flow(IGC_DEV_PRIVATE_FLOW_LIST(dev), flow); return flow; } /** * Check if the flow rule is supported by the device. * It only checks the format. Don't guarantee the rule can be programmed into * the HW. Because there can be no enough room for the rule. **/ static int igc_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr, const struct rte_flow_item patterns[], const struct rte_flow_action actions[], struct rte_flow_error *error) { struct igc_all_filter filter; int ret; ret = igc_parse_flow(dev, attr, patterns, actions, error, &filter); if (ret) return ret; switch (filter.mask) { case IGC_FILTER_MASK_NTUPLE: /* Check n-tuple filter is valid */ if (filter.ntuple.tuple_info.dst_port_mask == 0 && filter.ntuple.tuple_info.proto_mask == 0) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_NONE, NULL, "Flow can't be recognized"); break; } return 0; } /** * Disable a valid flow, the flow must be not NULL and * chained in the device flow list. **/ static int igc_disable_flow(struct rte_eth_dev *dev, struct rte_flow *flow) { int ret = 0; switch (flow->filter_type) { case IGC_FILTER_TYPE_ETHERTYPE: ret = igc_del_ethertype_filter(dev, (struct igc_ethertype_filter *)&flow->filter); break; case IGC_FILTER_TYPE_NTUPLE: ret = igc_del_ntuple_filter(dev, (struct igc_ntuple_filter *)&flow->filter); break; case IGC_FILTER_TYPE_SYN: igc_clear_syn_filter(dev); break; case IGC_FILTER_TYPE_HASH: ret = igc_del_rss_filter(dev); break; default: PMD_DRV_LOG(ERR, "Filter type (%d) not supported", flow->filter_type); ret = -EINVAL; } return ret; } /* Destroy a flow rule */ static int igc_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow, struct rte_flow_error *error) { struct igc_flow_list *list = IGC_DEV_PRIVATE_FLOW_LIST(dev); int ret; if (!flow) { PMD_DRV_LOG(ERR, "NULL flow!"); return -EINVAL; } /* check the flow is create by IGC PMD */ if (!igc_is_flow_in_list(list, flow)) { PMD_DRV_LOG(ERR, "Flow(%p) not been found!", flow); return -ENOENT; } ret = igc_disable_flow(dev, flow); if (ret) rte_flow_error_set(error, -ret, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, "Failed to destroy flow"); igc_remove_flow(list, flow); return ret; } /* Initiate device flow list header */ void igc_flow_init(struct rte_eth_dev *dev) { TAILQ_INIT(IGC_DEV_PRIVATE_FLOW_LIST(dev)); } /* Destroy all flow in the list and free memory */ int igc_flow_flush(struct rte_eth_dev *dev, __rte_unused struct rte_flow_error *error) { struct igc_flow_list *list = IGC_DEV_PRIVATE_FLOW_LIST(dev); struct rte_flow *flow; while ((flow = TAILQ_FIRST(list)) != NULL) { igc_disable_flow(dev, flow); igc_remove_flow(list, flow); } return 0; } const struct rte_flow_ops igc_flow_ops = { .validate = igc_flow_validate, .create = igc_flow_create, .destroy = igc_flow_destroy, .flush = igc_flow_flush, };