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 | |
12 | #include "vfio_ccw_private.h" |
13 | |
14 | static ssize_t vfio_ccw_async_region_read(struct vfio_ccw_private *private, |
15 | char __user *buf, size_t count, |
16 | loff_t *ppos) |
17 | { |
18 | unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; |
19 | loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; |
20 | struct ccw_cmd_region *region; |
21 | int ret; |
22 | |
23 | if (pos + count > sizeof(*region)) |
24 | return -EINVAL; |
25 | |
26 | mutex_lock(&private->io_mutex); |
27 | region = private->region[i].data; |
28 | if (copy_to_user(to: buf, from: (void *)region + pos, n: count)) |
29 | ret = -EFAULT; |
30 | else |
31 | ret = count; |
32 | mutex_unlock(lock: &private->io_mutex); |
33 | return ret; |
34 | } |
35 | |
36 | static ssize_t vfio_ccw_async_region_write(struct vfio_ccw_private *private, |
37 | const char __user *buf, size_t count, |
38 | loff_t *ppos) |
39 | { |
40 | unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; |
41 | loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; |
42 | struct ccw_cmd_region *region; |
43 | int ret; |
44 | |
45 | if (pos + count > sizeof(*region)) |
46 | return -EINVAL; |
47 | |
48 | if (!mutex_trylock(lock: &private->io_mutex)) |
49 | return -EAGAIN; |
50 | |
51 | region = private->region[i].data; |
52 | if (copy_from_user(to: (void *)region + pos, from: buf, n: count)) { |
53 | ret = -EFAULT; |
54 | goto out_unlock; |
55 | } |
56 | |
57 | vfio_ccw_fsm_event(private, event: VFIO_CCW_EVENT_ASYNC_REQ); |
58 | |
59 | ret = region->ret_code ? region->ret_code : count; |
60 | |
61 | out_unlock: |
62 | mutex_unlock(lock: &private->io_mutex); |
63 | return ret; |
64 | } |
65 | |
66 | static void vfio_ccw_async_region_release(struct vfio_ccw_private *private, |
67 | struct vfio_ccw_region *region) |
68 | { |
69 | |
70 | } |
71 | |
72 | static const struct vfio_ccw_regops vfio_ccw_async_region_ops = { |
73 | .read = vfio_ccw_async_region_read, |
74 | .write = vfio_ccw_async_region_write, |
75 | .release = vfio_ccw_async_region_release, |
76 | }; |
77 | |
78 | int vfio_ccw_register_async_dev_regions(struct vfio_ccw_private *private) |
79 | { |
80 | return vfio_ccw_register_dev_region(private, |
81 | VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD, |
82 | ops: &vfio_ccw_async_region_ops, |
83 | size: sizeof(struct ccw_cmd_region), |
84 | VFIO_REGION_INFO_FLAG_READ | |
85 | VFIO_REGION_INFO_FLAG_WRITE, |
86 | data: private->cmd_region); |
87 | } |
88 | |