Re: Implementation details of PCI Managed (devres) Functions

From: Danilo Krummrich
Date: Wed Nov 08 2023 - 18:49:22 EST


On Wed, Nov 08, 2023 at 10:02:29PM +0100, Philipp Stanner wrote:
> So, today I stared at the code for a while and come to a somewhat
> interesting insight:
>
>
> On Tue, 2023-11-07 at 20:38 +0100, Philipp Stanner wrote:
> > Hi all,
> >
> > I'm currently working on porting more drivers in DRM to managed pci-
> > functions. During this process I discovered something that might be
> > called an inconsistency in the implementation.
>
> I think I figured out why not all pci_ functions have a pcim_
> counterpart.
>
> I was interested in implementing pcim_iomap_range(), since we could use
> that for some drivers.

I think you don't need all the "per bar" stuff below for that. You can just use
the existing pci_iomap_range() (which simply uses ioremap() internally) and
connect it to devres.

>
> It turns out, that implementing this would be quite complicated (if I'm
> not mistaken).
>
> lib/devres.c:
>
> struct pcim_iomap_devres {
> void __iomem *table[PCIM_IOMAP_MAX];
> };
>
> void __iomem * const *pcim_iomap_table(struct pci_dev *pdev)
> {
> struct pcim_iomap_devres *dr, *new_dr;
>
> dr = devres_find(&pdev->dev, pcim_iomap_release, NULL, NULL);
> if (dr)
> return dr->table;
>
> new_dr = devres_alloc_node(pcim_iomap_release, sizeof(*new_dr), GFP_KERNEL,
> dev_to_node(&pdev->dev));
> if (!new_dr)
> return NULL;
> dr = devres_get(&pdev->dev, new_dr, NULL, NULL);
> return dr->table;
> }
>
> That struct keeps track of the requested BARs. That's why there can
> only be one mapping per BAR, because that table is statically allocated
> and is indexed with the bar-number.
> pcim_iomap_table() now only ever returns the table with the pointers to
> the BARs. Adding tables to that struct that keep track of which
> mappings exist in which bars would be a bit tricky and require probably
> an API change for everyone who currently uses pcim_iomap_table(), and
> that's more than 100 C-files.
>
> So, it seems that a concern back in 2007 was to keep things simple and
> skip the more complex data structures necessary for keeping track of
> the various mappings within a bar.
> In theory, there could be an almost unlimited number of such mappings
> of various sizes, almost forcing you to do book-keeping with the help
> of heap-allocations.
>
> I guess one way to keep things extensible would have been to name the
> function pcim_iomap_bar_table(), so you could later implement one like
> pcim_iomap_range_table() or something.
> But now it is what it is..
>
> That still doesn't explain why there's no pcim_request_region(),
> though...
>
>
> P.
>
> >
> > First, there would be the pcim_ functions being scattered across
> > several files. Some are implemented in drivers/pci/pci.c, others in
> > lib/devres.c, where they are guarded by #ifdef CONFIG_PCI
> > – this originates from an old cleanup, done in
> > 5ea8176994003483a18c8fed580901e2125f8a83
> >
> > Additionally, there is lib/pci_iomap.c, which contains the non-
> > managed
> > pci_iomap() functions.
> >
> > At first and second glance it's not obvious to me why these pci-
> > functions are scattered. Hints?
> >
> >
> > Second, it seems there are two competing philosophies behind managed
> > resource reservations. Some pci_ functions have pcim_ counterparts,
> > such as pci_iomap() <-> pcim_iomap(). So the API-user might expect
> > that
> > relevant pci_ functions that do not have a managed counterpart do so
> > because no one bothered implementing them so far.
> >
> > However, it turns out that for pci_request_region(), there is no
> > counterpart because a different mechanism / semantic was used to make
> > the function _sometimes_ managed:
> >
> >    1. If you use pcim_enable_device(), the member is_managed in
> > struct
> >       pci_dev is set to 1.
> >    2. This value is then evaluated in pci_request_region()'s call to
> >       find_pci_dr()
> >
> > Effectively, this makes pci_request_region() sometimes managed.
> > Why has it been implemented that way and not as a separate function –
> > like, e.g., pcim_iomap()?
> >
> > That's where an inconsistency lies. For things like iomappings there
> > are separate managed functions, for the region-request there's a
> > universal function doing managed or unmanaged, respectively.
> >
> > Furthermore, look at pcim_iomap_regions() – that function uses a mix
> > between the obviously managed function pcim_iomap() and
> > pci_request_region(), which appears unmanaged judging by the name,
> > but,
> > nevertheless, is (sometimes) managed below the surface.
> > Consequently, pcim_iomap_regions() could even be a little buggy: When
> > someone uses pci_enable_device() + pcim_iomap_regions(), wouldn't
> > that
> > leak the resource regions?
> >
> > The change to pci_request_region() hasn't grown historically but was
> > implemented that way in one run with the first set of managed
> > functions
> > in commit 9ac7849e35f70. So this implies it has been implemented that
> > way on purpose.
> >
> > What was that purpose?
> >
> > Would be great if someone can give some hints :)
> >
> > Regards,
> > P.
> >
>