Skip to content

Commit 3a3329a

Browse files
committed
iommufd: Add iommufd_group
When the hwpt to device attachment is fairly static we could get away with the simple approach of keeping track of the groups via a device list. But with replace this is infeasible. Add an automatically managed struct that is 1:1 with the iommu_group per-ictx so we can store the necessary tracking information there. Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Lu Baolu <[email protected]> Reviewed-by: Kevin Tian <[email protected]> Tested-by: Nicolin Chen <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent d525a5b commit 3a3329a

File tree

3 files changed

+137
-18
lines changed

3 files changed

+137
-18
lines changed

drivers/iommu/iommufd/device.c

Lines changed: 127 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,121 @@ MODULE_PARM_DESC(
1515
"Allow IOMMUFD to bind to devices even if the platform cannot isolate "
1616
"the MSI interrupt window. Enabling this is a security weakness.");
1717

18+
static void iommufd_group_release(struct kref *kref)
19+
{
20+
struct iommufd_group *igroup =
21+
container_of(kref, struct iommufd_group, ref);
22+
23+
xa_cmpxchg(&igroup->ictx->groups, iommu_group_id(igroup->group), igroup,
24+
NULL, GFP_KERNEL);
25+
iommu_group_put(igroup->group);
26+
kfree(igroup);
27+
}
28+
29+
static void iommufd_put_group(struct iommufd_group *group)
30+
{
31+
kref_put(&group->ref, iommufd_group_release);
32+
}
33+
34+
static bool iommufd_group_try_get(struct iommufd_group *igroup,
35+
struct iommu_group *group)
36+
{
37+
if (!igroup)
38+
return false;
39+
/*
40+
* group ID's cannot be re-used until the group is put back which does
41+
* not happen if we could get an igroup pointer under the xa_lock.
42+
*/
43+
if (WARN_ON(igroup->group != group))
44+
return false;
45+
return kref_get_unless_zero(&igroup->ref);
46+
}
47+
48+
/*
49+
* iommufd needs to store some more data for each iommu_group, we keep a
50+
* parallel xarray indexed by iommu_group id to hold this instead of putting it
51+
* in the core structure. To keep things simple the iommufd_group memory is
52+
* unique within the iommufd_ctx. This makes it easy to check there are no
53+
* memory leaks.
54+
*/
55+
static struct iommufd_group *iommufd_get_group(struct iommufd_ctx *ictx,
56+
struct device *dev)
57+
{
58+
struct iommufd_group *new_igroup;
59+
struct iommufd_group *cur_igroup;
60+
struct iommufd_group *igroup;
61+
struct iommu_group *group;
62+
unsigned int id;
63+
64+
group = iommu_group_get(dev);
65+
if (!group)
66+
return ERR_PTR(-ENODEV);
67+
68+
id = iommu_group_id(group);
69+
70+
xa_lock(&ictx->groups);
71+
igroup = xa_load(&ictx->groups, id);
72+
if (iommufd_group_try_get(igroup, group)) {
73+
xa_unlock(&ictx->groups);
74+
iommu_group_put(group);
75+
return igroup;
76+
}
77+
xa_unlock(&ictx->groups);
78+
79+
new_igroup = kzalloc(sizeof(*new_igroup), GFP_KERNEL);
80+
if (!new_igroup) {
81+
iommu_group_put(group);
82+
return ERR_PTR(-ENOMEM);
83+
}
84+
85+
kref_init(&new_igroup->ref);
86+
/* group reference moves into new_igroup */
87+
new_igroup->group = group;
88+
89+
/*
90+
* The ictx is not additionally refcounted here becase all objects using
91+
* an igroup must put it before their destroy completes.
92+
*/
93+
new_igroup->ictx = ictx;
94+
95+
/*
96+
* We dropped the lock so igroup is invalid. NULL is a safe and likely
97+
* value to assume for the xa_cmpxchg algorithm.
98+
*/
99+
cur_igroup = NULL;
100+
xa_lock(&ictx->groups);
101+
while (true) {
102+
igroup = __xa_cmpxchg(&ictx->groups, id, cur_igroup, new_igroup,
103+
GFP_KERNEL);
104+
if (xa_is_err(igroup)) {
105+
xa_unlock(&ictx->groups);
106+
iommufd_put_group(new_igroup);
107+
return ERR_PTR(xa_err(igroup));
108+
}
109+
110+
/* new_group was successfully installed */
111+
if (cur_igroup == igroup) {
112+
xa_unlock(&ictx->groups);
113+
return new_igroup;
114+
}
115+
116+
/* Check again if the current group is any good */
117+
if (iommufd_group_try_get(igroup, group)) {
118+
xa_unlock(&ictx->groups);
119+
iommufd_put_group(new_igroup);
120+
return igroup;
121+
}
122+
cur_igroup = igroup;
123+
}
124+
}
125+
18126
void iommufd_device_destroy(struct iommufd_object *obj)
19127
{
20128
struct iommufd_device *idev =
21129
container_of(obj, struct iommufd_device, obj);
22130

23131
iommu_device_release_dma_owner(idev->dev);
24-
iommu_group_put(idev->group);
132+
iommufd_put_group(idev->igroup);
25133
if (!iommufd_selftest_is_mock_dev(idev->dev))
26134
iommufd_ctx_put(idev->ictx);
27135
}
@@ -46,7 +154,7 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
46154
struct device *dev, u32 *id)
47155
{
48156
struct iommufd_device *idev;
49-
struct iommu_group *group;
157+
struct iommufd_group *igroup;
50158
int rc;
51159

52160
/*
@@ -56,9 +164,9 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
56164
if (!device_iommu_capable(dev, IOMMU_CAP_CACHE_COHERENCY))
57165
return ERR_PTR(-EINVAL);
58166

59-
group = iommu_group_get(dev);
60-
if (!group)
61-
return ERR_PTR(-ENODEV);
167+
igroup = iommufd_get_group(ictx, dev);
168+
if (IS_ERR(igroup))
169+
return ERR_CAST(igroup);
62170

63171
/*
64172
* For historical compat with VFIO the insecure interrupt path is
@@ -67,7 +175,7 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
67175
* interrupt outside this iommufd context.
68176
*/
69177
if (!iommufd_selftest_is_mock_dev(dev) &&
70-
!iommu_group_has_isolated_msi(group)) {
178+
!iommu_group_has_isolated_msi(igroup->group)) {
71179
if (!allow_unsafe_interrupts) {
72180
rc = -EPERM;
73181
goto out_group_put;
@@ -97,8 +205,8 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
97205
device_iommu_capable(dev, IOMMU_CAP_ENFORCE_CACHE_COHERENCY);
98206
/* The calling driver is a user until iommufd_device_unbind() */
99207
refcount_inc(&idev->obj.users);
100-
/* group refcount moves into iommufd_device */
101-
idev->group = group;
208+
/* igroup refcount moves into iommufd_device */
209+
idev->igroup = igroup;
102210

103211
/*
104212
* If the caller fails after this success it must call
@@ -113,7 +221,7 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
113221
out_release_owner:
114222
iommu_device_release_dma_owner(dev);
115223
out_group_put:
116-
iommu_group_put(group);
224+
iommufd_put_group(igroup);
117225
return ERR_PTR(rc);
118226
}
119227
EXPORT_SYMBOL_NS_GPL(iommufd_device_bind, IOMMUFD);
@@ -138,7 +246,8 @@ bool iommufd_ctx_has_group(struct iommufd_ctx *ictx, struct iommu_group *group)
138246
xa_lock(&ictx->objects);
139247
xa_for_each(&ictx->objects, index, obj) {
140248
if (obj->type == IOMMUFD_OBJ_DEVICE &&
141-
container_of(obj, struct iommufd_device, obj)->group == group) {
249+
container_of(obj, struct iommufd_device, obj)
250+
->igroup->group == group) {
142251
xa_unlock(&ictx->objects);
143252
return true;
144253
}
@@ -212,14 +321,14 @@ static int iommufd_device_setup_msi(struct iommufd_device *idev,
212321
}
213322

214323
static bool iommufd_hw_pagetable_has_group(struct iommufd_hw_pagetable *hwpt,
215-
struct iommu_group *group)
324+
struct iommufd_group *igroup)
216325
{
217326
struct iommufd_device *cur_dev;
218327

219328
lockdep_assert_held(&hwpt->devices_lock);
220329

221330
list_for_each_entry(cur_dev, &hwpt->devices, devices_item)
222-
if (cur_dev->group == group)
331+
if (cur_dev->igroup->group == igroup->group)
223332
return true;
224333
return false;
225334
}
@@ -253,7 +362,8 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
253362
}
254363

255364
rc = iopt_table_enforce_group_resv_regions(&hwpt->ioas->iopt, idev->dev,
256-
idev->group, &sw_msi_start);
365+
idev->igroup->group,
366+
&sw_msi_start);
257367
if (rc)
258368
return rc;
259369

@@ -265,8 +375,8 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
265375
* FIXME: Hack around missing a device-centric iommu api, only attach to
266376
* the group once for the first device that is in the group.
267377
*/
268-
if (!iommufd_hw_pagetable_has_group(hwpt, idev->group)) {
269-
rc = iommu_attach_group(hwpt->domain, idev->group);
378+
if (!iommufd_hw_pagetable_has_group(hwpt, idev->igroup)) {
379+
rc = iommu_attach_group(hwpt->domain, idev->igroup->group);
270380
if (rc)
271381
goto err_unresv;
272382
}
@@ -279,8 +389,8 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
279389
void iommufd_hw_pagetable_detach(struct iommufd_hw_pagetable *hwpt,
280390
struct iommufd_device *idev)
281391
{
282-
if (!iommufd_hw_pagetable_has_group(hwpt, idev->group))
283-
iommu_detach_group(hwpt->domain, idev->group);
392+
if (!iommufd_hw_pagetable_has_group(hwpt, idev->igroup))
393+
iommu_detach_group(hwpt->domain, idev->igroup->group);
284394
iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
285395
}
286396

drivers/iommu/iommufd/iommufd_private.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ struct iommufd_device;
1717
struct iommufd_ctx {
1818
struct file *file;
1919
struct xarray objects;
20+
struct xarray groups;
2021

2122
u8 account_mode;
2223
/* Compatibility with VFIO no iommu */
@@ -262,6 +263,12 @@ void iommufd_hw_pagetable_detach(struct iommufd_hw_pagetable *hwpt,
262263
struct iommufd_device *idev);
263264
void iommufd_hw_pagetable_destroy(struct iommufd_object *obj);
264265

266+
struct iommufd_group {
267+
struct kref ref;
268+
struct iommufd_ctx *ictx;
269+
struct iommu_group *group;
270+
};
271+
265272
/*
266273
* A iommufd_device object represents the binding relationship between a
267274
* consuming driver and the iommufd. These objects are created/destroyed by
@@ -270,12 +277,12 @@ void iommufd_hw_pagetable_destroy(struct iommufd_object *obj);
270277
struct iommufd_device {
271278
struct iommufd_object obj;
272279
struct iommufd_ctx *ictx;
280+
struct iommufd_group *igroup;
273281
struct iommufd_hw_pagetable *hwpt;
274282
/* Head at iommufd_hw_pagetable::devices */
275283
struct list_head devices_item;
276284
/* always the physical device */
277285
struct device *dev;
278-
struct iommu_group *group;
279286
bool enforce_cache_coherency;
280287
};
281288

drivers/iommu/iommufd/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ static int iommufd_fops_open(struct inode *inode, struct file *filp)
183183
}
184184

185185
xa_init_flags(&ictx->objects, XA_FLAGS_ALLOC1 | XA_FLAGS_ACCOUNT);
186+
xa_init(&ictx->groups);
186187
ictx->file = filp;
187188
filp->private_data = ictx;
188189
return 0;
@@ -218,6 +219,7 @@ static int iommufd_fops_release(struct inode *inode, struct file *filp)
218219
if (WARN_ON(!destroyed))
219220
break;
220221
}
222+
WARN_ON(!xa_empty(&ictx->groups));
221223
kfree(ictx);
222224
return 0;
223225
}

0 commit comments

Comments
 (0)