From nobody Fri Nov 7 14:37:10 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1548069000364400.9446731575116; Mon, 21 Jan 2019 03:10:00 -0800 (PST) Received: from localhost ([127.0.0.1]:51905 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1glXSp-00067Q-4d for importer@patchew.org; Mon, 21 Jan 2019 06:09:59 -0500 Received: from eggs.gnu.org ([209.51.188.92]:45014) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1glXNm-0002Ye-PP for qemu-devel@nongnu.org; Mon, 21 Jan 2019 06:04:51 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1glXNi-0003sp-VG for qemu-devel@nongnu.org; Mon, 21 Jan 2019 06:04:46 -0500 Received: from mx1.redhat.com ([209.132.183.28]:55992) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1glXNi-0003fJ-9c; Mon, 21 Jan 2019 06:04:42 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id D4B018E3C6; Mon, 21 Jan 2019 11:04:27 +0000 (UTC) Received: from localhost (ovpn-116-178.ams2.redhat.com [10.36.116.178]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 6279A6090E; Mon, 21 Jan 2019 11:04:23 +0000 (UTC) From: Cornelia Huck To: Halil Pasic , Eric Farman , Farhan Ali , Pierre Morel Date: Mon, 21 Jan 2019 12:03:52 +0100 Message-Id: <20190121110354.2247-4-cohuck@redhat.com> In-Reply-To: <20190121110354.2247-1-cohuck@redhat.com> References: <20190121110354.2247-1-cohuck@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Mon, 21 Jan 2019 11:04:28 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v2 3/5] vfio-ccw: add capabilities chain X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-s390@vger.kernel.org, kvm@vger.kernel.org, Cornelia Huck , Alex Williamson , qemu-devel@nongnu.org, qemu-s390x@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Allow to extend the regions used by vfio-ccw. The first user will be handling of halt and clear subchannel. Signed-off-by: Cornelia Huck Reviewed-by: Eric Farman --- drivers/s390/cio/vfio_ccw_ops.c | 181 ++++++++++++++++++++++++---- drivers/s390/cio/vfio_ccw_private.h | 38 ++++++ include/uapi/linux/vfio.h | 2 + 3 files changed, 195 insertions(+), 26 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_ops.c b/drivers/s390/cio/vfio_ccw_op= s.c index 3fa9fc570400..5a89d09f9271 100644 --- a/drivers/s390/cio/vfio_ccw_ops.c +++ b/drivers/s390/cio/vfio_ccw_ops.c @@ -3,9 +3,11 @@ * Physical device callbacks for vfio_ccw * * Copyright IBM Corp. 2017 + * Copyright Red Hat, Inc. 2019 * * Author(s): Dong Jia Shi * Xiao Feng Ren + * Cornelia Huck */ =20 #include @@ -157,27 +159,33 @@ static void vfio_ccw_mdev_release(struct mdev_device = *mdev) { struct vfio_ccw_private *private =3D dev_get_drvdata(mdev_parent_dev(mdev)); + int i; =20 vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY, &private->nb); + + for (i =3D 0; i < private->num_regions; i++) + private->region[i].ops->release(private, &private->region[i]); + + private->num_regions =3D 0; + kfree(private->region); + private->region =3D NULL; } =20 -static ssize_t vfio_ccw_mdev_read(struct mdev_device *mdev, - char __user *buf, - size_t count, - loff_t *ppos) +static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *priva= te, + char __user *buf, size_t count, + loff_t *ppos) { - struct vfio_ccw_private *private; + loff_t pos =3D *ppos & VFIO_CCW_OFFSET_MASK; struct ccw_io_region *region; int ret; =20 - if (*ppos + count > sizeof(*region)) + if (pos + count > sizeof(*region)) return -EINVAL; =20 - private =3D dev_get_drvdata(mdev_parent_dev(mdev)); mutex_lock(&private->io_mutex); region =3D private->io_region; - if (copy_to_user(buf, (void *)region + *ppos, count)) + if (copy_to_user(buf, (void *)region + pos, count)) ret =3D -EFAULT; else ret =3D count; @@ -185,19 +193,42 @@ static ssize_t vfio_ccw_mdev_read(struct mdev_device = *mdev, return ret; } =20 -static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev, - const char __user *buf, - size_t count, - loff_t *ppos) +static ssize_t vfio_ccw_mdev_read(struct mdev_device *mdev, + char __user *buf, + size_t count, + loff_t *ppos) { + unsigned int index =3D VFIO_CCW_OFFSET_TO_INDEX(*ppos); struct vfio_ccw_private *private; + + private =3D dev_get_drvdata(mdev_parent_dev(mdev)); + + if (index >=3D VFIO_CCW_NUM_REGIONS + private->num_regions) + return -EINVAL; + + switch (index) { + case VFIO_CCW_CONFIG_REGION_INDEX: + return vfio_ccw_mdev_read_io_region(private, buf, count, ppos); + default: + index -=3D VFIO_CCW_NUM_REGIONS; + return private->region[index].ops->read(private, buf, count, + ppos); + } + + return -EINVAL; +} + +static ssize_t vfio_ccw_mdev_write_io_region(struct vfio_ccw_private *priv= ate, + const char __user *buf, + size_t count, loff_t *ppos) +{ + loff_t pos =3D *ppos & VFIO_CCW_OFFSET_MASK; struct ccw_io_region *region; int ret; =20 - if (*ppos + count > sizeof(*region)) + if (pos + count > sizeof(*region)) return -EINVAL; =20 - private =3D dev_get_drvdata(mdev_parent_dev(mdev)); if (private->state =3D=3D VFIO_CCW_STATE_NOT_OPER || private->state =3D=3D VFIO_CCW_STATE_STANDBY) return -EACCES; @@ -205,7 +236,7 @@ static ssize_t vfio_ccw_mdev_write(struct mdev_device *= mdev, return -EAGAIN; =20 region =3D private->io_region; - if (copy_from_user((void *)region + *ppos, buf, count)) { + if (copy_from_user((void *)region + pos, buf, count)) { ret =3D -EFAULT; goto out_unlock; } @@ -218,19 +249,52 @@ static ssize_t vfio_ccw_mdev_write(struct mdev_device= *mdev, return ret; } =20 -static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info) +static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + unsigned int index =3D VFIO_CCW_OFFSET_TO_INDEX(*ppos); + struct vfio_ccw_private *private; + + private =3D dev_get_drvdata(mdev_parent_dev(mdev)); + + if (index >=3D VFIO_CCW_NUM_REGIONS + private->num_regions) + return -EINVAL; + + switch (index) { + case VFIO_CCW_CONFIG_REGION_INDEX: + return vfio_ccw_mdev_write_io_region(private, buf, count, ppos); + default: + index -=3D VFIO_CCW_NUM_REGIONS; + return private->region[index].ops->write(private, buf, count, + ppos); + } + + return -EINVAL; +} + +static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info, + struct mdev_device *mdev) { + struct vfio_ccw_private *private; + + private =3D dev_get_drvdata(mdev_parent_dev(mdev)); info->flags =3D VFIO_DEVICE_FLAGS_CCW | VFIO_DEVICE_FLAGS_RESET; - info->num_regions =3D VFIO_CCW_NUM_REGIONS; + info->num_regions =3D VFIO_CCW_NUM_REGIONS + private->num_regions; info->num_irqs =3D VFIO_CCW_NUM_IRQS; =20 return 0; } =20 static int vfio_ccw_mdev_get_region_info(struct vfio_region_info *info, - u16 *cap_type_id, - void **cap_type) + struct mdev_device *mdev, + unsigned long arg) { + struct vfio_ccw_private *private; + int i; + + private =3D dev_get_drvdata(mdev_parent_dev(mdev)); switch (info->index) { case VFIO_CCW_CONFIG_REGION_INDEX: info->offset =3D 0; @@ -238,9 +302,51 @@ static int vfio_ccw_mdev_get_region_info(struct vfio_r= egion_info *info, info->flags =3D VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE; return 0; - default: - return -EINVAL; + default: /* all other regions are handled via capability chain */ + { + struct vfio_info_cap caps =3D { .buf =3D NULL, .size =3D 0 }; + struct vfio_region_info_cap_type cap_type =3D { + .header.id =3D VFIO_REGION_INFO_CAP_TYPE, + .header.version =3D 1 }; + int ret; + + if (info->index >=3D + VFIO_CCW_NUM_REGIONS + private->num_regions) + return -EINVAL; + + i =3D info->index - VFIO_CCW_NUM_REGIONS; + + info->offset =3D VFIO_CCW_INDEX_TO_OFFSET(info->index); + info->size =3D private->region[i].size; + info->flags =3D private->region[i].flags; + + cap_type.type =3D private->region[i].type; + cap_type.subtype =3D private->region[i].subtype; + + ret =3D vfio_info_add_capability(&caps, &cap_type.header, + sizeof(cap_type)); + if (ret) + return ret; + + info->flags |=3D VFIO_REGION_INFO_FLAG_CAPS; + if (info->argsz < sizeof(*info) + caps.size) { + info->argsz =3D sizeof(*info) + caps.size; + info->cap_offset =3D 0; + } else { + vfio_info_cap_shift(&caps, sizeof(*info)); + if (copy_to_user((void __user *)arg + sizeof(*info), + caps.buf, caps.size)) { + kfree(caps.buf); + return -EFAULT; + } + info->cap_offset =3D sizeof(*info); + } + + kfree(caps.buf); + + } } + return 0; } =20 static int vfio_ccw_mdev_get_irq_info(struct vfio_irq_info *info) @@ -317,6 +423,32 @@ static int vfio_ccw_mdev_set_irqs(struct mdev_device *= mdev, } } =20 +int vfio_ccw_register_dev_region(struct vfio_ccw_private *private, + unsigned int subtype, + const struct vfio_ccw_regops *ops, + size_t size, u32 flags, void *data) +{ + struct vfio_ccw_region *region; + + region =3D krealloc(private->region, + (private->num_regions + 1) * sizeof(*region), + GFP_KERNEL); + if (!region) + return -ENOMEM; + + private->region =3D region; + private->region[private->num_regions].type =3D VFIO_REGION_TYPE_CCW; + private->region[private->num_regions].subtype =3D subtype; + private->region[private->num_regions].ops =3D ops; + private->region[private->num_regions].size =3D size; + private->region[private->num_regions].flags =3D flags; + private->region[private->num_regions].data =3D data; + + private->num_regions++; + + return 0; +} + static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev, unsigned int cmd, unsigned long arg) @@ -337,7 +469,7 @@ static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *= mdev, if (info.argsz < minsz) return -EINVAL; =20 - ret =3D vfio_ccw_mdev_get_device_info(&info); + ret =3D vfio_ccw_mdev_get_device_info(&info, mdev); if (ret) return ret; =20 @@ -346,8 +478,6 @@ static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *= mdev, case VFIO_DEVICE_GET_REGION_INFO: { struct vfio_region_info info; - u16 cap_type_id =3D 0; - void *cap_type =3D NULL; =20 minsz =3D offsetofend(struct vfio_region_info, offset); =20 @@ -357,8 +487,7 @@ static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *= mdev, if (info.argsz < minsz) return -EINVAL; =20 - ret =3D vfio_ccw_mdev_get_region_info(&info, &cap_type_id, - &cap_type); + ret =3D vfio_ccw_mdev_get_region_info(&info, mdev, arg); if (ret) return ret; =20 diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_cc= w_private.h index e88237697f83..20e75f4f3695 100644 --- a/drivers/s390/cio/vfio_ccw_private.h +++ b/drivers/s390/cio/vfio_ccw_private.h @@ -3,9 +3,11 @@ * Private stuff for vfio_ccw driver * * Copyright IBM Corp. 2017 + * Copyright Red Hat, Inc. 2019 * * Author(s): Dong Jia Shi * Xiao Feng Ren + * Cornelia Huck */ =20 #ifndef _VFIO_CCW_PRIVATE_H_ @@ -19,6 +21,38 @@ #include "css.h" #include "vfio_ccw_cp.h" =20 +#define VFIO_CCW_OFFSET_SHIFT 40 +#define VFIO_CCW_OFFSET_TO_INDEX(off) (off >> VFIO_CCW_OFFSET_SHIFT) +#define VFIO_CCW_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_CCW_OFFSET_S= HIFT) +#define VFIO_CCW_OFFSET_MASK (((u64)(1) << VFIO_CCW_OFFSET_SHIFT) - 1) + +/* capability chain handling similar to vfio-pci */ +struct vfio_ccw_private; +struct vfio_ccw_region; + +struct vfio_ccw_regops { + size_t (*read)(struct vfio_ccw_private *private, char __user *buf, + size_t count, loff_t *ppos); + size_t (*write)(struct vfio_ccw_private *private, + const char __user *buf, size_t count, loff_t *ppos); + void (*release)(struct vfio_ccw_private *private, + struct vfio_ccw_region *region); +}; + +struct vfio_ccw_region { + u32 type; + u32 subtype; + const struct vfio_ccw_regops *ops; + void *data; + size_t size; + u32 flags; +}; + +int vfio_ccw_register_dev_region(struct vfio_ccw_private *private, + unsigned int subtype, + const struct vfio_ccw_regops *ops, + size_t size, u32 flags, void *data); + /** * struct vfio_ccw_private * @sch: pointer to the subchannel @@ -29,6 +63,8 @@ * @nb: notifier for vfio events * @io_region: MMIO region to input/output I/O arguments/results * @io_mutex: protect against concurrent update of I/O structures + * @region: additional regions for other subchannel operations + * @num_regions: number of additional regions * @cp: channel program for the current I/O operation * @irb: irb info received from interrupt * @scsw: scsw info @@ -44,6 +80,8 @@ struct vfio_ccw_private { struct notifier_block nb; struct ccw_io_region *io_region; struct mutex io_mutex; + struct vfio_ccw_region *region; + int num_regions; =20 struct channel_program cp; struct irb irb; diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 02bb7ad6e986..56e2413d3e00 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -353,6 +353,8 @@ struct vfio_region_gfx_edid { #define VFIO_DEVICE_GFX_LINK_STATE_DOWN 2 }; =20 +#define VFIO_REGION_TYPE_CCW (2) + /* * 10de vendor sub-type * --=20 2.17.2