On 3/27/26 10:52, Zhenzhong Duan wrote:
Propagate guest's PRQ response to host by writing to fault_fd.
Create a new VTDPRQEntry to cache cookie for each fault group,
this cookie is used to mark the fault group on host side.
Signed-off-by: Zhenzhong Duan <[email protected]>
---
hw/i386/intel_iommu_accel.h | 14 ++++++++
include/hw/i386/intel_iommu.h | 6 ++++
hw/i386/intel_iommu.c | 4 +++
hw/i386/intel_iommu_accel.c | 65 +++++++++++++++++++++++++++++++++++
hw/i386/trace-events | 1 +
5 files changed, 90 insertions(+)
diff --git a/hw/i386/intel_iommu_accel.h b/hw/i386/intel_iommu_accel.h
index 10e6ee5722..b46c7126f7 100644
--- a/hw/i386/intel_iommu_accel.h
+++ b/hw/i386/intel_iommu_accel.h
@@ -19,6 +19,9 @@ typedef struct VTDAccelPASIDCacheEntry {
uint32_t fs_hwpt_id;
uint32_t fault_id;
int fault_fd;
+ QLIST_HEAD(, VTDPRQEntry) vtd_prq_list;
+ IOMMUPRINotifier pri_notifier_entry;
+ IOMMUPRINotifier *pri_notifier;
QLIST_ENTRY(VTDAccelPASIDCacheEntry) next;
} VTDAccelPASIDCacheEntry;
@@ -31,6 +34,9 @@ void vtd_flush_host_piotlb_all_accel(IntelIOMMUState *s, uint16_t domain_id,
uint64_t npages, bool ih);
void vtd_pasid_cache_sync_accel(IntelIOMMUState *s, VTDPASIDCacheInfo
*pc_info);
void vtd_pasid_cache_reset_accel(IntelIOMMUState *s);
+bool vtd_propagate_page_group_response_accel(IntelIOMMUState *s,
+ uint16_t rid, uint32_t pasid,
+ IOMMUPRIResponse *response);
void vtd_iommu_ops_update_accel(PCIIOMMUOps *ops);
#else
static inline bool vtd_check_hiod_accel(IntelIOMMUState *s,
@@ -69,6 +75,14 @@ static inline void
vtd_pasid_cache_reset_accel(IntelIOMMUState *s)
{
}
+static inline
+bool vtd_propagate_page_group_response_accel(IntelIOMMUState *s,
+ uint16_t rid, uint32_t pasid,
+ IOMMUPRIResponse *response)
+{
+ return false;
+}
+
static inline void vtd_iommu_ops_update_accel(PCIIOMMUOps *ops)
{
}
diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h
index 1842ba5840..5d44eac0ed 100644
--- a/include/hw/i386/intel_iommu.h
+++ b/include/hw/i386/intel_iommu.h
@@ -100,6 +100,12 @@ typedef struct VTDPASIDCacheEntry {
bool valid;
} VTDPASIDCacheEntry;
+typedef struct VTDPRQEntry {
+ uint32_t grpid;
+ uint32_t cookie;
+ QLIST_ENTRY(VTDPRQEntry) next;
+} VTDPRQEntry;
+
struct VTDAddressSpace {
PCIBus *bus;
uint8_t devfn;
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 96b4102ab9..d670a0377b 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -3390,6 +3390,10 @@ static bool
vtd_process_page_group_response_desc(IntelIOMMUState *s,
response.response_code = IOMMU_PRI_RESP_FAILURE;
}
+ if (vtd_propagate_page_group_response_accel(s, rid, pasid, &response)) {
+ return true;
+ }
+
if (vtd_dev_as->pri_notifier) {
vtd_dev_as->pri_notifier->notify(vtd_dev_as->pri_notifier, &response);
}
diff --git a/hw/i386/intel_iommu_accel.c b/hw/i386/intel_iommu_accel.c
index 0fce62ff75..44af534c55 100644
--- a/hw/i386/intel_iommu_accel.c
+++ b/hw/i386/intel_iommu_accel.c
@@ -102,6 +102,30 @@ VTDHostIOMMUDevice *vtd_find_hiod_iommufd(VTDAddressSpace
*as)
return NULL;
}
+bool vtd_propagate_page_group_response_accel(IntelIOMMUState *s,
+ uint16_t rid, uint32_t pasid,
+ IOMMUPRIResponse *response)
s/vtd_propagate_page_group_response_accel/vtd_accel_propagate_page_group_response/
+{
+ VTDAddressSpace *vtd_as = vtd_get_as_by_sid(s, rid);
+ VTDAccelPASIDCacheEntry *vtd_pce;
+ VTDHostIOMMUDevice *vtd_hiod = vtd_find_hiod_iommufd(vtd_as);
+
+ if (!vtd_hiod) {
+ return false;
+ }
+
+ QLIST_FOREACH(vtd_pce, &vtd_hiod->pasid_cache_list, next) {
+ if (vtd_pce->pasid == pasid) {
+ if (vtd_pce->pri_notifier) {
+ vtd_pce->pri_notifier->notify(vtd_pce->pri_notifier, response);
is the notify mechanism really needed? You already have vtd_pce here
which is the main input that needed by vtd_prq_response_notify().
You might call vtd_prq_response_notify() directly here since it's
internal helper?
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
static void vtd_prq_report_fault(VTDAccelPASIDCacheEntry *vtd_pce,
struct iommu_hwpt_pgfault *fault, int cnt)
{
@@ -117,6 +141,13 @@ static void vtd_prq_report_fault(VTDAccelPASIDCacheEntry
*vtd_pce,
fault->addr, last_page, fault->grpid,
fault->perm & IOMMU_PGFAULT_PERM_READ,
fault->perm & IOMMU_PGFAULT_PERM_WRITE);
+ if (last_page) {
+ VTDPRQEntry *prqe = g_malloc0(sizeof(*prqe));
+
+ prqe->grpid = fault->grpid;
+ prqe->cookie = fault->cookie;
+ QLIST_INSERT_HEAD(&vtd_pce->vtd_prq_list, prqe, next);
+ }
}
}
@@ -150,6 +181,36 @@ static void vtd_prq_read_fault(void *opaque)
vtd_prq_report_fault(vtd_pce, fault, bytes / sizeof(fault[0]));
}
+static void vtd_prq_response_notify(struct IOMMUPRINotifier *notifier,
+ IOMMUPRIResponse *response)
+{
+ VTDAccelPASIDCacheEntry *vtd_pce =
+ container_of(notifier, VTDAccelPASIDCacheEntry, pri_notifier_entry);
+ uint32_t id = vtd_pce->fault_id, fd = vtd_pce->fault_fd;
+ struct iommu_hwpt_page_response resp;
+ VTDPRQEntry *prqe, *tmp;
+ ssize_t bytes;
+
+ QLIST_FOREACH_SAFE(prqe, &vtd_pce->vtd_prq_list, next, tmp) {
+ if (prqe->grpid != response->prgi) {
+ continue;
+ }
+
+ resp.cookie = prqe->cookie;
+ resp.code = response->response_code;
+ bytes = write(fd, &resp, sizeof(resp));
+ trace_vtd_prq_response_notify(id, fd, resp.cookie, resp.code, bytes);
+ if (bytes < 0) {
+ error_report_once("FAULTQ(id %u): write failed "
+ "[cookie 0x%x code 0x%x] (%m)",
+ id, resp.cookie, resp.code);
+ }
+
+ QLIST_REMOVE(prqe, next);
+ g_free(prqe);
+ }
+}
+
static void vtd_destroy_fs_faultq(VTDHostIOMMUDevice *vtd_hiod,
uint32_t fault_id, uint32_t fault_fd)
{
@@ -213,6 +274,7 @@ static void vtd_destroy_old_fs_faultq(VTDHostIOMMUDevice
*vtd_hiod,
return;
}
+ vtd_pce->pri_notifier = NULL;
qemu_set_fd_handler(vtd_pce->fault_fd, NULL, NULL, NULL);
vtd_destroy_fs_faultq(vtd_hiod, vtd_pce->fault_id, vtd_pce->fault_fd);
vtd_pce->fault_id = 0;
@@ -228,6 +290,8 @@ static void vtd_setup_fs_faultq(VTDAccelPASIDCacheEntry
*vtd_pce,
vtd_pce->fault_id = fault_id;
vtd_pce->fault_fd = fault_fd;
+ vtd_pce->pri_notifier_entry.notify = vtd_prq_response_notify;
+ vtd_pce->pri_notifier = &vtd_pce->pri_notifier_entry;
qemu_set_fd_handler(fault_fd, vtd_prq_read_fault, NULL, vtd_pce);
}
@@ -492,6 +556,7 @@ static void vtd_accel_fill_pc(VTDHostIOMMUDevice *vtd_hiod, uint32_t pasid,
vtd_pce->vtd_hiod = vtd_hiod;
vtd_pce->pasid = pasid;
vtd_pce->pasid_entry = *pe;
+ QLIST_INIT(&vtd_pce->vtd_prq_list);
QLIST_INSERT_HEAD(&vtd_hiod->pasid_cache_list, vtd_pce, next);
if (!vtd_device_attach_iommufd(vtd_pce, &local_err)) {
diff --git a/hw/i386/trace-events b/hw/i386/trace-events
index bf139338f7..52dab0b508 100644
--- a/hw/i386/trace-events
+++ b/hw/i386/trace-events
@@ -78,6 +78,7 @@ vtd_device_attach_hwpt(uint32_t dev_id, uint32_t pasid,
uint32_t hwpt_id, int re
vtd_device_detach_hwpt(uint32_t dev_id, uint32_t pasid, int ret) "dev_id %d pasid
%d ret: %d"
vtd_device_reattach_def_hwpt(uint32_t dev_id, uint32_t pasid, uint32_t hwpt_id, int
ret) "dev_id %d pasid %d hwpt_id %d, ret: %d"
vtd_prq_read_fault(uint32_t fault_id, uint32_t fault_fd, ssize_t bytes) "fault_id
%d fault_fd %d ret: %zd"
+vtd_prq_response_notify(uint32_t fault_id, uint32_t fault_fd, uint32_t cookie, uint32_t
code, ssize_t bytes) "fault_id %d fault_fd %d cookie %d code %d ret: %zd"
# amd_iommu.c
amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr
0x%"PRIx64" + offset 0x%"PRIx32