i40evf: add client interface
In preparation for upcoming RDMA-capable hardware, add a client interface to the VF driver. This is a slightly-simplified version of the PF client interface, with the names changed to protect the innocent. Due to the nature of the VF<->PF interactions, the client interface sometimes needs to call back into itself to pass messages. Because of this, we can't use the coarse-grained locking like the PF's client interface uses. Instead, we handle all client interactions in a separate thread so the watchdog can still run and process virtual channel messages. Signed-off-by: Mitch Williams <mitch.a.williams@intel.com> Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com> Signed-off-by: Anjali Singhai Jain <anjali.singhai@intel.com> Signed-off-by: Avinash Dayanand <avinash.dayanand@intel.com> Tested-by: Andrew Bowers <andrewx.bowers@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
		
							parent
							
								
									d60be2ca9c
								
							
						
					
					
						commit
						ed0e894de7
					
				| @ -32,5 +32,5 @@ | ||||
| obj-$(CONFIG_I40EVF) += i40evf.o | ||||
| 
 | ||||
| i40evf-objs :=	i40evf_main.o i40evf_ethtool.o i40evf_virtchnl.o \
 | ||||
| 		i40e_txrx.o i40e_common.o i40e_adminq.o | ||||
| 		i40e_txrx.o i40e_common.o i40e_adminq.o i40evf_client.o | ||||
| 
 | ||||
|  | ||||
| @ -81,7 +81,9 @@ enum i40e_virtchnl_ops { | ||||
| 	I40E_VIRTCHNL_OP_GET_STATS = 15, | ||||
| 	I40E_VIRTCHNL_OP_FCOE = 16, | ||||
| 	I40E_VIRTCHNL_OP_EVENT = 17, /* must ALWAYS be 17 */ | ||||
| 	I40E_VIRTCHNL_OP_IWARP = 20, | ||||
| 	I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP = 21, | ||||
| 	I40E_VIRTCHNL_OP_RELEASE_IWARP_IRQ_MAP = 22, | ||||
| 	I40E_VIRTCHNL_OP_CONFIG_RSS_KEY = 23, | ||||
| 	I40E_VIRTCHNL_OP_CONFIG_RSS_LUT = 24, | ||||
| 	I40E_VIRTCHNL_OP_GET_RSS_HENA_CAPS = 25, | ||||
| @ -393,6 +395,37 @@ struct i40e_virtchnl_pf_event { | ||||
| 	int severity; | ||||
| }; | ||||
| 
 | ||||
| /* I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP
 | ||||
|  * VF uses this message to request PF to map IWARP vectors to IWARP queues. | ||||
|  * The request for this originates from the VF IWARP driver through | ||||
|  * a client interface between VF LAN and VF IWARP driver. | ||||
|  * A vector could have an AEQ and CEQ attached to it although | ||||
|  * there is a single AEQ per VF IWARP instance in which case | ||||
|  * most vectors will have an INVALID_IDX for aeq and valid idx for ceq. | ||||
|  * There will never be a case where there will be multiple CEQs attached | ||||
|  * to a single vector. | ||||
|  * PF configures interrupt mapping and returns status. | ||||
|  */ | ||||
| 
 | ||||
| /* HW does not define a type value for AEQ; only for RX/TX and CEQ.
 | ||||
|  * In order for us to keep the interface simple, SW will define a | ||||
|  * unique type value for AEQ. | ||||
|  */ | ||||
| #define I40E_QUEUE_TYPE_PE_AEQ  0x80 | ||||
| #define I40E_QUEUE_INVALID_IDX  0xFFFF | ||||
| 
 | ||||
| struct i40e_virtchnl_iwarp_qv_info { | ||||
| 	u32 v_idx; /* msix_vector */ | ||||
| 	u16 ceq_idx; | ||||
| 	u16 aeq_idx; | ||||
| 	u8 itr_idx; | ||||
| }; | ||||
| 
 | ||||
| struct i40e_virtchnl_iwarp_qvlist_info { | ||||
| 	u32 num_vectors; | ||||
| 	struct i40e_virtchnl_iwarp_qv_info qv_info[1]; | ||||
| }; | ||||
| 
 | ||||
| /* VF reset states - these are written into the RSTAT register:
 | ||||
|  * I40E_VFGEN_RSTAT1 on the PF | ||||
|  * I40E_VFGEN_RSTAT on the VF | ||||
|  | ||||
| @ -60,6 +60,7 @@ struct i40e_vsi { | ||||
| 	int base_vector; | ||||
| 	u16 work_limit; | ||||
| 	u16 qs_handle; | ||||
| 	void *priv;     /* client driver data reference. */ | ||||
| }; | ||||
| 
 | ||||
| /* How many Rx Buffers do we bundle into one write to the hardware ? */ | ||||
| @ -169,6 +170,7 @@ enum i40evf_state_t { | ||||
| 
 | ||||
| enum i40evf_critical_section_t { | ||||
| 	__I40EVF_IN_CRITICAL_TASK,	/* cannot be interrupted */ | ||||
| 	__I40EVF_IN_CLIENT_TASK, | ||||
| }; | ||||
| /* make common code happy */ | ||||
| #define __I40E_DOWN __I40EVF_DOWN | ||||
| @ -178,6 +180,7 @@ struct i40evf_adapter { | ||||
| 	struct timer_list watchdog_timer; | ||||
| 	struct work_struct reset_task; | ||||
| 	struct work_struct adminq_task; | ||||
| 	struct delayed_work client_task; | ||||
| 	struct delayed_work init_task; | ||||
| 	struct i40e_q_vector *q_vectors; | ||||
| 	struct list_head vlan_filter_list; | ||||
| @ -195,7 +198,10 @@ struct i40evf_adapter { | ||||
| 	u64 hw_csum_rx_error; | ||||
| 	u32 rx_desc_count; | ||||
| 	int num_msix_vectors; | ||||
| 	int num_iwarp_msix; | ||||
| 	int iwarp_base_vector; | ||||
| 	u32 client_pending; | ||||
| 	struct i40e_client_instance *cinst; | ||||
| 	struct msix_entry *msix_entries; | ||||
| 
 | ||||
| 	u32 flags; | ||||
| @ -211,8 +217,11 @@ struct i40evf_adapter { | ||||
| #define I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE	BIT(12) | ||||
| #define I40EVF_FLAG_ADDR_SET_BY_PF		BIT(13) | ||||
| #define I40EVF_FLAG_SERVICE_CLIENT_REQUESTED	BIT(14) | ||||
| #define I40EVF_FLAG_PROMISC_ON			BIT(15) | ||||
| #define I40EVF_FLAG_ALLMULTI_ON			BIT(16) | ||||
| #define I40EVF_FLAG_CLIENT_NEEDS_OPEN		BIT(15) | ||||
| #define I40EVF_FLAG_CLIENT_NEEDS_CLOSE		BIT(16) | ||||
| #define I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS	BIT(17) | ||||
| #define I40EVF_FLAG_PROMISC_ON			BIT(18) | ||||
| #define I40EVF_FLAG_ALLMULTI_ON			BIT(19) | ||||
| /* duplicates for common code */ | ||||
| #define I40E_FLAG_FDIR_ATR_ENABLED		0 | ||||
| #define I40E_FLAG_DCB_ENABLED			0 | ||||
| @ -258,10 +267,11 @@ struct i40evf_adapter { | ||||
| 	bool link_up; | ||||
| 	enum i40e_aq_link_speed link_speed; | ||||
| 	enum i40e_virtchnl_ops current_op; | ||||
| #define CLIENT_ENABLED(_a) ((_a)->vf_res ? \ | ||||
| #define CLIENT_ALLOWED(_a) ((_a)->vf_res ? \ | ||||
| 			    (_a)->vf_res->vf_offload_flags & \ | ||||
| 				I40E_VIRTCHNL_VF_OFFLOAD_IWARP : \ | ||||
| 			    0) | ||||
| #define CLIENT_ENABLED(_a) ((_a)->cinst) | ||||
| /* RSS by the PF should be preferred over RSS via other methods. */ | ||||
| #define RSS_PF(_a) ((_a)->vf_res->vf_offload_flags & \ | ||||
| 		    I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF) | ||||
| @ -292,6 +302,12 @@ struct i40evf_adapter { | ||||
| 
 | ||||
| /* Ethtool Private Flags */ | ||||
| 
 | ||||
| /* lan device */ | ||||
| struct i40e_device { | ||||
| 	struct list_head list; | ||||
| 	struct i40evf_adapter *vf; | ||||
| }; | ||||
| 
 | ||||
| /* needed by i40evf_ethtool.c */ | ||||
| extern char i40evf_driver_name[]; | ||||
| extern const char i40evf_driver_version[]; | ||||
| @ -337,4 +353,11 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, | ||||
| 				enum i40e_virtchnl_ops v_opcode, | ||||
| 				i40e_status v_retval, u8 *msg, u16 msglen); | ||||
| int i40evf_config_rss(struct i40evf_adapter *adapter); | ||||
| int i40evf_lan_add_device(struct i40evf_adapter *adapter); | ||||
| int i40evf_lan_del_device(struct i40evf_adapter *adapter); | ||||
| void i40evf_client_subtask(struct i40evf_adapter *adapter); | ||||
| void i40evf_notify_client_message(struct i40e_vsi *vsi, u8 *msg, u16 len); | ||||
| void i40evf_notify_client_l2_params(struct i40e_vsi *vsi); | ||||
| void i40evf_notify_client_open(struct i40e_vsi *vsi); | ||||
| void i40evf_notify_client_close(struct i40e_vsi *vsi, bool reset); | ||||
| #endif /* _I40EVF_H_ */ | ||||
|  | ||||
							
								
								
									
										563
									
								
								drivers/net/ethernet/intel/i40evf/i40evf_client.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										563
									
								
								drivers/net/ethernet/intel/i40evf/i40evf_client.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,563 @@ | ||||
| #include <linux/list.h> | ||||
| #include <linux/errno.h> | ||||
| 
 | ||||
| #include "i40evf.h" | ||||
| #include "i40e_prototype.h" | ||||
| #include "i40evf_client.h" | ||||
| 
 | ||||
| static | ||||
| const char i40evf_client_interface_version_str[] = I40EVF_CLIENT_VERSION_STR; | ||||
| static struct i40e_client *vf_registered_client; | ||||
| static LIST_HEAD(i40evf_devices); | ||||
| static DEFINE_MUTEX(i40evf_device_mutex); | ||||
| 
 | ||||
| static u32 i40evf_client_virtchnl_send(struct i40e_info *ldev, | ||||
| 				       struct i40e_client *client, | ||||
| 				       u8 *msg, u16 len); | ||||
| 
 | ||||
| static int i40evf_client_setup_qvlist(struct i40e_info *ldev, | ||||
| 				      struct i40e_client *client, | ||||
| 				      struct i40e_qvlist_info *qvlist_info); | ||||
| 
 | ||||
| static struct i40e_ops i40evf_lan_ops = { | ||||
| 	.virtchnl_send = i40evf_client_virtchnl_send, | ||||
| 	.setup_qvlist = i40evf_client_setup_qvlist, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_notify_client_message - call the client message receive callback | ||||
|  * @vsi: the VSI associated with this client | ||||
|  * @msg: message buffer | ||||
|  * @len: length of message | ||||
|  * | ||||
|  * If there is a client to this VSI, call the client | ||||
|  **/ | ||||
| void i40evf_notify_client_message(struct i40e_vsi *vsi, u8 *msg, u16 len) | ||||
| { | ||||
| 	struct i40evf_adapter *adapter = vsi->back; | ||||
| 	struct i40e_client_instance *cinst = adapter->cinst; | ||||
| 
 | ||||
| 	if (!vsi) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (!cinst || !cinst->client || !cinst->client->ops || | ||||
| 	    !cinst->client->ops->virtchnl_receive) { | ||||
| 		dev_dbg(&vsi->back->pdev->dev, | ||||
| 			"Cannot locate client instance virtchnl_receive function\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 	cinst->client->ops->virtchnl_receive(&cinst->lan_info,  cinst->client, | ||||
| 					     msg, len); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_notify_client_l2_params - call the client notify callback | ||||
|  * @vsi: the VSI with l2 param changes | ||||
|  * | ||||
|  * If there is a client to this VSI, call the client | ||||
|  **/ | ||||
| void i40evf_notify_client_l2_params(struct i40e_vsi *vsi) | ||||
| { | ||||
| 	struct i40evf_adapter *adapter = vsi->back; | ||||
| 	struct i40e_client_instance *cinst = adapter->cinst; | ||||
| 	struct i40e_params params; | ||||
| 
 | ||||
| 	if (!vsi) | ||||
| 		return; | ||||
| 	memset(¶ms, 0, sizeof(params)); | ||||
| 	params.mtu = vsi->netdev->mtu; | ||||
| 	params.link_up = vsi->back->link_up; | ||||
| 	params.qos.prio_qos[0].qs_handle = vsi->qs_handle; | ||||
| 
 | ||||
| 	if (!cinst || !cinst->client || !cinst->client->ops || | ||||
| 	    !cinst->client->ops->l2_param_change) { | ||||
| 		dev_dbg(&vsi->back->pdev->dev, | ||||
| 			"Cannot locate client instance l2_param_change function\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 	cinst->client->ops->l2_param_change(&cinst->lan_info, cinst->client, | ||||
| 					    ¶ms); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_notify_client_open - call the client open callback | ||||
|  * @vsi: the VSI with netdev opened | ||||
|  * | ||||
|  * If there is a client to this netdev, call the client with open | ||||
|  **/ | ||||
| void i40evf_notify_client_open(struct i40e_vsi *vsi) | ||||
| { | ||||
| 	struct i40evf_adapter *adapter = vsi->back; | ||||
| 	struct i40e_client_instance *cinst = adapter->cinst; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!cinst || !cinst->client || !cinst->client->ops || | ||||
| 	    !cinst->client->ops->open) { | ||||
| 		dev_dbg(&vsi->back->pdev->dev, | ||||
| 			"Cannot locate client instance open function\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (!(test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state))) { | ||||
| 		ret = cinst->client->ops->open(&cinst->lan_info, cinst->client); | ||||
| 		if (!ret) | ||||
| 			set_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_client_release_qvlist - send a message to the PF to release iwarp qv map | ||||
|  * @ldev: pointer to L2 context. | ||||
|  * | ||||
|  * Return 0 on success or < 0 on error | ||||
|  **/ | ||||
| static int i40evf_client_release_qvlist(struct i40e_info *ldev) | ||||
| { | ||||
| 	struct i40evf_adapter *adapter = ldev->vf; | ||||
| 	i40e_status err; | ||||
| 
 | ||||
| 	if (adapter->aq_required) | ||||
| 		return -EAGAIN; | ||||
| 
 | ||||
| 	err = i40e_aq_send_msg_to_pf(&adapter->hw, | ||||
| 			I40E_VIRTCHNL_OP_RELEASE_IWARP_IRQ_MAP, | ||||
| 			I40E_SUCCESS, NULL, 0, NULL); | ||||
| 
 | ||||
| 	if (err) | ||||
| 		dev_err(&adapter->pdev->dev, | ||||
| 			"Unable to send iWarp vector release message to PF, error %d, aq status %d\n", | ||||
| 			err, adapter->hw.aq.asq_last_status); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_notify_client_close - call the client close callback | ||||
|  * @vsi: the VSI with netdev closed | ||||
|  * @reset: true when close called due to reset pending | ||||
|  * | ||||
|  * If there is a client to this netdev, call the client with close | ||||
|  **/ | ||||
| void i40evf_notify_client_close(struct i40e_vsi *vsi, bool reset) | ||||
| { | ||||
| 	struct i40evf_adapter *adapter = vsi->back; | ||||
| 	struct i40e_client_instance *cinst = adapter->cinst; | ||||
| 
 | ||||
| 	if (!cinst || !cinst->client || !cinst->client->ops || | ||||
| 	    !cinst->client->ops->close) { | ||||
| 		dev_dbg(&vsi->back->pdev->dev, | ||||
| 			"Cannot locate client instance close function\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 	cinst->client->ops->close(&cinst->lan_info, cinst->client, reset); | ||||
| 	i40evf_client_release_qvlist(&cinst->lan_info); | ||||
| 	clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_client_add_instance - add a client instance to the instance list | ||||
|  * @adapter: pointer to the board struct | ||||
|  * @client: pointer to a client struct in the client list. | ||||
|  * | ||||
|  * Returns cinst ptr on success, NULL on failure | ||||
|  **/ | ||||
| static struct i40e_client_instance * | ||||
| i40evf_client_add_instance(struct i40evf_adapter *adapter) | ||||
| { | ||||
| 	struct i40e_client_instance *cinst = NULL; | ||||
| 	struct netdev_hw_addr *mac = NULL; | ||||
| 	struct i40e_vsi *vsi = &adapter->vsi; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (!vf_registered_client) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (adapter->cinst) { | ||||
| 		cinst = adapter->cinst; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	cinst = kzalloc(sizeof(*cinst), GFP_KERNEL); | ||||
| 	if (!cinst) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	cinst->lan_info.vf = (void *)adapter; | ||||
| 	cinst->lan_info.netdev = vsi->netdev; | ||||
| 	cinst->lan_info.pcidev = adapter->pdev; | ||||
| 	cinst->lan_info.fid = 0; | ||||
| 	cinst->lan_info.ftype = I40E_CLIENT_FTYPE_VF; | ||||
| 	cinst->lan_info.hw_addr = adapter->hw.hw_addr; | ||||
| 	cinst->lan_info.ops = &i40evf_lan_ops; | ||||
| 	cinst->lan_info.version.major = I40EVF_CLIENT_VERSION_MAJOR; | ||||
| 	cinst->lan_info.version.minor = I40EVF_CLIENT_VERSION_MINOR; | ||||
| 	cinst->lan_info.version.build = I40EVF_CLIENT_VERSION_BUILD; | ||||
| 	set_bit(__I40E_CLIENT_INSTANCE_NONE, &cinst->state); | ||||
| 
 | ||||
| 	cinst->lan_info.msix_count = adapter->num_iwarp_msix; | ||||
| 	cinst->lan_info.msix_entries = | ||||
| 			&adapter->msix_entries[adapter->iwarp_base_vector]; | ||||
| 
 | ||||
| 	for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { | ||||
| 		cinst->lan_info.params.qos.prio_qos[i].tc = 0; | ||||
| 		cinst->lan_info.params.qos.prio_qos[i].qs_handle = | ||||
| 								vsi->qs_handle; | ||||
| 	} | ||||
| 
 | ||||
| 	mac = list_first_entry(&cinst->lan_info.netdev->dev_addrs.list, | ||||
| 			       struct netdev_hw_addr, list); | ||||
| 	if (mac) | ||||
| 		ether_addr_copy(cinst->lan_info.lanmac, mac->addr); | ||||
| 	else | ||||
| 		dev_err(&adapter->pdev->dev, "MAC address list is empty!\n"); | ||||
| 
 | ||||
| 	cinst->client = vf_registered_client; | ||||
| 	adapter->cinst = cinst; | ||||
| out: | ||||
| 	return cinst; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_client_del_instance - removes a client instance from the list | ||||
|  * @adapter: pointer to the board struct | ||||
|  * @client: pointer to the client struct | ||||
|  * | ||||
|  **/ | ||||
| static | ||||
| void i40evf_client_del_instance(struct i40evf_adapter *adapter) | ||||
| { | ||||
| 	kfree(adapter->cinst); | ||||
| 	adapter->cinst = NULL; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_client_subtask - client maintenance work | ||||
|  * @adapter: board private structure | ||||
|  **/ | ||||
| void i40evf_client_subtask(struct i40evf_adapter *adapter) | ||||
| { | ||||
| 	struct i40e_client *client = vf_registered_client; | ||||
| 	struct i40e_client_instance *cinst; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (adapter->state < __I40EVF_DOWN) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* first check client is registered */ | ||||
| 	if (!client) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* Add the client instance to the instance list */ | ||||
| 	cinst = i40evf_client_add_instance(adapter); | ||||
| 	if (!cinst) | ||||
| 		return; | ||||
| 
 | ||||
| 	dev_info(&adapter->pdev->dev, "Added instance of Client %s\n", | ||||
| 		 client->name); | ||||
| 
 | ||||
| 	if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state)) { | ||||
| 		/* Send an Open request to the client */ | ||||
| 
 | ||||
| 		if (client->ops && client->ops->open) | ||||
| 			ret = client->ops->open(&cinst->lan_info, client); | ||||
| 		if (!ret) | ||||
| 			set_bit(__I40E_CLIENT_INSTANCE_OPENED, | ||||
| 				&cinst->state); | ||||
| 		else | ||||
| 			/* remove client instance */ | ||||
| 			i40evf_client_del_instance(adapter); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_lan_add_device - add a lan device struct to the list of lan devices | ||||
|  * @adapter: pointer to the board struct | ||||
|  * | ||||
|  * Returns 0 on success or none 0 on error | ||||
|  **/ | ||||
| int i40evf_lan_add_device(struct i40evf_adapter *adapter) | ||||
| { | ||||
| 	struct i40e_device *ldev; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	mutex_lock(&i40evf_device_mutex); | ||||
| 	list_for_each_entry(ldev, &i40evf_devices, list) { | ||||
| 		if (ldev->vf == adapter) { | ||||
| 			ret = -EEXIST; | ||||
| 			goto out; | ||||
| 		} | ||||
| 	} | ||||
| 	ldev = kzalloc(sizeof(*ldev), GFP_KERNEL); | ||||
| 	if (!ldev) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	ldev->vf = adapter; | ||||
| 	INIT_LIST_HEAD(&ldev->list); | ||||
| 	list_add(&ldev->list, &i40evf_devices); | ||||
| 	dev_info(&adapter->pdev->dev, "Added LAN device bus=0x%02x dev=0x%02x func=0x%02x\n", | ||||
| 		 adapter->hw.bus.bus_id, adapter->hw.bus.device, | ||||
| 		 adapter->hw.bus.func); | ||||
| 
 | ||||
| 	/* Since in some cases register may have happened before a device gets
 | ||||
| 	 * added, we can schedule a subtask to go initiate the clients. | ||||
| 	 */ | ||||
| 	adapter->flags |= I40EVF_FLAG_SERVICE_CLIENT_REQUESTED; | ||||
| 
 | ||||
| out: | ||||
| 	mutex_unlock(&i40evf_device_mutex); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_lan_del_device - removes a lan device from the device list | ||||
|  * @adapter: pointer to the board struct | ||||
|  * | ||||
|  * Returns 0 on success or non-0 on error | ||||
|  **/ | ||||
| int i40evf_lan_del_device(struct i40evf_adapter *adapter) | ||||
| { | ||||
| 	struct i40e_device *ldev, *tmp; | ||||
| 	int ret = -ENODEV; | ||||
| 
 | ||||
| 	mutex_lock(&i40evf_device_mutex); | ||||
| 	list_for_each_entry_safe(ldev, tmp, &i40evf_devices, list) { | ||||
| 		if (ldev->vf == adapter) { | ||||
| 			dev_info(&adapter->pdev->dev, | ||||
| 				 "Deleted LAN device bus=0x%02x dev=0x%02x func=0x%02x\n", | ||||
| 				 adapter->hw.bus.bus_id, adapter->hw.bus.device, | ||||
| 				 adapter->hw.bus.func); | ||||
| 			list_del(&ldev->list); | ||||
| 			kfree(ldev); | ||||
| 			ret = 0; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&i40evf_device_mutex); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_client_release - release client specific resources | ||||
|  * @client: pointer to the registered client | ||||
|  * | ||||
|  **/ | ||||
| static void i40evf_client_release(struct i40e_client *client) | ||||
| { | ||||
| 	struct i40e_client_instance *cinst; | ||||
| 	struct i40e_device *ldev; | ||||
| 	struct i40evf_adapter *adapter; | ||||
| 
 | ||||
| 	mutex_lock(&i40evf_device_mutex); | ||||
| 	list_for_each_entry(ldev, &i40evf_devices, list) { | ||||
| 		adapter = ldev->vf; | ||||
| 		cinst = adapter->cinst; | ||||
| 		if (!cinst) | ||||
| 			continue; | ||||
| 		if (test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state)) { | ||||
| 			if (client->ops && client->ops->close) | ||||
| 				client->ops->close(&cinst->lan_info, client, | ||||
| 						   false); | ||||
| 			i40evf_client_release_qvlist(&cinst->lan_info); | ||||
| 			clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state); | ||||
| 
 | ||||
| 			dev_warn(&adapter->pdev->dev, | ||||
| 				 "Client %s instance closed\n", client->name); | ||||
| 		} | ||||
| 		/* delete the client instance */ | ||||
| 		i40evf_client_del_instance(adapter); | ||||
| 		dev_info(&adapter->pdev->dev, "Deleted client instance of Client %s\n", | ||||
| 			 client->name); | ||||
| 	} | ||||
| 	mutex_unlock(&i40evf_device_mutex); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_client_prepare - prepare client specific resources | ||||
|  * @client: pointer to the registered client | ||||
|  * | ||||
|  **/ | ||||
| static void i40evf_client_prepare(struct i40e_client *client) | ||||
| { | ||||
| 	struct i40e_device *ldev; | ||||
| 	struct i40evf_adapter *adapter; | ||||
| 
 | ||||
| 	mutex_lock(&i40evf_device_mutex); | ||||
| 	list_for_each_entry(ldev, &i40evf_devices, list) { | ||||
| 		adapter = ldev->vf; | ||||
| 		/* Signal the watchdog to service the client */ | ||||
| 		adapter->flags |= I40EVF_FLAG_SERVICE_CLIENT_REQUESTED; | ||||
| 	} | ||||
| 	mutex_unlock(&i40evf_device_mutex); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_client_virtchnl_send - send a message to the PF instance | ||||
|  * @ldev: pointer to L2 context. | ||||
|  * @client: Client pointer. | ||||
|  * @msg: pointer to message buffer | ||||
|  * @len: message length | ||||
|  * | ||||
|  * Return 0 on success or < 0 on error | ||||
|  **/ | ||||
| static u32 i40evf_client_virtchnl_send(struct i40e_info *ldev, | ||||
| 				       struct i40e_client *client, | ||||
| 				       u8 *msg, u16 len) | ||||
| { | ||||
| 	struct i40evf_adapter *adapter = ldev->vf; | ||||
| 	i40e_status err; | ||||
| 
 | ||||
| 	if (adapter->aq_required) | ||||
| 		return -EAGAIN; | ||||
| 
 | ||||
| 	err = i40e_aq_send_msg_to_pf(&adapter->hw, I40E_VIRTCHNL_OP_IWARP, | ||||
| 				     I40E_SUCCESS, msg, len, NULL); | ||||
| 	if (err) | ||||
| 		dev_err(&adapter->pdev->dev, "Unable to send iWarp message to PF, error %d, aq status %d\n", | ||||
| 			err, adapter->hw.aq.asq_last_status); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_client_setup_qvlist - send a message to the PF to setup iwarp qv map | ||||
|  * @ldev: pointer to L2 context. | ||||
|  * @client: Client pointer. | ||||
|  * @qv_info: queue and vector list | ||||
|  * | ||||
|  * Return 0 on success or < 0 on error | ||||
|  **/ | ||||
| static int i40evf_client_setup_qvlist(struct i40e_info *ldev, | ||||
| 				      struct i40e_client *client, | ||||
| 				      struct i40e_qvlist_info *qvlist_info) | ||||
| { | ||||
| 	struct i40e_virtchnl_iwarp_qvlist_info *v_qvlist_info; | ||||
| 	struct i40evf_adapter *adapter = ldev->vf; | ||||
| 	struct i40e_qv_info *qv_info; | ||||
| 	i40e_status err; | ||||
| 	u32 v_idx, i; | ||||
| 	u32 msg_size; | ||||
| 
 | ||||
| 	if (adapter->aq_required) | ||||
| 		return -EAGAIN; | ||||
| 
 | ||||
| 	/* A quick check on whether the vectors belong to the client */ | ||||
| 	for (i = 0; i < qvlist_info->num_vectors; i++) { | ||||
| 		qv_info = &qvlist_info->qv_info[i]; | ||||
| 		if (!qv_info) | ||||
| 			continue; | ||||
| 		v_idx = qv_info->v_idx; | ||||
| 		if ((v_idx >= | ||||
| 		    (adapter->iwarp_base_vector + adapter->num_iwarp_msix)) || | ||||
| 		    (v_idx < adapter->iwarp_base_vector)) | ||||
| 			return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	v_qvlist_info = (struct i40e_virtchnl_iwarp_qvlist_info *)qvlist_info; | ||||
| 	msg_size = sizeof(struct i40e_virtchnl_iwarp_qvlist_info) + | ||||
| 			(sizeof(struct i40e_virtchnl_iwarp_qv_info) * | ||||
| 			(v_qvlist_info->num_vectors - 1)); | ||||
| 
 | ||||
| 	adapter->client_pending |= BIT(I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP); | ||||
| 	err = i40e_aq_send_msg_to_pf(&adapter->hw, | ||||
| 			I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP, | ||||
| 			I40E_SUCCESS, (u8 *)v_qvlist_info, msg_size, NULL); | ||||
| 
 | ||||
| 	if (err) { | ||||
| 		dev_err(&adapter->pdev->dev, | ||||
| 			"Unable to send iWarp vector config message to PF, error %d, aq status %d\n", | ||||
| 			err, adapter->hw.aq.asq_last_status); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	err = -EBUSY; | ||||
| 	for (i = 0; i < 5; i++) { | ||||
| 		msleep(100); | ||||
| 		if (!(adapter->client_pending & | ||||
| 		      BIT(I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP))) { | ||||
| 			err = 0; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| out: | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_register_client - Register a i40e client driver with the L2 driver | ||||
|  * @client: pointer to the i40e_client struct | ||||
|  * | ||||
|  * Returns 0 on success or non-0 on error | ||||
|  **/ | ||||
| int i40evf_register_client(struct i40e_client *client) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (!client) { | ||||
| 		ret = -EIO; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (strlen(client->name) == 0) { | ||||
| 		pr_info("i40evf: Failed to register client with no name\n"); | ||||
| 		ret = -EIO; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (vf_registered_client) { | ||||
| 		pr_info("i40evf: Client %s has already been registered!\n", | ||||
| 			client->name); | ||||
| 		ret = -EEXIST; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((client->version.major != I40EVF_CLIENT_VERSION_MAJOR) || | ||||
| 	    (client->version.minor != I40EVF_CLIENT_VERSION_MINOR)) { | ||||
| 		pr_info("i40evf: Failed to register client %s due to mismatched client interface version\n", | ||||
| 			client->name); | ||||
| 		pr_info("Client is using version: %02d.%02d.%02d while LAN driver supports %s\n", | ||||
| 			client->version.major, client->version.minor, | ||||
| 			client->version.build, | ||||
| 			i40evf_client_interface_version_str); | ||||
| 		ret = -EIO; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	vf_registered_client = client; | ||||
| 
 | ||||
| 	i40evf_client_prepare(client); | ||||
| 
 | ||||
| 	pr_info("i40evf: Registered client %s with return code %d\n", | ||||
| 		client->name, ret); | ||||
| out: | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL(i40evf_register_client); | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_unregister_client - Unregister a i40e client driver with the L2 driver | ||||
|  * @client: pointer to the i40e_client struct | ||||
|  * | ||||
|  * Returns 0 on success or non-0 on error | ||||
|  **/ | ||||
| int i40evf_unregister_client(struct i40e_client *client) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	/* When a unregister request comes through we would have to send
 | ||||
| 	 * a close for each of the client instances that were opened. | ||||
| 	 * client_release function is called to handle this. | ||||
| 	 */ | ||||
| 	i40evf_client_release(client); | ||||
| 
 | ||||
| 	if (vf_registered_client != client) { | ||||
| 		pr_info("i40evf: Client %s has not been registered\n", | ||||
| 			client->name); | ||||
| 		ret = -ENODEV; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	vf_registered_client = NULL; | ||||
| 	pr_info("i40evf: Unregistered client %s\n", client->name); | ||||
| out: | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL(i40evf_unregister_client); | ||||
							
								
								
									
										166
									
								
								drivers/net/ethernet/intel/i40evf/i40evf_client.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								drivers/net/ethernet/intel/i40evf/i40evf_client.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,166 @@ | ||||
| #ifndef _I40E_CLIENT_H_ | ||||
| #define _I40E_CLIENT_H_ | ||||
| 
 | ||||
| #define I40EVF_CLIENT_STR_LENGTH 10 | ||||
| 
 | ||||
| /* Client interface version should be updated anytime there is a change in the
 | ||||
|  * existing APIs or data structures. | ||||
|  */ | ||||
| #define I40EVF_CLIENT_VERSION_MAJOR 0 | ||||
| #define I40EVF_CLIENT_VERSION_MINOR 01 | ||||
| #define I40EVF_CLIENT_VERSION_BUILD 00 | ||||
| #define I40EVF_CLIENT_VERSION_STR     \ | ||||
| 	__stringify(I40EVF_CLIENT_VERSION_MAJOR) "." \ | ||||
| 	__stringify(I40EVF_CLIENT_VERSION_MINOR) "." \ | ||||
| 	__stringify(I40EVF_CLIENT_VERSION_BUILD) | ||||
| 
 | ||||
| struct i40e_client_version { | ||||
| 	u8 major; | ||||
| 	u8 minor; | ||||
| 	u8 build; | ||||
| 	u8 rsvd; | ||||
| }; | ||||
| 
 | ||||
| enum i40e_client_state { | ||||
| 	__I40E_CLIENT_NULL, | ||||
| 	__I40E_CLIENT_REGISTERED | ||||
| }; | ||||
| 
 | ||||
| enum i40e_client_instance_state { | ||||
| 	__I40E_CLIENT_INSTANCE_NONE, | ||||
| 	__I40E_CLIENT_INSTANCE_OPENED, | ||||
| }; | ||||
| 
 | ||||
| struct i40e_ops; | ||||
| struct i40e_client; | ||||
| 
 | ||||
| /* HW does not define a type value for AEQ; only for RX/TX and CEQ.
 | ||||
|  * In order for us to keep the interface simple, SW will define a | ||||
|  * unique type value for AEQ. | ||||
|  */ | ||||
| #define I40E_QUEUE_TYPE_PE_AEQ  0x80 | ||||
| #define I40E_QUEUE_INVALID_IDX	0xFFFF | ||||
| 
 | ||||
| struct i40e_qv_info { | ||||
| 	u32 v_idx; /* msix_vector */ | ||||
| 	u16 ceq_idx; | ||||
| 	u16 aeq_idx; | ||||
| 	u8 itr_idx; | ||||
| }; | ||||
| 
 | ||||
| struct i40e_qvlist_info { | ||||
| 	u32 num_vectors; | ||||
| 	struct i40e_qv_info qv_info[1]; | ||||
| }; | ||||
| 
 | ||||
| #define I40E_CLIENT_MSIX_ALL 0xFFFFFFFF | ||||
| 
 | ||||
| /* set of LAN parameters useful for clients managed by LAN */ | ||||
| 
 | ||||
| /* Struct to hold per priority info */ | ||||
| struct i40e_prio_qos_params { | ||||
| 	u16 qs_handle; /* qs handle for prio */ | ||||
| 	u8 tc; /* TC mapped to prio */ | ||||
| 	u8 reserved; | ||||
| }; | ||||
| 
 | ||||
| #define I40E_CLIENT_MAX_USER_PRIORITY        8 | ||||
| /* Struct to hold Client QoS */ | ||||
| struct i40e_qos_params { | ||||
| 	struct i40e_prio_qos_params prio_qos[I40E_CLIENT_MAX_USER_PRIORITY]; | ||||
| }; | ||||
| 
 | ||||
| struct i40e_params { | ||||
| 	struct i40e_qos_params qos; | ||||
| 	u16 mtu; | ||||
| 	u16 link_up; /* boolean */ | ||||
| }; | ||||
| 
 | ||||
| /* Structure to hold LAN device info for a client device */ | ||||
| struct i40e_info { | ||||
| 	struct i40e_client_version version; | ||||
| 	u8 lanmac[6]; | ||||
| 	struct net_device *netdev; | ||||
| 	struct pci_dev *pcidev; | ||||
| 	u8 __iomem *hw_addr; | ||||
| 	u8 fid;	/* function id, PF id or VF id */ | ||||
| #define I40E_CLIENT_FTYPE_PF 0 | ||||
| #define I40E_CLIENT_FTYPE_VF 1 | ||||
| 	u8 ftype; /* function type, PF or VF */ | ||||
| 	void *vf; /* cast to i40evf_adapter */ | ||||
| 
 | ||||
| 	/* All L2 params that could change during the life span of the device
 | ||||
| 	 * and needs to be communicated to the client when they change | ||||
| 	 */ | ||||
| 	struct i40e_params params; | ||||
| 	struct i40e_ops *ops; | ||||
| 
 | ||||
| 	u16 msix_count;	 /* number of msix vectors*/ | ||||
| 	/* Array down below will be dynamically allocated based on msix_count */ | ||||
| 	struct msix_entry *msix_entries; | ||||
| 	u16 itr_index; /* Which ITR index the PE driver is suppose to use */ | ||||
| }; | ||||
| 
 | ||||
| struct i40e_ops { | ||||
| 	/* setup_q_vector_list enables queues with a particular vector */ | ||||
| 	int (*setup_qvlist)(struct i40e_info *ldev, struct i40e_client *client, | ||||
| 			    struct i40e_qvlist_info *qv_info); | ||||
| 
 | ||||
| 	u32 (*virtchnl_send)(struct i40e_info *ldev, struct i40e_client *client, | ||||
| 			     u8 *msg, u16 len); | ||||
| 
 | ||||
| 	/* If the PE Engine is unresponsive, RDMA driver can request a reset.*/ | ||||
| 	void (*request_reset)(struct i40e_info *ldev, | ||||
| 			      struct i40e_client *client); | ||||
| }; | ||||
| 
 | ||||
| struct i40e_client_ops { | ||||
| 	/* Should be called from register_client() or whenever the driver is
 | ||||
| 	 * ready to create a specific client instance. | ||||
| 	 */ | ||||
| 	int (*open)(struct i40e_info *ldev, struct i40e_client *client); | ||||
| 
 | ||||
| 	/* Should be closed when netdev is unavailable or when unregister
 | ||||
| 	 * call comes in. If the close happens due to a reset, set the reset | ||||
| 	 * bit to true. | ||||
| 	 */ | ||||
| 	void (*close)(struct i40e_info *ldev, struct i40e_client *client, | ||||
| 		      bool reset); | ||||
| 
 | ||||
| 	/* called when some l2 managed parameters changes - mss */ | ||||
| 	void (*l2_param_change)(struct i40e_info *ldev, | ||||
| 				struct i40e_client *client, | ||||
| 				struct i40e_params *params); | ||||
| 
 | ||||
| 	/* called when a message is received from the PF */ | ||||
| 	int (*virtchnl_receive)(struct i40e_info *ldev, | ||||
| 				struct i40e_client *client, | ||||
| 				u8 *msg, u16 len); | ||||
| }; | ||||
| 
 | ||||
| /* Client device */ | ||||
| struct i40e_client_instance { | ||||
| 	struct list_head list; | ||||
| 	struct i40e_info lan_info; | ||||
| 	struct i40e_client *client; | ||||
| 	unsigned long  state; | ||||
| }; | ||||
| 
 | ||||
| struct i40e_client { | ||||
| 	struct list_head list;		/* list of registered clients */ | ||||
| 	char name[I40EVF_CLIENT_STR_LENGTH]; | ||||
| 	struct i40e_client_version version; | ||||
| 	unsigned long state;		/* client state */ | ||||
| 	atomic_t ref_cnt;  /* Count of all the client devices of this kind */ | ||||
| 	u32 flags; | ||||
| #define I40E_CLIENT_FLAGS_LAUNCH_ON_PROBE	BIT(0) | ||||
| #define I40E_TX_FLAGS_NOTIFY_OTHER_EVENTS	BIT(2) | ||||
| 	u8 type; | ||||
| #define I40E_CLIENT_IWARP 0 | ||||
| 	struct i40e_client_ops *ops;	/* client ops provided by the client */ | ||||
| }; | ||||
| 
 | ||||
| /* used by clients */ | ||||
| int i40evf_register_client(struct i40e_client *client); | ||||
| int i40evf_unregister_client(struct i40e_client *client); | ||||
| #endif /* _I40E_CLIENT_H_ */ | ||||
| @ -26,6 +26,7 @@ | ||||
| 
 | ||||
| #include "i40evf.h" | ||||
| #include "i40e_prototype.h" | ||||
| #include "i40evf_client.h" | ||||
| static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter); | ||||
| static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter); | ||||
| static int i40evf_close(struct net_device *netdev); | ||||
| @ -1058,6 +1059,8 @@ static void i40evf_up_complete(struct i40evf_adapter *adapter) | ||||
| 	i40evf_napi_enable_all(adapter); | ||||
| 
 | ||||
| 	adapter->aq_required |= I40EVF_FLAG_AQ_ENABLE_QUEUES; | ||||
| 	if (CLIENT_ENABLED(adapter)) | ||||
| 		adapter->flags |= I40EVF_FLAG_CLIENT_NEEDS_OPEN; | ||||
| 	mod_timer_pending(&adapter->watchdog_timer, jiffies + 1); | ||||
| } | ||||
| 
 | ||||
| @ -1685,6 +1688,7 @@ static void i40evf_watchdog_task(struct work_struct *work) | ||||
| 		i40evf_set_promiscuous(adapter, 0); | ||||
| 		goto watchdog_done; | ||||
| 	} | ||||
| 	schedule_delayed_work(&adapter->client_task, msecs_to_jiffies(5)); | ||||
| 
 | ||||
| 	if (adapter->state == __I40EVF_RUNNING) | ||||
| 		i40evf_request_stats(adapter); | ||||
| @ -1773,10 +1777,17 @@ static void i40evf_reset_task(struct work_struct *work) | ||||
| 	u32 reg_val; | ||||
| 	int i = 0, err; | ||||
| 
 | ||||
| 	while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, | ||||
| 	while (test_and_set_bit(__I40EVF_IN_CLIENT_TASK, | ||||
| 				&adapter->crit_section)) | ||||
| 		usleep_range(500, 1000); | ||||
| 
 | ||||
| 	if (CLIENT_ENABLED(adapter)) { | ||||
| 		adapter->flags &= ~(I40EVF_FLAG_CLIENT_NEEDS_OPEN | | ||||
| 				    I40EVF_FLAG_CLIENT_NEEDS_CLOSE | | ||||
| 				    I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS | | ||||
| 				    I40EVF_FLAG_SERVICE_CLIENT_REQUESTED); | ||||
| 		cancel_delayed_work_sync(&adapter->client_task); | ||||
| 		i40evf_notify_client_close(&adapter->vsi, true); | ||||
| 	} | ||||
| 	i40evf_misc_irq_disable(adapter); | ||||
| 	if (adapter->flags & I40EVF_FLAG_RESET_NEEDED) { | ||||
| 		adapter->flags &= ~I40EVF_FLAG_RESET_NEEDED; | ||||
| @ -1819,6 +1830,7 @@ static void i40evf_reset_task(struct work_struct *work) | ||||
| 		dev_err(&adapter->pdev->dev, "Reset never finished (%x)\n", | ||||
| 			reg_val); | ||||
| 		i40evf_disable_vf(adapter); | ||||
| 		clear_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section); | ||||
| 		return; /* Do not attempt to reinit. It's dead, Jim. */ | ||||
| 	} | ||||
| 
 | ||||
| @ -1861,9 +1873,8 @@ continue_reset: | ||||
| 	} | ||||
| 	adapter->aq_required |= I40EVF_FLAG_AQ_ADD_MAC_FILTER; | ||||
| 	adapter->aq_required |= I40EVF_FLAG_AQ_ADD_VLAN_FILTER; | ||||
| 	/* Open RDMA Client again */ | ||||
| 	adapter->aq_required |= I40EVF_FLAG_SERVICE_CLIENT_REQUESTED; | ||||
| 	clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); | ||||
| 	clear_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section); | ||||
| 	i40evf_misc_irq_enable(adapter); | ||||
| 
 | ||||
| 	mod_timer(&adapter->watchdog_timer, jiffies + 2); | ||||
| @ -1979,6 +1990,48 @@ out: | ||||
| 	i40evf_misc_irq_enable(adapter); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_client_task - worker thread to perform client work | ||||
|  * @work: pointer to work_struct containing our data | ||||
|  * | ||||
|  * This task handles client interactions. Because client calls can be | ||||
|  * reentrant, we can't handle them in the watchdog. | ||||
|  **/ | ||||
| static void i40evf_client_task(struct work_struct *work) | ||||
| { | ||||
| 	struct i40evf_adapter *adapter = | ||||
| 		container_of(work, struct i40evf_adapter, client_task.work); | ||||
| 
 | ||||
| 	/* If we can't get the client bit, just give up. We'll be rescheduled
 | ||||
| 	 * later. | ||||
| 	 */ | ||||
| 
 | ||||
| 	if (test_and_set_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section)) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (adapter->flags & I40EVF_FLAG_SERVICE_CLIENT_REQUESTED) { | ||||
| 		i40evf_client_subtask(adapter); | ||||
| 		adapter->flags &= ~I40EVF_FLAG_SERVICE_CLIENT_REQUESTED; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (adapter->flags & I40EVF_FLAG_CLIENT_NEEDS_CLOSE) { | ||||
| 		i40evf_notify_client_close(&adapter->vsi, false); | ||||
| 		adapter->flags &= ~I40EVF_FLAG_CLIENT_NEEDS_CLOSE; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (adapter->flags & I40EVF_FLAG_CLIENT_NEEDS_OPEN) { | ||||
| 		i40evf_notify_client_open(&adapter->vsi); | ||||
| 		adapter->flags &= ~I40EVF_FLAG_CLIENT_NEEDS_OPEN; | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (adapter->flags & I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS) { | ||||
| 		i40evf_notify_client_l2_params(&adapter->vsi); | ||||
| 		adapter->flags &= ~I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS; | ||||
| 	} | ||||
| out: | ||||
| 	clear_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * i40evf_free_all_tx_resources - Free Tx Resources for All Queues | ||||
|  * @adapter: board private structure | ||||
| @ -2148,6 +2201,8 @@ static int i40evf_close(struct net_device *netdev) | ||||
| 
 | ||||
| 
 | ||||
| 	set_bit(__I40E_DOWN, &adapter->vsi.state); | ||||
| 	if (CLIENT_ENABLED(adapter)) | ||||
| 		adapter->flags |= I40EVF_FLAG_CLIENT_NEEDS_CLOSE; | ||||
| 
 | ||||
| 	i40evf_down(adapter); | ||||
| 	adapter->state = __I40EVF_DOWN_PENDING; | ||||
| @ -2188,6 +2243,10 @@ static int i40evf_change_mtu(struct net_device *netdev, int new_mtu) | ||||
| 	struct i40evf_adapter *adapter = netdev_priv(netdev); | ||||
| 
 | ||||
| 	netdev->mtu = new_mtu; | ||||
| 	if (CLIENT_ENABLED(adapter)) { | ||||
| 		i40evf_notify_client_l2_params(&adapter->vsi); | ||||
| 		adapter->flags |= I40EVF_FLAG_SERVICE_CLIENT_REQUESTED; | ||||
| 	} | ||||
| 	adapter->flags |= I40EVF_FLAG_RESET_NEEDED; | ||||
| 	schedule_work(&adapter->reset_task); | ||||
| 
 | ||||
| @ -2581,6 +2640,12 @@ static void i40evf_init_task(struct work_struct *work) | ||||
| 	adapter->netdev_registered = true; | ||||
| 
 | ||||
| 	netif_tx_stop_all_queues(netdev); | ||||
| 	if (CLIENT_ALLOWED(adapter)) { | ||||
| 		err = i40evf_lan_add_device(adapter); | ||||
| 		if (err) | ||||
| 			dev_info(&pdev->dev, "Failed to add VF to client API service list: %d\n", | ||||
| 				 err); | ||||
| 	} | ||||
| 
 | ||||
| 	dev_info(&pdev->dev, "MAC address: %pM\n", adapter->hw.mac.addr); | ||||
| 	if (netdev->features & NETIF_F_GRO) | ||||
| @ -2745,6 +2810,7 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | ||||
| 	INIT_WORK(&adapter->reset_task, i40evf_reset_task); | ||||
| 	INIT_WORK(&adapter->adminq_task, i40evf_adminq_task); | ||||
| 	INIT_WORK(&adapter->watchdog_task, i40evf_watchdog_task); | ||||
| 	INIT_DELAYED_WORK(&adapter->client_task, i40evf_client_task); | ||||
| 	INIT_DELAYED_WORK(&adapter->init_task, i40evf_init_task); | ||||
| 	schedule_delayed_work(&adapter->init_task, | ||||
| 			      msecs_to_jiffies(5 * (pdev->devfn & 0x07))); | ||||
| @ -2857,14 +2923,21 @@ static void i40evf_remove(struct pci_dev *pdev) | ||||
| 	struct i40evf_adapter *adapter = netdev_priv(netdev); | ||||
| 	struct i40evf_mac_filter *f, *ftmp; | ||||
| 	struct i40e_hw *hw = &adapter->hw; | ||||
| 	int err; | ||||
| 
 | ||||
| 	cancel_delayed_work_sync(&adapter->init_task); | ||||
| 	cancel_work_sync(&adapter->reset_task); | ||||
| 
 | ||||
| 	cancel_delayed_work_sync(&adapter->client_task); | ||||
| 	if (adapter->netdev_registered) { | ||||
| 		unregister_netdev(netdev); | ||||
| 		adapter->netdev_registered = false; | ||||
| 	} | ||||
| 	if (CLIENT_ALLOWED(adapter)) { | ||||
| 		err = i40evf_lan_del_device(adapter); | ||||
| 		if (err) | ||||
| 			dev_warn(&pdev->dev, "Failed to delete client device: %d\n", | ||||
| 				 err); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Shut down all the garbage mashers on the detention level */ | ||||
| 	adapter->state = __I40EVF_REMOVE; | ||||
|  | ||||
| @ -26,6 +26,7 @@ | ||||
| 
 | ||||
| #include "i40evf.h" | ||||
| #include "i40e_prototype.h" | ||||
| #include "i40evf_client.h" | ||||
| 
 | ||||
| /* busy wait delay in msec */ | ||||
| #define I40EVF_BUSY_WAIT_DELAY 10 | ||||
| @ -999,6 +1000,16 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, | ||||
| 		if (v_opcode != adapter->current_op) | ||||
| 			return; | ||||
| 		break; | ||||
| 	case I40E_VIRTCHNL_OP_IWARP: | ||||
| 		/* Gobble zero-length replies from the PF. They indicate that
 | ||||
| 		 * a previous message was received OK, and the client doesn't | ||||
| 		 * care about that. | ||||
| 		 */ | ||||
| 		if (msglen && CLIENT_ENABLED(adapter)) | ||||
| 			i40evf_notify_client_message(&adapter->vsi, | ||||
| 						     msg, msglen); | ||||
| 		break; | ||||
| 
 | ||||
| 	case I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP: | ||||
| 		adapter->client_pending &= | ||||
| 				~(BIT(I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP)); | ||||
| @ -1014,7 +1025,7 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		if (v_opcode != adapter->current_op) | ||||
| 		if (adapter->current_op && (v_opcode != adapter->current_op)) | ||||
| 			dev_warn(&adapter->pdev->dev, "Expected response %d from PF, received %d\n", | ||||
| 				 adapter->current_op, v_opcode); | ||||
| 		break; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user