// SPDX-License-Identifier: GPL-2.0 /* * Async I/O region for vfio_ccw * * Copyright Red Hat, Inc. 2019 * * Author(s): Cornelia Huck <cohuck@redhat.com> */ #include <linux/vfio.h> #include "vfio_ccw_private.h" static ssize_t vfio_ccw_async_region_read(struct vfio_ccw_private *private, char __user *buf, size_t count, loff_t *ppos) { unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; struct ccw_cmd_region *region; int ret; if (pos + count > sizeof(*region)) return -EINVAL; mutex_lock(&private->io_mutex); region = private->region[i].data; if (copy_to_user(buf, (void *)region + pos, count)) ret = -EFAULT; else ret = count; mutex_unlock(&private->io_mutex); return ret; } static ssize_t vfio_ccw_async_region_write(struct vfio_ccw_private *private, const char __user *buf, size_t count, loff_t *ppos) { unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; struct ccw_cmd_region *region; int ret; if (pos + count > sizeof(*region)) return -EINVAL; if (!mutex_trylock(&private->io_mutex)) return -EAGAIN; region = private->region[i].data; if (copy_from_user((void *)region + pos, buf, count)) { ret = -EFAULT; goto out_unlock; } vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_ASYNC_REQ); ret = region->ret_code ? region->ret_code : count; out_unlock: mutex_unlock(&private->io_mutex); return ret; } static void vfio_ccw_async_region_release(struct vfio_ccw_private *private, struct vfio_ccw_region *region) { } static const struct vfio_ccw_regops vfio_ccw_async_region_ops = { .read = vfio_ccw_async_region_read, .write = vfio_ccw_async_region_write, .release = vfio_ccw_async_region_release, }; int vfio_ccw_register_async_dev_regions(struct vfio_ccw_private *private) { return vfio_ccw_register_dev_region(private, VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD, &vfio_ccw_async_region_ops, sizeof(struct ccw_cmd_region), VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE, private->cmd_region); }