Skip to content

Commit 5877515

Browse files
committed
cxl/mem: Fix synchronization mechanism for device removal vs ioctl operations
The percpu_ref to gate whether cxl_memdev_ioctl() is free to use the driver context (@cxlm) to issue I/O is overkill, implemented incorrectly (missing a device reference before accessing the percpu_ref), and the complexities of shutting down a percpu_ref contributed to a bug in the error unwind in cxl_mem_add_memdev() (missing put_device() to be fixed separately). Use an rwsem to explicitly synchronize the usage of cxlmd->cxlm, and add the missing reference counting for cxlmd in cxl_memdev_open() and cxl_memdev_release_file(). Fixes: b39cb10 ("cxl/mem: Register CXL memX devices") Reported-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Link: https://lore.kernel.org/r/161728759948.2474381.17481500816783671817.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent 6eff572 commit 5877515

File tree

1 file changed

+50
-47
lines changed

1 file changed

+50
-47
lines changed

drivers/cxl/mem.c

Lines changed: 50 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -96,21 +96,18 @@ struct mbox_cmd {
9696
* @dev: driver core device object
9797
* @cdev: char dev core object for ioctl operations
9898
* @cxlm: pointer to the parent device driver data
99-
* @ops_active: active user of @cxlm in ops handlers
100-
* @ops_dead: completion when all @cxlm ops users have exited
10199
* @id: id number of this memdev instance.
102100
*/
103101
struct cxl_memdev {
104102
struct device dev;
105103
struct cdev cdev;
106104
struct cxl_mem *cxlm;
107-
struct percpu_ref ops_active;
108-
struct completion ops_dead;
109105
int id;
110106
};
111107

112108
static int cxl_mem_major;
113109
static DEFINE_IDA(cxl_memdev_ida);
110+
static DECLARE_RWSEM(cxl_memdev_rwsem);
114111
static struct dentry *cxl_debugfs;
115112
static bool cxl_raw_allow_all;
116113

@@ -776,26 +773,43 @@ static long __cxl_memdev_ioctl(struct cxl_memdev *cxlmd, unsigned int cmd,
776773
static long cxl_memdev_ioctl(struct file *file, unsigned int cmd,
777774
unsigned long arg)
778775
{
779-
struct cxl_memdev *cxlmd;
780-
struct inode *inode;
781-
int rc = -ENOTTY;
776+
struct cxl_memdev *cxlmd = file->private_data;
777+
int rc = -ENXIO;
782778

783-
inode = file_inode(file);
784-
cxlmd = container_of(inode->i_cdev, typeof(*cxlmd), cdev);
779+
down_read(&cxl_memdev_rwsem);
780+
if (cxlmd->cxlm)
781+
rc = __cxl_memdev_ioctl(cxlmd, cmd, arg);
782+
up_read(&cxl_memdev_rwsem);
785783

786-
if (!percpu_ref_tryget_live(&cxlmd->ops_active))
787-
return -ENXIO;
784+
return rc;
785+
}
788786

789-
rc = __cxl_memdev_ioctl(cxlmd, cmd, arg);
787+
static int cxl_memdev_open(struct inode *inode, struct file *file)
788+
{
789+
struct cxl_memdev *cxlmd =
790+
container_of(inode->i_cdev, typeof(*cxlmd), cdev);
790791

791-
percpu_ref_put(&cxlmd->ops_active);
792+
get_device(&cxlmd->dev);
793+
file->private_data = cxlmd;
792794

793-
return rc;
795+
return 0;
796+
}
797+
798+
static int cxl_memdev_release_file(struct inode *inode, struct file *file)
799+
{
800+
struct cxl_memdev *cxlmd =
801+
container_of(inode->i_cdev, typeof(*cxlmd), cdev);
802+
803+
put_device(&cxlmd->dev);
804+
805+
return 0;
794806
}
795807

796808
static const struct file_operations cxl_memdev_fops = {
797809
.owner = THIS_MODULE,
798810
.unlocked_ioctl = cxl_memdev_ioctl,
811+
.open = cxl_memdev_open,
812+
.release = cxl_memdev_release_file,
799813
.compat_ioctl = compat_ptr_ioctl,
800814
.llseek = noop_llseek,
801815
};
@@ -1049,7 +1063,6 @@ static void cxl_memdev_release(struct device *dev)
10491063
{
10501064
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
10511065

1052-
percpu_ref_exit(&cxlmd->ops_active);
10531066
ida_free(&cxl_memdev_ida, cxlmd->id);
10541067
kfree(cxlmd);
10551068
}
@@ -1150,24 +1163,21 @@ static const struct device_type cxl_memdev_type = {
11501163
.groups = cxl_memdev_attribute_groups,
11511164
};
11521165

1153-
static void cxlmdev_unregister(void *_cxlmd)
1166+
static void cxl_memdev_shutdown(struct cxl_memdev *cxlmd)
11541167
{
1155-
struct cxl_memdev *cxlmd = _cxlmd;
1156-
struct device *dev = &cxlmd->dev;
1157-
1158-
percpu_ref_kill(&cxlmd->ops_active);
1159-
cdev_device_del(&cxlmd->cdev, dev);
1160-
wait_for_completion(&cxlmd->ops_dead);
1168+
down_write(&cxl_memdev_rwsem);
11611169
cxlmd->cxlm = NULL;
1162-
put_device(dev);
1170+
up_write(&cxl_memdev_rwsem);
11631171
}
11641172

1165-
static void cxlmdev_ops_active_release(struct percpu_ref *ref)
1173+
static void cxl_memdev_unregister(void *_cxlmd)
11661174
{
1167-
struct cxl_memdev *cxlmd =
1168-
container_of(ref, typeof(*cxlmd), ops_active);
1175+
struct cxl_memdev *cxlmd = _cxlmd;
1176+
struct device *dev = &cxlmd->dev;
11691177

1170-
complete(&cxlmd->ops_dead);
1178+
cdev_device_del(&cxlmd->cdev, dev);
1179+
cxl_memdev_shutdown(cxlmd);
1180+
put_device(dev);
11711181
}
11721182

11731183
static int cxl_mem_add_memdev(struct cxl_mem *cxlm)
@@ -1181,17 +1191,6 @@ static int cxl_mem_add_memdev(struct cxl_mem *cxlm)
11811191
cxlmd = kzalloc(sizeof(*cxlmd), GFP_KERNEL);
11821192
if (!cxlmd)
11831193
return -ENOMEM;
1184-
init_completion(&cxlmd->ops_dead);
1185-
1186-
/*
1187-
* @cxlm is deallocated when the driver unbinds so operations
1188-
* that are using it need to hold a live reference.
1189-
*/
1190-
cxlmd->cxlm = cxlm;
1191-
rc = percpu_ref_init(&cxlmd->ops_active, cxlmdev_ops_active_release, 0,
1192-
GFP_KERNEL);
1193-
if (rc)
1194-
goto err_ref;
11951194

11961195
rc = ida_alloc_range(&cxl_memdev_ida, 0, CXL_MEM_MAX_DEVS, GFP_KERNEL);
11971196
if (rc < 0)
@@ -1209,23 +1208,27 @@ static int cxl_mem_add_memdev(struct cxl_mem *cxlm)
12091208
cdev = &cxlmd->cdev;
12101209
cdev_init(cdev, &cxl_memdev_fops);
12111210

1211+
/*
1212+
* Activate ioctl operations, no cxl_memdev_rwsem manipulation
1213+
* needed as this is ordered with cdev_add() publishing the device.
1214+
*/
1215+
cxlmd->cxlm = cxlm;
1216+
12121217
rc = cdev_device_add(cdev, dev);
12131218
if (rc)
12141219
goto err_add;
12151220

1216-
return devm_add_action_or_reset(dev->parent, cxlmdev_unregister, cxlmd);
1221+
return devm_add_action_or_reset(dev->parent, cxl_memdev_unregister,
1222+
cxlmd);
12171223

12181224
err_add:
1219-
ida_free(&cxl_memdev_ida, cxlmd->id);
1220-
err_id:
12211225
/*
1222-
* Theoretically userspace could have already entered the fops,
1223-
* so flush ops_active.
1226+
* The cdev was briefly live, shutdown any ioctl operations that
1227+
* saw that state.
12241228
*/
1225-
percpu_ref_kill(&cxlmd->ops_active);
1226-
wait_for_completion(&cxlmd->ops_dead);
1227-
percpu_ref_exit(&cxlmd->ops_active);
1228-
err_ref:
1229+
cxl_memdev_shutdown(cxlmd);
1230+
ida_free(&cxl_memdev_ida, cxlmd->id);
1231+
err_id:
12291232
kfree(cxlmd);
12301233

12311234
return rc;

0 commit comments

Comments
 (0)