From patchwork Sat Dec 2 01:27:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Volodymyr Babchuk X-Patchwork-Id: 13476704 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 81A67C4167B for ; Sat, 2 Dec 2023 01:27:34 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.645833.1008270 (Exim 4.92) (envelope-from ) id 1r9Emw-0002wT-0H; Sat, 02 Dec 2023 01:27:22 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 645833.1008270; Sat, 02 Dec 2023 01:27:21 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1r9Emv-0002uL-Ri; Sat, 02 Dec 2023 01:27:21 +0000 Received: by outflank-mailman (input) for mailman id 645833; Sat, 02 Dec 2023 01:27:20 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1r9Emt-000271-O8 for xen-devel@lists.xenproject.org; Sat, 02 Dec 2023 01:27:19 +0000 Received: from mx0b-0039f301.pphosted.com (mx0b-0039f301.pphosted.com [148.163.137.242]) by se1-gles-flk1.inumbo.com (Halon) with ESMTPS id ed06d412-90b1-11ee-9b0f-b553b5be7939; Sat, 02 Dec 2023 02:27:16 +0100 (CET) Received: from pps.filterd (m0174680.ppops.net [127.0.0.1]) by mx0b-0039f301.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 3B20exWb004068; Sat, 2 Dec 2023 01:27:14 GMT Received: from eur02-vi1-obe.outbound.protection.outlook.com (mail-vi1eur02lp2040.outbound.protection.outlook.com [104.47.11.40]) by mx0b-0039f301.pphosted.com (PPS) with ESMTPS id 3uqt8mg1hm-4 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Sat, 02 Dec 2023 01:27:14 +0000 Received: from VI1PR03MB3710.eurprd03.prod.outlook.com (2603:10a6:803:31::18) by DU0PR03MB8599.eurprd03.prod.outlook.com (2603:10a6:10:3e6::18) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7046.27; Sat, 2 Dec 2023 01:27:09 +0000 Received: from VI1PR03MB3710.eurprd03.prod.outlook.com ([fe80::8e03:368:1fd7:1822]) by VI1PR03MB3710.eurprd03.prod.outlook.com ([fe80::8e03:368:1fd7:1822%6]) with mapi id 15.20.7046.027; Sat, 2 Dec 2023 01:27:08 +0000 X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: ed06d412-90b1-11ee-9b0f-b553b5be7939 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=LmNB+vq+TOgQkcGi81io7BXU1Y49/WmBS+YUC0NuOXDV6Yzk2lNrVuOoPfZZ4AY2g4WPSiNx8GI1/l/86DLzzHkKTKXW1nrpWF1EYgu+mZ5VGyzBRc8Pjm+kaF+67k/NqvVIpKXa8tMFedEBq1XEQlGzsJ+4b922DlPDLt639pC04KvR7b5BXvQ/8Y/fYJet7AKSo16tSGMYvnD5uC+DGy3g5OLl5f3w0f8F707PVv/Hs+wIYovUyodQ+5W1St2Fzn+22qAYlAG+VwDkn5paApHlbd0wheZ8cnG7LYirglGLVFrJT71BMIT12iURasru0Zfl7/rPnOlEZtqfDLegVA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=HU3w/MVRT7OnffWTfx0/oqZxB5j1V8feoRNAk0O5Ezo=; b=jq8IEYCGGOrhw2u32VsxwnUSe9zGje8Hg+hE4V7vAs+T49Sbxgtjz5v4szGRUPlS/44u/94qtGgI2YmaWq8TlNgRcACThbsq/gtTkeQKYP6lgLTnD2w9Tyzebay+Q7tYhAmA9Ch05zo4a1mkBhkcupmMSXIAyFrIsmxvYieLiSiDCKDw/5oW7P2QeAdHd3czatRjFXaoiFGAWFPZKgouesF5VHVMGlBbhsXH8fEw7OEXaWnMwltahJzoXutgs0DhSFZcUCi9u12gXYVR6FcI6twfx5wzfpcqlLYw3+yAeeZvdGemF9Yvmvo+9/O0trLgIHXHgytTJG284i+dOC7fiA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=epam.com; dmarc=pass action=none header.from=epam.com; dkim=pass header.d=epam.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=epam.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=HU3w/MVRT7OnffWTfx0/oqZxB5j1V8feoRNAk0O5Ezo=; b=LHdYUO2H4ADVu2K5xxb9f9z95unZ3poZ9Y4mndVEwMJNmuhf6dlyUWNcilrV2R6kB3DLUj11xictAhe99x0/a3z722GdVQ2oLEgi08bTp+QL84bN0Al2+yVqnMEmMBpnTIpar4eAYSoPttm+FrkwPDAOpV7nhrX8ipd17TqRXMp4fCz1ExgklBYIBI6zXvcST0YXSyL5t2tV2HD5OqgDZJ0TPqvdWgUAvx/k+nN/rPOXJbbtw2fricXkhJnTaS3ay9SPxYl/80cILpPE+nG1M7exs5cKL7jXc5Q9kTaGhgcG7MjbShTz+p2ovMfD2hJciS+5f+i6cWytuAyNULnhVA== From: Volodymyr Babchuk To: "xen-devel@lists.xenproject.org" CC: Stewart Hildebrand , Oleksandr Andrushchenko , =?utf-8?q?Roger?= =?utf-8?q?_Pau_Monn=C3=A9?= , Volodymyr Babchuk Subject: [PATCH v11 10/17] vpci/header: handle p2m range sets per BAR Thread-Topic: [PATCH v11 10/17] vpci/header: handle p2m range sets per BAR Thread-Index: AQHaJL6ovuONmUhRl0Ozo/OYp0VBgw== Date: Sat, 2 Dec 2023 01:27:05 +0000 Message-ID: <20231202012556.2012281-11-volodymyr_babchuk@epam.com> References: <20231202012556.2012281-1-volodymyr_babchuk@epam.com> In-Reply-To: <20231202012556.2012281-1-volodymyr_babchuk@epam.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-mailer: git-send-email 2.42.0 x-ms-publictraffictype: Email x-ms-traffictypediagnostic: VI1PR03MB3710:EE_|DU0PR03MB8599:EE_ x-ms-office365-filtering-correlation-id: 8c0d6dd1-22ac-4fdf-8f1a-08dbf2d5cd2c x-ld-processed: b41b72d0-4e9f-4c26-8a69-f949f367c91d,ExtAddr x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: ACc05rBYttSwdH0bhs5E9hNr/W3ulRZmqIMNWEWDv0GCUayoROtm5kKes8ORIyHI/3pdc33pdRKzrXEeBAbHZBOaBE3A4qxG+tadd1++YLaKn7pyPlq1DBC0l7UefgMD3lmb0NtI6qF2M5iJoKHZHdnP0r/BdNiAAYtR1kEnlP08R8jTdmmu5cyIvkiiw9kQyUfN20nbldXCnKEVIthybf+PfGFSP0DEV6dMxOhWxuIp1nfLOnpKyh3IroN6XrcxNxVMpIjlUFuO4BdaZKuSPwhGHi2J2DpwBJkdsjt40cj/CU4SJxN2ZgHPcFoHJTAxVA8+W2709iZCHzvfzR31rcVCZV/eVD3s/JDehTHUM3NgITLWJv+Fd6eV6RUZmiYnf+k2Pah38MbCypllkEhbLQYusy/NWKAIfphbOpdRapdtks8FLz5dOrZ9MGTQDWeUijCdncuuCV15RWyo+yBx+45/xE595MRNOvNDpXdqGWYgcVWlXezl2isx4fYww7PS/5mInujOTnHQqO/fNzM29BGm/lrCzwKXr2SfqUcPoAIyEUgWGBW1SIpjpj4hB/L9melAjtv28CZSfORoW9Eev5Nr0VfVOisl1idwI3V7Q9+cFNrm4yqjHXgAATL8aI1+JBQ9OGWmzT9zeQEheiTll2rr38WckUCIuoamW2Q0lZI= x-forefront-antispam-report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:VI1PR03MB3710.eurprd03.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230031)(136003)(396003)(39860400002)(346002)(376002)(366004)(230922051799003)(230273577357003)(230173577357003)(186009)(64100799003)(451199024)(1800799012)(5660300002)(86362001)(4326008)(8676002)(8936002)(2906002)(30864003)(38070700009)(41300700001)(36756003)(2616005)(107886003)(1076003)(6512007)(6506007)(55236004)(83380400001)(6486002)(478600001)(26005)(71200400001)(6666004)(38100700002)(122000001)(316002)(91956017)(54906003)(64756008)(66446008)(6916009)(66476007)(66556008)(76116006)(66946007);DIR:OUT;SFP:1101; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?OPT9tZLGLIguhAs+mWeiFApyMMBQ?= =?utf-8?q?Bv65pcAprDEEjAPDicARR6WTUo+5xyIYdCLjVdhbhtAShxBYGjcPe5j2p6LsO1RmR?= =?utf-8?q?z2qGfdx6hi6+Kh/CYe/mPWx562REj+jX65Pst9IdT0JiCkW5QrDtFyheA2QLCgqao?= =?utf-8?q?y5c8VRvmqVA/qJ/cdnwFmkWZoNrthy3X4V95Ur3xJvBW6WA1dPnpwxzwA4RJ7AYqi?= =?utf-8?q?OmCK9vTmbxG4zmiNgBhC6/jy2VyqiUuNBWsEcGJ5HFUqsMuV1cmkw4X8KO3ycQLy0?= =?utf-8?q?qz6T9/MzFFpX1c5kNQdsMIoQ9BucpVFRHuVw8eB6hr6ynB06wd+KnDDdDK6ujopwS?= =?utf-8?q?UoaqPogGQ1XArzv2cDXcX2wW+k+Oo5RGu8H8m0uN0NHWA60s8T6FtIH4Yf9NkCfjL?= =?utf-8?q?obigeniPAjdx+68RqL5/J47dmGFCUQQdSXIzVqOtBalZuAnUPpZOMDR2WCPMwJAG5?= =?utf-8?q?WCOTu+PZWu6IyBeQukmtcmTuPNOkjFC20rQ6EO3EfKtFwFtmZOs1dNzXXYGoxm6qf?= =?utf-8?q?nT58fRdAzU5mxNS39fNWNptehurMo/uWXUwX/n5LrYLk88xTy5V/XbDhkK7Ywpxx3?= =?utf-8?q?oba77vRh+hzxbbJvpT9frO0nUdfNpZ5ofiHoi8Jf0ggGnrpkfb3aBy/14w9jAYr7/?= =?utf-8?q?JODqgOnvaEos4UZGZsOdLoddmbem8w5Ze7RSQcNlYm7hdPfw7+Y8m0CD5IAP3fMfO?= =?utf-8?q?Ik1sbH3kFqpjnQ1aOQG8nLkFi3Sjhoel2v5ryUDo+cMIX8xq9K1pcmfU97xu103/g?= =?utf-8?q?teFcGTmcPxVMFTs7elbOAOLJXSI+e7QDEM916B4t7lPHX2H1/MGdZoXoe0skT4mLe?= =?utf-8?q?jh3EZ61UW9tpxQMAhfY4u7Y/9akiWaZtyvja8cjMLcznlLF8t13k/s8N+P2a9W/Qd?= =?utf-8?q?QdhTYgBG/XnLqT3gTx/lfxUnKK2QmeCiwwkJ5gddSdMsqg9PR48BH0Hq7wbsEjrAu?= =?utf-8?q?Yn5qeTJEmyWEUnE7ctpC6lkFBHDU7bcJ/R3evSgDB/i+3DcUbJ5MMQsoCrYpJS8qa?= =?utf-8?q?OQZhoD0BIPeFhru/J6n+GL7jkz4Af9XKqLotgHQi5TA6dAztfjgDGPEIu+GFaql+2?= =?utf-8?q?rxr48/hajQ8SM1viol4+p/LFUX3/BZwRXkH4dXjPEsMvVk3D7VeR/pL35szH81fdp?= =?utf-8?q?d172OJCrj3aLnP8vrtrSAXSoyFOazcvefW0ZS/6kRA4dpztMhEEcystb3d+xZ0AgD?= =?utf-8?q?uPQYQ2mS8ob42g4VZBXJAMifwJUy0ms/x9w4gkZu1Rduhs3zzhvEAkPV08kKWr6Oc?= =?utf-8?q?aBwSEJ9nAg+O4NHINq5gL6jeQoikrwegKHBD09DGl6Z+9mDqP+yhWxPZ3zYI9eJSH?= =?utf-8?q?QkpFI95vQNHzMBtjG6qke2uDWavzHyyflU1IWxECN1OchCksUCiblUFJrDfNVWVWZ?= =?utf-8?q?dRbc0Jcl3k3AduC2J54vkkv6yp4kJZNfKIqqj1Xf0wX8fpx83ZgHBleh2XZMvJNl/?= =?utf-8?q?Or8OZBobyLJkQN2ykK/87tH/tjbz9lWUT0tT3zZ4RGI7a/338MJLzZoyVDDSmS+y6?= =?utf-8?q?cqjrXI+cZvruOgoMltOV0Fdecaow7jdgAA=3D=3D?= Content-ID: <17F77D55BA824E4184BB3E1AAC4D11DB@eurprd03.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: epam.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: VI1PR03MB3710.eurprd03.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8c0d6dd1-22ac-4fdf-8f1a-08dbf2d5cd2c X-MS-Exchange-CrossTenant-originalarrivaltime: 02 Dec 2023 01:27:05.4098 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: b41b72d0-4e9f-4c26-8a69-f949f367c91d X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: qXvwjJSqm2E1ESW1zU1Ri18Umjfw4XchjKLd5GHJJwKzNP5oQujUKwrmV7jy+C2xZPRh2mzUdPEGukXbSXJQ7H2UxUQnLmoMheeHEm5di/o= X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU0PR03MB8599 X-Proofpoint-ORIG-GUID: 4Swmap5dXgCDj_U_mu-8DJ8dqBQi8fJV X-Proofpoint-GUID: 4Swmap5dXgCDj_U_mu-8DJ8dqBQi8fJV X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.997,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2023-12-01_24,2023-11-30_01,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 bulkscore=0 phishscore=0 suspectscore=0 impostorscore=0 mlxscore=0 adultscore=0 malwarescore=0 lowpriorityscore=0 mlxlogscore=999 clxscore=1015 priorityscore=1501 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2311060000 definitions=main-2312020008 From: Oleksandr Andrushchenko Instead of handling a single range set, that contains all the memory regions of all the BARs and ROM, have them per BAR. As the range sets are now created when a PCI device is added and destroyed when it is removed so make them named and accounted. Note that rangesets were chosen here despite there being only up to 3 separate ranges in each set (typically just 1). But rangeset per BAR was chosen for the ease of implementation and existing code re-usability. Also note that error handling of vpci_process_pending() is slightly modified, and that vPCI handlers are no longer removed if the creation of the mappings in vpci_process_pending() fails, as that's unlikely to lead to a functional device in any case. This is in preparation of making non-identity mappings in p2m for the MMIOs. Signed-off-by: Oleksandr Andrushchenko Signed-off-by: Volodymyr Babchuk Reviewed-by: Roger Pau Monné --- In v11: - Modified commit message to note changes in error handling in vpci_process_pending() - Removed redundant ASSERT() in defer_map. There is no reason to introduce it in this patch and there is no other patch where introducing that ASSERT() was appropriate. - Fixed formatting - vpci_process_pending() clears v->vpci.pdev if it failed checks at the beginning - Added Roger's R-B tag In v10: - Added additional checks to vpci_process_pending() - vpci_process_pending() now clears rangeset in case of failure - Fixed locks in vpci_process_pending() - Fixed coding style issues - Fixed error handling in init_bars In v9: - removed d->vpci.map_pending in favor of checking v->vpci.pdev != NULL - printk -> gprintk - renamed bar variable to fix shadowing - fixed bug with iterating on remote device's BARs - relaxed lock in vpci_process_pending - removed stale comment Since v6: - update according to the new locking scheme - remove odd fail label in modify_bars Since v5: - fix comments - move rangeset allocation to init_bars and only allocate for MAPPABLE BARs - check for overlap with the already setup BAR ranges Since v4: - use named range sets for BARs (Jan) - changes required by the new locking scheme - updated commit message (Jan) Since v3: - re-work vpci_cancel_pending accordingly to the per-BAR handling - s/num_mem_ranges/map_pending and s/uint8_t/bool - ASSERT(bar->mem) in modify_bars - create and destroy the rangesets on add/remove --- xen/drivers/vpci/header.c | 257 ++++++++++++++++++++++++++------------ xen/drivers/vpci/vpci.c | 6 + xen/include/xen/vpci.h | 2 +- 3 files changed, 185 insertions(+), 80 deletions(-) diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c index 43216429d9..7c84cee5d1 100644 --- a/xen/drivers/vpci/header.c +++ b/xen/drivers/vpci/header.c @@ -161,63 +161,107 @@ static void modify_decoding(const struct pci_dev *pdev, uint16_t cmd, bool vpci_process_pending(struct vcpu *v) { - if ( v->vpci.mem ) + struct pci_dev *pdev = v->vpci.pdev; + struct map_data data = { + .d = v->domain, + .map = v->vpci.cmd & PCI_COMMAND_MEMORY, + }; + struct vpci_header *header = NULL; + unsigned int i; + + if ( !pdev ) + return false; + + read_lock(&v->domain->pci_lock); + + if ( !pdev->vpci || (v->domain != pdev->domain) ) + { + v->vpci.pdev = NULL; + read_unlock(&v->domain->pci_lock); + return false; + } + + header = &pdev->vpci->header; + for ( i = 0; i < ARRAY_SIZE(header->bars); i++ ) { - struct map_data data = { - .d = v->domain, - .map = v->vpci.cmd & PCI_COMMAND_MEMORY, - }; - int rc = rangeset_consume_ranges(v->vpci.mem, map_range, &data); + struct vpci_bar *bar = &header->bars[i]; + int rc; + + if ( rangeset_is_empty(bar->mem) ) + continue; + + rc = rangeset_consume_ranges(bar->mem, map_range, &data); if ( rc == -ERESTART ) + { + read_unlock(&v->domain->pci_lock); return true; + } - write_lock(&v->domain->pci_lock); - spin_lock(&v->vpci.pdev->vpci->lock); - /* Disable memory decoding unconditionally on failure. */ - modify_decoding(v->vpci.pdev, - rc ? v->vpci.cmd & ~PCI_COMMAND_MEMORY : v->vpci.cmd, - !rc && v->vpci.rom_only); - spin_unlock(&v->vpci.pdev->vpci->lock); - - rangeset_destroy(v->vpci.mem); - v->vpci.mem = NULL; if ( rc ) - /* - * FIXME: in case of failure remove the device from the domain. - * Note that there might still be leftover mappings. While this is - * safe for Dom0, for DomUs the domain will likely need to be - * killed in order to avoid leaking stale p2m mappings on - * failure. - */ - vpci_deassign_device(v->vpci.pdev); - write_unlock(&v->domain->pci_lock); + { + spin_lock(&pdev->vpci->lock); + /* Disable memory decoding unconditionally on failure. */ + modify_decoding(pdev, v->vpci.cmd & ~PCI_COMMAND_MEMORY, + false); + spin_unlock(&pdev->vpci->lock); + + /* Clean all the rangesets */ + for ( i = 0; i < ARRAY_SIZE(header->bars); i++ ) + if ( !rangeset_is_empty(header->bars[i].mem) ) + rangeset_empty(header->bars[i].mem); + + v->vpci.pdev = NULL; + + read_unlock(&v->domain->pci_lock); + + if ( !is_hardware_domain(v->domain) ) + domain_crash(v->domain); + + return false; + } } + v->vpci.pdev = NULL; + + spin_lock(&pdev->vpci->lock); + modify_decoding(pdev, v->vpci.cmd, v->vpci.rom_only); + spin_unlock(&pdev->vpci->lock); + + read_unlock(&v->domain->pci_lock); return false; } static int __init apply_map(struct domain *d, const struct pci_dev *pdev, - struct rangeset *mem, uint16_t cmd) + uint16_t cmd) { struct map_data data = { .d = d, .map = true }; - int rc; + struct vpci_header *header = &pdev->vpci->header; + int rc = 0; + unsigned int i; ASSERT(rw_is_write_locked(&d->pci_lock)); - while ( (rc = rangeset_consume_ranges(mem, map_range, &data)) == -ERESTART ) + for ( i = 0; i < ARRAY_SIZE(header->bars); i++ ) { - /* - * It's safe to drop and reacquire the lock in this context - * without risking pdev disappearing because devices cannot be - * removed until the initial domain has been started. - */ - write_unlock(&d->pci_lock); - process_pending_softirqs(); - write_lock(&d->pci_lock); - } + struct vpci_bar *bar = &header->bars[i]; + + if ( rangeset_is_empty(bar->mem) ) + continue; - rangeset_destroy(mem); + while ( (rc = rangeset_consume_ranges(bar->mem, map_range, + &data)) == -ERESTART ) + { + /* + * It's safe to drop and reacquire the lock in this context + * without risking pdev disappearing because devices cannot be + * removed until the initial domain has been started. + */ + write_unlock(&d->pci_lock); + process_pending_softirqs(); + write_lock(&d->pci_lock); + } + } if ( !rc ) modify_decoding(pdev, cmd, false); @@ -225,7 +269,7 @@ static int __init apply_map(struct domain *d, const struct pci_dev *pdev, } static void defer_map(struct domain *d, struct pci_dev *pdev, - struct rangeset *mem, uint16_t cmd, bool rom_only) + uint16_t cmd, bool rom_only) { struct vcpu *curr = current; @@ -236,7 +280,6 @@ static void defer_map(struct domain *d, struct pci_dev *pdev, * started for the same device if the domain is not well-behaved. */ curr->vpci.pdev = pdev; - curr->vpci.mem = mem; curr->vpci.cmd = cmd; curr->vpci.rom_only = rom_only; /* @@ -250,33 +293,33 @@ static void defer_map(struct domain *d, struct pci_dev *pdev, static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) { struct vpci_header *header = &pdev->vpci->header; - struct rangeset *mem = rangeset_new(NULL, NULL, 0); struct pci_dev *tmp, *dev = NULL; const struct domain *d; const struct vpci_msix *msix = pdev->vpci->msix; - unsigned int i; + unsigned int i, j; int rc; ASSERT(rw_is_write_locked(&pdev->domain->pci_lock)); - if ( !mem ) - return -ENOMEM; - /* - * Create a rangeset that represents the current device BARs memory region - * and compare it against all the currently active BAR memory regions. If - * an overlap is found, subtract it from the region to be mapped/unmapped. + * Create a rangeset per BAR that represents the current device memory + * region and compare it against all the currently active BAR memory + * regions. If an overlap is found, subtract it from the region to be + * mapped/unmapped. * - * First fill the rangeset with all the BARs of this device or with the ROM + * First fill the rangesets with the BAR of this device or with the ROM * BAR only, depending on whether the guest is toggling the memory decode * bit of the command register, or the enable bit of the ROM BAR register. */ for ( i = 0; i < ARRAY_SIZE(header->bars); i++ ) { - const struct vpci_bar *bar = &header->bars[i]; + struct vpci_bar *bar = &header->bars[i]; unsigned long start = PFN_DOWN(bar->addr); unsigned long end = PFN_DOWN(bar->addr + bar->size - 1); + if ( !bar->mem ) + continue; + if ( !MAPPABLE_BAR(bar) || (rom_only ? bar->type != VPCI_BAR_ROM : (bar->type == VPCI_BAR_ROM && !header->rom_enabled)) || @@ -292,14 +335,31 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) continue; } - rc = rangeset_add_range(mem, start, end); + rc = rangeset_add_range(bar->mem, start, end); if ( rc ) { printk(XENLOG_G_WARNING "Failed to add [%lx, %lx]: %d\n", start, end, rc); - rangeset_destroy(mem); return rc; } + + /* Check for overlap with the already setup BAR ranges. */ + for ( j = 0; j < i; j++ ) + { + struct vpci_bar *prev_bar = &header->bars[j]; + + if ( rangeset_is_empty(prev_bar->mem) ) + continue; + + rc = rangeset_remove_range(prev_bar->mem, start, end); + if ( rc ) + { + gprintk(XENLOG_WARNING, + "%pp: failed to remove overlapping range [%lx, %lx]: %d\n", + &pdev->sbdf, start, end, rc); + return rc; + } + } } /* Remove any MSIX regions if present. */ @@ -309,14 +369,21 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) unsigned long end = PFN_DOWN(vmsix_table_addr(pdev->vpci, i) + vmsix_table_size(pdev->vpci, i) - 1); - rc = rangeset_remove_range(mem, start, end); - if ( rc ) + for ( j = 0; j < ARRAY_SIZE(header->bars); j++ ) { - printk(XENLOG_G_WARNING - "Failed to remove MSIX table [%lx, %lx]: %d\n", - start, end, rc); - rangeset_destroy(mem); - return rc; + const struct vpci_bar *bar = &header->bars[j]; + + if ( rangeset_is_empty(bar->mem) ) + continue; + + rc = rangeset_remove_range(bar->mem, start, end); + if ( rc ) + { + gprintk(XENLOG_WARNING, + "%pp: failed to remove MSIX table [%lx, %lx]: %d\n", + &pdev->sbdf, start, end, rc); + return rc; + } } } @@ -356,27 +423,37 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) for ( i = 0; i < ARRAY_SIZE(tmp->vpci->header.bars); i++ ) { - const struct vpci_bar *bar = &tmp->vpci->header.bars[i]; - unsigned long start = PFN_DOWN(bar->addr); - unsigned long end = PFN_DOWN(bar->addr + bar->size - 1); - - if ( !bar->enabled || - !rangeset_overlaps_range(mem, start, end) || - /* - * If only the ROM enable bit is toggled check against - * other BARs in the same device for overlaps, but not - * against the same ROM BAR. - */ - (rom_only && tmp == pdev && bar->type == VPCI_BAR_ROM) ) + const struct vpci_bar *remote_bar = &tmp->vpci->header.bars[i]; + unsigned long start = PFN_DOWN(remote_bar->addr); + unsigned long end = PFN_DOWN(remote_bar->addr + + remote_bar->size - 1); + + if ( !remote_bar->enabled ) continue; - rc = rangeset_remove_range(mem, start, end); - if ( rc ) + for ( j = 0; j < ARRAY_SIZE(header->bars); j++) { - printk(XENLOG_G_WARNING "Failed to remove [%lx, %lx]: %d\n", - start, end, rc); - rangeset_destroy(mem); - return rc; + const struct vpci_bar *bar = &header->bars[j]; + + if ( !rangeset_overlaps_range(bar->mem, start, end) || + /* + * If only the ROM enable bit is toggled check against + * other BARs in the same device for overlaps, but not + * against the same ROM BAR. + */ + (rom_only && + tmp == pdev && + bar->type == VPCI_BAR_ROM) ) + continue; + + rc = rangeset_remove_range(bar->mem, start, end); + if ( rc ) + { + gprintk(XENLOG_WARNING, + "%pp: failed to remove [%lx, %lx]: %d\n", + &pdev->sbdf, start, end, rc); + return rc; + } } } } @@ -400,10 +477,10 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) * will always be to establish mappings and process all the BARs. */ ASSERT((cmd & PCI_COMMAND_MEMORY) && !rom_only); - return apply_map(pdev->domain, pdev, mem, cmd); + return apply_map(pdev->domain, pdev, cmd); } - defer_map(dev->domain, dev, mem, cmd, rom_only); + defer_map(dev->domain, dev, cmd, rom_only); return 0; } @@ -598,6 +675,18 @@ static void cf_check rom_write( rom->addr = val & PCI_ROM_ADDRESS_MASK; } +static int bar_add_rangeset(const struct pci_dev *pdev, struct vpci_bar *bar, + unsigned int i) +{ + char str[32]; + + snprintf(str, sizeof(str), "%pp:BAR%u", &pdev->sbdf, i); + + bar->mem = rangeset_new(pdev->domain, str, RANGESETF_no_print); + + return !bar->mem ? -ENOMEM : 0; +} + static int cf_check init_bars(struct pci_dev *pdev) { uint16_t cmd; @@ -679,6 +768,10 @@ static int cf_check init_bars(struct pci_dev *pdev) else bars[i].type = VPCI_BAR_MEM32; + rc = bar_add_rangeset(pdev, &bars[i], i); + if ( rc ) + goto fail; + rc = pci_size_mem_bar(pdev->sbdf, reg, &addr, &size, (i == num_bars - 1) ? PCI_BAR_LAST : 0); if ( rc < 0 ) @@ -728,6 +821,12 @@ static int cf_check init_bars(struct pci_dev *pdev) rom_reg, 4, rom); if ( rc ) rom->type = VPCI_BAR_EMPTY; + else + { + rc = bar_add_rangeset(pdev, rom, i); + if ( rc ) + goto fail; + } } } else diff --git a/xen/drivers/vpci/vpci.c b/xen/drivers/vpci/vpci.c index 9dacbcf958..8865c1580e 100644 --- a/xen/drivers/vpci/vpci.c +++ b/xen/drivers/vpci/vpci.c @@ -38,6 +38,8 @@ extern vpci_register_init_t *const __end_vpci_array[]; void vpci_deassign_device(struct pci_dev *pdev) { + unsigned int i; + ASSERT(rw_is_write_locked(&pdev->domain->pci_lock)); if ( !has_vpci(pdev->domain) || !pdev->vpci ) @@ -63,6 +65,10 @@ void vpci_deassign_device(struct pci_dev *pdev) if ( pdev->vpci->msix->table[i] ) iounmap(pdev->vpci->msix->table[i]); } + + for ( i = 0; i < ARRAY_SIZE(pdev->vpci->header.bars); i++ ) + rangeset_destroy(pdev->vpci->header.bars[i].mem); + xfree(pdev->vpci->msix); xfree(pdev->vpci->msi); xfree(pdev->vpci); diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h index 2028f2151f..18a0eca3da 100644 --- a/xen/include/xen/vpci.h +++ b/xen/include/xen/vpci.h @@ -72,6 +72,7 @@ struct vpci { /* Guest address. */ uint64_t guest_addr; uint64_t size; + struct rangeset *mem; enum { VPCI_BAR_EMPTY, VPCI_BAR_IO, @@ -156,7 +157,6 @@ struct vpci { struct vpci_vcpu { /* Per-vcpu structure to store state while {un}mapping of PCI BARs. */ - struct rangeset *mem; struct pci_dev *pdev; uint16_t cmd; bool rom_only : 1;