1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright(c) 2020 Intel Corporation. */
3
4 #include <linux/device.h>
5 #include <linux/slab.h>
6 #include <linux/idr.h>
7 #include <linux/pci.h>
8 #include <cxlmem.h>
9 #include "core.h"
10
11 static DECLARE_RWSEM(cxl_memdev_rwsem);
12
13 /*
14 * An entire PCI topology full of devices should be enough for any
15 * config
16 */
17 #define CXL_MEM_MAX_DEVS 65536
18
19 static int cxl_mem_major;
20 static DEFINE_IDA(cxl_memdev_ida);
21
cxl_memdev_release(struct device * dev)22 static void cxl_memdev_release(struct device *dev)
23 {
24 struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
25
26 ida_free(&cxl_memdev_ida, cxlmd->id);
27 kfree(cxlmd);
28 }
29
cxl_memdev_devnode(struct device * dev,umode_t * mode,kuid_t * uid,kgid_t * gid)30 static char *cxl_memdev_devnode(struct device *dev, umode_t *mode, kuid_t *uid,
31 kgid_t *gid)
32 {
33 return kasprintf(GFP_KERNEL, "cxl/%s", dev_name(dev));
34 }
35
firmware_version_show(struct device * dev,struct device_attribute * attr,char * buf)36 static ssize_t firmware_version_show(struct device *dev,
37 struct device_attribute *attr, char *buf)
38 {
39 struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
40 struct cxl_mem *cxlm = cxlmd->cxlm;
41
42 return sysfs_emit(buf, "%.16s\n", cxlm->firmware_version);
43 }
44 static DEVICE_ATTR_RO(firmware_version);
45
payload_max_show(struct device * dev,struct device_attribute * attr,char * buf)46 static ssize_t payload_max_show(struct device *dev,
47 struct device_attribute *attr, char *buf)
48 {
49 struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
50 struct cxl_mem *cxlm = cxlmd->cxlm;
51
52 return sysfs_emit(buf, "%zu\n", cxlm->payload_size);
53 }
54 static DEVICE_ATTR_RO(payload_max);
55
label_storage_size_show(struct device * dev,struct device_attribute * attr,char * buf)56 static ssize_t label_storage_size_show(struct device *dev,
57 struct device_attribute *attr, char *buf)
58 {
59 struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
60 struct cxl_mem *cxlm = cxlmd->cxlm;
61
62 return sysfs_emit(buf, "%zu\n", cxlm->lsa_size);
63 }
64 static DEVICE_ATTR_RO(label_storage_size);
65
ram_size_show(struct device * dev,struct device_attribute * attr,char * buf)66 static ssize_t ram_size_show(struct device *dev, struct device_attribute *attr,
67 char *buf)
68 {
69 struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
70 struct cxl_mem *cxlm = cxlmd->cxlm;
71 unsigned long long len = range_len(&cxlm->ram_range);
72
73 return sysfs_emit(buf, "%#llx\n", len);
74 }
75
76 static struct device_attribute dev_attr_ram_size =
77 __ATTR(size, 0444, ram_size_show, NULL);
78
pmem_size_show(struct device * dev,struct device_attribute * attr,char * buf)79 static ssize_t pmem_size_show(struct device *dev, struct device_attribute *attr,
80 char *buf)
81 {
82 struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
83 struct cxl_mem *cxlm = cxlmd->cxlm;
84 unsigned long long len = range_len(&cxlm->pmem_range);
85
86 return sysfs_emit(buf, "%#llx\n", len);
87 }
88
89 static struct device_attribute dev_attr_pmem_size =
90 __ATTR(size, 0444, pmem_size_show, NULL);
91
92 static struct attribute *cxl_memdev_attributes[] = {
93 &dev_attr_firmware_version.attr,
94 &dev_attr_payload_max.attr,
95 &dev_attr_label_storage_size.attr,
96 NULL,
97 };
98
99 static struct attribute *cxl_memdev_pmem_attributes[] = {
100 &dev_attr_pmem_size.attr,
101 NULL,
102 };
103
104 static struct attribute *cxl_memdev_ram_attributes[] = {
105 &dev_attr_ram_size.attr,
106 NULL,
107 };
108
109 static struct attribute_group cxl_memdev_attribute_group = {
110 .attrs = cxl_memdev_attributes,
111 };
112
113 static struct attribute_group cxl_memdev_ram_attribute_group = {
114 .name = "ram",
115 .attrs = cxl_memdev_ram_attributes,
116 };
117
118 static struct attribute_group cxl_memdev_pmem_attribute_group = {
119 .name = "pmem",
120 .attrs = cxl_memdev_pmem_attributes,
121 };
122
123 static const struct attribute_group *cxl_memdev_attribute_groups[] = {
124 &cxl_memdev_attribute_group,
125 &cxl_memdev_ram_attribute_group,
126 &cxl_memdev_pmem_attribute_group,
127 NULL,
128 };
129
130 static const struct device_type cxl_memdev_type = {
131 .name = "cxl_memdev",
132 .release = cxl_memdev_release,
133 .devnode = cxl_memdev_devnode,
134 .groups = cxl_memdev_attribute_groups,
135 };
136
137 /**
138 * set_exclusive_cxl_commands() - atomically disable user cxl commands
139 * @cxlm: cxl_mem instance to modify
140 * @cmds: bitmap of commands to mark exclusive
141 *
142 * Grab the cxl_memdev_rwsem in write mode to flush in-flight
143 * invocations of the ioctl path and then disable future execution of
144 * commands with the command ids set in @cmds.
145 */
set_exclusive_cxl_commands(struct cxl_mem * cxlm,unsigned long * cmds)146 void set_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds)
147 {
148 down_write(&cxl_memdev_rwsem);
149 bitmap_or(cxlm->exclusive_cmds, cxlm->exclusive_cmds, cmds,
150 CXL_MEM_COMMAND_ID_MAX);
151 up_write(&cxl_memdev_rwsem);
152 }
153 EXPORT_SYMBOL_GPL(set_exclusive_cxl_commands);
154
155 /**
156 * clear_exclusive_cxl_commands() - atomically enable user cxl commands
157 * @cxlm: cxl_mem instance to modify
158 * @cmds: bitmap of commands to mark available for userspace
159 */
clear_exclusive_cxl_commands(struct cxl_mem * cxlm,unsigned long * cmds)160 void clear_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds)
161 {
162 down_write(&cxl_memdev_rwsem);
163 bitmap_andnot(cxlm->exclusive_cmds, cxlm->exclusive_cmds, cmds,
164 CXL_MEM_COMMAND_ID_MAX);
165 up_write(&cxl_memdev_rwsem);
166 }
167 EXPORT_SYMBOL_GPL(clear_exclusive_cxl_commands);
168
cxl_memdev_shutdown(struct device * dev)169 static void cxl_memdev_shutdown(struct device *dev)
170 {
171 struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
172
173 down_write(&cxl_memdev_rwsem);
174 cxlmd->cxlm = NULL;
175 up_write(&cxl_memdev_rwsem);
176 }
177
cxl_memdev_unregister(void * _cxlmd)178 static void cxl_memdev_unregister(void *_cxlmd)
179 {
180 struct cxl_memdev *cxlmd = _cxlmd;
181 struct device *dev = &cxlmd->dev;
182
183 cxl_memdev_shutdown(dev);
184 cdev_device_del(&cxlmd->cdev, dev);
185 put_device(dev);
186 }
187
cxl_memdev_alloc(struct cxl_mem * cxlm,const struct file_operations * fops)188 static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
189 const struct file_operations *fops)
190 {
191 struct cxl_memdev *cxlmd;
192 struct device *dev;
193 struct cdev *cdev;
194 int rc;
195
196 cxlmd = kzalloc(sizeof(*cxlmd), GFP_KERNEL);
197 if (!cxlmd)
198 return ERR_PTR(-ENOMEM);
199
200 rc = ida_alloc_range(&cxl_memdev_ida, 0, CXL_MEM_MAX_DEVS, GFP_KERNEL);
201 if (rc < 0)
202 goto err;
203 cxlmd->id = rc;
204
205 dev = &cxlmd->dev;
206 device_initialize(dev);
207 dev->parent = cxlm->dev;
208 dev->bus = &cxl_bus_type;
209 dev->devt = MKDEV(cxl_mem_major, cxlmd->id);
210 dev->type = &cxl_memdev_type;
211 device_set_pm_not_required(dev);
212
213 cdev = &cxlmd->cdev;
214 cdev_init(cdev, fops);
215 return cxlmd;
216
217 err:
218 kfree(cxlmd);
219 return ERR_PTR(rc);
220 }
221
__cxl_memdev_ioctl(struct cxl_memdev * cxlmd,unsigned int cmd,unsigned long arg)222 static long __cxl_memdev_ioctl(struct cxl_memdev *cxlmd, unsigned int cmd,
223 unsigned long arg)
224 {
225 switch (cmd) {
226 case CXL_MEM_QUERY_COMMANDS:
227 return cxl_query_cmd(cxlmd, (void __user *)arg);
228 case CXL_MEM_SEND_COMMAND:
229 return cxl_send_cmd(cxlmd, (void __user *)arg);
230 default:
231 return -ENOTTY;
232 }
233 }
234
cxl_memdev_ioctl(struct file * file,unsigned int cmd,unsigned long arg)235 static long cxl_memdev_ioctl(struct file *file, unsigned int cmd,
236 unsigned long arg)
237 {
238 struct cxl_memdev *cxlmd = file->private_data;
239 int rc = -ENXIO;
240
241 down_read(&cxl_memdev_rwsem);
242 if (cxlmd->cxlm)
243 rc = __cxl_memdev_ioctl(cxlmd, cmd, arg);
244 up_read(&cxl_memdev_rwsem);
245
246 return rc;
247 }
248
cxl_memdev_open(struct inode * inode,struct file * file)249 static int cxl_memdev_open(struct inode *inode, struct file *file)
250 {
251 struct cxl_memdev *cxlmd =
252 container_of(inode->i_cdev, typeof(*cxlmd), cdev);
253
254 get_device(&cxlmd->dev);
255 file->private_data = cxlmd;
256
257 return 0;
258 }
259
cxl_memdev_release_file(struct inode * inode,struct file * file)260 static int cxl_memdev_release_file(struct inode *inode, struct file *file)
261 {
262 struct cxl_memdev *cxlmd =
263 container_of(inode->i_cdev, typeof(*cxlmd), cdev);
264
265 put_device(&cxlmd->dev);
266
267 return 0;
268 }
269
270 static const struct file_operations cxl_memdev_fops = {
271 .owner = THIS_MODULE,
272 .unlocked_ioctl = cxl_memdev_ioctl,
273 .open = cxl_memdev_open,
274 .release = cxl_memdev_release_file,
275 .compat_ioctl = compat_ptr_ioctl,
276 .llseek = noop_llseek,
277 };
278
279 struct cxl_memdev *
devm_cxl_add_memdev(struct cxl_mem * cxlm)280 devm_cxl_add_memdev(struct cxl_mem *cxlm)
281 {
282 struct cxl_memdev *cxlmd;
283 struct device *dev;
284 struct cdev *cdev;
285 int rc;
286
287 cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops);
288 if (IS_ERR(cxlmd))
289 return cxlmd;
290
291 dev = &cxlmd->dev;
292 rc = dev_set_name(dev, "mem%d", cxlmd->id);
293 if (rc)
294 goto err;
295
296 /*
297 * Activate ioctl operations, no cxl_memdev_rwsem manipulation
298 * needed as this is ordered with cdev_add() publishing the device.
299 */
300 cxlmd->cxlm = cxlm;
301
302 cdev = &cxlmd->cdev;
303 rc = cdev_device_add(cdev, dev);
304 if (rc)
305 goto err;
306
307 rc = devm_add_action_or_reset(cxlm->dev, cxl_memdev_unregister, cxlmd);
308 if (rc)
309 return ERR_PTR(rc);
310 return cxlmd;
311
312 err:
313 /*
314 * The cdev was briefly live, shutdown any ioctl operations that
315 * saw that state.
316 */
317 cxl_memdev_shutdown(dev);
318 put_device(dev);
319 return ERR_PTR(rc);
320 }
321 EXPORT_SYMBOL_GPL(devm_cxl_add_memdev);
322
cxl_memdev_init(void)323 __init int cxl_memdev_init(void)
324 {
325 dev_t devt;
326 int rc;
327
328 rc = alloc_chrdev_region(&devt, 0, CXL_MEM_MAX_DEVS, "cxl");
329 if (rc)
330 return rc;
331
332 cxl_mem_major = MAJOR(devt);
333
334 return 0;
335 }
336
cxl_memdev_exit(void)337 void cxl_memdev_exit(void)
338 {
339 unregister_chrdev_region(MKDEV(cxl_mem_major, 0), CXL_MEM_MAX_DEVS);
340 }
341