aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/ocxl/file.c
diff options
context:
space:
mode:
authorFrederic Barrat <fbarrat@linux.vnet.ibm.com>2018-01-23 06:31:41 -0500
committerMichael Ellerman <mpe@ellerman.id.au>2018-01-23 19:42:58 -0500
commit5ef3166e8a32d78dfa985a323aa45ed485ff663a (patch)
treee1321e75dc2f802294f94d71aff7509057d01077 /drivers/misc/ocxl/file.c
parent2cb3d64b26984703a6bb80e66adcc3727ad37f9f (diff)
ocxl: Driver code for 'generic' opencapi devices
Add an ocxl driver to handle generic opencapi devices. Of course, it's not meant to be the only opencapi driver, any device is free to implement its own. But if a host application only needs basic services like attaching to an opencapi adapter, have translation faults handled or allocate AFU interrupts, it should suffice. The AFU config space must follow the opencapi specification and use the expected vendor/device ID to be seen by the generic driver. The driver exposes the device AFUs as a char device in /dev/ocxl/ Note that the driver currently doesn't handle memory attached to the opencapi device. Signed-off-by: Frederic Barrat <fbarrat@linux.vnet.ibm.com> Signed-off-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com> Signed-off-by: Alastair D'Silva <alastair@d-silva.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'drivers/misc/ocxl/file.c')
-rw-r--r--drivers/misc/ocxl/file.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/drivers/misc/ocxl/file.c b/drivers/misc/ocxl/file.c
new file mode 100644
index 000000000000..6f0befda6a8a
--- /dev/null
+++ b/drivers/misc/ocxl/file.c
@@ -0,0 +1,398 @@
1// SPDX-License-Identifier: GPL-2.0+
2// Copyright 2017 IBM Corp.
3#include <linux/fs.h>
4#include <linux/poll.h>
5#include <linux/sched/signal.h>
6#include <linux/uaccess.h>
7#include <uapi/misc/ocxl.h>
8#include "ocxl_internal.h"
9
10
11#define OCXL_NUM_MINORS 256 /* Total to reserve */
12
13static dev_t ocxl_dev;
14static struct class *ocxl_class;
15static struct mutex minors_idr_lock;
16static struct idr minors_idr;
17
18static struct ocxl_afu *find_and_get_afu(dev_t devno)
19{
20 struct ocxl_afu *afu;
21 int afu_minor;
22
23 afu_minor = MINOR(devno);
24 /*
25 * We don't declare an RCU critical section here, as our AFU
26 * is protected by a reference counter on the device. By the time the
27 * minor number of a device is removed from the idr, the ref count of
28 * the device is already at 0, so no user API will access that AFU and
29 * this function can't return it.
30 */
31 afu = idr_find(&minors_idr, afu_minor);
32 if (afu)
33 ocxl_afu_get(afu);
34 return afu;
35}
36
37static int allocate_afu_minor(struct ocxl_afu *afu)
38{
39 int minor;
40
41 mutex_lock(&minors_idr_lock);
42 minor = idr_alloc(&minors_idr, afu, 0, OCXL_NUM_MINORS, GFP_KERNEL);
43 mutex_unlock(&minors_idr_lock);
44 return minor;
45}
46
47static void free_afu_minor(struct ocxl_afu *afu)
48{
49 mutex_lock(&minors_idr_lock);
50 idr_remove(&minors_idr, MINOR(afu->dev.devt));
51 mutex_unlock(&minors_idr_lock);
52}
53
54static int afu_open(struct inode *inode, struct file *file)
55{
56 struct ocxl_afu *afu;
57 struct ocxl_context *ctx;
58 int rc;
59
60 pr_debug("%s for device %x\n", __func__, inode->i_rdev);
61
62 afu = find_and_get_afu(inode->i_rdev);
63 if (!afu)
64 return -ENODEV;
65
66 ctx = ocxl_context_alloc();
67 if (!ctx) {
68 rc = -ENOMEM;
69 goto put_afu;
70 }
71
72 rc = ocxl_context_init(ctx, afu, inode->i_mapping);
73 if (rc)
74 goto put_afu;
75 file->private_data = ctx;
76 ocxl_afu_put(afu);
77 return 0;
78
79put_afu:
80 ocxl_afu_put(afu);
81 return rc;
82}
83
84static long afu_ioctl_attach(struct ocxl_context *ctx,
85 struct ocxl_ioctl_attach __user *uarg)
86{
87 struct ocxl_ioctl_attach arg;
88 u64 amr = 0;
89 int rc;
90
91 pr_debug("%s for context %d\n", __func__, ctx->pasid);
92
93 if (copy_from_user(&arg, uarg, sizeof(arg)))
94 return -EFAULT;
95
96 /* Make sure reserved fields are not set for forward compatibility */
97 if (arg.reserved1 || arg.reserved2 || arg.reserved3)
98 return -EINVAL;
99
100 amr = arg.amr & mfspr(SPRN_UAMOR);
101 rc = ocxl_context_attach(ctx, amr);
102 return rc;
103}
104
105#define CMD_STR(x) (x == OCXL_IOCTL_ATTACH ? "ATTACH" : \
106 "UNKNOWN")
107
108static long afu_ioctl(struct file *file, unsigned int cmd,
109 unsigned long args)
110{
111 struct ocxl_context *ctx = file->private_data;
112 long rc;
113
114 pr_debug("%s for context %d, command %s\n", __func__, ctx->pasid,
115 CMD_STR(cmd));
116
117 if (ctx->status == CLOSED)
118 return -EIO;
119
120 switch (cmd) {
121 case OCXL_IOCTL_ATTACH:
122 rc = afu_ioctl_attach(ctx,
123 (struct ocxl_ioctl_attach __user *) args);
124 break;
125
126 default:
127 rc = -EINVAL;
128 }
129 return rc;
130}
131
132static long afu_compat_ioctl(struct file *file, unsigned int cmd,
133 unsigned long args)
134{
135 return afu_ioctl(file, cmd, args);
136}
137
138static int afu_mmap(struct file *file, struct vm_area_struct *vma)
139{
140 struct ocxl_context *ctx = file->private_data;
141
142 pr_debug("%s for context %d\n", __func__, ctx->pasid);
143 return ocxl_context_mmap(ctx, vma);
144}
145
146static bool has_xsl_error(struct ocxl_context *ctx)
147{
148 bool ret;
149
150 mutex_lock(&ctx->xsl_error_lock);
151 ret = !!ctx->xsl_error.addr;
152 mutex_unlock(&ctx->xsl_error_lock);
153
154 return ret;
155}
156
157/*
158 * Are there any events pending on the AFU
159 * ctx: The AFU context
160 * Returns: true if there are events pending
161 */
162static bool afu_events_pending(struct ocxl_context *ctx)
163{
164 if (has_xsl_error(ctx))
165 return true;
166 return false;
167}
168
169static unsigned int afu_poll(struct file *file, struct poll_table_struct *wait)
170{
171 struct ocxl_context *ctx = file->private_data;
172 unsigned int mask = 0;
173 bool closed;
174
175 pr_debug("%s for context %d\n", __func__, ctx->pasid);
176
177 poll_wait(file, &ctx->events_wq, wait);
178
179 mutex_lock(&ctx->status_mutex);
180 closed = (ctx->status == CLOSED);
181 mutex_unlock(&ctx->status_mutex);
182
183 if (afu_events_pending(ctx))
184 mask = POLLIN | POLLRDNORM;
185 else if (closed)
186 mask = POLLERR;
187
188 return mask;
189}
190
191/*
192 * Populate the supplied buffer with a single XSL error
193 * ctx: The AFU context to report the error from
194 * header: the event header to populate
195 * buf: The buffer to write the body into (should be at least
196 * AFU_EVENT_BODY_XSL_ERROR_SIZE)
197 * Return: the amount of buffer that was populated
198 */
199static ssize_t append_xsl_error(struct ocxl_context *ctx,
200 struct ocxl_kernel_event_header *header,
201 char __user *buf)
202{
203 struct ocxl_kernel_event_xsl_fault_error body;
204
205 memset(&body, 0, sizeof(body));
206
207 mutex_lock(&ctx->xsl_error_lock);
208 if (!ctx->xsl_error.addr) {
209 mutex_unlock(&ctx->xsl_error_lock);
210 return 0;
211 }
212
213 body.addr = ctx->xsl_error.addr;
214 body.dsisr = ctx->xsl_error.dsisr;
215 body.count = ctx->xsl_error.count;
216
217 ctx->xsl_error.addr = 0;
218 ctx->xsl_error.dsisr = 0;
219 ctx->xsl_error.count = 0;
220
221 mutex_unlock(&ctx->xsl_error_lock);
222
223 header->type = OCXL_AFU_EVENT_XSL_FAULT_ERROR;
224
225 if (copy_to_user(buf, &body, sizeof(body)))
226 return -EFAULT;
227
228 return sizeof(body);
229}
230
231#define AFU_EVENT_BODY_MAX_SIZE sizeof(struct ocxl_kernel_event_xsl_fault_error)
232
233/*
234 * Reports events on the AFU
235 * Format:
236 * Header (struct ocxl_kernel_event_header)
237 * Body (struct ocxl_kernel_event_*)
238 * Header...
239 */
240static ssize_t afu_read(struct file *file, char __user *buf, size_t count,
241 loff_t *off)
242{
243 struct ocxl_context *ctx = file->private_data;
244 struct ocxl_kernel_event_header header;
245 ssize_t rc;
246 size_t used = 0;
247 DEFINE_WAIT(event_wait);
248
249 memset(&header, 0, sizeof(header));
250
251 /* Require offset to be 0 */
252 if (*off != 0)
253 return -EINVAL;
254
255 if (count < (sizeof(struct ocxl_kernel_event_header) +
256 AFU_EVENT_BODY_MAX_SIZE))
257 return -EINVAL;
258
259 for (;;) {
260 prepare_to_wait(&ctx->events_wq, &event_wait,
261 TASK_INTERRUPTIBLE);
262
263 if (afu_events_pending(ctx))
264 break;
265
266 if (ctx->status == CLOSED)
267 break;
268
269 if (file->f_flags & O_NONBLOCK) {
270 finish_wait(&ctx->events_wq, &event_wait);
271 return -EAGAIN;
272 }
273
274 if (signal_pending(current)) {
275 finish_wait(&ctx->events_wq, &event_wait);
276 return -ERESTARTSYS;
277 }
278
279 schedule();
280 }
281
282 finish_wait(&ctx->events_wq, &event_wait);
283
284 if (has_xsl_error(ctx)) {
285 used = append_xsl_error(ctx, &header, buf + sizeof(header));
286 if (used < 0)
287 return used;
288 }
289
290 if (!afu_events_pending(ctx))
291 header.flags |= OCXL_KERNEL_EVENT_FLAG_LAST;
292
293 if (copy_to_user(buf, &header, sizeof(header)))
294 return -EFAULT;
295
296 used += sizeof(header);
297
298 rc = (ssize_t) used;
299 return rc;
300}
301
302static int afu_release(struct inode *inode, struct file *file)
303{
304 struct ocxl_context *ctx = file->private_data;
305 int rc;
306
307 pr_debug("%s for device %x\n", __func__, inode->i_rdev);
308 rc = ocxl_context_detach(ctx);
309 mutex_lock(&ctx->mapping_lock);
310 ctx->mapping = NULL;
311 mutex_unlock(&ctx->mapping_lock);
312 wake_up_all(&ctx->events_wq);
313 if (rc != -EBUSY)
314 ocxl_context_free(ctx);
315 return 0;
316}
317
318static const struct file_operations ocxl_afu_fops = {
319 .owner = THIS_MODULE,
320 .open = afu_open,
321 .unlocked_ioctl = afu_ioctl,
322 .compat_ioctl = afu_compat_ioctl,
323 .mmap = afu_mmap,
324 .poll = afu_poll,
325 .read = afu_read,
326 .release = afu_release,
327};
328
329int ocxl_create_cdev(struct ocxl_afu *afu)
330{
331 int rc;
332
333 cdev_init(&afu->cdev, &ocxl_afu_fops);
334 rc = cdev_add(&afu->cdev, afu->dev.devt, 1);
335 if (rc) {
336 dev_err(&afu->dev, "Unable to add afu char device: %d\n", rc);
337 return rc;
338 }
339 return 0;
340}
341
342void ocxl_destroy_cdev(struct ocxl_afu *afu)
343{
344 cdev_del(&afu->cdev);
345}
346
347int ocxl_register_afu(struct ocxl_afu *afu)
348{
349 int minor;
350
351 minor = allocate_afu_minor(afu);
352 if (minor < 0)
353 return minor;
354 afu->dev.devt = MKDEV(MAJOR(ocxl_dev), minor);
355 afu->dev.class = ocxl_class;
356 return device_register(&afu->dev);
357}
358
359void ocxl_unregister_afu(struct ocxl_afu *afu)
360{
361 free_afu_minor(afu);
362}
363
364static char *ocxl_devnode(struct device *dev, umode_t *mode)
365{
366 return kasprintf(GFP_KERNEL, "ocxl/%s", dev_name(dev));
367}
368
369int ocxl_file_init(void)
370{
371 int rc;
372
373 mutex_init(&minors_idr_lock);
374 idr_init(&minors_idr);
375
376 rc = alloc_chrdev_region(&ocxl_dev, 0, OCXL_NUM_MINORS, "ocxl");
377 if (rc) {
378 pr_err("Unable to allocate ocxl major number: %d\n", rc);
379 return rc;
380 }
381
382 ocxl_class = class_create(THIS_MODULE, "ocxl");
383 if (IS_ERR(ocxl_class)) {
384 pr_err("Unable to create ocxl class\n");
385 unregister_chrdev_region(ocxl_dev, OCXL_NUM_MINORS);
386 return PTR_ERR(ocxl_class);
387 }
388
389 ocxl_class->devnode = ocxl_devnode;
390 return 0;
391}
392
393void ocxl_file_exit(void)
394{
395 class_destroy(ocxl_class);
396 unregister_chrdev_region(ocxl_dev, OCXL_NUM_MINORS);
397 idr_destroy(&minors_idr);
398}