Skip to content

Commit c23b071

Browse files
committed
Merge tag 'vfio-v5.2-rc5' of git://github.com/awilliam/linux-vfio
Pull VFIO fixes from Alex Williamson: "Fix mdev device create/remove paths to provide initialized device for parent driver create callback and correct ordering of device removal from bus prior to initiating removal by parent. Also resolve races between parent removal and device create/remove paths (all from Parav Pandit)" * tag 'vfio-v5.2-rc5' of git://github.com/awilliam/linux-vfio: vfio/mdev: Synchronize device create/remove with parent removal vfio/mdev: Avoid creating sysfs remove file on stale device removal vfio/mdev: Improve the create/remove sequence
2 parents 6fa425a + 5715c4d commit c23b071

File tree

3 files changed

+69
-77
lines changed

3 files changed

+69
-77
lines changed

drivers/vfio/mdev/mdev_core.c

Lines changed: 64 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -102,56 +102,35 @@ static void mdev_put_parent(struct mdev_parent *parent)
102102
kref_put(&parent->ref, mdev_release_parent);
103103
}
104104

105-
static int mdev_device_create_ops(struct kobject *kobj,
106-
struct mdev_device *mdev)
105+
/* Caller must hold parent unreg_sem read or write lock */
106+
static void mdev_device_remove_common(struct mdev_device *mdev)
107107
{
108-
struct mdev_parent *parent = mdev->parent;
109-
int ret;
110-
111-
ret = parent->ops->create(kobj, mdev);
112-
if (ret)
113-
return ret;
114-
115-
ret = sysfs_create_groups(&mdev->dev.kobj,
116-
parent->ops->mdev_attr_groups);
117-
if (ret)
118-
parent->ops->remove(mdev);
119-
120-
return ret;
121-
}
122-
123-
/*
124-
* mdev_device_remove_ops gets called from sysfs's 'remove' and when parent
125-
* device is being unregistered from mdev device framework.
126-
* - 'force_remove' is set to 'false' when called from sysfs's 'remove' which
127-
* indicates that if the mdev device is active, used by VMM or userspace
128-
* application, vendor driver could return error then don't remove the device.
129-
* - 'force_remove' is set to 'true' when called from mdev_unregister_device()
130-
* which indicate that parent device is being removed from mdev device
131-
* framework so remove mdev device forcefully.
132-
*/
133-
static int mdev_device_remove_ops(struct mdev_device *mdev, bool force_remove)
134-
{
135-
struct mdev_parent *parent = mdev->parent;
108+
struct mdev_parent *parent;
109+
struct mdev_type *type;
136110
int ret;
137111

138-
/*
139-
* Vendor driver can return error if VMM or userspace application is
140-
* using this mdev device.
141-
*/
112+
type = to_mdev_type(mdev->type_kobj);
113+
mdev_remove_sysfs_files(&mdev->dev, type);
114+
device_del(&mdev->dev);
115+
parent = mdev->parent;
116+
lockdep_assert_held(&parent->unreg_sem);
142117
ret = parent->ops->remove(mdev);
143-
if (ret && !force_remove)
144-
return ret;
118+
if (ret)
119+
dev_err(&mdev->dev, "Remove failed: err=%d\n", ret);
145120

146-
sysfs_remove_groups(&mdev->dev.kobj, parent->ops->mdev_attr_groups);
147-
return 0;
121+
/* Balances with device_initialize() */
122+
put_device(&mdev->dev);
123+
mdev_put_parent(parent);
148124
}
149125

150126
static int mdev_device_remove_cb(struct device *dev, void *data)
151127
{
152-
if (dev_is_mdev(dev))
153-
mdev_device_remove(dev, true);
128+
if (dev_is_mdev(dev)) {
129+
struct mdev_device *mdev;
154130

131+
mdev = to_mdev_device(dev);
132+
mdev_device_remove_common(mdev);
133+
}
155134
return 0;
156135
}
157136

@@ -193,6 +172,7 @@ int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops)
193172
}
194173

195174
kref_init(&parent->ref);
175+
init_rwsem(&parent->unreg_sem);
196176

197177
parent->dev = dev;
198178
parent->ops = ops;
@@ -251,21 +231,23 @@ void mdev_unregister_device(struct device *dev)
251231
dev_info(dev, "MDEV: Unregistering\n");
252232

253233
list_del(&parent->next);
234+
mutex_unlock(&parent_list_lock);
235+
236+
down_write(&parent->unreg_sem);
237+
254238
class_compat_remove_link(mdev_bus_compat_class, dev, NULL);
255239

256240
device_for_each_child(dev, NULL, mdev_device_remove_cb);
257241

258242
parent_remove_sysfs_files(parent);
243+
up_write(&parent->unreg_sem);
259244

260-
mutex_unlock(&parent_list_lock);
261245
mdev_put_parent(parent);
262246
}
263247
EXPORT_SYMBOL(mdev_unregister_device);
264248

265-
static void mdev_device_release(struct device *dev)
249+
static void mdev_device_free(struct mdev_device *mdev)
266250
{
267-
struct mdev_device *mdev = to_mdev_device(dev);
268-
269251
mutex_lock(&mdev_list_lock);
270252
list_del(&mdev->next);
271253
mutex_unlock(&mdev_list_lock);
@@ -274,6 +256,13 @@ static void mdev_device_release(struct device *dev)
274256
kfree(mdev);
275257
}
276258

259+
static void mdev_device_release(struct device *dev)
260+
{
261+
struct mdev_device *mdev = to_mdev_device(dev);
262+
263+
mdev_device_free(mdev);
264+
}
265+
277266
int mdev_device_create(struct kobject *kobj,
278267
struct device *dev, const guid_t *uuid)
279268
{
@@ -310,46 +299,55 @@ int mdev_device_create(struct kobject *kobj,
310299

311300
mdev->parent = parent;
312301

302+
/* Check if parent unregistration has started */
303+
if (!down_read_trylock(&parent->unreg_sem)) {
304+
mdev_device_free(mdev);
305+
ret = -ENODEV;
306+
goto mdev_fail;
307+
}
308+
309+
device_initialize(&mdev->dev);
313310
mdev->dev.parent = dev;
314311
mdev->dev.bus = &mdev_bus_type;
315312
mdev->dev.release = mdev_device_release;
316313
dev_set_name(&mdev->dev, "%pUl", uuid);
314+
mdev->dev.groups = parent->ops->mdev_attr_groups;
315+
mdev->type_kobj = kobj;
317316

318-
ret = device_register(&mdev->dev);
319-
if (ret) {
320-
put_device(&mdev->dev);
321-
goto mdev_fail;
322-
}
317+
ret = parent->ops->create(kobj, mdev);
318+
if (ret)
319+
goto ops_create_fail;
323320

324-
ret = mdev_device_create_ops(kobj, mdev);
321+
ret = device_add(&mdev->dev);
325322
if (ret)
326-
goto create_fail;
323+
goto add_fail;
327324

328325
ret = mdev_create_sysfs_files(&mdev->dev, type);
329-
if (ret) {
330-
mdev_device_remove_ops(mdev, true);
331-
goto create_fail;
332-
}
326+
if (ret)
327+
goto sysfs_fail;
333328

334-
mdev->type_kobj = kobj;
335329
mdev->active = true;
336330
dev_dbg(&mdev->dev, "MDEV: created\n");
331+
up_read(&parent->unreg_sem);
337332

338333
return 0;
339334

340-
create_fail:
341-
device_unregister(&mdev->dev);
335+
sysfs_fail:
336+
device_del(&mdev->dev);
337+
add_fail:
338+
parent->ops->remove(mdev);
339+
ops_create_fail:
340+
up_read(&parent->unreg_sem);
341+
put_device(&mdev->dev);
342342
mdev_fail:
343343
mdev_put_parent(parent);
344344
return ret;
345345
}
346346

347-
int mdev_device_remove(struct device *dev, bool force_remove)
347+
int mdev_device_remove(struct device *dev)
348348
{
349349
struct mdev_device *mdev, *tmp;
350350
struct mdev_parent *parent;
351-
struct mdev_type *type;
352-
int ret;
353351

354352
mdev = to_mdev_device(dev);
355353

@@ -372,19 +370,13 @@ int mdev_device_remove(struct device *dev, bool force_remove)
372370
mdev->active = false;
373371
mutex_unlock(&mdev_list_lock);
374372

375-
type = to_mdev_type(mdev->type_kobj);
376373
parent = mdev->parent;
374+
/* Check if parent unregistration has started */
375+
if (!down_read_trylock(&parent->unreg_sem))
376+
return -ENODEV;
377377

378-
ret = mdev_device_remove_ops(mdev, force_remove);
379-
if (ret) {
380-
mdev->active = true;
381-
return ret;
382-
}
383-
384-
mdev_remove_sysfs_files(dev, type);
385-
device_unregister(dev);
386-
mdev_put_parent(parent);
387-
378+
mdev_device_remove_common(mdev);
379+
up_read(&parent->unreg_sem);
388380
return 0;
389381
}
390382

drivers/vfio/mdev/mdev_private.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ struct mdev_parent {
2323
struct list_head next;
2424
struct kset *mdev_types_kset;
2525
struct list_head type_list;
26+
/* Synchronize device creation/removal with parent unregistration */
27+
struct rw_semaphore unreg_sem;
2628
};
2729

2830
struct mdev_device {
@@ -60,6 +62,6 @@ void mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type);
6062

6163
int mdev_device_create(struct kobject *kobj,
6264
struct device *dev, const guid_t *uuid);
63-
int mdev_device_remove(struct device *dev, bool force_remove);
65+
int mdev_device_remove(struct device *dev);
6466

6567
#endif /* MDEV_PRIVATE_H */

drivers/vfio/mdev/mdev_sysfs.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,9 @@ static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
236236
if (val && device_remove_file_self(dev, attr)) {
237237
int ret;
238238

239-
ret = mdev_device_remove(dev, false);
240-
if (ret) {
241-
device_create_file(dev, attr);
239+
ret = mdev_device_remove(dev);
240+
if (ret)
242241
return ret;
243-
}
244242
}
245243

246244
return count;

0 commit comments

Comments
 (0)