From patchwork Mon Dec 18 05:02:53 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alex Williamson X-Patchwork-Id: 849791 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3z0TW919H7z9s4s for ; Mon, 18 Dec 2017 16:05:25 +1100 (AEDT) Received: from localhost ([::1]:56808 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eQncA-0002Xm-8d for incoming@patchwork.ozlabs.org; Mon, 18 Dec 2017 00:05:22 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38041) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eQnZw-0001EO-I8 for qemu-devel@nongnu.org; Mon, 18 Dec 2017 00:03:06 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eQnZt-0002Jv-Cd for qemu-devel@nongnu.org; Mon, 18 Dec 2017 00:03:04 -0500 Received: from mx1.redhat.com ([209.132.183.28]:34488) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1eQnZt-0002H5-3C for qemu-devel@nongnu.org; Mon, 18 Dec 2017 00:03:01 -0500 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3A6D35F7B7 for ; Mon, 18 Dec 2017 05:03:00 +0000 (UTC) Received: from gimli.home (ovpn-116-49.phx2.redhat.com [10.3.116.49]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0A8196E72E; Mon, 18 Dec 2017 05:02:54 +0000 (UTC) From: Alex Williamson To: qemu-devel@nongnu.org Date: Sun, 17 Dec 2017 22:02:53 -0700 Message-ID: <20171218050253.13478.49457.stgit@gimli.home> In-Reply-To: <20171218040852.13478.19208.stgit@gimli.home> References: <20171218040852.13478.19208.stgit@gimli.home> User-Agent: StGit/0.18 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Mon, 18 Dec 2017 05:03:00 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [RFC/RFT PATCH 5/5] vfio/pci: Allow relocating MSI-X MMIO 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: eric.auger@redhat.com, alex.williamson@redhat.com Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" With recently proposed kernel side vfio-pci changes, the MSI-X vector table area can be mmap'd from userspace, allowing direct access to non-MSI-X registers within the host page size of this area. However, we only get that direct access if QEMU isn't also emulating MSI-X within that same page. For x86/64 host, the system page size is 4K and the PCI spec recommends a minimum of 4K to 8K alignment to separate MSI-X from non-MSI-X registers, therefore only devices which don't honor this recommendation would see any improvement from this option. The real targets for this feature are hosts where the page size exceeds the PCI spec recommended alignment, such as ARM64 systems with 64K pages. This new x-msix-relocation option accepts the following options: off: Disable MSI-X relocation, use native device config (default) auto: Automaically relocate MSI-X MMIO to another BAR or offset based on minimum additional MMIO requirement bar0..bar5: Specify the target BAR, which will either be extended if the BAR exists or added if the BAR slot is available. Signed-off-by: Alex Williamson --- hw/vfio/pci.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/vfio/pci.h | 1 hw/vfio/trace-events | 2 + 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index c383b842da20..b4426abf297a 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -1352,6 +1352,101 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev) } } +static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev) +{ + int target_bar = -1; + size_t msix_sz; + + if (!vdev->msix || vdev->msix_relo == OFF_AUTOPCIBAR_OFF) { + return; + } + + /* The actual minimum size of MSI-X structures */ + msix_sz = (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE) + + (QEMU_ALIGN_UP(vdev->msix->entries, 64) / 8); + /* Round up to host pages, we don't want to share a page */ + msix_sz = REAL_HOST_PAGE_ALIGN(msix_sz); + /* PCI BARs must be a power of 2 */ + msix_sz = pow2ceil(msix_sz); + + /* Auto: pick the BAR that incurs the least additional MMIO space */ + if (vdev->msix_relo == OFF_AUTOPCIBAR_AUTO) { + int i; + size_t best = UINT64_MAX; + + for (i = 0; i < PCI_ROM_SLOT; i++) { + size_t size; + + if (vdev->bars[i].ioport) { + continue; + } + + /* MSI-X MMIO must reside within first 32bit offset of BAR */ + if (vdev->bars[i].size > (UINT32_MAX / 2)) + continue; + + /* + * Must be pow2, so larger of double existing or double msix_sz, + * or if BAR unimplemented, msix_sz + */ + size = MAX(vdev->bars[i].size * 2, + vdev->bars[i].size ? msix_sz * 2 : msix_sz); + + trace_vfio_msix_relo_cost(vdev->vbasedev.name, i, size); + + if (size < best) { + best = size; + target_bar = i; + } + + if (vdev->bars[i].mem64) { + i++; + } + } + } else { + target_bar = (int)(vdev->msix_relo - OFF_AUTOPCIBAR_BAR0); + } + + if (target_bar < 0 || vdev->bars[target_bar].ioport || + (!vdev->bars[target_bar].size && + target_bar > 0 && vdev->bars[target_bar - 1].mem64)) { + return; /* Go BOOM? Plumb Error */ + } + + /* + * If adding a new BAR, test if we can make it 64bit. We make it + * prefetchable since QEMU MSI-X emulation has no read side effects + * and doing so makes mapping more flexible. + */ + if (!vdev->bars[target_bar].size) { + if (target_bar < (PCI_ROM_SLOT - 1) && + !vdev->bars[target_bar + 1].size) { + vdev->bars[target_bar].mem64 = true; + vdev->bars[target_bar].type = PCI_BASE_ADDRESS_MEM_TYPE_64; + } + vdev->bars[target_bar].type |= PCI_BASE_ADDRESS_MEM_PREFETCH; + vdev->bars[target_bar].size = msix_sz; + vdev->msix->table_offset = 0; + } else { + vdev->bars[target_bar].size = MAX(vdev->bars[target_bar].size * 2, + msix_sz * 2); + /* + * Due to above size calc, MSI-X always starts halfway into the BAR, + * which will always be a separate host page. + */ + vdev->msix->table_offset = vdev->bars[target_bar].size / 2; + } + + vdev->msix->table_bar = target_bar; + vdev->msix->pba_bar = target_bar; + /* Requires 8-byte alignment, but PCI_MSIX_ENTRY_SIZE guarantees that */ + vdev->msix->pba_offset = vdev->msix->table_offset + + (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE); + + trace_vfio_msix_relo(vdev->vbasedev.name, + vdev->msix->table_bar, vdev->msix->table_offset); +} + /* * We don't have any control over how pci_add_capability() inserts * capabilities into the chain. In order to setup MSI-X we need a @@ -1430,6 +1525,8 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) vdev->msix = msix; vfio_pci_fixup_msix_region(vdev); + + vfio_pci_relocate_msix(vdev); } static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) @@ -2845,13 +2942,14 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_pci_size_rom(vdev); + vfio_bars_prepare(vdev); + vfio_msix_early_setup(vdev, &err); if (err) { error_propagate(errp, err); goto error; } - vfio_bars_prepare(vdev); vfio_bars_register(vdev); ret = vfio_add_capabilities(vdev, errp); @@ -3041,6 +3139,8 @@ static Property vfio_pci_dev_properties[] = { DEFINE_PROP_UNSIGNED_NODEFAULT("x-nv-gpudirect-clique", VFIOPCIDevice, nv_gpudirect_clique, qdev_prop_nv_gpudirect_clique, uint8_t), + DEFINE_PROP_OFF_AUTO_PCIBAR("x-msix-relocation", VFIOPCIDevice, msix_relo, + OFF_AUTOPCIBAR_OFF), /* * TODO - support passed fds... is this necessary? * DEFINE_PROP_STRING("vfiofd", VFIOPCIDevice, vfiofd_name), diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index dcdb1a806769..588381f201b4 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -135,6 +135,7 @@ typedef struct VFIOPCIDevice { (1 << VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT) int32_t bootindex; uint32_t igd_gms; + OffAutoPCIBAR msix_relo; uint8_t pm_cap; uint8_t nv_gpudirect_clique; bool pci_aer; diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index fae096c0724f..437ccdd29053 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -16,6 +16,8 @@ vfio_msix_pba_disable(const char *name) " (%s)" vfio_msix_pba_enable(const char *name) " (%s)" vfio_msix_disable(const char *name) " (%s)" vfio_msix_fixup(const char *name, int bar, uint64_t start, uint64_t end) " (%s) MSI-X region %d mmap fixup [0x%"PRIx64" - 0x%"PRIx64"]" +vfio_msix_relo_cost(const char *name, int bar, uint64_t cost) " (%s) BAR %d cost 0x%"PRIx64"" +vfio_msix_relo(const char *name, int bar, uint64_t offset) " (%s) BAR %d offset 0x%"PRIx64"" vfio_msi_enable(const char *name, int nr_vectors) " (%s) Enabled %d MSI vectors" vfio_msi_disable(const char *name) " (%s)" vfio_pci_load_rom(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s ROM:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx"