Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
11static 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
19static int cxl_mem_major;
20static DEFINE_IDA(cxl_memdev_ida);
21
22static 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
30static 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
36static 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}
44static DEVICE_ATTR_RO(firmware_version);
45
46static 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}
54static DEVICE_ATTR_RO(payload_max);
55
56static 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}
64static DEVICE_ATTR_RO(label_storage_size);
65
66static 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
76static struct device_attribute dev_attr_ram_size =
77 __ATTR(size, 0444, ram_size_show, NULL);
78
79static 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
89static struct device_attribute dev_attr_pmem_size =
90 __ATTR(size, 0444, pmem_size_show, NULL);
91
92static 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
99static struct attribute *cxl_memdev_pmem_attributes[] = {
100 &dev_attr_pmem_size.attr,
101 NULL,
102};
103
104static struct attribute *cxl_memdev_ram_attributes[] = {
105 &dev_attr_ram_size.attr,
106 NULL,
107};
108
109static struct attribute_group cxl_memdev_attribute_group = {
110 .attrs = cxl_memdev_attributes,
111};
112
113static struct attribute_group cxl_memdev_ram_attribute_group = {
114 .name = "ram",
115 .attrs = cxl_memdev_ram_attributes,
116};
117
118static struct attribute_group cxl_memdev_pmem_attribute_group = {
119 .name = "pmem",
120 .attrs = cxl_memdev_pmem_attributes,
121};
122
123static 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
130static 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 */
146void 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}
153EXPORT_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 */
160void 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}
167EXPORT_SYMBOL_GPL(clear_exclusive_cxl_commands);
168
169static 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
178static 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
188static 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
217err:
218 kfree(cxlmd);
219 return ERR_PTR(rc);
220}
221
222static 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
235static 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
249static 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
260static 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
270static 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
279struct cxl_memdev *
280devm_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
312err:
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}
321EXPORT_SYMBOL_GPL(devm_cxl_add_memdev);
322
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
337void cxl_memdev_exit(void)
338{
339 unregister_chrdev_region(MKDEV(cxl_mem_major, 0), CXL_MEM_MAX_DEVS);
340}