Skip to content

Commit b7d8833

Browse files
LuBaolujgunthorpe
authored andcommitted
iommufd: Fault-capable hwpt attach/detach/replace
Add iopf-capable hw page table attach/detach/replace helpers. The pointer to iommufd_device is stored in the domain attachment handle, so that it can be echo'ed back in the iopf_group. The iopf-capable hw page tables can only be attached to devices that support the IOMMU_DEV_FEAT_IOPF feature. On the first attachment of an iopf-capable hw_pagetable to the device, the IOPF feature is enabled on the device. Similarly, after the last iopf-capable hwpt is detached from the device, the IOPF feature is disabled on the device. The current implementation allows a replacement between iopf-capable and non-iopf-capable hw page tables. This matches the nested translation use case, where a parent domain is attached by default and can then be replaced with a nested user domain with iopf support. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Lu Baolu <[email protected]> Reviewed-by: Kevin Tian <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 07838f7 commit b7d8833

File tree

3 files changed

+235
-3
lines changed

3 files changed

+235
-3
lines changed

drivers/iommu/iommufd/device.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
215215
refcount_inc(&idev->obj.users);
216216
/* igroup refcount moves into iommufd_device */
217217
idev->igroup = igroup;
218+
mutex_init(&idev->iopf_lock);
218219

219220
/*
220221
* If the caller fails after this success it must call
@@ -376,7 +377,7 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
376377
* attachment.
377378
*/
378379
if (list_empty(&idev->igroup->device_list)) {
379-
rc = iommu_attach_group(hwpt->domain, idev->igroup->group);
380+
rc = iommufd_hwpt_attach_device(hwpt, idev);
380381
if (rc)
381382
goto err_unresv;
382383
idev->igroup->hwpt = hwpt;
@@ -402,7 +403,7 @@ iommufd_hw_pagetable_detach(struct iommufd_device *idev)
402403
mutex_lock(&idev->igroup->lock);
403404
list_del(&idev->group_item);
404405
if (list_empty(&idev->igroup->device_list)) {
405-
iommu_detach_group(hwpt->domain, idev->igroup->group);
406+
iommufd_hwpt_detach_device(hwpt, idev);
406407
idev->igroup->hwpt = NULL;
407408
}
408409
if (hwpt_is_paging(hwpt))
@@ -497,7 +498,7 @@ iommufd_device_do_replace(struct iommufd_device *idev,
497498
goto err_unlock;
498499
}
499500

500-
rc = iommu_group_replace_domain(igroup->group, hwpt->domain);
501+
rc = iommufd_hwpt_replace_device(idev, hwpt, old_hwpt);
501502
if (rc)
502503
goto err_unresv;
503504

drivers/iommu/iommufd/fault.c

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,203 @@
88
#include <linux/module.h>
99
#include <linux/mutex.h>
1010
#include <linux/iommufd.h>
11+
#include <linux/pci.h>
1112
#include <linux/poll.h>
1213
#include <linux/anon_inodes.h>
1314
#include <uapi/linux/iommufd.h>
1415

1516
#include "../iommu-priv.h"
1617
#include "iommufd_private.h"
1718

19+
static int iommufd_fault_iopf_enable(struct iommufd_device *idev)
20+
{
21+
struct device *dev = idev->dev;
22+
int ret;
23+
24+
/*
25+
* Once we turn on PCI/PRI support for VF, the response failure code
26+
* should not be forwarded to the hardware due to PRI being a shared
27+
* resource between PF and VFs. There is no coordination for this
28+
* shared capability. This waits for a vPRI reset to recover.
29+
*/
30+
if (dev_is_pci(dev) && to_pci_dev(dev)->is_virtfn)
31+
return -EINVAL;
32+
33+
mutex_lock(&idev->iopf_lock);
34+
/* Device iopf has already been on. */
35+
if (++idev->iopf_enabled > 1) {
36+
mutex_unlock(&idev->iopf_lock);
37+
return 0;
38+
}
39+
40+
ret = iommu_dev_enable_feature(dev, IOMMU_DEV_FEAT_IOPF);
41+
if (ret)
42+
--idev->iopf_enabled;
43+
mutex_unlock(&idev->iopf_lock);
44+
45+
return ret;
46+
}
47+
48+
static void iommufd_fault_iopf_disable(struct iommufd_device *idev)
49+
{
50+
mutex_lock(&idev->iopf_lock);
51+
if (!WARN_ON(idev->iopf_enabled == 0)) {
52+
if (--idev->iopf_enabled == 0)
53+
iommu_dev_disable_feature(idev->dev, IOMMU_DEV_FEAT_IOPF);
54+
}
55+
mutex_unlock(&idev->iopf_lock);
56+
}
57+
58+
static int __fault_domain_attach_dev(struct iommufd_hw_pagetable *hwpt,
59+
struct iommufd_device *idev)
60+
{
61+
struct iommufd_attach_handle *handle;
62+
int ret;
63+
64+
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
65+
if (!handle)
66+
return -ENOMEM;
67+
68+
handle->idev = idev;
69+
ret = iommu_attach_group_handle(hwpt->domain, idev->igroup->group,
70+
&handle->handle);
71+
if (ret)
72+
kfree(handle);
73+
74+
return ret;
75+
}
76+
77+
int iommufd_fault_domain_attach_dev(struct iommufd_hw_pagetable *hwpt,
78+
struct iommufd_device *idev)
79+
{
80+
int ret;
81+
82+
if (!hwpt->fault)
83+
return -EINVAL;
84+
85+
ret = iommufd_fault_iopf_enable(idev);
86+
if (ret)
87+
return ret;
88+
89+
ret = __fault_domain_attach_dev(hwpt, idev);
90+
if (ret)
91+
iommufd_fault_iopf_disable(idev);
92+
93+
return ret;
94+
}
95+
96+
static void iommufd_auto_response_faults(struct iommufd_hw_pagetable *hwpt,
97+
struct iommufd_attach_handle *handle)
98+
{
99+
struct iommufd_fault *fault = hwpt->fault;
100+
struct iopf_group *group, *next;
101+
unsigned long index;
102+
103+
if (!fault)
104+
return;
105+
106+
mutex_lock(&fault->mutex);
107+
list_for_each_entry_safe(group, next, &fault->deliver, node) {
108+
if (group->attach_handle != &handle->handle)
109+
continue;
110+
list_del(&group->node);
111+
iopf_group_response(group, IOMMU_PAGE_RESP_INVALID);
112+
iopf_free_group(group);
113+
}
114+
115+
xa_for_each(&fault->response, index, group) {
116+
if (group->attach_handle != &handle->handle)
117+
continue;
118+
xa_erase(&fault->response, index);
119+
iopf_group_response(group, IOMMU_PAGE_RESP_INVALID);
120+
iopf_free_group(group);
121+
}
122+
mutex_unlock(&fault->mutex);
123+
}
124+
125+
static struct iommufd_attach_handle *
126+
iommufd_device_get_attach_handle(struct iommufd_device *idev)
127+
{
128+
struct iommu_attach_handle *handle;
129+
130+
handle = iommu_attach_handle_get(idev->igroup->group, IOMMU_NO_PASID, 0);
131+
if (!handle)
132+
return NULL;
133+
134+
return to_iommufd_handle(handle);
135+
}
136+
137+
void iommufd_fault_domain_detach_dev(struct iommufd_hw_pagetable *hwpt,
138+
struct iommufd_device *idev)
139+
{
140+
struct iommufd_attach_handle *handle;
141+
142+
handle = iommufd_device_get_attach_handle(idev);
143+
iommu_detach_group_handle(hwpt->domain, idev->igroup->group);
144+
iommufd_auto_response_faults(hwpt, handle);
145+
iommufd_fault_iopf_disable(idev);
146+
kfree(handle);
147+
}
148+
149+
static int __fault_domain_replace_dev(struct iommufd_device *idev,
150+
struct iommufd_hw_pagetable *hwpt,
151+
struct iommufd_hw_pagetable *old)
152+
{
153+
struct iommufd_attach_handle *handle, *curr = NULL;
154+
int ret;
155+
156+
if (old->fault)
157+
curr = iommufd_device_get_attach_handle(idev);
158+
159+
if (hwpt->fault) {
160+
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
161+
if (!handle)
162+
return -ENOMEM;
163+
164+
handle->handle.domain = hwpt->domain;
165+
handle->idev = idev;
166+
ret = iommu_replace_group_handle(idev->igroup->group,
167+
hwpt->domain, &handle->handle);
168+
} else {
169+
ret = iommu_replace_group_handle(idev->igroup->group,
170+
hwpt->domain, NULL);
171+
}
172+
173+
if (!ret && curr) {
174+
iommufd_auto_response_faults(old, curr);
175+
kfree(curr);
176+
}
177+
178+
return ret;
179+
}
180+
181+
int iommufd_fault_domain_replace_dev(struct iommufd_device *idev,
182+
struct iommufd_hw_pagetable *hwpt,
183+
struct iommufd_hw_pagetable *old)
184+
{
185+
bool iopf_off = !hwpt->fault && old->fault;
186+
bool iopf_on = hwpt->fault && !old->fault;
187+
int ret;
188+
189+
if (iopf_on) {
190+
ret = iommufd_fault_iopf_enable(idev);
191+
if (ret)
192+
return ret;
193+
}
194+
195+
ret = __fault_domain_replace_dev(idev, hwpt, old);
196+
if (ret) {
197+
if (iopf_on)
198+
iommufd_fault_iopf_disable(idev);
199+
return ret;
200+
}
201+
202+
if (iopf_off)
203+
iommufd_fault_iopf_disable(idev);
204+
205+
return 0;
206+
}
207+
18208
void iommufd_fault_destroy(struct iommufd_object *obj)
19209
{
20210
struct iommufd_fault *fault = container_of(obj, struct iommufd_fault, obj);

drivers/iommu/iommufd/iommufd_private.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/iommu.h>
1212
#include <linux/iova_bitmap.h>
1313
#include <uapi/linux/iommufd.h>
14+
#include "../iommu-priv.h"
1415

1516
struct iommu_domain;
1617
struct iommu_group;
@@ -293,6 +294,7 @@ int iommufd_check_iova_range(struct io_pagetable *iopt,
293294
struct iommufd_hw_pagetable {
294295
struct iommufd_object obj;
295296
struct iommu_domain *domain;
297+
struct iommufd_fault *fault;
296298
};
297299

298300
struct iommufd_hwpt_paging {
@@ -396,6 +398,9 @@ struct iommufd_device {
396398
/* always the physical device */
397399
struct device *dev;
398400
bool enforce_cache_coherency;
401+
/* protect iopf_enabled counter */
402+
struct mutex iopf_lock;
403+
unsigned int iopf_enabled;
399404
};
400405

401406
static inline struct iommufd_device *
@@ -456,6 +461,42 @@ struct iommufd_attach_handle {
456461
int iommufd_fault_alloc(struct iommufd_ucmd *ucmd);
457462
void iommufd_fault_destroy(struct iommufd_object *obj);
458463

464+
int iommufd_fault_domain_attach_dev(struct iommufd_hw_pagetable *hwpt,
465+
struct iommufd_device *idev);
466+
void iommufd_fault_domain_detach_dev(struct iommufd_hw_pagetable *hwpt,
467+
struct iommufd_device *idev);
468+
int iommufd_fault_domain_replace_dev(struct iommufd_device *idev,
469+
struct iommufd_hw_pagetable *hwpt,
470+
struct iommufd_hw_pagetable *old);
471+
472+
static inline int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
473+
struct iommufd_device *idev)
474+
{
475+
if (hwpt->fault)
476+
return iommufd_fault_domain_attach_dev(hwpt, idev);
477+
478+
return iommu_attach_group(hwpt->domain, idev->igroup->group);
479+
}
480+
481+
static inline void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt,
482+
struct iommufd_device *idev)
483+
{
484+
if (hwpt->fault)
485+
iommufd_fault_domain_detach_dev(hwpt, idev);
486+
487+
iommu_detach_group(hwpt->domain, idev->igroup->group);
488+
}
489+
490+
static inline int iommufd_hwpt_replace_device(struct iommufd_device *idev,
491+
struct iommufd_hw_pagetable *hwpt,
492+
struct iommufd_hw_pagetable *old)
493+
{
494+
if (old->fault || hwpt->fault)
495+
return iommufd_fault_domain_replace_dev(idev, hwpt, old);
496+
497+
return iommu_group_replace_domain(idev->igroup->group, hwpt->domain);
498+
}
499+
459500
#ifdef CONFIG_IOMMUFD_TEST
460501
int iommufd_test(struct iommufd_ucmd *ucmd);
461502
void iommufd_selftest_destroy(struct iommufd_object *obj);

0 commit comments

Comments
 (0)