Re: [kvm-unit-tests PATCH 02/11] arm/pci: PCI bus scanning

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Feb 02, 2016 at 10:34:01AM +0100, Alexander Gordeev wrote:
> On Wed, Jan 13, 2016 at 04:58:46PM +0100, Andrew Jones wrote:
> > On Sat, Jan 09, 2016 at 01:22:49PM +0100, Alexander Gordeev wrote:
> > > Scan bus 0 and function 0 only for now
> > > 
> > > Cc: Andrew Jones <drjones@xxxxxxxxxx>
> > > Signed-off-by: Alexander Gordeev <agordeev@xxxxxxxxxx>
> > > ---
> > >  arm/pci-test.c         |  4 +++
> > >  lib/pci-host-generic.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++
> > >  lib/pci-host-generic.h |  1 +
> > >  lib/pci.h              |  1 +
> > >  4 files changed, 73 insertions(+)
> > > 
> > > diff --git a/arm/pci-test.c b/arm/pci-test.c
> > > index 8629c89..1539d3e 100644
> > > --- a/arm/pci-test.c
> > > +++ b/arm/pci-test.c
> > > @@ -11,6 +11,7 @@
> > >  int main(void)
> > >  {
> > >  	struct pci *pci;
> > > +	int ret;
> > >  
> > >  	pci = pci_dt_probe();
> > >  
> > > @@ -18,6 +19,9 @@ int main(void)
> > >  	if (!pci)
> > >  		goto done;
> > >  
> > > +	ret = pci_bus_scan(pci);
> > > +	report("PCI bus scanning detected %d devices", true, ret);
> > 
> > How many devices are expected in this test? You can pass the expected
> > number in on the command line, and then make this a "real" test report
> > with
> > 
> >   report(..., ret == atol(argv[0]), ret)
> 
> What if a machine's PCI hierarchy changed? Such test would break then.

How would the number *of* detected devices change if we never pass more
or less devices on the qemu command line? I realize the PCI addresses
may change.

> 
> Also, this report is not entirely dummy. It ensures PCI configuration
> space access operates, devices do respond and generally the bus is fine.
> 
> > > +
> > >  	pci_shutdown(pci);
> > >  
> > >  done:
> > > diff --git a/lib/pci-host-generic.c b/lib/pci-host-generic.c
> > > index 62b5662..e479989 100644
> > > --- a/lib/pci-host-generic.c
> > > +++ b/lib/pci-host-generic.c
> > > @@ -14,6 +14,49 @@
> > >  #include "pci-host-generic.h"
> > >  #include <linux/pci_regs.h>
> > >  
> > > +#define for_each_pci_dev(pci, dev, conf)		\
> > > +	for (dev = find_next_dev(pci, -1, &conf);	\
> > > +	     dev >= 0;					\
> > > +	     dev = find_next_dev(pci, dev, &conf))
> > 
> > Why do we need to pass both a dev index and a conf
> > pointer? I see below you can get the later from the
> > former easily enough.
> 
> No particular reason apart from the fact each for_each_pci_dev()
> would be followed by dev_conf() call - so I paired it to save
> few lines. I have no preference here, though.

Then please drop one or the other.

> 
> > > +
> > > +static u8 pci_config_readb(const void *conf, int off)
> > > +{
> > > +	return readb(conf + off);
> > > +}
> > > +
> > > +static u32 pci_config_readl(const void *conf, int off)
> > > +{
> > > +	return readl(conf + off);
> > > +}
> > > +
> > > +static void pci_config_writel(u32 val, void *conf, int off)
> > > +{
> > > +	writel(val, conf + off);
> > > +}
> > 
> > These read/write wrappers don't look all that useful to me. They would
> > be more useful if they took a 'struct pci[_dev] like Linux's
> > pci_read_config_byte/word/dword do. In fact, stealing those from Linux
> > makes sense.
> 
> I put those ones only to emphasize PCI reads/writes as opposed to
> direct read*/write* accesses. Matching the Linux names is probably
> confusing.
> 
> Bringing Linux-like pci_read_config_byte/word/dword seems as an
> overkill to me as it entails pulling the whole data hierarchy
> pci_host_bridge->pci_bus->pci_dev.
> 
> By contrast, if we do not pull the whole hierarchy then we could
> introduce our own minimalistic data structure(s) and get off with
> it easily.

Yes, that's what I was thinking. We already do have our own minimalistic
data structure 'struct pci[_dev]'. However, now that I've made x86 pci
code common you'll either just need to create a lib/arm/asm/pci.h with
a pci_config_read like in lib/x86/asm/pci.h, or perhaps expand on that
if you need to read different sizes, by adding the _byte/word/...
variants.

> 
> > > +
> > > +static void* dev_conf(struct pci *pci, int dev)
> > > +{
> > > +	struct pci_host_bridge *host = pci->sysdata;
> > > +	return (void*)host->cpu_range.start + (dev * 8) * PCI_ECAM_CONFIG_SIZE;
> > > +}
> > > +
> > > +/* We scan bus 0 only for now */
> > > +static int find_next_dev(struct pci *pci, int dev, void **pconf)
> > > +{
> > > +	struct pci_host_bridge *host = pci->sysdata;
> > > +	int limit = MIN(32u, host->cpu_range.size / PCI_ECAM_CONFIG_SIZE);
> > 
> > 32 is the pci max devices per bus limit, right? Assuming a sane DT, is it
> > possible to have host->cpu_range.size/PCI_ECAM_CONFIG_SIZE > 32?
> 
> The point here is not host->cpu_range.size/PCI_ECAM_CONFIG_SIZE > 32
> but rather host->cpu_range.size/PCI_ECAM_CONFIG_SIZE < 32.
> 
> But you are right - a bus with less than 32 device slots is likely
> insane.
> 
> > > +
> > > +	for (dev++; dev < limit; dev++) {
> > > +		void *conf = dev_conf(pci, dev);
> > > +		if (pci_config_readl(conf, PCI_VENDOR_ID) != ((u32)~0)) {
> > > +			*pconf = conf;
> > > +			return dev;
> > > +		}
> > > +	}
> > > +
> > > +	return -1;
> > > +}
> > > +
> > >  static void pci_host_addr_space_init(struct pci_addr_space as[], int nr_as,
> > >  				     u32 cells[], int nr_range_cells)
> > >  {
> > > @@ -133,6 +176,24 @@ static struct pci_host_bridge *pci_host_bridge_probe(void)
> > >  	return host;
> > >  }
> > >  
> > > +int pci_bus_scan(struct pci *pci)
> > > +{
> > > +	void *conf;
> > > +	int dev;
> > > +	int nr_dev = 0;
> > > +
> > > +	for_each_pci_dev(pci, dev, conf) {
> > > +		/* We are only interested in normal PCI devices */
> > > +		if (pci_config_readb(conf, PCI_HEADER_TYPE) !=
> > > +					   PCI_HEADER_TYPE_NORMAL)
> > > +			continue;
> > > +		pci_config_writel(PCI_COMMAND_SERR, conf, PCI_COMMAND);
> > 
> > Shouldn't the driver (unit test) be the one that writes SERR? Also,
> > doesn't this clear everything in the Command register except SERR?
> 
> I do not thing a driver is in charge. It is a minimal devices
> initialization that is expected from firmware/BIOS AFAICT. A driver
> should inherit the properly initialized bus with allocated io/memory
> buffers to use.

OK, does QEMU not provide that already?

> 
> > If a scan gets run again after driver/unittest has started using a device
> > then this write may mess things up.
> 
> PCI bus scan is normally done once to initialize hierarchy. I am
> struggling to imagine a usecase when it is called repeatedly,
> especially in such a small framework.

A unit test can do whatever it likes. I'd like the initial setup to be
useful, but minimal, and easily modifiable.

> 
> > > +		nr_dev++;
> > > +	}
> > > +
> > > +	return nr_dev;
> > > +}
> > > +
> > >  struct pci *pci_dt_probe(void)
> > >  {
> > >  	struct pci_host_bridge *host;
> > > @@ -150,6 +211,12 @@ struct pci *pci_dt_probe(void)
> > >  
> > >  void pci_shutdown(struct pci *pci)
> > >  {
> > > +	void *conf;
> > > +	int dev;
> > > +
> > > +	for_each_pci_dev(pci, dev, conf)
> > > +		pci_config_writel(PCI_COMMAND_INTX_DISABLE, conf, PCI_COMMAND);
> > 
> > I think I know why this is here, but I don't think that kvm-unit-tests
> > needs to bother with it.
> 
> Well, it is not about interrupts, rather about disabling devices
> access to access io/memory buffers, which is writing 0 to the
> command regiester. PCI_COMMAND_INTX_DISABLE is just a good one
> to write as well.
> 
> BTW, this is the reason I named this routine pci_shutdown(), not
> pci_free().

I'm currently thinking any sort of shutdown is overkill for
kvm-unit-tests, but I'll accept anything you can make a good
case for :-)

> 
> > > +
> > >  	free(pci->sysdata);
> > >  	free(pci);
> > >  }
> > > diff --git a/lib/pci-host-generic.h b/lib/pci-host-generic.h
> > > index 097ac2d..c14d32d 100644
> > > --- a/lib/pci-host-generic.h
> > > +++ b/lib/pci-host-generic.h
> > > @@ -22,5 +22,6 @@ struct pci_host_bridge {
> > >  };
> > >  
> > >  #define PCI_ECAM_BUS_SIZE		(1 << 20)
> > > +#define PCI_ECAM_CONFIG_SIZE		(1 << 12)
> > 
> > Are these also defined in the Linux kernel? I'm too lazy to pull up the
> > PCI spec right now, so I'd rather confirm the sizes by looking them up
> > in Linux. If they are defined there somewhere, then please use the same
> > names here. Otherwise, I guess references to some documentation or
> > something would be nice for pci dummies like me.
> > 
> > >  
> > >  #endif
> > > diff --git a/lib/pci.h b/lib/pci.h
> > > index 22b8e31..bc4f2d2 100644
> > > --- a/lib/pci.h
> > > +++ b/lib/pci.h
> > > @@ -8,6 +8,7 @@ struct pci {
> > >  };
> > >  
> > >  extern struct pci *pci_dt_probe(void);
> > > +extern int pci_bus_scan(struct pci *pci);
> > >  extern void pci_shutdown(struct pci *pci);
> > >  
> > >  #endif
> > > -- 
> > > 1.8.3.1
> > > 
_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm



[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux