Skip to contentSkip to conversation

Driver for new "VMD" device

PATCH Superseded Keith Busch 5 participants 23 messages Dec 7, 2015 – Jan 11, 2016

Subsystems (5)
x86 PCI Library code IRQ +1 more subsystems

v6 of 8 Superseded Latest is v8 →

lore
Share & tools
lore
#0 lore raw

v6 cover Driver for new "VMD" device

v5 -> v6:

Fixed kernel doc.

Fixed S-o-B on PATCH 1/7.

Added driver power management to save and restoring VMD pci state.

Allow VMD domains to be accisble to aer_inject, which requires a minor change to use 32-bit pci domains.

Changed child bus resource conflict detection on walking the pci domain. If the bus resource aperture is not large enough, we can enumerate only a sub-tree of the topology. A bridge device with subordinate outside the range should not be allocated. Checking for the conflict after the child is allocated retains pointer to the removed subordinate bus. The way to fix that is to call "pci_remove_bus_device" instead, but I don't think we want to remove the bridge dev since it is accessible, albeit not very useful as a bridge device.

Keith Busch (6):
pci: child bus alloc fix on constrained resource Export msi and irq functions for module use
x86-pci: allow pci domain specific dma ops x86/pci: Initial commit for new VMD device driver
aer_inject: Use 32 bit int type domains
pciutils: Allow 32-bit domains

Liu Jiang (1):
msi: Relax msi_domain_alloc() to support parentless MSI irqdomains

 arch/x86/Kconfig                  |  13 +
 arch/x86/include/asm/device.h     |  10 +
 arch/x86/include/asm/hw_irq.h     |   5 +
 arch/x86/pci/Makefile             |   2 +
 arch/x86/pci/common.c             |  38 +++
 arch/x86/pci/vmd.c                | 695 ++++++++++++++++++++++++++++++++++++++
 drivers/pci/msi.c                 |   2 +
 drivers/pci/pcie/aer/aer_inject.c |  16 +-
 drivers/pci/probe.c               |   6 +
 kernel/irq/irqdomain.c            |   1 +
 kernel/irq/msi.c                  |   8 +-
 11 files changed, 785 insertions(+), 11 deletions(-)
 create mode 100644 arch/x86/pci/vmd.c
Signature · 1 line
2.6.2.307.g37023ba
#1 lore raw

PATCH v5 7/7 pciutils: Allow 32-bit domains

PCI-e segments will continue to use the lower 16 bits as required by ACPI. Special domains may use the full 32-bits.

---
 lib/filter.c |    2 +-
 lib/pci.h    |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
lib/filter.c +1 -1
index d4254a0..075dc2f 100644
@@ -45,7 +45,7 @@ pci_filter_parse_slot_v33(struct pci_filter *f, char *str)
 	  if (str[0] && strcmp(str, "*"))
 	    {
 	      long int x = strtol(str, &e, 16);
-	      if ((e && *e) || (x < 0 || x > 0xffff))
+	      if ((e && *e) || (x < 0))
 		return "Invalid domain number";
 	      f->domain = x;
 	    }
lib/pci.h +1 -1
index 10ba831..7e42765 100644
@@ -119,7 +119,7 @@ struct pci_param *pci_walk_params(struct pci_access *acc, struct pci_param *prev
 
 struct pci_dev {
   struct pci_dev *next;			/* Next device in the chain */
-  u16 domain;				/* PCI domain (host bridge) */
+  int32_t domain;			/* PCI domain (host bridge) */
   u8 bus, dev, func;			/* Bus inside domain, device and function */
 
   /* These fields are set by pci_fill_info() */
Signature · 1 line
1.7.10.4
#2 lore raw

Re: [PATCHv5 7/7] pciutils: Allow 32-bit domains

Quoted 27 lines · Keith Busch

PCI-e segments will continue to use the lower 16 bits as required by ACPI. Special domains may use the full 32-bits.

---
 lib/filter.c |    2 +-
 lib/pci.h    |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
lib/filter.c +1 -1
index d4254a0..075dc2f 100644
@@ -45,7 +45,7 @@ pci_filter_parse_slot_v33(struct pci_filter *f, char *str)
          if (str[0] && strcmp(str, "*"))
            {
              long int x = strtol(str, &e, 16);
-             if ((e && *e) || (x < 0 || x > 0xffff))
+             if ((e && *e) || (x < 0))
                return "Invalid domain number";
              f->domain = x;
            }
lib/pci.h +0 -0
index 10ba831..7e42765 100644

lib/pci.h struct pci_param *pci_walk_params(struct pci_access *acc, struct pci_param *prev · line 123

Quoted 6 lines
lib/pci.h +1 -1
@@ -119,7 +119,7 @@ struct pci_param *pci_walk_params(struct pci_access *acc, struct pci_param *prev
 
 struct pci_dev {
   struct pci_dev *next;                        /* Next device in the chain */
-  u16 domain;                          /* PCI domain (host bridge) */
 
+  int32_t domain;                      /* PCI domain (host bridge) */

Why not u32 ?

Quoted 11 lines
lib/pci.h +0 -0
   u8 bus, dev, func;                   /* Bus inside domain, device and function */
 
   /* These fields are set by pci_fill_info() */

--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#3 lore raw

Re: [PATCHv5 7/7] pciutils: Allow 32-bit domains

In reply to Keith Busch

Hi Keith,

Quoted 14 lines · Keith Busch

PCI-e segments will continue to use the lower 16 bits as required by ACPI. Special domains may use the full 32-bits.

---
 lib/filter.c |    2 +-
 lib/pci.h    |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
lib/filter.c +0 -0
index d4254a0..075dc2f 100644

lib/filter.c pci_filter_parse_slot_v33(struct pci_filter *f, char *str) · line 48

Quoted 5 lines
lib/filter.c +1 -1
@@ -45,7 +45,7 @@ pci_filter_parse_slot_v33(struct pci_filter *f, char *str)
 	  if (str[0] && strcmp(str, "*"))
 	    {
 	      long int x = strtol(str, &e, 16);
-	      if ((e && *e) || (x < 0 || x > 0xffff))
+	      if ((e && *e) || (x < 0))

Just out of curiosity (I don't maintain pciutils; Martin would apply this one), is there some part of the PCI or PCI firmware spec that is relevant to this change? Maybe this is connected to parsing things exported by the kernel and not directly tied to PCI at the spec level.

Whatever it is, a pointer to the producer of the information you're consuming here would help us understand and review the patch.

Quoted 24 lines
lib/filter.c +0 -0
 		return "Invalid domain number";
 	      f->domain = x;
 	    }
lib/pci.h +1 -1
index 10ba831..7e42765 100644
@@ -119,7 +119,7 @@ struct pci_param *pci_walk_params(struct pci_access *acc, struct pci_param *prev
 
 struct pci_dev {
   struct pci_dev *next;			/* Next device in the chain */
-  u16 domain;				/* PCI domain (host bridge) */
+  int32_t domain;			/* PCI domain (host bridge) */
   u8 bus, dev, func;			/* Bus inside domain, device and function */
 
   /* These fields are set by pci_fill_info() */
Signature · 7 lines
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
#4 lore raw

Re: [PATCHv5 7/7] pciutils: Allow 32-bit domains

Quoted 12 lines · Bjorn Helgaas

lib/filter.c pci_filter_parse_slot_v33(struct pci_filter *f, char *str) · line 48

Quoted 6 lines
lib/filter.c +1 -1
@@ -45,7 +45,7 @@ pci_filter_parse_slot_v33(struct pci_filter *f, char *str)
 	  if (str[0] && strcmp(str, "*"))
 	    {
 	      long int x = strtol(str, &e, 16);
-	      if ((e && *e) || (x < 0 || x > 0xffff))
+	      if ((e && *e) || (x < 0))

Just out of curiosity (I don't maintain pciutils; Martin would apply this one), is there some part of the PCI or PCI firmware spec that is relevant to this change? Maybe this is connected to parsing things exported by the kernel and not directly tied to PCI at the spec level.

Quoted 2 lines

Whatever it is, a pointer to the producer of the information you're consuming here would help us understand and review the patch.

Hi Bjorn,

This is not tied to anything defined in PCI spec. Domain numbers being a software construct (ACPI6, §6.5.6), we don't need to constrain the representation. ACPI defines 16-bit segments, and domains provided by this new host bridge do not define _SEG, so this series proposes domain numbers outside the ACPI reachable range to avoid potential clashes.

The pciutils patch just synchronizes the essential tooling software with the kernel software's new representation.

#5 lore raw

Re: [PATCHv5 7/7] pciutils: Allow 32-bit domains

Quoted 18 lines · Keith Busch
Quoted 12 lines · Bjorn Helgaas

lib/filter.c pci_filter_parse_slot_v33(struct pci_filter *f, char *str) · line 48

Quoted 6 lines
lib/filter.c +1 -1
@@ -45,7 +45,7 @@ pci_filter_parse_slot_v33(struct pci_filter *f, char *str)
 	  if (str[0] && strcmp(str, "*"))
 	    {
 	      long int x = strtol(str, &e, 16);
-	      if ((e && *e) || (x < 0 || x > 0xffff))
+	      if ((e && *e) || (x < 0))

Just out of curiosity (I don't maintain pciutils; Martin would apply this one), is there some part of the PCI or PCI firmware spec that is relevant to this change? Maybe this is connected to parsing things exported by the kernel and not directly tied to PCI at the spec level.

Quoted 2 lines

Whatever it is, a pointer to the producer of the information you're consuming here would help us understand and review the patch.

Hi Bjorn,

Quoted 8 lines

This is not tied to anything defined in PCI spec. Domain numbers being a software construct (ACPI6, §6.5.6), we don't need to constrain the representation. ACPI defines 16-bit segments, and domains provided by this new host bridge do not define _SEG, so this series proposes domain numbers outside the ACPI reachable range to avoid potential clashes.

The pciutils patch just synchronizes the essential tooling software with the kernel software's new representation.

That's what I figured. It'd be useful to know exactly what is on the other end of this, e.g., a Linux /proc or /sys file or whatever it is.

Your changelog assumes a lot of implicit knowledge about Linux, VMD, and the previous patches in this series. But pciutils is not Linux-specific, and it's maintained completely separately from Linux.

This patch needs to supply enough explicit context that it makes sense all by itself, apart from the kernel series.

Bjorn

#6 lore raw

Re: [PATCHv5 7/7] pciutils: Allow 32-bit domains

In reply to Keith Busch

Hello!

Quoted 27 lines

PCI-e segments will continue to use the lower 16 bits as required by ACPI. Special domains may use the full 32-bits.

---
 lib/filter.c |    2 +-
 lib/pci.h    |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
lib/filter.c +1 -1
index d4254a0..075dc2f 100644
@@ -45,7 +45,7 @@ pci_filter_parse_slot_v33(struct pci_filter *f, char *str)
 	  if (str[0] && strcmp(str, "*"))
 	    {
 	      long int x = strtol(str, &e, 16);
-	      if ((e && *e) || (x < 0 || x > 0xffff))
+	      if ((e && *e) || (x < 0))
 		return "Invalid domain number";
 	      f->domain = x;
 	    }
lib/pci.h +0 -0
index 10ba831..7e42765 100644

lib/pci.h struct pci_param *pci_walk_params(struct pci_access *acc, struct pci_param *prev · line 123

Quoted 6 lines
lib/pci.h +1 -1
@@ -119,7 +119,7 @@ struct pci_param *pci_walk_params(struct pci_access *acc, struct pci_param *prev
 
 struct pci_dev {
   struct pci_dev *next;			/* Next device in the chain */
-  u16 domain;				/* PCI domain (host bridge) */
+  int32_t domain;			/* PCI domain (host bridge) */
   u8 bus, dev, func;			/* Bus inside domain, device and function */

This is definitely not enough. Try grepping the source for "domain" :-)

At least the following places need updating, too:

o struct pci_filter and operations on it

o Format strings for printing domains at various places

o ABI compability ... changing a field in the middle of struct pci_dev (or pci_filter) is going to break ABI, so you either need to change the structures in a backward-compatible way, or to use ABI versioning.

Also, we should decide on what type the domain should have -- currently, some places use "int", others use u16, and your patch introduces int32_t. I would prefer u32 myself, but especially in the filters we should be careful about how to encode "any domain".

Have a nice new year
Martin

#7 lore raw

Re: [PATCHv5 7/7] pciutils: Allow 32-bit domains

Hi, thanks for the feedback. I've a few follow up questions.

Quoted 5 lines · Martin Mares

This is definitely not enough. Try grepping the source for "domain" :-)

At least the following places need updating, too:

o struct pci_filter and operations on it

Not sure I follow. struct pci_filter's domain was already a 32-bit int.

Quoted 1 line

o Format strings for printing domains at various places

Are you wanting a %04x for 16 bit domains and %08x for 32 bit ones? The %04x specifier still works with 32-bit values.

We just need a bit so this new h/w can't collide with ACPI _SEG defined domains. I don't know of any real need for the full 32-bits; we'd do fine using only 17 bits, so thought the leading 0's wasn't useful.

Quoted 3 lines

o ABI compability ... changing a field in the middle of struct pci_dev (or pci_filter) is going to break ABI, so you either need to change the structures in a backward-compatible way, or to use ABI versioning.

It looks like there's a 16-bit gap after device_class. Would it be acceptable place the domain's upper 16 bits in there to keep ABI compatibility?

Quoted 4 lines

Also, we should decide on what type the domain should have -- currently, some places use "int", others use u16, and your patch introduces int32_t. I would prefer u32 myself, but especially in the filters we should be careful about how to encode "any domain".

I left it as a signed int to allow a negative number for "any", and that's also what the linux kernel uses.

#8 lore raw

Re: [PATCHv5 7/7] pciutils: Allow 32-bit domains

Hi!

Quoted 8 lines · Martin Mares
Quoted 5 lines

This is definitely not enough. Try grepping the source for "domain" :-)

At least the following places need updating, too:

o struct pci_filter and operations on it

Not sure I follow. struct pci_filter's domain was already a 32-bit int.

Yes, but it has to encode a "don't care" value, too.

However, if 17 bits are sufficient, then let us define that the domain number is currently 31-bit and keep -1 as "don't care".

In any case, it would be nice to make the sysfs back-end check that the number provided by the kernel fits in 31 bits.

Quoted 4 lines
Quoted 1 line

o Format strings for printing domains at various places

Are you wanting a %04x for 16 bit domains and %08x for 32 bit ones? The %04x specifier still works with 32-bit values.

After some more thought, keeping %04x will be better.

Quoted 7 lines
Quoted 3 lines

o ABI compability ... changing a field in the middle of struct pci_dev (or pci_filter) is going to break ABI, so you either need to change the structures in a backward-compatible way, or to use ABI versioning.

It looks like there's a 16-bit gap after device_class. Would it be acceptable place the domain's upper 16 bits in there to keep ABI compatibility?

This would mean that all new programs supporting 32-bit domains would have to combine both fields to get the full domain number.

I think we can just rename the 16-bit field to domain_16 (where only the lower 16 bits will be stored) and add a new 32-bit domain field at the end of the public part of the structure. Since the structures are always allocated by libpci, that will work. Old programs will fail silently on machines with large domain numbers, but that is hopefully acceptable.

Also, we should add a new version of some basic function call, for example pci_init, so that new programs will require a new version of the ABI.

Martin

#9 lore raw

PATCH v6 1/7 msi: Relax msi_domain_alloc() to support parentless MSI irqdomains

In reply to Keith Busch

From: Liu Jiang <[email protected]>

Previously msi_domain_alloc() assumes MSI irqdomains always have parent irqdomains, but that's not true for the new Intel VMD devices. So relax msi_domain_alloc() to support parentless MSI irqdomains.

---
 kernel/irq/msi.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
kernel/irq/msi.c +5 -3
index 6b0c0b7..5e15cb4 100644
@@ -109,9 +109,11 @@ static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
 	if (irq_find_mapping(domain, hwirq) > 0)
 		return -EEXIST;
 
-	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
-	if (ret < 0)
-		return ret;
+	if (domain->parent) {
+		ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+		if (ret < 0)
+			return ret;
+	}
 
 	for (i = 0; i < nr_irqs; i++) {
 		ret = ops->msi_init(domain, info, virq + i, hwirq + i, arg);
Signature · 1 line
2.6.2.307.g37023ba
#10 lore raw

PATCH v6 2/7 pci: child bus alloc fix on constrained resource

In reply to Keith Busch

Does not allocate a child bus if the new bus number does not fit in the parent's bus resource window.

---
 drivers/pci/probe.c | 6 ++++++
 1 file changed, 6 insertions(+)
drivers/pci/probe.c +6 -0
index edb1984..6e29f7a 100644
@@ -704,6 +704,12 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
 	int i;
 	int ret;
 
+	if (busnr > parent->busn_res.end) {
+		dev_printk(KERN_DEBUG, &parent->dev,
+			  "can not alloc bus:%d under %pR\n", busnr,
+			  &parent->busn_res);
+		return NULL;
+	}
 	/*
 	 * Allocate a new bus, and inherit stuff from the parent..
 	 */
Signature · 1 line
2.6.2.307.g37023ba
#11 lore raw

Re: [PATCHv6 2/7] pci: child bus alloc fix on constrained resource

Hi Keith,

Quoted 13 lines · Keith Busch

Does not allocate a child bus if the new bus number does not fit in the parent's bus resource window.

---
 drivers/pci/probe.c | 6 ++++++
 1 file changed, 6 insertions(+)
drivers/pci/probe.c +0 -0
index edb1984..6e29f7a 100644

drivers/pci/probe.c static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, · line 711

Quoted 8 lines
drivers/pci/probe.c +5 -0
@@ -704,6 +704,12 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
 	int i;
 	int ret;
 
+	if (busnr > parent->busn_res.end) {
+		dev_printk(KERN_DEBUG, &parent->dev,
+			  "can not alloc bus:%d under %pR\n", busnr,
+			  &parent->busn_res);
+		return NULL;

Can you take a look at 1820ffdccb9b ("PCI: Make sure bus number resources stay within their parents bounds") and 12d8706963f0 ("Revert "PCI: Make sure bus number resources stay within their parents bounds"")?

This is implemented differently, but it seems like it might expose the same problem we found with 1820ffdccb9b.

If you could take a look and confirm that "no, this does something differently than 1820ffdccb9b did" or "yes, this might expose that problem again," that would help.

Bjorn

Quoted 11 lines
drivers/pci/probe.c +1 -0
+	}
 	/*
 	 * Allocate a new bus, and inherit stuff from the parent..
 	 */
Signature · 6 lines
2.6.2.307.g37023ba

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#12 lore raw

Re: [PATCHv6 2/7] pci: child bus alloc fix on constrained resource

Quoted 11 lines · Bjorn Helgaas

drivers/pci/probe.c static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, · line 711

Quoted 5 lines · Keith Busch
drivers/pci/probe.c +5 -0
+	if (busnr > parent->busn_res.end) {
+		dev_printk(KERN_DEBUG, &parent->dev,
+			  "can not alloc bus:%d under %pR\n", busnr,
+			  &parent->busn_res);
+		return NULL;

Can you take a look at 1820ffdccb9b ("PCI: Make sure bus number resources stay within their parents bounds") and 12d8706963f0 ("Revert "PCI: Make sure bus number resources stay within their parents bounds"")?

Quoted 6 lines

This is implemented differently, but it seems like it might expose the same problem we found with 1820ffdccb9b.

If you could take a look and confirm that "no, this does something differently than 1820ffdccb9b did" or "yes, this might expose that problem again," that would help.

Thank you for the references. I think 1820ffdccb9b had the check too late, creating the child bus with resource conflicts. That'd probably make it appear unavailable. But I believe this new proposal will still break the same setup for a different reason.

We can live with this issue for now if we want to drop this patch from the series. The only way we encounter a problem is when the config space aperture is artificially constrained, so I can request the feature be put on hold while a better fix is developed.

#13 lore raw

Re: [PATCHv6 2/7] pci: child bus alloc fix on constrained resource

In reply to Keith Busch
Quoted 4 lines · Keith Busch

Does not allocate a child bus if the new bus number does not fit in the parent's bus resource window.

Other nits: please make this similar to previous changelogs in capitalization, sentence structure, etc. If we figure out that this is safe, and I end up applying it, I can just tweak it myself, but if you post it again, something like this is what I'm looking for:

PCI: Don't allocate child bus outside parent bus number range

Don't allocate a child bus if the new bus number does not fit in the parent's bus resource window.

The next patch is missing a subject line:

PCI/MSI: Export MSI and IRQ functions for use by modules

Export the following functions for use by modules:

    msi_desc_to_pci_dev()
    pci_msi_create_irq_domain()
    irq_domain_set_info()

And the following:

x86/PCI: Allow PCI domain-specific DMA ops

I know this seems pedantic, but try to use "x86/PCI" consistently because I think it's distracting to have the boiler-plate be different in non-useful ways, e.g.,

510fb6779eed x86/pci: Initial commit for new VMD device driver 510fb6779eed x86/pci: Initial commit for new VMD device driver b7a48e92f508 x86-pci: allow pci domain specific dma ops 8affb487d4a4 x86/PCI: Don't alloc pcibios-irq when MSI is enabled 991de2e59090 PCI, x86: Implement pcibios_alloc_irq() and pcibios_free_irq()

Bjorn

Quoted 28 lines
---
 drivers/pci/probe.c | 6 ++++++
 1 file changed, 6 insertions(+)
drivers/pci/probe.c +6 -0
index edb1984..6e29f7a 100644
@@ -704,6 +704,12 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
 	int i;
 	int ret;
 
+	if (busnr > parent->busn_res.end) {
+		dev_printk(KERN_DEBUG, &parent->dev,
+			  "can not alloc bus:%d under %pR\n", busnr,
+			  &parent->busn_res);
+		return NULL;
+	}
 	/*
 	 * Allocate a new bus, and inherit stuff from the parent..
 	 */
Signature · 6 lines
2.6.2.307.g37023ba

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#14 lore raw

PATCH v6 3/7 Export msi and irq functions for module use

In reply to Keith Busch
---
 drivers/pci/msi.c      | 2 ++
 kernel/irq/irqdomain.c | 1 +
 2 files changed, 3 insertions(+)
drivers/pci/msi.c +2 -0
index 53e4632..0fec654 100644
@@ -1126,6 +1126,7 @@ struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
 {
 	return to_pci_dev(desc->dev);
 }
+EXPORT_SYMBOL_GPL(msi_desc_to_pci_dev);
 
 void *msi_desc_to_pci_sysdata(struct msi_desc *desc)
 {
@@ -1285,6 +1286,7 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
 	domain->bus_token = DOMAIN_BUS_PCI_MSI;
 	return domain;
 }
+EXPORT_SYMBOL_GPL(pci_msi_create_irq_domain);
 
 /**
  * pci_msi_domain_alloc_irqs - Allocate interrupts for @dev in @domain
kernel/irq/irqdomain.c +1 -0
index 22aa961..ca05cc8 100644
@@ -1058,6 +1058,7 @@ void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
 	__irq_set_handler(virq, handler, 0, handler_name);
 	irq_set_handler_data(virq, handler_data);
 }
+EXPORT_SYMBOL(irq_domain_set_info);
 
 /**
  * irq_domain_reset_irq_data - Clear hwirq, chip and chip_data in @irq_data
Signature · 1 line
2.6.2.307.g37023ba
#15 lore raw

PATCH v6 4/7 x86-pci: allow pci domain specific dma ops

In reply to Keith Busch

New x86 pci h/w will require dma operations specific to that domain. This patch allows those domains to register their operations, and sets devices as they are discovere3d in that domain to use them.

---
 arch/x86/include/asm/device.h | 10 ++++++++++
 arch/x86/pci/common.c         | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)
arch/x86/include/asm/device.h +10 -0
index 03dd729..3b23897 100644
@@ -10,6 +10,16 @@ struct dev_archdata {
 #endif
 };
 
+#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
+struct dma_domain {
+	struct list_head node;
+	struct dma_map_ops *dma_ops;
+	int domain_nr;
+};
+extern void add_dma_domain(struct dma_domain *domain);
+extern void del_dma_domain(struct dma_domain *domain);
+#endif
+
 struct pdev_archdata {
 };
 
arch/x86/pci/common.c +38 -0
index eccd4d9..106fd13 100644
@@ -641,6 +641,43 @@ unsigned int pcibios_assign_all_busses(void)
 	return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0;
 }
 
+#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
+LIST_HEAD(dma_domain_list);
+DEFINE_SPINLOCK(dma_domain_list_lock);
+
+void add_dma_domain(struct dma_domain *domain)
+{
+	spin_lock(&dma_domain_list_lock);
+	list_add(&domain->node, &dma_domain_list);
+	spin_unlock(&dma_domain_list_lock);
+}
+EXPORT_SYMBOL_GPL(add_dma_domain);
+
+void del_dma_domain(struct dma_domain *domain)
+{
+	spin_lock(&dma_domain_list_lock);
+	list_del(&domain->node);
+	spin_unlock(&dma_domain_list_lock);
+}
+EXPORT_SYMBOL_GPL(del_dma_domain);
+
+static void set_dma_domain_ops(struct pci_dev *pdev)
+{
+	struct dma_domain *domain;
+
+	spin_lock(&dma_domain_list_lock);
+	list_for_each_entry(domain, &dma_domain_list, node) {
+		if (pci_domain_nr(pdev->bus) == domain->domain_nr) {
+			pdev->dev.archdata.dma_ops = domain->dma_ops;
+			break;
+		}
+	}
+	spin_unlock(&dma_domain_list_lock);
+}
+#else
+static void set_dma_domain_ops(struct pci_dev *pdev) {}
+#endif
+
 int pcibios_add_device(struct pci_dev *dev)
 {
 	struct setup_data *data;
@@ -670,6 +707,7 @@ int pcibios_add_device(struct pci_dev *dev)
 		pa_data = data->next;
 		iounmap(data);
 	}
+	set_dma_domain_ops(dev);
 	return 0;
 }
 
Signature · 1 line
2.6.2.307.g37023ba
#16 lore raw

PATCH v6 5/7 x86/pci: Initial commit for new VMD device driver

In reply to Keith Busch

The Intel Volume Management Device (VMD) is an integrated endpoint on the platform's PCIe root complex that acts as a host bridge to a secondary PCIe domain. BIOS can reassign one or more root ports to appear within a VMD domain instead of the primary domain. The immediate benefit is that additional PCI-e domains allow more than 256 buses in a system by letting bus number be reused across different domains.

VMD domains do not define ACPI _SEG, so to avoid domain clashing with host bridges defining this segment, VMD domains start at 0x10000 which is greater than the highest possible 16-bit ACPI defined _SEG.

This driver enumerates and enables the domain using the root bus configuration interface provided by the PCI subsystem. The driver provides configuration space accessor functions (pci_ops), bus and memory resources, an MSI irq domain with irq_chip implementation, and dma operations necessary to use devices in through the VMD endpoint's interface.

VMD routes I/O as follows:

1) Configuration Space: BAR 0 ("CFGBAR") of VMD provides the base address and size for configuration space register access to VMD-owned root ports. It works similarly to MMCONFIG for extended configuration space. Bus numbering is independent and does not conflict with the primary domain.

2) MMIO Space: BARs 2 and 4 ("MEMBAR1" and "MEMBAR2") of VMD provide the base address, size, and type for MMIO register access. These addresses are not translated by VMD hardware; they are simply reservations to be distributed to root ports' memory base/limit registers and subdivided among devices downstream.

3) DMA: To interact appropriately with IOMMU, the source ID DMA read and write requests are translated to the bus-device-function of the VMD endpoint. Otherwise, DMA operates normally without VMD-specific address translation.

4) Interrupts: Part of VMD's BAR 4 is reserved for VMD's MSI-X Table and PBA. MSIs from VMD domain devices and ports are remapped to appear if they were issued using one of VMD's MSI-X table entries. Each MSI and MSI-X addresses of VMD-owned devices and ports have a special format where the address refers to specific entries in VMD's MSI-X table. As with DMA, the interrupt source id is translated to VMD's bus-device-function.

The driver provides its own MSI and MSI-X configuration functions specific to how MSI messages are used within the VMD domain, and provides an irq_chip for independent IRQ allocation to relay interrupts from VMD's interrupt handler to the appropriate device driver's handler.

5) Errors: PCIe error message are intercepted by the root ports normally (e.g. AER), except with VMD, system errors (i.e. firmware first) are disabled by default. AER and hotplug interrupts are translated in the same way as endpoint interrupts.

6) VMD does not support INTx interrupts or IO ports. Devices or drivers requiring these features should either not be placed below VMD-owned root ports, or VMD should be disabled by BIOS for such endpoints.

---
 arch/x86/Kconfig              |  13 +
 arch/x86/include/asm/hw_irq.h |   5 +
 arch/x86/pci/Makefile         |   2 +
 arch/x86/pci/vmd.c            | 695 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 715 insertions(+)
 create mode 100644 arch/x86/pci/vmd.c
arch/x86/Kconfig +13 -0
index db3622f..9a5ab69 100644
@@ -2665,6 +2665,19 @@ config PMC_ATOM
 	def_bool y
         depends on PCI
 
+config VMD
+	depends on PCI_MSI
+	tristate "Volume Management Device Driver"
+	default N
+	---help---
+	  Adds support for the Intel Volume Manage Device (VMD). VMD is a
+	  secondary PCI host bridge that allows PCI Express root ports,
+	  and devices attached to them, to be removed from the default
+	  PCI domain and placed within the VMD domain. This provides
+	  additional bus resources than are otherwise possible with a
+	  single domain. If you know your system provides one of these and
+	  have devices attached to it, say Y; if you are not sure, say N.
+
 source "net/Kconfig"
 
 source "drivers/Kconfig"
arch/x86/include/asm/hw_irq.h +5 -0
index 1e3408e..79c35c4 100644
@@ -108,6 +108,11 @@ struct irq_alloc_info {
 			struct IO_APIC_route_entry *ioapic_entry;
 		};
 #endif
+#if IS_ENABLED(CONFIG_VMD)
+		struct {
+			struct msi_desc	*desc;
+		};
+#endif
 #ifdef	CONFIG_DMAR_TABLE
 		struct {
 			int		dmar_id;
arch/x86/pci/Makefile +2 -0
index 5c6fc35..97062a6 100644
@@ -23,6 +23,8 @@ obj-y				+= bus_numa.o
 obj-$(CONFIG_AMD_NB)		+= amd_bus.o
 obj-$(CONFIG_PCI_CNB20LE_QUIRK)	+= broadcom_bus.o
 
+obj-$(CONFIG_VMD) += vmd.o
+
 ifeq ($(CONFIG_PCI_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
 endif
arch/x86/pci/vmd.c new file +695 -0

695 lines not on this page · raw message

Signature · 1 line
2.6.2.307.g37023ba
#17 lore raw

Re: [PATCHv6 5/7] x86/pci: Initial commit for new VMD device driver

Quoted 11 lines · Keith Busch

The Intel Volume Management Device (VMD) is an integrated endpoint on the platform's PCIe root complex that acts as a host bridge to a secondary PCIe domain. BIOS can reassign one or more root ports to appear within a VMD domain instead of the primary domain. The immediate benefit is that additional PCI-e domains allow more than 256 buses in a system by letting bus number be reused across different domains.

+/*
+ * VMD h/w converts posted config writes to non-posted. The read-back in this
+ * function forces the completion so it returns only after the config space was
+ * written, as expected.

This comment sounds backwards:

posted writes don't wait for completion
non-posted writes do wait for completion

If the hardware converts to non-posted writes, you shouldn't need a read-back. It seems like you would need the read-back if the hardware converted non-posted to posted.

Quoted 84 lines

+ */

arch/x86/pci/vmd.c new file +82 -0
+static int vmd_pci_write(struct pci_bus *bus, unsigned int devfn, int reg,
+							int len, u32 value)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct vmd_dev *vmd = vmd_from_bus(bus);
+	char __iomem *addr = vmd->cfgbar + (bus->number << 20) +
+						(devfn << 12) + reg;
+
+	if ((addr - vmd->cfgbar) + len >= resource_size(&vmd->dev->resource[0]))
+		return -EFAULT;
+
+	spin_lock_irqsave(&vmd->cfg_lock, flags);
+	switch (len) {
+	case 1:
+		writeb(value, addr);
+		readb(addr);
+		break;
+	case 2:
+		writew(value, addr);
+		readw(addr);
+		break;
+	case 4:
+		writel(value, addr);
+		readl(addr);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	spin_unlock_irqrestore(&vmd->cfg_lock, flags);
+	return ret;
+}
 
+static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct vmd_dev *vmd;
+	int i, err;
+
+	if (resource_size(&dev->resource[0]) < (1 << 20))
+		return -ENOMEM;
+
+	vmd = devm_kzalloc(&dev->dev, sizeof(*vmd), GFP_KERNEL);
+	if (!vmd)
+		return -ENOMEM;
+
+	vmd->dev = dev;
+	err = pcim_enable_device(dev);
+	if (err < 0)
+		return err;
+
+	vmd->cfgbar = pcim_iomap(dev, 0, 0);
+	if (!vmd->cfgbar)
+		return -ENOMEM;
+
+	pci_set_master(dev);
+	if (dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(64)) &&
+	    dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)))
+		return -ENODEV;
+
+	vmd->msix_count = pci_msix_vec_count(dev);
+	if (vmd->msix_count < 0)
+		return -ENODEV;
+
+	vmd->irqs = devm_kcalloc(&dev->dev, vmd->msix_count, sizeof(*vmd->irqs),
+							GFP_KERNEL);
+	if (!vmd->irqs)
+		return -ENOMEM;
+
+	vmd->msix_entries = devm_kcalloc(&dev->dev, vmd->msix_count,
+					sizeof(*vmd->msix_entries), GFP_KERNEL);
+	if(!vmd->msix_entries)
+		return -ENOMEM;
+	for (i = 0; i < vmd->msix_count; i++)
+		vmd->msix_entries[i].entry = i;
+
+	vmd->msix_count = pci_enable_msix_range(vmd->dev, vmd->msix_entries, 1,
+							vmd->msix_count);
+	if (vmd->msix_count < 0)
+		return vmd->msix_count;
+
+	for (i = 0; i < vmd->msix_count; i++) {
+		INIT_LIST_HEAD(&vmd->irqs[i].irq_list);

arch/x86/pci/vmd.c line 638

Quoted 10 lines
arch/x86/pci/vmd.c new file +10 -0
+		vmd->irqs[i].vmd_vector = vmd->msix_entries[i].vector;
+		vmd->irqs[i].index = i;
+
+		err = devm_request_irq(&dev->dev, vmd->irqs[i].vmd_vector,
+				vmd_irq, 0, "vmd", &vmd->irqs[i]);
+		if (err)
+			return err;
+	}
+	spin_lock_init(&vmd->cfg_lock);
+	pci_set_drvdata(dev, vmd);

Seems like it might be nice to have something in dmesg that would connect this PCI device to the new PCI domain. It's a new, unusual topology and a hint might help everybody understand what's going on.

arch/x86/pci/vmd.c line 643

Quoted 5 lines
arch/x86/pci/vmd.c new file +5 -0
+	err = vmd_enable_domain(vmd);
+	if (err)
+		return err;
+	return 0;
+}

Bjorn

#18 lore raw

Re: [PATCHv6 5/7] x86/pci: Initial commit for new VMD device driver

Quoted 6 lines · Bjorn Helgaas
Quoted 4 lines · Keith Busch

+/*
+ * VMD h/w converts posted config writes to non-posted. The read-back in this
+ * function forces the completion so it returns only after the config space was
+ * written, as expected.

Quoted 8 lines

This comment sounds backwards:

posted writes don't wait for completion
non-posted writes do wait for completion

If the hardware converts to non-posted writes, you shouldn't need a read-back. It seems like you would need the read-back if the hardware converted non-posted to posted.

Oops, the comment has it backwards. Non-posted config writes become posted memory write requests with this h/w.

Quoted 3 lines

Seems like it might be nice to have something in dmesg that would connect this PCI device to the new PCI domain. It's a new, unusual topology and a hint might help everybody understand what's going on.

Sounds good, will add.

In addition that, I'd like to mention this patch links a new domain's root bus kobject under the VMD end-point's so the sysfs hierarchy captures this as well.

#19 lore raw

PATCH v6 6/7 aer_inject: Use 32 bit int type domains

In reply to Keith Busch

New pci device provides additional pci domains that start above what 16 bits can address.

---
 drivers/pci/pcie/aer/aer_inject.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
drivers/pci/pcie/aer/aer_inject.c +8 -8
index 182224a..24b191e 100644
@@ -41,12 +41,12 @@ struct aer_error_inj {
 	u32 header_log1;
 	u32 header_log2;
 	u32 header_log3;
-	u16 domain;
+	int domain;
 };
 
 struct aer_error {
 	struct list_head list;
-	u16 domain;
+	int domain;
 	unsigned int bus;
 	unsigned int devfn;
 	int pos_cap_err;
@@ -74,7 +74,7 @@ static LIST_HEAD(pci_bus_ops_list);
 /* Protect einjected and pci_bus_ops_list */
 static DEFINE_SPINLOCK(inject_lock);
 
-static void aer_error_init(struct aer_error *err, u16 domain,
+static void aer_error_init(struct aer_error *err, int domain,
 			   unsigned int bus, unsigned int devfn,
 			   int pos_cap_err)
 {
@@ -86,7 +86,7 @@ static void aer_error_init(struct aer_error *err, u16 domain,
 }
 
 /* inject_lock must be held before calling */
-static struct aer_error *__find_aer_error(u16 domain, unsigned int bus,
+static struct aer_error *__find_aer_error(int domain, unsigned int bus,
 					  unsigned int devfn)
 {
 	struct aer_error *err;
@@ -106,7 +106,7 @@ static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev)
 	int domain = pci_domain_nr(dev->bus);
 	if (domain < 0)
 		return NULL;
-	return __find_aer_error((u16)domain, dev->bus->number, dev->devfn);
+	return __find_aer_error(domain, dev->bus->number, dev->devfn);
 }
 
 /* inject_lock must be held before calling */
@@ -196,7 +196,7 @@ static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where,
 	domain = pci_domain_nr(bus);
 	if (domain < 0)
 		goto out;
-	err = __find_aer_error((u16)domain, bus->number, devfn);
+	err = __find_aer_error(domain, bus->number, devfn);
 	if (!err)
 		goto out;
 
@@ -228,7 +228,7 @@ static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where,
 	domain = pci_domain_nr(bus);
 	if (domain < 0)
 		goto out;
-	err = __find_aer_error((u16)domain, bus->number, devfn);
+	err = __find_aer_error(domain, bus->number, devfn);
 	if (!err)
 		goto out;
 
@@ -329,7 +329,7 @@ static int aer_inject(struct aer_error_inj *einj)
 	u32 sever, cor_mask, uncor_mask, cor_mask_orig = 0, uncor_mask_orig = 0;
 	int ret = 0;
 
-	dev = pci_get_domain_bus_and_slot((int)einj->domain, einj->bus, devfn);
+	dev = pci_get_domain_bus_and_slot(einj->domain, einj->bus, devfn);
 	if (!dev)
 		return -ENODEV;
 	rpdev = pcie_find_root_port(dev);
Signature · 1 line
2.6.2.307.g37023ba
#20 lore raw

Re: [PATCHv6 6/7] aer_inject: Use 32 bit int type domains

PCI/AER: Increase domain type from 16 to 32 bits

Quoted 13 lines · Keith Busch

New pci device provides additional pci domains that start above what 16 bits can address.

---
 drivers/pci/pcie/aer/aer_inject.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
drivers/pci/pcie/aer/aer_inject.c +0 -0
index 182224a..24b191e 100644

drivers/pci/pcie/aer/aer_inject.c struct aer_error_inj { · line 44

Quoted 5 lines
drivers/pci/pcie/aer/aer_inject.c +1 -1
@@ -41,12 +41,12 @@ struct aer_error_inj {
 	u32 header_log1;
 	u32 header_log2;
 	u32 header_log3;
-	u16 domain;
+	int domain;

If you want 32 bits explicitly, why don't you use u32 here?

Quoted 70 lines
drivers/pci/pcie/aer/aer_inject.c +7 -7
 };
 
 struct aer_error {
 	struct list_head list;
-	u16 domain;
+	int domain;
 	unsigned int bus;
 	unsigned int devfn;
 	int pos_cap_err;
@@ -74,7 +74,7 @@ static LIST_HEAD(pci_bus_ops_list);
 /* Protect einjected and pci_bus_ops_list */
 static DEFINE_SPINLOCK(inject_lock);
 
-static void aer_error_init(struct aer_error *err, u16 domain,
+static void aer_error_init(struct aer_error *err, int domain,
 			   unsigned int bus, unsigned int devfn,
 			   int pos_cap_err)
 {
@@ -86,7 +86,7 @@ static void aer_error_init(struct aer_error *err, u16 domain,
 }
 
 /* inject_lock must be held before calling */
-static struct aer_error *__find_aer_error(u16 domain, unsigned int bus,
+static struct aer_error *__find_aer_error(int domain, unsigned int bus,
 					  unsigned int devfn)
 {
 	struct aer_error *err;
@@ -106,7 +106,7 @@ static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev)
 	int domain = pci_domain_nr(dev->bus);
 	if (domain < 0)
 		return NULL;
-	return __find_aer_error((u16)domain, dev->bus->number, dev->devfn);
+	return __find_aer_error(domain, dev->bus->number, dev->devfn);
 }
 
 /* inject_lock must be held before calling */
@@ -196,7 +196,7 @@ static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where,
 	domain = pci_domain_nr(bus);
 	if (domain < 0)
 		goto out;
-	err = __find_aer_error((u16)domain, bus->number, devfn);
+	err = __find_aer_error(domain, bus->number, devfn);
 	if (!err)
 		goto out;
 
@@ -228,7 +228,7 @@ static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where,
 	domain = pci_domain_nr(bus);
 	if (domain < 0)
 		goto out;
-	err = __find_aer_error((u16)domain, bus->number, devfn);
+	err = __find_aer_error(domain, bus->number, devfn);
 	if (!err)
 		goto out;
 
@@ -329,7 +329,7 @@ static int aer_inject(struct aer_error_inj *einj)
 	u32 sever, cor_mask, uncor_mask, cor_mask_orig = 0, uncor_mask_orig = 0;
 	int ret = 0;
 
-	dev = pci_get_domain_bus_and_slot((int)einj->domain, einj->bus, devfn);
+	dev = pci_get_domain_bus_and_slot(einj->domain, einj->bus, devfn);
 	if (!dev)
 		return -ENODEV;
 	rpdev = pcie_find_root_port(dev);
Signature · 6 lines
2.6.2.307.g37023ba

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#21 lore raw

Re: [PATCHv6 6/7] aer_inject: Use 32 bit int type domains

Quoted 5 lines · Bjorn Helgaas

Review

Quoted 2 lines · Keith Busch
(quoted diff) +1 -1
-	u16 domain;
+	int domain;

If you want 32 bits explicitly, why don't you use u32 here?

It matches the types already defined in struct pci_dev and the pci_domain_nr return value.

Would you prefer changing all of them to u32, just aer_inject's, or leave as-is?

#22 lore raw
In reply to Keith Busch

On Mon, 7 Dec 2015, Keith Busch wrote:

So this looks really good now (at least the irq related parts).

Bjorn, how should we handle this?

I can take it all through tip or just apply the irq/msi related parts and provide you a branch to pull them from. Either way is fine.

Thanks,

tglx