qed/qede: Add setter APIs support for RX flow classification
This patch adds support for adding and deleting rx flow classification rules. Using this user can classify RX flow constituting of TCP/UDP 4-tuples [src_ip/dst_ip and src_port/dst_port] to be steered on a given RX queue Signed-off-by: Manish Chopra <manish.chopra@cavium.com> Signed-off-by: Yuval Mintz <yuval.mintz@cavium.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									ec9b8dbd82
								
							
						
					
					
						commit
						3f2a2b8b7a
					
				| @ -954,9 +954,7 @@ static int qed_slowpath_start(struct qed_dev *cdev, | ||||
| 	struct qed_tunnel_info tunn_info; | ||||
| 	const u8 *data = NULL; | ||||
| 	struct qed_hwfn *hwfn; | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| 	struct qed_ptt *p_ptt; | ||||
| #endif | ||||
| 	int rc = -EINVAL; | ||||
| 
 | ||||
| 	if (qed_iov_wq_start(cdev)) | ||||
| @ -972,7 +970,6 @@ static int qed_slowpath_start(struct qed_dev *cdev, | ||||
| 			goto err; | ||||
| 		} | ||||
| 
 | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| 		if (cdev->num_hwfns == 1) { | ||||
| 			p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev)); | ||||
| 			if (p_ptt) { | ||||
| @ -983,7 +980,6 @@ static int qed_slowpath_start(struct qed_dev *cdev, | ||||
| 				goto err; | ||||
| 			} | ||||
| 		} | ||||
| #endif | ||||
| 	} | ||||
| 
 | ||||
| 	cdev->rx_coalesce_usecs = QED_DEFAULT_RX_USECS; | ||||
| @ -1091,12 +1087,10 @@ err: | ||||
| 	if (IS_PF(cdev)) | ||||
| 		release_firmware(cdev->firmware); | ||||
| 
 | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| 	if (IS_PF(cdev) && (cdev->num_hwfns == 1) && | ||||
| 	    QED_LEADING_HWFN(cdev)->p_arfs_ptt) | ||||
| 		qed_ptt_release(QED_LEADING_HWFN(cdev), | ||||
| 				QED_LEADING_HWFN(cdev)->p_arfs_ptt); | ||||
| #endif | ||||
| 
 | ||||
| 	qed_iov_wq_stop(cdev, false); | ||||
| 
 | ||||
| @ -1111,11 +1105,9 @@ static int qed_slowpath_stop(struct qed_dev *cdev) | ||||
| 	qed_ll2_dealloc_if(cdev); | ||||
| 
 | ||||
| 	if (IS_PF(cdev)) { | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| 		if (cdev->num_hwfns == 1) | ||||
| 			qed_ptt_release(QED_LEADING_HWFN(cdev), | ||||
| 					QED_LEADING_HWFN(cdev)->p_arfs_ptt); | ||||
| #endif | ||||
| 		qed_free_stream_mem(cdev); | ||||
| 		if (IS_QED_ETH_IF(cdev)) | ||||
| 			qed_sriov_disable(cdev, true); | ||||
|  | ||||
| @ -447,16 +447,17 @@ struct qede_fastpath { | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, | ||||
| 		       u16 rxq_index, u32 flow_id); | ||||
| #define QEDE_SP_ARFS_CONFIG	4 | ||||
| #define QEDE_SP_TASK_POLL_DELAY	(5 * HZ) | ||||
| #endif | ||||
| 
 | ||||
| void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr); | ||||
| void qede_poll_for_freeing_arfs_filters(struct qede_dev *edev); | ||||
| void qede_arfs_filter_op(void *dev, void *filter, u8 fw_rc); | ||||
| void qede_free_arfs(struct qede_dev *edev); | ||||
| int qede_alloc_arfs(struct qede_dev *edev); | ||||
| 
 | ||||
| #define QEDE_SP_ARFS_CONFIG	4 | ||||
| #define QEDE_SP_TASK_POLL_DELAY	(5 * HZ) | ||||
| #endif | ||||
| 
 | ||||
| int qede_add_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info); | ||||
| int qede_del_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info); | ||||
| int qede_get_cls_rule_entry(struct qede_dev *edev, struct ethtool_rxnfc *cmd); | ||||
| int qede_get_cls_rule_all(struct qede_dev *edev, struct ethtool_rxnfc *info, | ||||
| 			  u32 *rule_locs); | ||||
|  | ||||
| @ -1182,14 +1182,24 @@ static int qede_set_rss_flags(struct qede_dev *edev, struct ethtool_rxnfc *info) | ||||
| static int qede_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info) | ||||
| { | ||||
| 	struct qede_dev *edev = netdev_priv(dev); | ||||
| 	int rc; | ||||
| 
 | ||||
| 	switch (info->cmd) { | ||||
| 	case ETHTOOL_SRXFH: | ||||
| 		return qede_set_rss_flags(edev, info); | ||||
| 		rc = qede_set_rss_flags(edev, info); | ||||
| 		break; | ||||
| 	case ETHTOOL_SRXCLSRLINS: | ||||
| 		rc = qede_add_cls_rule(edev, info); | ||||
| 		break; | ||||
| 	case ETHTOOL_SRXCLSRLDEL: | ||||
| 		rc = qede_del_cls_rule(edev, info); | ||||
| 		break; | ||||
| 	default: | ||||
| 		DP_INFO(edev, "Command parameters not supported\n"); | ||||
| 		return -EOPNOTSUPP; | ||||
| 		rc = -EOPNOTSUPP; | ||||
| 	} | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static u32 qede_get_rxfh_indir_size(struct net_device *dev) | ||||
|  | ||||
| @ -75,6 +75,7 @@ struct qede_arfs_fltr_node { | ||||
| 	u16 next_rxq_id; | ||||
| 	bool filter_op; | ||||
| 	bool used; | ||||
| 	u8 fw_rc; | ||||
| 	struct hlist_node node; | ||||
| }; | ||||
| 
 | ||||
| @ -92,7 +93,6 @@ struct qede_arfs { | ||||
| 	bool			enable; | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| static void qede_configure_arfs_fltr(struct qede_dev *edev, | ||||
| 				     struct qede_arfs_fltr_node *n, | ||||
| 				     u16 rxq_id, bool add_fltr) | ||||
| @ -122,11 +122,56 @@ qede_free_arfs_filter(struct qede_dev *edev,  struct qede_arfs_fltr_node *fltr) | ||||
| 	kfree(fltr); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| qede_enqueue_fltr_and_config_searcher(struct qede_dev *edev, | ||||
| 				      struct qede_arfs_fltr_node *fltr, | ||||
| 				      u16 bucket_idx) | ||||
| { | ||||
| 	fltr->mapping = dma_map_single(&edev->pdev->dev, fltr->data, | ||||
| 				       fltr->buf_len, DMA_TO_DEVICE); | ||||
| 	if (dma_mapping_error(&edev->pdev->dev, fltr->mapping)) { | ||||
| 		DP_NOTICE(edev, "Failed to map DMA memory for rule\n"); | ||||
| 		qede_free_arfs_filter(edev, fltr); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	INIT_HLIST_NODE(&fltr->node); | ||||
| 	hlist_add_head(&fltr->node, | ||||
| 		       QEDE_ARFS_BUCKET_HEAD(edev, bucket_idx)); | ||||
| 	edev->arfs->filter_count++; | ||||
| 
 | ||||
| 	if (edev->arfs->filter_count == 1 && !edev->arfs->enable) { | ||||
| 		edev->ops->configure_arfs_searcher(edev->cdev, true); | ||||
| 		edev->arfs->enable = true; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| qede_dequeue_fltr_and_config_searcher(struct qede_dev *edev, | ||||
| 				      struct qede_arfs_fltr_node *fltr) | ||||
| { | ||||
| 	hlist_del(&fltr->node); | ||||
| 	dma_unmap_single(&edev->pdev->dev, fltr->mapping, | ||||
| 			 fltr->buf_len, DMA_TO_DEVICE); | ||||
| 
 | ||||
| 	qede_free_arfs_filter(edev, fltr); | ||||
| 	edev->arfs->filter_count--; | ||||
| 
 | ||||
| 	if (!edev->arfs->filter_count && edev->arfs->enable) { | ||||
| 		edev->arfs->enable = false; | ||||
| 		edev->ops->configure_arfs_searcher(edev->cdev, false); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void qede_arfs_filter_op(void *dev, void *filter, u8 fw_rc) | ||||
| { | ||||
| 	struct qede_arfs_fltr_node *fltr = filter; | ||||
| 	struct qede_dev *edev = dev; | ||||
| 
 | ||||
| 	fltr->fw_rc = fw_rc; | ||||
| 
 | ||||
| 	if (fw_rc) { | ||||
| 		DP_NOTICE(edev, | ||||
| 			  "Failed arfs filter configuration fw_rc=%d, flow_id=%d, sw_id=%d, src_port=%d, dst_port=%d, rxq=%d\n", | ||||
| @ -186,18 +231,17 @@ void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr) | ||||
| 
 | ||||
| 			if ((!test_bit(QEDE_FLTR_VALID, &fltr->state) && | ||||
| 			     !fltr->used) || free_fltr) { | ||||
| 				hlist_del(&fltr->node); | ||||
| 				dma_unmap_single(&edev->pdev->dev, | ||||
| 						 fltr->mapping, | ||||
| 						 fltr->buf_len, DMA_TO_DEVICE); | ||||
| 				qede_free_arfs_filter(edev, fltr); | ||||
| 				edev->arfs->filter_count--; | ||||
| 				qede_dequeue_fltr_and_config_searcher(edev, | ||||
| 								      fltr); | ||||
| 			} else { | ||||
| 				if ((rps_may_expire_flow(edev->ndev, | ||||
| 							 fltr->rxq_id, | ||||
| 							 fltr->flow_id, | ||||
| 							 fltr->sw_id) || del) && | ||||
| 							 !free_fltr) | ||||
| 				bool flow_exp = false; | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| 				flow_exp = rps_may_expire_flow(edev->ndev, | ||||
| 							       fltr->rxq_id, | ||||
| 							       fltr->flow_id, | ||||
| 							       fltr->sw_id); | ||||
| #endif | ||||
| 				if ((flow_exp || del) && !free_fltr) | ||||
| 					qede_configure_arfs_fltr(edev, fltr, | ||||
| 								 fltr->rxq_id, | ||||
| 								 false); | ||||
| @ -214,10 +258,12 @@ void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr) | ||||
| 			edev->arfs->enable = false; | ||||
| 			edev->ops->configure_arfs_searcher(edev->cdev, false); | ||||
| 		} | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| 	} else { | ||||
| 		set_bit(QEDE_SP_ARFS_CONFIG, &edev->sp_flags); | ||||
| 		schedule_delayed_work(&edev->sp_task, | ||||
| 				      QEDE_SP_TASK_POLL_DELAY); | ||||
| #endif | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock_bh(&edev->arfs->arfs_list_lock); | ||||
| @ -259,25 +305,26 @@ int qede_alloc_arfs(struct qede_dev *edev) | ||||
| 	spin_lock_init(&edev->arfs->arfs_list_lock); | ||||
| 
 | ||||
| 	for (i = 0; i <= QEDE_RFS_FLW_MASK; i++) | ||||
| 		INIT_HLIST_HEAD(&edev->arfs->arfs_hl_head[i]); | ||||
| 
 | ||||
| 	edev->ndev->rx_cpu_rmap = alloc_irq_cpu_rmap(QEDE_RSS_COUNT(edev)); | ||||
| 	if (!edev->ndev->rx_cpu_rmap) { | ||||
| 		vfree(edev->arfs); | ||||
| 		edev->arfs = NULL; | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 		INIT_HLIST_HEAD(QEDE_ARFS_BUCKET_HEAD(edev, i)); | ||||
| 
 | ||||
| 	edev->arfs->arfs_fltr_bmap = vzalloc(BITS_TO_LONGS(QEDE_RFS_MAX_FLTR) * | ||||
| 					     sizeof(long)); | ||||
| 	if (!edev->arfs->arfs_fltr_bmap) { | ||||
| 		free_irq_cpu_rmap(edev->ndev->rx_cpu_rmap); | ||||
| 		edev->ndev->rx_cpu_rmap = NULL; | ||||
| 		vfree(edev->arfs); | ||||
| 		edev->arfs = NULL; | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| 	edev->ndev->rx_cpu_rmap = alloc_irq_cpu_rmap(QEDE_RSS_COUNT(edev)); | ||||
| 	if (!edev->ndev->rx_cpu_rmap) { | ||||
| 		vfree(edev->arfs->arfs_fltr_bmap); | ||||
| 		edev->arfs->arfs_fltr_bmap = NULL; | ||||
| 		vfree(edev->arfs); | ||||
| 		edev->arfs = NULL; | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| #endif | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @ -286,16 +333,19 @@ void qede_free_arfs(struct qede_dev *edev) | ||||
| 	if (!edev->arfs) | ||||
| 		return; | ||||
| 
 | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| 	if (edev->ndev->rx_cpu_rmap) | ||||
| 		free_irq_cpu_rmap(edev->ndev->rx_cpu_rmap); | ||||
| 
 | ||||
| 	edev->ndev->rx_cpu_rmap = NULL; | ||||
| #endif | ||||
| 	vfree(edev->arfs->arfs_fltr_bmap); | ||||
| 	edev->arfs->arfs_fltr_bmap = NULL; | ||||
| 	vfree(edev->arfs); | ||||
| 	edev->arfs = NULL; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| static bool qede_compare_ip_addr(struct qede_arfs_fltr_node *tpos, | ||||
| 				 const struct sk_buff *skb) | ||||
| { | ||||
| @ -395,9 +445,8 @@ int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, | ||||
| 
 | ||||
| 	spin_lock_bh(&edev->arfs->arfs_list_lock); | ||||
| 
 | ||||
| 	n = qede_arfs_htbl_key_search(&edev->arfs->arfs_hl_head[tbl_idx], | ||||
| 	n = qede_arfs_htbl_key_search(QEDE_ARFS_BUCKET_HEAD(edev, tbl_idx), | ||||
| 				      skb, ports[0], ports[1], ip_proto); | ||||
| 
 | ||||
| 	if (n) { | ||||
| 		/* Filter match */ | ||||
| 		n->next_rxq_id = rxq_index; | ||||
| @ -449,23 +498,9 @@ int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, | ||||
| 	n->tuple.ip_proto = ip_proto; | ||||
| 	memcpy(n->data + ETH_HLEN, skb->data, skb_headlen(skb)); | ||||
| 
 | ||||
| 	n->mapping = dma_map_single(&edev->pdev->dev, n->data, | ||||
| 				    n->buf_len, DMA_TO_DEVICE); | ||||
| 	if (dma_mapping_error(&edev->pdev->dev, n->mapping)) { | ||||
| 		DP_NOTICE(edev, "Failed to map DMA memory for arfs\n"); | ||||
| 		qede_free_arfs_filter(edev, n); | ||||
| 		rc = -ENOMEM; | ||||
| 	rc = qede_enqueue_fltr_and_config_searcher(edev, n, tbl_idx); | ||||
| 	if (rc) | ||||
| 		goto ret_unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	INIT_HLIST_NODE(&n->node); | ||||
| 	hlist_add_head(&n->node, &edev->arfs->arfs_hl_head[tbl_idx]); | ||||
| 	edev->arfs->filter_count++; | ||||
| 
 | ||||
| 	if (edev->arfs->filter_count == 1 && !edev->arfs->enable) { | ||||
| 		edev->ops->configure_arfs_searcher(edev->cdev, true); | ||||
| 		edev->arfs->enable = true; | ||||
| 	} | ||||
| 
 | ||||
| 	qede_configure_arfs_fltr(edev, n, n->rxq_id, true); | ||||
| 
 | ||||
| @ -473,6 +508,7 @@ int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, | ||||
| 
 | ||||
| 	set_bit(QEDE_SP_ARFS_CONFIG, &edev->sp_flags); | ||||
| 	schedule_delayed_work(&edev->sp_task, 0); | ||||
| 
 | ||||
| 	return n->sw_id; | ||||
| 
 | ||||
| ret_unlock: | ||||
| @ -1277,6 +1313,38 @@ qede_get_arfs_fltr_by_loc(struct hlist_head *head, u32 location) | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
| qede_compare_user_flow_ips(struct qede_arfs_fltr_node *tpos, | ||||
| 			   struct ethtool_rx_flow_spec *fsp, | ||||
| 			   __be16 proto) | ||||
| { | ||||
| 	if (proto == htons(ETH_P_IP)) { | ||||
| 		struct ethtool_tcpip4_spec *ip; | ||||
| 
 | ||||
| 		ip = &fsp->h_u.tcp_ip4_spec; | ||||
| 
 | ||||
| 		if (tpos->tuple.src_ipv4 == ip->ip4src && | ||||
| 		    tpos->tuple.dst_ipv4 == ip->ip4dst) | ||||
| 			return true; | ||||
| 		else | ||||
| 			return false; | ||||
| 	} else { | ||||
| 		struct ethtool_tcpip6_spec *ip6; | ||||
| 		struct in6_addr *src; | ||||
| 
 | ||||
| 		ip6 = &fsp->h_u.tcp_ip6_spec; | ||||
| 		src = &tpos->tuple.src_ipv6; | ||||
| 
 | ||||
| 		if (!memcmp(src, &ip6->ip6src, sizeof(struct in6_addr)) && | ||||
| 		    !memcmp(&tpos->tuple.dst_ipv6, &ip6->ip6dst, | ||||
| 			    sizeof(struct in6_addr))) | ||||
| 			return true; | ||||
| 		else | ||||
| 			return false; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| int qede_get_cls_rule_all(struct qede_dev *edev, struct ethtool_rxnfc *info, | ||||
| 			  u32 *rule_locs) | ||||
| { | ||||
| @ -1366,6 +1434,225 @@ unlock: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| qede_validate_and_check_flow_exist(struct qede_dev *edev, | ||||
| 				   struct ethtool_rx_flow_spec *fsp, | ||||
| 				   int *min_hlen) | ||||
| { | ||||
| 	__be16 src_port = 0x0, dst_port = 0x0; | ||||
| 	struct qede_arfs_fltr_node *fltr; | ||||
| 	struct hlist_node *temp; | ||||
| 	struct hlist_head *head; | ||||
| 	__be16 eth_proto; | ||||
| 	u8 ip_proto; | ||||
| 
 | ||||
| 	if (fsp->location >= QEDE_RFS_MAX_FLTR || | ||||
| 	    fsp->ring_cookie >= QEDE_RSS_COUNT(edev)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (fsp->flow_type == TCP_V4_FLOW) { | ||||
| 		*min_hlen += sizeof(struct iphdr) + | ||||
| 				sizeof(struct tcphdr); | ||||
| 		eth_proto = htons(ETH_P_IP); | ||||
| 		ip_proto = IPPROTO_TCP; | ||||
| 	} else if (fsp->flow_type == UDP_V4_FLOW) { | ||||
| 		*min_hlen += sizeof(struct iphdr) + | ||||
| 				sizeof(struct udphdr); | ||||
| 		eth_proto = htons(ETH_P_IP); | ||||
| 		ip_proto = IPPROTO_UDP; | ||||
| 	} else if (fsp->flow_type == TCP_V6_FLOW) { | ||||
| 		*min_hlen += sizeof(struct ipv6hdr) + | ||||
| 				sizeof(struct tcphdr); | ||||
| 		eth_proto = htons(ETH_P_IPV6); | ||||
| 		ip_proto = IPPROTO_TCP; | ||||
| 	} else if (fsp->flow_type == UDP_V6_FLOW) { | ||||
| 		*min_hlen += sizeof(struct ipv6hdr) + | ||||
| 				sizeof(struct udphdr); | ||||
| 		eth_proto = htons(ETH_P_IPV6); | ||||
| 		ip_proto = IPPROTO_UDP; | ||||
| 	} else { | ||||
| 		DP_NOTICE(edev, "Unsupported flow type = 0x%x\n", | ||||
| 			  fsp->flow_type); | ||||
| 		return -EPROTONOSUPPORT; | ||||
| 	} | ||||
| 
 | ||||
| 	if (eth_proto == htons(ETH_P_IP)) { | ||||
| 		src_port = fsp->h_u.tcp_ip4_spec.psrc; | ||||
| 		dst_port = fsp->h_u.tcp_ip4_spec.pdst; | ||||
| 	} else { | ||||
| 		src_port = fsp->h_u.tcp_ip6_spec.psrc; | ||||
| 		dst_port = fsp->h_u.tcp_ip6_spec.pdst; | ||||
| 	} | ||||
| 
 | ||||
| 	head = QEDE_ARFS_BUCKET_HEAD(edev, 0); | ||||
| 	hlist_for_each_entry_safe(fltr, temp, head, node) { | ||||
| 		if ((fltr->tuple.ip_proto == ip_proto && | ||||
| 		     fltr->tuple.eth_proto == eth_proto && | ||||
| 		     qede_compare_user_flow_ips(fltr, fsp, eth_proto) && | ||||
| 		     fltr->tuple.src_port == src_port && | ||||
| 		     fltr->tuple.dst_port == dst_port) || | ||||
| 		    fltr->sw_id == fsp->location) | ||||
| 			return -EEXIST; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| qede_poll_arfs_filter_config(struct qede_dev *edev, | ||||
| 			     struct qede_arfs_fltr_node *fltr) | ||||
| { | ||||
| 	int count = QEDE_ARFS_POLL_COUNT; | ||||
| 
 | ||||
| 	while (fltr->used && count) { | ||||
| 		msleep(20); | ||||
| 		count--; | ||||
| 	} | ||||
| 
 | ||||
| 	if (count == 0 || fltr->fw_rc) { | ||||
| 		qede_dequeue_fltr_and_config_searcher(edev, fltr); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	return fltr->fw_rc; | ||||
| } | ||||
| 
 | ||||
| int qede_add_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info) | ||||
| { | ||||
| 	struct ethtool_rx_flow_spec *fsp = &info->fs; | ||||
| 	struct qede_arfs_fltr_node *n; | ||||
| 	int min_hlen = ETH_HLEN, rc; | ||||
| 	struct ethhdr *eth; | ||||
| 	struct iphdr *ip; | ||||
| 	__be16 *ports; | ||||
| 
 | ||||
| 	__qede_lock(edev); | ||||
| 
 | ||||
| 	if (!edev->arfs) { | ||||
| 		rc = -EPERM; | ||||
| 		goto unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = qede_validate_and_check_flow_exist(edev, fsp, &min_hlen); | ||||
| 	if (rc) | ||||
| 		goto unlock; | ||||
| 
 | ||||
| 	n = kzalloc(sizeof(*n), GFP_KERNEL); | ||||
| 	if (!n) { | ||||
| 		rc = -ENOMEM; | ||||
| 		goto unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	n->data = kzalloc(min_hlen, GFP_KERNEL); | ||||
| 	if (!n->data) { | ||||
| 		kfree(n); | ||||
| 		rc = -ENOMEM; | ||||
| 		goto unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	n->sw_id = fsp->location; | ||||
| 	set_bit(n->sw_id, edev->arfs->arfs_fltr_bmap); | ||||
| 	n->buf_len = min_hlen; | ||||
| 	n->rxq_id = fsp->ring_cookie; | ||||
| 	n->next_rxq_id = n->rxq_id; | ||||
| 	eth = (struct ethhdr *)n->data; | ||||
| 
 | ||||
| 	if (info->fs.flow_type == TCP_V4_FLOW || | ||||
| 	    info->fs.flow_type == UDP_V4_FLOW) { | ||||
| 		ports = (__be16 *)(n->data + ETH_HLEN + | ||||
| 					sizeof(struct iphdr)); | ||||
| 		eth->h_proto = htons(ETH_P_IP); | ||||
| 		n->tuple.eth_proto = htons(ETH_P_IP); | ||||
| 		n->tuple.src_ipv4 = info->fs.h_u.tcp_ip4_spec.ip4src; | ||||
| 		n->tuple.dst_ipv4 = info->fs.h_u.tcp_ip4_spec.ip4dst; | ||||
| 		n->tuple.src_port = info->fs.h_u.tcp_ip4_spec.psrc; | ||||
| 		n->tuple.dst_port = info->fs.h_u.tcp_ip4_spec.pdst; | ||||
| 		ports[0] = n->tuple.src_port; | ||||
| 		ports[1] = n->tuple.dst_port; | ||||
| 		ip = (struct iphdr *)(n->data + ETH_HLEN); | ||||
| 		ip->saddr = info->fs.h_u.tcp_ip4_spec.ip4src; | ||||
| 		ip->daddr = info->fs.h_u.tcp_ip4_spec.ip4dst; | ||||
| 		ip->version = 0x4; | ||||
| 		ip->ihl = 0x5; | ||||
| 
 | ||||
| 		if (info->fs.flow_type == TCP_V4_FLOW) { | ||||
| 			n->tuple.ip_proto = IPPROTO_TCP; | ||||
| 			ip->protocol = IPPROTO_TCP; | ||||
| 		} else { | ||||
| 			n->tuple.ip_proto = IPPROTO_UDP; | ||||
| 			ip->protocol = IPPROTO_UDP; | ||||
| 		} | ||||
| 		ip->tot_len = cpu_to_be16(min_hlen - ETH_HLEN); | ||||
| 	} else { | ||||
| 		struct ipv6hdr *ip6; | ||||
| 
 | ||||
| 		ip6 = (struct ipv6hdr *)(n->data + ETH_HLEN); | ||||
| 		ports = (__be16 *)(n->data + ETH_HLEN + | ||||
| 					sizeof(struct ipv6hdr)); | ||||
| 		eth->h_proto = htons(ETH_P_IPV6); | ||||
| 		n->tuple.eth_proto = htons(ETH_P_IPV6); | ||||
| 		memcpy(&n->tuple.src_ipv6, &info->fs.h_u.tcp_ip6_spec.ip6src, | ||||
| 		       sizeof(struct in6_addr)); | ||||
| 		memcpy(&n->tuple.dst_ipv6, &info->fs.h_u.tcp_ip6_spec.ip6dst, | ||||
| 		       sizeof(struct in6_addr)); | ||||
| 		n->tuple.src_port = info->fs.h_u.tcp_ip6_spec.psrc; | ||||
| 		n->tuple.dst_port = info->fs.h_u.tcp_ip6_spec.pdst; | ||||
| 		ports[0] = n->tuple.src_port; | ||||
| 		ports[1] = n->tuple.dst_port; | ||||
| 		memcpy(&ip6->saddr, &n->tuple.src_ipv6, | ||||
| 		       sizeof(struct in6_addr)); | ||||
| 		memcpy(&ip6->daddr, &n->tuple.dst_ipv6, | ||||
| 		       sizeof(struct in6_addr)); | ||||
| 		ip6->version = 0x6; | ||||
| 
 | ||||
| 		if (info->fs.flow_type == TCP_V6_FLOW) { | ||||
| 			n->tuple.ip_proto = IPPROTO_TCP; | ||||
| 			ip6->nexthdr = NEXTHDR_TCP; | ||||
| 			ip6->payload_len = cpu_to_be16(sizeof(struct tcphdr)); | ||||
| 		} else { | ||||
| 			n->tuple.ip_proto = IPPROTO_UDP; | ||||
| 			ip6->nexthdr = NEXTHDR_UDP; | ||||
| 			ip6->payload_len = cpu_to_be16(sizeof(struct udphdr)); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	rc = qede_enqueue_fltr_and_config_searcher(edev, n, 0); | ||||
| 	if (rc) | ||||
| 		goto unlock; | ||||
| 
 | ||||
| 	qede_configure_arfs_fltr(edev, n, n->rxq_id, true); | ||||
| 	rc = qede_poll_arfs_filter_config(edev, n); | ||||
| unlock: | ||||
| 	__qede_unlock(edev); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| int qede_del_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info) | ||||
| { | ||||
| 	struct ethtool_rx_flow_spec *fsp = &info->fs; | ||||
| 	struct qede_arfs_fltr_node *fltr = NULL; | ||||
| 	int rc = -EPERM; | ||||
| 
 | ||||
| 	__qede_lock(edev); | ||||
| 	if (!edev->arfs) | ||||
| 		goto unlock; | ||||
| 
 | ||||
| 	fltr = qede_get_arfs_fltr_by_loc(QEDE_ARFS_BUCKET_HEAD(edev, 0), | ||||
| 					 fsp->location); | ||||
| 	if (!fltr) | ||||
| 		goto unlock; | ||||
| 
 | ||||
| 	qede_configure_arfs_fltr(edev, fltr, fltr->rxq_id, false); | ||||
| 
 | ||||
| 	rc = qede_poll_arfs_filter_config(edev, fltr); | ||||
| 	if (rc == 0) | ||||
| 		qede_dequeue_fltr_and_config_searcher(edev, fltr); | ||||
| 
 | ||||
| unlock: | ||||
| 	__qede_unlock(edev); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| int qede_get_arfs_filter_count(struct qede_dev *edev) | ||||
| { | ||||
| 	int count = 0; | ||||
|  | ||||
| @ -873,9 +873,7 @@ static void qede_update_pf_params(struct qed_dev *cdev) | ||||
| 	 */ | ||||
| 	pf_params.eth_pf_params.num_vf_cons = 48; | ||||
| 
 | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| 	pf_params.eth_pf_params.num_arfs_filters = QEDE_RFS_MAX_FLTR; | ||||
| #endif | ||||
| 	qed_ops->common->update_pf_params(cdev, &pf_params); | ||||
| } | ||||
| 
 | ||||
| @ -1984,12 +1982,12 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode, | ||||
| 
 | ||||
| 	qede_vlan_mark_nonconfigured(edev); | ||||
| 	edev->ops->fastpath_stop(edev->cdev); | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| 
 | ||||
| 	if (!IS_VF(edev) && edev->dev_info.common.num_hwfns == 1) { | ||||
| 		qede_poll_for_freeing_arfs_filters(edev); | ||||
| 		qede_free_arfs(edev); | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	/* Release the interrupts */ | ||||
| 	qede_sync_free_irqs(edev); | ||||
| 	edev->ops->common->set_fp_int(edev->cdev, 0); | ||||
| @ -2041,13 +2039,12 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode, | ||||
| 	if (rc) | ||||
| 		goto err2; | ||||
| 
 | ||||
| #ifdef CONFIG_RFS_ACCEL | ||||
| 	if (!IS_VF(edev) && edev->dev_info.common.num_hwfns == 1) { | ||||
| 		rc = qede_alloc_arfs(edev); | ||||
| 		if (rc) | ||||
| 			DP_NOTICE(edev, "aRFS memory allocation failed\n"); | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	qede_napi_add_enable(edev); | ||||
| 	DP_INFO(edev, "Napi added and enabled\n"); | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user