From ed0e894de7c1339be55ca0dcc11783d923ac5248 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Tue, 24 Jan 2017 10:23:59 -0800 Subject: [PATCH] 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 Signed-off-by: Jesse Brandeburg Signed-off-by: Anjali Singhai Jain Signed-off-by: Avinash Dayanand Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/Makefile | 2 +- .../net/ethernet/intel/i40evf/i40e_virtchnl.h | 33 + drivers/net/ethernet/intel/i40evf/i40evf.h | 29 +- .../net/ethernet/intel/i40evf/i40evf_client.c | 563 ++++++++++++++++++ .../net/ethernet/intel/i40evf/i40evf_client.h | 166 ++++++ .../net/ethernet/intel/i40evf/i40evf_main.c | 83 ++- .../ethernet/intel/i40evf/i40evf_virtchnl.c | 13 +- 7 files changed, 879 insertions(+), 10 deletions(-) create mode 100644 drivers/net/ethernet/intel/i40evf/i40evf_client.c create mode 100644 drivers/net/ethernet/intel/i40evf/i40evf_client.h diff --git a/drivers/net/ethernet/intel/i40evf/Makefile b/drivers/net/ethernet/intel/i40evf/Makefile index 3a423836a565..827c7a6ed0ba 100644 --- a/drivers/net/ethernet/intel/i40evf/Makefile +++ b/drivers/net/ethernet/intel/i40evf/Makefile @@ -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 diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h index d38a2b2aea2b..f431fbc4a3e7 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h @@ -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 diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 00c42d803276..b2b48511f457 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -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_ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_client.c b/drivers/net/ethernet/intel/i40evf/i40evf_client.c new file mode 100644 index 000000000000..5b43e5b6e2eb --- /dev/null +++ b/drivers/net/ethernet/intel/i40evf/i40evf_client.c @@ -0,0 +1,563 @@ +#include +#include + +#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); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_client.h b/drivers/net/ethernet/intel/i40evf/i40evf_client.h new file mode 100644 index 000000000000..7d283c7506a5 --- /dev/null +++ b/drivers/net/ethernet/intel/i40evf/i40evf_client.h @@ -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_ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index f35dcaac5bb7..9492b20da557 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -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; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index bee58af390e1..a2a7354426a3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -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; -- 2.20.1