mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Doug Ledford <doug.ledford@hpe.com>
To: "Slingshot Open Source Team"
	<slingshot-os-team@groups.ext.hpe.com>,
	"Nathan Chancellor" <nathan@kernel.org>,
	"Nicolas Schier" <nsc@kernel.org>,
	"Richard Cochran" <richardcochran@gmail.com>,
	"Andrew Lunn" <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	"Eric Dumazet" <edumazet@kernel.org>,
	"Jakub Kicinski" <kuba@kernel.org>,
	"Paolo Abeni" <pabeni@redhat.com>,
	"Sumit Semwal" <sumit.semwal@linaro.org>,
	"Christian König" <christian.koenig@amd.com>,
	"Guenter Roeck" <linux@roeck-us.net>
Cc: <linux-kernel@vger.kernel.org>, <netdev@vger.kernel.org>,
	<linux-kbuild@vger.kernel.org>,
	Doug Ledford <doug.ledford@hpe.com>
Subject: [PATCH 19/23] net: hpe: ss1: add SR-IOV support
Date: Fri, 30 Oct 2026 13:59:15 -0700	[thread overview]
Message-ID: <20261030-cxi-netdev-submission-v1-19-8c2c391cc283@hpe.com> (raw)
In-Reply-To: <20261030-cxi-netdev-submission-v1-0-8c2c391cc283@hpe.com>

Add SR-IOV support: VF creation, the PF/VF mailbox and its
notifications.

Known open issues, deferred to the V2 SR-IOV update:

- Sashiko H58: the PF/VF mailbox path is not wired up in-tree.
- Sashiko H59: pf_vf_handshake() can abort VF initialization when it
  probes the wrong half of the endpoint pair.

Known limitation, deferred to V2: this patch carries SR-IOV
virtual-function code (VF-to-PF mailbox requests, mailbox opcodes or VF
resource handling) from an older, incomplete version of the Cassini
SR-IOV support. V2 of this series will include the updated SR-IOV
implementation, so issues in the VF paths should be ignored for now.

Signed-off-by: Doug Ledford <doug.ledford@hpe.com>
---
 drivers/net/ethernet/hpe/ss1/cass_sriov.c    | 1274 ++++++++++++++++++++++++++
 drivers/net/ethernet/hpe/ss1/cass_vf.c       |   58 ++
 drivers/net/ethernet/hpe/ss1/cass_vf_notif.c |   51 ++
 3 files changed, 1383 insertions(+)

diff --git a/drivers/net/ethernet/hpe/ss1/cass_sriov.c b/drivers/net/ethernet/hpe/ss1/cass_sriov.c
new file mode 100644
index 000000000000..5c947372ab9c
--- /dev/null
+++ b/drivers/net/ethernet/hpe/ss1/cass_sriov.c
@@ -0,0 +1,1274 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2019, 2024-2026 Hewlett Packard Enterprise Development LP */
+
+/* Cassini SRIOV and VFs handler
+ *
+ * AF_VSOCK sockets are used to pass messages and responses from the VF to the
+ * PF, both when the VF is attached to a guest (using the appropriate hypervisor
+ * vsock transport), and when the VF is attached to the host (using the
+ * vsock_loopback module).
+ *
+ * The VF index of an incoming vsock connection is identified by probing the
+ * PF-to-VF interrupt of each unaccounted-for VF and waiting for a response.
+ * After this initial handshake, communication is always initiated by the VF,
+ * and an acknowledgment from the PF is always expected. Messages are prefixed
+ * with an integer result code (only used by the response from the PF) and
+ * message length.
+ */
+
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/kmod.h>
+#include <linux/kthread.h>
+#include <linux/kvm_host.h>
+#include <linux/net.h>
+#include <linux/vfio.h>
+#include <linux/vm_sockets.h>
+#include <net/sock.h>
+
+#include "cass_core.h"
+#include "cass_vf_notif.h"
+#include "cxi_core.h"
+
+MODULE_SOFTDEP("pre: vsock vsock_loopback");
+
+#if defined(CXI_DISABLE_SRIOV)
+#warning "SR-IOV support is disabled."
+
+int cass_sriov_configure(struct pci_dev *pdev, int num_vfs)
+{
+	return -EOPNOTSUPP;
+}
+
+int cass_vf_init(struct cass_dev *hw)
+{
+	return -EOPNOTSUPP;
+}
+
+void cass_vf_fini(struct cass_dev *hw)
+{
+}
+
+int cxi_send_msg_to_pf(struct cxi_dev *cdev, const void *req,
+		       size_t req_len, void *rsp, size_t *rsp_len)
+{
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(cxi_send_msg_to_pf);
+
+int cxi_send_msg_to_vf(struct cxi_dev *cdev, int vf_num, const void *req,
+		       size_t req_len, void *rsp, size_t *rsp_len)
+{
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(cxi_send_msg_to_vf);
+
+int cxi_register_msg_relay(struct cxi_dev *cdev, cxi_msg_relay_t msg_relay,
+			   void *msg_relay_data)
+{
+	return 0;
+}
+EXPORT_SYMBOL(cxi_register_msg_relay);
+
+int cxi_unregister_msg_relay(struct cxi_dev *cdev)
+{
+	return 0;
+}
+EXPORT_SYMBOL(cxi_unregister_msg_relay);
+
+int cass_vf_get_token(struct cxi_dev *hw, int vf_idx, unsigned int *token)
+{
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(cass_vf_get_token);
+
+#else /* CXI_DISABLE_SRIOV */
+
+/* TODO: make port configurable? (0x17db is arbitrary, taken from C1 PCI vendor ID) */
+#define CXI_SRIOV_VSOCK_PORT 0x17db
+#define CXI_SRIOV_VSOCK_NOTIF_PORT (CXI_SRIOV_VSOCK_PORT + 1)
+
+/* Magic numbers used in VF-PF handshake */
+#define CXI_SRIOV_MAGIC1 0x17db0000
+#define CXI_SRIOV_MAGIC2 0x12345678
+
+/* PF-side timeout for vsock. Kept short so that listener-thread loop still runs. */
+#define CXI_SRIOV_PF_TIMEOUT (HZ / 4)
+
+/* VF-side timeout - longer than PF timeout, to allow time for PF to respond to requests */
+#define CXI_SRIOV_VF_TIMEOUT (HZ * 10)
+
+static int vsock_send_all(struct socket *sock, const void *buffer, size_t length)
+{
+	const u8 *data = buffer;
+	int rc;
+
+	while (length) {
+		struct msghdr msghdr = { .msg_flags = MSG_NOSIGNAL };
+		struct kvec vec = {
+			.iov_base = (void *)data,
+			.iov_len = length,
+		};
+
+		rc = kernel_sendmsg(sock, &msghdr, &vec, 1, length);
+		if (rc <= 0) {
+			kernel_sock_shutdown(sock, SHUT_RDWR);
+			return rc ? rc : -EPIPE;
+		}
+		data += rc;
+		length -= rc;
+	}
+	return 0;
+}
+
+static int write_message_to_vsock(struct socket *sock, const void *msg,
+				  size_t msg_len, int msg_rc, int seq)
+{
+	struct vf_pf_msg_hdr hdr = {
+		.len = msg_len,
+		.rc = msg_rc,
+		.uid = __kuid_val(current_euid()),
+		.gid = __kgid_val(current_egid()),
+		.seq = seq,
+	};
+	int rc;
+
+	if (msg_len > MAX_VFMSG_SIZE || (msg_len && !msg))
+		return -EINVAL;
+	rc = vsock_send_all(sock, &hdr, sizeof(hdr));
+	if (rc)
+		return rc;
+	rc = vsock_send_all(sock, msg, msg_len);
+	if (rc)
+		return rc;
+	return sizeof(hdr) + msg_len;
+}
+
+static int vsock_recv_all(struct socket *sock, void *buffer, size_t length)
+{
+	u8 *data = buffer;
+	size_t received = 0;
+	int rc;
+
+	while (received < length) {
+		struct msghdr msghdr = {};
+		struct kvec vec = {
+			.iov_base = data + received,
+			.iov_len = length - received,
+		};
+
+		rc = kernel_recvmsg(sock, &msghdr, &vec, 1, vec.iov_len, 0);
+		if (rc <= 0) {
+			if (received || !rc)
+				kernel_sock_shutdown(sock, SHUT_RDWR);
+			if (received && (rc == -EAGAIN || rc == -EINTR))
+				return -EPROTO;
+			return rc ? rc : -ECONNRESET;
+		}
+		received += rc;
+	}
+	return 0;
+}
+
+static int read_message_from_vsock(struct socket *sock, void *msg,
+				   size_t *msg_len, int *msg_rc,
+				   uid_t *uid, gid_t *gid, int *seq)
+{
+	struct vf_pf_msg_hdr hdr;
+	u8 discard[64];
+	size_t remaining;
+	int rc;
+
+	rc = vsock_recv_all(sock, &hdr, sizeof(hdr));
+	if (rc)
+		return rc;
+
+	if (hdr.len > MAX_VFMSG_SIZE) {
+		pr_err("Bad message size (%u > %zu)", hdr.len, MAX_VFMSG_SIZE);
+		rc = -EINVAL;
+		goto fail_frame;
+	}
+
+	if (msg && hdr.len && (!msg_len || *msg_len < hdr.len)) {
+		pr_err("Message buffer too small (%u > %zu)", hdr.len, msg_len ? *msg_len : 0);
+		rc = -EMSGSIZE;
+		goto fail_frame;
+	}
+
+	remaining = hdr.len;
+	while (remaining) {
+		size_t chunk = msg ? remaining :
+			min_t(size_t, remaining, sizeof(discard));
+
+		rc = vsock_recv_all(sock, msg ? msg : discard, chunk);
+		if (rc) {
+			if (rc == -EAGAIN || rc == -EINTR)
+				rc = -EPROTO;
+			goto fail_frame;
+		}
+		remaining -= chunk;
+	}
+
+	if (msg_len)
+		*msg_len = hdr.len;
+	if (msg_rc)
+		*msg_rc = hdr.rc;
+	if (uid)
+		*uid = hdr.uid;
+	if (gid)
+		*gid = hdr.gid;
+	if (seq)
+		*seq = hdr.seq;
+
+	return hdr.len ? hdr.len : sizeof(hdr);
+fail_frame:
+	kernel_sock_shutdown(sock, SHUT_RDWR);
+	return rc;
+}
+
+/* Given a PF's pci_dev struct, find the pci_dev of one of its VFs */
+static struct pci_dev *get_vf_pdev(struct pci_dev *pdev, int vf_idx)
+{
+	int devfn = pci_iov_virtfn_devfn(pdev, vf_idx);
+
+	if (devfn < 0)
+		return ERR_PTR(devfn);
+	return pci_get_slot(pdev->bus, devfn);
+}
+
+/* Identify the KVM task associated with a VF that is bound to a guest VM. */
+static struct task_struct *pf_get_kvm_task(struct cass_vf *vf)
+{
+	struct cass_dev *hw = vf->hw;
+	struct pci_dev *vf_pdev = NULL;
+	struct vfio_device *vfio_dev = NULL;
+	struct task_struct *task = NULL;
+	struct pid *pid;
+
+	vf_pdev = get_vf_pdev(hw->cdev.pdev, vf->vf_idx);
+	if (IS_ERR(vf_pdev)) {
+		cxidev_err(&hw->cdev, "vf %d: could not get device", vf->vf_idx);
+		return ERR_PTR(PTR_ERR(vf_pdev));
+	}
+
+	cxidev_dbg(&hw->cdev, "vf %d: device at %s", vf->vf_idx, pci_name(vf_pdev));
+
+	if (!vf_pdev->driver) {
+		task = ERR_PTR(-ENODEV);
+	} else if (!strncmp(vf_pdev->driver->name, "vfio-pci", strlen("vfio-pci"))) {
+		/* VF is under control of VFIO driver, look for KVM task. */
+		vfio_dev = pci_get_drvdata(vf_pdev);
+		if (vfio_dev->kvm) {
+			pid = find_get_pid(vfio_dev->kvm->userspace_pid);
+			task = get_pid_task(pid, PIDTYPE_PID);
+			put_pid(pid);
+		} else {
+			task = ERR_PTR(-ESRCH);
+		}
+	} else if (!strncmp(vf_pdev->driver->name, KBUILD_MODNAME,
+			    strlen(KBUILD_MODNAME))) {
+		/* VF is under control of our driver, KVM task is not applicable */
+		task = NULL;
+	} else {
+		task = ERR_PTR(-EPERM);
+	}
+	pci_dev_put(vf_pdev);
+	return task;
+}
+
+/* Handler thread for incoming messages from the VF driver to the PF. 1 instance
+ * per active VF.
+ */
+static int pf_vf_msghandler(void *data)
+{
+	struct cass_vf *vf = (struct cass_vf *)data;
+	struct cass_dev *hw = vf->hw;
+	struct task_struct *kvm_task;
+	int rc, msg_rc;
+	size_t request_len, reply_len;
+	uid_t uid;
+	gid_t gid;
+	int seq;
+
+	vf->req_sock->sk->sk_rcvtimeo = CXI_SRIOV_PF_TIMEOUT;
+
+	cxidev_dbg(&hw->cdev, "vf %d: started message handler", vf->vf_idx);
+
+	kvm_task = pf_get_kvm_task(vf);
+	if (IS_ERR(kvm_task)) {
+		rc = PTR_ERR(kvm_task);
+		goto err;
+	}
+	vf->kvm_task = kvm_task;
+
+	while (!kthread_should_stop()) {
+		request_len = MAX_VFMSG_SIZE;
+		rc = read_message_from_vsock(vf->req_sock, vf->request,
+					     &request_len, NULL, &uid, &gid, &seq);
+		if (rc == -EAGAIN) {
+			continue;
+		} else if (rc == -EINTR) {
+			/* Expected when thread is asked to terminate */
+			continue;
+		} else if (rc < 0) {
+			cxidev_err(&hw->cdev, "vf %d: error reading request: %d",
+				   vf->vf_idx, rc);
+			break;
+		} else if (rc == 0) {
+			cxidev_err(&hw->cdev, "vf %d: connection closed by VF",
+				   vf->vf_idx);
+			break;
+		}
+
+		cxidev_dbg(&hw->cdev, "vf %d: got %ld byte message", vf->vf_idx,
+			   request_len);
+
+		if (vf->kvm_task) {
+			/* VFs associated with a guest virtual machine must use the
+			 * credentials of the VM's process on the host, rather than the
+			 * guest's user creds that come over the vsock.
+			 */
+			const struct cred *cred;
+
+			rcu_read_lock();
+			cred = rcu_dereference(vf->kvm_task->cred);
+			uid = __kuid_val(cred->uid);
+			gid = __kgid_val(cred->gid);
+			rcu_read_unlock();
+		}
+
+		mutex_lock(&hw->msg_relay_lock);
+		if (hw->msg_relay) {
+			reply_len = MAX_VFMSG_SIZE;
+			msg_rc = hw->msg_relay(hw->msg_relay_data, vf->vf_idx,
+					       vf->request, request_len, uid, gid,
+					       vf->reply, &reply_len);
+		} else {
+			reply_len = 0;
+			msg_rc = -ENODEV;
+		}
+		mutex_unlock(&hw->msg_relay_lock);
+
+		if (reply_len > MAX_VFMSG_SIZE) {
+			reply_len = 0;
+			msg_rc = -E2BIG;
+		}
+
+		cxidev_dbg(&hw->cdev, "vf %d: responding with %ld bytes, rc=%d",
+			   vf->vf_idx, reply_len, msg_rc);
+		rc = write_message_to_vsock(vf->req_sock, vf->reply, reply_len,
+					    msg_rc, seq);
+		if (rc < 0) {
+			cxidev_err(&hw->cdev, "vf %d: error sending response: %d",
+				   vf->vf_idx, rc);
+			break;
+		}
+	}
+
+	/* Send a NULL request message to indicate that this VF disconnected */
+	mutex_lock(&hw->msg_relay_lock);
+	if (hw->msg_relay)
+		hw->msg_relay(hw->msg_relay_data, vf->vf_idx, NULL, 0, 0, 0, NULL,
+			      &reply_len);
+	mutex_unlock(&hw->msg_relay_lock);
+
+	if (rc > 0)
+		rc = 0;
+
+err:
+	cxidev_dbg(&hw->cdev, "vf %d: handler exiting, rc=%d", vf->vf_idx, rc);
+
+	if (vf->kvm_task)
+		put_task_struct(vf->kvm_task);
+	kernel_sock_shutdown(vf->req_sock, SHUT_RDWR);
+	sock_release(vf->req_sock);
+	vf->req_sock = NULL;
+	mutex_lock(&vf->notif_lock);
+	if (vf->notif_sock) {
+		kernel_sock_shutdown(vf->notif_sock, SHUT_RDWR);
+		sock_release(vf->notif_sock);
+		vf->notif_sock = NULL;
+	}
+	mutex_unlock(&vf->notif_lock);
+
+	vf->kvm_task = NULL;
+
+	cass_ac_phys_free(hw, vf->phys_ac);
+	vf->phys_ac = 0;
+
+	return rc;
+}
+
+/* Probe inactive VFs from range_min to range_max inclusive */
+static int pf_probe_vfs(struct cass_dev *hw, struct socket *sock,
+			int range_min, int range_max)
+{
+	int i, rc;
+	int magic;
+	size_t msg_len = sizeof(magic);
+	union c_pi_ipd_cfg_pf_vf_irq irqs = {
+		.irq = 0,
+	};
+
+	for (i = range_min; i <= range_max; i++)
+		if (!hw->vfs[i].req_sock) /* Only probe VFs that aren't already active */
+			irqs.irq |= 1ULL << i;
+	if (!irqs.irq)
+		return 0;
+
+	magic = CXI_SRIOV_MAGIC1;
+	rc = write_message_to_vsock(sock, &magic, sizeof(magic), 0, 0);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "could not send magic to incoming VF: %d", rc);
+		return rc;
+	}
+
+	cass_write(hw, C_PI_IPD_CFG_PF_VF_IRQ, &irqs,
+		   sizeof(union c_pi_ipd_cfg_pf_vf_irq));
+
+	msg_len = sizeof(magic);
+	do {
+		rc = read_message_from_vsock(sock, &magic, &msg_len, NULL, NULL, NULL, NULL);
+		if (rc == -EINTR)
+			schedule();
+	} while (rc == -EINTR);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "could not read magic from incoming VF: %d", rc);
+		return rc;
+	}
+
+	return magic;
+}
+
+/* Identify which VF an incoming connection belongs to, by probing the PF-to-VF
+ * interrupt of VFs that do not have an active connection.
+ */
+static int pf_vf_handshake(struct cass_dev *hw, struct socket *sock)
+{
+	int range_min = 0;
+	int range_max = hw->num_vfs - 1;
+	int range_mid, rc, seq, vf_idx;
+	int magic;
+
+	seq = 0; /* Handshake sequence number */
+	do {
+		magic = CXI_SRIOV_MAGIC1 + seq;
+		range_mid = range_min + (range_max - range_min) / 2;
+		if (pf_probe_vfs(hw, sock, range_min, range_mid) == magic) {
+			seq += 1;
+			range_max = range_mid;
+		} else if (pf_probe_vfs(hw, sock, range_mid + 1, range_max) == magic) {
+			seq += 1;
+			range_min = range_mid + 1;
+		} else {
+			break;
+		}
+	} while (range_max != range_min);
+
+	if (range_max != range_min) {
+		cxidev_err(&hw->cdev, "vf search failed");
+		return -ENOENT;
+	}
+	vf_idx = range_min;
+
+	magic = CXI_SRIOV_MAGIC2;
+	rc = write_message_to_vsock(sock, &magic, sizeof(magic), 0, 0);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "could not send magic to incoming VF: %d", rc);
+		return rc;
+	}
+
+	return vf_idx;
+}
+
+/* Handle an incoming request connection from a VF */
+static void handle_vf_req_conn(struct cass_dev *hw,
+			       struct socket *incoming)
+{
+	struct sockaddr_vm peeraddr;
+	struct cass_vf *vf;
+	int rc, vf_idx;
+
+	rc = kernel_getpeername(incoming, (struct sockaddr *)&peeraddr);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "could not get peer addr for vf: %d", rc);
+		goto close_sock;
+	}
+
+	vf_idx = pf_vf_handshake(hw, incoming);
+	if (vf_idx < 0) {
+		cxidev_err(&hw->cdev, "vf handshake from cid %d failed: %d",
+			   peeraddr.svm_cid, vf_idx);
+		goto close_sock;
+	}
+
+	cxidev_dbg(&hw->cdev, "pf got request connection from cid %d, vf %d",
+		   peeraddr.svm_cid, vf_idx);
+
+	vf = &hw->vfs[vf_idx];
+	if (vf->req_sock) {
+		cxidev_err(&hw->cdev, "vf %d already in use", vf_idx);
+		goto close_sock;
+	}
+
+	vf->vf_idx = vf_idx;
+	vf->hw = hw;
+	vf->req_sock = incoming;
+	get_random_bytes(&vf->token, sizeof(vf->token));
+
+	rc = cass_ac_phys_alloc(hw, true, vf_idx);
+	if (rc <= 0) {
+		cxidev_err(&hw->cdev, "could not allocate ac for vf %d: %d",
+			   vf_idx, rc);
+		vf->req_sock = NULL;
+		goto close_sock;
+	}
+	vf->phys_ac = rc;
+	cxidev_dbg(&hw->cdev, "allocated acid %d for vf %d", vf->phys_ac, vf_idx);
+
+	vf->task = kthread_run(pf_vf_msghandler, vf, "cxi_vf_%d", vf_idx);
+	if (IS_ERR(vf->task)) {
+		cxidev_err(&hw->cdev, "failed to start handler for vf %d", vf_idx);
+		cass_ac_phys_free(hw, vf->phys_ac);
+		vf->phys_ac = 0;
+		vf->req_sock = NULL;
+		vf->task = NULL;
+		goto close_sock;
+	}
+	return;
+
+close_sock:
+	kernel_sock_shutdown(incoming, SHUT_RDWR);
+	sock_release(incoming);
+}
+
+/* Handle an incoming notification connection from a VF */
+static void handle_vf_notif_conn(struct cass_dev *hw,
+				 struct socket *incoming)
+{
+	struct sockaddr_vm peeraddr;
+	struct cass_vf *vf;
+	int rc, vf_idx, i;
+	unsigned int token;
+	struct cass_vf_notif_ping ping = {
+		.op = CASS_VF_NOTIF_OP_PING,
+	};
+	size_t msg_len;
+
+	rc = kernel_getpeername(incoming, (struct sockaddr *)&peeraddr);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev,
+			   "could not get peer addr for notification connection: %d", rc);
+		goto close_sock;
+	}
+
+	/* Read token to identify which VF this is */
+	msg_len = sizeof(token);
+	do {
+		rc = read_message_from_vsock(incoming, &token, &msg_len, NULL, NULL, NULL, NULL);
+		if (rc == -EINTR)
+			schedule();
+	} while (rc == -EINTR);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev,
+			   "could not read token from notification connection: %d", rc);
+		goto close_sock;
+	}
+
+	if (msg_len != sizeof(token)) {
+		cxidev_err(&hw->cdev, "invalid token size: %zu", msg_len);
+		goto close_sock;
+	}
+
+	/* Find VF by matching token */
+	vf_idx = -1;
+	for (i = 0; i < hw->num_vfs; i++) {
+		if (hw->vfs[i].req_sock && hw->vfs[i].token == token) {
+			vf_idx = i;
+			break;
+		}
+	}
+
+	if (vf_idx < 0) {
+		cxidev_err(&hw->cdev, "notification connection with unknown token %u",
+			   token);
+		goto close_sock;
+	}
+
+	cxidev_dbg(&hw->cdev, "pf got notification connection from cid %d, vf %d",
+		   peeraddr.svm_cid, vf_idx);
+
+	vf = &hw->vfs[vf_idx];
+	mutex_lock(&vf->notif_lock);
+	if (vf->notif_sock) {
+		cxidev_err(&hw->cdev, "vf %d notification socket already in use", vf_idx);
+		mutex_unlock(&vf->notif_lock);
+		goto close_sock;
+	}
+
+	vf->notif_seq = 0;
+	vf->notif_sock = incoming;
+	mutex_unlock(&vf->notif_lock);
+	cxidev_dbg(&hw->cdev, "vf %d: notification socket connected", vf_idx);
+
+	/* Send test notification */
+	msg_len = sizeof(ping);
+	rc = cxi_send_msg_to_vf(&hw->cdev, vf_idx, &ping, msg_len,
+				NULL, NULL);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "vf %d: could not send test notification: %d",
+			   vf_idx, rc);
+		mutex_lock(&vf->notif_lock);
+		if (vf->notif_sock != incoming) {
+			mutex_unlock(&vf->notif_lock);
+			return;
+		}
+		vf->notif_sock = NULL;
+		mutex_unlock(&vf->notif_lock);
+		goto close_sock;
+	}
+
+	return;
+
+close_sock:
+	kernel_sock_shutdown(incoming, SHUT_RDWR);
+	sock_release(incoming);
+}
+
+static int vsock_create_listen(struct socket **sock, unsigned int port,
+			       int timeout, int backlog)
+{
+	int rc;
+	struct sockaddr_vm addr = {
+		.svm_family = AF_VSOCK,
+		.svm_port = port,
+		.svm_cid = VMADDR_CID_ANY
+	};
+
+	rc = sock_create_kern(&init_net, PF_VSOCK, SOCK_STREAM, 0, sock);
+	if (rc < 0)
+		return rc;
+
+	rc = kernel_bind(*sock, (struct sockaddr_unsized *)&addr, sizeof(addr));
+	if (rc < 0)
+		goto release_sock;
+
+	(*sock)->sk->sk_rcvtimeo = timeout;
+	rc = kernel_listen(*sock, backlog);
+	if (rc < 0)
+		goto release_sock;
+
+	return 0;
+release_sock:
+	sock_release(*sock);
+	*sock = NULL;
+	return rc;
+}
+
+/* Combined listener thread for incoming connections from VFs to the PF.
+ * Handles both request and notification sockets. 1 instance only.
+ */
+static int pf_vf_listener(void *data)
+{
+	int rc, vf_idx;
+	struct cass_dev *hw = (struct cass_dev *)data;
+
+	cxidev_dbg(&hw->cdev, "started vf listener");
+
+	rc = vsock_create_listen(&hw->vf_req_sock, CXI_SRIOV_VSOCK_PORT,
+				 CXI_SRIOV_PF_TIMEOUT, hw->num_vfs);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "could not create vf request listener socket: %d", rc);
+		return rc;
+	}
+
+	rc = vsock_create_listen(&hw->vf_notif_sock, CXI_SRIOV_VSOCK_NOTIF_PORT,
+				 CXI_SRIOV_PF_TIMEOUT, hw->num_vfs);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "could not create vf notification listener socket: %d", rc);
+		goto shutdown_req;
+	}
+
+	while (!kthread_should_stop()) {
+		struct socket *incoming;
+
+		rc = kernel_accept(hw->vf_req_sock, &incoming, 0);
+		if (rc == 0) {
+			handle_vf_req_conn(hw, incoming);
+			continue;
+		} else if (rc != -EAGAIN && rc != -EINTR) {
+			cxidev_err(&hw->cdev, "vf request listener socket accept error: %d", rc);
+			break;
+		}
+
+		rc = kernel_accept(hw->vf_notif_sock, &incoming, 0);
+		if (rc == 0) {
+			handle_vf_notif_conn(hw, incoming);
+			continue;
+		} else if (rc != -EAGAIN && rc != -EINTR) {
+			cxidev_err(&hw->cdev, "vf notification listener socket accept error: %d",
+				   rc);
+			break;
+		}
+	}
+
+	cxidev_dbg(&hw->cdev, "vf listener exiting");
+
+	for (vf_idx = 0; vf_idx < hw->num_vfs; vf_idx++) {
+		if (hw->vfs[vf_idx].req_sock) {
+			kthread_stop(hw->vfs[vf_idx].task);
+			hw->vfs[vf_idx].task = NULL;
+		}
+		mutex_lock(&hw->vfs[vf_idx].notif_lock);
+		if (hw->vfs[vf_idx].notif_sock) {
+			kernel_sock_shutdown(hw->vfs[vf_idx].notif_sock, SHUT_RDWR);
+			sock_release(hw->vfs[vf_idx].notif_sock);
+			hw->vfs[vf_idx].notif_sock = NULL;
+		}
+		mutex_unlock(&hw->vfs[vf_idx].notif_lock);
+	}
+
+	kernel_sock_shutdown(hw->vf_notif_sock, SHUT_RDWR);
+	sock_release(hw->vf_notif_sock);
+	hw->vf_notif_sock = NULL;
+
+shutdown_req:
+	kernel_sock_shutdown(hw->vf_req_sock, SHUT_RDWR);
+	sock_release(hw->vf_req_sock);
+	hw->vf_req_sock = NULL;
+
+	return rc;
+}
+
+static void disable_sriov(struct pci_dev *pdev)
+{
+	struct cass_dev *hw = pci_get_drvdata(pdev);
+
+	pci_disable_sriov(pdev);
+
+	if (hw->vf_listener) {
+		kthread_stop(hw->vf_listener);
+		hw->vf_listener = NULL;
+	}
+
+	hw->num_vfs = 0;
+}
+
+static int enable_sriov(struct pci_dev *pdev, int num_vfs)
+{
+	int rc;
+	int sriov;
+	u16 offset;
+	u16 stride;
+	union c_pi_cfg_pri_sriov pri_sriov = {};
+	struct cass_dev *hw = pci_get_drvdata(pdev);
+
+	rc = request_module("vsock");
+	if (rc) {
+		cxidev_err(&hw->cdev, "could not load vsock module");
+		goto err_novf;
+	}
+
+	rc = request_module("vsock_loopback");
+	if (rc) {
+		cxidev_err(&hw->cdev, "could not load vsock_loopback module");
+		goto err_novf;
+	}
+
+	hw->num_vfs = num_vfs;
+
+	if (!hw->vf_listener)
+		hw->vf_listener = kthread_run(pf_vf_listener, hw, "cxi_vf_listener");
+	if (IS_ERR(hw->vf_listener)) {
+		cxidev_err(&hw->cdev, "could not start vf listener thread");
+		rc = PTR_ERR(hw->vf_listener);
+		hw->vf_listener = NULL;
+		goto err_novf;
+	}
+
+	/* The VF Offset and Stride need to match the SR-IOV configuration. */
+	sriov = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+	if (!sriov) {
+		cxidev_err(&hw->cdev, "No extended capabilities found\n");
+		rc = -ENODEV;
+		goto err_kill_listener;
+	}
+
+	pci_read_config_word(pdev, sriov + PCI_SRIOV_VF_OFFSET, &offset);
+	pci_read_config_word(pdev, sriov + PCI_SRIOV_VF_STRIDE, &stride);
+
+	pri_sriov.vf_offset = offset;
+	pri_sriov.vf_stride = stride;
+
+	cass_write(hw, C_PI_CFG_PRI_SRIOV, &pri_sriov,
+		   sizeof(union c_pi_cfg_pri_sriov));
+
+	rc = pci_enable_sriov(pdev, num_vfs);
+	if (rc) {
+		cxidev_err(&hw->cdev, "SRIOV enable failed %d\n", rc);
+		goto err_kill_listener;
+	}
+
+	return num_vfs;
+
+err_kill_listener:
+	kthread_stop(hw->vf_listener);
+	hw->vf_listener = NULL;
+err_novf:
+	hw->num_vfs = 0;
+	return rc;
+}
+
+int cass_sriov_configure(struct pci_dev *pdev, int num_vfs)
+{
+	if (num_vfs < 0)
+		return -EINVAL;
+
+	if (num_vfs == 0) {
+		disable_sriov(pdev);
+		return 0;
+	}
+
+	return enable_sriov(pdev, num_vfs);
+}
+
+static irqreturn_t pf_to_vf_int_cb(int irq, void *context)
+{
+	struct cass_dev *hw = context;
+
+	complete(&hw->pf_to_vf_comp);
+	return IRQ_HANDLED;
+}
+
+/* VF-side handler thread for receiving notifications from PF */
+static int vf_notif_handler(void *data)
+{
+	struct cass_dev *hw = (struct cass_dev *)data;
+	int rc;
+	size_t msg_len, rsp_len;
+
+	cxidev_dbg(&hw->cdev, "started notification handler");
+
+	while (!kthread_should_stop()) {
+		msg_len = MAX_VFMSG_SIZE;
+		rc = read_message_from_vsock(hw->vf_notif_sock, hw->vf_notif_buf,
+					     &msg_len, NULL, NULL, NULL, NULL);
+		if (rc == -EAGAIN) {
+			continue;
+		} else if (rc == -EINTR) {
+			/* Expected when thread is asked to terminate */
+			continue;
+		} else if (rc < 0) {
+			cxidev_err(&hw->cdev, "error reading notification from PF: %d", rc);
+			break;
+		} else if (rc == 0) {
+			cxidev_dbg(&hw->cdev, "notification connection closed by PF");
+			break;
+		}
+
+		cxidev_dbg(&hw->cdev, "received %ld byte notification from PF", msg_len);
+
+		rsp_len = 0;
+		rc = dispatch_vf_notif(hw, hw->vf_notif_buf, msg_len,
+				       hw->vf_notif_rsp_buf, &rsp_len);
+		rc = write_message_to_vsock(hw->vf_notif_sock, hw->vf_notif_rsp_buf,
+					    rsp_len, rc, 0);
+		if (rc < 0) {
+			cxidev_err(&hw->cdev, "failed to send notification response: %d", rc);
+			break;
+		}
+	}
+
+	cxidev_dbg(&hw->cdev, "notification handler exiting");
+
+	return 0;
+}
+
+int cass_vf_init(struct cass_dev *hw)
+{
+	int rc, seq;
+	unsigned int magic;
+	size_t msg_len = sizeof(magic);
+	bool init_done = false;
+	const struct cxi_vf_get_token_cmd token_cmd = {
+		.op = CXI_OP_VF_GET_TOKEN,
+	};
+	struct cxi_vf_get_token_resp token_resp;
+	size_t resp_len = sizeof(token_resp);
+	struct sockaddr_vm addr = {
+		.svm_family = AF_VSOCK,
+		.svm_port = CXI_SRIOV_VSOCK_PORT,
+		.svm_cid = VMADDR_CID_HOST,
+	};
+
+	if (!hw->with_vf_support)
+		return 0;
+
+	init_completion(&hw->pf_to_vf_comp);
+
+	mutex_init(&hw->vf_cmd_lock);
+
+	scnprintf(hw->pf_vf_int_name, sizeof(hw->pf_vf_int_name),
+		  "%s_from_pf", hw->cdev.name);
+	hw->pf_vf_vec = pci_irq_vector(hw->cdev.pdev, 0);
+	rc = request_irq(hw->pf_vf_vec, pf_to_vf_int_cb, 0, hw->pf_vf_int_name, hw);
+	if (rc)
+		return rc;
+
+	rc = request_module("vsock");
+	if (rc) {
+		cxidev_err(&hw->cdev, "could not load vsock module");
+		goto free_irq;
+	}
+
+	rc = sock_create_kern(&init_net, PF_VSOCK, SOCK_STREAM, 0, &hw->vf_req_sock);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "vf socket create failed: %d", rc);
+		goto free_irq;
+	}
+
+	hw->vf_req_sock->sk->sk_rcvtimeo = CXI_SRIOV_VF_TIMEOUT;
+	hw->vf_cmd_seq = 0;
+
+	rc = kernel_connect(hw->vf_req_sock, (struct sockaddr_unsized *)&addr,
+			    sizeof(addr), 0);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "vf socket connect failed: %d", rc);
+		goto release_vf_sock;
+	}
+
+	/* Handshake with PF driver starts here. */
+	seq = 0;
+	while (!init_done) {
+		msg_len = sizeof(magic);
+		do {
+			rc = read_message_from_vsock(hw->vf_req_sock, &magic, &msg_len,
+						     NULL, NULL, NULL, NULL);
+			if (rc == -EINTR)
+				schedule();
+		} while (rc == -EINTR);
+		if (rc < 0) {
+			cxidev_err(&hw->cdev, "error receiving magic from PF: %d", rc);
+			init_done = true;
+			break;
+		} else if (rc == 0) {
+			cxidev_err(&hw->cdev, "PF closed connection during handshake");
+			rc = -ENOTCONN;
+			init_done = true;
+			break;
+		}
+		switch (magic) {
+		case CXI_SRIOV_MAGIC1:
+			rc = wait_for_completion_timeout(&hw->pf_to_vf_comp,
+							 CXI_SRIOV_VF_TIMEOUT);
+			if (rc == 0) {
+				cxidev_err(&hw->cdev, "timed out waiting for irq from pf");
+				rc = -ETIMEDOUT;
+				init_done = true;
+				break;
+			}
+			magic = CXI_SRIOV_MAGIC1 + seq;
+			msg_len = sizeof(magic);
+			rc = write_message_to_vsock(hw->vf_req_sock, &magic,
+						    msg_len, 0, 0);
+			if (rc < 0) {
+				cxidev_err(&hw->cdev, "error sending magic to PF: %d", rc);
+				init_done = true;
+				break;
+			}
+			seq += 1;
+			break;
+		case CXI_SRIOV_MAGIC2:
+			init_done = true;
+			break;
+		default:
+			cxidev_err(&hw->cdev, "got unexpected magic from PF: %x", magic);
+			rc = -EINVAL;
+			init_done = true;
+			break;
+		}
+	}
+
+	if (rc < 0)
+		goto shutdown_vf_sock;
+
+	/* Handshake successful, now retrieve token and connect notification socket */
+	rc = cxi_send_msg_to_pf(&hw->cdev, &token_cmd, sizeof(token_cmd),
+				&token_resp, &resp_len);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "failed to get token from PF: %d", rc);
+		goto shutdown_vf_sock;
+	}
+
+	rc = sock_create_kern(&init_net, PF_VSOCK, SOCK_STREAM, 0,
+			      &hw->vf_notif_sock);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "vf notif socket create failed: %d", rc);
+		goto shutdown_vf_sock;
+	}
+
+	hw->vf_notif_sock->sk->sk_rcvtimeo = CXI_SRIOV_VF_TIMEOUT;
+
+	addr.svm_port = CXI_SRIOV_VSOCK_NOTIF_PORT;
+	rc = kernel_connect(hw->vf_notif_sock, (struct sockaddr_unsized *)&addr,
+			    sizeof(addr), 0);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "vf notif socket connect failed: %d", rc);
+		goto release_notif_sock;
+	}
+
+	/* Send token to identify this VF */
+	rc = write_message_to_vsock(hw->vf_notif_sock, &token_resp.token,
+				    sizeof(token_resp.token), 0, 0);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "failed to send token to PF: %d", rc);
+		goto shutdown_notif_sock;
+	}
+
+	/* Start notification handler thread */
+	hw->vf_notif_handler = kthread_run(vf_notif_handler, hw, "cxi_vf_notif");
+	if (IS_ERR(hw->vf_notif_handler)) {
+		cxidev_err(&hw->cdev, "failed to start notification handler");
+		rc = PTR_ERR(hw->vf_notif_handler);
+		hw->vf_notif_handler = NULL;
+		goto shutdown_notif_sock;
+	}
+
+	return 0;
+
+shutdown_notif_sock:
+	kernel_sock_shutdown(hw->vf_notif_sock, SHUT_RDWR);
+release_notif_sock:
+	sock_release(hw->vf_notif_sock);
+	hw->vf_notif_sock = NULL;
+shutdown_vf_sock:
+	kernel_sock_shutdown(hw->vf_req_sock, SHUT_RDWR);
+release_vf_sock:
+	sock_release(hw->vf_req_sock);
+	hw->vf_req_sock = NULL;
+free_irq:
+	free_irq(hw->pf_vf_vec, hw);
+	return rc;
+}
+
+void cass_vf_fini(struct cass_dev *hw)
+{
+	if (!hw->with_vf_support)
+		return;
+
+	free_irq(hw->pf_vf_vec, hw);
+
+	if (hw->vf_notif_handler) {
+		kthread_stop(hw->vf_notif_handler);
+		hw->vf_notif_handler = NULL;
+	}
+
+	mutex_lock(&hw->vf_cmd_lock);
+	if (hw->vf_req_sock) {
+		kernel_sock_shutdown(hw->vf_req_sock, SHUT_RDWR);
+		sock_release(hw->vf_req_sock);
+		hw->vf_req_sock = NULL;
+	}
+	mutex_unlock(&hw->vf_cmd_lock);
+
+	if (hw->vf_notif_sock) {
+		kernel_sock_shutdown(hw->vf_notif_sock, SHUT_RDWR);
+		sock_release(hw->vf_notif_sock);
+		hw->vf_notif_sock = NULL;
+	}
+}
+
+/**
+ * cxi_vsock_send() - Send a vsock message using a provided socket
+ *
+ * @cdev: the device
+ * @sock: connected vsock socket
+ * @req: message data
+ * @req_len: length of message
+ * @rsp: buffer for response
+ * @rsp_len: length of response buffer (updated to reflect response length)
+ * @seq: sequence number to include in message header and expect in response
+ */
+
+static int cxi_vsock_send(struct cxi_dev *cdev, struct socket *sock, const void *req,
+			  size_t req_len, void *rsp, size_t *rsp_len, int seq)
+{
+	struct cass_dev *hw = container_of(cdev, struct cass_dev, cdev);
+	int rc;
+	int msg_rc;
+	int rsp_seq;
+
+	if (req_len > MAX_VFMSG_SIZE)
+		return -EINVAL;
+
+	if (!sock)
+		return -ENOTCONN;
+
+	rc = write_message_to_vsock(sock, req, req_len, 0, seq);
+	if (rc < 0) {
+		cxidev_err(&hw->cdev, "error sending vsock message: %d", rc);
+		return -EPROTO;
+	}
+	rc = read_message_from_vsock(sock, rsp, rsp_len, &msg_rc,
+				     NULL, NULL, &rsp_seq);
+	if (rc == -EAGAIN) {
+		kernel_sock_shutdown(sock, SHUT_RDWR);
+		cxidev_err(&hw->cdev, "vsock response timed out\n");
+		return -ETIMEDOUT;
+	} else if (rc <= 0) {
+		kernel_sock_shutdown(sock, SHUT_RDWR);
+		cxidev_err(&hw->cdev, "error reading vsock response: %d", rc);
+		return -EPROTO;
+	}
+
+	if (rsp_seq != seq) {
+		kernel_sock_shutdown(sock, SHUT_RDWR);
+		cxidev_err(&hw->cdev, "expected response seq %d but got %d",
+			   seq, rsp_seq);
+		return -EPROTO;
+	}
+
+	return msg_rc;
+}
+
+/**
+ * cxi_send_msg_to_pf() - Send a message from VF to PF
+ *
+ * @cdev: the device
+ * @req: message data
+ * @req_len: length of message
+ * @rsp: buffer for response from PF
+ * @rsp_len: length of response buffer (updated to reflect response length)
+ */
+int cxi_send_msg_to_pf(struct cxi_dev *cdev, const void *req,
+		       size_t req_len, void *rsp, size_t *rsp_len)
+{
+	struct cass_dev *hw = container_of(cdev, struct cass_dev, cdev);
+	int rc;
+
+	if (cdev->is_physfn)
+		return -EINVAL;
+
+	cxidev_dbg(&hw->cdev, "Sending %ld bytes to PF", req_len);
+
+	mutex_lock(&hw->vf_cmd_lock);
+	rc = cxi_vsock_send(cdev, hw->vf_req_sock, req, req_len, rsp, rsp_len,
+			    hw->vf_cmd_seq++);
+	mutex_unlock(&hw->vf_cmd_lock);
+
+	cxidev_dbg(&hw->cdev, "Got %ld byte reply from PF, rc=%d",
+		   rsp_len ? *rsp_len : 0, rc);
+
+	return rc;
+}
+EXPORT_SYMBOL(cxi_send_msg_to_pf);
+
+/**
+ * cxi_send_msg_to_vf() - Send a message from PF to a specific VF
+ *
+ * @cdev: the device
+ * @vf_num: VF index to send the message to
+ * @req: message data
+ * @req_len: length of message
+ * @rsp: buffer for response from VF
+ * @rsp_len: length of response buffer (updated to reflect response length)
+ */
+int cxi_send_msg_to_vf(struct cxi_dev *cdev, int vf_num, const void *req,
+		       size_t req_len, void *rsp, size_t *rsp_len)
+{
+	struct cass_dev *hw = container_of(cdev, struct cass_dev, cdev);
+	int rc;
+
+	if (!cdev->is_physfn)
+		return -EINVAL;
+
+	if (vf_num < 0 || vf_num >= hw->num_vfs)
+		return -EINVAL;
+
+	cxidev_dbg(&hw->cdev, "Sending %ld bytes to VF %d", req_len, vf_num);
+
+	mutex_lock(&hw->vfs[vf_num].notif_lock);
+	rc = cxi_vsock_send(cdev, hw->vfs[vf_num].notif_sock,
+			    req, req_len, rsp, rsp_len,
+			    hw->vfs[vf_num].notif_seq++);
+	mutex_unlock(&hw->vfs[vf_num].notif_lock);
+
+	cxidev_dbg(&hw->cdev, "Got %ld byte reply from VF %d, rc=%d",
+		   rsp_len ? *rsp_len : 0, vf_num, rc);
+
+	return rc;
+}
+EXPORT_SYMBOL(cxi_send_msg_to_vf);
+
+/**
+ * cxi_register_msg_relay() - Register a VF to PF message handler
+ *
+ * The user driver, when inserting a new PF device, is registering a
+ * callback to receive messages from VFs.
+ *
+ * @cdev: the device
+ * @msg_relay: the message handler
+ * @msg_relay_data: opaque pointer to give when caller the handler
+ */
+int cxi_register_msg_relay(struct cxi_dev *cdev, cxi_msg_relay_t msg_relay,
+			   void *msg_relay_data)
+{
+	struct cass_dev *hw = container_of(cdev, struct cass_dev, cdev);
+	int rc;
+
+	if (!cdev->is_physfn)
+		return -EINVAL;
+
+	mutex_lock(&hw->msg_relay_lock);
+
+	if (hw->msg_relay) {
+		rc = -EINVAL;
+	} else {
+		hw->msg_relay = msg_relay;
+		hw->msg_relay_data = msg_relay_data;
+		rc = 0;
+	}
+
+	mutex_unlock(&hw->msg_relay_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(cxi_register_msg_relay);
+
+int cxi_unregister_msg_relay(struct cxi_dev *cdev)
+{
+	struct cass_dev *hw = container_of(cdev, struct cass_dev, cdev);
+	int rc;
+
+	mutex_lock(&hw->msg_relay_lock);
+
+	if (!hw->msg_relay) {
+		rc = -EINVAL;
+	} else {
+		hw->msg_relay = NULL;
+		hw->msg_relay_data = NULL;
+		rc = 0;
+	}
+
+	mutex_unlock(&hw->msg_relay_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(cxi_unregister_msg_relay);
+
+int cass_vf_get_token(struct cxi_dev *cdev, int vf_idx, unsigned int *token)
+{
+	struct cass_dev *hw = container_of(cdev, struct cass_dev, cdev);
+
+	if (!cdev->is_physfn)
+		return -EOPNOTSUPP;
+
+	if (vf_idx < 0 || vf_idx >= hw->num_vfs)
+		return -EINVAL;
+
+	*token = hw->vfs[vf_idx].token;
+	return 0;
+}
+EXPORT_SYMBOL(cass_vf_get_token);
+
+#endif /* CXI_DISABLE_SRIOV */
diff --git a/drivers/net/ethernet/hpe/ss1/cass_vf.c b/drivers/net/ethernet/hpe/ss1/cass_vf.c
new file mode 100644
index 000000000000..fde40d1b491a
--- /dev/null
+++ b/drivers/net/ethernet/hpe/ss1/cass_vf.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2026 Hewlett Packard Enterprise Development LP */
+
+#include <linux/hpe/cxi/cxi.h>
+#include <linux/dma-mapping.h>
+#include "cass_core.h"
+#include "cass_vf.h"
+
+/* Compile-time assertions to ensure VF structures can be safely cast to their
+ * corresponding PF structures and vice versa.
+ * These verify that the common fields are at the same offsets in both structures.
+ */
+static inline void __cxi_vf_structure_offset_checks(void)
+{
+	/* Ensure cxi_lni_priv_vf can be cast to cxi_lni_priv */
+	BUILD_BUG_ON(offsetof(struct cxi_lni_priv_vf, dev) !=
+		     offsetof(struct cxi_lni_priv, dev));
+	BUILD_BUG_ON(offsetof(struct cxi_lni_priv_vf, lni) !=
+		     offsetof(struct cxi_lni_priv, lni));
+
+	/* Ensure cxi_domain_priv_vf can be cast to cxi_domain_priv */
+	BUILD_BUG_ON(offsetof(struct cxi_domain_priv_vf, domain) !=
+		     offsetof(struct cxi_domain_priv, domain));
+	BUILD_BUG_ON(offsetof(struct cxi_domain_priv_vf, lni_priv) !=
+		     offsetof(struct cxi_domain_priv, lni_priv));
+
+	/* Ensure cxi_cp_priv_vf can be cast to cxi_cp_priv */
+	BUILD_BUG_ON(offsetof(struct cxi_cp_priv_vf, dev) !=
+		     offsetof(struct cxi_cp_priv, dev));
+	BUILD_BUG_ON(offsetof(struct cxi_cp_priv_vf, cp) !=
+		     offsetof(struct cxi_cp_priv, cp));
+
+	/* Ensure cxi_cq_priv_vf can be cast to cxi_cq_priv */
+	BUILD_BUG_ON(offsetof(struct cxi_cq_priv_vf, lni_priv) !=
+		     offsetof(struct cxi_cq_priv, lni_priv));
+	BUILD_BUG_ON(offsetof(struct cxi_cq_priv_vf, cass_cq) !=
+		     offsetof(struct cxi_cq_priv, cass_cq));
+	BUILD_BUG_ON(offsetof(struct cxi_cq_priv_vf, pages) !=
+		     offsetof(struct cxi_cq_priv, pages));
+	BUILD_BUG_ON(offsetof(struct cxi_cq_priv_vf, cq_mmio) !=
+		     offsetof(struct cxi_cq_priv, cq_mmio));
+	BUILD_BUG_ON(offsetof(struct cxi_cq_priv_vf, flags) !=
+		     offsetof(struct cxi_cq_priv, flags));
+
+	/* Ensure cxi_md_priv_vf can be cast to cxi_md_priv */
+	BUILD_BUG_ON(offsetof(struct cxi_md_priv_vf, lni_priv) !=
+		     offsetof(struct cxi_md_priv, lni_priv));
+	BUILD_BUG_ON(offsetof(struct cxi_md_priv_vf, device) !=
+		     offsetof(struct cxi_md_priv, device));
+	BUILD_BUG_ON(offsetof(struct cxi_md_priv_vf, md) !=
+		     offsetof(struct cxi_md_priv, md));
+	BUILD_BUG_ON(offsetof(struct cxi_md_priv_vf, sgt) !=
+		     offsetof(struct cxi_md_priv, sgt));
+	BUILD_BUG_ON(offsetof(struct cxi_md_priv_vf, pages) !=
+		     offsetof(struct cxi_md_priv, pages));
+	BUILD_BUG_ON(offsetof(struct cxi_md_priv_vf, flags) !=
+		     offsetof(struct cxi_md_priv, flags));
+}
diff --git a/drivers/net/ethernet/hpe/ss1/cass_vf_notif.c b/drivers/net/ethernet/hpe/ss1/cass_vf_notif.c
new file mode 100644
index 000000000000..c46e19869873
--- /dev/null
+++ b/drivers/net/ethernet/hpe/ss1/cass_vf_notif.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2026 Hewlett Packard Enterprise Development LP */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+
+#include "cass_core.h"
+#include "cxi_core.h"
+#include "cass_vf_notif.h"
+
+#if !defined(CXI_DISABLE_SRIOV)
+
+static int cass_vf_notif_ping_handler(struct cass_dev *hw,
+				      const void *cmd_in, void *resp,
+				      size_t *resp_len)
+{
+	return 0;
+}
+
+static const struct cass_vf_notif_info vf_notif_info[] = {
+	[CASS_VF_NOTIF_OP_PING] = {
+		.req_size   = sizeof(struct cass_vf_notif_ping),
+		.name       = "PING",
+		.handler    = cass_vf_notif_ping_handler, },
+};
+
+int dispatch_vf_notif(struct cass_dev *hw, const void *req, size_t req_len,
+		      void *rsp, size_t *rsp_len)
+{
+	const struct cass_vf_notif_common *common_req = req;
+	enum cass_vf_notif_ops op;
+	const struct cass_vf_notif_info *info;
+
+	op = common_req->op;
+	if (op <= CASS_VF_NOTIF_OP_INVALID || op >= CASS_VF_NOTIF_OP_MAX)
+		return -EINVAL;
+
+	info = &vf_notif_info[op];
+	if (!info->handler)
+		return -EOPNOTSUPP;
+
+	if (req_len != info->req_size)
+		return -EINVAL;
+
+	cxidev_dbg(&hw->cdev, "VF notification received: %s\n", info->name);
+
+	return info->handler(hw, req, rsp, rsp_len);
+}
+
+#endif /* CXI_DISABLE_SRIOV */

-- 
2.56.0


  parent reply	other threads:[~2026-10-30 21:01 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30 20:58 [PATCH 00/23] Subject: [PATCH net-next 00/23] net/hpe: add HPE Slingshot NIC drivers Doug Ledford
2026-10-30 20:58 ` [PATCH 01/23] net: hpe: ss1: add the Cassini headers Doug Ledford
2026-10-31 13:58   ` Andrew Lunn
2026-10-31 19:34     ` Doug Ledford
2026-10-30 20:58 ` [PATCH 02/23] net: hpe: sbl: add the Slingshot Base Link headers Doug Ledford
2026-10-30 20:58 ` [PATCH 03/23] net: hpe: sbl: add the core instance and configuration layer Doug Ledford
2026-10-30 20:59 ` [PATCH 04/23] net: hpe: sbl: add the SerDes layer Doug Ledford
2026-10-30 20:59 ` [PATCH 05/23] net: hpe: sbl: add the PML link layer Doug Ledford
2026-10-30 20:59 ` [PATCH 06/23] net: hpe: sbl: add link state management Doug Ledford
2026-10-30 20:59 ` [PATCH 07/23] net: hpe: sbl: add the SBM serial bus layer Doug Ledford
2026-10-30 20:59 ` [PATCH 08/23] net: hpe: sbl: add media, FEC and counter support Doug Ledford
2026-10-30 20:59 ` [PATCH 09/23] net: hpe: sl: add the Slingshot Link headers Doug Ledford
2026-10-30 20:59 ` [PATCH 10/23] net: hpe: sl: add the module entry points Doug Ledford
2026-10-30 20:59 ` [PATCH 11/23] net: hpe: sl: add the core link state machines Doug Ledford
2026-10-30 20:59 ` [PATCH 12/23] net: hpe: sl: add the control layer Doug Ledford
2026-10-30 20:59 ` [PATCH 13/23] net: hpe: sl: add media and cable handling Doug Ledford
2026-10-30 20:59 ` [PATCH 14/23] net: hpe: sl: add the sysfs interface Doug Ledford
2026-10-30 20:59 ` [PATCH 15/23] net: hpe: sl: add the NIC binding and logging helpers Doug Ledford
2026-10-30 20:59 ` [PATCH 16/23] net: hpe: ss1: add the Cassini core device layer Doug Ledford
2026-10-30 20:59 ` [PATCH 17/23] net: hpe: ss1: add the datapath and memory translation layers Doug Ledford
2026-10-30 20:59 ` [PATCH 18/23] net: hpe: ss1: add the link layer glue Doug Ledford
2026-10-30 20:59 ` Doug Ledford [this message]
2026-10-30 20:59 ` [PATCH 20/23] net: hpe: ss1: add the CXI core interfaces Doug Ledford
2026-10-30 20:59 ` [PATCH 21/23] net: hpe: ss1: add resource groups, QoS and services Doug Ledford
2026-10-30 20:59 ` [PATCH 22/23] net: hpe: ss1: add the Ethernet personality Doug Ledford
2026-10-30 20:59 ` [PATCH 23/23] net: hpe: wire up the Slingshot NIC drivers Doug Ledford
2026-10-30 22:11 ` [PATCH 00/23] Subject: [PATCH net-next 00/23] net/hpe: add HPE " Jakub Kicinski
2026-10-31 20:17   ` Andrew Lunn
2026-11-02  1:02     ` Doug Ledford
2026-11-02  1:35       ` Andrew Lunn
2026-11-02  3:45         ` Doug Ledford
2026-11-02 14:26           ` Andrew Lunn
2026-11-04  4:21             ` Doug Ledford
2026-11-04 13:39               ` Jason Gunthorpe
2026-11-04 16:03                 ` Doug Ledford
2026-11-04 14:48               ` Andrew Lunn
2026-11-04 15:04                 ` Jason Gunthorpe
2026-11-04 16:13                   ` Doug Ledford
2026-11-02  3:01       ` Guenter Roeck
2026-11-02 17:01         ` Doug Ledford
2026-11-02 17:39           ` Andrew Lunn
2026-11-02 18:12             ` Doug Ledford

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261030-cxi-netdev-submission-v1-19-8c2c391cc283@hpe.com \
    --to=doug.ledford@hpe.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=christian.koenig@amd.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kbuild@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@roeck-us.net \
    --cc=nathan@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=nsc@kernel.org \
    --cc=pabeni@redhat.com \
    --cc=richardcochran@gmail.com \
    --cc=slingshot-os-team@groups.ext.hpe.com \
    --cc=sumit.semwal@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®