From nobody Mon Feb 9 17:35:28 2026 Received: from mx0b-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 94F4325DAEA; Tue, 11 Nov 2025 18:48:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=67.231.153.30 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762886936; cv=none; b=LqGq61T8lcCgOb48B2ntsaSY9Zv6qMBgvBOGm6+kGJQ2KAPPU92LUAPrpmv/gvE3iQxdSp1dVzTGKhUpqm1JgEyH2KypSprclgXsYwzWIXd/LNDnc/SE649HGeaCI0909SdVww+JKWT2Fwp/9tHQKIM8MEWTeh1DrONLoIjijHQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762886936; c=relaxed/simple; bh=qGSVtTdIQfwYImmAqfekJpkI03YVXdvBPZgCprnZIjY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=byefJp9cXHvABK2aTTtL98LlPSULJFsqyd/WZbyS17BJklc/HzhraUtkovIvSCNOrYNO34RhXQz16TGB9QRa8eWf0VF7tZbVKIBASlzxng5z5/NTe700cRhoadSpt1G3kBuciqKnSdyNzoDlk/n7Wzn54SYxx5m6xJweQaJKqJ0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=fb.com; spf=pass smtp.mailfrom=meta.com; dkim=pass (2048-bit key) header.d=fb.com header.i=@fb.com header.b=pKzoYXpN; arc=none smtp.client-ip=67.231.153.30 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=fb.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=meta.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=fb.com header.i=@fb.com header.b="pKzoYXpN" Received: from pps.filterd (m0148460.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 5ABGKpnQ2601076; Tue, 11 Nov 2025 10:48:47 -0800 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fb.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=s2048-2025-q2; bh=jxZzy80bQh9jauTcZWOpfobeqFyunTSAHfyArvzcsfQ=; b=pKzoYXpNdMkY QdPdbsCOSJ5OfOsdyQtLWdzq3c9mAT2Sm46YitYra2hQM9OSIrGjQdVKaoZCCT5O 7SQ0x2oAjtkyPa9xxokHauLSUYbgxPL0aEOxLKlSkr9d2juRqdtSK4MZsI2tSy9V 6ZNwH/BnRJ7ekrt8KAvu+jY+rCwsZ+3ZMYWwDoXoVAaXjn11UyFzmidGNQTzwm8R ipWOqKc7KCmjwAA78Omv6VGyCJWLCNdAvsmHnBdrGlFjtzlUcyuQg1ykP5Qv/MFF JICJh35XCyuucYT/Ko0p5IrmgNqkP/gfoqwE+RD454QOMitsTq9Fxs5dnyVyE98A lsx5IO3J5A== Received: from maileast.thefacebook.com ([163.114.135.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 4ac8k51cdm-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT); Tue, 11 Nov 2025 10:48:47 -0800 (PST) Received: from devgpu015.cco6.facebook.com (2620:10d:c0a8:fe::f072) by mail.thefacebook.com (2620:10d:c0a9:6f::237c) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.2.2562.20; Tue, 11 Nov 2025 18:48:34 +0000 From: Alex Mastro Date: Tue, 11 Nov 2025 10:48:24 -0800 Subject: [PATCH v3 1/4] vfio: selftests: add iova range query helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20251111-iova-ranges-v3-1-7960244642c5@fb.com> References: <20251111-iova-ranges-v3-0-7960244642c5@fb.com> In-Reply-To: <20251111-iova-ranges-v3-0-7960244642c5@fb.com> To: Alex Williamson , David Matlack , Shuah Khan , Jason Gunthorpe CC: , , , Alex Mastro X-Mailer: b4 0.13.0 X-Proofpoint-ORIG-GUID: EG2Na0iztcYGmlahczIhBWa4sO9xAP2D X-Authority-Analysis: v=2.4 cv=X95f6WTe c=1 sm=1 tr=0 ts=6913850f cx=c_pps a=MfjaFnPeirRr97d5FC5oHw==:117 a=MfjaFnPeirRr97d5FC5oHw==:17 a=IkcTkHD0fZMA:10 a=6UeiqGixMTsA:10 a=VkNPw1HP01LnGYTKEx00:22 a=1XWaLZrsAAAA:8 a=FOH2dFAWAAAA:8 a=VzKgKez753p4LaRha9sA:9 a=QEXdDO2ut3YA:10 a=cPQSjfK2_nFv0Q5t_7PE:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUxMTExMDE1MiBTYWx0ZWRfX6xjIMfyhjSWo CSYkBCGrHtbQJx824jbPFk2zVT/JDqrie2qyHgjGdMPCnewJxxYD2teofEyLnSndHlgTQZN7JgR p4ncWgR2FrjcZdNqkEjgcTIY8BXk80hgfBSn2pHsMAPftYeDDqmvz14Z3BwAV+kw/IZZjntcw24 0CdgwSXVn8hzg78gS8LBXmsrWziwMtUd59PBSlf4XvhEZ7VdsfLNgMo0JiEU/k9azuaooKdRD8c aXIwc8VLflcK3y53Y3y6yD4OxatxLXfgHI8AgDnbnlzNki/mSdEOD4JT1ZFpzCuJpprbVKOFVoq 0WFfLPD8fmqQNFE8IQDkYVTa9wVT86KJOuIHkA9IH42S775aPXGM2zJDJxpLR7fO3vMSItbyYVV cOogdPZGzHWvEwnuzmaMNnFBERSBRw== X-Proofpoint-GUID: EG2Na0iztcYGmlahczIhBWa4sO9xAP2D X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1121,Hydra:6.1.9,FMLib:17.12.100.49 definitions=2025-11-11_03,2025-11-11_03,2025-10-01_01 VFIO selftests need to map IOVAs from legally accessible ranges, which could vary between hardware. Tests in vfio_dma_mapping_test.c are making excessively strong assumptions about which IOVAs can be mapped. Add vfio_iommu_iova_ranges(), which queries IOVA ranges from the IOMMUFD or VFIO container associated with the device. The queried ranges are normalized to IOMMUFD's iommu_iova_range representation so that handling of IOVA ranges up the stack can be implementation-agnostic. iommu_iova_range and vfio_iova_range are equivalent, so bias to using the new interface's struct. Query IOMMUFD's ranges with IOMMU_IOAS_IOVA_RANGES. Query VFIO container's ranges with VFIO_IOMMU_GET_INFO and VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE. The underlying vfio_iommu_type1_info buffer-related functionality has been kept generic so the same helpers can be used to query other capability chain information, if needed. Reviewed-by: David Matlack Tested-by: David Matlack Signed-off-by: Alex Mastro --- .../testing/selftests/vfio/lib/include/vfio_util.h | 8 +- tools/testing/selftests/vfio/lib/vfio_pci_device.c | 172 +++++++++++++++++= ++++ 2 files changed, 179 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/vfio/lib/include/vfio_util.h b/tools/t= esting/selftests/vfio/lib/include/vfio_util.h index 240409bf5f8a..ef8f06ef0c13 100644 --- a/tools/testing/selftests/vfio/lib/include/vfio_util.h +++ b/tools/testing/selftests/vfio/lib/include/vfio_util.h @@ -4,9 +4,12 @@ =20 #include #include -#include + +#include +#include #include #include +#include =20 #include "../../../kselftest.h" =20 @@ -206,6 +209,9 @@ struct vfio_pci_device *vfio_pci_device_init(const char= *bdf, const char *iommu_ void vfio_pci_device_cleanup(struct vfio_pci_device *device); void vfio_pci_device_reset(struct vfio_pci_device *device); =20 +struct iommu_iova_range *vfio_pci_iova_ranges(struct vfio_pci_device *devi= ce, + u32 *nranges); + int __vfio_pci_dma_map(struct vfio_pci_device *device, struct vfio_dma_region *region); int __vfio_pci_dma_unmap(struct vfio_pci_device *device, diff --git a/tools/testing/selftests/vfio/lib/vfio_pci_device.c b/tools/tes= ting/selftests/vfio/lib/vfio_pci_device.c index a381fd253aa7..11749348f53f 100644 --- a/tools/testing/selftests/vfio/lib/vfio_pci_device.c +++ b/tools/testing/selftests/vfio/lib/vfio_pci_device.c @@ -29,6 +29,178 @@ VFIO_ASSERT_EQ(__ret, 0, "ioctl(%s, %s, %s) returned %d\n", #_fd, #_op, #= _arg, __ret); \ } while (0) =20 +static struct vfio_info_cap_header *next_cap_hdr(void *buf, u32 bufsz, + u32 *cap_offset) +{ + struct vfio_info_cap_header *hdr; + + if (!*cap_offset) + return NULL; + + VFIO_ASSERT_LT(*cap_offset, bufsz); + VFIO_ASSERT_GE(bufsz - *cap_offset, sizeof(*hdr)); + + hdr =3D (struct vfio_info_cap_header *)((u8 *)buf + *cap_offset); + *cap_offset =3D hdr->next; + + return hdr; +} + +static struct vfio_info_cap_header *vfio_iommu_info_cap_hdr(struct vfio_io= mmu_type1_info *info, + u16 cap_id) +{ + struct vfio_info_cap_header *hdr; + u32 cap_offset =3D info->cap_offset; + u32 max_depth; + u32 depth =3D 0; + + if (!(info->flags & VFIO_IOMMU_INFO_CAPS)) + return NULL; + + if (cap_offset) + VFIO_ASSERT_GE(cap_offset, sizeof(*info)); + + max_depth =3D (info->argsz - sizeof(*info)) / sizeof(*hdr); + + while ((hdr =3D next_cap_hdr(info, info->argsz, &cap_offset))) { + depth++; + VFIO_ASSERT_LE(depth, max_depth, "Capability chain contains a cycle\n"); + + if (hdr->id =3D=3D cap_id) + return hdr; + } + + return NULL; +} + +/* Return buffer including capability chain, if present. Free with free() = */ +static struct vfio_iommu_type1_info *vfio_iommu_get_info(struct vfio_pci_d= evice *device) +{ + struct vfio_iommu_type1_info *info; + + info =3D malloc(sizeof(*info)); + VFIO_ASSERT_NOT_NULL(info); + + *info =3D (struct vfio_iommu_type1_info) { + .argsz =3D sizeof(*info), + }; + + ioctl_assert(device->container_fd, VFIO_IOMMU_GET_INFO, info); + VFIO_ASSERT_GE(info->argsz, sizeof(*info)); + + info =3D realloc(info, info->argsz); + VFIO_ASSERT_NOT_NULL(info); + + ioctl_assert(device->container_fd, VFIO_IOMMU_GET_INFO, info); + VFIO_ASSERT_GE(info->argsz, sizeof(*info)); + + return info; +} + +/* + * Return iova ranges for the device's container. Normalize vfio_iommu_typ= e1 to + * report iommufd's iommu_iova_range. Free with free(). + */ +static struct iommu_iova_range *vfio_iommu_iova_ranges(struct vfio_pci_dev= ice *device, + u32 *nranges) +{ + struct vfio_iommu_type1_info_cap_iova_range *cap_range; + struct vfio_iommu_type1_info *info; + struct vfio_info_cap_header *hdr; + struct iommu_iova_range *ranges =3D NULL; + + info =3D vfio_iommu_get_info(device); + hdr =3D vfio_iommu_info_cap_hdr(info, VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANG= E); + VFIO_ASSERT_NOT_NULL(hdr); + + cap_range =3D container_of(hdr, struct vfio_iommu_type1_info_cap_iova_ran= ge, header); + VFIO_ASSERT_GT(cap_range->nr_iovas, 0); + + ranges =3D calloc(cap_range->nr_iovas, sizeof(*ranges)); + VFIO_ASSERT_NOT_NULL(ranges); + + for (u32 i =3D 0; i < cap_range->nr_iovas; i++) { + ranges[i] =3D (struct iommu_iova_range){ + .start =3D cap_range->iova_ranges[i].start, + .last =3D cap_range->iova_ranges[i].end, + }; + } + + *nranges =3D cap_range->nr_iovas; + + free(info); + return ranges; +} + +/* Return iova ranges of the device's IOAS. Free with free() */ +static struct iommu_iova_range *iommufd_iova_ranges(struct vfio_pci_device= *device, + u32 *nranges) +{ + struct iommu_iova_range *ranges; + int ret; + + struct iommu_ioas_iova_ranges query =3D { + .size =3D sizeof(query), + .ioas_id =3D device->ioas_id, + }; + + ret =3D ioctl(device->iommufd, IOMMU_IOAS_IOVA_RANGES, &query); + VFIO_ASSERT_EQ(ret, -1); + VFIO_ASSERT_EQ(errno, EMSGSIZE); + VFIO_ASSERT_GT(query.num_iovas, 0); + + ranges =3D calloc(query.num_iovas, sizeof(*ranges)); + VFIO_ASSERT_NOT_NULL(ranges); + + query.allowed_iovas =3D (uintptr_t)ranges; + + ioctl_assert(device->iommufd, IOMMU_IOAS_IOVA_RANGES, &query); + *nranges =3D query.num_iovas; + + return ranges; +} + +static int iova_range_comp(const void *a, const void *b) +{ + const struct iommu_iova_range *ra =3D a, *rb =3D b; + + if (ra->start < rb->start) + return -1; + + if (ra->start > rb->start) + return 1; + + return 0; +} + +/* Return sorted IOVA ranges of the device. Free with free(). */ +struct iommu_iova_range *vfio_pci_iova_ranges(struct vfio_pci_device *devi= ce, + u32 *nranges) +{ + struct iommu_iova_range *ranges; + + if (device->iommufd) + ranges =3D iommufd_iova_ranges(device, nranges); + else + ranges =3D vfio_iommu_iova_ranges(device, nranges); + + if (!ranges) + return NULL; + + VFIO_ASSERT_GT(*nranges, 0); + + /* Sort and check that ranges are sane and non-overlapping */ + qsort(ranges, *nranges, sizeof(*ranges), iova_range_comp); + VFIO_ASSERT_LT(ranges[0].start, ranges[0].last); + + for (u32 i =3D 1; i < *nranges; i++) { + VFIO_ASSERT_LT(ranges[i].start, ranges[i].last); + VFIO_ASSERT_LT(ranges[i - 1].last, ranges[i].start); + } + + return ranges; +} + iova_t __to_iova(struct vfio_pci_device *device, void *vaddr) { struct vfio_dma_region *region; --=20 2.47.3