Hi All,
This series adds the vdpa support for EF100 devices.
For now, only a network class of vdpa device is supported and
they can be created only on a VF. Each EF100 VF can have one
of the three function personalities (EF100, vDPA & None) at
any time with EF100 being the default. A VF's function personality
is changed to vDPA while creating the vdpa device using vdpa tool.
A vDPA management device is created per VF to allow selection of
the desired VF for vDPA device creation. The MAC address for the
target net device must be specified at the device creation time
via the `mac` parameter of the `vdpa dev add` command as the control
virtqueue is not supported yet.
To use with vhost-vdpa, QEMU version 6.1.0 or later must be used
as it fixes the incorrect feature negotiation (vhost-vdpa backend)
without which VIRTIO_F_IN_ORDER feature bit is negotiated but not
honored when using the guest kernel virtio driver.
Gautam Dawar (11):
sfc: add function personality support for EF100 devices
sfc: implement MCDI interface for vDPA operations
sfc: implement init and fini functions for vDPA personality
sfc: implement vDPA management device operations
sfc: implement vdpa device config operations
sfc: implement vdpa vring config operations
sfc: implement filters for receiving traffic
sfc: implement device status related vdpa config operations
sfc: implement iova rbtree to store dma mappings
sfc: implement vdpa config_ops for dma operations
sfc: register the vDPA device
drivers/net/ethernet/sfc/Kconfig | 8 +
drivers/net/ethernet/sfc/Makefile | 2 +
drivers/net/ethernet/sfc/ef10.c | 2 +-
drivers/net/ethernet/sfc/ef100.c | 6 +-
drivers/net/ethernet/sfc/ef100_iova.c | 205 +++++
drivers/net/ethernet/sfc/ef100_iova.h | 40 +
drivers/net/ethernet/sfc/ef100_nic.c | 126 ++-
drivers/net/ethernet/sfc/ef100_nic.h | 22 +
drivers/net/ethernet/sfc/ef100_vdpa.c | 693 +++++++++++++++++
drivers/net/ethernet/sfc/ef100_vdpa.h | 241 ++++++
drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 897 ++++++++++++++++++++++
drivers/net/ethernet/sfc/mcdi.h | 7 +
drivers/net/ethernet/sfc/mcdi_filters.c | 51 +-
drivers/net/ethernet/sfc/mcdi_functions.c | 9 +-
drivers/net/ethernet/sfc/mcdi_functions.h | 3 +-
drivers/net/ethernet/sfc/mcdi_vdpa.c | 268 +++++++
drivers/net/ethernet/sfc/mcdi_vdpa.h | 84 ++
drivers/net/ethernet/sfc/net_driver.h | 19 +
18 files changed, 2650 insertions(+), 33 deletions(-)
create mode 100644 drivers/net/ethernet/sfc/ef100_iova.c
create mode 100644 drivers/net/ethernet/sfc/ef100_iova.h
create mode 100644 drivers/net/ethernet/sfc/ef100_vdpa.c
create mode 100644 drivers/net/ethernet/sfc/ef100_vdpa.h
create mode 100644 drivers/net/ethernet/sfc/ef100_vdpa_ops.c
create mode 100644 drivers/net/ethernet/sfc/mcdi_vdpa.c
create mode 100644 drivers/net/ethernet/sfc/mcdi_vdpa.h
--
2.30.1
vDPA config operations can be broadly categorized in to either
virtqueue operations, device operations or DMA operations.
This patch implements most of the device level config operations.
SN1022 supports VIRTIO_F_IN_ORDER which is supported by the DPDK
virtio driver but not the kernel virtio driver. Due to a bug in
QEMU (https://gitlab.com/qemu-project/qemu/-/issues/331#), with
vhost-vdpa, this feature bit is returned with guest kernel virtio
driver in set_features config operation. The fix for this bug
(qemu_commit c33f23a419f95da16ab4faaf08be635c89b96ff0) is available
in QEMU versions 6.1.0 and later. Hence, that's the oldest QEMU
version required for testing with the vhost-vdpa driver.
With older QEMU releases, VIRTIO_F_IN_ORDER is negotiated but
not honored causing Firmware exception.
Signed-off-by: Gautam Dawar <[email protected]>
---
drivers/net/ethernet/sfc/ef100_vdpa.h | 14 ++
drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 148 ++++++++++++++++++++++
2 files changed, 162 insertions(+)
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
index 83f6d819f6a5..be7650c3166a 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa.h
+++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
@@ -21,6 +21,18 @@
/* Max queue pairs currently supported */
#define EF100_VDPA_MAX_QUEUES_PAIRS 1
+/* Device ID of a virtio net device */
+#define EF100_VDPA_VIRTIO_NET_DEVICE_ID VIRTIO_ID_NET
+
+/* Vendor ID of Xilinx vDPA NIC */
+#define EF100_VDPA_VENDOR_ID PCI_VENDOR_ID_XILINX
+
+/* Max number of Buffers supported in the virtqueue */
+#define EF100_VDPA_VQ_NUM_MAX_SIZE 512
+
+/* Alignment requirement of the Virtqueue */
+#define EF100_VDPA_VQ_ALIGN 4096
+
/**
* enum ef100_vdpa_nic_state - possible states for a vDPA NIC
*
@@ -61,6 +73,7 @@ enum ef100_vdpa_vq_type {
* @net_config: virtio_net_config data
* @mac_address: mac address of interface associated with this vdpa device
* @mac_configured: true after MAC address is configured
+ * @cfg_cb: callback for config change
*/
struct ef100_vdpa_nic {
struct vdpa_device vdpa_dev;
@@ -76,6 +89,7 @@ struct ef100_vdpa_nic {
struct virtio_net_config net_config;
u8 *mac_address;
bool mac_configured;
+ struct vdpa_callback cfg_cb;
};
int ef100_vdpa_init(struct efx_probe_data *probe_data);
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
index 31952931c198..87899baa1c52 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
+++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
@@ -10,12 +10,148 @@
#include <linux/vdpa.h>
#include "ef100_vdpa.h"
+#include "mcdi_vdpa.h"
static struct ef100_vdpa_nic *get_vdpa_nic(struct vdpa_device *vdev)
{
return container_of(vdev, struct ef100_vdpa_nic, vdpa_dev);
}
+static u32 ef100_vdpa_get_vq_align(struct vdpa_device *vdev)
+{
+ return EF100_VDPA_VQ_ALIGN;
+}
+
+static u64 ef100_vdpa_get_device_features(struct vdpa_device *vdev)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+ u64 features;
+ int rc;
+
+ rc = efx_vdpa_get_features(vdpa_nic->efx,
+ EF100_VDPA_DEVICE_TYPE_NET, &features);
+ if (rc) {
+ dev_err(&vdev->dev, "%s: MCDI get features error:%d\n",
+ __func__, rc);
+ /* Returning 0 as value of features will lead to failure
+ * of feature negotiation.
+ */
+ return 0;
+ }
+
+ /* SN1022 supports VIRTIO_F_IN_ORDER which is supported by the DPDK
+ * virtio driver but not the kernel virtio driver. Due to a bug in
+ * QEMU (https://gitlab.com/qemu-project/qemu/-/issues/331#), with
+ * vhost-vdpa, this feature bit is returned with guest kernel virtio
+ * driver in set_features config operation. The fix for this bug
+ * (commit c33f23a419f95da16ab4faaf08be635c89b96ff0) is available
+ * in QEMU versions 6.1.0 and later. Hence, that's the oldest QEMU
+ * version required for testing with the vhost-vdpa driver.
+ */
+ features |= BIT_ULL(VIRTIO_NET_F_MAC);
+
+ return features;
+}
+
+static int ef100_vdpa_set_driver_features(struct vdpa_device *vdev,
+ u64 features)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+ u64 verify_features;
+ int rc;
+
+ mutex_lock(&vdpa_nic->lock);
+ if (vdpa_nic->vdpa_state != EF100_VDPA_STATE_INITIALIZED) {
+ dev_err(&vdev->dev, "%s: Invalid state %u\n",
+ __func__, vdpa_nic->vdpa_state);
+ rc = -EINVAL;
+ goto err;
+ }
+ verify_features = features & ~BIT_ULL(VIRTIO_NET_F_MAC);
+ rc = efx_vdpa_verify_features(vdpa_nic->efx,
+ EF100_VDPA_DEVICE_TYPE_NET,
+ verify_features);
+
+ if (rc) {
+ dev_err(&vdev->dev, "%s: MCDI verify features error:%d\n",
+ __func__, rc);
+ goto err;
+ }
+
+ vdpa_nic->features = features;
+err:
+ mutex_unlock(&vdpa_nic->lock);
+ return rc;
+}
+
+static u64 ef100_vdpa_get_driver_features(struct vdpa_device *vdev)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+
+ return vdpa_nic->features;
+}
+
+static void ef100_vdpa_set_config_cb(struct vdpa_device *vdev,
+ struct vdpa_callback *cb)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+
+ if (cb)
+ vdpa_nic->cfg_cb = *cb;
+}
+
+static u16 ef100_vdpa_get_vq_num_max(struct vdpa_device *vdev)
+{
+ return EF100_VDPA_VQ_NUM_MAX_SIZE;
+}
+
+static u32 ef100_vdpa_get_device_id(struct vdpa_device *vdev)
+{
+ return EF100_VDPA_VIRTIO_NET_DEVICE_ID;
+}
+
+static u32 ef100_vdpa_get_vendor_id(struct vdpa_device *vdev)
+{
+ return EF100_VDPA_VENDOR_ID;
+}
+
+static size_t ef100_vdpa_get_config_size(struct vdpa_device *vdev)
+{
+ return sizeof(struct virtio_net_config);
+}
+
+static void ef100_vdpa_get_config(struct vdpa_device *vdev,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+
+ /* Avoid the possibility of wrap-up after the sum exceeds U32_MAX */
+ if (WARN_ON(((u64)offset + len) > sizeof(struct virtio_net_config))) {
+ dev_err(&vdev->dev,
+ "%s: Offset + len exceeds config size\n", __func__);
+ return;
+ }
+ memcpy(buf, (u8 *)&vdpa_nic->net_config + offset, len);
+}
+
+static void ef100_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
+ const void *buf, unsigned int len)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+
+ /* Avoid the possibility of wrap-up after the sum exceeds U32_MAX */
+ if (WARN_ON(((u64)offset + len) > sizeof(vdpa_nic->net_config))) {
+ dev_err(&vdev->dev,
+ "%s: Offset + len exceeds config size\n", __func__);
+ return;
+ }
+
+ memcpy((u8 *)&vdpa_nic->net_config + offset, buf, len);
+ if (is_valid_ether_addr(vdpa_nic->mac_address))
+ vdpa_nic->mac_configured = true;
+}
+
static void ef100_vdpa_free(struct vdpa_device *vdev)
{
struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
@@ -24,5 +160,17 @@ static void ef100_vdpa_free(struct vdpa_device *vdev)
}
const struct vdpa_config_ops ef100_vdpa_config_ops = {
+ .get_vq_align = ef100_vdpa_get_vq_align,
+ .get_device_features = ef100_vdpa_get_device_features,
+ .set_driver_features = ef100_vdpa_set_driver_features,
+ .get_driver_features = ef100_vdpa_get_driver_features,
+ .set_config_cb = ef100_vdpa_set_config_cb,
+ .get_vq_num_max = ef100_vdpa_get_vq_num_max,
+ .get_device_id = ef100_vdpa_get_device_id,
+ .get_vendor_id = ef100_vdpa_get_vendor_id,
+ .get_config_size = ef100_vdpa_get_config_size,
+ .get_config = ef100_vdpa_get_config,
+ .set_config = ef100_vdpa_set_config,
+ .get_generation = NULL,
.free = ef100_vdpa_free,
};
--
2.30.1
Implement unicast, broadcast and unknown multicast
filters for receiving different types of traffic.
Signed-off-by: Gautam Dawar <[email protected]>
---
drivers/net/ethernet/sfc/ef100_vdpa.c | 159 ++++++++++++++++++++++
drivers/net/ethernet/sfc/ef100_vdpa.h | 35 +++++
drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 27 +++-
3 files changed, 220 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.c b/drivers/net/ethernet/sfc/ef100_vdpa.c
index 41eb7aef6798..04d64bfe3c93 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa.c
+++ b/drivers/net/ethernet/sfc/ef100_vdpa.c
@@ -17,12 +17,168 @@
#include "mcdi_filters.h"
#include "mcdi_functions.h"
#include "ef100_netdev.h"
+#include "filter.h"
+#include "efx.h"
+#define EFX_INVALID_FILTER_ID -1
+
+/* vDPA queues starts from 2nd VI or qid 1 */
+#define EF100_VDPA_BASE_RX_QID 1
+
+static const char * const filter_names[] = { "bcast", "ucast", "mcast" };
static struct virtio_device_id ef100_vdpa_id_table[] = {
{ .device = VIRTIO_ID_NET, .vendor = PCI_VENDOR_ID_REDHAT_QUMRANET },
{ 0 },
};
+static int ef100_vdpa_set_mac_filter(struct efx_nic *efx,
+ struct efx_filter_spec *spec,
+ u32 qid,
+ u8 *mac_addr)
+{
+ int rc;
+
+ efx_filter_init_rx(spec, EFX_FILTER_PRI_AUTO, 0, qid);
+
+ if (mac_addr) {
+ rc = efx_filter_set_eth_local(spec, EFX_FILTER_VID_UNSPEC,
+ mac_addr);
+ if (rc)
+ pci_err(efx->pci_dev,
+ "Filter set eth local failed, err: %d\n", rc);
+ } else {
+ efx_filter_set_mc_def(spec);
+ }
+
+ rc = efx_filter_insert_filter(efx, spec, true);
+ if (rc < 0)
+ pci_err(efx->pci_dev,
+ "Filter insert failed, err: %d\n", rc);
+
+ return rc;
+}
+
+static int ef100_vdpa_delete_filter(struct ef100_vdpa_nic *vdpa_nic,
+ enum ef100_vdpa_mac_filter_type type)
+{
+ struct vdpa_device *vdev = &vdpa_nic->vdpa_dev;
+ int rc;
+
+ if (vdpa_nic->filters[type].filter_id == EFX_INVALID_FILTER_ID)
+ return rc;
+
+ rc = efx_filter_remove_id_safe(vdpa_nic->efx,
+ EFX_FILTER_PRI_AUTO,
+ vdpa_nic->filters[type].filter_id);
+ if (rc) {
+ dev_err(&vdev->dev, "%s filter id: %d remove failed, err: %d\n",
+ filter_names[type], vdpa_nic->filters[type].filter_id,
+ rc);
+ } else {
+ vdpa_nic->filters[type].filter_id = EFX_INVALID_FILTER_ID;
+ vdpa_nic->filter_cnt--;
+ }
+ return rc;
+}
+
+int ef100_vdpa_add_filter(struct ef100_vdpa_nic *vdpa_nic,
+ enum ef100_vdpa_mac_filter_type type)
+{
+ struct vdpa_device *vdev = &vdpa_nic->vdpa_dev;
+ struct efx_nic *efx = vdpa_nic->efx;
+ /* Configure filter on base Rx queue only */
+ u32 qid = EF100_VDPA_BASE_RX_QID;
+ struct efx_filter_spec *spec;
+ u8 baddr[ETH_ALEN];
+ int rc;
+
+ /* remove existing filter */
+ rc = ef100_vdpa_delete_filter(vdpa_nic, type);
+ if (rc < 0) {
+ dev_err(&vdev->dev, "%s MAC filter deletion failed, err: %d",
+ filter_names[type], rc);
+ return rc;
+ }
+
+ /* Configure MAC Filter */
+ spec = &vdpa_nic->filters[type].spec;
+ if (type == EF100_VDPA_BCAST_MAC_FILTER) {
+ eth_broadcast_addr(baddr);
+ rc = ef100_vdpa_set_mac_filter(efx, spec, qid, baddr);
+ } else if (type == EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER) {
+ rc = ef100_vdpa_set_mac_filter(efx, spec, qid, NULL);
+ } else {
+ /* Ensure we have everything required to insert the filter */
+ if (!vdpa_nic->mac_configured ||
+ !vdpa_nic->vring[0].vring_created ||
+ !is_valid_ether_addr(vdpa_nic->mac_address))
+ return -EINVAL;
+
+ rc = ef100_vdpa_set_mac_filter(efx, spec, qid,
+ vdpa_nic->mac_address);
+ }
+
+ if (rc >= 0) {
+ vdpa_nic->filters[type].filter_id = rc;
+ vdpa_nic->filter_cnt++;
+
+ return 0;
+ }
+
+ dev_err(&vdev->dev, "%s MAC filter insert failed, err: %d\n",
+ filter_names[type], rc);
+
+ if (type != EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER) {
+ ef100_vdpa_filter_remove(vdpa_nic);
+ return rc;
+ }
+
+ return 0;
+}
+
+int ef100_vdpa_filter_remove(struct ef100_vdpa_nic *vdpa_nic)
+{
+ enum ef100_vdpa_mac_filter_type filter;
+ int err = 0;
+ int rc;
+
+ for (filter = EF100_VDPA_BCAST_MAC_FILTER;
+ filter <= EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER; filter++) {
+ rc = ef100_vdpa_delete_filter(vdpa_nic, filter);
+ if (rc < 0)
+ /* store status of last failed filter remove */
+ err = rc;
+ }
+ return err;
+}
+
+int ef100_vdpa_filter_configure(struct ef100_vdpa_nic *vdpa_nic)
+{
+ struct vdpa_device *vdev = &vdpa_nic->vdpa_dev;
+ enum ef100_vdpa_mac_filter_type filter;
+ int rc;
+
+ /* remove existing filters, if any */
+ rc = ef100_vdpa_filter_remove(vdpa_nic);
+ if (rc < 0) {
+ dev_err(&vdev->dev,
+ "MAC filter deletion failed, err: %d", rc);
+ goto fail;
+ }
+
+ for (filter = EF100_VDPA_BCAST_MAC_FILTER;
+ filter <= EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER; filter++) {
+ if (filter == EF100_VDPA_UCAST_MAC_FILTER &&
+ !vdpa_nic->mac_configured)
+ continue;
+ rc = ef100_vdpa_add_filter(vdpa_nic, filter);
+ if (rc < 0)
+ goto fail;
+ }
+fail:
+ return rc;
+}
+
int ef100_vdpa_init(struct efx_probe_data *probe_data)
{
struct efx_nic *efx = &probe_data->efx;
@@ -168,6 +324,9 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
ether_addr_copy(vdpa_nic->mac_address, mac);
vdpa_nic->mac_configured = true;
+ for (i = 0; i < EF100_VDPA_MAC_FILTER_NTYPES; i++)
+ vdpa_nic->filters[i].filter_id = EFX_INVALID_FILTER_ID;
+
for (i = 0; i < (2 * vdpa_nic->max_queue_pairs); i++)
vdpa_nic->vring[i].irq = -EINVAL;
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
index 3cc33daa0431..a33edd6dda12 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa.h
+++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
@@ -72,6 +72,22 @@ enum ef100_vdpa_vq_type {
EF100_VDPA_VQ_NTYPES
};
+/**
+ * enum ef100_vdpa_mac_filter_type - vdpa filter types
+ *
+ * @EF100_VDPA_BCAST_MAC_FILTER: Broadcast MAC filter
+ * @EF100_VDPA_UCAST_MAC_FILTER: Unicast MAC filter
+ * @EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER: Unknown multicast MAC filter to allow
+ * IPv6 Neighbor Solicitation Message
+ * @EF100_VDPA_MAC_FILTER_NTYPES: Number of vDPA filter types
+ */
+enum ef100_vdpa_mac_filter_type {
+ EF100_VDPA_BCAST_MAC_FILTER,
+ EF100_VDPA_UCAST_MAC_FILTER,
+ EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER,
+ EF100_VDPA_MAC_FILTER_NTYPES,
+};
+
/**
* struct ef100_vdpa_vring_info - vDPA vring data structure
*
@@ -109,6 +125,17 @@ struct ef100_vdpa_vring_info {
struct vdpa_callback cb;
};
+/**
+ * struct ef100_vdpa_filter - vDPA filter data structure
+ *
+ * @filter_id: filter id of this filter
+ * @efx_filter_spec: hardware filter specs for this vdpa device
+ */
+struct ef100_vdpa_filter {
+ s32 filter_id;
+ struct efx_filter_spec spec;
+};
+
/**
* struct ef100_vdpa_nic - vDPA NIC data structure
*
@@ -118,6 +145,7 @@ struct ef100_vdpa_vring_info {
* @lock: Managing access to vdpa config operations
* @pf_index: PF index of the vDPA VF
* @vf_index: VF index of the vDPA VF
+ * @filter_cnt: total number of filters created on this vdpa device
* @status: device status as per VIRTIO spec
* @features: negotiated feature bits
* @max_queue_pairs: maximum number of queue pairs supported
@@ -125,6 +153,7 @@ struct ef100_vdpa_vring_info {
* @vring: vring information of the vDPA device.
* @mac_address: mac address of interface associated with this vdpa device
* @mac_configured: true after MAC address is configured
+ * @filters: details of all filters created on this vdpa device
* @cfg_cb: callback for config change
*/
struct ef100_vdpa_nic {
@@ -135,6 +164,7 @@ struct ef100_vdpa_nic {
struct mutex lock;
u32 pf_index;
u32 vf_index;
+ u32 filter_cnt;
u8 status;
u64 features;
u32 max_queue_pairs;
@@ -142,6 +172,7 @@ struct ef100_vdpa_nic {
struct ef100_vdpa_vring_info vring[EF100_VDPA_MAX_QUEUES_PAIRS * 2];
u8 *mac_address;
bool mac_configured;
+ struct ef100_vdpa_filter filters[EF100_VDPA_MAC_FILTER_NTYPES];
struct vdpa_callback cfg_cb;
};
@@ -149,6 +180,10 @@ int ef100_vdpa_init(struct efx_probe_data *probe_data);
void ef100_vdpa_fini(struct efx_probe_data *probe_data);
int ef100_vdpa_register_mgmtdev(struct efx_nic *efx);
void ef100_vdpa_unregister_mgmtdev(struct efx_nic *efx);
+int ef100_vdpa_filter_configure(struct ef100_vdpa_nic *vdpa_nic);
+int ef100_vdpa_filter_remove(struct ef100_vdpa_nic *vdpa_nic);
+int ef100_vdpa_add_filter(struct ef100_vdpa_nic *vdpa_nic,
+ enum ef100_vdpa_mac_filter_type type);
int ef100_vdpa_irq_vectors_alloc(struct pci_dev *pci_dev, u16 nvqs);
void ef100_vdpa_irq_vectors_free(void *data);
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
index b7efd3e0c901..132ddb4a647b 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
+++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
@@ -135,6 +135,15 @@ static int delete_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
if (vdpa_nic->vring[idx].vring_ctx)
delete_vring_ctx(vdpa_nic, idx);
+ if (idx == 0 && vdpa_nic->filter_cnt != 0) {
+ rc = ef100_vdpa_filter_remove(vdpa_nic);
+ if (rc < 0) {
+ dev_err(&vdpa_nic->vdpa_dev.dev,
+ "%s: vdpa remove filter failed, err:%d\n",
+ __func__, rc);
+ }
+ }
+
return rc;
}
@@ -193,8 +202,22 @@ static int create_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
vdpa_nic->vring[idx].doorbell_offset_valid = true;
}
+ /* Configure filters on rxq 0 */
+ if (idx == 0) {
+ rc = ef100_vdpa_filter_configure(vdpa_nic);
+ if (rc < 0) {
+ dev_err(&vdpa_nic->vdpa_dev.dev,
+ "%s: vdpa configure filter failed, err:%d\n",
+ __func__, rc);
+ goto err_filter_configure;
+ }
+ }
+
return 0;
+err_filter_configure:
+ ef100_vdpa_filter_remove(vdpa_nic);
+ vdpa_nic->vring[idx].doorbell_offset_valid = false;
err_get_doorbell_offset:
efx_vdpa_vring_destroy(vdpa_nic->vring[idx].vring_ctx,
&vring_dyn_cfg);
@@ -578,8 +601,10 @@ static void ef100_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
}
memcpy((u8 *)&vdpa_nic->net_config + offset, buf, len);
- if (is_valid_ether_addr(vdpa_nic->mac_address))
+ if (is_valid_ether_addr(vdpa_nic->mac_address)) {
vdpa_nic->mac_configured = true;
+ ef100_vdpa_add_filter(vdpa_nic, EF100_VDPA_UCAST_MAC_FILTER);
+ }
}
static void ef100_vdpa_free(struct vdpa_device *vdev)
--
2.30.1
This patch implements the vDPA config operations related to
virtqueues or vrings. These include setting vring address,
getting vq state, operations to enable/disable a vq etc.
The resources required for vring operations eg. VI, interrupts etc.
are also allocated.
Signed-off-by: Gautam Dawar <[email protected]>
---
drivers/net/ethernet/sfc/ef100_vdpa.c | 58 ++-
drivers/net/ethernet/sfc/ef100_vdpa.h | 55 +++
drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 449 +++++++++++++++++++++-
3 files changed, 560 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.c b/drivers/net/ethernet/sfc/ef100_vdpa.c
index ff4bb61e598e..41eb7aef6798 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa.c
+++ b/drivers/net/ethernet/sfc/ef100_vdpa.c
@@ -15,6 +15,7 @@
#include "ef100_vdpa.h"
#include "mcdi_vdpa.h"
#include "mcdi_filters.h"
+#include "mcdi_functions.h"
#include "ef100_netdev.h"
static struct virtio_device_id ef100_vdpa_id_table[] = {
@@ -48,6 +49,24 @@ int ef100_vdpa_init(struct efx_probe_data *probe_data)
return rc;
}
+static int vdpa_allocate_vis(struct efx_nic *efx, unsigned int *allocated_vis)
+{
+ /* The first VI is reserved for MCDI
+ * 1 VI each for rx + tx ring
+ */
+ unsigned int max_vis = 1 + EF100_VDPA_MAX_QUEUES_PAIRS;
+ unsigned int min_vis = 1 + 1;
+ int rc;
+
+ rc = efx_mcdi_alloc_vis(efx, min_vis, max_vis,
+ NULL, allocated_vis);
+ if (!rc)
+ return rc;
+ if (*allocated_vis < min_vis)
+ return -ENOSPC;
+ return 0;
+}
+
static void ef100_vdpa_delete(struct efx_nic *efx)
{
if (efx->vdpa_nic) {
@@ -55,6 +74,7 @@ static void ef100_vdpa_delete(struct efx_nic *efx)
put_device(&efx->vdpa_nic->vdpa_dev.dev);
efx->vdpa_nic = NULL;
}
+ efx_mcdi_free_vis(efx);
}
void ef100_vdpa_fini(struct efx_probe_data *probe_data)
@@ -106,10 +126,20 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
{
struct ef100_nic_data *nic_data = efx->nic_data;
struct ef100_vdpa_nic *vdpa_nic;
+ unsigned int allocated_vis;
struct device *dev;
int rc;
+ u8 i;
nic_data->vdpa_class = dev_type;
+ rc = vdpa_allocate_vis(efx, &allocated_vis);
+ if (rc) {
+ pci_err(efx->pci_dev,
+ "%s Alloc VIs failed for vf:%u error:%d\n",
+ __func__, nic_data->vf_index, rc);
+ return ERR_PTR(rc);
+ }
+
vdpa_nic = vdpa_alloc_device(struct ef100_vdpa_nic,
vdpa_dev, &efx->pci_dev->dev,
&ef100_vdpa_config_ops,
@@ -120,7 +150,8 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
"vDPA device allocation failed for vf: %u\n",
nic_data->vf_index);
nic_data->vdpa_class = EF100_VDPA_CLASS_NONE;
- return ERR_PTR(-ENOMEM);
+ rc = -ENOMEM;
+ goto err_alloc_vis_free;
}
mutex_init(&vdpa_nic->lock);
@@ -129,6 +160,7 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
vdpa_nic->vdpa_dev.dma_dev = &efx->pci_dev->dev;
vdpa_nic->vdpa_dev.mdev = efx->mgmt_dev;
vdpa_nic->efx = efx;
+ vdpa_nic->max_queue_pairs = allocated_vis - 1;
vdpa_nic->pf_index = nic_data->pf_index;
vdpa_nic->vf_index = nic_data->vf_index;
vdpa_nic->vdpa_state = EF100_VDPA_STATE_INITIALIZED;
@@ -136,6 +168,27 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
ether_addr_copy(vdpa_nic->mac_address, mac);
vdpa_nic->mac_configured = true;
+ for (i = 0; i < (2 * vdpa_nic->max_queue_pairs); i++)
+ vdpa_nic->vring[i].irq = -EINVAL;
+
+ rc = ef100_vdpa_irq_vectors_alloc(efx->pci_dev,
+ vdpa_nic->max_queue_pairs * 2);
+ if (rc < 0) {
+ pci_err(efx->pci_dev,
+ "vDPA IRQ alloc failed for vf: %u err:%d\n",
+ nic_data->vf_index, rc);
+ goto err_put_device;
+ }
+
+ rc = devm_add_action_or_reset(&efx->pci_dev->dev,
+ ef100_vdpa_irq_vectors_free,
+ efx->pci_dev);
+ if (rc) {
+ pci_err(efx->pci_dev,
+ "Failed adding devres for freeing irq vectors\n");
+ goto err_put_device;
+ }
+
rc = get_net_config(vdpa_nic);
if (rc)
goto err_put_device;
@@ -147,6 +200,9 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
err_put_device:
/* put_device invokes ef100_vdpa_free */
put_device(&vdpa_nic->vdpa_dev.dev);
+
+err_alloc_vis_free:
+ efx_mcdi_free_vis(efx);
return ERR_PTR(rc);
}
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
index be7650c3166a..3cc33daa0431 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa.h
+++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
@@ -33,6 +33,20 @@
/* Alignment requirement of the Virtqueue */
#define EF100_VDPA_VQ_ALIGN 4096
+/* Vring configuration definitions */
+#define EF100_VRING_ADDRESS_CONFIGURED 0x1
+#define EF100_VRING_SIZE_CONFIGURED 0x10
+#define EF100_VRING_READY_CONFIGURED 0x100
+#define EF100_VRING_CONFIGURED (EF100_VRING_ADDRESS_CONFIGURED | \
+ EF100_VRING_SIZE_CONFIGURED | \
+ EF100_VRING_READY_CONFIGURED)
+
+/* Maximum size of msix name */
+#define EF100_VDPA_MAX_MSIX_NAME_SIZE 256
+
+/* Default high IOVA for MCDI buffer */
+#define EF100_VDPA_IOVA_BASE_ADDR 0x20000000000
+
/**
* enum ef100_vdpa_nic_state - possible states for a vDPA NIC
*
@@ -58,6 +72,43 @@ enum ef100_vdpa_vq_type {
EF100_VDPA_VQ_NTYPES
};
+/**
+ * struct ef100_vdpa_vring_info - vDPA vring data structure
+ *
+ * @desc: Descriptor area address of the vring
+ * @avail: Available area address of the vring
+ * @used: Device area address of the vring
+ * @size: Number of entries in the vring
+ * @vring_state: bit map to track vring configuration
+ * @vring_created: set to true when vring is created.
+ * @last_avail_idx: last available index of the vring
+ * @last_used_idx: last used index of the vring
+ * @doorbell_offset: doorbell offset
+ * @doorbell_offset_valid: true if @doorbell_offset is updated
+ * @vring_type: type of vring created
+ * @vring_ctx: vring context information
+ * @msix_name: device name for vring irq handler
+ * @irq: irq number for vring irq handler
+ * @cb: callback for vring interrupts
+ */
+struct ef100_vdpa_vring_info {
+ dma_addr_t desc;
+ dma_addr_t avail;
+ dma_addr_t used;
+ u32 size;
+ u16 vring_state;
+ bool vring_created;
+ u32 last_avail_idx;
+ u32 last_used_idx;
+ u32 doorbell_offset;
+ bool doorbell_offset_valid;
+ enum ef100_vdpa_vq_type vring_type;
+ struct efx_vring_ctx *vring_ctx;
+ char msix_name[EF100_VDPA_MAX_MSIX_NAME_SIZE];
+ u32 irq;
+ struct vdpa_callback cb;
+};
+
/**
* struct ef100_vdpa_nic - vDPA NIC data structure
*
@@ -71,6 +122,7 @@ enum ef100_vdpa_vq_type {
* @features: negotiated feature bits
* @max_queue_pairs: maximum number of queue pairs supported
* @net_config: virtio_net_config data
+ * @vring: vring information of the vDPA device.
* @mac_address: mac address of interface associated with this vdpa device
* @mac_configured: true after MAC address is configured
* @cfg_cb: callback for config change
@@ -87,6 +139,7 @@ struct ef100_vdpa_nic {
u64 features;
u32 max_queue_pairs;
struct virtio_net_config net_config;
+ struct ef100_vdpa_vring_info vring[EF100_VDPA_MAX_QUEUES_PAIRS * 2];
u8 *mac_address;
bool mac_configured;
struct vdpa_callback cfg_cb;
@@ -96,6 +149,8 @@ int ef100_vdpa_init(struct efx_probe_data *probe_data);
void ef100_vdpa_fini(struct efx_probe_data *probe_data);
int ef100_vdpa_register_mgmtdev(struct efx_nic *efx);
void ef100_vdpa_unregister_mgmtdev(struct efx_nic *efx);
+int ef100_vdpa_irq_vectors_alloc(struct pci_dev *pci_dev, u16 nvqs);
+void ef100_vdpa_irq_vectors_free(void *data);
static inline bool efx_vdpa_is_little_endian(struct ef100_vdpa_nic *vdpa_nic)
{
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
index 87899baa1c52..b7efd3e0c901 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
+++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
@@ -10,13 +10,441 @@
#include <linux/vdpa.h>
#include "ef100_vdpa.h"
+#include "io.h"
#include "mcdi_vdpa.h"
+/* Get the queue's function-local index of the associated VI
+ * virtqueue number queue 0 is reserved for MCDI
+ */
+#define EFX_GET_VI_INDEX(vq_num) (((vq_num) / 2) + 1)
+
static struct ef100_vdpa_nic *get_vdpa_nic(struct vdpa_device *vdev)
{
return container_of(vdev, struct ef100_vdpa_nic, vdpa_dev);
}
+static irqreturn_t vring_intr_handler(int irq, void *arg)
+{
+ struct ef100_vdpa_vring_info *vring = arg;
+
+ if (vring->cb.callback)
+ return vring->cb.callback(vring->cb.private);
+
+ return IRQ_NONE;
+}
+
+int ef100_vdpa_irq_vectors_alloc(struct pci_dev *pci_dev, u16 nvqs)
+{
+ int rc;
+
+ rc = pci_alloc_irq_vectors(pci_dev, nvqs, nvqs, PCI_IRQ_MSIX);
+ if (rc < 0)
+ pci_err(pci_dev,
+ "Failed to alloc %d IRQ vectors, err:%d\n", nvqs, rc);
+ return rc;
+}
+
+void ef100_vdpa_irq_vectors_free(void *data)
+{
+ pci_free_irq_vectors(data);
+}
+
+static int irq_vring_init(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
+{
+ struct ef100_vdpa_vring_info *vring = &vdpa_nic->vring[idx];
+ struct pci_dev *pci_dev = vdpa_nic->efx->pci_dev;
+ int irq;
+ int rc;
+
+ snprintf(vring->msix_name, 256, "x_vdpa[%s]-%d\n",
+ pci_name(pci_dev), idx);
+ irq = pci_irq_vector(pci_dev, idx);
+ rc = devm_request_irq(&pci_dev->dev, irq, vring_intr_handler, 0,
+ vring->msix_name, vring);
+ if (rc)
+ pci_err(pci_dev,
+ "devm_request_irq failed for vring %d, rc %d\n",
+ idx, rc);
+ else
+ vring->irq = irq;
+
+ return rc;
+}
+
+static void irq_vring_fini(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
+{
+ struct ef100_vdpa_vring_info *vring = &vdpa_nic->vring[idx];
+ struct pci_dev *pci_dev = vdpa_nic->efx->pci_dev;
+
+ devm_free_irq(&pci_dev->dev, vring->irq, vring);
+ vring->irq = -EINVAL;
+}
+
+static bool can_create_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
+{
+ if (vdpa_nic->vring[idx].vring_state == EF100_VRING_CONFIGURED &&
+ vdpa_nic->status & VIRTIO_CONFIG_S_DRIVER_OK &&
+ !vdpa_nic->vring[idx].vring_created)
+ return true;
+
+ return false;
+}
+
+static int create_vring_ctx(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
+{
+ struct efx_vring_ctx *vring_ctx;
+ u32 vi_index;
+
+ if (idx % 2) /* Even VQ for RX and odd for TX */
+ vdpa_nic->vring[idx].vring_type = EF100_VDPA_VQ_TYPE_NET_TXQ;
+ else
+ vdpa_nic->vring[idx].vring_type = EF100_VDPA_VQ_TYPE_NET_RXQ;
+ vi_index = EFX_GET_VI_INDEX(idx);
+ vring_ctx = efx_vdpa_vring_init(vdpa_nic->efx, vi_index,
+ vdpa_nic->vring[idx].vring_type);
+ if (IS_ERR(vring_ctx))
+ return PTR_ERR(vring_ctx);
+
+ vdpa_nic->vring[idx].vring_ctx = vring_ctx;
+ return 0;
+}
+
+static void delete_vring_ctx(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
+{
+ efx_vdpa_vring_fini(vdpa_nic->vring[idx].vring_ctx);
+ vdpa_nic->vring[idx].vring_ctx = NULL;
+}
+
+static int delete_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
+{
+ struct efx_vring_dyn_cfg vring_dyn_cfg;
+ int rc;
+
+ rc = efx_vdpa_vring_destroy(vdpa_nic->vring[idx].vring_ctx,
+ &vring_dyn_cfg);
+ if (rc)
+ dev_err(&vdpa_nic->vdpa_dev.dev,
+ "%s: delete vring failed index:%u, err:%d\n",
+ __func__, idx, rc);
+ vdpa_nic->vring[idx].last_avail_idx = vring_dyn_cfg.avail_idx;
+ vdpa_nic->vring[idx].last_used_idx = vring_dyn_cfg.used_idx;
+ vdpa_nic->vring[idx].vring_created = false;
+
+ irq_vring_fini(vdpa_nic, idx);
+
+ if (vdpa_nic->vring[idx].vring_ctx)
+ delete_vring_ctx(vdpa_nic, idx);
+
+ return rc;
+}
+
+static int create_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
+{
+ struct efx_vring_dyn_cfg vring_dyn_cfg;
+ struct efx_vring_ctx *vring_ctx;
+ struct efx_vring_cfg vring_cfg;
+ u32 offset;
+ int rc;
+
+ if (!vdpa_nic->vring[idx].vring_ctx) {
+ rc = create_vring_ctx(vdpa_nic, idx);
+ if (rc) {
+ dev_err(&vdpa_nic->vdpa_dev.dev,
+ "%s: create_vring_ctx failed idx:%u, err:%d\n",
+ __func__, idx, rc);
+ return rc;
+ }
+ }
+ vring_ctx = vdpa_nic->vring[idx].vring_ctx;
+
+ rc = irq_vring_init(vdpa_nic, idx);
+ if (rc) {
+ dev_err(&vdpa_nic->vdpa_dev.dev,
+ "%s: irq_vring_init failed. index:%u, err:%d\n",
+ __func__, idx, rc);
+ goto err_irq_vring_init;
+ }
+ vring_cfg.desc = vdpa_nic->vring[idx].desc;
+ vring_cfg.avail = vdpa_nic->vring[idx].avail;
+ vring_cfg.used = vdpa_nic->vring[idx].used;
+ vring_cfg.size = vdpa_nic->vring[idx].size;
+ vring_cfg.features = vdpa_nic->features;
+ vring_cfg.msix_vector = idx;
+ vring_dyn_cfg.avail_idx = vdpa_nic->vring[idx].last_avail_idx;
+ vring_dyn_cfg.used_idx = vdpa_nic->vring[idx].last_used_idx;
+
+ rc = efx_vdpa_vring_create(vring_ctx, &vring_cfg, &vring_dyn_cfg);
+ if (rc) {
+ dev_err(&vdpa_nic->vdpa_dev.dev,
+ "%s: vring_create failed index:%u, err:%d\n",
+ __func__, idx, rc);
+ goto err_vring_create;
+ }
+ vdpa_nic->vring[idx].vring_created = true;
+ if (!vdpa_nic->vring[idx].doorbell_offset_valid) {
+ rc = efx_vdpa_get_doorbell_offset(vring_ctx, &offset);
+ if (rc) {
+ dev_err(&vdpa_nic->vdpa_dev.dev,
+ "%s: get offset failed, index:%u err:%d\n",
+ __func__, idx, rc);
+ goto err_get_doorbell_offset;
+ }
+ vdpa_nic->vring[idx].doorbell_offset = offset;
+ vdpa_nic->vring[idx].doorbell_offset_valid = true;
+ }
+
+ return 0;
+
+err_get_doorbell_offset:
+ efx_vdpa_vring_destroy(vdpa_nic->vring[idx].vring_ctx,
+ &vring_dyn_cfg);
+ vdpa_nic->vring[idx].vring_created = false;
+err_vring_create:
+ irq_vring_fini(vdpa_nic, idx);
+err_irq_vring_init:
+ delete_vring_ctx(vdpa_nic, idx);
+
+ return rc;
+}
+
+static void reset_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
+{
+ if (vdpa_nic->vring[idx].vring_created)
+ delete_vring(vdpa_nic, idx);
+
+ memset((void *)&vdpa_nic->vring[idx], 0,
+ sizeof(vdpa_nic->vring[idx]));
+ vdpa_nic->vring[idx].vring_type = EF100_VDPA_VQ_NTYPES;
+}
+
+static bool is_qid_invalid(struct ef100_vdpa_nic *vdpa_nic, u16 idx,
+ const char *caller)
+{
+ if (unlikely(idx >= (vdpa_nic->max_queue_pairs * 2))) {
+ dev_err(&vdpa_nic->vdpa_dev.dev,
+ "%s: Invalid qid %u\n", caller, idx);
+ return true;
+ }
+ return false;
+}
+
+static int ef100_vdpa_set_vq_address(struct vdpa_device *vdev,
+ u16 idx, u64 desc_area, u64 driver_area,
+ u64 device_area)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+
+ if (is_qid_invalid(vdpa_nic, idx, __func__))
+ return -EINVAL;
+
+ mutex_lock(&vdpa_nic->lock);
+ vdpa_nic->vring[idx].desc = desc_area;
+ vdpa_nic->vring[idx].avail = driver_area;
+ vdpa_nic->vring[idx].used = device_area;
+ vdpa_nic->vring[idx].vring_state |= EF100_VRING_ADDRESS_CONFIGURED;
+ mutex_unlock(&vdpa_nic->lock);
+ return 0;
+}
+
+static void ef100_vdpa_set_vq_num(struct vdpa_device *vdev, u16 idx, u32 num)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+
+ if (is_qid_invalid(vdpa_nic, idx, __func__))
+ return;
+
+ if (!is_power_of_2(num)) {
+ dev_err(&vdev->dev, "%s: Index:%u size:%u not power of 2\n",
+ __func__, idx, num);
+ return;
+ }
+ if (num > EF100_VDPA_VQ_NUM_MAX_SIZE) {
+ dev_err(&vdev->dev, "%s: Index:%u size:%u more than max:%u\n",
+ __func__, idx, num, EF100_VDPA_VQ_NUM_MAX_SIZE);
+ return;
+ }
+ mutex_lock(&vdpa_nic->lock);
+ vdpa_nic->vring[idx].size = num;
+ vdpa_nic->vring[idx].vring_state |= EF100_VRING_SIZE_CONFIGURED;
+ mutex_unlock(&vdpa_nic->lock);
+}
+
+static void ef100_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+ u32 idx_val;
+
+ if (is_qid_invalid(vdpa_nic, idx, __func__))
+ return;
+
+ if (!vdpa_nic->vring[idx].vring_created) {
+ dev_err(&vdev->dev, "%s: Invalid vring%u\n", __func__, idx);
+ return;
+ }
+
+ idx_val = idx;
+ _efx_writed(vdpa_nic->efx, cpu_to_le32(idx_val),
+ vdpa_nic->vring[idx].doorbell_offset);
+}
+
+static void ef100_vdpa_set_vq_cb(struct vdpa_device *vdev, u16 idx,
+ struct vdpa_callback *cb)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+
+ if (is_qid_invalid(vdpa_nic, idx, __func__))
+ return;
+
+ if (cb)
+ vdpa_nic->vring[idx].cb = *cb;
+}
+
+static void ef100_vdpa_set_vq_ready(struct vdpa_device *vdev, u16 idx,
+ bool ready)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+
+ if (is_qid_invalid(vdpa_nic, idx, __func__))
+ return;
+
+ mutex_lock(&vdpa_nic->lock);
+ if (ready) {
+ vdpa_nic->vring[idx].vring_state |=
+ EF100_VRING_READY_CONFIGURED;
+ if (vdpa_nic->vdpa_state == EF100_VDPA_STATE_STARTED &&
+ can_create_vring(vdpa_nic, idx)) {
+ if (create_vring(vdpa_nic, idx))
+ /* Rollback ready configuration
+ * So that the above layer driver
+ * can make another attempt to set ready
+ */
+ vdpa_nic->vring[idx].vring_state &=
+ ~EF100_VRING_READY_CONFIGURED;
+ }
+ } else {
+ vdpa_nic->vring[idx].vring_state &=
+ ~EF100_VRING_READY_CONFIGURED;
+ if (vdpa_nic->vring[idx].vring_created)
+ delete_vring(vdpa_nic, idx);
+ }
+ mutex_unlock(&vdpa_nic->lock);
+}
+
+static bool ef100_vdpa_get_vq_ready(struct vdpa_device *vdev, u16 idx)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+ bool ready;
+
+ if (is_qid_invalid(vdpa_nic, idx, __func__))
+ return false;
+
+ mutex_lock(&vdpa_nic->lock);
+ ready = vdpa_nic->vring[idx].vring_state & EF100_VRING_READY_CONFIGURED;
+ mutex_unlock(&vdpa_nic->lock);
+ return ready;
+}
+
+static int ef100_vdpa_set_vq_state(struct vdpa_device *vdev, u16 idx,
+ const struct vdpa_vq_state *state)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+
+ if (is_qid_invalid(vdpa_nic, idx, __func__))
+ return -EINVAL;
+
+ mutex_lock(&vdpa_nic->lock);
+ vdpa_nic->vring[idx].last_avail_idx = state->split.avail_index;
+ mutex_unlock(&vdpa_nic->lock);
+ return 0;
+}
+
+static int ef100_vdpa_get_vq_state(struct vdpa_device *vdev,
+ u16 idx, struct vdpa_vq_state *state)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+
+ if (is_qid_invalid(vdpa_nic, idx, __func__))
+ return -EINVAL;
+
+ mutex_lock(&vdpa_nic->lock);
+ state->split.avail_index = (u16)vdpa_nic->vring[idx].last_avail_idx;
+ mutex_unlock(&vdpa_nic->lock);
+
+ return 0;
+}
+
+static struct vdpa_notification_area
+ ef100_vdpa_get_vq_notification(struct vdpa_device *vdev,
+ u16 idx)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+ struct vdpa_notification_area notify_area = {0, 0};
+ struct efx_vring_ctx *vring_ctx;
+ struct efx_nic *efx;
+ u32 offset;
+ int rc;
+
+ if (is_qid_invalid(vdpa_nic, idx, __func__))
+ goto end;
+
+ mutex_lock(&vdpa_nic->lock);
+ vring_ctx = vdpa_nic->vring[idx].vring_ctx;
+ if (!vring_ctx) {
+ rc = create_vring_ctx(vdpa_nic, idx);
+ if (rc) {
+ dev_err(&vdpa_nic->vdpa_dev.dev,
+ "%s: vring ctx failed index:%u, err:%d\n",
+ __func__, idx, rc);
+ goto err_create_vring_ctx;
+ }
+ vring_ctx = vdpa_nic->vring[idx].vring_ctx;
+ }
+ if (!vdpa_nic->vring[idx].doorbell_offset_valid) {
+ rc = efx_vdpa_get_doorbell_offset(vring_ctx, &offset);
+ if (rc) {
+ dev_err(&vdpa_nic->vdpa_dev.dev,
+ "%s: get_doorbell failed idx:%u, err:%d\n",
+ __func__, idx, rc);
+ goto err_get_doorbell_off;
+ }
+ vdpa_nic->vring[idx].doorbell_offset = offset;
+ vdpa_nic->vring[idx].doorbell_offset_valid = true;
+ }
+
+ efx = vdpa_nic->efx;
+ notify_area.addr = (uintptr_t)(efx->membase_phys +
+ vdpa_nic->vring[idx].doorbell_offset);
+ /* VDPA doorbells are at a stride of VI/2
+ * One VI stride is shared by both rx & tx doorbells
+ */
+ notify_area.size = efx->vi_stride / 2;
+
+ mutex_unlock(&vdpa_nic->lock);
+ return notify_area;
+
+err_get_doorbell_off:
+ delete_vring_ctx(vdpa_nic, idx);
+err_create_vring_ctx:
+ mutex_unlock(&vdpa_nic->lock);
+end:
+ return notify_area;
+}
+
+static int ef100_get_vq_irq(struct vdpa_device *vdev, u16 idx)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+ u32 irq;
+
+ if (is_qid_invalid(vdpa_nic, idx, __func__))
+ return -EINVAL;
+
+ mutex_lock(&vdpa_nic->lock);
+ irq = vdpa_nic->vring[idx].irq;
+ mutex_unlock(&vdpa_nic->lock);
+
+ return irq;
+}
+
static u32 ef100_vdpa_get_vq_align(struct vdpa_device *vdev)
{
return EF100_VDPA_VQ_ALIGN;
@@ -98,6 +526,8 @@ static void ef100_vdpa_set_config_cb(struct vdpa_device *vdev,
if (cb)
vdpa_nic->cfg_cb = *cb;
+ else
+ memset(&vdpa_nic->cfg_cb, 0, sizeof(vdpa_nic->cfg_cb));
}
static u16 ef100_vdpa_get_vq_num_max(struct vdpa_device *vdev)
@@ -155,11 +585,28 @@ static void ef100_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
static void ef100_vdpa_free(struct vdpa_device *vdev)
{
struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+ int i;
- mutex_destroy(&vdpa_nic->lock);
+ if (vdpa_nic) {
+ for (i = 0; i < (vdpa_nic->max_queue_pairs * 2); i++)
+ reset_vring(vdpa_nic, i);
+ ef100_vdpa_irq_vectors_free(vdpa_nic->efx->pci_dev);
+ mutex_destroy(&vdpa_nic->lock);
+ vdpa_nic->efx->vdpa_nic = NULL;
+ }
}
const struct vdpa_config_ops ef100_vdpa_config_ops = {
+ .set_vq_address = ef100_vdpa_set_vq_address,
+ .set_vq_num = ef100_vdpa_set_vq_num,
+ .kick_vq = ef100_vdpa_kick_vq,
+ .set_vq_cb = ef100_vdpa_set_vq_cb,
+ .set_vq_ready = ef100_vdpa_set_vq_ready,
+ .get_vq_ready = ef100_vdpa_get_vq_ready,
+ .set_vq_state = ef100_vdpa_set_vq_state,
+ .get_vq_state = ef100_vdpa_get_vq_state,
+ .get_vq_notification = ef100_vdpa_get_vq_notification,
+ .get_vq_irq = ef100_get_vq_irq,
.get_vq_align = ef100_vdpa_get_vq_align,
.get_device_features = ef100_vdpa_get_device_features,
.set_driver_features = ef100_vdpa_set_driver_features,
--
2.30.1
To allow vDPA device creation and deletion, add a vDPA management
device per function. Currently, the vDPA devices can be created
only on a VF. Also, for now only network class of vDPA devices
are supported.
Signed-off-by: Gautam Dawar <[email protected]>
---
drivers/net/ethernet/sfc/Makefile | 2 +-
drivers/net/ethernet/sfc/ef10.c | 2 +-
drivers/net/ethernet/sfc/ef100_nic.c | 24 ++-
drivers/net/ethernet/sfc/ef100_nic.h | 11 +
drivers/net/ethernet/sfc/ef100_vdpa.c | 232 ++++++++++++++++++++++
drivers/net/ethernet/sfc/ef100_vdpa.h | 84 ++++++++
drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 28 +++
drivers/net/ethernet/sfc/mcdi_functions.c | 9 +-
drivers/net/ethernet/sfc/mcdi_functions.h | 3 +-
drivers/net/ethernet/sfc/net_driver.h | 6 +
10 files changed, 394 insertions(+), 7 deletions(-)
create mode 100644 drivers/net/ethernet/sfc/ef100_vdpa_ops.c
diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile
index 84c9f0590368..a10eac91ab23 100644
--- a/drivers/net/ethernet/sfc/Makefile
+++ b/drivers/net/ethernet/sfc/Makefile
@@ -11,7 +11,7 @@ sfc-$(CONFIG_SFC_MTD) += mtd.o
sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \
mae.o tc.o tc_bindings.o tc_counters.o
-sfc-$(CONFIG_SFC_VDPA) += mcdi_vdpa.o ef100_vdpa.o
+sfc-$(CONFIG_SFC_VDPA) += mcdi_vdpa.o ef100_vdpa.o ef100_vdpa_ops.o
obj-$(CONFIG_SFC) += sfc.o
obj-$(CONFIG_SFC_FALCON) += falcon/
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index 7022fb2005a2..366ecd3c80b1 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -589,7 +589,7 @@ static int efx_ef10_probe(struct efx_nic *efx)
if (rc)
goto fail4;
- rc = efx_get_pf_index(efx, &nic_data->pf_index);
+ rc = efx_get_fn_info(efx, &nic_data->pf_index, NULL);
if (rc)
goto fail5;
diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c
index 41175eb00326..41811c519275 100644
--- a/drivers/net/ethernet/sfc/ef100_nic.c
+++ b/drivers/net/ethernet/sfc/ef100_nic.c
@@ -1160,7 +1160,7 @@ static int ef100_probe_main(struct efx_nic *efx)
if (rc)
goto fail;
- rc = efx_get_pf_index(efx, &nic_data->pf_index);
+ rc = efx_get_fn_info(efx, &nic_data->pf_index, &nic_data->vf_index);
if (rc)
goto fail;
@@ -1247,13 +1247,33 @@ int ef100_probe_netdev_pf(struct efx_nic *efx)
int ef100_probe_vf(struct efx_nic *efx)
{
- return ef100_probe_main(efx);
+#if defined(CONFIG_SFC_VDPA)
+ int err;
+#endif
+ int rc;
+
+ rc = ef100_probe_main(efx);
+ if (rc)
+ return rc;
+
+#if defined(CONFIG_SFC_VDPA)
+ err = ef100_vdpa_register_mgmtdev(efx);
+ if (err)
+ pci_warn(efx->pci_dev,
+ "vdpa_register_mgmtdev failed, err: %d\n", err);
+#endif
+ return 0;
}
void ef100_remove(struct efx_nic *efx)
{
struct ef100_nic_data *nic_data = efx->nic_data;
+#if defined(CONFIG_SFC_VDPA)
+ if (efx_vdpa_supported(efx))
+ ef100_vdpa_unregister_mgmtdev(efx);
+#endif
+
efx_mcdi_detach(efx);
efx_mcdi_fini(efx);
if (nic_data) {
diff --git a/drivers/net/ethernet/sfc/ef100_nic.h b/drivers/net/ethernet/sfc/ef100_nic.h
index 5ed693fbe79f..730c8bb932b0 100644
--- a/drivers/net/ethernet/sfc/ef100_nic.h
+++ b/drivers/net/ethernet/sfc/ef100_nic.h
@@ -68,6 +68,13 @@ enum ef100_bar_config {
EF100_BAR_CONFIG_VDPA,
};
+#ifdef CONFIG_SFC_VDPA
+enum ef100_vdpa_class {
+ EF100_VDPA_CLASS_NONE,
+ EF100_VDPA_CLASS_NET,
+};
+#endif
+
struct ef100_nic_data {
struct efx_nic *efx;
struct efx_buffer mcdi_buf;
@@ -75,7 +82,11 @@ struct ef100_nic_data {
u32 datapath_caps2;
u32 datapath_caps3;
unsigned int pf_index;
+ unsigned int vf_index;
u16 warm_boot_count;
+#ifdef CONFIG_SFC_VDPA
+ enum ef100_vdpa_class vdpa_class;
+#endif
u8 port_id[ETH_ALEN];
DECLARE_BITMAP(evq_phases, EFX_MAX_CHANNELS);
enum ef100_bar_config bar_config;
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.c b/drivers/net/ethernet/sfc/ef100_vdpa.c
index 5e215cee585a..ff4bb61e598e 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa.c
+++ b/drivers/net/ethernet/sfc/ef100_vdpa.c
@@ -11,11 +11,17 @@
#include <linux/err.h>
#include <linux/vdpa.h>
#include <linux/virtio_net.h>
+#include <uapi/linux/vdpa.h>
#include "ef100_vdpa.h"
#include "mcdi_vdpa.h"
#include "mcdi_filters.h"
#include "ef100_netdev.h"
+static struct virtio_device_id ef100_vdpa_id_table[] = {
+ { .device = VIRTIO_ID_NET, .vendor = PCI_VENDOR_ID_REDHAT_QUMRANET },
+ { 0 },
+};
+
int ef100_vdpa_init(struct efx_probe_data *probe_data)
{
struct efx_nic *efx = &probe_data->efx;
@@ -42,17 +48,243 @@ int ef100_vdpa_init(struct efx_probe_data *probe_data)
return rc;
}
+static void ef100_vdpa_delete(struct efx_nic *efx)
+{
+ if (efx->vdpa_nic) {
+ /* replace with _vdpa_unregister_device later */
+ put_device(&efx->vdpa_nic->vdpa_dev.dev);
+ efx->vdpa_nic = NULL;
+ }
+}
+
void ef100_vdpa_fini(struct efx_probe_data *probe_data)
{
struct efx_nic *efx = &probe_data->efx;
+ struct ef100_nic_data *nic_data;
if (efx->state != STATE_VDPA && efx->state != STATE_DISABLED) {
pci_err(efx->pci_dev, "Invalid efx state %u", efx->state);
return;
}
+ /* Handle vdpa device deletion, if not done explicitly */
+ ef100_vdpa_delete(efx);
+ nic_data = efx->nic_data;
+ nic_data->vdpa_class = EF100_VDPA_CLASS_NONE;
efx->state = STATE_PROBED;
down_write(&efx->filter_sem);
efx_mcdi_filter_table_remove(efx);
up_write(&efx->filter_sem);
}
+
+static int get_net_config(struct ef100_vdpa_nic *vdpa_nic)
+{
+ struct efx_nic *efx = vdpa_nic->efx;
+ u16 mtu;
+ int rc;
+
+ vdpa_nic->net_config.max_virtqueue_pairs =
+ cpu_to_efx_vdpa16(vdpa_nic, vdpa_nic->max_queue_pairs);
+
+ rc = efx_vdpa_get_mtu(efx, &mtu);
+ if (rc) {
+ dev_err(&vdpa_nic->vdpa_dev.dev,
+ "%s: Get MTU for vf:%u failed:%d\n", __func__,
+ vdpa_nic->vf_index, rc);
+ return rc;
+ }
+ vdpa_nic->net_config.mtu = cpu_to_efx_vdpa16(vdpa_nic, mtu);
+ vdpa_nic->net_config.status = cpu_to_efx_vdpa16(vdpa_nic,
+ VIRTIO_NET_S_LINK_UP);
+ return 0;
+}
+
+static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
+ const char *dev_name,
+ enum ef100_vdpa_class dev_type,
+ const u8 *mac)
+{
+ struct ef100_nic_data *nic_data = efx->nic_data;
+ struct ef100_vdpa_nic *vdpa_nic;
+ struct device *dev;
+ int rc;
+
+ nic_data->vdpa_class = dev_type;
+ vdpa_nic = vdpa_alloc_device(struct ef100_vdpa_nic,
+ vdpa_dev, &efx->pci_dev->dev,
+ &ef100_vdpa_config_ops,
+ 1, 1,
+ dev_name, false);
+ if (!vdpa_nic) {
+ pci_err(efx->pci_dev,
+ "vDPA device allocation failed for vf: %u\n",
+ nic_data->vf_index);
+ nic_data->vdpa_class = EF100_VDPA_CLASS_NONE;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ mutex_init(&vdpa_nic->lock);
+ dev = &vdpa_nic->vdpa_dev.dev;
+ efx->vdpa_nic = vdpa_nic;
+ vdpa_nic->vdpa_dev.dma_dev = &efx->pci_dev->dev;
+ vdpa_nic->vdpa_dev.mdev = efx->mgmt_dev;
+ vdpa_nic->efx = efx;
+ vdpa_nic->pf_index = nic_data->pf_index;
+ vdpa_nic->vf_index = nic_data->vf_index;
+ vdpa_nic->vdpa_state = EF100_VDPA_STATE_INITIALIZED;
+ vdpa_nic->mac_address = (u8 *)&vdpa_nic->net_config.mac;
+ ether_addr_copy(vdpa_nic->mac_address, mac);
+ vdpa_nic->mac_configured = true;
+
+ rc = get_net_config(vdpa_nic);
+ if (rc)
+ goto err_put_device;
+
+ /* _vdpa_register_device when its ready */
+
+ return vdpa_nic;
+
+err_put_device:
+ /* put_device invokes ef100_vdpa_free */
+ put_device(&vdpa_nic->vdpa_dev.dev);
+ return ERR_PTR(rc);
+}
+
+static void ef100_vdpa_net_dev_del(struct vdpa_mgmt_dev *mgmt_dev,
+ struct vdpa_device *vdev)
+{
+ struct ef100_nic_data *nic_data;
+ struct efx_nic *efx;
+ int rc;
+
+ efx = pci_get_drvdata(to_pci_dev(mgmt_dev->device));
+ nic_data = efx->nic_data;
+
+ rc = efx_ef100_set_bar_config(efx, EF100_BAR_CONFIG_EF100);
+ if (rc)
+ pci_err(efx->pci_dev,
+ "set_bar_config EF100 failed, err: %d\n", rc);
+ else
+ pci_dbg(efx->pci_dev,
+ "vdpa net device deleted, vf: %u\n",
+ nic_data->vf_index);
+}
+
+static int ef100_vdpa_net_dev_add(struct vdpa_mgmt_dev *mgmt_dev,
+ const char *name,
+ const struct vdpa_dev_set_config *config)
+{
+ struct ef100_vdpa_nic *vdpa_nic;
+ struct ef100_nic_data *nic_data;
+ struct efx_nic *efx;
+ int rc, err;
+
+ efx = pci_get_drvdata(to_pci_dev(mgmt_dev->device));
+ if (efx->vdpa_nic) {
+ pci_warn(efx->pci_dev,
+ "vDPA device already exists on this VF\n");
+ return -EEXIST;
+ }
+
+ if (config->mask & BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR)) {
+ if (!is_valid_ether_addr(config->net.mac)) {
+ pci_err(efx->pci_dev, "Invalid MAC address %pM\n",
+ config->net.mac);
+ return -EINVAL;
+ }
+ } else {
+ pci_err(efx->pci_dev, "MAC address parameter missing\n");
+ return -EIO;
+ }
+
+ nic_data = efx->nic_data;
+
+ rc = efx_ef100_set_bar_config(efx, EF100_BAR_CONFIG_VDPA);
+ if (rc) {
+ pci_err(efx->pci_dev,
+ "set_bar_config vDPA failed, err: %d\n", rc);
+ goto err_set_bar_config;
+ }
+
+ vdpa_nic = ef100_vdpa_create(efx, name, EF100_VDPA_CLASS_NET,
+ (const u8 *)config->net.mac);
+ if (IS_ERR(vdpa_nic)) {
+ pci_err(efx->pci_dev,
+ "vDPA device creation failed, vf: %u, err: %ld\n",
+ nic_data->vf_index, PTR_ERR(vdpa_nic));
+ rc = PTR_ERR(vdpa_nic);
+ goto err_set_bar_config;
+ } else {
+ pci_dbg(efx->pci_dev,
+ "vdpa net device created, vf: %u\n",
+ nic_data->vf_index);
+ pci_warn(efx->pci_dev,
+ "Use QEMU versions 6.1.0 and later with vhost-vdpa\n");
+ }
+
+ return 0;
+
+err_set_bar_config:
+ err = efx_ef100_set_bar_config(efx, EF100_BAR_CONFIG_EF100);
+ if (err)
+ pci_err(efx->pci_dev,
+ "set_bar_config EF100 failed, err: %d\n", err);
+
+ return rc;
+}
+
+static const struct vdpa_mgmtdev_ops ef100_vdpa_net_mgmtdev_ops = {
+ .dev_add = ef100_vdpa_net_dev_add,
+ .dev_del = ef100_vdpa_net_dev_del
+};
+
+int ef100_vdpa_register_mgmtdev(struct efx_nic *efx)
+{
+ struct vdpa_mgmt_dev *mgmt_dev;
+ u64 features;
+ int rc;
+
+ mgmt_dev = kzalloc(sizeof(*mgmt_dev), GFP_KERNEL);
+ if (!mgmt_dev)
+ return -ENOMEM;
+
+ rc = efx_vdpa_get_features(efx, EF100_VDPA_DEVICE_TYPE_NET, &features);
+ if (rc) {
+ pci_err(efx->pci_dev, "%s: MCDI get features error:%d\n",
+ __func__, rc);
+ goto err_get_features;
+ }
+
+ efx->mgmt_dev = mgmt_dev;
+ mgmt_dev->device = &efx->pci_dev->dev;
+ mgmt_dev->id_table = ef100_vdpa_id_table;
+ mgmt_dev->ops = &ef100_vdpa_net_mgmtdev_ops;
+ mgmt_dev->supported_features = features;
+ mgmt_dev->max_supported_vqs = EF100_VDPA_MAX_QUEUES_PAIRS * 2;
+ mgmt_dev->config_attr_mask = BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR);
+
+ rc = vdpa_mgmtdev_register(mgmt_dev);
+ if (rc) {
+ pci_err(efx->pci_dev,
+ "vdpa_mgmtdev_register failed, err: %d\n", rc);
+ goto err_mgmtdev_register;
+ }
+
+ return 0;
+
+err_mgmtdev_register:
+err_get_features:
+ kfree(mgmt_dev);
+ efx->mgmt_dev = NULL;
+
+ return rc;
+}
+
+void ef100_vdpa_unregister_mgmtdev(struct efx_nic *efx)
+{
+ if (efx->mgmt_dev) {
+ vdpa_mgmtdev_unregister(efx->mgmt_dev);
+ kfree(efx->mgmt_dev);
+ efx->mgmt_dev = NULL;
+ }
+}
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
index 6b51a05becd8..83f6d819f6a5 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa.h
+++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
@@ -18,6 +18,24 @@
#if defined(CONFIG_SFC_VDPA)
+/* Max queue pairs currently supported */
+#define EF100_VDPA_MAX_QUEUES_PAIRS 1
+
+/**
+ * enum ef100_vdpa_nic_state - possible states for a vDPA NIC
+ *
+ * @EF100_VDPA_STATE_INITIALIZED: State after vDPA NIC created
+ * @EF100_VDPA_STATE_NEGOTIATED: State after feature negotiation
+ * @EF100_VDPA_STATE_STARTED: State after driver ok
+ * @EF100_VDPA_STATE_NSTATES: Number of VDPA states
+ */
+enum ef100_vdpa_nic_state {
+ EF100_VDPA_STATE_INITIALIZED,
+ EF100_VDPA_STATE_NEGOTIATED,
+ EF100_VDPA_STATE_STARTED,
+ EF100_VDPA_STATE_NSTATES
+};
+
enum ef100_vdpa_device_type {
EF100_VDPA_DEVICE_TYPE_NET,
};
@@ -28,7 +46,73 @@ enum ef100_vdpa_vq_type {
EF100_VDPA_VQ_NTYPES
};
+/**
+ * struct ef100_vdpa_nic - vDPA NIC data structure
+ *
+ * @vdpa_dev: vdpa_device object which registers on the vDPA bus.
+ * @vdpa_state: NIC state machine governed by ef100_vdpa_nic_state
+ * @efx: pointer to the VF's efx_nic object
+ * @lock: Managing access to vdpa config operations
+ * @pf_index: PF index of the vDPA VF
+ * @vf_index: VF index of the vDPA VF
+ * @status: device status as per VIRTIO spec
+ * @features: negotiated feature bits
+ * @max_queue_pairs: maximum number of queue pairs supported
+ * @net_config: virtio_net_config data
+ * @mac_address: mac address of interface associated with this vdpa device
+ * @mac_configured: true after MAC address is configured
+ */
+struct ef100_vdpa_nic {
+ struct vdpa_device vdpa_dev;
+ enum ef100_vdpa_nic_state vdpa_state;
+ struct efx_nic *efx;
+ /* for synchronizing access to vdpa config operations */
+ struct mutex lock;
+ u32 pf_index;
+ u32 vf_index;
+ u8 status;
+ u64 features;
+ u32 max_queue_pairs;
+ struct virtio_net_config net_config;
+ u8 *mac_address;
+ bool mac_configured;
+};
+
int ef100_vdpa_init(struct efx_probe_data *probe_data);
void ef100_vdpa_fini(struct efx_probe_data *probe_data);
+int ef100_vdpa_register_mgmtdev(struct efx_nic *efx);
+void ef100_vdpa_unregister_mgmtdev(struct efx_nic *efx);
+
+static inline bool efx_vdpa_is_little_endian(struct ef100_vdpa_nic *vdpa_nic)
+{
+ return virtio_legacy_is_little_endian() ||
+ (vdpa_nic->features & (1ULL << VIRTIO_F_VERSION_1));
+}
+
+static inline u16 efx_vdpa16_to_cpu(struct ef100_vdpa_nic *vdpa_nic,
+ __virtio16 val)
+{
+ return __virtio16_to_cpu(efx_vdpa_is_little_endian(vdpa_nic), val);
+}
+
+static inline __virtio16 cpu_to_efx_vdpa16(struct ef100_vdpa_nic *vdpa_nic,
+ u16 val)
+{
+ return __cpu_to_virtio16(efx_vdpa_is_little_endian(vdpa_nic), val);
+}
+
+static inline u32 efx_vdpa32_to_cpu(struct ef100_vdpa_nic *vdpa_nic,
+ __virtio32 val)
+{
+ return __virtio32_to_cpu(efx_vdpa_is_little_endian(vdpa_nic), val);
+}
+
+static inline __virtio32 cpu_to_efx_vdpa32(struct ef100_vdpa_nic *vdpa_nic,
+ u32 val)
+{
+ return __cpu_to_virtio32(efx_vdpa_is_little_endian(vdpa_nic), val);
+}
+
+extern const struct vdpa_config_ops ef100_vdpa_config_ops;
#endif /* CONFIG_SFC_VDPA */
#endif /* __EF100_VDPA_H__ */
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
new file mode 100644
index 000000000000..31952931c198
--- /dev/null
+++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Driver for Xilinx network controllers and boards
+ * Copyright(C) 2020-2022 Xilinx, Inc.
+ * Copyright(C) 2022 Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <linux/vdpa.h>
+#include "ef100_vdpa.h"
+
+static struct ef100_vdpa_nic *get_vdpa_nic(struct vdpa_device *vdev)
+{
+ return container_of(vdev, struct ef100_vdpa_nic, vdpa_dev);
+}
+
+static void ef100_vdpa_free(struct vdpa_device *vdev)
+{
+ struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+
+ mutex_destroy(&vdpa_nic->lock);
+}
+
+const struct vdpa_config_ops ef100_vdpa_config_ops = {
+ .free = ef100_vdpa_free,
+};
diff --git a/drivers/net/ethernet/sfc/mcdi_functions.c b/drivers/net/ethernet/sfc/mcdi_functions.c
index d3e6d8239f5c..4415f19cf68f 100644
--- a/drivers/net/ethernet/sfc/mcdi_functions.c
+++ b/drivers/net/ethernet/sfc/mcdi_functions.c
@@ -413,7 +413,8 @@ int efx_mcdi_window_mode_to_stride(struct efx_nic *efx, u8 vi_window_mode)
return 0;
}
-int efx_get_pf_index(struct efx_nic *efx, unsigned int *pf_index)
+int efx_get_fn_info(struct efx_nic *efx, unsigned int *pf_index,
+ unsigned int *vf_index)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_FUNCTION_INFO_OUT_LEN);
size_t outlen;
@@ -426,6 +427,10 @@ int efx_get_pf_index(struct efx_nic *efx, unsigned int *pf_index)
if (outlen < sizeof(outbuf))
return -EIO;
- *pf_index = MCDI_DWORD(outbuf, GET_FUNCTION_INFO_OUT_PF);
+ if (pf_index)
+ *pf_index = MCDI_DWORD(outbuf, GET_FUNCTION_INFO_OUT_PF);
+
+ if (efx->type->is_vf && vf_index)
+ *vf_index = MCDI_DWORD(outbuf, GET_FUNCTION_INFO_OUT_VF);
return 0;
}
diff --git a/drivers/net/ethernet/sfc/mcdi_functions.h b/drivers/net/ethernet/sfc/mcdi_functions.h
index b0e2f53a0d9b..76dc0a13463e 100644
--- a/drivers/net/ethernet/sfc/mcdi_functions.h
+++ b/drivers/net/ethernet/sfc/mcdi_functions.h
@@ -28,6 +28,7 @@ void efx_mcdi_rx_remove(struct efx_rx_queue *rx_queue);
void efx_mcdi_rx_fini(struct efx_rx_queue *rx_queue);
int efx_fini_dmaq(struct efx_nic *efx);
int efx_mcdi_window_mode_to_stride(struct efx_nic *efx, u8 vi_window_mode);
-int efx_get_pf_index(struct efx_nic *efx, unsigned int *pf_index);
+int efx_get_fn_info(struct efx_nic *efx, unsigned int *pf_index,
+ unsigned int *vf_index);
#endif
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
index ffda80a95221..79356d614109 100644
--- a/drivers/net/ethernet/sfc/net_driver.h
+++ b/drivers/net/ethernet/sfc/net_driver.h
@@ -1182,6 +1182,12 @@ struct efx_nic {
unsigned int mem_bar;
u32 reg_base;
+#ifdef CONFIG_SFC_VDPA
+ /** @mgmt_dev: vDPA Management device */
+ struct vdpa_mgmt_dev *mgmt_dev;
+ /** @vdpa_nic: vDPA device structure (EF100) */
+ struct ef100_vdpa_nic *vdpa_nic;
+#endif
/* The following fields may be written more often */
--
2.30.1
Hi Gautam,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on net-next/master]
url: https://github.com/intel-lab-lkp/linux/commits/Gautam-Dawar/sfc-add-vDPA-support-for-EF100-devices/20221207-230026
patch link: https://lore.kernel.org/r/20221207145428.31544-5-gautam.dawar%40amd.com
patch subject: [PATCH net-next 04/11] sfc: implement vDPA management device operations
config: ia64-allyesconfig
compiler: ia64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/ff563410d33798677670893391c1d56ec659d3e3
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Gautam-Dawar/sfc-add-vDPA-support-for-EF100-devices/20221207-230026
git checkout ff563410d33798677670893391c1d56ec659d3e3
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=ia64 SHELL=/bin/bash drivers/net/ethernet/sfc/
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
All warnings (new ones prefixed by >>):
drivers/net/ethernet/sfc/ef100_vdpa.c: In function 'ef100_vdpa_create':
>> drivers/net/ethernet/sfc/ef100_vdpa.c:109:24: warning: variable 'dev' set but not used [-Wunused-but-set-variable]
109 | struct device *dev;
| ^~~
vim +/dev +109 drivers/net/ethernet/sfc/ef100_vdpa.c
101
102 static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
103 const char *dev_name,
104 enum ef100_vdpa_class dev_type,
105 const u8 *mac)
106 {
107 struct ef100_nic_data *nic_data = efx->nic_data;
108 struct ef100_vdpa_nic *vdpa_nic;
> 109 struct device *dev;
110 int rc;
111
112 nic_data->vdpa_class = dev_type;
113 vdpa_nic = vdpa_alloc_device(struct ef100_vdpa_nic,
114 vdpa_dev, &efx->pci_dev->dev,
115 &ef100_vdpa_config_ops,
116 1, 1,
117 dev_name, false);
118 if (!vdpa_nic) {
119 pci_err(efx->pci_dev,
120 "vDPA device allocation failed for vf: %u\n",
121 nic_data->vf_index);
122 nic_data->vdpa_class = EF100_VDPA_CLASS_NONE;
123 return ERR_PTR(-ENOMEM);
124 }
125
126 mutex_init(&vdpa_nic->lock);
127 dev = &vdpa_nic->vdpa_dev.dev;
128 efx->vdpa_nic = vdpa_nic;
129 vdpa_nic->vdpa_dev.dma_dev = &efx->pci_dev->dev;
130 vdpa_nic->vdpa_dev.mdev = efx->mgmt_dev;
131 vdpa_nic->efx = efx;
132 vdpa_nic->pf_index = nic_data->pf_index;
133 vdpa_nic->vf_index = nic_data->vf_index;
134 vdpa_nic->vdpa_state = EF100_VDPA_STATE_INITIALIZED;
135 vdpa_nic->mac_address = (u8 *)&vdpa_nic->net_config.mac;
136 ether_addr_copy(vdpa_nic->mac_address, mac);
137 vdpa_nic->mac_configured = true;
138
139 rc = get_net_config(vdpa_nic);
140 if (rc)
141 goto err_put_device;
142
143 /* _vdpa_register_device when its ready */
144
145 return vdpa_nic;
146
147 err_put_device:
148 /* put_device invokes ef100_vdpa_free */
149 put_device(&vdpa_nic->vdpa_dev.dev);
150 return ERR_PTR(rc);
151 }
152
--
0-DAY CI Kernel Test Service
https://01.org/lkp
On Wed, Dec 07, 2022 at 08:24:16PM +0530, Gautam Dawar wrote:
> Hi All,
>
> This series adds the vdpa support for EF100 devices.
> For now, only a network class of vdpa device is supported and
> they can be created only on a VF. Each EF100 VF can have one
> of the three function personalities (EF100, vDPA & None) at
> any time with EF100 being the default. A VF's function personality
> is changed to vDPA while creating the vdpa device using vdpa tool.
>
> A vDPA management device is created per VF to allow selection of
> the desired VF for vDPA device creation. The MAC address for the
> target net device must be specified at the device creation time
> via the `mac` parameter of the `vdpa dev add` command as the control
> virtqueue is not supported yet.
>
> To use with vhost-vdpa, QEMU version 6.1.0 or later must be used
> as it fixes the incorrect feature negotiation (vhost-vdpa backend)
> without which VIRTIO_F_IN_ORDER feature bit is negotiated but not
> honored when using the guest kernel virtio driver.
>
> Gautam Dawar (11):
> sfc: add function personality support for EF100 devices
> sfc: implement MCDI interface for vDPA operations
> sfc: implement init and fini functions for vDPA personality
> sfc: implement vDPA management device operations
> sfc: implement vdpa device config operations
> sfc: implement vdpa vring config operations
> sfc: implement filters for receiving traffic
> sfc: implement device status related vdpa config operations
> sfc: implement iova rbtree to store dma mappings
> sfc: implement vdpa config_ops for dma operations
> sfc: register the vDPA device
For the series:
Acked-by: Martin Habets <[email protected]>
>
> drivers/net/ethernet/sfc/Kconfig | 8 +
> drivers/net/ethernet/sfc/Makefile | 2 +
> drivers/net/ethernet/sfc/ef10.c | 2 +-
> drivers/net/ethernet/sfc/ef100.c | 6 +-
> drivers/net/ethernet/sfc/ef100_iova.c | 205 +++++
> drivers/net/ethernet/sfc/ef100_iova.h | 40 +
> drivers/net/ethernet/sfc/ef100_nic.c | 126 ++-
> drivers/net/ethernet/sfc/ef100_nic.h | 22 +
> drivers/net/ethernet/sfc/ef100_vdpa.c | 693 +++++++++++++++++
> drivers/net/ethernet/sfc/ef100_vdpa.h | 241 ++++++
> drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 897 ++++++++++++++++++++++
> drivers/net/ethernet/sfc/mcdi.h | 7 +
> drivers/net/ethernet/sfc/mcdi_filters.c | 51 +-
> drivers/net/ethernet/sfc/mcdi_functions.c | 9 +-
> drivers/net/ethernet/sfc/mcdi_functions.h | 3 +-
> drivers/net/ethernet/sfc/mcdi_vdpa.c | 268 +++++++
> drivers/net/ethernet/sfc/mcdi_vdpa.h | 84 ++
> drivers/net/ethernet/sfc/net_driver.h | 19 +
> 18 files changed, 2650 insertions(+), 33 deletions(-)
> create mode 100644 drivers/net/ethernet/sfc/ef100_iova.c
> create mode 100644 drivers/net/ethernet/sfc/ef100_iova.h
> create mode 100644 drivers/net/ethernet/sfc/ef100_vdpa.c
> create mode 100644 drivers/net/ethernet/sfc/ef100_vdpa.h
> create mode 100644 drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> create mode 100644 drivers/net/ethernet/sfc/mcdi_vdpa.c
> create mode 100644 drivers/net/ethernet/sfc/mcdi_vdpa.h
>
> --
> 2.30.1
On Wed, Dec 7, 2022 at 10:56 PM Gautam Dawar <[email protected]> wrote:
>
> To allow vDPA device creation and deletion, add a vDPA management
> device per function. Currently, the vDPA devices can be created
> only on a VF. Also, for now only network class of vDPA devices
> are supported.
>
> Signed-off-by: Gautam Dawar <[email protected]>
> ---
> drivers/net/ethernet/sfc/Makefile | 2 +-
> drivers/net/ethernet/sfc/ef10.c | 2 +-
> drivers/net/ethernet/sfc/ef100_nic.c | 24 ++-
> drivers/net/ethernet/sfc/ef100_nic.h | 11 +
> drivers/net/ethernet/sfc/ef100_vdpa.c | 232 ++++++++++++++++++++++
> drivers/net/ethernet/sfc/ef100_vdpa.h | 84 ++++++++
> drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 28 +++
> drivers/net/ethernet/sfc/mcdi_functions.c | 9 +-
> drivers/net/ethernet/sfc/mcdi_functions.h | 3 +-
> drivers/net/ethernet/sfc/net_driver.h | 6 +
> 10 files changed, 394 insertions(+), 7 deletions(-)
> create mode 100644 drivers/net/ethernet/sfc/ef100_vdpa_ops.c
>
> diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile
> index 84c9f0590368..a10eac91ab23 100644
> --- a/drivers/net/ethernet/sfc/Makefile
> +++ b/drivers/net/ethernet/sfc/Makefile
> @@ -11,7 +11,7 @@ sfc-$(CONFIG_SFC_MTD) += mtd.o
> sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \
> mae.o tc.o tc_bindings.o tc_counters.o
>
> -sfc-$(CONFIG_SFC_VDPA) += mcdi_vdpa.o ef100_vdpa.o
> +sfc-$(CONFIG_SFC_VDPA) += mcdi_vdpa.o ef100_vdpa.o ef100_vdpa_ops.o
> obj-$(CONFIG_SFC) += sfc.o
>
> obj-$(CONFIG_SFC_FALCON) += falcon/
> diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
> index 7022fb2005a2..366ecd3c80b1 100644
> --- a/drivers/net/ethernet/sfc/ef10.c
> +++ b/drivers/net/ethernet/sfc/ef10.c
> @@ -589,7 +589,7 @@ static int efx_ef10_probe(struct efx_nic *efx)
> if (rc)
> goto fail4;
>
> - rc = efx_get_pf_index(efx, &nic_data->pf_index);
> + rc = efx_get_fn_info(efx, &nic_data->pf_index, NULL);
> if (rc)
> goto fail5;
>
> diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c
> index 41175eb00326..41811c519275 100644
> --- a/drivers/net/ethernet/sfc/ef100_nic.c
> +++ b/drivers/net/ethernet/sfc/ef100_nic.c
> @@ -1160,7 +1160,7 @@ static int ef100_probe_main(struct efx_nic *efx)
> if (rc)
> goto fail;
>
> - rc = efx_get_pf_index(efx, &nic_data->pf_index);
> + rc = efx_get_fn_info(efx, &nic_data->pf_index, &nic_data->vf_index);
> if (rc)
> goto fail;
>
> @@ -1247,13 +1247,33 @@ int ef100_probe_netdev_pf(struct efx_nic *efx)
>
> int ef100_probe_vf(struct efx_nic *efx)
> {
> - return ef100_probe_main(efx);
> +#if defined(CONFIG_SFC_VDPA)
> + int err;
> +#endif
> + int rc;
> +
> + rc = ef100_probe_main(efx);
> + if (rc)
> + return rc;
> +
> +#if defined(CONFIG_SFC_VDPA)
> + err = ef100_vdpa_register_mgmtdev(efx);
> + if (err)
> + pci_warn(efx->pci_dev,
> + "vdpa_register_mgmtdev failed, err: %d\n", err);
> +#endif
> + return 0;
> }
>
> void ef100_remove(struct efx_nic *efx)
> {
> struct ef100_nic_data *nic_data = efx->nic_data;
>
> +#if defined(CONFIG_SFC_VDPA)
> + if (efx_vdpa_supported(efx))
> + ef100_vdpa_unregister_mgmtdev(efx);
> +#endif
> +
> efx_mcdi_detach(efx);
> efx_mcdi_fini(efx);
> if (nic_data) {
> diff --git a/drivers/net/ethernet/sfc/ef100_nic.h b/drivers/net/ethernet/sfc/ef100_nic.h
> index 5ed693fbe79f..730c8bb932b0 100644
> --- a/drivers/net/ethernet/sfc/ef100_nic.h
> +++ b/drivers/net/ethernet/sfc/ef100_nic.h
> @@ -68,6 +68,13 @@ enum ef100_bar_config {
> EF100_BAR_CONFIG_VDPA,
> };
>
> +#ifdef CONFIG_SFC_VDPA
> +enum ef100_vdpa_class {
> + EF100_VDPA_CLASS_NONE,
> + EF100_VDPA_CLASS_NET,
> +};
> +#endif
> +
> struct ef100_nic_data {
> struct efx_nic *efx;
> struct efx_buffer mcdi_buf;
> @@ -75,7 +82,11 @@ struct ef100_nic_data {
> u32 datapath_caps2;
> u32 datapath_caps3;
> unsigned int pf_index;
> + unsigned int vf_index;
> u16 warm_boot_count;
> +#ifdef CONFIG_SFC_VDPA
> + enum ef100_vdpa_class vdpa_class;
> +#endif
> u8 port_id[ETH_ALEN];
> DECLARE_BITMAP(evq_phases, EFX_MAX_CHANNELS);
> enum ef100_bar_config bar_config;
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.c b/drivers/net/ethernet/sfc/ef100_vdpa.c
> index 5e215cee585a..ff4bb61e598e 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa.c
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.c
> @@ -11,11 +11,17 @@
> #include <linux/err.h>
> #include <linux/vdpa.h>
> #include <linux/virtio_net.h>
> +#include <uapi/linux/vdpa.h>
> #include "ef100_vdpa.h"
> #include "mcdi_vdpa.h"
> #include "mcdi_filters.h"
> #include "ef100_netdev.h"
>
> +static struct virtio_device_id ef100_vdpa_id_table[] = {
> + { .device = VIRTIO_ID_NET, .vendor = PCI_VENDOR_ID_REDHAT_QUMRANET },
> + { 0 },
> +};
> +
> int ef100_vdpa_init(struct efx_probe_data *probe_data)
> {
> struct efx_nic *efx = &probe_data->efx;
> @@ -42,17 +48,243 @@ int ef100_vdpa_init(struct efx_probe_data *probe_data)
> return rc;
> }
>
> +static void ef100_vdpa_delete(struct efx_nic *efx)
> +{
> + if (efx->vdpa_nic) {
> + /* replace with _vdpa_unregister_device later */
> + put_device(&efx->vdpa_nic->vdpa_dev.dev);
> + efx->vdpa_nic = NULL;
> + }
> +}
> +
> void ef100_vdpa_fini(struct efx_probe_data *probe_data)
> {
> struct efx_nic *efx = &probe_data->efx;
> + struct ef100_nic_data *nic_data;
>
> if (efx->state != STATE_VDPA && efx->state != STATE_DISABLED) {
> pci_err(efx->pci_dev, "Invalid efx state %u", efx->state);
> return;
> }
>
> + /* Handle vdpa device deletion, if not done explicitly */
> + ef100_vdpa_delete(efx);
> + nic_data = efx->nic_data;
> + nic_data->vdpa_class = EF100_VDPA_CLASS_NONE;
> efx->state = STATE_PROBED;
> down_write(&efx->filter_sem);
> efx_mcdi_filter_table_remove(efx);
> up_write(&efx->filter_sem);
> }
> +
> +static int get_net_config(struct ef100_vdpa_nic *vdpa_nic)
> +{
> + struct efx_nic *efx = vdpa_nic->efx;
> + u16 mtu;
> + int rc;
> +
> + vdpa_nic->net_config.max_virtqueue_pairs =
> + cpu_to_efx_vdpa16(vdpa_nic, vdpa_nic->max_queue_pairs);
> +
> + rc = efx_vdpa_get_mtu(efx, &mtu);
> + if (rc) {
> + dev_err(&vdpa_nic->vdpa_dev.dev,
> + "%s: Get MTU for vf:%u failed:%d\n", __func__,
> + vdpa_nic->vf_index, rc);
> + return rc;
> + }
> + vdpa_nic->net_config.mtu = cpu_to_efx_vdpa16(vdpa_nic, mtu);
> + vdpa_nic->net_config.status = cpu_to_efx_vdpa16(vdpa_nic,
> + VIRTIO_NET_S_LINK_UP);
> + return 0;
> +}
> +
> +static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
> + const char *dev_name,
> + enum ef100_vdpa_class dev_type,
> + const u8 *mac)
> +{
> + struct ef100_nic_data *nic_data = efx->nic_data;
> + struct ef100_vdpa_nic *vdpa_nic;
> + struct device *dev;
> + int rc;
> +
> + nic_data->vdpa_class = dev_type;
> + vdpa_nic = vdpa_alloc_device(struct ef100_vdpa_nic,
> + vdpa_dev, &efx->pci_dev->dev,
> + &ef100_vdpa_config_ops,
> + 1, 1,
> + dev_name, false);
> + if (!vdpa_nic) {
> + pci_err(efx->pci_dev,
> + "vDPA device allocation failed for vf: %u\n",
> + nic_data->vf_index);
> + nic_data->vdpa_class = EF100_VDPA_CLASS_NONE;
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + mutex_init(&vdpa_nic->lock);
> + dev = &vdpa_nic->vdpa_dev.dev;
> + efx->vdpa_nic = vdpa_nic;
> + vdpa_nic->vdpa_dev.dma_dev = &efx->pci_dev->dev;
> + vdpa_nic->vdpa_dev.mdev = efx->mgmt_dev;
> + vdpa_nic->efx = efx;
> + vdpa_nic->pf_index = nic_data->pf_index;
> + vdpa_nic->vf_index = nic_data->vf_index;
> + vdpa_nic->vdpa_state = EF100_VDPA_STATE_INITIALIZED;
> + vdpa_nic->mac_address = (u8 *)&vdpa_nic->net_config.mac;
> + ether_addr_copy(vdpa_nic->mac_address, mac);
> + vdpa_nic->mac_configured = true;
> +
> + rc = get_net_config(vdpa_nic);
> + if (rc)
> + goto err_put_device;
> +
> + /* _vdpa_register_device when its ready */
> +
> + return vdpa_nic;
> +
> +err_put_device:
> + /* put_device invokes ef100_vdpa_free */
> + put_device(&vdpa_nic->vdpa_dev.dev);
> + return ERR_PTR(rc);
> +}
> +
> +static void ef100_vdpa_net_dev_del(struct vdpa_mgmt_dev *mgmt_dev,
> + struct vdpa_device *vdev)
> +{
> + struct ef100_nic_data *nic_data;
> + struct efx_nic *efx;
> + int rc;
> +
> + efx = pci_get_drvdata(to_pci_dev(mgmt_dev->device));
> + nic_data = efx->nic_data;
> +
> + rc = efx_ef100_set_bar_config(efx, EF100_BAR_CONFIG_EF100);
> + if (rc)
> + pci_err(efx->pci_dev,
> + "set_bar_config EF100 failed, err: %d\n", rc);
> + else
> + pci_dbg(efx->pci_dev,
> + "vdpa net device deleted, vf: %u\n",
> + nic_data->vf_index);
> +}
> +
> +static int ef100_vdpa_net_dev_add(struct vdpa_mgmt_dev *mgmt_dev,
> + const char *name,
> + const struct vdpa_dev_set_config *config)
> +{
> + struct ef100_vdpa_nic *vdpa_nic;
> + struct ef100_nic_data *nic_data;
> + struct efx_nic *efx;
> + int rc, err;
> +
> + efx = pci_get_drvdata(to_pci_dev(mgmt_dev->device));
> + if (efx->vdpa_nic) {
> + pci_warn(efx->pci_dev,
> + "vDPA device already exists on this VF\n");
> + return -EEXIST;
> + }
> +
> + if (config->mask & BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR)) {
> + if (!is_valid_ether_addr(config->net.mac)) {
> + pci_err(efx->pci_dev, "Invalid MAC address %pM\n",
> + config->net.mac);
> + return -EINVAL;
> + }
> + } else {
> + pci_err(efx->pci_dev, "MAC address parameter missing\n");
Does this mean ef100 vf doesn't have a given mac address?
> + return -EIO;
> + }
> +
> + nic_data = efx->nic_data;
> +
> + rc = efx_ef100_set_bar_config(efx, EF100_BAR_CONFIG_VDPA);
> + if (rc) {
> + pci_err(efx->pci_dev,
> + "set_bar_config vDPA failed, err: %d\n", rc);
> + goto err_set_bar_config;
> + }
> +
> + vdpa_nic = ef100_vdpa_create(efx, name, EF100_VDPA_CLASS_NET,
> + (const u8 *)config->net.mac);
> + if (IS_ERR(vdpa_nic)) {
> + pci_err(efx->pci_dev,
> + "vDPA device creation failed, vf: %u, err: %ld\n",
> + nic_data->vf_index, PTR_ERR(vdpa_nic));
> + rc = PTR_ERR(vdpa_nic);
> + goto err_set_bar_config;
> + } else {
> + pci_dbg(efx->pci_dev,
> + "vdpa net device created, vf: %u\n",
> + nic_data->vf_index);
> + pci_warn(efx->pci_dev,
> + "Use QEMU versions 6.1.0 and later with vhost-vdpa\n");
> + }
> +
> + return 0;
> +
> +err_set_bar_config:
> + err = efx_ef100_set_bar_config(efx, EF100_BAR_CONFIG_EF100);
> + if (err)
> + pci_err(efx->pci_dev,
> + "set_bar_config EF100 failed, err: %d\n", err);
> +
> + return rc;
> +}
> +
> +static const struct vdpa_mgmtdev_ops ef100_vdpa_net_mgmtdev_ops = {
> + .dev_add = ef100_vdpa_net_dev_add,
> + .dev_del = ef100_vdpa_net_dev_del
> +};
> +
> +int ef100_vdpa_register_mgmtdev(struct efx_nic *efx)
> +{
> + struct vdpa_mgmt_dev *mgmt_dev;
> + u64 features;
> + int rc;
> +
> + mgmt_dev = kzalloc(sizeof(*mgmt_dev), GFP_KERNEL);
> + if (!mgmt_dev)
> + return -ENOMEM;
> +
> + rc = efx_vdpa_get_features(efx, EF100_VDPA_DEVICE_TYPE_NET, &features);
> + if (rc) {
> + pci_err(efx->pci_dev, "%s: MCDI get features error:%d\n",
> + __func__, rc);
> + goto err_get_features;
> + }
> +
> + efx->mgmt_dev = mgmt_dev;
> + mgmt_dev->device = &efx->pci_dev->dev;
> + mgmt_dev->id_table = ef100_vdpa_id_table;
> + mgmt_dev->ops = &ef100_vdpa_net_mgmtdev_ops;
> + mgmt_dev->supported_features = features;
> + mgmt_dev->max_supported_vqs = EF100_VDPA_MAX_QUEUES_PAIRS * 2;
> + mgmt_dev->config_attr_mask = BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR);
> +
> + rc = vdpa_mgmtdev_register(mgmt_dev);
> + if (rc) {
> + pci_err(efx->pci_dev,
> + "vdpa_mgmtdev_register failed, err: %d\n", rc);
> + goto err_mgmtdev_register;
> + }
> +
> + return 0;
> +
> +err_mgmtdev_register:
> +err_get_features:
> + kfree(mgmt_dev);
> + efx->mgmt_dev = NULL;
> +
> + return rc;
> +}
> +
> +void ef100_vdpa_unregister_mgmtdev(struct efx_nic *efx)
> +{
> + if (efx->mgmt_dev) {
> + vdpa_mgmtdev_unregister(efx->mgmt_dev);
> + kfree(efx->mgmt_dev);
> + efx->mgmt_dev = NULL;
> + }
> +}
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
> index 6b51a05becd8..83f6d819f6a5 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa.h
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
> @@ -18,6 +18,24 @@
>
> #if defined(CONFIG_SFC_VDPA)
>
> +/* Max queue pairs currently supported */
> +#define EF100_VDPA_MAX_QUEUES_PAIRS 1
> +
> +/**
> + * enum ef100_vdpa_nic_state - possible states for a vDPA NIC
> + *
> + * @EF100_VDPA_STATE_INITIALIZED: State after vDPA NIC created
> + * @EF100_VDPA_STATE_NEGOTIATED: State after feature negotiation
> + * @EF100_VDPA_STATE_STARTED: State after driver ok
> + * @EF100_VDPA_STATE_NSTATES: Number of VDPA states
> + */
> +enum ef100_vdpa_nic_state {
> + EF100_VDPA_STATE_INITIALIZED,
> + EF100_VDPA_STATE_NEGOTIATED,
> + EF100_VDPA_STATE_STARTED,
> + EF100_VDPA_STATE_NSTATES
> +};
> +
> enum ef100_vdpa_device_type {
> EF100_VDPA_DEVICE_TYPE_NET,
> };
> @@ -28,7 +46,73 @@ enum ef100_vdpa_vq_type {
> EF100_VDPA_VQ_NTYPES
> };
>
> +/**
> + * struct ef100_vdpa_nic - vDPA NIC data structure
> + *
> + * @vdpa_dev: vdpa_device object which registers on the vDPA bus.
> + * @vdpa_state: NIC state machine governed by ef100_vdpa_nic_state
> + * @efx: pointer to the VF's efx_nic object
> + * @lock: Managing access to vdpa config operations
> + * @pf_index: PF index of the vDPA VF
> + * @vf_index: VF index of the vDPA VF
> + * @status: device status as per VIRTIO spec
> + * @features: negotiated feature bits
> + * @max_queue_pairs: maximum number of queue pairs supported
> + * @net_config: virtio_net_config data
> + * @mac_address: mac address of interface associated with this vdpa device
> + * @mac_configured: true after MAC address is configured
> + */
> +struct ef100_vdpa_nic {
> + struct vdpa_device vdpa_dev;
> + enum ef100_vdpa_nic_state vdpa_state;
> + struct efx_nic *efx;
> + /* for synchronizing access to vdpa config operations */
> + struct mutex lock;
> + u32 pf_index;
> + u32 vf_index;
> + u8 status;
> + u64 features;
> + u32 max_queue_pairs;
> + struct virtio_net_config net_config;
> + u8 *mac_address;
> + bool mac_configured;
> +};
> +
> int ef100_vdpa_init(struct efx_probe_data *probe_data);
> void ef100_vdpa_fini(struct efx_probe_data *probe_data);
> +int ef100_vdpa_register_mgmtdev(struct efx_nic *efx);
> +void ef100_vdpa_unregister_mgmtdev(struct efx_nic *efx);
> +
> +static inline bool efx_vdpa_is_little_endian(struct ef100_vdpa_nic *vdpa_nic)
> +{
> + return virtio_legacy_is_little_endian() ||
> + (vdpa_nic->features & (1ULL << VIRTIO_F_VERSION_1));
> +}
> +
> +static inline u16 efx_vdpa16_to_cpu(struct ef100_vdpa_nic *vdpa_nic,
> + __virtio16 val)
> +{
> + return __virtio16_to_cpu(efx_vdpa_is_little_endian(vdpa_nic), val);
> +}
> +
> +static inline __virtio16 cpu_to_efx_vdpa16(struct ef100_vdpa_nic *vdpa_nic,
> + u16 val)
> +{
> + return __cpu_to_virtio16(efx_vdpa_is_little_endian(vdpa_nic), val);
> +}
> +
> +static inline u32 efx_vdpa32_to_cpu(struct ef100_vdpa_nic *vdpa_nic,
> + __virtio32 val)
> +{
> + return __virtio32_to_cpu(efx_vdpa_is_little_endian(vdpa_nic), val);
> +}
> +
> +static inline __virtio32 cpu_to_efx_vdpa32(struct ef100_vdpa_nic *vdpa_nic,
> + u32 val)
> +{
> + return __cpu_to_virtio32(efx_vdpa_is_little_endian(vdpa_nic), val);
> +}
> +
> +extern const struct vdpa_config_ops ef100_vdpa_config_ops;
> #endif /* CONFIG_SFC_VDPA */
> #endif /* __EF100_VDPA_H__ */
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> new file mode 100644
> index 000000000000..31952931c198
> --- /dev/null
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> @@ -0,0 +1,28 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Driver for Xilinx network controllers and boards
> + * Copyright(C) 2020-2022 Xilinx, Inc.
> + * Copyright(C) 2022 Advanced Micro Devices, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published
> + * by the Free Software Foundation, incorporated herein by reference.
> + */
> +
> +#include <linux/vdpa.h>
> +#include "ef100_vdpa.h"
> +
> +static struct ef100_vdpa_nic *get_vdpa_nic(struct vdpa_device *vdev)
> +{
> + return container_of(vdev, struct ef100_vdpa_nic, vdpa_dev);
> +}
> +
> +static void ef100_vdpa_free(struct vdpa_device *vdev)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> +
> + mutex_destroy(&vdpa_nic->lock);
> +}
> +
> +const struct vdpa_config_ops ef100_vdpa_config_ops = {
> + .free = ef100_vdpa_free,
> +};
> diff --git a/drivers/net/ethernet/sfc/mcdi_functions.c b/drivers/net/ethernet/sfc/mcdi_functions.c
> index d3e6d8239f5c..4415f19cf68f 100644
> --- a/drivers/net/ethernet/sfc/mcdi_functions.c
> +++ b/drivers/net/ethernet/sfc/mcdi_functions.c
> @@ -413,7 +413,8 @@ int efx_mcdi_window_mode_to_stride(struct efx_nic *efx, u8 vi_window_mode)
> return 0;
> }
>
> -int efx_get_pf_index(struct efx_nic *efx, unsigned int *pf_index)
> +int efx_get_fn_info(struct efx_nic *efx, unsigned int *pf_index,
> + unsigned int *vf_index)
> {
> MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_FUNCTION_INFO_OUT_LEN);
> size_t outlen;
> @@ -426,6 +427,10 @@ int efx_get_pf_index(struct efx_nic *efx, unsigned int *pf_index)
> if (outlen < sizeof(outbuf))
> return -EIO;
>
> - *pf_index = MCDI_DWORD(outbuf, GET_FUNCTION_INFO_OUT_PF);
> + if (pf_index)
> + *pf_index = MCDI_DWORD(outbuf, GET_FUNCTION_INFO_OUT_PF);
> +
> + if (efx->type->is_vf && vf_index)
> + *vf_index = MCDI_DWORD(outbuf, GET_FUNCTION_INFO_OUT_VF);
> return 0;
> }
> diff --git a/drivers/net/ethernet/sfc/mcdi_functions.h b/drivers/net/ethernet/sfc/mcdi_functions.h
> index b0e2f53a0d9b..76dc0a13463e 100644
> --- a/drivers/net/ethernet/sfc/mcdi_functions.h
> +++ b/drivers/net/ethernet/sfc/mcdi_functions.h
> @@ -28,6 +28,7 @@ void efx_mcdi_rx_remove(struct efx_rx_queue *rx_queue);
> void efx_mcdi_rx_fini(struct efx_rx_queue *rx_queue);
> int efx_fini_dmaq(struct efx_nic *efx);
> int efx_mcdi_window_mode_to_stride(struct efx_nic *efx, u8 vi_window_mode);
> -int efx_get_pf_index(struct efx_nic *efx, unsigned int *pf_index);
> +int efx_get_fn_info(struct efx_nic *efx, unsigned int *pf_index,
> + unsigned int *vf_index);
>
> #endif
> diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
> index ffda80a95221..79356d614109 100644
> --- a/drivers/net/ethernet/sfc/net_driver.h
> +++ b/drivers/net/ethernet/sfc/net_driver.h
> @@ -1182,6 +1182,12 @@ struct efx_nic {
>
> unsigned int mem_bar;
> u32 reg_base;
> +#ifdef CONFIG_SFC_VDPA
> + /** @mgmt_dev: vDPA Management device */
> + struct vdpa_mgmt_dev *mgmt_dev;
> + /** @vdpa_nic: vDPA device structure (EF100) */
> + struct ef100_vdpa_nic *vdpa_nic;
> +#endif
>
> /* The following fields may be written more often */
>
> --
> 2.30.1
>
On Wed, Dec 7, 2022 at 10:56 PM Gautam Dawar <[email protected]> wrote:
>
> This patch implements the vDPA config operations related to
> virtqueues or vrings. These include setting vring address,
> getting vq state, operations to enable/disable a vq etc.
> The resources required for vring operations eg. VI, interrupts etc.
> are also allocated.
>
> Signed-off-by: Gautam Dawar <[email protected]>
> ---
> drivers/net/ethernet/sfc/ef100_vdpa.c | 58 ++-
> drivers/net/ethernet/sfc/ef100_vdpa.h | 55 +++
> drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 449 +++++++++++++++++++++-
> 3 files changed, 560 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.c b/drivers/net/ethernet/sfc/ef100_vdpa.c
> index ff4bb61e598e..41eb7aef6798 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa.c
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.c
> @@ -15,6 +15,7 @@
> #include "ef100_vdpa.h"
> #include "mcdi_vdpa.h"
> #include "mcdi_filters.h"
> +#include "mcdi_functions.h"
> #include "ef100_netdev.h"
>
> static struct virtio_device_id ef100_vdpa_id_table[] = {
> @@ -48,6 +49,24 @@ int ef100_vdpa_init(struct efx_probe_data *probe_data)
> return rc;
> }
>
> +static int vdpa_allocate_vis(struct efx_nic *efx, unsigned int *allocated_vis)
> +{
> + /* The first VI is reserved for MCDI
> + * 1 VI each for rx + tx ring
> + */
> + unsigned int max_vis = 1 + EF100_VDPA_MAX_QUEUES_PAIRS;
> + unsigned int min_vis = 1 + 1;
> + int rc;
> +
> + rc = efx_mcdi_alloc_vis(efx, min_vis, max_vis,
> + NULL, allocated_vis);
> + if (!rc)
> + return rc;
> + if (*allocated_vis < min_vis)
> + return -ENOSPC;
> + return 0;
> +}
> +
> static void ef100_vdpa_delete(struct efx_nic *efx)
> {
> if (efx->vdpa_nic) {
> @@ -55,6 +74,7 @@ static void ef100_vdpa_delete(struct efx_nic *efx)
> put_device(&efx->vdpa_nic->vdpa_dev.dev);
> efx->vdpa_nic = NULL;
> }
> + efx_mcdi_free_vis(efx);
> }
>
> void ef100_vdpa_fini(struct efx_probe_data *probe_data)
> @@ -106,10 +126,20 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
> {
> struct ef100_nic_data *nic_data = efx->nic_data;
> struct ef100_vdpa_nic *vdpa_nic;
> + unsigned int allocated_vis;
> struct device *dev;
> int rc;
> + u8 i;
>
> nic_data->vdpa_class = dev_type;
> + rc = vdpa_allocate_vis(efx, &allocated_vis);
> + if (rc) {
> + pci_err(efx->pci_dev,
> + "%s Alloc VIs failed for vf:%u error:%d\n",
> + __func__, nic_data->vf_index, rc);
> + return ERR_PTR(rc);
> + }
> +
> vdpa_nic = vdpa_alloc_device(struct ef100_vdpa_nic,
> vdpa_dev, &efx->pci_dev->dev,
> &ef100_vdpa_config_ops,
> @@ -120,7 +150,8 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
> "vDPA device allocation failed for vf: %u\n",
> nic_data->vf_index);
> nic_data->vdpa_class = EF100_VDPA_CLASS_NONE;
> - return ERR_PTR(-ENOMEM);
> + rc = -ENOMEM;
> + goto err_alloc_vis_free;
> }
>
> mutex_init(&vdpa_nic->lock);
> @@ -129,6 +160,7 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
> vdpa_nic->vdpa_dev.dma_dev = &efx->pci_dev->dev;
> vdpa_nic->vdpa_dev.mdev = efx->mgmt_dev;
> vdpa_nic->efx = efx;
> + vdpa_nic->max_queue_pairs = allocated_vis - 1;
> vdpa_nic->pf_index = nic_data->pf_index;
> vdpa_nic->vf_index = nic_data->vf_index;
> vdpa_nic->vdpa_state = EF100_VDPA_STATE_INITIALIZED;
> @@ -136,6 +168,27 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
> ether_addr_copy(vdpa_nic->mac_address, mac);
> vdpa_nic->mac_configured = true;
>
> + for (i = 0; i < (2 * vdpa_nic->max_queue_pairs); i++)
> + vdpa_nic->vring[i].irq = -EINVAL;
> +
> + rc = ef100_vdpa_irq_vectors_alloc(efx->pci_dev,
> + vdpa_nic->max_queue_pairs * 2);
> + if (rc < 0) {
> + pci_err(efx->pci_dev,
> + "vDPA IRQ alloc failed for vf: %u err:%d\n",
> + nic_data->vf_index, rc);
> + goto err_put_device;
> + }
Is it possible to move irq allocation/free to set_status()? This is
what other vDPA drivers did and it helped to save resources.
> +
> + rc = devm_add_action_or_reset(&efx->pci_dev->dev,
> + ef100_vdpa_irq_vectors_free,
> + efx->pci_dev);
> + if (rc) {
> + pci_err(efx->pci_dev,
> + "Failed adding devres for freeing irq vectors\n");
> + goto err_put_device;
> + }
> +
> rc = get_net_config(vdpa_nic);
> if (rc)
> goto err_put_device;
> @@ -147,6 +200,9 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
> err_put_device:
> /* put_device invokes ef100_vdpa_free */
> put_device(&vdpa_nic->vdpa_dev.dev);
> +
> +err_alloc_vis_free:
> + efx_mcdi_free_vis(efx);
> return ERR_PTR(rc);
> }
>
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
> index be7650c3166a..3cc33daa0431 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa.h
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
> @@ -33,6 +33,20 @@
> /* Alignment requirement of the Virtqueue */
> #define EF100_VDPA_VQ_ALIGN 4096
>
> +/* Vring configuration definitions */
> +#define EF100_VRING_ADDRESS_CONFIGURED 0x1
> +#define EF100_VRING_SIZE_CONFIGURED 0x10
> +#define EF100_VRING_READY_CONFIGURED 0x100
> +#define EF100_VRING_CONFIGURED (EF100_VRING_ADDRESS_CONFIGURED | \
> + EF100_VRING_SIZE_CONFIGURED | \
> + EF100_VRING_READY_CONFIGURED)
> +
> +/* Maximum size of msix name */
> +#define EF100_VDPA_MAX_MSIX_NAME_SIZE 256
> +
> +/* Default high IOVA for MCDI buffer */
> +#define EF100_VDPA_IOVA_BASE_ADDR 0x20000000000
> +
> /**
> * enum ef100_vdpa_nic_state - possible states for a vDPA NIC
> *
> @@ -58,6 +72,43 @@ enum ef100_vdpa_vq_type {
> EF100_VDPA_VQ_NTYPES
> };
>
> +/**
> + * struct ef100_vdpa_vring_info - vDPA vring data structure
> + *
> + * @desc: Descriptor area address of the vring
> + * @avail: Available area address of the vring
> + * @used: Device area address of the vring
> + * @size: Number of entries in the vring
> + * @vring_state: bit map to track vring configuration
> + * @vring_created: set to true when vring is created.
I wonder if we can merge vring_created into one bit of the vring_state.
> + * @last_avail_idx: last available index of the vring
> + * @last_used_idx: last used index of the vring
> + * @doorbell_offset: doorbell offset
> + * @doorbell_offset_valid: true if @doorbell_offset is updated
> + * @vring_type: type of vring created
> + * @vring_ctx: vring context information
> + * @msix_name: device name for vring irq handler
> + * @irq: irq number for vring irq handler
> + * @cb: callback for vring interrupts
> + */
> +struct ef100_vdpa_vring_info {
> + dma_addr_t desc;
> + dma_addr_t avail;
> + dma_addr_t used;
> + u32 size;
> + u16 vring_state;
> + bool vring_created;
> + u32 last_avail_idx;
> + u32 last_used_idx;
> + u32 doorbell_offset;
> + bool doorbell_offset_valid;
> + enum ef100_vdpa_vq_type vring_type;
> + struct efx_vring_ctx *vring_ctx;
> + char msix_name[EF100_VDPA_MAX_MSIX_NAME_SIZE];
> + u32 irq;
> + struct vdpa_callback cb;
> +};
> +
> /**
> * struct ef100_vdpa_nic - vDPA NIC data structure
> *
> @@ -71,6 +122,7 @@ enum ef100_vdpa_vq_type {
> * @features: negotiated feature bits
> * @max_queue_pairs: maximum number of queue pairs supported
> * @net_config: virtio_net_config data
> + * @vring: vring information of the vDPA device.
> * @mac_address: mac address of interface associated with this vdpa device
> * @mac_configured: true after MAC address is configured
> * @cfg_cb: callback for config change
> @@ -87,6 +139,7 @@ struct ef100_vdpa_nic {
> u64 features;
> u32 max_queue_pairs;
> struct virtio_net_config net_config;
> + struct ef100_vdpa_vring_info vring[EF100_VDPA_MAX_QUEUES_PAIRS * 2];
> u8 *mac_address;
> bool mac_configured;
> struct vdpa_callback cfg_cb;
> @@ -96,6 +149,8 @@ int ef100_vdpa_init(struct efx_probe_data *probe_data);
> void ef100_vdpa_fini(struct efx_probe_data *probe_data);
> int ef100_vdpa_register_mgmtdev(struct efx_nic *efx);
> void ef100_vdpa_unregister_mgmtdev(struct efx_nic *efx);
> +int ef100_vdpa_irq_vectors_alloc(struct pci_dev *pci_dev, u16 nvqs);
> +void ef100_vdpa_irq_vectors_free(void *data);
>
> static inline bool efx_vdpa_is_little_endian(struct ef100_vdpa_nic *vdpa_nic)
> {
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> index 87899baa1c52..b7efd3e0c901 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> @@ -10,13 +10,441 @@
>
> #include <linux/vdpa.h>
> #include "ef100_vdpa.h"
> +#include "io.h"
> #include "mcdi_vdpa.h"
>
> +/* Get the queue's function-local index of the associated VI
> + * virtqueue number queue 0 is reserved for MCDI
> + */
> +#define EFX_GET_VI_INDEX(vq_num) (((vq_num) / 2) + 1)
> +
> static struct ef100_vdpa_nic *get_vdpa_nic(struct vdpa_device *vdev)
> {
> return container_of(vdev, struct ef100_vdpa_nic, vdpa_dev);
> }
>
> +static irqreturn_t vring_intr_handler(int irq, void *arg)
> +{
> + struct ef100_vdpa_vring_info *vring = arg;
> +
> + if (vring->cb.callback)
> + return vring->cb.callback(vring->cb.private);
> +
> + return IRQ_NONE;
> +}
> +
> +int ef100_vdpa_irq_vectors_alloc(struct pci_dev *pci_dev, u16 nvqs)
> +{
> + int rc;
> +
> + rc = pci_alloc_irq_vectors(pci_dev, nvqs, nvqs, PCI_IRQ_MSIX);
> + if (rc < 0)
> + pci_err(pci_dev,
> + "Failed to alloc %d IRQ vectors, err:%d\n", nvqs, rc);
> + return rc;
> +}
> +
> +void ef100_vdpa_irq_vectors_free(void *data)
> +{
> + pci_free_irq_vectors(data);
> +}
> +
> +static int irq_vring_init(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
> +{
> + struct ef100_vdpa_vring_info *vring = &vdpa_nic->vring[idx];
> + struct pci_dev *pci_dev = vdpa_nic->efx->pci_dev;
> + int irq;
> + int rc;
> +
> + snprintf(vring->msix_name, 256, "x_vdpa[%s]-%d\n",
> + pci_name(pci_dev), idx);
> + irq = pci_irq_vector(pci_dev, idx);
> + rc = devm_request_irq(&pci_dev->dev, irq, vring_intr_handler, 0,
> + vring->msix_name, vring);
> + if (rc)
> + pci_err(pci_dev,
> + "devm_request_irq failed for vring %d, rc %d\n",
> + idx, rc);
> + else
> + vring->irq = irq;
> +
> + return rc;
> +}
> +
> +static void irq_vring_fini(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
> +{
> + struct ef100_vdpa_vring_info *vring = &vdpa_nic->vring[idx];
> + struct pci_dev *pci_dev = vdpa_nic->efx->pci_dev;
> +
> + devm_free_irq(&pci_dev->dev, vring->irq, vring);
> + vring->irq = -EINVAL;
> +}
> +
> +static bool can_create_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
> +{
> + if (vdpa_nic->vring[idx].vring_state == EF100_VRING_CONFIGURED &&
> + vdpa_nic->status & VIRTIO_CONFIG_S_DRIVER_OK &&
> + !vdpa_nic->vring[idx].vring_created)
> + return true;
> +
> + return false;
> +}
> +
> +static int create_vring_ctx(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
> +{
> + struct efx_vring_ctx *vring_ctx;
> + u32 vi_index;
> +
> + if (idx % 2) /* Even VQ for RX and odd for TX */
> + vdpa_nic->vring[idx].vring_type = EF100_VDPA_VQ_TYPE_NET_TXQ;
> + else
> + vdpa_nic->vring[idx].vring_type = EF100_VDPA_VQ_TYPE_NET_RXQ;
> + vi_index = EFX_GET_VI_INDEX(idx);
> + vdpa_nic->vring[idx].vring_type);
> + if (IS_ERR(vring_ctx))
> + return PTR_ERR(vring_ctx);
> +
> + vdpa_nic->vring[idx].vring_ctx = vring_ctx;
> + return 0;
> +}
> +
> +static void delete_vring_ctx(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
> +{
> + efx_vdpa_vring_fini(vdpa_nic->vring[idx].vring_ctx);
> + vdpa_nic->vring[idx].vring_ctx = NULL;
> +}
> +
> +static int delete_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
> +{
> + struct efx_vring_dyn_cfg vring_dyn_cfg;
> + int rc;
> +
> + rc = efx_vdpa_vring_destroy(vdpa_nic->vring[idx].vring_ctx,
> + &vring_dyn_cfg);
> + if (rc)
> + dev_err(&vdpa_nic->vdpa_dev.dev,
> + "%s: delete vring failed index:%u, err:%d\n",
> + __func__, idx, rc);
> + vdpa_nic->vring[idx].last_avail_idx = vring_dyn_cfg.avail_idx;
> + vdpa_nic->vring[idx].last_used_idx = vring_dyn_cfg.used_idx;
> + vdpa_nic->vring[idx].vring_created = false;
> +
> + irq_vring_fini(vdpa_nic, idx);
> +
> + if (vdpa_nic->vring[idx].vring_ctx)
> + delete_vring_ctx(vdpa_nic, idx);
> +
> + return rc;
> +}
> +
> +static int create_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
> +{
> + struct efx_vring_dyn_cfg vring_dyn_cfg;
> + struct efx_vring_ctx *vring_ctx;
> + struct efx_vring_cfg vring_cfg;
> + u32 offset;
> + int rc;
> +
> + if (!vdpa_nic->vring[idx].vring_ctx) {
> + rc = create_vring_ctx(vdpa_nic, idx);
> + if (rc) {
> + dev_err(&vdpa_nic->vdpa_dev.dev,
> + "%s: create_vring_ctx failed idx:%u, err:%d\n",
> + __func__, idx, rc);
> + return rc;
> + }
> + }
> + vring_ctx = vdpa_nic->vring[idx].vring_ctx;
> +
> + rc = irq_vring_init(vdpa_nic, idx);
> + if (rc) {
> + dev_err(&vdpa_nic->vdpa_dev.dev,
> + "%s: irq_vring_init failed. index:%u, err:%d\n",
> + __func__, idx, rc);
> + goto err_irq_vring_init;
> + }
> + vring_cfg.desc = vdpa_nic->vring[idx].desc;
> + vring_cfg.avail = vdpa_nic->vring[idx].avail;
> + vring_cfg.used = vdpa_nic->vring[idx].used;
> + vring_cfg.size = vdpa_nic->vring[idx].size;
> + vring_cfg.features = vdpa_nic->features;
> + vring_cfg.msix_vector = idx;
> + vring_dyn_cfg.avail_idx = vdpa_nic->vring[idx].last_avail_idx;
> + vring_dyn_cfg.used_idx = vdpa_nic->vring[idx].last_used_idx;
> +
> + rc = efx_vdpa_vring_create(vring_ctx, &vring_cfg, &vring_dyn_cfg);
> + if (rc) {
> + dev_err(&vdpa_nic->vdpa_dev.dev,
> + "%s: vring_create failed index:%u, err:%d\n",
> + __func__, idx, rc);
> + goto err_vring_create;
> + }
> + vdpa_nic->vring[idx].vring_created = true;
> + if (!vdpa_nic->vring[idx].doorbell_offset_valid) {
> + rc = efx_vdpa_get_doorbell_offset(vring_ctx, &offset);
> + if (rc) {
> + dev_err(&vdpa_nic->vdpa_dev.dev,
> + "%s: get offset failed, index:%u err:%d\n",
> + __func__, idx, rc);
> + goto err_get_doorbell_offset;
> + }
> + vdpa_nic->vring[idx].doorbell_offset = offset;
> + vdpa_nic->vring[idx].doorbell_offset_valid = true;
> + }
> +
> + return 0;
> +
> +err_get_doorbell_offset:
> + efx_vdpa_vring_destroy(vdpa_nic->vring[idx].vring_ctx,
> + &vring_dyn_cfg);
> + vdpa_nic->vring[idx].vring_created = false;
> +err_vring_create:
> + irq_vring_fini(vdpa_nic, idx);
> +err_irq_vring_init:
> + delete_vring_ctx(vdpa_nic, idx);
> +
> + return rc;
> +}
> +
> +static void reset_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
> +{
> + if (vdpa_nic->vring[idx].vring_created)
> + delete_vring(vdpa_nic, idx);
> +
> + memset((void *)&vdpa_nic->vring[idx], 0,
> + sizeof(vdpa_nic->vring[idx]));
> + vdpa_nic->vring[idx].vring_type = EF100_VDPA_VQ_NTYPES;
> +}
> +
> +static bool is_qid_invalid(struct ef100_vdpa_nic *vdpa_nic, u16 idx,
> + const char *caller)
> +{
> + if (unlikely(idx >= (vdpa_nic->max_queue_pairs * 2))) {
> + dev_err(&vdpa_nic->vdpa_dev.dev,
> + "%s: Invalid qid %u\n", caller, idx);
> + return true;
> + }
> + return false;
> +}
> +
> +static int ef100_vdpa_set_vq_address(struct vdpa_device *vdev,
> + u16 idx, u64 desc_area, u64 driver_area,
> + u64 device_area)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> +
> + if (is_qid_invalid(vdpa_nic, idx, __func__))
> + return -EINVAL;
> +
> + mutex_lock(&vdpa_nic->lock);
> + vdpa_nic->vring[idx].desc = desc_area;
> + vdpa_nic->vring[idx].avail = driver_area;
> + vdpa_nic->vring[idx].used = device_area;
> + vdpa_nic->vring[idx].vring_state |= EF100_VRING_ADDRESS_CONFIGURED;
Does this mean the NIC is unable to handle the case where the
virtqueue is mis-configured?
> + mutex_unlock(&vdpa_nic->lock);
> + return 0;
> +}
> +
> +static void ef100_vdpa_set_vq_num(struct vdpa_device *vdev, u16 idx, u32 num)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> +
> + if (is_qid_invalid(vdpa_nic, idx, __func__))
> + return;
> +
> + if (!is_power_of_2(num)) {
> + dev_err(&vdev->dev, "%s: Index:%u size:%u not power of 2\n",
> + __func__, idx, num);
> + return;
> + }
> + if (num > EF100_VDPA_VQ_NUM_MAX_SIZE) {
> + dev_err(&vdev->dev, "%s: Index:%u size:%u more than max:%u\n",
> + __func__, idx, num, EF100_VDPA_VQ_NUM_MAX_SIZE);
> + return;
> + }
> + mutex_lock(&vdpa_nic->lock);
> + vdpa_nic->vring[idx].size = num;
> + vdpa_nic->vring[idx].vring_state |= EF100_VRING_SIZE_CONFIGURED;
> + mutex_unlock(&vdpa_nic->lock);
> +}
> +
> +static void ef100_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> + u32 idx_val;
> +
> + if (is_qid_invalid(vdpa_nic, idx, __func__))
> + return;
> +
> + if (!vdpa_nic->vring[idx].vring_created) {
> + dev_err(&vdev->dev, "%s: Invalid vring%u\n", __func__, idx);
> + return;
> + }
This seems guest/user space trigger-able, if yes, let's avoid this warning.
> +
> + idx_val = idx;
> + _efx_writed(vdpa_nic->efx, cpu_to_le32(idx_val),
> + vdpa_nic->vring[idx].doorbell_offset);
> +}
> +
> +static void ef100_vdpa_set_vq_cb(struct vdpa_device *vdev, u16 idx,
> + struct vdpa_callback *cb)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> +
> + if (is_qid_invalid(vdpa_nic, idx, __func__))
> + return;
> +
> + if (cb)
> + vdpa_nic->vring[idx].cb = *cb;
> +}
> +
> +static void ef100_vdpa_set_vq_ready(struct vdpa_device *vdev, u16 idx,
> + bool ready)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> +
> + if (is_qid_invalid(vdpa_nic, idx, __func__))
> + return;
> +
> + mutex_lock(&vdpa_nic->lock);
> + if (ready) {
> + vdpa_nic->vring[idx].vring_state |=
> + EF100_VRING_READY_CONFIGURED;
> + if (vdpa_nic->vdpa_state == EF100_VDPA_STATE_STARTED &&
> + can_create_vring(vdpa_nic, idx)) {
If this is the only caller of can_create_vring(), let's move the check
of vdpa_state into the can_create_vring().
> + if (create_vring(vdpa_nic, idx))
> + /* Rollback ready configuration
> + * So that the above layer driver
> + * can make another attempt to set ready
> + */
> + vdpa_nic->vring[idx].vring_state &=
> + ~EF100_VRING_READY_CONFIGURED;
> + }
> + } else {
> + vdpa_nic->vring[idx].vring_state &=
> + ~EF100_VRING_READY_CONFIGURED;
> + if (vdpa_nic->vring[idx].vring_created)
> + delete_vring(vdpa_nic, idx);
> + }
> + mutex_unlock(&vdpa_nic->lock);
> +}
> +
> +static bool ef100_vdpa_get_vq_ready(struct vdpa_device *vdev, u16 idx)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> + bool ready;
> +
> + if (is_qid_invalid(vdpa_nic, idx, __func__))
> + return false;
> +
> + mutex_lock(&vdpa_nic->lock);
> + ready = vdpa_nic->vring[idx].vring_state & EF100_VRING_READY_CONFIGURED;
> + mutex_unlock(&vdpa_nic->lock);
> + return ready;
> +}
> +
> +static int ef100_vdpa_set_vq_state(struct vdpa_device *vdev, u16 idx,
> + const struct vdpa_vq_state *state)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> +
> + if (is_qid_invalid(vdpa_nic, idx, __func__))
> + return -EINVAL;
> +
> + mutex_lock(&vdpa_nic->lock);
> + vdpa_nic->vring[idx].last_avail_idx = state->split.avail_index;
Should we update last_used_idx here as well?
> + mutex_unlock(&vdpa_nic->lock);
> + return 0;
> +}
> +
> +static int ef100_vdpa_get_vq_state(struct vdpa_device *vdev,
> + u16 idx, struct vdpa_vq_state *state)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> +
> + if (is_qid_invalid(vdpa_nic, idx, __func__))
> + return -EINVAL;
> +
> + mutex_lock(&vdpa_nic->lock);
> + state->split.avail_index = (u16)vdpa_nic->vring[idx].last_avail_idx;
> + mutex_unlock(&vdpa_nic->lock);
> +
> + return 0;
> +}
> +
> +static struct vdpa_notification_area
> + ef100_vdpa_get_vq_notification(struct vdpa_device *vdev,
> + u16 idx)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> + struct vdpa_notification_area notify_area = {0, 0};
> + struct efx_vring_ctx *vring_ctx;
> + struct efx_nic *efx;
> + u32 offset;
> + int rc;
> +
> + if (is_qid_invalid(vdpa_nic, idx, __func__))
> + goto end;
> +
> + mutex_lock(&vdpa_nic->lock);
> + vring_ctx = vdpa_nic->vring[idx].vring_ctx;
> + if (!vring_ctx) {
> + rc = create_vring_ctx(vdpa_nic, idx);
Interesting, in set_vq_ready() we call can_create_vring() before
create_vring(). The code here implies usespace can bypass that check,
is this intended?
> + if (rc) {
> + dev_err(&vdpa_nic->vdpa_dev.dev,
> + "%s: vring ctx failed index:%u, err:%d\n",
> + __func__, idx, rc);
> + goto err_create_vring_ctx;
> + }
> + vring_ctx = vdpa_nic->vring[idx].vring_ctx;
> + }
> + if (!vdpa_nic->vring[idx].doorbell_offset_valid) {
I wonder if it's better to do this in the dev_add() since it is not
expected to be changed.
> + rc = efx_vdpa_get_doorbell_offset(vring_ctx, &offset);
> + if (rc) {
> + dev_err(&vdpa_nic->vdpa_dev.dev,
> + "%s: get_doorbell failed idx:%u, err:%d\n",
> + __func__, idx, rc);
> + goto err_get_doorbell_off;
> + }
> + vdpa_nic->vring[idx].doorbell_offset = offset;
> + vdpa_nic->vring[idx].doorbell_offset_valid = true;
> + }
> +
> + efx = vdpa_nic->efx;
> + notify_area.addr = (uintptr_t)(efx->membase_phys +
> + vdpa_nic->vring[idx].doorbell_offset);
> + /* VDPA doorbells are at a stride of VI/2
> + * One VI stride is shared by both rx & tx doorbells
> + */
> + notify_area.size = efx->vi_stride / 2;
Note that, if each area does not occupy a page, it means it can't be
mapped into userspace.
> +
> + mutex_unlock(&vdpa_nic->lock);
> + return notify_area;
> +
> +err_get_doorbell_off:
> + delete_vring_ctx(vdpa_nic, idx);
> +err_create_vring_ctx:
> + mutex_unlock(&vdpa_nic->lock);
> +end:
> + return notify_area;
> +}
> +
> +static int ef100_get_vq_irq(struct vdpa_device *vdev, u16 idx)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> + u32 irq;
> +
> + if (is_qid_invalid(vdpa_nic, idx, __func__))
It might be easier to use a macro to implement is_qid_invalid() then
we don't need to __func__ trick.
Thanks
> + return -EINVAL;
> +
> + mutex_lock(&vdpa_nic->lock);
> + irq = vdpa_nic->vring[idx].irq;
> + mutex_unlock(&vdpa_nic->lock);
> +
> + return irq;
> +}
> +
> static u32 ef100_vdpa_get_vq_align(struct vdpa_device *vdev)
> {
> return EF100_VDPA_VQ_ALIGN;
> @@ -98,6 +526,8 @@ static void ef100_vdpa_set_config_cb(struct vdpa_device *vdev,
>
> if (cb)
> vdpa_nic->cfg_cb = *cb;
> + else
> + memset(&vdpa_nic->cfg_cb, 0, sizeof(vdpa_nic->cfg_cb));
> }
>
> static u16 ef100_vdpa_get_vq_num_max(struct vdpa_device *vdev)
> @@ -155,11 +585,28 @@ static void ef100_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
> static void ef100_vdpa_free(struct vdpa_device *vdev)
> {
> struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> + int i;
>
> - mutex_destroy(&vdpa_nic->lock);
> + if (vdpa_nic) {
> + for (i = 0; i < (vdpa_nic->max_queue_pairs * 2); i++)
> + reset_vring(vdpa_nic, i);
> + ef100_vdpa_irq_vectors_free(vdpa_nic->efx->pci_dev);
> + mutex_destroy(&vdpa_nic->lock);
> + vdpa_nic->efx->vdpa_nic = NULL;
> + }
> }
>
> const struct vdpa_config_ops ef100_vdpa_config_ops = {
> + .set_vq_address = ef100_vdpa_set_vq_address,
> + .set_vq_num = ef100_vdpa_set_vq_num,
> + .kick_vq = ef100_vdpa_kick_vq,
> + .set_vq_cb = ef100_vdpa_set_vq_cb,
> + .set_vq_ready = ef100_vdpa_set_vq_ready,
> + .get_vq_ready = ef100_vdpa_get_vq_ready,
> + .set_vq_state = ef100_vdpa_set_vq_state,
> + .get_vq_state = ef100_vdpa_get_vq_state,
> + .get_vq_notification = ef100_vdpa_get_vq_notification,
> + .get_vq_irq = ef100_get_vq_irq,
> .get_vq_align = ef100_vdpa_get_vq_align,
> .get_device_features = ef100_vdpa_get_device_features,
> .set_driver_features = ef100_vdpa_set_driver_features,
> --
> 2.30.1
>
On Wed, Dec 7, 2022 at 10:57 PM Gautam Dawar <[email protected]> wrote:
>
> Implement unicast, broadcast and unknown multicast
> filters for receiving different types of traffic.
I suggest squashing this into the patch that implements set_config().
>
> Signed-off-by: Gautam Dawar <[email protected]>
> ---
> drivers/net/ethernet/sfc/ef100_vdpa.c | 159 ++++++++++++++++++++++
> drivers/net/ethernet/sfc/ef100_vdpa.h | 35 +++++
> drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 27 +++-
> 3 files changed, 220 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.c b/drivers/net/ethernet/sfc/ef100_vdpa.c
> index 41eb7aef6798..04d64bfe3c93 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa.c
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.c
> @@ -17,12 +17,168 @@
> #include "mcdi_filters.h"
> #include "mcdi_functions.h"
> #include "ef100_netdev.h"
> +#include "filter.h"
> +#include "efx.h"
>
> +#define EFX_INVALID_FILTER_ID -1
> +
> +/* vDPA queues starts from 2nd VI or qid 1 */
> +#define EF100_VDPA_BASE_RX_QID 1
> +
> +static const char * const filter_names[] = { "bcast", "ucast", "mcast" };
> static struct virtio_device_id ef100_vdpa_id_table[] = {
> { .device = VIRTIO_ID_NET, .vendor = PCI_VENDOR_ID_REDHAT_QUMRANET },
> { 0 },
> };
>
> +static int ef100_vdpa_set_mac_filter(struct efx_nic *efx,
> + struct efx_filter_spec *spec,
> + u32 qid,
> + u8 *mac_addr)
> +{
> + int rc;
> +
> + efx_filter_init_rx(spec, EFX_FILTER_PRI_AUTO, 0, qid);
> +
> + if (mac_addr) {
> + rc = efx_filter_set_eth_local(spec, EFX_FILTER_VID_UNSPEC,
> + mac_addr);
> + if (rc)
> + pci_err(efx->pci_dev,
> + "Filter set eth local failed, err: %d\n", rc);
> + } else {
> + efx_filter_set_mc_def(spec);
> + }
> +
> + rc = efx_filter_insert_filter(efx, spec, true);
> + if (rc < 0)
> + pci_err(efx->pci_dev,
> + "Filter insert failed, err: %d\n", rc);
> +
> + return rc;
> +}
> +
> +static int ef100_vdpa_delete_filter(struct ef100_vdpa_nic *vdpa_nic,
> + enum ef100_vdpa_mac_filter_type type)
> +{
> + struct vdpa_device *vdev = &vdpa_nic->vdpa_dev;
> + int rc;
> +
> + if (vdpa_nic->filters[type].filter_id == EFX_INVALID_FILTER_ID)
> + return rc;
> +
> + rc = efx_filter_remove_id_safe(vdpa_nic->efx,
> + EFX_FILTER_PRI_AUTO,
> + vdpa_nic->filters[type].filter_id);
> + if (rc) {
> + dev_err(&vdev->dev, "%s filter id: %d remove failed, err: %d\n",
> + filter_names[type], vdpa_nic->filters[type].filter_id,
> + rc);
> + } else {
> + vdpa_nic->filters[type].filter_id = EFX_INVALID_FILTER_ID;
> + vdpa_nic->filter_cnt--;
> + }
> + return rc;
> +}
> +
> +int ef100_vdpa_add_filter(struct ef100_vdpa_nic *vdpa_nic,
> + enum ef100_vdpa_mac_filter_type type)
> +{
> + struct vdpa_device *vdev = &vdpa_nic->vdpa_dev;
> + struct efx_nic *efx = vdpa_nic->efx;
> + /* Configure filter on base Rx queue only */
> + u32 qid = EF100_VDPA_BASE_RX_QID;
> + struct efx_filter_spec *spec;
> + u8 baddr[ETH_ALEN];
> + int rc;
> +
> + /* remove existing filter */
> + rc = ef100_vdpa_delete_filter(vdpa_nic, type);
> + if (rc < 0) {
> + dev_err(&vdev->dev, "%s MAC filter deletion failed, err: %d",
> + filter_names[type], rc);
> + return rc;
> + }
> +
> + /* Configure MAC Filter */
> + spec = &vdpa_nic->filters[type].spec;
> + if (type == EF100_VDPA_BCAST_MAC_FILTER) {
> + eth_broadcast_addr(baddr);
> + rc = ef100_vdpa_set_mac_filter(efx, spec, qid, baddr);
> + } else if (type == EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER) {
> + rc = ef100_vdpa_set_mac_filter(efx, spec, qid, NULL);
> + } else {
> + /* Ensure we have everything required to insert the filter */
> + if (!vdpa_nic->mac_configured ||
> + !vdpa_nic->vring[0].vring_created ||
> + !is_valid_ether_addr(vdpa_nic->mac_address))
> + return -EINVAL;
> +
> + rc = ef100_vdpa_set_mac_filter(efx, spec, qid,
> + vdpa_nic->mac_address);
> + }
> +
> + if (rc >= 0) {
> + vdpa_nic->filters[type].filter_id = rc;
> + vdpa_nic->filter_cnt++;
I'm not sure I get this, but the code seems doesn't allow the driver
to set multiple uc filters, so I think filter_cnt should be always 3?
If yes, I don't see how filter_cnt can help here.
> +
> + return 0;
> + }
> +
> + dev_err(&vdev->dev, "%s MAC filter insert failed, err: %d\n",
> + filter_names[type], rc);
> +
> + if (type != EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER) {
> + ef100_vdpa_filter_remove(vdpa_nic);
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +int ef100_vdpa_filter_remove(struct ef100_vdpa_nic *vdpa_nic)
> +{
> + enum ef100_vdpa_mac_filter_type filter;
> + int err = 0;
> + int rc;
> +
> + for (filter = EF100_VDPA_BCAST_MAC_FILTER;
> + filter <= EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER; filter++) {
> + rc = ef100_vdpa_delete_filter(vdpa_nic, filter);
> + if (rc < 0)
> + /* store status of last failed filter remove */
> + err = rc;
> + }
> + return err;
> +}
> +
> +int ef100_vdpa_filter_configure(struct ef100_vdpa_nic *vdpa_nic)
> +{
> + struct vdpa_device *vdev = &vdpa_nic->vdpa_dev;
> + enum ef100_vdpa_mac_filter_type filter;
> + int rc;
> +
> + /* remove existing filters, if any */
> + rc = ef100_vdpa_filter_remove(vdpa_nic);
> + if (rc < 0) {
> + dev_err(&vdev->dev,
> + "MAC filter deletion failed, err: %d", rc);
> + goto fail;
> + }
> +
> + for (filter = EF100_VDPA_BCAST_MAC_FILTER;
> + filter <= EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER; filter++) {
> + if (filter == EF100_VDPA_UCAST_MAC_FILTER &&
> + !vdpa_nic->mac_configured)
> + continue;
> + rc = ef100_vdpa_add_filter(vdpa_nic, filter);
> + if (rc < 0)
> + goto fail;
> + }
> +fail:
> + return rc;
> +}
> +
> int ef100_vdpa_init(struct efx_probe_data *probe_data)
> {
> struct efx_nic *efx = &probe_data->efx;
> @@ -168,6 +324,9 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
> ether_addr_copy(vdpa_nic->mac_address, mac);
> vdpa_nic->mac_configured = true;
>
> + for (i = 0; i < EF100_VDPA_MAC_FILTER_NTYPES; i++)
> + vdpa_nic->filters[i].filter_id = EFX_INVALID_FILTER_ID;
> +
> for (i = 0; i < (2 * vdpa_nic->max_queue_pairs); i++)
> vdpa_nic->vring[i].irq = -EINVAL;
>
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
> index 3cc33daa0431..a33edd6dda12 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa.h
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
> @@ -72,6 +72,22 @@ enum ef100_vdpa_vq_type {
> EF100_VDPA_VQ_NTYPES
> };
>
> +/**
> + * enum ef100_vdpa_mac_filter_type - vdpa filter types
> + *
> + * @EF100_VDPA_BCAST_MAC_FILTER: Broadcast MAC filter
> + * @EF100_VDPA_UCAST_MAC_FILTER: Unicast MAC filter
> + * @EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER: Unknown multicast MAC filter to allow
> + * IPv6 Neighbor Solicitation Message
> + * @EF100_VDPA_MAC_FILTER_NTYPES: Number of vDPA filter types
> + */
> +enum ef100_vdpa_mac_filter_type {
> + EF100_VDPA_BCAST_MAC_FILTER,
> + EF100_VDPA_UCAST_MAC_FILTER,
> + EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER,
> + EF100_VDPA_MAC_FILTER_NTYPES,
> +};
> +
> /**
> * struct ef100_vdpa_vring_info - vDPA vring data structure
> *
> @@ -109,6 +125,17 @@ struct ef100_vdpa_vring_info {
> struct vdpa_callback cb;
> };
>
> +/**
> + * struct ef100_vdpa_filter - vDPA filter data structure
> + *
> + * @filter_id: filter id of this filter
> + * @efx_filter_spec: hardware filter specs for this vdpa device
> + */
> +struct ef100_vdpa_filter {
> + s32 filter_id;
> + struct efx_filter_spec spec;
> +};
> +
> /**
> * struct ef100_vdpa_nic - vDPA NIC data structure
> *
> @@ -118,6 +145,7 @@ struct ef100_vdpa_vring_info {
> * @lock: Managing access to vdpa config operations
> * @pf_index: PF index of the vDPA VF
> * @vf_index: VF index of the vDPA VF
> + * @filter_cnt: total number of filters created on this vdpa device
> * @status: device status as per VIRTIO spec
> * @features: negotiated feature bits
> * @max_queue_pairs: maximum number of queue pairs supported
> @@ -125,6 +153,7 @@ struct ef100_vdpa_vring_info {
> * @vring: vring information of the vDPA device.
> * @mac_address: mac address of interface associated with this vdpa device
> * @mac_configured: true after MAC address is configured
> + * @filters: details of all filters created on this vdpa device
> * @cfg_cb: callback for config change
> */
> struct ef100_vdpa_nic {
> @@ -135,6 +164,7 @@ struct ef100_vdpa_nic {
> struct mutex lock;
> u32 pf_index;
> u32 vf_index;
> + u32 filter_cnt;
> u8 status;
> u64 features;
> u32 max_queue_pairs;
> @@ -142,6 +172,7 @@ struct ef100_vdpa_nic {
> struct ef100_vdpa_vring_info vring[EF100_VDPA_MAX_QUEUES_PAIRS * 2];
> u8 *mac_address;
> bool mac_configured;
> + struct ef100_vdpa_filter filters[EF100_VDPA_MAC_FILTER_NTYPES];
> struct vdpa_callback cfg_cb;
> };
>
> @@ -149,6 +180,10 @@ int ef100_vdpa_init(struct efx_probe_data *probe_data);
> void ef100_vdpa_fini(struct efx_probe_data *probe_data);
> int ef100_vdpa_register_mgmtdev(struct efx_nic *efx);
> void ef100_vdpa_unregister_mgmtdev(struct efx_nic *efx);
> +int ef100_vdpa_filter_configure(struct ef100_vdpa_nic *vdpa_nic);
> +int ef100_vdpa_filter_remove(struct ef100_vdpa_nic *vdpa_nic);
> +int ef100_vdpa_add_filter(struct ef100_vdpa_nic *vdpa_nic,
> + enum ef100_vdpa_mac_filter_type type);
> int ef100_vdpa_irq_vectors_alloc(struct pci_dev *pci_dev, u16 nvqs);
> void ef100_vdpa_irq_vectors_free(void *data);
>
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> index b7efd3e0c901..132ddb4a647b 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> @@ -135,6 +135,15 @@ static int delete_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
> if (vdpa_nic->vring[idx].vring_ctx)
> delete_vring_ctx(vdpa_nic, idx);
>
> + if (idx == 0 && vdpa_nic->filter_cnt != 0) {
> + rc = ef100_vdpa_filter_remove(vdpa_nic);
> + if (rc < 0) {
> + dev_err(&vdpa_nic->vdpa_dev.dev,
> + "%s: vdpa remove filter failed, err:%d\n",
> + __func__, rc);
> + }
> + }
> +
> return rc;
> }
>
> @@ -193,8 +202,22 @@ static int create_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
> vdpa_nic->vring[idx].doorbell_offset_valid = true;
> }
>
> + /* Configure filters on rxq 0 */
> + if (idx == 0) {
This seems tricky, can we move this to set_status() when DRIVER_OK is set?
Thanks
> + rc = ef100_vdpa_filter_configure(vdpa_nic);
> + if (rc < 0) {
> + dev_err(&vdpa_nic->vdpa_dev.dev,
> + "%s: vdpa configure filter failed, err:%d\n",
> + __func__, rc);
> + goto err_filter_configure;
> + }
> + }
> +
> return 0;
>
> +err_filter_configure:
> + ef100_vdpa_filter_remove(vdpa_nic);
> + vdpa_nic->vring[idx].doorbell_offset_valid = false;
> err_get_doorbell_offset:
> efx_vdpa_vring_destroy(vdpa_nic->vring[idx].vring_ctx,
> &vring_dyn_cfg);
> @@ -578,8 +601,10 @@ static void ef100_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
> }
>
> memcpy((u8 *)&vdpa_nic->net_config + offset, buf, len);
> - if (is_valid_ether_addr(vdpa_nic->mac_address))
> + if (is_valid_ether_addr(vdpa_nic->mac_address)) {
> vdpa_nic->mac_configured = true;
> + ef100_vdpa_add_filter(vdpa_nic, EF100_VDPA_UCAST_MAC_FILTER);
> + }
> }
>
> static void ef100_vdpa_free(struct vdpa_device *vdev)
> --
> 2.30.1
>
On Wed, Dec 7, 2022 at 10:56 PM Gautam Dawar <[email protected]> wrote:
>
> vDPA config operations can be broadly categorized in to either
> virtqueue operations, device operations or DMA operations.
> This patch implements most of the device level config operations.
>
> SN1022 supports VIRTIO_F_IN_ORDER which is supported by the DPDK
> virtio driver but not the kernel virtio driver. Due to a bug in
> QEMU (https://gitlab.com/qemu-project/qemu/-/issues/331#), with
> vhost-vdpa, this feature bit is returned with guest kernel virtio
> driver in set_features config operation. The fix for this bug
> (qemu_commit c33f23a419f95da16ab4faaf08be635c89b96ff0) is available
> in QEMU versions 6.1.0 and later. Hence, that's the oldest QEMU
> version required for testing with the vhost-vdpa driver.
>
> With older QEMU releases, VIRTIO_F_IN_ORDER is negotiated but
> not honored causing Firmware exception.
>
> Signed-off-by: Gautam Dawar <[email protected]>
> ---
> drivers/net/ethernet/sfc/ef100_vdpa.h | 14 ++
> drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 148 ++++++++++++++++++++++
> 2 files changed, 162 insertions(+)
>
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
> index 83f6d819f6a5..be7650c3166a 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa.h
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
> @@ -21,6 +21,18 @@
> /* Max queue pairs currently supported */
> #define EF100_VDPA_MAX_QUEUES_PAIRS 1
>
> +/* Device ID of a virtio net device */
> +#define EF100_VDPA_VIRTIO_NET_DEVICE_ID VIRTIO_ID_NET
> +
> +/* Vendor ID of Xilinx vDPA NIC */
> +#define EF100_VDPA_VENDOR_ID PCI_VENDOR_ID_XILINX
> +
> +/* Max number of Buffers supported in the virtqueue */
> +#define EF100_VDPA_VQ_NUM_MAX_SIZE 512
> +
> +/* Alignment requirement of the Virtqueue */
> +#define EF100_VDPA_VQ_ALIGN 4096
> +
> /**
> * enum ef100_vdpa_nic_state - possible states for a vDPA NIC
> *
> @@ -61,6 +73,7 @@ enum ef100_vdpa_vq_type {
> * @net_config: virtio_net_config data
> * @mac_address: mac address of interface associated with this vdpa device
> * @mac_configured: true after MAC address is configured
> + * @cfg_cb: callback for config change
> */
> struct ef100_vdpa_nic {
> struct vdpa_device vdpa_dev;
> @@ -76,6 +89,7 @@ struct ef100_vdpa_nic {
> struct virtio_net_config net_config;
> u8 *mac_address;
> bool mac_configured;
> + struct vdpa_callback cfg_cb;
> };
>
> int ef100_vdpa_init(struct efx_probe_data *probe_data);
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> index 31952931c198..87899baa1c52 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> @@ -10,12 +10,148 @@
>
> #include <linux/vdpa.h>
> #include "ef100_vdpa.h"
> +#include "mcdi_vdpa.h"
>
> static struct ef100_vdpa_nic *get_vdpa_nic(struct vdpa_device *vdev)
> {
> return container_of(vdev, struct ef100_vdpa_nic, vdpa_dev);
> }
>
> +static u32 ef100_vdpa_get_vq_align(struct vdpa_device *vdev)
> +{
> + return EF100_VDPA_VQ_ALIGN;
> +}
> +
> +static u64 ef100_vdpa_get_device_features(struct vdpa_device *vdev)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> + u64 features;
> + int rc;
> +
> + rc = efx_vdpa_get_features(vdpa_nic->efx,
> + EF100_VDPA_DEVICE_TYPE_NET, &features);
> + if (rc) {
> + dev_err(&vdev->dev, "%s: MCDI get features error:%d\n",
> + __func__, rc);
> + /* Returning 0 as value of features will lead to failure
> + * of feature negotiation.
> + */
> + return 0;
> + }
> +
> + /* SN1022 supports VIRTIO_F_IN_ORDER which is supported by the DPDK
> + * virtio driver but not the kernel virtio driver. Due to a bug in
> + * QEMU (https://gitlab.com/qemu-project/qemu/-/issues/331#), with
> + * vhost-vdpa, this feature bit is returned with guest kernel virtio
> + * driver in set_features config operation. The fix for this bug
> + * (commit c33f23a419f95da16ab4faaf08be635c89b96ff0) is available
> + * in QEMU versions 6.1.0 and later. Hence, that's the oldest QEMU
> + * version required for testing with the vhost-vdpa driver.
> + */
I don't see why this comment is placed here?
> + features |= BIT_ULL(VIRTIO_NET_F_MAC);
> +
> + return features;
> +}
> +
> +static int ef100_vdpa_set_driver_features(struct vdpa_device *vdev,
> + u64 features)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> + u64 verify_features;
> + int rc;
> +
> + mutex_lock(&vdpa_nic->lock);
> + if (vdpa_nic->vdpa_state != EF100_VDPA_STATE_INITIALIZED) {
Under which case could we reach this condition? The
vdpa_device_register() should be called after switching the state to
EF100_VDPA_STATE_INITIALIZED.
> + dev_err(&vdev->dev, "%s: Invalid state %u\n",
> + __func__, vdpa_nic->vdpa_state);
> + rc = -EINVAL;
> + goto err;
> + }
> + verify_features = features & ~BIT_ULL(VIRTIO_NET_F_MAC);
> + rc = efx_vdpa_verify_features(vdpa_nic->efx,
> + EF100_VDPA_DEVICE_TYPE_NET,
> + verify_features);
It looks to me this will use MC_CMD_VIRTIO_TEST_FEATURES command, I
wonder if it's better to use
MC_CMD_VIRTIO_SET_FEATURES to align with the virtio spec and maybe
change efx_vdpa_verify_features to efx_vdpa_set_features()?
> +
> + if (rc) {
> + dev_err(&vdev->dev, "%s: MCDI verify features error:%d\n",
> + __func__, rc);
> + goto err;
> + }
> +
> + vdpa_nic->features = features;
> +err:
> + mutex_unlock(&vdpa_nic->lock);
> + return rc;
> +}
> +
> +static u64 ef100_vdpa_get_driver_features(struct vdpa_device *vdev)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> +
> + return vdpa_nic->features;
> +}
> +
> +static void ef100_vdpa_set_config_cb(struct vdpa_device *vdev,
> + struct vdpa_callback *cb)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> +
> + if (cb)
> + vdpa_nic->cfg_cb = *cb;
> +}
> +
> +static u16 ef100_vdpa_get_vq_num_max(struct vdpa_device *vdev)
> +{
> + return EF100_VDPA_VQ_NUM_MAX_SIZE;
> +}
> +
> +static u32 ef100_vdpa_get_device_id(struct vdpa_device *vdev)
> +{
> + return EF100_VDPA_VIRTIO_NET_DEVICE_ID;
> +}
> +
> +static u32 ef100_vdpa_get_vendor_id(struct vdpa_device *vdev)
> +{
> + return EF100_VDPA_VENDOR_ID;
> +}
> +
> +static size_t ef100_vdpa_get_config_size(struct vdpa_device *vdev)
> +{
> + return sizeof(struct virtio_net_config);
> +}
> +
> +static void ef100_vdpa_get_config(struct vdpa_device *vdev,
> + unsigned int offset,
> + void *buf, unsigned int len)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> +
> + /* Avoid the possibility of wrap-up after the sum exceeds U32_MAX */
> + if (WARN_ON(((u64)offset + len) > sizeof(struct virtio_net_config))) {
> + dev_err(&vdev->dev,
> + "%s: Offset + len exceeds config size\n", __func__);
> + return;
I wonder if we need similar checks in the vdpa core.
> + }
> + memcpy(buf, (u8 *)&vdpa_nic->net_config + offset, len);
> +}
> +
> +static void ef100_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
> + const void *buf, unsigned int len)
> +{
> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> +
> + /* Avoid the possibility of wrap-up after the sum exceeds U32_MAX */
> + if (WARN_ON(((u64)offset + len) > sizeof(vdpa_nic->net_config))) {
> + dev_err(&vdev->dev,
> + "%s: Offset + len exceeds config size\n", __func__);
> + return;
> + }
> +
> + memcpy((u8 *)&vdpa_nic->net_config + offset, buf, len);
> + if (is_valid_ether_addr(vdpa_nic->mac_address))
> + vdpa_nic->mac_configured = true;
Do we need to update hardware filters?
Thanks
> +}
> +
> static void ef100_vdpa_free(struct vdpa_device *vdev)
> {
> struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
> @@ -24,5 +160,17 @@ static void ef100_vdpa_free(struct vdpa_device *vdev)
> }
>
> const struct vdpa_config_ops ef100_vdpa_config_ops = {
> + .get_vq_align = ef100_vdpa_get_vq_align,
> + .get_device_features = ef100_vdpa_get_device_features,
> + .set_driver_features = ef100_vdpa_set_driver_features,
> + .get_driver_features = ef100_vdpa_get_driver_features,
> + .set_config_cb = ef100_vdpa_set_config_cb,
> + .get_vq_num_max = ef100_vdpa_get_vq_num_max,
> + .get_device_id = ef100_vdpa_get_device_id,
> + .get_vendor_id = ef100_vdpa_get_vendor_id,
> + .get_config_size = ef100_vdpa_get_config_size,
> + .get_config = ef100_vdpa_get_config,
> + .set_config = ef100_vdpa_set_config,
> + .get_generation = NULL,
> .free = ef100_vdpa_free,
> };
> --
> 2.30.1
>
On 12/14/22 12:13, Jason Wang wrote:
> Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
>
>
> On Wed, Dec 7, 2022 at 10:56 PM Gautam Dawar <[email protected]> wrote:
>> To allow vDPA device creation and deletion, add a vDPA management
>> device per function. Currently, the vDPA devices can be created
>> only on a VF. Also, for now only network class of vDPA devices
>> are supported.
>>
>> Signed-off-by: Gautam Dawar <[email protected]>
>> ---
>> drivers/net/ethernet/sfc/Makefile | 2 +-
>> drivers/net/ethernet/sfc/ef10.c | 2 +-
>> drivers/net/ethernet/sfc/ef100_nic.c | 24 ++-
>> drivers/net/ethernet/sfc/ef100_nic.h | 11 +
>> drivers/net/ethernet/sfc/ef100_vdpa.c | 232 ++++++++++++++++++++++
>> drivers/net/ethernet/sfc/ef100_vdpa.h | 84 ++++++++
>> drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 28 +++
>> drivers/net/ethernet/sfc/mcdi_functions.c | 9 +-
>> drivers/net/ethernet/sfc/mcdi_functions.h | 3 +-
>> drivers/net/ethernet/sfc/net_driver.h | 6 +
>> 10 files changed, 394 insertions(+), 7 deletions(-)
>> create mode 100644 drivers/net/ethernet/sfc/ef100_vdpa_ops.c
>>
>> diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile
>> index 84c9f0590368..a10eac91ab23 100644
>> --- a/drivers/net/ethernet/sfc/Makefile
>> +++ b/drivers/net/ethernet/sfc/Makefile
>> @@ -11,7 +11,7 @@ sfc-$(CONFIG_SFC_MTD) += mtd.o
>> sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \
>> mae.o tc.o tc_bindings.o tc_counters.o
>>
>> -sfc-$(CONFIG_SFC_VDPA) += mcdi_vdpa.o ef100_vdpa.o
>> +sfc-$(CONFIG_SFC_VDPA) += mcdi_vdpa.o ef100_vdpa.o ef100_vdpa_ops.o
>> obj-$(CONFIG_SFC) += sfc.o
>>
>> obj-$(CONFIG_SFC_FALCON) += falcon/
>> diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
>> index 7022fb2005a2..366ecd3c80b1 100644
>> --- a/drivers/net/ethernet/sfc/ef10.c
>> +++ b/drivers/net/ethernet/sfc/ef10.c
>> @@ -589,7 +589,7 @@ static int efx_ef10_probe(struct efx_nic *efx)
>> if (rc)
>> goto fail4;
>>
>> - rc = efx_get_pf_index(efx, &nic_data->pf_index);
>> + rc = efx_get_fn_info(efx, &nic_data->pf_index, NULL);
>> if (rc)
>> goto fail5;
>>
>> diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c
>> index 41175eb00326..41811c519275 100644
>> --- a/drivers/net/ethernet/sfc/ef100_nic.c
>> +++ b/drivers/net/ethernet/sfc/ef100_nic.c
>> @@ -1160,7 +1160,7 @@ static int ef100_probe_main(struct efx_nic *efx)
>> if (rc)
>> goto fail;
>>
>> - rc = efx_get_pf_index(efx, &nic_data->pf_index);
>> + rc = efx_get_fn_info(efx, &nic_data->pf_index, &nic_data->vf_index);
>> if (rc)
>> goto fail;
>>
>> @@ -1247,13 +1247,33 @@ int ef100_probe_netdev_pf(struct efx_nic *efx)
>>
>> int ef100_probe_vf(struct efx_nic *efx)
>> {
>> - return ef100_probe_main(efx);
>> +#if defined(CONFIG_SFC_VDPA)
>> + int err;
>> +#endif
>> + int rc;
>> +
>> + rc = ef100_probe_main(efx);
>> + if (rc)
>> + return rc;
>> +
>> +#if defined(CONFIG_SFC_VDPA)
>> + err = ef100_vdpa_register_mgmtdev(efx);
>> + if (err)
>> + pci_warn(efx->pci_dev,
>> + "vdpa_register_mgmtdev failed, err: %d\n", err);
>> +#endif
>> + return 0;
>> }
>>
>> void ef100_remove(struct efx_nic *efx)
>> {
>> struct ef100_nic_data *nic_data = efx->nic_data;
>>
>> +#if defined(CONFIG_SFC_VDPA)
>> + if (efx_vdpa_supported(efx))
>> + ef100_vdpa_unregister_mgmtdev(efx);
>> +#endif
>> +
>> efx_mcdi_detach(efx);
>> efx_mcdi_fini(efx);
>> if (nic_data) {
>> diff --git a/drivers/net/ethernet/sfc/ef100_nic.h b/drivers/net/ethernet/sfc/ef100_nic.h
>> index 5ed693fbe79f..730c8bb932b0 100644
>> --- a/drivers/net/ethernet/sfc/ef100_nic.h
>> +++ b/drivers/net/ethernet/sfc/ef100_nic.h
>> @@ -68,6 +68,13 @@ enum ef100_bar_config {
>> EF100_BAR_CONFIG_VDPA,
>> };
>>
>> +#ifdef CONFIG_SFC_VDPA
>> +enum ef100_vdpa_class {
>> + EF100_VDPA_CLASS_NONE,
>> + EF100_VDPA_CLASS_NET,
>> +};
>> +#endif
>> +
>> struct ef100_nic_data {
>> struct efx_nic *efx;
>> struct efx_buffer mcdi_buf;
>> @@ -75,7 +82,11 @@ struct ef100_nic_data {
>> u32 datapath_caps2;
>> u32 datapath_caps3;
>> unsigned int pf_index;
>> + unsigned int vf_index;
>> u16 warm_boot_count;
>> +#ifdef CONFIG_SFC_VDPA
>> + enum ef100_vdpa_class vdpa_class;
>> +#endif
>> u8 port_id[ETH_ALEN];
>> DECLARE_BITMAP(evq_phases, EFX_MAX_CHANNELS);
>> enum ef100_bar_config bar_config;
>> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.c b/drivers/net/ethernet/sfc/ef100_vdpa.c
>> index 5e215cee585a..ff4bb61e598e 100644
>> --- a/drivers/net/ethernet/sfc/ef100_vdpa.c
>> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.c
>> @@ -11,11 +11,17 @@
>> #include <linux/err.h>
>> #include <linux/vdpa.h>
>> #include <linux/virtio_net.h>
>> +#include <uapi/linux/vdpa.h>
>> #include "ef100_vdpa.h"
>> #include "mcdi_vdpa.h"
>> #include "mcdi_filters.h"
>> #include "ef100_netdev.h"
>>
>> +static struct virtio_device_id ef100_vdpa_id_table[] = {
>> + { .device = VIRTIO_ID_NET, .vendor = PCI_VENDOR_ID_REDHAT_QUMRANET },
>> + { 0 },
>> +};
>> +
>> int ef100_vdpa_init(struct efx_probe_data *probe_data)
>> {
>> struct efx_nic *efx = &probe_data->efx;
>> @@ -42,17 +48,243 @@ int ef100_vdpa_init(struct efx_probe_data *probe_data)
>> return rc;
>> }
>>
>> +static void ef100_vdpa_delete(struct efx_nic *efx)
>> +{
>> + if (efx->vdpa_nic) {
>> + /* replace with _vdpa_unregister_device later */
>> + put_device(&efx->vdpa_nic->vdpa_dev.dev);
>> + efx->vdpa_nic = NULL;
>> + }
>> +}
>> +
>> void ef100_vdpa_fini(struct efx_probe_data *probe_data)
>> {
>> struct efx_nic *efx = &probe_data->efx;
>> + struct ef100_nic_data *nic_data;
>>
>> if (efx->state != STATE_VDPA && efx->state != STATE_DISABLED) {
>> pci_err(efx->pci_dev, "Invalid efx state %u", efx->state);
>> return;
>> }
>>
>> + /* Handle vdpa device deletion, if not done explicitly */
>> + ef100_vdpa_delete(efx);
>> + nic_data = efx->nic_data;
>> + nic_data->vdpa_class = EF100_VDPA_CLASS_NONE;
>> efx->state = STATE_PROBED;
>> down_write(&efx->filter_sem);
>> efx_mcdi_filter_table_remove(efx);
>> up_write(&efx->filter_sem);
>> }
>> +
>> +static int get_net_config(struct ef100_vdpa_nic *vdpa_nic)
>> +{
>> + struct efx_nic *efx = vdpa_nic->efx;
>> + u16 mtu;
>> + int rc;
>> +
>> + vdpa_nic->net_config.max_virtqueue_pairs =
>> + cpu_to_efx_vdpa16(vdpa_nic, vdpa_nic->max_queue_pairs);
>> +
>> + rc = efx_vdpa_get_mtu(efx, &mtu);
>> + if (rc) {
>> + dev_err(&vdpa_nic->vdpa_dev.dev,
>> + "%s: Get MTU for vf:%u failed:%d\n", __func__,
>> + vdpa_nic->vf_index, rc);
>> + return rc;
>> + }
>> + vdpa_nic->net_config.mtu = cpu_to_efx_vdpa16(vdpa_nic, mtu);
>> + vdpa_nic->net_config.status = cpu_to_efx_vdpa16(vdpa_nic,
>> + VIRTIO_NET_S_LINK_UP);
>> + return 0;
>> +}
>> +
>> +static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
>> + const char *dev_name,
>> + enum ef100_vdpa_class dev_type,
>> + const u8 *mac)
>> +{
>> + struct ef100_nic_data *nic_data = efx->nic_data;
>> + struct ef100_vdpa_nic *vdpa_nic;
>> + struct device *dev;
>> + int rc;
>> +
>> + nic_data->vdpa_class = dev_type;
>> + vdpa_nic = vdpa_alloc_device(struct ef100_vdpa_nic,
>> + vdpa_dev, &efx->pci_dev->dev,
>> + &ef100_vdpa_config_ops,
>> + 1, 1,
>> + dev_name, false);
>> + if (!vdpa_nic) {
>> + pci_err(efx->pci_dev,
>> + "vDPA device allocation failed for vf: %u\n",
>> + nic_data->vf_index);
>> + nic_data->vdpa_class = EF100_VDPA_CLASS_NONE;
>> + return ERR_PTR(-ENOMEM);
>> + }
>> +
>> + mutex_init(&vdpa_nic->lock);
>> + dev = &vdpa_nic->vdpa_dev.dev;
>> + efx->vdpa_nic = vdpa_nic;
>> + vdpa_nic->vdpa_dev.dma_dev = &efx->pci_dev->dev;
>> + vdpa_nic->vdpa_dev.mdev = efx->mgmt_dev;
>> + vdpa_nic->efx = efx;
>> + vdpa_nic->pf_index = nic_data->pf_index;
>> + vdpa_nic->vf_index = nic_data->vf_index;
>> + vdpa_nic->vdpa_state = EF100_VDPA_STATE_INITIALIZED;
>> + vdpa_nic->mac_address = (u8 *)&vdpa_nic->net_config.mac;
>> + ether_addr_copy(vdpa_nic->mac_address, mac);
>> + vdpa_nic->mac_configured = true;
>> +
>> + rc = get_net_config(vdpa_nic);
>> + if (rc)
>> + goto err_put_device;
>> +
>> + /* _vdpa_register_device when its ready */
>> +
>> + return vdpa_nic;
>> +
>> +err_put_device:
>> + /* put_device invokes ef100_vdpa_free */
>> + put_device(&vdpa_nic->vdpa_dev.dev);
>> + return ERR_PTR(rc);
>> +}
>> +
>> +static void ef100_vdpa_net_dev_del(struct vdpa_mgmt_dev *mgmt_dev,
>> + struct vdpa_device *vdev)
>> +{
>> + struct ef100_nic_data *nic_data;
>> + struct efx_nic *efx;
>> + int rc;
>> +
>> + efx = pci_get_drvdata(to_pci_dev(mgmt_dev->device));
>> + nic_data = efx->nic_data;
>> +
>> + rc = efx_ef100_set_bar_config(efx, EF100_BAR_CONFIG_EF100);
>> + if (rc)
>> + pci_err(efx->pci_dev,
>> + "set_bar_config EF100 failed, err: %d\n", rc);
>> + else
>> + pci_dbg(efx->pci_dev,
>> + "vdpa net device deleted, vf: %u\n",
>> + nic_data->vf_index);
>> +}
>> +
>> +static int ef100_vdpa_net_dev_add(struct vdpa_mgmt_dev *mgmt_dev,
>> + const char *name,
>> + const struct vdpa_dev_set_config *config)
>> +{
>> + struct ef100_vdpa_nic *vdpa_nic;
>> + struct ef100_nic_data *nic_data;
>> + struct efx_nic *efx;
>> + int rc, err;
>> +
>> + efx = pci_get_drvdata(to_pci_dev(mgmt_dev->device));
>> + if (efx->vdpa_nic) {
>> + pci_warn(efx->pci_dev,
>> + "vDPA device already exists on this VF\n");
>> + return -EEXIST;
>> + }
>> +
>> + if (config->mask & BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR)) {
>> + if (!is_valid_ether_addr(config->net.mac)) {
>> + pci_err(efx->pci_dev, "Invalid MAC address %pM\n",
>> + config->net.mac);
>> + return -EINVAL;
>> + }
>> + } else {
>> + pci_err(efx->pci_dev, "MAC address parameter missing\n");
> Does this mean ef100 vf doesn't have a given mac address?
We are working on adding support to configure VF MAC addresses using
devlink port and once that is available, we can get rid of this
restriction of specifying the MAC address at vdpa device creation time.
>
>
>
>> + return -EIO;
>> + }
>> +
>> + nic_data = efx->nic_data;
>> +
>> + rc = efx_ef100_set_bar_config(efx, EF100_BAR_CONFIG_VDPA);
>> + if (rc) {
>> + pci_err(efx->pci_dev,
>> + "set_bar_config vDPA failed, err: %d\n", rc);
>> + goto err_set_bar_config;
>> + }
>> +
>> + vdpa_nic = ef100_vdpa_create(efx, name, EF100_VDPA_CLASS_NET,
>> + (const u8 *)config->net.mac);
>> + if (IS_ERR(vdpa_nic)) {
>> + pci_err(efx->pci_dev,
>> + "vDPA device creation failed, vf: %u, err: %ld\n",
>> + nic_data->vf_index, PTR_ERR(vdpa_nic));
>> + rc = PTR_ERR(vdpa_nic);
>> + goto err_set_bar_config;
>> + } else {
>> + pci_dbg(efx->pci_dev,
>> + "vdpa net device created, vf: %u\n",
>> + nic_data->vf_index);
>> + pci_warn(efx->pci_dev,
>> + "Use QEMU versions 6.1.0 and later with vhost-vdpa\n");
>> + }
>> +
>> + return 0;
>> +
>> +err_set_bar_config:
>> + err = efx_ef100_set_bar_config(efx, EF100_BAR_CONFIG_EF100);
>> + if (err)
>> + pci_err(efx->pci_dev,
>> + "set_bar_config EF100 failed, err: %d\n", err);
>> +
>> + return rc;
>> +}
>> +
>> +static const struct vdpa_mgmtdev_ops ef100_vdpa_net_mgmtdev_ops = {
>> + .dev_add = ef100_vdpa_net_dev_add,
>> + .dev_del = ef100_vdpa_net_dev_del
>> +};
>> +
>> +int ef100_vdpa_register_mgmtdev(struct efx_nic *efx)
>> +{
>> + struct vdpa_mgmt_dev *mgmt_dev;
>> + u64 features;
>> + int rc;
>> +
>> + mgmt_dev = kzalloc(sizeof(*mgmt_dev), GFP_KERNEL);
>> + if (!mgmt_dev)
>> + return -ENOMEM;
>> +
>> + rc = efx_vdpa_get_features(efx, EF100_VDPA_DEVICE_TYPE_NET, &features);
>> + if (rc) {
>> + pci_err(efx->pci_dev, "%s: MCDI get features error:%d\n",
>> + __func__, rc);
>> + goto err_get_features;
>> + }
>> +
>> + efx->mgmt_dev = mgmt_dev;
>> + mgmt_dev->device = &efx->pci_dev->dev;
>> + mgmt_dev->id_table = ef100_vdpa_id_table;
>> + mgmt_dev->ops = &ef100_vdpa_net_mgmtdev_ops;
>> + mgmt_dev->supported_features = features;
>> + mgmt_dev->max_supported_vqs = EF100_VDPA_MAX_QUEUES_PAIRS * 2;
>> + mgmt_dev->config_attr_mask = BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR);
>> +
>> + rc = vdpa_mgmtdev_register(mgmt_dev);
>> + if (rc) {
>> + pci_err(efx->pci_dev,
>> + "vdpa_mgmtdev_register failed, err: %d\n", rc);
>> + goto err_mgmtdev_register;
>> + }
>> +
>> + return 0;
>> +
>> +err_mgmtdev_register:
>> +err_get_features:
>> + kfree(mgmt_dev);
>> + efx->mgmt_dev = NULL;
>> +
>> + return rc;
>> +}
>> +
>> +void ef100_vdpa_unregister_mgmtdev(struct efx_nic *efx)
>> +{
>> + if (efx->mgmt_dev) {
>> + vdpa_mgmtdev_unregister(efx->mgmt_dev);
>> + kfree(efx->mgmt_dev);
>> + efx->mgmt_dev = NULL;
>> + }
>> +}
>> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
>> index 6b51a05becd8..83f6d819f6a5 100644
>> --- a/drivers/net/ethernet/sfc/ef100_vdpa.h
>> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
>> @@ -18,6 +18,24 @@
>>
>> #if defined(CONFIG_SFC_VDPA)
>>
>> +/* Max queue pairs currently supported */
>> +#define EF100_VDPA_MAX_QUEUES_PAIRS 1
>> +
>> +/**
>> + * enum ef100_vdpa_nic_state - possible states for a vDPA NIC
>> + *
>> + * @EF100_VDPA_STATE_INITIALIZED: State after vDPA NIC created
>> + * @EF100_VDPA_STATE_NEGOTIATED: State after feature negotiation
>> + * @EF100_VDPA_STATE_STARTED: State after driver ok
>> + * @EF100_VDPA_STATE_NSTATES: Number of VDPA states
>> + */
>> +enum ef100_vdpa_nic_state {
>> + EF100_VDPA_STATE_INITIALIZED,
>> + EF100_VDPA_STATE_NEGOTIATED,
>> + EF100_VDPA_STATE_STARTED,
>> + EF100_VDPA_STATE_NSTATES
>> +};
>> +
>> enum ef100_vdpa_device_type {
>> EF100_VDPA_DEVICE_TYPE_NET,
>> };
>> @@ -28,7 +46,73 @@ enum ef100_vdpa_vq_type {
>> EF100_VDPA_VQ_NTYPES
>> };
>>
>> +/**
>> + * struct ef100_vdpa_nic - vDPA NIC data structure
>> + *
>> + * @vdpa_dev: vdpa_device object which registers on the vDPA bus.
>> + * @vdpa_state: NIC state machine governed by ef100_vdpa_nic_state
>> + * @efx: pointer to the VF's efx_nic object
>> + * @lock: Managing access to vdpa config operations
>> + * @pf_index: PF index of the vDPA VF
>> + * @vf_index: VF index of the vDPA VF
>> + * @status: device status as per VIRTIO spec
>> + * @features: negotiated feature bits
>> + * @max_queue_pairs: maximum number of queue pairs supported
>> + * @net_config: virtio_net_config data
>> + * @mac_address: mac address of interface associated with this vdpa device
>> + * @mac_configured: true after MAC address is configured
>> + */
>> +struct ef100_vdpa_nic {
>> + struct vdpa_device vdpa_dev;
>> + enum ef100_vdpa_nic_state vdpa_state;
>> + struct efx_nic *efx;
>> + /* for synchronizing access to vdpa config operations */
>> + struct mutex lock;
>> + u32 pf_index;
>> + u32 vf_index;
>> + u8 status;
>> + u64 features;
>> + u32 max_queue_pairs;
>> + struct virtio_net_config net_config;
>> + u8 *mac_address;
>> + bool mac_configured;
>> +};
>> +
>> int ef100_vdpa_init(struct efx_probe_data *probe_data);
>> void ef100_vdpa_fini(struct efx_probe_data *probe_data);
>> +int ef100_vdpa_register_mgmtdev(struct efx_nic *efx);
>> +void ef100_vdpa_unregister_mgmtdev(struct efx_nic *efx);
>> +
>> +static inline bool efx_vdpa_is_little_endian(struct ef100_vdpa_nic *vdpa_nic)
>> +{
>> + return virtio_legacy_is_little_endian() ||
>> + (vdpa_nic->features & (1ULL << VIRTIO_F_VERSION_1));
>> +}
>> +
>> +static inline u16 efx_vdpa16_to_cpu(struct ef100_vdpa_nic *vdpa_nic,
>> + __virtio16 val)
>> +{
>> + return __virtio16_to_cpu(efx_vdpa_is_little_endian(vdpa_nic), val);
>> +}
>> +
>> +static inline __virtio16 cpu_to_efx_vdpa16(struct ef100_vdpa_nic *vdpa_nic,
>> + u16 val)
>> +{
>> + return __cpu_to_virtio16(efx_vdpa_is_little_endian(vdpa_nic), val);
>> +}
>> +
>> +static inline u32 efx_vdpa32_to_cpu(struct ef100_vdpa_nic *vdpa_nic,
>> + __virtio32 val)
>> +{
>> + return __virtio32_to_cpu(efx_vdpa_is_little_endian(vdpa_nic), val);
>> +}
>> +
>> +static inline __virtio32 cpu_to_efx_vdpa32(struct ef100_vdpa_nic *vdpa_nic,
>> + u32 val)
>> +{
>> + return __cpu_to_virtio32(efx_vdpa_is_little_endian(vdpa_nic), val);
>> +}
>> +
>> +extern const struct vdpa_config_ops ef100_vdpa_config_ops;
>> #endif /* CONFIG_SFC_VDPA */
>> #endif /* __EF100_VDPA_H__ */
>> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
>> new file mode 100644
>> index 000000000000..31952931c198
>> --- /dev/null
>> +++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
>> @@ -0,0 +1,28 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/* Driver for Xilinx network controllers and boards
>> + * Copyright(C) 2020-2022 Xilinx, Inc.
>> + * Copyright(C) 2022 Advanced Micro Devices, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published
>> + * by the Free Software Foundation, incorporated herein by reference.
>> + */
>> +
>> +#include <linux/vdpa.h>
>> +#include "ef100_vdpa.h"
>> +
>> +static struct ef100_vdpa_nic *get_vdpa_nic(struct vdpa_device *vdev)
>> +{
>> + return container_of(vdev, struct ef100_vdpa_nic, vdpa_dev);
>> +}
>> +
>> +static void ef100_vdpa_free(struct vdpa_device *vdev)
>> +{
>> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
>> +
>> + mutex_destroy(&vdpa_nic->lock);
>> +}
>> +
>> +const struct vdpa_config_ops ef100_vdpa_config_ops = {
>> + .free = ef100_vdpa_free,
>> +};
>> diff --git a/drivers/net/ethernet/sfc/mcdi_functions.c b/drivers/net/ethernet/sfc/mcdi_functions.c
>> index d3e6d8239f5c..4415f19cf68f 100644
>> --- a/drivers/net/ethernet/sfc/mcdi_functions.c
>> +++ b/drivers/net/ethernet/sfc/mcdi_functions.c
>> @@ -413,7 +413,8 @@ int efx_mcdi_window_mode_to_stride(struct efx_nic *efx, u8 vi_window_mode)
>> return 0;
>> }
>>
>> -int efx_get_pf_index(struct efx_nic *efx, unsigned int *pf_index)
>> +int efx_get_fn_info(struct efx_nic *efx, unsigned int *pf_index,
>> + unsigned int *vf_index)
>> {
>> MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_FUNCTION_INFO_OUT_LEN);
>> size_t outlen;
>> @@ -426,6 +427,10 @@ int efx_get_pf_index(struct efx_nic *efx, unsigned int *pf_index)
>> if (outlen < sizeof(outbuf))
>> return -EIO;
>>
>> - *pf_index = MCDI_DWORD(outbuf, GET_FUNCTION_INFO_OUT_PF);
>> + if (pf_index)
>> + *pf_index = MCDI_DWORD(outbuf, GET_FUNCTION_INFO_OUT_PF);
>> +
>> + if (efx->type->is_vf && vf_index)
>> + *vf_index = MCDI_DWORD(outbuf, GET_FUNCTION_INFO_OUT_VF);
>> return 0;
>> }
>> diff --git a/drivers/net/ethernet/sfc/mcdi_functions.h b/drivers/net/ethernet/sfc/mcdi_functions.h
>> index b0e2f53a0d9b..76dc0a13463e 100644
>> --- a/drivers/net/ethernet/sfc/mcdi_functions.h
>> +++ b/drivers/net/ethernet/sfc/mcdi_functions.h
>> @@ -28,6 +28,7 @@ void efx_mcdi_rx_remove(struct efx_rx_queue *rx_queue);
>> void efx_mcdi_rx_fini(struct efx_rx_queue *rx_queue);
>> int efx_fini_dmaq(struct efx_nic *efx);
>> int efx_mcdi_window_mode_to_stride(struct efx_nic *efx, u8 vi_window_mode);
>> -int efx_get_pf_index(struct efx_nic *efx, unsigned int *pf_index);
>> +int efx_get_fn_info(struct efx_nic *efx, unsigned int *pf_index,
>> + unsigned int *vf_index);
>>
>> #endif
>> diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
>> index ffda80a95221..79356d614109 100644
>> --- a/drivers/net/ethernet/sfc/net_driver.h
>> +++ b/drivers/net/ethernet/sfc/net_driver.h
>> @@ -1182,6 +1182,12 @@ struct efx_nic {
>>
>> unsigned int mem_bar;
>> u32 reg_base;
>> +#ifdef CONFIG_SFC_VDPA
>> + /** @mgmt_dev: vDPA Management device */
>> + struct vdpa_mgmt_dev *mgmt_dev;
>> + /** @vdpa_nic: vDPA device structure (EF100) */
>> + struct ef100_vdpa_nic *vdpa_nic;
>> +#endif
>>
>> /* The following fields may be written more often */
>>
>> --
>> 2.30.1
>>
On 12/14/22 12:14, Jason Wang wrote:
> Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
>
>
> On Wed, Dec 7, 2022 at 10:56 PM Gautam Dawar <[email protected]> wrote:
>> vDPA config operations can be broadly categorized in to either
>> virtqueue operations, device operations or DMA operations.
>> This patch implements most of the device level config operations.
>>
>> SN1022 supports VIRTIO_F_IN_ORDER which is supported by the DPDK
>> virtio driver but not the kernel virtio driver. Due to a bug in
>> QEMU (https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgitlab.com%2Fqemu-project%2Fqemu%2F-%2Fissues%2F331%23&data=05%7C01%7Cgautam.dawar%40amd.com%7C58787eb502484eeaa6f508dadd9ea016%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C638065970623127805%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=cVomPr2aFJBHW7kwzeZJrNHuU6oTYOTV14eOS%2BpeNVc%3D&reserved=0), with
>> vhost-vdpa, this feature bit is returned with guest kernel virtio
>> driver in set_features config operation. The fix for this bug
>> (qemu_commit c33f23a419f95da16ab4faaf08be635c89b96ff0) is available
>> in QEMU versions 6.1.0 and later. Hence, that's the oldest QEMU
>> version required for testing with the vhost-vdpa driver.
>>
>> With older QEMU releases, VIRTIO_F_IN_ORDER is negotiated but
>> not honored causing Firmware exception.
>>
>> Signed-off-by: Gautam Dawar <[email protected]>
>> ---
>> drivers/net/ethernet/sfc/ef100_vdpa.h | 14 ++
>> drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 148 ++++++++++++++++++++++
>> 2 files changed, 162 insertions(+)
>>
>> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
>> index 83f6d819f6a5..be7650c3166a 100644
>> --- a/drivers/net/ethernet/sfc/ef100_vdpa.h
>> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
>> @@ -21,6 +21,18 @@
>> /* Max queue pairs currently supported */
>> #define EF100_VDPA_MAX_QUEUES_PAIRS 1
>>
>> +/* Device ID of a virtio net device */
>> +#define EF100_VDPA_VIRTIO_NET_DEVICE_ID VIRTIO_ID_NET
>> +
>> +/* Vendor ID of Xilinx vDPA NIC */
>> +#define EF100_VDPA_VENDOR_ID PCI_VENDOR_ID_XILINX
>> +
>> +/* Max number of Buffers supported in the virtqueue */
>> +#define EF100_VDPA_VQ_NUM_MAX_SIZE 512
>> +
>> +/* Alignment requirement of the Virtqueue */
>> +#define EF100_VDPA_VQ_ALIGN 4096
>> +
>> /**
>> * enum ef100_vdpa_nic_state - possible states for a vDPA NIC
>> *
>> @@ -61,6 +73,7 @@ enum ef100_vdpa_vq_type {
>> * @net_config: virtio_net_config data
>> * @mac_address: mac address of interface associated with this vdpa device
>> * @mac_configured: true after MAC address is configured
>> + * @cfg_cb: callback for config change
>> */
>> struct ef100_vdpa_nic {
>> struct vdpa_device vdpa_dev;
>> @@ -76,6 +89,7 @@ struct ef100_vdpa_nic {
>> struct virtio_net_config net_config;
>> u8 *mac_address;
>> bool mac_configured;
>> + struct vdpa_callback cfg_cb;
>> };
>>
>> int ef100_vdpa_init(struct efx_probe_data *probe_data);
>> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
>> index 31952931c198..87899baa1c52 100644
>> --- a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
>> +++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
>> @@ -10,12 +10,148 @@
>>
>> #include <linux/vdpa.h>
>> #include "ef100_vdpa.h"
>> +#include "mcdi_vdpa.h"
>>
>> static struct ef100_vdpa_nic *get_vdpa_nic(struct vdpa_device *vdev)
>> {
>> return container_of(vdev, struct ef100_vdpa_nic, vdpa_dev);
>> }
>>
>> +static u32 ef100_vdpa_get_vq_align(struct vdpa_device *vdev)
>> +{
>> + return EF100_VDPA_VQ_ALIGN;
>> +}
>> +
>> +static u64 ef100_vdpa_get_device_features(struct vdpa_device *vdev)
>> +{
>> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
>> + u64 features;
>> + int rc;
>> +
>> + rc = efx_vdpa_get_features(vdpa_nic->efx,
>> + EF100_VDPA_DEVICE_TYPE_NET, &features);
>> + if (rc) {
>> + dev_err(&vdev->dev, "%s: MCDI get features error:%d\n",
>> + __func__, rc);
>> + /* Returning 0 as value of features will lead to failure
>> + * of feature negotiation.
>> + */
>> + return 0;
>> + }
>> +
>> + /* SN1022 supports VIRTIO_F_IN_ORDER which is supported by the DPDK
>> + * virtio driver but not the kernel virtio driver. Due to a bug in
>> + * QEMU (https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgitlab.com%2Fqemu-project%2Fqemu%2F-%2Fissues%2F331%23&data=05%7C01%7Cgautam.dawar%40amd.com%7C58787eb502484eeaa6f508dadd9ea016%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C638065970623127805%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=cVomPr2aFJBHW7kwzeZJrNHuU6oTYOTV14eOS%2BpeNVc%3D&reserved=0), with
>> + * vhost-vdpa, this feature bit is returned with guest kernel virtio
>> + * driver in set_features config operation. The fix for this bug
>> + * (commit c33f23a419f95da16ab4faaf08be635c89b96ff0) is available
>> + * in QEMU versions 6.1.0 and later. Hence, that's the oldest QEMU
>> + * version required for testing with the vhost-vdpa driver.
>> + */
> I don't see why this comment is placed here?
As the comment was related to VIRTIO_F_IN_ORDER, I thought of adding it
in config operation related to virtio features handling. But I think
since it is already added in the commit description, this code comment
can be removed.
>
>> + features |= BIT_ULL(VIRTIO_NET_F_MAC);
>> +
>> + return features;
>> +}
>> +
>> +static int ef100_vdpa_set_driver_features(struct vdpa_device *vdev,
>> + u64 features)
>> +{
>> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
>> + u64 verify_features;
>> + int rc;
>> +
>> + mutex_lock(&vdpa_nic->lock);
>> + if (vdpa_nic->vdpa_state != EF100_VDPA_STATE_INITIALIZED) {
> Under which case could we reach this condition? The
> vdpa_device_register() should be called after switching the state to
> EF100_VDPA_STATE_INITIALIZED.
You're right. I'll remove this check as state is set to
EF100_VDPA_STATE_INITIALIZED soon after vdpa_alloc_device but before
_vdpa_register_device()
>
>> + dev_err(&vdev->dev, "%s: Invalid state %u\n",
>> + __func__, vdpa_nic->vdpa_state);
>> + rc = -EINVAL;
>> + goto err;
>> + }
>> + verify_features = features & ~BIT_ULL(VIRTIO_NET_F_MAC);
>> + rc = efx_vdpa_verify_features(vdpa_nic->efx,
>> + EF100_VDPA_DEVICE_TYPE_NET,
>> + verify_features);
> It looks to me this will use MC_CMD_VIRTIO_TEST_FEATURES command, I
> wonder if it's better to use
>
> MC_CMD_VIRTIO_SET_FEATURES to align with the virtio spec and maybe
> change efx_vdpa_verify_features to efx_vdpa_set_features()?
Yes, it makes more sense to match the function names with the operations
in virtio spec. However, MC_CMD_VIRTIO_TEST_FEATURES queries if the
passed set of features is supported and it fails in case either driver
requests an unsupported feature or misses a feature that device requires.
Hence, it's more of a verification of feature set than applying/setting
them.
>> +
>> + if (rc) {
>> + dev_err(&vdev->dev, "%s: MCDI verify features error:%d\n",
>> + __func__, rc);
>> + goto err;
>> + }
>> +
>> + vdpa_nic->features = features;
>> +err:
>> + mutex_unlock(&vdpa_nic->lock);
>> + return rc;
>> +}
>> +
>> +static u64 ef100_vdpa_get_driver_features(struct vdpa_device *vdev)
>> +{
>> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
>> +
>> + return vdpa_nic->features;
>> +}
>> +
>> +static void ef100_vdpa_set_config_cb(struct vdpa_device *vdev,
>> + struct vdpa_callback *cb)
>> +{
>> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
>> +
>> + if (cb)
>> + vdpa_nic->cfg_cb = *cb;
>> +}
>> +
>> +static u16 ef100_vdpa_get_vq_num_max(struct vdpa_device *vdev)
>> +{
>> + return EF100_VDPA_VQ_NUM_MAX_SIZE;
>> +}
>> +
>> +static u32 ef100_vdpa_get_device_id(struct vdpa_device *vdev)
>> +{
>> + return EF100_VDPA_VIRTIO_NET_DEVICE_ID;
>> +}
>> +
>> +static u32 ef100_vdpa_get_vendor_id(struct vdpa_device *vdev)
>> +{
>> + return EF100_VDPA_VENDOR_ID;
>> +}
>> +
>> +static size_t ef100_vdpa_get_config_size(struct vdpa_device *vdev)
>> +{
>> + return sizeof(struct virtio_net_config);
>> +}
>> +
>> +static void ef100_vdpa_get_config(struct vdpa_device *vdev,
>> + unsigned int offset,
>> + void *buf, unsigned int len)
>> +{
>> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
>> +
>> + /* Avoid the possibility of wrap-up after the sum exceeds U32_MAX */
>> + if (WARN_ON(((u64)offset + len) > sizeof(struct virtio_net_config))) {
>> + dev_err(&vdev->dev,
>> + "%s: Offset + len exceeds config size\n", __func__);
>> + return;
> I wonder if we need similar checks in the vdpa core.
Yes, it would be better to have this validation at one place (vdpa
framework) instead of individual vendor drivers. Although I am not sure
if the framework can choose the correct config size based on the device
class.
>
>> + }
>> + memcpy(buf, (u8 *)&vdpa_nic->net_config + offset, len);
>> +}
>> +
>> +static void ef100_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
>> + const void *buf, unsigned int len)
>> +{
>> + struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
>> +
>> + /* Avoid the possibility of wrap-up after the sum exceeds U32_MAX */
>> + if (WARN_ON(((u64)offset + len) > sizeof(vdpa_nic->net_config))) {
>> + dev_err(&vdev->dev,
>> + "%s: Offset + len exceeds config size\n", __func__);
>> + return;
>> + }
>> +
>> + memcpy((u8 *)&vdpa_nic->net_config + offset, buf, len);
>> + if (is_valid_ether_addr(vdpa_nic->mac_address))
>> + vdpa_nic->mac_configured = true;
> Do we need to update hardware filters?
Yes, it's done in a later patch where filters support is added (sfc:
implement filters for receiving traffic).
>
> Thanks
>
>> +}
>> +
>> static void ef100_vdpa_free(struct vdpa_device *vdev)
>> {
>> struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
>> @@ -24,5 +160,17 @@ static void ef100_vdpa_free(struct vdpa_device *vdev)
>> }
>>
>> const struct vdpa_config_ops ef100_vdpa_config_ops = {
>> + .get_vq_align = ef100_vdpa_get_vq_align,
>> + .get_device_features = ef100_vdpa_get_device_features,
>> + .set_driver_features = ef100_vdpa_set_driver_features,
>> + .get_driver_features = ef100_vdpa_get_driver_features,
>> + .set_config_cb = ef100_vdpa_set_config_cb,
>> + .get_vq_num_max = ef100_vdpa_get_vq_num_max,
>> + .get_device_id = ef100_vdpa_get_device_id,
>> + .get_vendor_id = ef100_vdpa_get_vendor_id,
>> + .get_config_size = ef100_vdpa_get_config_size,
>> + .get_config = ef100_vdpa_get_config,
>> + .set_config = ef100_vdpa_set_config,
>> + .get_generation = NULL,
>> .free = ef100_vdpa_free,
>> };
>> --
>> 2.30.1
>>
On 12/14/22 12:15, Jason Wang wrote:
> Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
>
>
> On Wed, Dec 7, 2022 at 10:57 PM Gautam Dawar <[email protected]> wrote:
>> Implement unicast, broadcast and unknown multicast
>> filters for receiving different types of traffic.
> I suggest squashing this into the patch that implements set_config().
The patch that implements set_config() also implements device level vdpa
config operations. I think that squashing the filtering support (this
patch) in to that patch will make it unnecessary long and combine lot of
unrelated functionality (except the call to configure filters in
set_config operation).
>
>> Signed-off-by: Gautam Dawar <[email protected]>
>> ---
>> drivers/net/ethernet/sfc/ef100_vdpa.c | 159 ++++++++++++++++++++++
>> drivers/net/ethernet/sfc/ef100_vdpa.h | 35 +++++
>> drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 27 +++-
>> 3 files changed, 220 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.c b/drivers/net/ethernet/sfc/ef100_vdpa.c
>> index 41eb7aef6798..04d64bfe3c93 100644
>> --- a/drivers/net/ethernet/sfc/ef100_vdpa.c
>> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.c
>> @@ -17,12 +17,168 @@
>> #include "mcdi_filters.h"
>> #include "mcdi_functions.h"
>> #include "ef100_netdev.h"
>> +#include "filter.h"
>> +#include "efx.h"
>>
>> +#define EFX_INVALID_FILTER_ID -1
>> +
>> +/* vDPA queues starts from 2nd VI or qid 1 */
>> +#define EF100_VDPA_BASE_RX_QID 1
>> +
>> +static const char * const filter_names[] = { "bcast", "ucast", "mcast" };
>> static struct virtio_device_id ef100_vdpa_id_table[] = {
>> { .device = VIRTIO_ID_NET, .vendor = PCI_VENDOR_ID_REDHAT_QUMRANET },
>> { 0 },
>> };
>>
>> +static int ef100_vdpa_set_mac_filter(struct efx_nic *efx,
>> + struct efx_filter_spec *spec,
>> + u32 qid,
>> + u8 *mac_addr)
>> +{
>> + int rc;
>> +
>> + efx_filter_init_rx(spec, EFX_FILTER_PRI_AUTO, 0, qid);
>> +
>> + if (mac_addr) {
>> + rc = efx_filter_set_eth_local(spec, EFX_FILTER_VID_UNSPEC,
>> + mac_addr);
>> + if (rc)
>> + pci_err(efx->pci_dev,
>> + "Filter set eth local failed, err: %d\n", rc);
>> + } else {
>> + efx_filter_set_mc_def(spec);
>> + }
>> +
>> + rc = efx_filter_insert_filter(efx, spec, true);
>> + if (rc < 0)
>> + pci_err(efx->pci_dev,
>> + "Filter insert failed, err: %d\n", rc);
>> +
>> + return rc;
>> +}
>> +
>> +static int ef100_vdpa_delete_filter(struct ef100_vdpa_nic *vdpa_nic,
>> + enum ef100_vdpa_mac_filter_type type)
>> +{
>> + struct vdpa_device *vdev = &vdpa_nic->vdpa_dev;
>> + int rc;
>> +
>> + if (vdpa_nic->filters[type].filter_id == EFX_INVALID_FILTER_ID)
>> + return rc;
>> +
>> + rc = efx_filter_remove_id_safe(vdpa_nic->efx,
>> + EFX_FILTER_PRI_AUTO,
>> + vdpa_nic->filters[type].filter_id);
>> + if (rc) {
>> + dev_err(&vdev->dev, "%s filter id: %d remove failed, err: %d\n",
>> + filter_names[type], vdpa_nic->filters[type].filter_id,
>> + rc);
>> + } else {
>> + vdpa_nic->filters[type].filter_id = EFX_INVALID_FILTER_ID;
>> + vdpa_nic->filter_cnt--;
>> + }
>> + return rc;
>> +}
>> +
>> +int ef100_vdpa_add_filter(struct ef100_vdpa_nic *vdpa_nic,
>> + enum ef100_vdpa_mac_filter_type type)
>> +{
>> + struct vdpa_device *vdev = &vdpa_nic->vdpa_dev;
>> + struct efx_nic *efx = vdpa_nic->efx;
>> + /* Configure filter on base Rx queue only */
>> + u32 qid = EF100_VDPA_BASE_RX_QID;
>> + struct efx_filter_spec *spec;
>> + u8 baddr[ETH_ALEN];
>> + int rc;
>> +
>> + /* remove existing filter */
>> + rc = ef100_vdpa_delete_filter(vdpa_nic, type);
>> + if (rc < 0) {
>> + dev_err(&vdev->dev, "%s MAC filter deletion failed, err: %d",
>> + filter_names[type], rc);
>> + return rc;
>> + }
>> +
>> + /* Configure MAC Filter */
>> + spec = &vdpa_nic->filters[type].spec;
>> + if (type == EF100_VDPA_BCAST_MAC_FILTER) {
>> + eth_broadcast_addr(baddr);
>> + rc = ef100_vdpa_set_mac_filter(efx, spec, qid, baddr);
>> + } else if (type == EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER) {
>> + rc = ef100_vdpa_set_mac_filter(efx, spec, qid, NULL);
>> + } else {
>> + /* Ensure we have everything required to insert the filter */
>> + if (!vdpa_nic->mac_configured ||
>> + !vdpa_nic->vring[0].vring_created ||
>> + !is_valid_ether_addr(vdpa_nic->mac_address))
>> + return -EINVAL;
>> +
>> + rc = ef100_vdpa_set_mac_filter(efx, spec, qid,
>> + vdpa_nic->mac_address);
>> + }
>> +
>> + if (rc >= 0) {
>> + vdpa_nic->filters[type].filter_id = rc;
>> + vdpa_nic->filter_cnt++;
> I'm not sure I get this, but the code seems doesn't allow the driver
> to set multiple uc filters, so I think filter_cnt should be always 3?
> If yes, I don't see how filter_cnt can help here.
The filter_cnt is supposed to maintain the total number of filters
created on a vdpa device. For now, this count will always be 3 and may
not be of much use but later it will include the count of multiple
multicast and unicast filters.
>
>> +
>> + return 0;
>> + }
>> +
>> + dev_err(&vdev->dev, "%s MAC filter insert failed, err: %d\n",
>> + filter_names[type], rc);
>> +
>> + if (type != EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER) {
>> + ef100_vdpa_filter_remove(vdpa_nic);
>> + return rc;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +int ef100_vdpa_filter_remove(struct ef100_vdpa_nic *vdpa_nic)
>> +{
>> + enum ef100_vdpa_mac_filter_type filter;
>> + int err = 0;
>> + int rc;
>> +
>> + for (filter = EF100_VDPA_BCAST_MAC_FILTER;
>> + filter <= EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER; filter++) {
>> + rc = ef100_vdpa_delete_filter(vdpa_nic, filter);
>> + if (rc < 0)
>> + /* store status of last failed filter remove */
>> + err = rc;
>> + }
>> + return err;
>> +}
>> +
>> +int ef100_vdpa_filter_configure(struct ef100_vdpa_nic *vdpa_nic)
>> +{
>> + struct vdpa_device *vdev = &vdpa_nic->vdpa_dev;
>> + enum ef100_vdpa_mac_filter_type filter;
>> + int rc;
>> +
>> + /* remove existing filters, if any */
>> + rc = ef100_vdpa_filter_remove(vdpa_nic);
>> + if (rc < 0) {
>> + dev_err(&vdev->dev,
>> + "MAC filter deletion failed, err: %d", rc);
>> + goto fail;
>> + }
>> +
>> + for (filter = EF100_VDPA_BCAST_MAC_FILTER;
>> + filter <= EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER; filter++) {
>> + if (filter == EF100_VDPA_UCAST_MAC_FILTER &&
>> + !vdpa_nic->mac_configured)
>> + continue;
>> + rc = ef100_vdpa_add_filter(vdpa_nic, filter);
>> + if (rc < 0)
>> + goto fail;
>> + }
>> +fail:
>> + return rc;
>> +}
>> +
>> int ef100_vdpa_init(struct efx_probe_data *probe_data)
>> {
>> struct efx_nic *efx = &probe_data->efx;
>> @@ -168,6 +324,9 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
>> ether_addr_copy(vdpa_nic->mac_address, mac);
>> vdpa_nic->mac_configured = true;
>>
>> + for (i = 0; i < EF100_VDPA_MAC_FILTER_NTYPES; i++)
>> + vdpa_nic->filters[i].filter_id = EFX_INVALID_FILTER_ID;
>> +
>> for (i = 0; i < (2 * vdpa_nic->max_queue_pairs); i++)
>> vdpa_nic->vring[i].irq = -EINVAL;
>>
>> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
>> index 3cc33daa0431..a33edd6dda12 100644
>> --- a/drivers/net/ethernet/sfc/ef100_vdpa.h
>> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
>> @@ -72,6 +72,22 @@ enum ef100_vdpa_vq_type {
>> EF100_VDPA_VQ_NTYPES
>> };
>>
>> +/**
>> + * enum ef100_vdpa_mac_filter_type - vdpa filter types
>> + *
>> + * @EF100_VDPA_BCAST_MAC_FILTER: Broadcast MAC filter
>> + * @EF100_VDPA_UCAST_MAC_FILTER: Unicast MAC filter
>> + * @EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER: Unknown multicast MAC filter to allow
>> + * IPv6 Neighbor Solicitation Message
>> + * @EF100_VDPA_MAC_FILTER_NTYPES: Number of vDPA filter types
>> + */
>> +enum ef100_vdpa_mac_filter_type {
>> + EF100_VDPA_BCAST_MAC_FILTER,
>> + EF100_VDPA_UCAST_MAC_FILTER,
>> + EF100_VDPA_UNKNOWN_MCAST_MAC_FILTER,
>> + EF100_VDPA_MAC_FILTER_NTYPES,
>> +};
>> +
>> /**
>> * struct ef100_vdpa_vring_info - vDPA vring data structure
>> *
>> @@ -109,6 +125,17 @@ struct ef100_vdpa_vring_info {
>> struct vdpa_callback cb;
>> };
>>
>> +/**
>> + * struct ef100_vdpa_filter - vDPA filter data structure
>> + *
>> + * @filter_id: filter id of this filter
>> + * @efx_filter_spec: hardware filter specs for this vdpa device
>> + */
>> +struct ef100_vdpa_filter {
>> + s32 filter_id;
>> + struct efx_filter_spec spec;
>> +};
>> +
>> /**
>> * struct ef100_vdpa_nic - vDPA NIC data structure
>> *
>> @@ -118,6 +145,7 @@ struct ef100_vdpa_vring_info {
>> * @lock: Managing access to vdpa config operations
>> * @pf_index: PF index of the vDPA VF
>> * @vf_index: VF index of the vDPA VF
>> + * @filter_cnt: total number of filters created on this vdpa device
>> * @status: device status as per VIRTIO spec
>> * @features: negotiated feature bits
>> * @max_queue_pairs: maximum number of queue pairs supported
>> @@ -125,6 +153,7 @@ struct ef100_vdpa_vring_info {
>> * @vring: vring information of the vDPA device.
>> * @mac_address: mac address of interface associated with this vdpa device
>> * @mac_configured: true after MAC address is configured
>> + * @filters: details of all filters created on this vdpa device
>> * @cfg_cb: callback for config change
>> */
>> struct ef100_vdpa_nic {
>> @@ -135,6 +164,7 @@ struct ef100_vdpa_nic {
>> struct mutex lock;
>> u32 pf_index;
>> u32 vf_index;
>> + u32 filter_cnt;
>> u8 status;
>> u64 features;
>> u32 max_queue_pairs;
>> @@ -142,6 +172,7 @@ struct ef100_vdpa_nic {
>> struct ef100_vdpa_vring_info vring[EF100_VDPA_MAX_QUEUES_PAIRS * 2];
>> u8 *mac_address;
>> bool mac_configured;
>> + struct ef100_vdpa_filter filters[EF100_VDPA_MAC_FILTER_NTYPES];
>> struct vdpa_callback cfg_cb;
>> };
>>
>> @@ -149,6 +180,10 @@ int ef100_vdpa_init(struct efx_probe_data *probe_data);
>> void ef100_vdpa_fini(struct efx_probe_data *probe_data);
>> int ef100_vdpa_register_mgmtdev(struct efx_nic *efx);
>> void ef100_vdpa_unregister_mgmtdev(struct efx_nic *efx);
>> +int ef100_vdpa_filter_configure(struct ef100_vdpa_nic *vdpa_nic);
>> +int ef100_vdpa_filter_remove(struct ef100_vdpa_nic *vdpa_nic);
>> +int ef100_vdpa_add_filter(struct ef100_vdpa_nic *vdpa_nic,
>> + enum ef100_vdpa_mac_filter_type type);
>> int ef100_vdpa_irq_vectors_alloc(struct pci_dev *pci_dev, u16 nvqs);
>> void ef100_vdpa_irq_vectors_free(void *data);
>>
>> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
>> index b7efd3e0c901..132ddb4a647b 100644
>> --- a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
>> +++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
>> @@ -135,6 +135,15 @@ static int delete_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
>> if (vdpa_nic->vring[idx].vring_ctx)
>> delete_vring_ctx(vdpa_nic, idx);
>>
>> + if (idx == 0 && vdpa_nic->filter_cnt != 0) {
>> + rc = ef100_vdpa_filter_remove(vdpa_nic);
>> + if (rc < 0) {
>> + dev_err(&vdpa_nic->vdpa_dev.dev,
>> + "%s: vdpa remove filter failed, err:%d\n",
>> + __func__, rc);
>> + }
>> + }
>> +
>> return rc;
>> }
>>
>> @@ -193,8 +202,22 @@ static int create_vring(struct ef100_vdpa_nic *vdpa_nic, u16 idx)
>> vdpa_nic->vring[idx].doorbell_offset_valid = true;
>> }
>>
>> + /* Configure filters on rxq 0 */
>> + if (idx == 0) {
> This seems tricky, can we move this to set_status() when DRIVER_OK is set?
For a virtqueue to be created, qenable must be set to 1 and status
should be set to DRIVER_OK. The current implementation ensures both
conditions before creating the HW queue. This will handle the situation
where DRIVER_OK is set by a bad guest virtio-net driver but queue has
not been enabled.
>
> Thanks
>
>
>> + rc = ef100_vdpa_filter_configure(vdpa_nic);
>> + if (rc < 0) {
>> + dev_err(&vdpa_nic->vdpa_dev.dev,
>> + "%s: vdpa configure filter failed, err:%d\n",
>> + __func__, rc);
>> + goto err_filter_configure;
>> + }
>> + }
>> +
>> return 0;
>>
>> +err_filter_configure:
>> + ef100_vdpa_filter_remove(vdpa_nic);
>> + vdpa_nic->vring[idx].doorbell_offset_valid = false;
>> err_get_doorbell_offset:
>> efx_vdpa_vring_destroy(vdpa_nic->vring[idx].vring_ctx,
>> &vring_dyn_cfg);
>> @@ -578,8 +601,10 @@ static void ef100_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
>> }
>>
>> memcpy((u8 *)&vdpa_nic->net_config + offset, buf, len);
>> - if (is_valid_ether_addr(vdpa_nic->mac_address))
>> + if (is_valid_ether_addr(vdpa_nic->mac_address)) {
>> vdpa_nic->mac_configured = true;
>> + ef100_vdpa_add_filter(vdpa_nic, EF100_VDPA_UCAST_MAC_FILTER);
>> + }
>> }
>>
>> static void ef100_vdpa_free(struct vdpa_device *vdev)
>> --
>> 2.30.1
>>