1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Async I/O region for vfio_ccw
4 *
5 * Copyright Red Hat, Inc. 2019
6 *
7 * Author(s): Cornelia Huck <cohuck@redhat.com>
8 */
9
10 #include <linux/vfio.h>
11 #include <linux/mdev.h>
12
13 #include "vfio_ccw_private.h"
14
vfio_ccw_async_region_read(struct vfio_ccw_private * private,char __user * buf,size_t count,loff_t * ppos)15 static ssize_t vfio_ccw_async_region_read(struct vfio_ccw_private *private,
16 char __user *buf, size_t count,
17 loff_t *ppos)
18 {
19 unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS;
20 loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
21 struct ccw_cmd_region *region;
22 int ret;
23
24 if (pos + count > sizeof(*region))
25 return -EINVAL;
26
27 mutex_lock(&private->io_mutex);
28 region = private->region[i].data;
29 if (copy_to_user(buf, (void *)region + pos, count))
30 ret = -EFAULT;
31 else
32 ret = count;
33 mutex_unlock(&private->io_mutex);
34 return ret;
35 }
36
vfio_ccw_async_region_write(struct vfio_ccw_private * private,const char __user * buf,size_t count,loff_t * ppos)37 static ssize_t vfio_ccw_async_region_write(struct vfio_ccw_private *private,
38 const char __user *buf, size_t count,
39 loff_t *ppos)
40 {
41 unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS;
42 loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
43 struct ccw_cmd_region *region;
44 int ret;
45
46 if (pos + count > sizeof(*region))
47 return -EINVAL;
48
49 if (!mutex_trylock(&private->io_mutex))
50 return -EAGAIN;
51
52 region = private->region[i].data;
53 if (copy_from_user((void *)region + pos, buf, count)) {
54 ret = -EFAULT;
55 goto out_unlock;
56 }
57
58 vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_ASYNC_REQ);
59
60 ret = region->ret_code ? region->ret_code : count;
61
62 out_unlock:
63 mutex_unlock(&private->io_mutex);
64 return ret;
65 }
66
vfio_ccw_async_region_release(struct vfio_ccw_private * private,struct vfio_ccw_region * region)67 static void vfio_ccw_async_region_release(struct vfio_ccw_private *private,
68 struct vfio_ccw_region *region)
69 {
70
71 }
72
73 static const struct vfio_ccw_regops vfio_ccw_async_region_ops = {
74 .read = vfio_ccw_async_region_read,
75 .write = vfio_ccw_async_region_write,
76 .release = vfio_ccw_async_region_release,
77 };
78
vfio_ccw_register_async_dev_regions(struct vfio_ccw_private * private)79 int vfio_ccw_register_async_dev_regions(struct vfio_ccw_private *private)
80 {
81 return vfio_ccw_register_dev_region(private,
82 VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD,
83 &vfio_ccw_async_region_ops,
84 sizeof(struct ccw_cmd_region),
85 VFIO_REGION_INFO_FLAG_READ |
86 VFIO_REGION_INFO_FLAG_WRITE,
87 private->cmd_region);
88 }
89