i40e: Flow Director sideband accounting
This patch completes implementation of the ethtool ntuple rule management interface. It adds the get, update and delete interface reset. Change-ID: Ida7f481d9ee4e405ed91340b858eabb18a52fdb5 Signed-off-by: Joseph Gasparakis <joseph.gasparakis@intel.com> Signed-off-by: Anjali Singhai Jain <anjali.singhai@intel.com> Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com> Signed-off-by: Catherine Sullivan <catherine.sullivan@intel.com> Tested-by: Kavindya Deegala <kavindya.s.deegala@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
		
							parent
							
								
									3415e8ce0d
								
							
						
					
					
						commit
						17a73f6b14
					
				| @ -152,8 +152,18 @@ struct i40e_lump_tracking { | ||||
| }; | ||||
| 
 | ||||
| #define I40E_DEFAULT_ATR_SAMPLE_RATE	20 | ||||
| #define I40E_FDIR_MAX_RAW_PACKET_LOOKUP 512 | ||||
| struct i40e_fdir_data { | ||||
| #define I40E_FDIR_MAX_RAW_PACKET_SIZE   512 | ||||
| struct i40e_fdir_filter { | ||||
| 	struct hlist_node fdir_node; | ||||
| 	/* filter ipnut set */ | ||||
| 	u8 flow_type; | ||||
| 	u8 ip4_proto; | ||||
| 	__be32 dst_ip[4]; | ||||
| 	__be32 src_ip[4]; | ||||
| 	__be16 src_port; | ||||
| 	__be16 dst_port; | ||||
| 	__be32 sctp_v_tag; | ||||
| 	/* filter control */ | ||||
| 	u16 q_index; | ||||
| 	u8  flex_off; | ||||
| 	u8  pctype; | ||||
| @ -162,7 +172,6 @@ struct i40e_fdir_data { | ||||
| 	u8  fd_status; | ||||
| 	u16 cnt_index; | ||||
| 	u32 fd_id; | ||||
| 	u8  *raw_packet; | ||||
| }; | ||||
| 
 | ||||
| #define I40E_ETH_P_LLDP			0x88cc | ||||
| @ -210,6 +219,9 @@ struct i40e_pf { | ||||
| 	u8 atr_sample_rate; | ||||
| 	bool wol_en; | ||||
| 
 | ||||
| 	struct hlist_head fdir_filter_list; | ||||
| 	u16 fdir_pf_active_filters; | ||||
| 
 | ||||
| #ifdef CONFIG_I40E_VXLAN | ||||
| 	__be16  vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS]; | ||||
| 	u16 pending_vxlan_bitmap; | ||||
| @ -534,9 +546,10 @@ struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi); | ||||
| int i40e_fetch_switch_configuration(struct i40e_pf *pf, | ||||
| 				    bool printconfig); | ||||
| 
 | ||||
| int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data, | ||||
| int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet, | ||||
| 			     struct i40e_pf *pf, bool add); | ||||
| 
 | ||||
| int i40e_add_del_fdir(struct i40e_vsi *vsi, | ||||
| 		      struct i40e_fdir_filter *input, bool add); | ||||
| void i40e_set_ethtool_ops(struct net_device *netdev); | ||||
| struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, | ||||
| 					u8 *macaddr, s16 vlan, | ||||
|  | ||||
| @ -1663,21 +1663,22 @@ static ssize_t i40e_dbg_command_write(struct file *filp, | ||||
| 		desc = NULL; | ||||
| 	} else if ((strncmp(cmd_buf, "add fd_filter", 13) == 0) || | ||||
| 		   (strncmp(cmd_buf, "rem fd_filter", 13) == 0)) { | ||||
| 		struct i40e_fdir_data fd_data; | ||||
| 		struct i40e_fdir_filter fd_data; | ||||
| 		u16 packet_len, i, j = 0; | ||||
| 		char *asc_packet; | ||||
| 		u8 *raw_packet; | ||||
| 		bool add = false; | ||||
| 		int ret; | ||||
| 
 | ||||
| 		asc_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_LOOKUP, | ||||
| 		asc_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, | ||||
| 				     GFP_KERNEL); | ||||
| 		if (!asc_packet) | ||||
| 			goto command_write_done; | ||||
| 
 | ||||
| 		fd_data.raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_LOOKUP, | ||||
| 					     GFP_KERNEL); | ||||
| 		raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, | ||||
| 				     GFP_KERNEL); | ||||
| 
 | ||||
| 		if (!fd_data.raw_packet) { | ||||
| 		if (!raw_packet) { | ||||
| 			kfree(asc_packet); | ||||
| 			asc_packet = NULL; | ||||
| 			goto command_write_done; | ||||
| @ -1698,36 +1699,36 @@ static ssize_t i40e_dbg_command_write(struct file *filp, | ||||
| 				 cnt); | ||||
| 			kfree(asc_packet); | ||||
| 			asc_packet = NULL; | ||||
| 			kfree(fd_data.raw_packet); | ||||
| 			kfree(raw_packet); | ||||
| 			goto command_write_done; | ||||
| 		} | ||||
| 
 | ||||
| 		/* fix packet length if user entered 0 */ | ||||
| 		if (packet_len == 0) | ||||
| 			packet_len = I40E_FDIR_MAX_RAW_PACKET_LOOKUP; | ||||
| 			packet_len = I40E_FDIR_MAX_RAW_PACKET_SIZE; | ||||
| 
 | ||||
| 		/* make sure to check the max as well */ | ||||
| 		packet_len = min_t(u16, | ||||
| 				   packet_len, I40E_FDIR_MAX_RAW_PACKET_LOOKUP); | ||||
| 				   packet_len, I40E_FDIR_MAX_RAW_PACKET_SIZE); | ||||
| 
 | ||||
| 		for (i = 0; i < packet_len; i++) { | ||||
| 			sscanf(&asc_packet[j], "%2hhx ", | ||||
| 			       &fd_data.raw_packet[i]); | ||||
| 			       &raw_packet[i]); | ||||
| 			j += 3; | ||||
| 		} | ||||
| 		dev_info(&pf->pdev->dev, "FD raw packet dump\n"); | ||||
| 		print_hex_dump(KERN_INFO, "FD raw packet: ", | ||||
| 			       DUMP_PREFIX_OFFSET, 16, 1, | ||||
| 			       fd_data.raw_packet, packet_len, true); | ||||
| 		ret = i40e_program_fdir_filter(&fd_data, pf, add); | ||||
| 			       raw_packet, packet_len, true); | ||||
| 		ret = i40e_program_fdir_filter(&fd_data, raw_packet, pf, add); | ||||
| 		if (!ret) { | ||||
| 			dev_info(&pf->pdev->dev, "Filter command send Status : Success\n"); | ||||
| 		} else { | ||||
| 			dev_info(&pf->pdev->dev, | ||||
| 				 "Filter command send failed %d\n", ret); | ||||
| 		} | ||||
| 		kfree(fd_data.raw_packet); | ||||
| 		fd_data.raw_packet = NULL; | ||||
| 		kfree(raw_packet); | ||||
| 		raw_packet = NULL; | ||||
| 		kfree(asc_packet); | ||||
| 		asc_packet = NULL; | ||||
| 	} else if (strncmp(cmd_buf, "fd-atr off", 10) == 0) { | ||||
|  | ||||
| @ -62,6 +62,9 @@ static const struct i40e_stats i40e_gstrings_net_stats[] = { | ||||
| 	I40E_NETDEV_STAT(rx_crc_errors), | ||||
| }; | ||||
| 
 | ||||
| static int i40e_add_del_fdir_ethtool(struct i40e_vsi *vsi, | ||||
| 				     struct ethtool_rxnfc *cmd, bool add); | ||||
| 
 | ||||
| /* These PF_STATs might look like duplicates of some NETDEV_STATs,
 | ||||
|  * but they are separate.  This device supports Virtualization, and | ||||
|  * as such might have several netdevs supporting VMDq and FCoE going | ||||
| @ -1111,6 +1114,84 @@ static int i40e_get_rss_hash_opts(struct i40e_pf *pf, struct ethtool_rxnfc *cmd) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_get_ethtool_fdir_all - Populates the rule count of a command | ||||
|  * @pf: Pointer to the physical function struct | ||||
|  * @cmd: The command to get or set Rx flow classification rules | ||||
|  * @rule_locs: Array of used rule locations | ||||
|  * | ||||
|  * This function populates both the total and actual rule count of | ||||
|  * the ethtool flow classification command | ||||
|  * | ||||
|  * Returns 0 on success or -EMSGSIZE if entry not found | ||||
|  **/ | ||||
| static int i40e_get_ethtool_fdir_all(struct i40e_pf *pf, | ||||
| 				     struct ethtool_rxnfc *cmd, | ||||
| 				     u32 *rule_locs) | ||||
| { | ||||
| 	struct i40e_fdir_filter *rule; | ||||
| 	struct hlist_node *node2; | ||||
| 	int cnt = 0; | ||||
| 
 | ||||
| 	/* report total rule count */ | ||||
| 	cmd->data = pf->hw.fdir_shared_filter_count + | ||||
| 		    pf->fdir_pf_filter_count; | ||||
| 
 | ||||
| 	hlist_for_each_entry_safe(rule, node2, | ||||
| 				  &pf->fdir_filter_list, fdir_node) { | ||||
| 		if (cnt == cmd->rule_cnt) | ||||
| 			return -EMSGSIZE; | ||||
| 
 | ||||
| 		rule_locs[cnt] = rule->fd_id; | ||||
| 		cnt++; | ||||
| 	} | ||||
| 
 | ||||
| 	cmd->rule_cnt = cnt; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_get_ethtool_fdir_entry - Look up a filter based on Rx flow | ||||
|  * @pf: Pointer to the physical function struct | ||||
|  * @cmd: The command to get or set Rx flow classification rules | ||||
|  * | ||||
|  * This function looks up a filter based on the Rx flow classification | ||||
|  * command and fills the flow spec info for it if found | ||||
|  * | ||||
|  * Returns 0 on success or -EINVAL if filter not found | ||||
|  **/ | ||||
| static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf, | ||||
| 				       struct ethtool_rxnfc *cmd) | ||||
| { | ||||
| 	struct ethtool_rx_flow_spec *fsp = | ||||
| 			(struct ethtool_rx_flow_spec *)&cmd->fs; | ||||
| 	struct i40e_fdir_filter *rule = NULL; | ||||
| 	struct hlist_node *node2; | ||||
| 
 | ||||
| 	/* report total rule count */ | ||||
| 	cmd->data = pf->hw.fdir_shared_filter_count + | ||||
| 		    pf->fdir_pf_filter_count; | ||||
| 
 | ||||
| 	hlist_for_each_entry_safe(rule, node2, | ||||
| 				  &pf->fdir_filter_list, fdir_node) { | ||||
| 		if (fsp->location <= rule->fd_id) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!rule || fsp->location != rule->fd_id) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	fsp->flow_type = rule->flow_type; | ||||
| 	fsp->h_u.tcp_ip4_spec.psrc = rule->src_port; | ||||
| 	fsp->h_u.tcp_ip4_spec.pdst = rule->dst_port; | ||||
| 	fsp->h_u.tcp_ip4_spec.ip4src = rule->src_ip[0]; | ||||
| 	fsp->h_u.tcp_ip4_spec.ip4dst = rule->dst_ip[0]; | ||||
| 	fsp->ring_cookie = rule->q_index; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_get_rxnfc - command to get RX flow classification rules | ||||
|  * @netdev: network interface device structure | ||||
| @ -1135,15 +1216,15 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, | ||||
| 		ret = i40e_get_rss_hash_opts(pf, cmd); | ||||
| 		break; | ||||
| 	case ETHTOOL_GRXCLSRLCNT: | ||||
| 		cmd->rule_cnt = 10; | ||||
| 		cmd->rule_cnt = pf->fdir_pf_active_filters; | ||||
| 		ret = 0; | ||||
| 		break; | ||||
| 	case ETHTOOL_GRXCLSRULE: | ||||
| 		ret = 0; | ||||
| 		ret = i40e_get_ethtool_fdir_entry(pf, cmd); | ||||
| 		break; | ||||
| 	case ETHTOOL_GRXCLSRLALL: | ||||
| 		cmd->data = 500; | ||||
| 		ret = 0; | ||||
| 		ret = i40e_get_ethtool_fdir_all(pf, cmd, rule_locs); | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| @ -1274,289 +1355,158 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #define IP_HEADER_OFFSET 14 | ||||
| #define I40E_UDPIP_DUMMY_PACKET_LEN 42 | ||||
| /**
 | ||||
|  * i40e_add_del_fdir_udpv4 - Add/Remove UDPv4 Flow Director filters for | ||||
|  * a specific flow spec | ||||
|  * @vsi: pointer to the targeted VSI | ||||
|  * @fd_data: the flow director data required from the FDir descriptor | ||||
|  * @ethtool_rx_flow_spec: the flow spec | ||||
|  * @add: true adds a filter, false removes it | ||||
|  * i40e_update_ethtool_fdir_entry - Updates the fdir filter entry | ||||
|  * @vsi: Pointer to the targeted VSI | ||||
|  * @input: The filter to update or NULL to indicate deletion | ||||
|  * @sw_idx: Software index to the filter | ||||
|  * @cmd: The command to get or set Rx flow classification rules | ||||
|  * | ||||
|  * Returns 0 if the filters were successfully added or removed | ||||
|  * This function updates (or deletes) a Flow Director entry from | ||||
|  * the hlist of the corresponding PF | ||||
|  * | ||||
|  * Returns 0 on success | ||||
|  **/ | ||||
| static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, | ||||
| 				   struct i40e_fdir_data *fd_data, | ||||
| 				   struct ethtool_rx_flow_spec *fsp, bool add) | ||||
| static int i40e_update_ethtool_fdir_entry(struct i40e_vsi *vsi, | ||||
| 					  struct i40e_fdir_filter *input, | ||||
| 					  u16 sw_idx, | ||||
| 					  struct ethtool_rxnfc *cmd) | ||||
| { | ||||
| 	struct i40e_fdir_filter *rule, *parent; | ||||
| 	struct i40e_pf *pf = vsi->back; | ||||
| 	struct udphdr *udp; | ||||
| 	struct iphdr *ip; | ||||
| 	bool err = false; | ||||
| 	int ret; | ||||
| 	int i; | ||||
| 	char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0, | ||||
| 			 0x45, 0, 0, 0x1c, 0, 0, 0x40, 0, 0x40, 0x11, | ||||
| 			 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 			 0, 0, 0, 0, 0, 0, 0, 0}; | ||||
| 	struct hlist_node *node2; | ||||
| 	int err = -EINVAL; | ||||
| 
 | ||||
| 	memcpy(fd_data->raw_packet, packet, I40E_UDPIP_DUMMY_PACKET_LEN); | ||||
| 	parent = NULL; | ||||
| 	rule = NULL; | ||||
| 
 | ||||
| 	ip = (struct iphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET); | ||||
| 	udp = (struct udphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET | ||||
| 	      + sizeof(struct iphdr)); | ||||
| 
 | ||||
| 	ip->saddr = fsp->h_u.tcp_ip4_spec.ip4src; | ||||
| 	ip->daddr = fsp->h_u.tcp_ip4_spec.ip4dst; | ||||
| 	udp->source = fsp->h_u.tcp_ip4_spec.psrc; | ||||
| 	udp->dest = fsp->h_u.tcp_ip4_spec.pdst; | ||||
| 
 | ||||
| 	for (i = I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP; | ||||
| 	     i <= I40E_FILTER_PCTYPE_NONF_IPV4_UDP; i++) { | ||||
| 		fd_data->pctype = i; | ||||
| 		ret = i40e_program_fdir_filter(fd_data, pf, add); | ||||
| 
 | ||||
| 		if (ret) { | ||||
| 			dev_info(&pf->pdev->dev, | ||||
| 				 "Filter command send failed for PCTYPE %d (ret = %d)\n", | ||||
| 				 fd_data->pctype, ret); | ||||
| 			err = true; | ||||
| 		} else { | ||||
| 			dev_info(&pf->pdev->dev, | ||||
| 				 "Filter OK for PCTYPE %d (ret = %d)\n", | ||||
| 				 fd_data->pctype, ret); | ||||
| 		} | ||||
| 	hlist_for_each_entry_safe(rule, node2, | ||||
| 				  &pf->fdir_filter_list, fdir_node) { | ||||
| 		/* hash found, or no matching entry */ | ||||
| 		if (rule->fd_id >= sw_idx) | ||||
| 			break; | ||||
| 		parent = rule; | ||||
| 	} | ||||
| 
 | ||||
| 	return err ? -EOPNOTSUPP : 0; | ||||
| 	/* if there is an old rule occupying our place remove it */ | ||||
| 	if (rule && (rule->fd_id == sw_idx)) { | ||||
| 		if (!input || (rule->fd_id != input->fd_id)) { | ||||
| 			cmd->fs.flow_type = rule->flow_type; | ||||
| 			err = i40e_add_del_fdir_ethtool(vsi, cmd, false); | ||||
| 		} | ||||
| 
 | ||||
| 		hlist_del(&rule->fdir_node); | ||||
| 		kfree(rule); | ||||
| 		pf->fdir_pf_active_filters--; | ||||
| 	} | ||||
| 
 | ||||
| 	/* If no input this was a delete, err should be 0 if a rule was
 | ||||
| 	 * successfully found and removed from the list else -EINVAL | ||||
| 	 */ | ||||
| 	if (!input) | ||||
| 		return err; | ||||
| 
 | ||||
| 	/* initialize node and set software index */ | ||||
| 	INIT_HLIST_NODE(&input->fdir_node); | ||||
| 
 | ||||
| 	/* add filter to the list */ | ||||
| 	if (parent) | ||||
| 		hlist_add_after(&parent->fdir_node, &input->fdir_node); | ||||
| 	else | ||||
| 		hlist_add_head(&input->fdir_node, | ||||
| 			       &pf->fdir_filter_list); | ||||
| 
 | ||||
| 	/* update counts */ | ||||
| 	pf->fdir_pf_active_filters++; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #define I40E_TCPIP_DUMMY_PACKET_LEN 54 | ||||
| /**
 | ||||
|  * i40e_add_del_fdir_tcpv4 - Add/Remove TCPv4 Flow Director filters for | ||||
|  * a specific flow spec | ||||
|  * @vsi: pointer to the targeted VSI | ||||
|  * @fd_data: the flow director data required from the FDir descriptor | ||||
|  * @ethtool_rx_flow_spec: the flow spec | ||||
|  * @add: true adds a filter, false removes it | ||||
|  * i40e_del_fdir_entry - Deletes a Flow Director filter entry | ||||
|  * @vsi: Pointer to the targeted VSI | ||||
|  * @cmd: The command to get or set Rx flow classification rules | ||||
|  * | ||||
|  * Returns 0 if the filters were successfully added or removed | ||||
|  **/ | ||||
| static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, | ||||
| 				   struct i40e_fdir_data *fd_data, | ||||
| 				   struct ethtool_rx_flow_spec *fsp, bool add) | ||||
|  * The function removes a Flow Director filter entry from the | ||||
|  * hlist of the corresponding PF | ||||
|  * | ||||
|  * Returns 0 on success | ||||
|  */ | ||||
| static int i40e_del_fdir_entry(struct i40e_vsi *vsi, | ||||
| 			       struct ethtool_rxnfc *cmd) | ||||
| { | ||||
| 	struct ethtool_rx_flow_spec *fsp = | ||||
| 		(struct ethtool_rx_flow_spec *)&cmd->fs; | ||||
| 	struct i40e_pf *pf = vsi->back; | ||||
| 	struct tcphdr *tcp; | ||||
| 	struct iphdr *ip; | ||||
| 	bool err = false; | ||||
| 	int ret; | ||||
| 	/* Dummy packet */ | ||||
| 	char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0, | ||||
| 			 0x45, 0, 0, 0x28, 0, 0, 0x40, 0, 0x40, 0x6, | ||||
| 			 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 			 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 			 0x80, 0x11, 0x0, 0x72, 0, 0, 0, 0}; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	memcpy(fd_data->raw_packet, packet, I40E_TCPIP_DUMMY_PACKET_LEN); | ||||
| 	ret = i40e_update_ethtool_fdir_entry(vsi, NULL, fsp->location, cmd); | ||||
| 
 | ||||
| 	ip = (struct iphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET); | ||||
| 	tcp = (struct tcphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET | ||||
| 	      + sizeof(struct iphdr)); | ||||
| 
 | ||||
| 	ip->daddr = fsp->h_u.tcp_ip4_spec.ip4dst; | ||||
| 	tcp->dest = fsp->h_u.tcp_ip4_spec.pdst; | ||||
| 	ip->saddr = fsp->h_u.tcp_ip4_spec.ip4src; | ||||
| 	tcp->source = fsp->h_u.tcp_ip4_spec.psrc; | ||||
| 
 | ||||
| 	if (add) { | ||||
| 		if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) { | ||||
| 			dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n"); | ||||
| 			pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN; | ||||
| 	ret = i40e_program_fdir_filter(fd_data, pf, add); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| 		dev_info(&pf->pdev->dev, | ||||
| 			 "Filter command send failed for PCTYPE %d (ret = %d)\n", | ||||
| 			 fd_data->pctype, ret); | ||||
| 		err = true; | ||||
| 	} else { | ||||
| 		dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n", | ||||
| 			 fd_data->pctype, ret); | ||||
| 	} | ||||
| 
 | ||||
| 	fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; | ||||
| 
 | ||||
| 	ret = i40e_program_fdir_filter(fd_data, pf, add); | ||||
| 	if (ret) { | ||||
| 		dev_info(&pf->pdev->dev, | ||||
| 			 "Filter command send failed for PCTYPE %d (ret = %d)\n", | ||||
| 			 fd_data->pctype, ret); | ||||
| 		err = true; | ||||
| 	} else { | ||||
| 		dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n", | ||||
| 			  fd_data->pctype, ret); | ||||
| 	} | ||||
| 
 | ||||
| 	return err ? -EOPNOTSUPP : 0; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_add_del_fdir_sctpv4 - Add/Remove SCTPv4 Flow Director filters for | ||||
|  * a specific flow spec | ||||
|  * @vsi: pointer to the targeted VSI | ||||
|  * @fd_data: the flow director data required from the FDir descriptor | ||||
|  * @ethtool_rx_flow_spec: the flow spec | ||||
|  * @add: true adds a filter, false removes it | ||||
|  * | ||||
|  * Returns 0 if the filters were successfully added or removed | ||||
|  **/ | ||||
| static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi, | ||||
| 				    struct i40e_fdir_data *fd_data, | ||||
| 				    struct ethtool_rx_flow_spec *fsp, bool add) | ||||
| { | ||||
| 	return -EOPNOTSUPP; | ||||
| } | ||||
| 
 | ||||
| #define I40E_IP_DUMMY_PACKET_LEN 34 | ||||
| /**
 | ||||
|  * i40e_add_del_fdir_ipv4 - Add/Remove IPv4 Flow Director filters for | ||||
|  * a specific flow spec | ||||
|  * @vsi: pointer to the targeted VSI | ||||
|  * @fd_data: the flow director data required for the FDir descriptor | ||||
|  * @fsp: the ethtool flow spec | ||||
|  * @add: true adds a filter, false removes it | ||||
|  * | ||||
|  * Returns 0 if the filters were successfully added or removed | ||||
|  **/ | ||||
| static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi, | ||||
| 				  struct i40e_fdir_data *fd_data, | ||||
| 				  struct ethtool_rx_flow_spec *fsp, bool add) | ||||
| { | ||||
| 	struct i40e_pf *pf = vsi->back; | ||||
| 	struct iphdr *ip; | ||||
| 	bool err = false; | ||||
| 	int ret; | ||||
| 	int i; | ||||
| 	char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0, | ||||
| 			 0x45, 0, 0, 0x14, 0, 0, 0x40, 0, 0x40, 0x10, | ||||
| 			 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | ||||
| 
 | ||||
| 	memcpy(fd_data->raw_packet, packet, I40E_IP_DUMMY_PACKET_LEN); | ||||
| 	ip = (struct iphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET); | ||||
| 
 | ||||
| 	ip->saddr = fsp->h_u.usr_ip4_spec.ip4src; | ||||
| 	ip->daddr = fsp->h_u.usr_ip4_spec.ip4dst; | ||||
| 	ip->protocol = fsp->h_u.usr_ip4_spec.proto; | ||||
| 
 | ||||
| 	for (i = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER; | ||||
| 	     i <= I40E_FILTER_PCTYPE_FRAG_IPV4;	i++) { | ||||
| 		fd_data->pctype = i; | ||||
| 		ret = i40e_program_fdir_filter(fd_data, pf, add); | ||||
| 
 | ||||
| 		if (ret) { | ||||
| 			dev_info(&pf->pdev->dev, | ||||
| 				 "Filter command send failed for PCTYPE %d (ret = %d)\n", | ||||
| 				 fd_data->pctype, ret); | ||||
| 			err = true; | ||||
| 		} else { | ||||
| 			dev_info(&pf->pdev->dev, | ||||
| 				 "Filter OK for PCTYPE %d (ret = %d)\n", | ||||
| 				 fd_data->pctype, ret); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return err ? -EOPNOTSUPP : 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_add_del_fdir_ethtool - Add/Remove Flow Director filters for | ||||
|  * a specific flow spec based on their protocol | ||||
|  * i40e_add_del_fdir_ethtool - Add/Remove Flow Director filters | ||||
|  * @vsi: pointer to the targeted VSI | ||||
|  * @cmd: command to get or set RX flow classification rules | ||||
|  * @add: true adds a filter, false removes it | ||||
|  * | ||||
|  * Returns 0 if the filters were successfully added or removed | ||||
|  * Add/Remove Flow Director filters for a specific flow spec based on their | ||||
|  * protocol.  Returns 0 if the filters were successfully added or removed. | ||||
|  **/ | ||||
| static int i40e_add_del_fdir_ethtool(struct i40e_vsi *vsi, | ||||
| 			struct ethtool_rxnfc *cmd, bool add) | ||||
| 				     struct ethtool_rxnfc *cmd, bool add) | ||||
| { | ||||
| 	struct i40e_fdir_data fd_data; | ||||
| 	int ret = -EINVAL; | ||||
| 	struct ethtool_rx_flow_spec *fsp; | ||||
| 	struct i40e_fdir_filter *input; | ||||
| 	struct i40e_pf *pf; | ||||
| 	struct ethtool_rx_flow_spec *fsp = | ||||
| 		(struct ethtool_rx_flow_spec *)&cmd->fs; | ||||
| 	int ret = -EINVAL; | ||||
| 
 | ||||
| 	if (!vsi) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	fsp = (struct ethtool_rx_flow_spec *)&cmd->fs; | ||||
| 	pf = vsi->back; | ||||
| 
 | ||||
| 	if ((fsp->ring_cookie != RX_CLS_FLOW_DISC) && | ||||
| 	    (fsp->ring_cookie >= vsi->num_queue_pairs)) | ||||
| 	if (fsp->location >= (pf->hw.func_caps.fd_filters_best_effort + | ||||
| 			      pf->hw.func_caps.fd_filters_guaranteed)) { | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((fsp->ring_cookie >= vsi->num_queue_pairs) && add) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/* Populate the Flow Director that we have at the moment
 | ||||
| 	 * and allocate the raw packet buffer for the calling functions | ||||
| 	 */ | ||||
| 	fd_data.raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_LOOKUP, | ||||
| 				     GFP_KERNEL); | ||||
| 	input = kzalloc(sizeof(*input), GFP_KERNEL); | ||||
| 
 | ||||
| 	if (!fd_data.raw_packet) { | ||||
| 		dev_info(&pf->pdev->dev, "Could not allocate memory\n"); | ||||
| 	if (!input) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	input->fd_id = fsp->location; | ||||
| 
 | ||||
| 	input->q_index = fsp->ring_cookie; | ||||
| 	input->flex_off = 0; | ||||
| 	input->pctype = 0; | ||||
| 	input->dest_vsi = vsi->id; | ||||
| 	input->dest_ctl = I40E_FILTER_PROGRAM_DESC_DEST_DIRECT_PACKET_QINDEX; | ||||
| 	input->fd_status = I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID; | ||||
| 	input->cnt_index = 0; | ||||
| 	input->flow_type = fsp->flow_type; | ||||
| 	input->ip4_proto = fsp->h_u.usr_ip4_spec.proto; | ||||
| 	input->src_port = fsp->h_u.tcp_ip4_spec.psrc; | ||||
| 	input->dst_port = fsp->h_u.tcp_ip4_spec.pdst; | ||||
| 	input->src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src; | ||||
| 	input->dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst; | ||||
| 
 | ||||
| 	ret = i40e_add_del_fdir(vsi, input, add); | ||||
| 	if (ret) { | ||||
| 		kfree(input); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	fd_data.q_index = fsp->ring_cookie; | ||||
| 	fd_data.flex_off = 0; | ||||
| 	fd_data.pctype = 0; | ||||
| 	fd_data.dest_vsi = vsi->id; | ||||
| 	fd_data.dest_ctl = I40E_FILTER_PROGRAM_DESC_DEST_DIRECT_PACKET_QINDEX; | ||||
| 	fd_data.fd_status = I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID; | ||||
| 	fd_data.cnt_index = 0; | ||||
| 	fd_data.fd_id = 0; | ||||
| 
 | ||||
| 	switch (fsp->flow_type & ~FLOW_EXT) { | ||||
| 	case TCP_V4_FLOW: | ||||
| 		ret = i40e_add_del_fdir_tcpv4(vsi, &fd_data, fsp, add); | ||||
| 		break; | ||||
| 	case UDP_V4_FLOW: | ||||
| 		ret = i40e_add_del_fdir_udpv4(vsi, &fd_data, fsp, add); | ||||
| 		break; | ||||
| 	case SCTP_V4_FLOW: | ||||
| 		ret = i40e_add_del_fdir_sctpv4(vsi, &fd_data, fsp, add); | ||||
| 		break; | ||||
| 	case IPV4_FLOW: | ||||
| 		ret = i40e_add_del_fdir_ipv4(vsi, &fd_data, fsp, add); | ||||
| 		break; | ||||
| 	case IP_USER_FLOW: | ||||
| 		switch (fsp->h_u.usr_ip4_spec.proto) { | ||||
| 		case IPPROTO_TCP: | ||||
| 			ret = i40e_add_del_fdir_tcpv4(vsi, &fd_data, fsp, add); | ||||
| 			break; | ||||
| 		case IPPROTO_UDP: | ||||
| 			ret = i40e_add_del_fdir_udpv4(vsi, &fd_data, fsp, add); | ||||
| 			break; | ||||
| 		case IPPROTO_SCTP: | ||||
| 			ret = i40e_add_del_fdir_sctpv4(vsi, &fd_data, fsp, add); | ||||
| 			break; | ||||
| 		default: | ||||
| 			ret = i40e_add_del_fdir_ipv4(vsi, &fd_data, fsp, add); | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		dev_info(&pf->pdev->dev, "Could not specify spec type\n"); | ||||
| 		ret = -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	kfree(fd_data.raw_packet); | ||||
| 	fd_data.raw_packet = NULL; | ||||
| 	if (!ret && add) | ||||
| 		i40e_update_ethtool_fdir_entry(vsi, input, fsp->location, NULL); | ||||
| 	else | ||||
| 		kfree(input); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| @ -1583,7 +1533,7 @@ static int i40e_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) | ||||
| 		ret = i40e_add_del_fdir_ethtool(vsi, cmd, true); | ||||
| 		break; | ||||
| 	case ETHTOOL_SRXCLSRLDEL: | ||||
| 		ret = i40e_add_del_fdir_ethtool(vsi, cmd, false); | ||||
| 		ret = i40e_del_fdir_entry(vsi, cmd); | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
|  | ||||
| @ -2420,6 +2420,25 @@ static void i40e_set_vsi_rx_mode(struct i40e_vsi *vsi) | ||||
| 		i40e_set_rx_mode(vsi->netdev); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_fdir_filter_restore - Restore the Sideband Flow Director filters | ||||
|  * @vsi: Pointer to the targeted VSI | ||||
|  * | ||||
|  * This function replays the hlist on the hw where all the SB Flow Director | ||||
|  * filters were saved. | ||||
|  **/ | ||||
| static void i40e_fdir_filter_restore(struct i40e_vsi *vsi) | ||||
| { | ||||
| 	struct i40e_fdir_filter *filter; | ||||
| 	struct i40e_pf *pf = vsi->back; | ||||
| 	struct hlist_node *node; | ||||
| 
 | ||||
| 	hlist_for_each_entry_safe(filter, node, | ||||
| 				  &pf->fdir_filter_list, fdir_node) { | ||||
| 		i40e_add_del_fdir(vsi, filter, true); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_vsi_configure - Set up the VSI for action | ||||
|  * @vsi: the VSI being configured | ||||
| @ -2431,6 +2450,8 @@ static int i40e_vsi_configure(struct i40e_vsi *vsi) | ||||
| 	i40e_set_vsi_rx_mode(vsi); | ||||
| 	i40e_restore_vlan(vsi); | ||||
| 	i40e_vsi_config_dcb_rings(vsi); | ||||
| 	if (vsi->type == I40E_VSI_FDIR) | ||||
| 		i40e_fdir_filter_restore(vsi); | ||||
| 	err = i40e_vsi_configure_tx(vsi); | ||||
| 	if (!err) | ||||
| 		err = i40e_vsi_configure_rx(vsi); | ||||
| @ -4267,6 +4288,26 @@ err_setup_tx: | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_fdir_filter_exit - Cleans up the Flow Director accounting | ||||
|  * @pf: Pointer to pf | ||||
|  * | ||||
|  * This function destroys the hlist where all the Flow Director | ||||
|  * filters were saved. | ||||
|  **/ | ||||
| static void i40e_fdir_filter_exit(struct i40e_pf *pf) | ||||
| { | ||||
| 	struct i40e_fdir_filter *filter; | ||||
| 	struct hlist_node *node2; | ||||
| 
 | ||||
| 	hlist_for_each_entry_safe(filter, node2, | ||||
| 				  &pf->fdir_filter_list, fdir_node) { | ||||
| 		hlist_del(&filter->fdir_node); | ||||
| 		kfree(filter); | ||||
| 	} | ||||
| 	pf->fdir_pf_active_filters = 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_close - Disables a network interface | ||||
|  * @netdev: network interface device structure | ||||
| @ -5131,9 +5172,9 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf) | ||||
| 		err = i40e_up_complete(vsi); | ||||
| 		if (err) | ||||
| 			goto err_up_complete; | ||||
| 		clear_bit(__I40E_NEEDS_RESTART, &vsi->state); | ||||
| 	} | ||||
| 
 | ||||
| 	clear_bit(__I40E_NEEDS_RESTART, &vsi->state); | ||||
| 	return; | ||||
| 
 | ||||
| err_up_complete: | ||||
| @ -5156,6 +5197,7 @@ static void i40e_fdir_teardown(struct i40e_pf *pf) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	i40e_fdir_filter_exit(pf); | ||||
| 	for (i = 0; i < pf->hw.func_caps.num_vsis; i++) { | ||||
| 		if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) { | ||||
| 			i40e_vsi_release(pf->vsi[i]); | ||||
|  | ||||
| @ -39,11 +39,12 @@ static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, | ||||
| #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) | ||||
| /**
 | ||||
|  * i40e_program_fdir_filter - Program a Flow Director filter | ||||
|  * @fdir_input: Packet data that will be filter parameters | ||||
|  * @fdir_data: Packet data that will be filter parameters | ||||
|  * @raw_packet: the pre-allocated packet buffer for FDir | ||||
|  * @pf: The pf pointer | ||||
|  * @add: True for add/update, False for remove | ||||
|  **/ | ||||
| int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data, | ||||
| int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet, | ||||
| 			     struct i40e_pf *pf, bool add) | ||||
| { | ||||
| 	struct i40e_filter_program_desc *fdir_desc; | ||||
| @ -68,8 +69,8 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data, | ||||
| 	tx_ring = vsi->tx_rings[0]; | ||||
| 	dev = tx_ring->dev; | ||||
| 
 | ||||
| 	dma = dma_map_single(dev, fdir_data->raw_packet, | ||||
| 			     I40E_FDIR_MAX_RAW_PACKET_LOOKUP, DMA_TO_DEVICE); | ||||
| 	dma = dma_map_single(dev, raw_packet, | ||||
| 			     I40E_FDIR_MAX_RAW_PACKET_SIZE, DMA_TO_DEVICE); | ||||
| 	if (dma_mapping_error(dev, dma)) | ||||
| 		goto dma_fail; | ||||
| 
 | ||||
| @ -132,14 +133,14 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data, | ||||
| 	tx_ring->next_to_use = (i + 1 < tx_ring->count) ? i + 1 : 0; | ||||
| 
 | ||||
| 	/* record length, and DMA address */ | ||||
| 	dma_unmap_len_set(tx_buf, len, I40E_FDIR_MAX_RAW_PACKET_LOOKUP); | ||||
| 	dma_unmap_len_set(tx_buf, len, I40E_FDIR_MAX_RAW_PACKET_SIZE); | ||||
| 	dma_unmap_addr_set(tx_buf, dma, dma); | ||||
| 
 | ||||
| 	tx_desc->buffer_addr = cpu_to_le64(dma); | ||||
| 	td_cmd = I40E_TXD_CMD | I40E_TX_DESC_CMD_DUMMY; | ||||
| 
 | ||||
| 	tx_desc->cmd_type_offset_bsz = | ||||
| 		build_ctob(td_cmd, 0, I40E_FDIR_MAX_RAW_PACKET_LOOKUP, 0); | ||||
| 		build_ctob(td_cmd, 0, I40E_FDIR_MAX_RAW_PACKET_SIZE, 0); | ||||
| 
 | ||||
| 	/* set the timestamp */ | ||||
| 	tx_buf->time_stamp = jiffies; | ||||
| @ -161,6 +162,270 @@ dma_fail: | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| #define IP_HEADER_OFFSET 14 | ||||
| #define I40E_UDPIP_DUMMY_PACKET_LEN 42 | ||||
| /**
 | ||||
|  * i40e_add_del_fdir_udpv4 - Add/Remove UDPv4 filters | ||||
|  * @vsi: pointer to the targeted VSI | ||||
|  * @fd_data: the flow director data required for the FDir descriptor | ||||
|  * @raw_packet: the pre-allocated packet buffer for FDir | ||||
|  * @add: true adds a filter, false removes it | ||||
|  * | ||||
|  * Returns 0 if the filters were successfully added or removed | ||||
|  **/ | ||||
| static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, | ||||
| 				   struct i40e_fdir_filter *fd_data, | ||||
| 				   u8 *raw_packet, bool add) | ||||
| { | ||||
| 	struct i40e_pf *pf = vsi->back; | ||||
| 	struct udphdr *udp; | ||||
| 	struct iphdr *ip; | ||||
| 	bool err = false; | ||||
| 	int ret; | ||||
| 	int i; | ||||
| 	static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0, | ||||
| 		0x45, 0, 0, 0x1c, 0, 0, 0x40, 0, 0x40, 0x11, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | ||||
| 
 | ||||
| 	memcpy(raw_packet, packet, I40E_UDPIP_DUMMY_PACKET_LEN); | ||||
| 
 | ||||
| 	ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET); | ||||
| 	udp = (struct udphdr *)(raw_packet + IP_HEADER_OFFSET | ||||
| 	      + sizeof(struct iphdr)); | ||||
| 
 | ||||
| 	ip->daddr = fd_data->dst_ip[0]; | ||||
| 	udp->dest = fd_data->dst_port; | ||||
| 	ip->saddr = fd_data->src_ip[0]; | ||||
| 	udp->source = fd_data->src_port; | ||||
| 
 | ||||
| 	for (i = I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP; | ||||
| 	     i <= I40E_FILTER_PCTYPE_NONF_IPV4_UDP; i++) { | ||||
| 		fd_data->pctype = i; | ||||
| 		ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); | ||||
| 
 | ||||
| 		if (ret) { | ||||
| 			dev_info(&pf->pdev->dev, | ||||
| 				 "Filter command send failed for PCTYPE %d (ret = %d)\n", | ||||
| 				 fd_data->pctype, ret); | ||||
| 			err = true; | ||||
| 		} else { | ||||
| 			dev_info(&pf->pdev->dev, | ||||
| 				 "Filter OK for PCTYPE %d (ret = %d)\n", | ||||
| 				 fd_data->pctype, ret); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return err ? -EOPNOTSUPP : 0; | ||||
| } | ||||
| 
 | ||||
| #define I40E_TCPIP_DUMMY_PACKET_LEN 54 | ||||
| /**
 | ||||
|  * i40e_add_del_fdir_tcpv4 - Add/Remove TCPv4 filters | ||||
|  * @vsi: pointer to the targeted VSI | ||||
|  * @fd_data: the flow director data required for the FDir descriptor | ||||
|  * @raw_packet: the pre-allocated packet buffer for FDir | ||||
|  * @add: true adds a filter, false removes it | ||||
|  * | ||||
|  * Returns 0 if the filters were successfully added or removed | ||||
|  **/ | ||||
| static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, | ||||
| 				   struct i40e_fdir_filter *fd_data, | ||||
| 				   u8 *raw_packet, bool add) | ||||
| { | ||||
| 	struct i40e_pf *pf = vsi->back; | ||||
| 	struct tcphdr *tcp; | ||||
| 	struct iphdr *ip; | ||||
| 	bool err = false; | ||||
| 	int ret; | ||||
| 	/* Dummy packet */ | ||||
| 	static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0, | ||||
| 		0x45, 0, 0, 0x28, 0, 0, 0x40, 0, 0x40, 0x6, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x11, | ||||
| 		0x0, 0x72, 0, 0, 0, 0}; | ||||
| 
 | ||||
| 	memcpy(raw_packet, packet, I40E_TCPIP_DUMMY_PACKET_LEN); | ||||
| 
 | ||||
| 	ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET); | ||||
| 	tcp = (struct tcphdr *)(raw_packet + IP_HEADER_OFFSET | ||||
| 	      + sizeof(struct iphdr)); | ||||
| 
 | ||||
| 	ip->daddr = fd_data->dst_ip[0]; | ||||
| 	tcp->dest = fd_data->dst_port; | ||||
| 	ip->saddr = fd_data->src_ip[0]; | ||||
| 	tcp->source = fd_data->src_port; | ||||
| 
 | ||||
| 	if (add) { | ||||
| 		if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) { | ||||
| 			dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n"); | ||||
| 			pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN; | ||||
| 	ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| 		dev_info(&pf->pdev->dev, | ||||
| 			 "Filter command send failed for PCTYPE %d (ret = %d)\n", | ||||
| 			 fd_data->pctype, ret); | ||||
| 		err = true; | ||||
| 	} else { | ||||
| 		dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n", | ||||
| 			 fd_data->pctype, ret); | ||||
| 	} | ||||
| 
 | ||||
| 	fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; | ||||
| 
 | ||||
| 	ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); | ||||
| 	if (ret) { | ||||
| 		dev_info(&pf->pdev->dev, | ||||
| 			 "Filter command send failed for PCTYPE %d (ret = %d)\n", | ||||
| 			 fd_data->pctype, ret); | ||||
| 		err = true; | ||||
| 	} else { | ||||
| 		dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n", | ||||
| 			  fd_data->pctype, ret); | ||||
| 	} | ||||
| 
 | ||||
| 	return err ? -EOPNOTSUPP : 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_add_del_fdir_sctpv4 - Add/Remove SCTPv4 Flow Director filters for | ||||
|  * a specific flow spec | ||||
|  * @vsi: pointer to the targeted VSI | ||||
|  * @fd_data: the flow director data required for the FDir descriptor | ||||
|  * @raw_packet: the pre-allocated packet buffer for FDir | ||||
|  * @add: true adds a filter, false removes it | ||||
|  * | ||||
|  * Returns 0 if the filters were successfully added or removed | ||||
|  **/ | ||||
| static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi, | ||||
| 				    struct i40e_fdir_filter *fd_data, | ||||
| 				    u8 *raw_packet, bool add) | ||||
| { | ||||
| 	return -EOPNOTSUPP; | ||||
| } | ||||
| 
 | ||||
| #define I40E_IP_DUMMY_PACKET_LEN 34 | ||||
| /**
 | ||||
|  * i40e_add_del_fdir_ipv4 - Add/Remove IPv4 Flow Director filters for | ||||
|  * a specific flow spec | ||||
|  * @vsi: pointer to the targeted VSI | ||||
|  * @fd_data: the flow director data required for the FDir descriptor | ||||
|  * @raw_packet: the pre-allocated packet buffer for FDir | ||||
|  * @add: true adds a filter, false removes it | ||||
|  * | ||||
|  * Returns 0 if the filters were successfully added or removed | ||||
|  **/ | ||||
| static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi, | ||||
| 				  struct i40e_fdir_filter *fd_data, | ||||
| 				  u8 *raw_packet, bool add) | ||||
| { | ||||
| 	struct i40e_pf *pf = vsi->back; | ||||
| 	struct iphdr *ip; | ||||
| 	bool err = false; | ||||
| 	int ret; | ||||
| 	int i; | ||||
| 	static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0, | ||||
| 		0x45, 0, 0, 0x14, 0, 0, 0x40, 0, 0x40, 0x10, 0, 0, 0, 0, 0, 0, | ||||
| 		0, 0, 0, 0}; | ||||
| 
 | ||||
| 	memcpy(raw_packet, packet, I40E_IP_DUMMY_PACKET_LEN); | ||||
| 	ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET); | ||||
| 
 | ||||
| 	ip->saddr = fd_data->src_ip[0]; | ||||
| 	ip->daddr = fd_data->dst_ip[0]; | ||||
| 	ip->protocol = 0; | ||||
| 
 | ||||
| 	for (i = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER; | ||||
| 	     i <= I40E_FILTER_PCTYPE_FRAG_IPV4;	i++) { | ||||
| 		fd_data->pctype = i; | ||||
| 		ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); | ||||
| 
 | ||||
| 		if (ret) { | ||||
| 			dev_info(&pf->pdev->dev, | ||||
| 				 "Filter command send failed for PCTYPE %d (ret = %d)\n", | ||||
| 				 fd_data->pctype, ret); | ||||
| 			err = true; | ||||
| 		} else { | ||||
| 			dev_info(&pf->pdev->dev, | ||||
| 				 "Filter OK for PCTYPE %d (ret = %d)\n", | ||||
| 				 fd_data->pctype, ret); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return err ? -EOPNOTSUPP : 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_add_del_fdir - Build raw packets to add/del fdir filter | ||||
|  * @vsi: pointer to the targeted VSI | ||||
|  * @cmd: command to get or set RX flow classification rules | ||||
|  * @add: true adds a filter, false removes it | ||||
|  * | ||||
|  **/ | ||||
| int i40e_add_del_fdir(struct i40e_vsi *vsi, | ||||
| 		      struct i40e_fdir_filter *input, bool add) | ||||
| { | ||||
| 	struct i40e_pf *pf = vsi->back; | ||||
| 	u8 *raw_packet; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* Populate the Flow Director that we have at the moment
 | ||||
| 	 * and allocate the raw packet buffer for the calling functions | ||||
| 	 */ | ||||
| 	raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, GFP_KERNEL); | ||||
| 	if (!raw_packet) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	switch (input->flow_type & ~FLOW_EXT) { | ||||
| 	case TCP_V4_FLOW: | ||||
| 		ret = i40e_add_del_fdir_tcpv4(vsi, input, raw_packet, | ||||
| 					      add); | ||||
| 		break; | ||||
| 	case UDP_V4_FLOW: | ||||
| 		ret = i40e_add_del_fdir_udpv4(vsi, input, raw_packet, | ||||
| 					      add); | ||||
| 		break; | ||||
| 	case SCTP_V4_FLOW: | ||||
| 		ret = i40e_add_del_fdir_sctpv4(vsi, input, raw_packet, | ||||
| 					       add); | ||||
| 		break; | ||||
| 	case IPV4_FLOW: | ||||
| 		ret = i40e_add_del_fdir_ipv4(vsi, input, raw_packet, | ||||
| 					     add); | ||||
| 		break; | ||||
| 	case IP_USER_FLOW: | ||||
| 		switch (input->ip4_proto) { | ||||
| 		case IPPROTO_TCP: | ||||
| 			ret = i40e_add_del_fdir_tcpv4(vsi, input, | ||||
| 						      raw_packet, add); | ||||
| 			break; | ||||
| 		case IPPROTO_UDP: | ||||
| 			ret = i40e_add_del_fdir_udpv4(vsi, input, | ||||
| 						      raw_packet, add); | ||||
| 			break; | ||||
| 		case IPPROTO_SCTP: | ||||
| 			ret = i40e_add_del_fdir_sctpv4(vsi, input, | ||||
| 						       raw_packet, add); | ||||
| 			break; | ||||
| 		default: | ||||
| 			ret = i40e_add_del_fdir_ipv4(vsi, input, | ||||
| 						     raw_packet, add); | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		dev_info(&pf->pdev->dev, "Could not specify spec type %d", | ||||
| 			 input->flow_type); | ||||
| 		ret = -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	kfree(raw_packet); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40e_fd_handle_status - check the Programming Status for FD | ||||
|  * @rx_ring: the Rx ring for this descriptor | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user