Re: [PATCH] pci-sysfs: Make PCI bridge attribute visible in sysfs

2017-05-04 Thread Bjorn Helgaas
On Thu, May 4, 2017 at 2:32 AM, Vee Khee Wong  wrote:
>
>
> On Mon, 2017-04-17 at 10:41 -0500, Bjorn Helgaas wrote:
>> On Mon, Apr 17, 2017 at 12:50 AM, Wong Vee Khee > > wrote:
>> >
>> > From: vwong 
>> >
>> > Export the PCIe link attributes of PCI bridges to sysfs.
>> This needs justification for *why* we should export these via sysfs.
>>
>> Some of these things, e.g., secondary/subordinate bus numbers, are
>> already visible to non-privileged users via "lspci -v".
>>
> We need to expose these attributes via sysfs due to several reasons
> listed below:
>
> 1) PCIe capabilities info such as Maximum/Actual link width and link speed 
> only visible to privileged users via "lspci -vvv". The software that my team 
> is working on need to get PCIe link information as non-root user.
>
> 2) From a client perspective, it require a lot of overhead parsing output of 
> lspci to get PCIe capabilities. Our application is only interested in getting 
> PCIe bridges but lspci print info for all PCIe devices.

I'm looking for a revised patch that incorporates the justification in
the changelog and addresses the code comments I made.

Bjorn


Re: [PATCH] pci-sysfs: Make PCI bridge attribute visible in sysfs

2017-05-04 Thread Vee Khee Wong


On Mon, 2017-04-17 at 10:41 -0500, Bjorn Helgaas wrote:
> On Mon, Apr 17, 2017 at 12:50 AM, Wong Vee Khee  > wrote:
> > 
> > From: vwong 
> > 
> > Export the PCIe link attributes of PCI bridges to sysfs.
> This needs justification for *why* we should export these via sysfs.
> 
> Some of these things, e.g., secondary/subordinate bus numbers, are
> already visible to non-privileged users via "lspci -v".
> 
We need to expose these attributes via sysfs due to several reasons
listed below:

1) PCIe capabilities info such as Maximum/Actual link width and link speed only 
visible to privileged users via "lspci -vvv". The software that my team is 
working on need to get PCIe link information as non-root user.

2) From a client perspective, it require a lot of overhead parsing output of 
lspci to get PCIe capabilities. Our application is only interested in getting 
PCIe bridges but lspci print info for all PCIe devices.

> > 
> > Signed-off-by: Wong Vee Khee 
> > Signed-off-by: Hui Chun Ong 
> > ---
> >  drivers/pci/pci-sysfs.c   | 197
> > +-
> >  include/uapi/linux/pci_regs.h |   4 +
> >  2 files changed, 197 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
> > index 25d010d..a218c43 100644
> > --- a/drivers/pci/pci-sysfs.c
> > +++ b/drivers/pci/pci-sysfs.c
> > @@ -154,6 +154,127 @@ static ssize_t resource_show(struct device
> > *dev, struct device_attribute *attr,
> >  }
> >  static DEVICE_ATTR_RO(resource);
> > 
> > +static ssize_t max_link_speed_show(struct device *dev,
> > +  struct device_attribute *attr,
> > char *buf)
> > +{
> > +   struct pci_dev *pci_dev = to_pci_dev(dev);
> > +   u32 linkcap;
> > +   int err;
> > +   const char *speed;
> > +
> > +   err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP,
> > &linkcap);
> > +
> > +   if (!err && linkcap) {
>   if (err)
> return -EINVAL;
> 
> I don't think there's a reason to test "linkcap" here.  Per spec,
> zero
> is not a valid value of LNKCAP, so if we got a zero, I think showing
> "Unknown speed" would be fine.
> 
> > 
> > +   switch (linkcap & PCI_EXP_LNKCAP_MLS) {
> > +   case PCI_EXP_LNKCAP_MLS_8_0GB:
> > +   speed = "8 GT/s";
> > +   break;
> > +   case PCI_EXP_LNKCAP_MLS_5_0GB:
> > +   speed = "5 GT/s";
> > +   break;
> > +   case PCI_EXP_LNKCAP_MLS_2_5GB:
> > +   speed = "2.5 GT/s";
> > +   break;
> > +   default:
> > +   speed = "Unknown speed";
> > +   }
> > +
> > +   return sprintf(buf, "%s\n", speed);
> > +   }
> > +
> > +   return -EINVAL;
> > +}
> > +static DEVICE_ATTR_RO(max_link_speed);
> > +
> > +static ssize_t max_link_width_show(struct device *dev,
> > +  struct device_attribute *attr,
> > char *buf)
> > +{
> > +   struct pci_dev *pci_dev = to_pci_dev(dev);
> > +   u32 linkcap;
> > +   int err;
> > +
> > +   err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP,
> > &linkcap);
> > +
> > +   return err ? -EINVAL : sprintf(
> > +   buf, "%u\n", (linkcap & PCI_EXP_LNKCAP_MLW) >> 4);
>   if (err)
> return -EINVAL;
> 
>   return sprintf(...);
> 
> > 
> > +}
> > +static DEVICE_ATTR_RO(max_link_width);
> > +
> > +static ssize_t current_link_speed_show(struct device *dev,
> > +  struct device_attribute
> > *attr, char *buf)
> > +{
> > +   struct pci_dev *pci_dev = to_pci_dev(dev);
> > +   u16 linkstat;
> > +   int err;
> > +   const char *speed;
> > +
> > +   err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA,
> > &linkstat);
> > +
> > +   if (!err && linkstat) {
> See max_link_speed above.
> 
> > 
> > +   switch (linkstat & PCI_EXP_LNKSTA_CLS) {
> > +   case PCI_EXP_LNKSTA_CLS_8_0GB:
> > +   speed = "8 GT/s";
> > +   break;
> > +   case PCI_EXP_LNKSTA_CLS_5_0GB:
> > +   speed = "5 GT/s";
> > +   break;
> > +   case PCI_EXP_LNKSTA_CLS_2_5GB:
> > +   speed = "2.5 GT/s";
> > +   break;
> > +   default:
> > +   speed = "Unknown speed";
> > +   }
> > +
> > +   return sprintf(buf, "%s\n", speed);
> > +   }
> > +
> > +   return -EINVAL;
> > +}
> > +static DEVICE_ATTR_RO(current_link_speed);
> > +
> > +static ssize_t current_link_width_show(struct device *dev,
> > +  struct device_attribute
> > *attr, char *buf)
> > +{
> > +   struct pci_dev *pci_dev = to_pci_dev(dev);
> > +   u16 linkstat;
> > +   int err;
> > +
> > +   err = pcie_capability_read_word

Re: [PATCH] pci-sysfs: Make PCI bridge attribute visible in sysfs

2017-04-17 Thread Bjorn Helgaas
On Mon, Apr 17, 2017 at 12:50 AM, Wong Vee Khee  wrote:
> From: vwong 
>
> Export the PCIe link attributes of PCI bridges to sysfs.

This needs justification for *why* we should export these via sysfs.

Some of these things, e.g., secondary/subordinate bus numbers, are
already visible to non-privileged users via "lspci -v".

> Signed-off-by: Wong Vee Khee 
> Signed-off-by: Hui Chun Ong 
> ---
>  drivers/pci/pci-sysfs.c   | 197 
> +-
>  include/uapi/linux/pci_regs.h |   4 +
>  2 files changed, 197 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
> index 25d010d..a218c43 100644
> --- a/drivers/pci/pci-sysfs.c
> +++ b/drivers/pci/pci-sysfs.c
> @@ -154,6 +154,127 @@ static ssize_t resource_show(struct device *dev, struct 
> device_attribute *attr,
>  }
>  static DEVICE_ATTR_RO(resource);
>
> +static ssize_t max_link_speed_show(struct device *dev,
> +  struct device_attribute *attr, char *buf)
> +{
> +   struct pci_dev *pci_dev = to_pci_dev(dev);
> +   u32 linkcap;
> +   int err;
> +   const char *speed;
> +
> +   err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap);
> +
> +   if (!err && linkcap) {

  if (err)
return -EINVAL;

I don't think there's a reason to test "linkcap" here.  Per spec, zero
is not a valid value of LNKCAP, so if we got a zero, I think showing
"Unknown speed" would be fine.

> +   switch (linkcap & PCI_EXP_LNKCAP_MLS) {
> +   case PCI_EXP_LNKCAP_MLS_8_0GB:
> +   speed = "8 GT/s";
> +   break;
> +   case PCI_EXP_LNKCAP_MLS_5_0GB:
> +   speed = "5 GT/s";
> +   break;
> +   case PCI_EXP_LNKCAP_MLS_2_5GB:
> +   speed = "2.5 GT/s";
> +   break;
> +   default:
> +   speed = "Unknown speed";
> +   }
> +
> +   return sprintf(buf, "%s\n", speed);
> +   }
> +
> +   return -EINVAL;
> +}
> +static DEVICE_ATTR_RO(max_link_speed);
> +
> +static ssize_t max_link_width_show(struct device *dev,
> +  struct device_attribute *attr, char *buf)
> +{
> +   struct pci_dev *pci_dev = to_pci_dev(dev);
> +   u32 linkcap;
> +   int err;
> +
> +   err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap);
> +
> +   return err ? -EINVAL : sprintf(
> +   buf, "%u\n", (linkcap & PCI_EXP_LNKCAP_MLW) >> 4);

  if (err)
return -EINVAL;

  return sprintf(...);

> +}
> +static DEVICE_ATTR_RO(max_link_width);
> +
> +static ssize_t current_link_speed_show(struct device *dev,
> +  struct device_attribute *attr, char 
> *buf)
> +{
> +   struct pci_dev *pci_dev = to_pci_dev(dev);
> +   u16 linkstat;
> +   int err;
> +   const char *speed;
> +
> +   err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat);
> +
> +   if (!err && linkstat) {

See max_link_speed above.

> +   switch (linkstat & PCI_EXP_LNKSTA_CLS) {
> +   case PCI_EXP_LNKSTA_CLS_8_0GB:
> +   speed = "8 GT/s";
> +   break;
> +   case PCI_EXP_LNKSTA_CLS_5_0GB:
> +   speed = "5 GT/s";
> +   break;
> +   case PCI_EXP_LNKSTA_CLS_2_5GB:
> +   speed = "2.5 GT/s";
> +   break;
> +   default:
> +   speed = "Unknown speed";
> +   }
> +
> +   return sprintf(buf, "%s\n", speed);
> +   }
> +
> +   return -EINVAL;
> +}
> +static DEVICE_ATTR_RO(current_link_speed);
> +
> +static ssize_t current_link_width_show(struct device *dev,
> +  struct device_attribute *attr, char 
> *buf)
> +{
> +   struct pci_dev *pci_dev = to_pci_dev(dev);
> +   u16 linkstat;
> +   int err;
> +
> +   err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat);
> +
> +   return err ? -EINVAL : sprintf(
> +   buf, "%u\n",
> +   (linkstat & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT);
> +}
> +static DEVICE_ATTR_RO(current_link_width);
> +
> +static ssize_t secondary_bus_number_show(struct device *dev,
> +struct device_attribute *attr,
> +char *buf)
> +{
> +   struct pci_dev *pci_dev = to_pci_dev(dev);
> +   u8 sec_bus;
> +   int err;
> +
> +   err = pci_read_config_byte(pci_dev, PCI_SECONDARY_BUS, &sec_bus);

There is already a /sys/devices/pci:BB/:BB:dd.f//
directory and a .../pci_bus/ directory that looks like it is the
secondary bus.  Is that enough?

If we also need this file, I would like it to do something sensible
when there is no secondary bus

[PATCH] pci-sysfs: Make PCI bridge attribute visible in sysfs

2017-04-16 Thread Wong Vee Khee
From: vwong 

Export the PCIe link attributes of PCI bridges to sysfs.

Signed-off-by: Wong Vee Khee 
Signed-off-by: Hui Chun Ong 
---
 drivers/pci/pci-sysfs.c   | 197 +-
 include/uapi/linux/pci_regs.h |   4 +
 2 files changed, 197 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 25d010d..a218c43 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -154,6 +154,127 @@ static ssize_t resource_show(struct device *dev, struct 
device_attribute *attr,
 }
 static DEVICE_ATTR_RO(resource);
 
+static ssize_t max_link_speed_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u32 linkcap;
+   int err;
+   const char *speed;
+
+   err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap);
+
+   if (!err && linkcap) {
+   switch (linkcap & PCI_EXP_LNKCAP_MLS) {
+   case PCI_EXP_LNKCAP_MLS_8_0GB:
+   speed = "8 GT/s";
+   break;
+   case PCI_EXP_LNKCAP_MLS_5_0GB:
+   speed = "5 GT/s";
+   break;
+   case PCI_EXP_LNKCAP_MLS_2_5GB:
+   speed = "2.5 GT/s";
+   break;
+   default:
+   speed = "Unknown speed";
+   }
+
+   return sprintf(buf, "%s\n", speed);
+   }
+
+   return -EINVAL;
+}
+static DEVICE_ATTR_RO(max_link_speed);
+
+static ssize_t max_link_width_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u32 linkcap;
+   int err;
+
+   err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap);
+
+   return err ? -EINVAL : sprintf(
+   buf, "%u\n", (linkcap & PCI_EXP_LNKCAP_MLW) >> 4);
+}
+static DEVICE_ATTR_RO(max_link_width);
+
+static ssize_t current_link_speed_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u16 linkstat;
+   int err;
+   const char *speed;
+
+   err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat);
+
+   if (!err && linkstat) {
+   switch (linkstat & PCI_EXP_LNKSTA_CLS) {
+   case PCI_EXP_LNKSTA_CLS_8_0GB:
+   speed = "8 GT/s";
+   break;
+   case PCI_EXP_LNKSTA_CLS_5_0GB:
+   speed = "5 GT/s";
+   break;
+   case PCI_EXP_LNKSTA_CLS_2_5GB:
+   speed = "2.5 GT/s";
+   break;
+   default:
+   speed = "Unknown speed";
+   }
+
+   return sprintf(buf, "%s\n", speed);
+   }
+
+   return -EINVAL;
+}
+static DEVICE_ATTR_RO(current_link_speed);
+
+static ssize_t current_link_width_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u16 linkstat;
+   int err;
+
+   err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat);
+
+   return err ? -EINVAL : sprintf(
+   buf, "%u\n",
+   (linkstat & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT);
+}
+static DEVICE_ATTR_RO(current_link_width);
+
+static ssize_t secondary_bus_number_show(struct device *dev,
+struct device_attribute *attr,
+char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u8 sec_bus;
+   int err;
+
+   err = pci_read_config_byte(pci_dev, PCI_SECONDARY_BUS, &sec_bus);
+
+   return err ? -EINVAL : sprintf(buf, "%u\n", sec_bus);
+}
+static DEVICE_ATTR_RO(secondary_bus_number);
+
+static ssize_t subordinate_bus_number_show(struct device *dev,
+  struct device_attribute *attr,
+  char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u8 sub_bus;
+   int err;
+
+   err = pci_read_config_byte(pci_dev, PCI_SUBORDINATE_BUS, &sub_bus);
+
+   return err ? -EINVAL : sprintf(buf, "%u\n", sub_bus);
+}
+static DEVICE_ATTR_RO(subordinate_bus_number);
+
 static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
 char *buf)
 {
@@ -602,12 +723,17 @@ static struct attribute *pci_dev_attrs[] = {
NULL,
 };
 
-static const struct attribute_group pci_dev_group = {
-   .attrs = pci_dev_attrs,
+static struct attribute *pci_bridge_attrs[] = {
+   &dev_attr_subordinate_bus_number.attr,
+   &dev_attr_secondary_bus_number.attr,
+   NU