123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright(c) 2013 - 2018 Intel Corporation. */
- #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_client_get_params - retrieve relevant client parameters
- * @vsi: VSI with parameters
- * @params: client param struct
- **/
- static
- void i40evf_client_get_params(struct i40e_vsi *vsi, struct i40e_params *params)
- {
- int i;
- memset(params, 0, sizeof(struct i40e_params));
- params->mtu = vsi->netdev->mtu;
- params->link_up = vsi->back->link_up;
- for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
- params->qos.prio_qos[i].tc = 0;
- params->qos.prio_qos[i].qs_handle = vsi->qs_handle;
- }
- }
- /**
- * 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 i40e_client_instance *cinst;
- if (!vsi)
- return;
- cinst = vsi->back->cinst;
- 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 i40e_client_instance *cinst;
- struct i40e_params params;
- if (!vsi)
- return;
- cinst = vsi->back->cinst;
- 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;
- }
- i40evf_client_get_params(vsi, ¶ms);
- cinst->lan_info.params = params;
- 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,
- 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
- *
- * 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 i40e_vsi *vsi = &adapter->vsi;
- struct netdev_hw_addr *mac = NULL;
- struct i40e_params params;
- 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;
- i40evf_client_get_params(vsi, ¶ms);
- cinst->lan_info.params = params;
- 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];
- 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
- *
- **/
- 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, 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.
- * @qvlist_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 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 virtchnl_iwarp_qvlist_info *)qvlist_info;
- msg_size = sizeof(struct virtchnl_iwarp_qvlist_info) +
- (sizeof(struct virtchnl_iwarp_qv_info) *
- (v_qvlist_info->num_vectors - 1));
- adapter->client_pending |= BIT(VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP);
- err = i40e_aq_send_msg_to_pf(&adapter->hw,
- 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(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);
|