public inbox for linux-doc@vger.kernel.org
 help / color / mirror / Atom feed
From: andy.shevchenko@gmail.com
To: Philipp Stanner <pstanner@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>,
	Hans de Goede <hdegoede@redhat.com>,
	Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
	Maxime Ripard <mripard@kernel.org>,
	Thomas Zimmermann <tzimmermann@suse.de>,
	David Airlie <airlied@gmail.com>, Daniel Vetter <daniel@ffwll.ch>,
	Bjorn Helgaas <bhelgaas@google.com>,
	Sam Ravnborg <sam@ravnborg.org>,
	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
Date: Tue, 16 Jan 2024 23:15:38 +0200	[thread overview]
Message-ID: <Zabx-u-R-VsYIeIz@surfacebook.localdomain> (raw)
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



  parent reply	other threads:[~2024-01-16 21:16 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-15 14:46 [PATCH 00/10] Make PCI's devres API more consistent Philipp Stanner
2024-01-15 14:46 ` [PATCH 01/10] pci: add new set of devres functions Philipp Stanner
2024-01-16 18:44   ` Bjorn Helgaas
2024-01-17  8:54     ` Philipp Stanner
2024-01-19 22:52       ` Bjorn Helgaas
2024-01-16 21:15   ` andy.shevchenko [this message]
2024-01-17  9:21     ` Philipp Stanner
2024-01-15 14:46 ` [PATCH 02/10] pci: deprecate iomap-table functions Philipp Stanner
2024-01-16 21:27   ` andy.shevchenko
2024-01-17  9:40     ` Philipp Stanner
2024-01-15 14:46 ` [PATCH 03/10] pci: warn users about complicated devres nature Philipp Stanner
2024-01-15 14:46 ` [PATCH 04/10] pci: devres: make devres region requests consistent Philipp Stanner
2024-01-16 21:29   ` andy.shevchenko
2024-01-15 14:46 ` [PATCH 05/10] pci: move enabled status bit to pci_dev struct Philipp Stanner
2024-01-15 14:46 ` [PATCH 06/10] pci: move pinned " Philipp Stanner
2024-01-16 21:34   ` andy.shevchenko
2024-01-17  9:02     ` Philipp Stanner
2024-01-15 14:46 ` [PATCH 07/10] pci: devres: give mwi its own callback Philipp Stanner
2024-01-16 18:35   ` Bjorn Helgaas
2024-01-15 14:46 ` [PATCH 08/10] pci: devres: give pci(m)_intx " Philipp Stanner
2024-01-16 21:37   ` andy.shevchenko
2024-01-15 14:46 ` [PATCH 09/10] pci: devres: remove legacy pcim_release() Philipp Stanner
2024-01-16 21:40   ` andy.shevchenko
2024-01-17 13:49     ` Philipp Stanner
2024-01-15 14:46 ` [PATCH 10/10] drm/vboxvideo: fix mapping leaks Philipp Stanner
2024-01-16 18:29 ` [PATCH 00/10] Make PCI's devres API more consistent Bjorn Helgaas
2024-01-16 21:17 ` andy.shevchenko
2024-01-17  9:59   ` Philipp Stanner
2024-01-18  8:48     ` Philipp Stanner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Zabx-u-R-VsYIeIz@surfacebook.localdomain \
    --to=andy.shevchenko@gmail.com \
    --cc=airlied@gmail.com \
    --cc=bhelgaas@google.com \
    --cc=corbet@lwn.net \
    --cc=dakr@redhat.com \
    --cc=daniel@ffwll.ch \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=hdegoede@redhat.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=mripard@kernel.org \
    --cc=pstanner@redhat.com \
    --cc=sam@ravnborg.org \
    --cc=tzimmermann@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox