Received: by 2002:a05:7412:8d1c:b0:fa:4c10:6cad with SMTP id bj28csp52094rdb; Tue, 16 Jan 2024 14:48:20 -0800 (PST) X-Google-Smtp-Source: AGHT+IE3SwIzxfYqYiyjd+HI7ivoDDT8L4vAcc5zQlR1t3pFPWgc3pxHd+m3Zc7hpZPlM6zFUklr X-Received: by 2002:a17:90b:3746:b0:28d:c9b6:f610 with SMTP id ne6-20020a17090b374600b0028dc9b6f610mr4449848pjb.1.1705445300370; Tue, 16 Jan 2024 14:48:20 -0800 (PST) Return-Path: Received: from sy.mirrors.kernel.org (sy.mirrors.kernel.org. [147.75.48.161]) by mx.google.com with ESMTPS id m15-20020a17090a2c0f00b0028bf7ab5a92si12357206pjd.147.2024.01.16.14.48.19 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 16 Jan 2024 14:48:20 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-28289-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) client-ip=147.75.48.161; Authentication-Results: mx.google.com; arc=fail (body hash mismatch); spf=pass (google.com: domain of linux-kernel+bounces-28289-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-28289-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.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 sy.mirrors.kernel.org (Postfix) with ESMTPS id E23F1B25612 for ; Tue, 16 Jan 2024 22:23:59 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5AF952E40E; Tue, 16 Jan 2024 21:16:52 +0000 (UTC) Received: from fgw22-7.mail.saunalahti.fi (fgw22-7.mail.saunalahti.fi [62.142.5.83]) (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 94D822D7A5 for ; Tue, 16 Jan 2024 21:16:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=62.142.5.83 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1705439811; cv=none; b=u2pQ56xDftGC7BipV5r7XZ6+4s5OwxK+v0cTIKmnisYDni/dGEzKVEOzFPpVvbkuuKClI6WM3t6x47JOHi2Za9zHOEBQKufdyfY80u3/Yf0iQoc5yIKO8GuUNSGBupHK+xuE6L8K+7hhFILF7gjpE/kB8MhJN7OK8KvzmXlv5CI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1705439811; c=relaxed/simple; bh=xBHW4qYf3NcxxENUHmwdw0yhtGOW1i9//29Q4Cdd+0M=; h=Received:From:Date:To:Cc:Subject:Message-ID:References: MIME-Version:Content-Type:Content-Disposition:In-Reply-To; b=qwYg8h17+sVV+jzBIu38R44oQWxZpu86I/BOCkiXg7oEQ1cqKsyZCUna7yGvSjgeVCDLLLMkbqaXuD/2GC22W8avgTCn0eIQ8mhE66vV/e+uj/Dvbgwp3Q5mY/t99bPvWknrTOfbCul8O2474ZTiMqW/YRVse5DpFnVHOVq3fJE= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com; spf=fail smtp.mailfrom=gmail.com; arc=none smtp.client-ip=62.142.5.83 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=gmail.com Received: from localhost (88-113-24-108.elisa-laajakaista.fi [88.113.24.108]) by fgw22.mail.saunalahti.fi (Halon) with ESMTP id 655d8f3c-b4b4-11ee-a9de-005056bdf889; Tue, 16 Jan 2024 23:15:39 +0200 (EET) From: andy.shevchenko@gmail.com Date: Tue, 16 Jan 2024 23:15:38 +0200 To: Philipp Stanner Cc: Jonathan Corbet , Hans de Goede , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter , Bjorn Helgaas , Sam Ravnborg , dakr@redhat.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-pci@vger.kernel.org Subject: Re: [PATCH 01/10] pci: add new set of devres functions Message-ID: References: <20240115144655.32046-2-pstanner@redhat.com> <20240115144655.32046-3-pstanner@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20240115144655.32046-3-pstanner@redhat.com> Mon, Jan 15, 2024 at 03:46:12PM +0100, Philipp Stanner kirjoitti: > PCI's devres API is not extensible to ranged mappings and has > bug-provoking features. Improve that by providing better alternatives. > > When the original devres API for PCI was implemented, priority was given > to the creation of a set of "pural functions" such as > pcim_request_regions(). These functions have bit masks as parameters to > specify which BARs shall get mapped. Most users, however, only use those > to mapp 1-3 BARs. > A complete set of "singular functions" does not exist. > > As functions mapping / requesting multiple BARs at once have (almost) no > mechanism in C to return the resources to the caller of the plural > function, the devres API utilizes the iomap-table administrated by the > function pcim_iomap_table(). > > The entire PCI devres implementation was strongly tied to that table > which only allows for mapping whole, complete BARs, as the BAR's index > is used as table index. Consequently, it's not possible to, e.g., have a > pcim_iomap_range() function with that mechanism. > > An additional problem is that pci-devres has been ipmlemented in a sort > of "hybrid-mode": Some unmanaged functions have managed counterparts > (e.g.: pci_iomap() <-> pcim_iomap()), making their managed nature > obvious to the programmer. However, the region-request functions in > pci.c, prefixed with pci_, behave either managed or unmanaged, depending > on whether pci_enable_device() or pcim_enable_device() has been called > in advance. > > This hybrid API is confusing and should be more cleanly separated by > providing always-managed functions prefixed with pcim_. > > Thus, the existing devres API is not desirable because: > a) The vast majority of the users of the plural functions only > ever sets a single bit in the bit mask, consequently making > them singular functions anyways. > b) There is no mechanism to request / iomap only part of a BAR. > c) The iomap-table mechanism is over-engineered, complicated and > can by definition not perform bounds checks, thus, provoking > memory faults: pcim_iomap_table(pdev)[42] > d) region-request functions being sometimes managed and > sometimes not is bug-provoking. > > Implement a set of singular pcim_ functions that use devres directly > and bypass the legacy iomap table mechanism. > Add devres.c to driver-api documentation. .. > +struct pcim_addr_devres { > + enum pcim_addr_devres_type type; > + void __iomem *baseaddr; > + unsigned long offset; > + unsigned long len; > + short bar; > +}; > + > +static inline void pcim_addr_devres_clear(struct pcim_addr_devres *res) > +{ > + res->type = PCIM_ADDR_DEVRES_TYPE_INVALID; > + res->bar = -1; > + res->baseaddr = NULL; > + res->offset = 0; > + res->len = 0; More robust (in case the data type gets extended) is memset() + individual (non-0) sets. > +} .. > +static int __pcim_request_region_range(struct pci_dev *pdev, int bar, > + unsigned long offset, unsigned long maxlen, > + const char *name, int exclusive) > +{ > + resource_size_t start = pci_resource_start(pdev, bar); > + resource_size_t len = pci_resource_len(pdev, bar); > + unsigned long flags = pci_resource_flags(pdev, bar); > + > + if (start == 0 || len == 0) /* that's an unused BAR. */ > + return 0; > + if (len <= offset) > + return -EINVAL; > + > + start += offset; > + len -= offset; > + if (len > maxlen && maxlen != 0) > + len = maxlen; if (maxlen && ...) ? > + if (flags & IORESOURCE_IO) { > + if (!request_region(start, len, name)) > + return -EBUSY; > + } else if (flags & IORESOURCE_MEM) { > + if (!__request_mem_region(start, len, name, exclusive)) > + return -EBUSY; > + } else { > + /* That's not a device we can request anything on. */ > + return -ENODEV; > + } Hmm... Not sure, but the switch-case against type might be considered: switch (resource_type(...)) { ... } > + return 0; > +} > +static void __pcim_release_region_range(struct pci_dev *pdev, int bar, > + unsigned long offset, unsigned long maxlen) > +{ > + resource_size_t start = pci_resource_start(pdev, bar); > + resource_size_t len = pci_resource_len(pdev, bar); > + unsigned long flags = pci_resource_flags(pdev, bar); > + > + if (len <= offset || start == 0) > + return; > + > + if (len == 0 || maxlen == 0) /* This an unused BAR. Do nothing. */ > + return; > + > + start += offset; > + len -= offset; > + > + if (len > maxlen) > + len = maxlen; This part is quite a duplication of the above function, no? > + if (flags & IORESOURCE_IO) > + release_region(start, len); > + else if (flags & IORESOURCE_MEM) > + release_mem_region(start, len); > +} .. > +static int __pcim_request_region(struct pci_dev *pdev, int bar, > + const char *name, int exclusive) > +{ > + const unsigned long offset = 0; > + const unsigned long len = pci_resource_len(pdev, bar); How const anyhow useful here? Ditto for other places like this. > + return __pcim_request_region_range(pdev, bar, offset, len, name, exclusive); > +} .. > +static int pcim_addr_resources_match(struct device *dev, void *a_raw, void *b_raw) > +{ > + struct pcim_addr_devres *a, *b; > + > + a = a_raw; > + b = b_raw; > + (void)dev; /* unused. */ Why do we need this? > + if (a->type != b->type) > + return 0; > + > + switch (a->type) { > + case PCIM_ADDR_DEVRES_TYPE_REGION: > + case PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING: > + return a->bar == b->bar; > + case PCIM_ADDR_DEVRES_TYPE_MAPPING: > + return a->baseaddr == b->baseaddr; > + case PCIM_ADDR_DEVRES_TYPE_REGION_RANGE_MAPPING: > + return a->bar == b->bar && > + a->offset == b->offset && a->len == b->len; Indentation or made it a single line. > + default: > + break; > + } > + > + return 0; return directly from default case. > +} .. > +/** > + * pcim_iomap_region - Request and iomap a PCI BAR > + * @pdev: PCI device to map IO resources for > + * @bar: Index of a BAR to map > + * @name: Name associated with the request > + * > + * Returns __iomem pointer on success, an IOMEM_ERR_PTR on failure. Please, make sure the kernel-doc won't complain scripts/kernel-doc -v -none -Wall ... > + * Mapping and region will get automatically released on driver detach. If > + * desired, release manually only with pcim_iounmap_region(). > + */ > +void __iomem *pcim_iomap_region(struct pci_dev *pdev, int bar, const char *name) > +{ > + int ret = 0; Redundant assignment. > + struct pcim_addr_devres *res; Perhaps reversed xmas tree order? > + res = pcim_addr_devres_alloc(pdev); > + if (!res) > + return IOMEM_ERR_PTR(-ENOMEM); > + > + res->type = PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING; > + res->bar = bar; > + > + ret = __pcim_request_region(pdev, bar, name, 0); > + if (ret != 0) > + goto err_region; > + > + res->baseaddr = pci_iomap(pdev, bar, 0); > + if (!res->baseaddr) { > + ret = -EINVAL; > + goto err_iomap; > + } > + > + devres_add(&pdev->dev, res); > + return res->baseaddr; > + > +err_iomap: > + __pcim_release_region(pdev, bar); > +err_region: > + pcim_addr_devres_free(res); > + > + return IOMEM_ERR_PTR(ret); > +} .. > +static int _pcim_request_region(struct pci_dev *pdev, int bar, const char *name, > + int request_flags) Indentation? > +{ > + int ret = 0; Unneded assignment. Also fix this in other places. > + struct pcim_addr_devres *res; > + > + res = pcim_addr_devres_alloc(pdev); > + if (!res) > + return -ENOMEM; > + res->type = PCIM_ADDR_DEVRES_TYPE_REGION; > + res->bar = bar; > + > + ret = __pcim_request_region(pdev, bar, name, request_flags); > + if (ret != 0) { if (ret) Also fix this in other places. > + pcim_addr_devres_free(res); > + return ret; > + } > + > + devres_add(&pdev->dev, res); > + return 0; > +} -- With Best Regards, Andy Shevchenko