[SPDK] [PATCH] vfio_user: fix the errors in 64K page kernel

Huang Shijie <[email protected]> Fri, 9 Jun 2023 11:25:51 +0800
Newsgroups dev.linux.lists.spdk
Message-ID <[email protected]>
We will meet the followings errors in 64K page kernel:
    " vfio_user.c:4454:nvmf_vfio_user_listen: *ERROR*: /tmp/nvme-vfio-user: error to mmap file /tmp/nvme-vfio-user/bar0: Invalid argument."
    " vfio_user.c:3088:vfio_user_log: *ERROR*: /tmp/nvme-vfio-user: migration registers cannot be memory mapped"

Fix them by
    0.) Pass 0 to mmap's offset parameter.
	The doorbell offset is fixed at 0x1000 (kernel NVME driver uses it too).
	But mmap requires the offset must be a multiple of the page size as returned by
	sysconf(_SC_PAGE_SIZE).
	In 64K page size kernel will meet the failure. So set 0 to mmap's offset,
       	and then change to doorbell offset manually.

    1.) convert the hardcode to PAGE_SIZE.

Tested this patch with cloud-hypervisor in 64K page size kernel.

Signed-off-by: Huang Shijie <[email protected]>
---
 lib/nvmf/vfio_user.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/lib/nvmf/vfio_user.c b/lib/nvmf/vfio_user.c
index 2c05a72bd..22ade6f03 100644
--- a/lib/nvmf/vfio_user.c
+++ b/lib/nvmf/vfio_user.c
@@ -1149,7 +1149,8 @@ nvmf_vfio_user_destroy_endpoint(struct nvmf_vfio_user_endpoint *endpoint)
 	spdk_poller_unregister(&endpoint->accept_poller);
 
 	if (endpoint->bar0_doorbells) {
-		munmap((void *)endpoint->bar0_doorbells, NVMF_VFIO_USER_DOORBELLS_SIZE);
+		endpoint->bar0_doorbells = (uint32_t*)(((unsigned long)endpoint->bar0_doorbells) - NVME_DOORBELLS_OFFSET);
+		munmap((void *)endpoint->bar0_doorbells, NVMF_VFIO_USER_DOORBELLS_SIZE + NVME_DOORBELLS_OFFSET);
 	}
 
 	if (endpoint->devmem_fd > 0) {
@@ -4202,7 +4203,7 @@ vfio_user_dev_info_fill(struct nvmf_vfio_user_transport *vu_transport,
 
 	vfu_setup_device_quiesce_cb(vfu_ctx, vfio_user_dev_quiesce_cb);
 
-	migr_sparse_mmap.iov_base = (void *)4096;
+	migr_sparse_mmap.iov_base = (void *)PAGE_SIZE;
 	migr_sparse_mmap.iov_len = vfio_user_migr_data_len();
 	ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_MIGR_REGION_IDX,
 			       vfu_get_migr_register_area_size() + vfio_user_migr_data_len(),
@@ -4448,14 +4449,22 @@ nvmf_vfio_user_listen(struct spdk_nvmf_transport *transport,
 		goto out;
 	}
 
-	endpoint->bar0_doorbells = mmap(NULL, NVMF_VFIO_USER_DOORBELLS_SIZE,
-					PROT_READ | PROT_WRITE, MAP_SHARED, endpoint->devmem_fd, NVME_DOORBELLS_OFFSET);
+	/*
+	 * The doorbell offset is fixed at 0x1000.
+	 * But mmap requires the offset must be a multiple of the page size as returned by
+	 * sysconf(_SC_PAGE_SIZE).
+	 * In order to avoid the mmap failure in non-4K page size kernel,
+	 * set 0 to mmap's offset, and then change to doorbell offset manually.
+	 */
+	endpoint->bar0_doorbells = mmap(NULL, NVMF_VFIO_USER_DOORBELLS_SIZE + NVME_DOORBELLS_OFFSET,
+				PROT_READ | PROT_WRITE, MAP_SHARED, endpoint->devmem_fd, 0);
 	if (endpoint->bar0_doorbells == MAP_FAILED) {
 		SPDK_ERRLOG("%s: error to mmap file %s: %s.\n", endpoint_id(endpoint), path, spdk_strerror(errno));
 		endpoint->bar0_doorbells = NULL;
 		ret = -1;
 		goto out;
 	}
+	endpoint->bar0_doorbells = (uint32_t *)(((unsigned long)endpoint->bar0_doorbells) + NVME_DOORBELLS_OFFSET);
 
 	ret = snprintf(path, PATH_MAX, "%s/migr", endpoint_id(endpoint));
 	if (ret < 0 || ret >= PATH_MAX) {
-- 
2.39.2