From patchwork Tue Feb 6 13:39:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philipp Stanner X-Patchwork-Id: 197447 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7301:168b:b0:106:860b:bbdd with SMTP id ma11csp1543783dyb; Tue, 6 Feb 2024 05:42:02 -0800 (PST) X-Google-Smtp-Source: AGHT+IEerEjkc3T9O4fubbbdXwCv6w87iZs6Gdb827mDrgsADbS7VW13+ei8eIUuOmRQ6LSNc2wc X-Received: by 2002:a17:906:6c8c:b0:a37:249d:9ff6 with SMTP id s12-20020a1709066c8c00b00a37249d9ff6mr1765037ejr.69.1707226922383; Tue, 06 Feb 2024 05:42:02 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1707226922; cv=pass; d=google.com; s=arc-20160816; b=FJqJfEXZkA+Ma/ILkRcZhv74FbFTakKu6tKaojNJpvSup7RiKMjksjTDRqAhDK0qlY KyWLdPnPY+7OkzTRM6vVKw6qoWuXyHKazf5TVi9QhBc3Bsv6dqfFt4MkkKw/9TY2Tfiw 9zatOZ0QB7y4WDywFvGE9sNWe2vhhlXvpgId0V9DAZycuJIxU30JlLizikcU5cz74vIB tlFp6ZH0RJYHFf8sQYoWk6yqlAXUCcWMYAD537rlY6ryMygr4ZZGeynESZqRNewInRJ+ eNW6T88+NZ8GSMY+d0nR0AA0J82mc6fzQBGheA7Cvk9qrVEX+aW5A6DpiFh7RvXsg1Mp TwoQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=tQLdT5/oe3g+sF83qFqQEI70V41MpGXR9AOtVTLwpto=; fh=Zc4FofXHxsEj25VTDjkAYkLExmPJ0wZKabTZMYkuooA=; b=DyzFjlhk7ya1WAwpC46dut8N4hxUjSvMBJJzZHUloIZBQRqwwq326yeabnvqW72Eme McTLCWldVAqNlFQ45SpEBRTdtgadJCS2uB8HJtsMPC2SLjsDc/J+ITeCG7QbOfjYnoi9 I8qmzeLvrKLFw54nYOvlLoCG4I4Obyts4aUdRltUemm5tCvv8jIReaSK4+HFNN8gYM8w xrnQ8C3Nz0Hpc6V53XbW7Cy7v+CuufL+SRTBmKqx40RGko1tFN36qlone8VwONgHdjvW FTc7REVF0wiOi7GwxifKQydbfY7f0+tyoGTZ8/uan+A/BJpiTZ1xTGQ4ROJs44QAkoju O6mg==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=KgX9RTYo; arc=pass (i=1 spf=pass spfdomain=redhat.com dkim=pass dkdomain=redhat.com dmarc=pass fromdomain=redhat.com); spf=pass (google.com: domain of linux-kernel+bounces-55032-ouuuleilei=gmail.com@vger.kernel.org designates 2604:1380:4601:e00::3 as permitted sender) smtp.mailfrom="linux-kernel+bounces-55032-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com X-Forwarded-Encrypted: i=1; AJvYcCU244ZxE0j3AeOpjCsxxTUTAEon+auWwT5nVLB5KIL7Fp1XvBvYmT2Y6D7HwaSyTteomdw8WkrxsGM8Swjo7Au1HoBzRw== Received: from am.mirrors.kernel.org (am.mirrors.kernel.org. [2604:1380:4601:e00::3]) by mx.google.com with ESMTPS id gu10-20020a170906f28a00b00a338fffc21fsi1145219ejb.670.2024.02.06.05.42.02 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Feb 2024 05:42:02 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-55032-ouuuleilei=gmail.com@vger.kernel.org designates 2604:1380:4601:e00::3 as permitted sender) client-ip=2604:1380:4601:e00::3; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=KgX9RTYo; arc=pass (i=1 spf=pass spfdomain=redhat.com dkim=pass dkdomain=redhat.com dmarc=pass fromdomain=redhat.com); spf=pass (google.com: domain of linux-kernel+bounces-55032-ouuuleilei=gmail.com@vger.kernel.org designates 2604:1380:4601:e00::3 as permitted sender) smtp.mailfrom="linux-kernel+bounces-55032-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id 0DF621F27427 for ; Tue, 6 Feb 2024 13:41:47 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 58C5213247D; Tue, 6 Feb 2024 13:40:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="KgX9RTYo" Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3C04313175F for ; Tue, 6 Feb 2024 13:40:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707226838; cv=none; b=tw5lXU/UkMEXD7L8i1m6dtWjRwTOTdOF9sFcxm8T3SBs/+zJssTwH5f5cMmfvoIvV0SS60I1p0bgmD3AiEbwCqS3tgS1IniRRvpuyGYS7a7v6vrR1wu6fhiHr3PRrIfqth2z8SMxt0boXzC8dsPgKnQs6RfZIyDmuYDRTi2GDm8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707226838; c=relaxed/simple; bh=lcMUgUb3jVUPExIMieyUd2sDp2aU5XE8ZW1LqgOKaIE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DbWz2f0iQ30kjz9HKoi1nnxccQwYCdfXPdCgS3DZ/cmkEpuPthssSZOYy13THVNgnfo2CbbjEBPqC96rZMl9eS2kev65wEnGYeOBgYzIJQGWTaPcc6SK8jMJuXW/MNhc6ft6xJKzN1Yxdsytjgzx+0SY8rEiMW5D2pGB9fyO0fA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=KgX9RTYo; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1707226835; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tQLdT5/oe3g+sF83qFqQEI70V41MpGXR9AOtVTLwpto=; b=KgX9RTYozWUART/djuxF6X+yCr1ibSPzqtNPkEg4PbAn1FzMjeq2rAx0n3lb1Njsrng2yl ZD01PFy3SUxCbpQwfWUD2CUv2axw3zo0mxUCW9Gp+HKaZOi7ecpE3bsCgXy9wcpVjGSMG+ DOU3LmpKC1VE7sSVtYLpf7Z82VLKExU= Received: from mail-qk1-f198.google.com (mail-qk1-f198.google.com [209.85.222.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-661-ctIr52ZzMpOie5xpCphe1A-1; Tue, 06 Feb 2024 08:40:33 -0500 X-MC-Unique: ctIr52ZzMpOie5xpCphe1A-1 Received: by mail-qk1-f198.google.com with SMTP id af79cd13be357-78356ddf3cfso293207985a.1 for ; Tue, 06 Feb 2024 05:40:33 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707226833; x=1707831633; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=tQLdT5/oe3g+sF83qFqQEI70V41MpGXR9AOtVTLwpto=; b=BUb3YXytK/F6dPGOkqgxNh6okmclsR5cwYs6gXTq8pM7hXysfoESavOW4Kuro53fqQ e+ycwly89bJBhzdEV+vPPQX9y3K+QNR4RzFZ9nbmwrM6c4jWgzZzMHM2axl1tx3MLWdg 09REeBTtNjbirn5pMXGrazp/OUuWYJGUNRIzRDSgDalwiQqIQ+X1LUjxOcw7sxdKHun5 l8quU0TMuCHugP//ofSxWPASHd0WFDtHst934VDR5poW/faKRkOk2OXNBsqhsXZiAlOU o5OTE+p8LGVGlgUDXCQWOj4tdIqsfO5T4TVoxqZ+VQQeP7omfSHdQ6jYR3SoGICNEqxy H/Pw== X-Forwarded-Encrypted: i=1; AJvYcCXWb+Bj1p4pgRUz2VNtwypQ205LI5rRTFZmA8Spw0L+OXsqEXspeumdWbKm78v7D3BmEBVzLOF4Xd7rv3p00gJmk5ZQnHIQiRvfInFU X-Gm-Message-State: AOJu0Yy+rTqrHBePdnJFMYtDcHqL7Ph5lxWaIxBpazuWLkeB/BYscNBq jmtpyNKQKF2azjFGc0cond77UZYvnaQHbTMQlColkLV+lS5hcUnmY4pRwrPukiJqz053A6QpWzV P56MGgtarfg/RNW/dAgCztArVjVpUDVsbQTEsdoX6xJHMn/kzL9lOq7uZq9wInQ== X-Received: by 2002:a05:620a:40c5:b0:785:4475:7f7d with SMTP id g5-20020a05620a40c500b0078544757f7dmr2814019qko.4.1707226832928; Tue, 06 Feb 2024 05:40:32 -0800 (PST) X-Received: by 2002:a05:620a:40c5:b0:785:4475:7f7d with SMTP id g5-20020a05620a40c500b0078544757f7dmr2813987qko.4.1707226832472; Tue, 06 Feb 2024 05:40:32 -0800 (PST) X-Forwarded-Encrypted: i=0; AJvYcCWPaGsX6AocbtIWNVY7FAyjMFGJ99rnmnclsSLrCyjQXWdv7WZTM7TsMvX0Uwel9E35owhxBDZ4SETZQvGDd7YzVQGB3B1h44+pb7Gcs7RSPecm2mUROIQKv6WuX7AEC0LjEG6t6aVjHjnMq1JiADxQj2scxDFmvxApJR1d4cOH9dQ3pyfKOOqQQ+rd79tT53h60cJQNKJUxpyk4En3GiMAZmaWL+S9a4OiI7wgKs1Lhxn19jI26sc6yEFY/XuRYT4QUntnW8GOwwtkQkPOCuHzDA9sAQ4CcHwBZogNQYfDZsEVIA7tjGxX8Jnax1YX0RWwvJfIxDrH3tattp/VJRbN99jakSTBKo9F+tS5dP5nxmoJiIQa9bIriFNDxHtJik2ScKtG Received: from pstanner-thinkpadt14sgen1.remote.csb (nat-pool-muc-t.redhat.com. [149.14.88.26]) by smtp.gmail.com with ESMTPSA id vu4-20020a05620a560400b0078544c8be9asm903791qkn.87.2024.02.06.05.40.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Feb 2024 05:40:31 -0800 (PST) From: Philipp Stanner To: Hans de Goede , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter , Bjorn Helgaas , Sam Ravnborg , dakr@redhat.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Philipp Stanner Subject: [PATCH v3 02/10] PCI: Deprecate iomap-table functions Date: Tue, 6 Feb 2024 14:39:48 +0100 Message-ID: <20240206134000.23561-4-pstanner@redhat.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240206134000.23561-2-pstanner@redhat.com> References: <20240206134000.23561-2-pstanner@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1790157177238089416 X-GMAIL-MSGID: 1790157177238089416 The old plural devres functions tie the PCI devres API to the iomap-table mechanism which unfortunately is not extensible. As the plural functions are almost never used with more than one bit set in their bit-mask, deprecating them and encouraging users to use the new singular functions instead is reasonable. Furthermore, to make the implementation more consistent and extensible, the plural functions should use the singular functions. Add new wrapper to request / release all BARs. Make the plural functions call into the singular functions. Mark the plural functions as deprecated. Remove as much of the iomap-table-mechanism as possible. Add comments describing the path towards a cleaned-up API. Signed-off-by: Philipp Stanner --- drivers/pci/devres.c | 374 +++++++++++++++++++++++++++++++++---------- drivers/pci/pci.c | 20 +++ drivers/pci/pci.h | 5 + include/linux/pci.h | 2 + 4 files changed, 318 insertions(+), 83 deletions(-) diff --git a/drivers/pci/devres.c b/drivers/pci/devres.c index 4e47f00a03a2..724429ab4f64 100644 --- a/drivers/pci/devres.c +++ b/drivers/pci/devres.c @@ -4,15 +4,43 @@ #include "pci.h" /* - * PCI iomap devres + * On the state of PCI's devres implementation: + * + * The older devres API for PCI has two significant problems: + * + * 1. It is very strongly tied to the statically allocated mapping table in + * struct pcim_iomap_devres below. This is mostly solved in the sense of the + * pcim_ functions in this file providing things like ranged mapping by + * bypassing this table, wheras the functions that were present in the old + * API still enter the mapping addresses into the table for users of the old + * API. + * 2. The region-request-functions in pci.c do become managed IF the device has + * been enabled with pcim_enable_device() instead of pci_enable_device(). + * This resulted in the API becoming inconsistent: Some functions have an + * obviously managed counter-part (e.g., pci_iomap() <-> pcim_iomap()), + * whereas some don't and are never managed, while others don't and are + * _sometimes_ managed (e.g. pci_request_region()). + * Consequently, in the new API, region requests performed by the pcim_ + * functions are automatically cleaned up through the devres callback + * pcim_addr_resource_release(), while requests performed by + * pcim_enable_device() + pci_*region*() are automatically cleaned up + * through the for-loop in pcim_release(). + * + * TODO 1: + * Remove the legacy table entirely once all calls to pcim_iomap_table() in + * the kernel have been removed. + * + * TODO 2: + * Port everyone calling pcim_enable_device() + pci_*region*() to using the + * pcim_ functions. Then, remove all devres functionality from pci_*region*() + * functions and remove the associated cleanups described above in point #2. */ -#define PCIM_IOMAP_MAX PCI_STD_NUM_BARS /* * Legacy struct storing addresses to whole mapped BARs. */ struct pcim_iomap_devres { - void __iomem *table[PCIM_IOMAP_MAX]; + void __iomem *table[PCI_STD_NUM_BARS]; }; enum pcim_addr_devres_type { @@ -373,6 +401,16 @@ static void pcim_release(struct device *gendev, void *res) struct pci_devres *this = res; int i; + /* + * This is legacy code. + * All regions requested by a pcim_ function do get released through + * pcim_addr_resource_release(). Thanks to the hybrid nature of the pci_ + * region-request functions, this for-loop has to release the regions + * if they have been requested by such a function. + * + * TODO: Remove this once all users of pcim_enable_device() PLUS + * pci-region-request-functions have been ported to pcim_ functions. + */ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) if (this->region_mask & (1 << i)) pci_release_region(dev, i); @@ -461,19 +499,21 @@ EXPORT_SYMBOL(pcim_pin_device); static void pcim_iomap_release(struct device *gendev, void *res) { - struct pci_dev *dev = to_pci_dev(gendev); - struct pcim_iomap_devres *this = res; - int i; - - for (i = 0; i < PCIM_IOMAP_MAX; i++) - if (this->table[i]) - pci_iounmap(dev, this->table[i]); + /* + * Do nothing. This is legacy code. + * + * Cleanup of the mappings is now done directly through the callbacks + * registered when creating them. + */ } /** - * pcim_iomap_table - access iomap allocation table + * pcim_iomap_table - access iomap allocation table (DEPRECATED) * @pdev: PCI device to access iomap table for * + * Returns: + * Const pointer to array of __iomem pointers on success NULL on failure. + * * Access iomap allocation table for @dev. If iomap table doesn't * exist and @pdev is managed, it will be allocated. All iomaps * recorded in the iomap table are automatically unmapped on driver @@ -482,6 +522,11 @@ static void pcim_iomap_release(struct device *gendev, void *res) * This function might sleep when the table is first allocated but can * be safely called without context and guaranteed to succeed once * allocated. + * + * This function is DEPRECATED. Do not use it in new code. + * Instead, obtain a mapping's address directly from one of the pcim_* mapping + * functions. For example: + * void __iomem *mappy = pcim_iomap(pdev, barnr, length); */ void __iomem * const *pcim_iomap_table(struct pci_dev *pdev) { @@ -500,27 +545,114 @@ void __iomem * const *pcim_iomap_table(struct pci_dev *pdev) } EXPORT_SYMBOL(pcim_iomap_table); +/* + * Fill the legacy mapping-table, so that drivers using the old API + * can still get a BAR's mapping address through pcim_iomap_table(). + */ +static int pcim_add_mapping_to_legacy_table(struct pci_dev *pdev, + void __iomem *mapping, short bar) +{ + void __iomem **legacy_iomap_table; + + if (bar >= PCI_STD_NUM_BARS) + return -EINVAL; + + legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev); + if (!legacy_iomap_table) + return -ENOMEM; + + /* The legacy mechanism doesn't allow for duplicate mappings. */ + WARN_ON(legacy_iomap_table[bar]); + + legacy_iomap_table[bar] = mapping; + + return 0; +} + +/* + * Removes a mapping. The table only contains whole-bar-mappings, so this will + * never interfere with ranged mappings. + */ +static void pcim_remove_mapping_from_legacy_table(struct pci_dev *pdev, + void __iomem *addr) +{ + short bar; + void __iomem **legacy_iomap_table; + + legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev); + if (!legacy_iomap_table) + return; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + if (legacy_iomap_table[bar] == addr) { + legacy_iomap_table[bar] = NULL; + return; + } + } +} + +/* + * The same as pcim_remove_mapping_from_legacy_table(), but identifies the + * mapping by its BAR index. + */ +static void pcim_remove_bar_from_legacy_table(struct pci_dev *pdev, short bar) +{ + void __iomem **legacy_iomap_table; + + if (bar >= PCI_STD_NUM_BARS) + return; + + legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev); + if (!legacy_iomap_table) + return; + + legacy_iomap_table[bar] = NULL; +} + /** * pcim_iomap - Managed pcim_iomap() * @pdev: PCI device to iomap for * @bar: BAR to iomap * @maxlen: Maximum length of iomap * - * Managed pci_iomap(). Map is automatically unmapped on driver - * detach. + * Returns: __iomem pointer on success, NULL on failure. + * + * Managed pci_iomap(). Map is automatically unmapped on driver detach. If + * desired, unmap manually only with pcim_iounmap(). + * + * This SHOULD only be used once per BAR. + * + * NOTE: + * Contrary to the other pcim_* functions, this function does not return an + * IOMEM_ERR_PTR() on failure, but a simple NULL. This is done for backwards + * compatibility. */ void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen) { - void __iomem **tbl; - - BUG_ON(bar >= PCIM_IOMAP_MAX); + void __iomem *mapping; + struct pcim_addr_devres *res; - tbl = (void __iomem **)pcim_iomap_table(pdev); - if (!tbl || tbl[bar]) /* duplicate mappings not allowed */ + res = pcim_addr_devres_alloc(pdev); + if (!res) return NULL; + res->type = PCIM_ADDR_DEVRES_TYPE_MAPPING; + + mapping = pci_iomap(pdev, bar, maxlen); + if (!mapping) + goto err_iomap; + res->baseaddr = mapping; + + if (pcim_add_mapping_to_legacy_table(pdev, mapping, bar) != 0) + goto err_table; - tbl[bar] = pci_iomap(pdev, bar, maxlen); - return tbl[bar]; + devres_add(&pdev->dev, res); + return mapping; + +err_table: + pci_iounmap(pdev, mapping); +err_iomap: + pcim_addr_devres_free(res); + return NULL; } EXPORT_SYMBOL(pcim_iomap); @@ -529,23 +661,24 @@ EXPORT_SYMBOL(pcim_iomap); * @pdev: PCI device to iounmap for * @addr: Address to unmap * - * Managed pci_iounmap(). @addr must have been mapped using pcim_iomap(). + * Managed pci_iounmap(). @addr must have been mapped using pcim_iomap() or + * pcim_iomap_range(). */ void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr) { - void __iomem **tbl; - int i; + struct pcim_addr_devres res_searched; - pci_iounmap(pdev, addr); + pcim_addr_devres_clear(&res_searched); + res_searched.type = PCIM_ADDR_DEVRES_TYPE_MAPPING; + res_searched.baseaddr = addr; - tbl = (void __iomem **)pcim_iomap_table(pdev); - BUG_ON(!tbl); + if (devres_release(&pdev->dev, pcim_addr_resource_release, + pcim_addr_resources_match, &res_searched) != 0) { + /* Doesn't exist. User passed nonsense. */ + return; + } - for (i = 0; i < PCIM_IOMAP_MAX; i++) - if (tbl[i] == addr) { - tbl[i] = NULL; - return; - } + pcim_remove_mapping_from_legacy_table(pdev, addr); } EXPORT_SYMBOL(pcim_iounmap); @@ -615,106 +748,181 @@ void pcim_iounmap_region(struct pci_dev *pdev, int bar) } EXPORT_SYMBOL(pcim_iounmap_region); +static inline bool mask_contains_bar(int mask, int bar) +{ + return mask & BIT(bar); +} + /** - * pcim_iomap_regions - Request and iomap PCI BARs + * pcim_iomap_regions - Request and iomap PCI BARs (DEPRECATED) * @pdev: PCI device to map IO resources for * @mask: Mask of BARs to request and iomap * @name: Name associated with the requests * + * Returns: 0 on success, negative error code on failure. + * * Request and iomap regions specified by @mask. + * + * This function is DEPRECATED. Don't use it in new code. + * Use pcim_iomap_region() instead. */ int pcim_iomap_regions(struct pci_dev *pdev, int mask, const char *name) { - void __iomem * const *iomap; - int i, rc; + int ret; + short bar; + void __iomem *mapping; - iomap = pcim_iomap_table(pdev); - if (!iomap) - return -ENOMEM; + for (bar = 0; bar < DEVICE_COUNT_RESOURCE; bar++) { + if (!mask_contains_bar(mask, bar)) + continue; - for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { - unsigned long len; + mapping = pcim_iomap_region(pdev, bar, name); + if (IS_ERR(mapping)) { + ret = PTR_ERR(mapping); + goto err; + } + ret = pcim_add_mapping_to_legacy_table(pdev, mapping, bar); + if (ret != 0) + goto err; + } - if (!(mask & (1 << i))) - continue; + return 0; - rc = -EINVAL; - len = pci_resource_len(pdev, i); - if (!len) - goto err_inval; +err: + while (--bar >= 0) { + pcim_iounmap_region(pdev, bar); + pcim_remove_bar_from_legacy_table(pdev, bar); + } - rc = pci_request_region(pdev, i, name); - if (rc) - goto err_inval; + return ret; +} +EXPORT_SYMBOL(pcim_iomap_regions); - rc = -ENOMEM; - if (!pcim_iomap(pdev, i, 0)) - goto err_region; +/** + * pcim_release_all_regions - Release all regions of a PCI-device + * @pdev: the PCI device + * + * Will release all regions previously requested through pcim_request_region() + * or pcim_request_all_regions(). + * + * Can be called from any context, i.e., not necessarily as a counterpart to + * pcim_request_all_regions(). + */ +void pcim_release_all_regions(struct pci_dev *pdev) +{ + short bar; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) + pcim_release_region(pdev, bar); +} +EXPORT_SYMBOL(pcim_release_all_regions); + +/** + * pcim_request_all_regions - Request all regions + * @pdev: PCI device to map IO resources for + * @name: name associated with the request + * + * Returns: 0 on success, negative error code on failure. + * + * Requested regions will automatically be released at driver detach. If desired, + * release individual regions with pcim_release_region() or all of them at once + * with pcim_release_all_regions(). + */ +int pcim_request_all_regions(struct pci_dev *pdev, const char *name) +{ + int ret; + short bar; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + ret = pcim_request_region(pdev, bar, name); + if (ret != 0) + goto err; } return 0; - err_region: - pci_release_region(pdev, i); - err_inval: - while (--i >= 0) { - if (!(mask & (1 << i))) - continue; - pcim_iounmap(pdev, iomap[i]); - pci_release_region(pdev, i); - } +err: + pcim_release_all_regions(pdev); - return rc; + return ret; } -EXPORT_SYMBOL(pcim_iomap_regions); +EXPORT_SYMBOL(pcim_request_all_regions); /** - * pcim_iomap_regions_request_all - Request all BARs and iomap specified ones + * pcim_iomap_regions_request_all - Request all BARs and iomap specified ones (DEPRECATED) * @pdev: PCI device to map IO resources for * @mask: Mask of BARs to iomap * @name: Name associated with the requests * + * Returns: 0 on success, negative error code on failure. + * * Request all PCI BARs and iomap regions specified by @mask. + * + * To release these resources manually, call pcim_release_region() for the + * regions and pcim_iounmap() for the mappings. + * + * This function is DEPRECATED. Don't use it in new code. + * Use pcim_request_all_regions() + pcim_iomap*() instead. */ int pcim_iomap_regions_request_all(struct pci_dev *pdev, int mask, const char *name) { - int request_mask = ((1 << 6) - 1) & ~mask; - int rc; + short bar; + int ret; + void __iomem **legacy_iomap_table; + + ret = pcim_request_all_regions(pdev, name); + if (ret != 0) + return ret; - rc = pci_request_selected_regions(pdev, request_mask, name); - if (rc) - return rc; + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + if (!mask_contains_bar(mask, bar)) + continue; + if (!pcim_iomap(pdev, bar, 0)) + goto err; + } - rc = pcim_iomap_regions(pdev, mask, name); - if (rc) - pci_release_selected_regions(pdev, request_mask); - return rc; + return 0; + +err: + /* + * Here it gets tricky: pcim_iomap() above has most likely + * failed because it got an OOM when trying to create the + * legacy-table. + * We check here if that has happened. If not, pcim_iomap() + * must have failed because of EINVAL. + */ + legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev); + ret = legacy_iomap_table ? -EINVAL : -ENOMEM; + + while (--bar >= 0) + pcim_iounmap(pdev, legacy_iomap_table[bar]); + + pcim_release_all_regions(pdev); + + return ret; } EXPORT_SYMBOL(pcim_iomap_regions_request_all); /** - * pcim_iounmap_regions - Unmap and release PCI BARs + * pcim_iounmap_regions - Unmap and release PCI BARs (DEPRECATED) * @pdev: PCI device to map IO resources for * @mask: Mask of BARs to unmap and release * * Unmap and release regions specified by @mask. + * + * This function is DEPRECATED. Don't use it in new code. */ void pcim_iounmap_regions(struct pci_dev *pdev, int mask) { - void __iomem * const *iomap; - int i; - - iomap = pcim_iomap_table(pdev); - if (!iomap) - return; + short bar; - for (i = 0; i < PCIM_IOMAP_MAX; i++) { - if (!(mask & (1 << i))) + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + if (!mask_contains_bar(mask, bar)) continue; - pcim_iounmap(pdev, iomap[i]); - pci_release_region(pdev, i); + pcim_iounmap_region(pdev, bar); + pcim_remove_bar_from_legacy_table(pdev, bar); } } EXPORT_SYMBOL(pcim_iounmap_regions); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 19f18c3856e8..adf3c627c712 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3946,6 +3946,16 @@ void pci_release_region(struct pci_dev *pdev, int bar) release_mem_region(pci_resource_start(pdev, bar), pci_resource_len(pdev, bar)); + /* + * This devres utility makes this function sometimes managed + * (when pcim_enable_device() has been called before). + * This is bad because it conflicts with the pcim_ functions being + * exclusively responsible for managed pci. Its "sometimes yes, sometimes + * no" nature can cause bugs. + * + * TODO: Remove this once all users that use pcim_enable_device() PLUS + * a region request function have been ported to using pcim_ functions. + */ dr = find_pci_dr(pdev); if (dr) dr->region_mask &= ~(1 << bar); @@ -3990,6 +4000,16 @@ static int __pci_request_region(struct pci_dev *pdev, int bar, goto err_out; } + /* + * This devres utility makes this function sometimes managed + * (when pcim_enable_device() has been called before). + * This is bad because it conflicts with the pcim_ functions being + * exclusively responsible for managed pci. Its "sometimes yes, sometimes + * no" nature can cause bugs. + * + * TODO: Remove this once all users that use pcim_enable_device() PLUS + * a region request function have been ported to using pcim_ functions. + */ dr = find_pci_dr(pdev); if (dr) dr->region_mask |= 1 << bar; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 2215858b2584..4883be71e40c 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -816,6 +816,11 @@ struct pci_devres { unsigned int orig_intx:1; unsigned int restore_intx:1; unsigned int mwi:1; + + /* + * TODO: remove the region_mask once everyone calling + * pcim_enable_device() + pci_*region*() is ported to pcim_ functions. + */ u32 region_mask; }; diff --git a/include/linux/pci.h b/include/linux/pci.h index 5c676a1a1a3f..04a0065974e5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2326,6 +2326,8 @@ int pcim_iomap_regions_request_all(struct pci_dev *pdev, int mask, void pcim_iounmap_regions(struct pci_dev *pdev, int mask); int pcim_request_region(struct pci_dev *pdev, int bar, const char *res_name); void pcim_release_region(struct pci_dev *pdev, int bar); +void pcim_release_all_regions(struct pci_dev *pdev); +int pcim_request_all_regions(struct pci_dev *pdev, const char *name); void __iomem *pcim_iomap_range(struct pci_dev *pdev, int bar, unsigned long offset, unsigned long len); void __iomem *pcim_iomap_region_range(struct pci_dev *pdev, int bar,