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 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 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 67 static void vfio_ccw_async_region_release(struct vfio_ccw_private *private, 68 struct vfio_ccw_region *region) 69 { 70 71 } 72 73 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 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