Contributors: 1
Author Tokens Token Proportion Commits Commit Proportion
Raghavendra Rao Ananta 777 100.00% 1 100.00%
Total 777 1


// SPDX-License-Identifier: GPL-2.0-only
#include "lib/include/libvfio/assert.h"
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/limits.h>

#include <libvfio.h>

#include "../kselftest_harness.h"

#define UUID_1 "52ac9bff-3a88-4fbd-901a-0d767c3b6c97"
#define UUID_2 "88594674-90a0-47a9-aea8-9d9b352ac08a"

static const char *pf_bdf;
static char *vf_bdf;

static pid_t main_pid;

static int container_setup(struct vfio_pci_device *device, const char *bdf,
			   const char *vf_token)
{
	vfio_pci_group_setup(device, bdf);
	vfio_container_set_iommu(device);
	__vfio_pci_group_get_device_fd(device, bdf, vf_token);

	/* The device fd will be -1 in case of mismatched tokens */
	return (device->fd < 0);
}

static int iommufd_setup(struct vfio_pci_device *device, const char *bdf,
			 const char *vf_token)
{
	vfio_pci_cdev_open(device, bdf);
	return __vfio_device_bind_iommufd(device->fd,
					  device->iommu->iommufd, vf_token);
}

static int device_init(const char *bdf, struct iommu *iommu,
		       const char *vf_token, struct vfio_pci_device **out_dev)
{
	struct vfio_pci_device *device = vfio_pci_device_alloc(bdf, iommu);
	int ret;

	if (iommu->mode->container_path)
		ret = container_setup(device, bdf, vf_token);
	else
		ret = iommufd_setup(device, bdf, vf_token);

	*out_dev = device;
	return ret;
}

static void device_cleanup(struct vfio_pci_device *device)
{
	if (!device)
		return;

	if (device->fd > 0)
		VFIO_ASSERT_EQ(close(device->fd), 0);

	if (device->group_fd)
		VFIO_ASSERT_EQ(close(device->group_fd), 0);

	vfio_pci_device_free(device);
}

FIXTURE(vfio_pci_sriov_uapi_test) {
	struct vfio_pci_device *pf;
	struct vfio_pci_device *vf;
	struct iommu *iommu;
	char *pf_token;
};

FIXTURE_VARIANT(vfio_pci_sriov_uapi_test) {
	const char *iommu_mode;
	char *vf_token;
};

#define FIXTURE_VARIANT_ADD_IOMMU_MODE(_iommu_mode, _name, _vf_token)		\
FIXTURE_VARIANT_ADD(vfio_pci_sriov_uapi_test, _iommu_mode ## _ ## _name) {	\
	.iommu_mode = #_iommu_mode,						\
	.vf_token = (_vf_token),						\
}

FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(same_uuid, UUID_1);
FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(diff_uuid, UUID_2);
FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(null_uuid, NULL);

FIXTURE_SETUP(vfio_pci_sriov_uapi_test)
{
	self->iommu = iommu_init(variant->iommu_mode);

	self->pf_token = UUID_1;
	ASSERT_EQ(device_init(pf_bdf, self->iommu, self->pf_token, &self->pf), 0);
}

FIXTURE_TEARDOWN(vfio_pci_sriov_uapi_test)
{
	device_cleanup(self->vf);
	device_cleanup(self->pf);
	iommu_cleanup(self->iommu);
}

/*
 * This asserts if the VF device is successfully created if its token matches
 * with the token used to create/override the PF or fails during a mismatch.
 */
#define ASSERT_COND_VF_CREATION(_ret) do {					\
	if (!variant->vf_token || strcmp(self->pf_token, variant->vf_token)) {	\
		ASSERT_NE((_ret), 0);						\
	} else {								\
		ASSERT_EQ((_ret), 0);						\
	}									\
} while (0)

/*
 * Validate if the UAPI handles correctly and incorrectly set token on the VF.
 */
TEST_F(vfio_pci_sriov_uapi_test, init_token_match)
{
	int ret;

	ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf);
	ASSERT_COND_VF_CREATION(ret);
}

/*
 * After closing the PF, validate if the VF access still needs the right token.
 */
TEST_F(vfio_pci_sriov_uapi_test, pf_early_close)
{
	int ret;

	device_cleanup(self->pf);

	/* Clean the 'pf' to avoid calling device_cleanup() again. */
	self->pf = NULL;

	ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf);
	ASSERT_COND_VF_CREATION(ret);
}

/*
 * After PF device init, override the existing token and validate if the newly
 * set token is the one that's active.
 */
TEST_F(vfio_pci_sriov_uapi_test, override_token)
{
	int ret;

	self->pf_token = UUID_2;
	vfio_device_set_vf_token(self->pf->fd, self->pf_token);

	ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf);
	ASSERT_COND_VF_CREATION(ret);
}

static void vf_teardown(void)
{
	/*
	 * The child processes, created by TEST_F()s, inherits this atexit()
	 * handler. Hence, check and destroy the VF only when the main/parent
	 * process exits.
	 */
	if (getpid() != main_pid)
		return;

	free(vf_bdf);
	sysfs_sriov_numvfs_set(pf_bdf, 0);
}

static void vf_setup(void)
{
	char *vf_driver;
	int nr_vfs;

	nr_vfs = sysfs_sriov_totalvfs_get(pf_bdf);
	if (nr_vfs <= 0)
		ksft_exit_skip("SR-IOV may not be supported by the PF: %s\n", pf_bdf);

	nr_vfs = sysfs_sriov_numvfs_get(pf_bdf);
	if (nr_vfs != 0)
		ksft_exit_skip("SR-IOV already configured for the PF: %s\n", pf_bdf);

	/* Create only one VF for testing */
	sysfs_sriov_numvfs_set(pf_bdf, 1);

	/*
	 * Setup an exit handler to destroy the VF in case of failures
	 * during further setup at the end of the test run.
	 */
	main_pid = getpid();
	VFIO_ASSERT_EQ(atexit(vf_teardown), 0);

	vf_bdf = sysfs_sriov_vf_bdf_get(pf_bdf, 0);

	/*
	 * The VF inherits the driver from the PF.
	 * Ensure this is 'vfio-pci' before proceeding.
	 */
	vf_driver = sysfs_driver_get(vf_bdf);
	VFIO_ASSERT_NE(vf_driver, NULL);
	VFIO_ASSERT_EQ(strcmp(vf_driver, "vfio-pci"), 0);
	free(vf_driver);

	printf("Created 1 VF (%s) under the PF: %s\n", vf_bdf, pf_bdf);
}

int main(int argc, char *argv[])
{
	pf_bdf = vfio_selftests_get_bdf(&argc, argv);
	vf_setup();

	return test_harness_run(argc, argv);
}