Received: by 10.223.176.5 with SMTP id f5csp188179wra; Tue, 6 Feb 2018 20:10:57 -0800 (PST) X-Google-Smtp-Source: AH8x227/LhSJFZ9ZrX/K6X6ySyOdK3+1nSTjK7kVuZCURTg5vy0LWiZdNQAwYSbLjCuA/yuo5hOL X-Received: by 2002:a17:902:7e0e:: with SMTP id b14-v6mr4523104plm.97.1517976657046; Tue, 06 Feb 2018 20:10:57 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1517976657; cv=none; d=google.com; s=arc-20160816; b=L35uX11pcDq7Kc9Vl89e2HXm/uoOjTXVRrbLAPHT8cdGpXrNQxsrczCpSzIgiEtYGc VjALFl5Z6SUBtXmSwH8dNrTyvoRCZQi5hSPCQu58QEvghTBDys6yw8t+he7haCusghcv cWZVJgT6QR8EY3QVbuS5JqaFqkVKo84rvJiozd4M8z8Gfj3sf8eXqjvaQWg+P58YTAhR ZYN7w34ElGRk7ZA9YlByksyqNIejXIUOPgJBSJrRMAgb2cMWgoBjp5CDZ0fP1eNGJK6J Gfl7+AXYy63MAZoHDNccZZLIMk93guFYO+WAAc4dXOVydIxzlDSj8yIuHIWbjLPdYyB9 LibA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding :content-language:in-reply-to:mime-version:user-agent:date :message-id:from:references:cc:to:subject:dkim-signature :arc-authentication-results; bh=SJZwRyZAnOPAQSGDX4iNtOtWrkDCRz20iS2S2pamL0Q=; b=Jd7OPlGWXirQbwVBPWRSLuRhPc1lKsGBenvPlJGTluBAKoa/vHUtuiDEd2xRfgy3ks At7YdzYLnvXjLVReMWFt9lHHTMdUL8HFdKsZohJbnjGTascpHKg8boY93xbY3manBkVe 9bo3UQ2CjmXDkV0u9mGv63TQMYeN5O4CHXBV96RMXeOLYl91MYKgYwweKz92h9yGOb/J NH8kxELnii0Z4Qd5t3+ASohzEEQJRAnrMo3nYFvjRRCzwHfm4PfYsL9dewxKLZoTK/3x 2ja8c6+yBs+eYaEsCoTc7nxxjrXFQpU5/OCHacz+dUDvYbRtcJMeQnic4sMWFtvvV71f lizg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@ozlabs-ru.20150623.gappssmtp.com header.s=20150623 header.b=UC5E4Upi; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id o32-v6si459936pld.52.2018.02.06.20.10.43; Tue, 06 Feb 2018 20:10:57 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@ozlabs-ru.20150623.gappssmtp.com header.s=20150623 header.b=UC5E4Upi; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753358AbeBGEJc (ORCPT + 99 others); Tue, 6 Feb 2018 23:09:32 -0500 Received: from mail-it0-f67.google.com ([209.85.214.67]:37040 "EHLO mail-it0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752862AbeBGEJ3 (ORCPT ); Tue, 6 Feb 2018 23:09:29 -0500 Received: by mail-it0-f67.google.com with SMTP id h129so561017ita.2 for ; Tue, 06 Feb 2018 20:09:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ozlabs-ru.20150623.gappssmtp.com; s=20150623; h=subject:to:cc:references:from:message-id:date:user-agent :mime-version:in-reply-to:content-language:content-transfer-encoding; bh=SJZwRyZAnOPAQSGDX4iNtOtWrkDCRz20iS2S2pamL0Q=; b=UC5E4Upi3JIlMlOpXShG8SVuegNejnMXB4RcqO1B1lZjlqUT3W024gnamj4U3nRpkM MBiB6CCDfc+8PPMTYjZM7AJc/GlD0ggcwEzE08AOTjrghKbIDda7ES43+urQYhLwv9OS i+v2+c5jKnuytrJRUaRMYE1bKDCnNfhxYTHEWI9UxlKLiiPH7EcCUx9tRhX0bY5EJ6a7 c4HFwzZGXCUPtGZIIlCZOtajvXA3dBYfPDDjcS3wJkl6eflOKcmHLgu2bL/z0jO3Z/YU CZ7Hsa+x7loFd/34nerTNKlVASl1S1z8WxrBbjCrAkBP7KaA9AeCexOfV/uHL05C6aWB C5Bg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=SJZwRyZAnOPAQSGDX4iNtOtWrkDCRz20iS2S2pamL0Q=; b=RPRKZBZKboheo3FoMerufpSJhNI4PEGAwOqxzy611w6VZOR/D/0Q7rix2YztFrZpxn O5nxyssKcx7QqfxyyCn0MJDqQ7oIp4Z7uojl74ywIW8EmteofTvVvqbNvoZ91XzLU/r/ 4rXNbmqlnmOxA3RgagZOIyaFkzIGxcCwogsDLAwuHQGYQxxneTNDFYR3WsqE9zCo7q9b xYN0PITWz18TtJUZqQx7lqFZmDWUVvhhel9E4zRhXHFFGdc/1G6sL+KL7TZv73GVGJJJ mvb/LytfF2+MTx0zmEBiNCAxJORzOavtkNV1Wj/CEHOcaPx3w1fd+0iyAHYQY+gS1FBs w4Pg== X-Gm-Message-State: APf1xPBiE5LW2tBXSiqF/Ou92vIuERPY4YNnXEesQxlu/vad6YddHt9Z bA7yoyk5v6i4HrsSLBvhzOiV1Q== X-Received: by 10.36.250.193 with SMTP id v184mr5870221ith.64.1517976568473; Tue, 06 Feb 2018 20:09:28 -0800 (PST) Received: from [10.61.2.175] ([122.99.82.10]) by smtp.googlemail.com with ESMTPSA id c62sm347460ioj.28.2018.02.06.20.09.25 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 06 Feb 2018 20:09:28 -0800 (PST) Subject: Re: [RFC PATCH] vfio/pci: Add ioeventfd support To: Alex Williamson , kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org, qemu-devel@nongnu.org References: <20180207000731.32764.95992.stgit@gimli.home> From: Alexey Kardashevskiy Message-ID: Date: Wed, 7 Feb 2018 15:09:22 +1100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.4.0 MIME-Version: 1.0 In-Reply-To: <20180207000731.32764.95992.stgit@gimli.home> Content-Type: text/plain; charset=utf-8 Content-Language: en-AU Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 07/02/18 11:08, Alex Williamson wrote: > The ioeventfd here is actually irqfd handling of an ioeventfd such as > supported in KVM. A user is able to pre-program a device write to > occur when the eventfd triggers. This is yet another instance of > eventfd-irqfd triggering between KVM and vfio. The impetus for this > is high frequency writes to pages which are virtualized in QEMU. > Enabling this near-direct write path for selected registers within > the virtualized page can improve performance and reduce overhead. > Specifically this is initially targeted at NVIDIA graphics cards where > the driver issues a write to an MMIO register within a virtualized > region in order to allow the MSI interrupt to re-trigger. > > Signed-off-by: Alex Williamson > --- > drivers/vfio/pci/vfio_pci.c | 33 +++++++ > drivers/vfio/pci/vfio_pci_private.h | 14 +++ > drivers/vfio/pci/vfio_pci_rdwr.c | 165 ++++++++++++++++++++++++++++++++--- > include/uapi/linux/vfio.h | 24 +++++ > 4 files changed, 224 insertions(+), 12 deletions(-) > > diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c > index f041b1a6cf66..c8e7297a61a3 100644 > --- a/drivers/vfio/pci/vfio_pci.c > +++ b/drivers/vfio/pci/vfio_pci.c > @@ -302,6 +302,7 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) > { > struct pci_dev *pdev = vdev->pdev; > struct vfio_pci_dummy_resource *dummy_res, *tmp; > + struct vfio_pci_ioeventfd *ioeventfd, *ioeventfd_tmp; > int i, bar; > > /* Stop the device from further DMA */ > @@ -311,6 +312,14 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) > VFIO_IRQ_SET_ACTION_TRIGGER, > vdev->irq_type, 0, 0, NULL); > > + /* Device closed, don't need mutex here */ > + list_for_each_entry_safe(ioeventfd, ioeventfd_tmp, > + &vdev->ioeventfds_list, next) { > + vfio_virqfd_disable(&ioeventfd->virqfd); > + list_del(&ioeventfd->next); > + kfree(ioeventfd); > + } > + > vdev->virq_disabled = false; > > for (i = 0; i < vdev->num_regions; i++) > @@ -1039,6 +1048,28 @@ static long vfio_pci_ioctl(void *device_data, > > kfree(groups); > return ret; > + } else if (cmd == VFIO_DEVICE_IOEVENTFD) { > + struct vfio_device_ioeventfd ioeventfd; > + int count; > + > + minsz = offsetofend(struct vfio_device_ioeventfd, fd); > + > + if (copy_from_user(&ioeventfd, (void __user*)arg, minsz)) > + return -EFAULT; > + > + if (ioeventfd.argsz < minsz) > + return -EINVAL; > + > + if (ioeventfd.flags & ~VFIO_DEVICE_IOEVENTFD_SIZE_MASK) > + return -EINVAL; > + > + count = ioeventfd.flags & VFIO_DEVICE_IOEVENTFD_SIZE_MASK; > + > + if (hweight8(count) != 1 || ioeventfd.fd < -1) > + return -EINVAL; > + > + return vfio_pci_ioeventfd(vdev, ioeventfd.offset, > + ioeventfd.data, count, ioeventfd.fd); > } > > return -ENOTTY; > @@ -1217,6 +1248,8 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) > vdev->irq_type = VFIO_PCI_NUM_IRQS; > mutex_init(&vdev->igate); > spin_lock_init(&vdev->irqlock); > + mutex_init(&vdev->ioeventfds_lock); > + INIT_LIST_HEAD(&vdev->ioeventfds_list); > > ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev); > if (ret) { > diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h > index f561ac1c78a0..23797622396e 100644 > --- a/drivers/vfio/pci/vfio_pci_private.h > +++ b/drivers/vfio/pci/vfio_pci_private.h > @@ -29,6 +29,15 @@ > #define PCI_CAP_ID_INVALID 0xFF /* default raw access */ > #define PCI_CAP_ID_INVALID_VIRT 0xFE /* default virt access */ > > +struct vfio_pci_ioeventfd { > + struct list_head next; > + struct virqfd *virqfd; > + loff_t pos; > + uint64_t data; > + int bar; > + int count; > +}; > + > struct vfio_pci_irq_ctx { > struct eventfd_ctx *trigger; > struct virqfd *unmask; > @@ -95,6 +104,8 @@ struct vfio_pci_device { > struct eventfd_ctx *err_trigger; > struct eventfd_ctx *req_trigger; > struct list_head dummy_resources_list; > + struct mutex ioeventfds_lock; > + struct list_head ioeventfds_list; > }; > > #define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX) > @@ -120,6 +131,9 @@ extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, > extern ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, > size_t count, loff_t *ppos, bool iswrite); > > +extern long vfio_pci_ioeventfd(struct vfio_pci_device *vdev, loff_t offset, > + uint64_t data, int count, int fd); > + > extern int vfio_pci_init_perm_bits(void); > extern void vfio_pci_uninit_perm_bits(void); > > diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c > index 357243d76f10..55bb4517d4ba 100644 > --- a/drivers/vfio/pci/vfio_pci_rdwr.c > +++ b/drivers/vfio/pci/vfio_pci_rdwr.c > @@ -17,6 +17,7 @@ > #include > #include > #include > +#include > #include > > #include "vfio_pci_private.h" > @@ -113,6 +114,30 @@ static ssize_t do_io_rw(void __iomem *io, char __user *buf, > return done; > } > > +static int vfio_pci_setup_barmap(struct vfio_pci_device *vdev, int bar) > +{ > + struct pci_dev *pdev = vdev->pdev; > + int ret; > + void __iomem *io; > + > + if (vdev->barmap[bar]) > + return 0; > + > + ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); > + if (ret) > + return ret; > + > + io = pci_iomap(pdev, bar, 0); > + if (!io) { > + pci_release_selected_regions(pdev, 1 << bar); > + return -ENOMEM; > + } > + > + vdev->barmap[bar] = io; > + > + return 0; > +} > + > ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, > size_t count, loff_t *ppos, bool iswrite) > { > @@ -147,22 +172,13 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, > if (!io) > return -ENOMEM; > x_end = end; > - } else if (!vdev->barmap[bar]) { > - int ret; > - > - ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); > + } else { > + int ret = vfio_pci_setup_barmap(vdev, bar); > if (ret) > return ret; > > - io = pci_iomap(pdev, bar, 0); > - if (!io) { > - pci_release_selected_regions(pdev, 1 << bar); > - return -ENOMEM; > - } > - > - vdev->barmap[bar] = io; > - } else > io = vdev->barmap[bar]; > + } > > if (bar == vdev->msix_bar) { > x_start = vdev->msix_offset; > @@ -242,3 +258,128 @@ ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, > > return done; > } > + > +static int vfio_pci_ioeventfd_handler8(void *opaque, void *data) > +{ > + iowrite8((unsigned long)data, opaque); > + return 0; > +} > + > +static int vfio_pci_ioeventfd_handler16(void *opaque, void *data) > +{ > + iowrite16((unsigned long)data, opaque); > + return 0; > +} > + > +static int vfio_pci_ioeventfd_handler32(void *opaque, void *data) > +{ > + iowrite32((unsigned long)data, opaque); > + return 0; > +} > + > +#ifdef iowrite64 > +static int vfio_pci_ioeventfd_handler64(void *opaque, void *data) > +{ > + iowrite64((unsigned long)data, opaque); > + return 0; > +} > +#endif > + > +long vfio_pci_ioeventfd(struct vfio_pci_device *vdev, loff_t offset, > + uint64_t data, int count, int fd) > +{ > + struct pci_dev *pdev = vdev->pdev; > + loff_t pos = offset & VFIO_PCI_OFFSET_MASK; > + int ret, bar = VFIO_PCI_OFFSET_TO_INDEX(offset); > + struct vfio_pci_ioeventfd *ioeventfd; > + int (*handler)(void *, void *); > + unsigned long val; > + > + /* Only support ioeventfds into BARs */ > + if (bar > VFIO_PCI_BAR5_REGION_INDEX) > + return -EINVAL; > + > + if (pos + count > pci_resource_len(pdev, bar)) > + return -EINVAL; > + > + /* Disallow ioeventfds working around MSI-X table writes */ > + if (bar == vdev->msix_bar && > + !(pos + count <= vdev->msix_offset || > + pos >= vdev->msix_offset + vdev->msix_size)) > + return -EINVAL; > + > + switch (count) { > + case 1: > + handler = &vfio_pci_ioeventfd_handler8; > + val = data; > + break; > + case 2: > + handler = &vfio_pci_ioeventfd_handler16; > + val = le16_to_cpu(data); > + break; > + case 4: > + handler = &vfio_pci_ioeventfd_handler32; > + val = le32_to_cpu(data); > + break; > +#ifdef iowrite64 > + case 8: > + handler = &vfio_pci_ioeventfd_handler64; > + val = le64_to_cpu(data); > + break; > +#endif > + default: > + return -EINVAL; > + } > + > + ret = vfio_pci_setup_barmap(vdev, bar); > + if (ret) > + return ret; > + > + mutex_lock(&vdev->ioeventfds_lock); > + > + list_for_each_entry(ioeventfd, &vdev->ioeventfds_list, next) { > + if (ioeventfd->pos == pos && ioeventfd->bar == bar && > + ioeventfd->data == data && ioeventfd->count == count) { > + if (fd == -1) { > + vfio_virqfd_disable(&ioeventfd->virqfd); > + list_del(&ioeventfd->next); > + kfree(ioeventfd); > + ret = 0; > + } else > + ret = -EEXIST; > + > + goto out_unlock; > + } > + } > + > + if (fd < 0) { > + ret = -ENODEV; > + goto out_unlock; > + } > + > + ioeventfd = kzalloc(sizeof(*ioeventfd), GFP_KERNEL); > + if (!ioeventfd) { > + ret = -ENOMEM; > + goto out_unlock; > + } > + > + ioeventfd->pos = pos; > + ioeventfd->bar = bar; > + ioeventfd->data = data; > + ioeventfd->count = count; > + > + ret = vfio_virqfd_enable(vdev->barmap[ioeventfd->bar] + ioeventfd->pos, > + handler, NULL, (void *)val, > + &ioeventfd->virqfd, fd); > + if (ret) { > + kfree(ioeventfd); > + goto out_unlock; > + } > + > + list_add(&ioeventfd->next, &vdev->ioeventfds_list); > + > +out_unlock: > + mutex_unlock(&vdev->ioeventfds_lock); > + > + return ret; > +} > diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h > index e3301dbd27d4..07966a5f0832 100644 > --- a/include/uapi/linux/vfio.h > +++ b/include/uapi/linux/vfio.h > @@ -503,6 +503,30 @@ struct vfio_pci_hot_reset { > > #define VFIO_DEVICE_PCI_HOT_RESET _IO(VFIO_TYPE, VFIO_BASE + 13) > > +/** > + * VFIO_DEVICE_IOEVENTFD - _IOW(VFIO_TYPE, VFIO_BASE + 14, > + * struct vfio_device_ioeventfd) > + * > + * Perform a write to the device at the specified device fd offset, with > + * the specified data and width when the provided eventfd is triggered. > + * > + * Return: 0 on success, -errno on failure. > + */ > +struct vfio_device_ioeventfd { > + __u32 argsz; > + __u32 flags; > +#define VFIO_DEVICE_IOEVENTFD_8 (1 << 0) /* 1-byte write */ > +#define VFIO_DEVICE_IOEVENTFD_16 (1 << 1) /* 2-byte write */ > +#define VFIO_DEVICE_IOEVENTFD_32 (1 << 2) /* 4-byte write */ > +#define VFIO_DEVICE_IOEVENTFD_64 (1 << 3) /* 8-byte write */ > +#define VFIO_DEVICE_IOEVENTFD_SIZE_MASK (0xf) > + __u64 offset; /* device fd offset of write */ > + __u64 data; /* data to be written */ > + __s32 fd; /* -1 for de-assignment */ > +}; > + > +#define VFIO_DEVICE_IOEVENTFD _IO(VFIO_TYPE, VFIO_BASE + 14) Is this a first ioctl with endianness fixed to little-endian? I'd suggest to comment on that as things like vfio_info_cap_header do use the host endianness. > + > /* -------- API for Type1 VFIO IOMMU -------- */ > > /** > -- Alexey