RE: [PATCH v3 1/3] Add sysfs attribute for CXL 1.1 device link status

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

 



Dan Williams wrote:
> Kobayashi,Daisuke wrote:
> > This patch implements a process to output the link status information
> > of the CXL1.1 device to sysfs. The values of the registers related to
> > the link status are outputted into three separate files.
> >
> > In CXL1.1, the link status of the device is included in the RCRB
> > mapped to the memory mapped register area. This function accesses the
> > address where the device's RCRB is mapped.
> 
> Per the comments on the cover letter I would rewrite this as:
> 
> ---
> In CXL1.1, the link status of the device is included in the RCRB mapped to the
> memory mapped register area. Critically, that arrangement makes the link
> status and control registers invisible to existing PCI user tooling.
> 
> Export those registers via sysfs with the expectation that PCI user tooling will
> alternatively look for these sysfs files when attempting to access these
> registers on CXL 1.1 endpoints.
> ---
> 

This message will be updated in the next patch.
Thank you for your helpful feedback.

> > Signed-off-by: "Kobayashi,Daisuke" <kobayashi.da-06@xxxxxxxxxxx>
> > ---
> >  drivers/cxl/pci.c | 193
> > ++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 193 insertions(+)
> >
> > diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index
> > 4fd1f207c84e..8f66f80a7bdc 100644
> > --- a/drivers/cxl/pci.c
> > +++ b/drivers/cxl/pci.c
> > @@ -781,6 +781,195 @@ static int cxl_event_config(struct pci_host_bridge
> *host_bridge,
> >  	return 0;
> >  }
> >
> > +static u8 cxl_rcrb_get_pcie_cap_offset(void __iomem *addr){
> > +	u8 offset;
> > +	u32 cap_hdr;
> > +
> > +	offset = readb(addr + PCI_CAPABILITY_LIST);
> > +	cap_hdr = readl(addr + offset);
> > +	while ((cap_hdr & 0x000000ff) != PCI_CAP_ID_EXP) {
> > +		offset = (cap_hdr >> 8) & 0x000000ff;
> > +		if (offset == 0) // End of capability list
> > +			return 0;
> > +		cap_hdr = readl(addr + offset);
> > +	}
> > +	return offset;
> 
> The location is static, so there should be no need to lookup the location every
> time the sysfs attribute is accessed. I also think the values are static unless the
> link is reset. So my expectation is that these register values can just be read
> once and cached.
> 
> Likely the best place to do this is inside __rcrb_to_component(). That routine
> already has the RCRB mapped and can be refactored to collect the the link
> status registers. Something like, rename __rcrb_to_component() to
> __rcrb_to_regs() and then have it fill in an updated cxl_rcrb_info():
> 

Add processing to__rcrb_to_component() to change the implementation
to cache these values. As you say, I think these values are static, 
so it's not efficient to access the RCRB every time you access the sysfs attribute.

> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index
> 534e25e2f0a4..16c7472877b7 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -651,7 +651,12 @@ cxl_find_dport_by_dev(struct cxl_port *port, const
> struct device *dport_dev)
> 
>  struct cxl_rcrb_info {
>         resource_size_t base;
> +       resource_size_t component_reg;
> +       resource_size_t rcd_component_reg;
>         u16 aer_cap;
> +       u16 rcd_lnkctrl;
> +       u16 rcd_lnkstatus;
> +       u32 rcd_lnkcap;
>  };
> 
>  /**
> 
> > +
> > +}
> > +
> > +static u32 cxl_rcrb_to_linkcap(struct device *dev, resource_size_t
> > +rcrb) {
> > +	void __iomem *addr;
> > +	u8 offset;
> > +	u32 linkcap;
> > +
> > +	if (WARN_ON_ONCE(rcrb == CXL_RESOURCE_NONE))
> > +		return 0;
> 
> Why is this a WARN_ON_ONCE()? In other words the caller should know ahead
> of time whether it has a valid RCRB base or not.
> 
> ...oh, I see this is copying cxl_rcrb_to_aer(). I think that
> WARN_ON_ONCE() in that function is bogus as well.
> 
> 

Yes, as you mentioned, I have copied cxl_rcrb_to_aer().
However, it seems to be an improper implementation.
In the next patch, I will modify the implementation to cache the value, 
and consequently, this part of the code will be removed.

> > +	if (!request_mem_region(rcrb, SZ_4K, dev_name(dev)))
> > +		return 0;
> 
> This is awkward because it may collide with usages of the RCRB, so that is
> another reason to cache the values.
> 
> > +
> > +	addr = ioremap(rcrb, SZ_4K);
> > +	if (!addr)
> > +		goto out;
> > +
> > +	offset = cxl_rcrb_get_pcie_cap_offset(addr);
> > +	if (offset)
> > +		dev_dbg(dev, "found PCIe capability (0x%x)\n", offset);
> > +	else
> > +		goto out;
> > +
> > +	linkcap = readl(addr + offset + PCI_EXP_LNKCAP);
> > +	iounmap(addr);
> > +out:
> > +	release_mem_region(rcrb, SZ_4K);
> > +
> > +	return linkcap;
> > +}
> > +
> > +static ssize_t rcd_link_cap_show(struct device *dev,
> > +				   struct device_attribute *attr, char *buf) {
> > +	struct cxl_port *port;
> > +	struct cxl_dport *dport;
> > +	struct device *parent = dev->parent;
> > +	struct pci_dev *parent_pdev = to_pci_dev(parent);
> > +	u32 linkcap;
> > +
> > +	port = cxl_pci_find_port(parent_pdev, &dport);
> > +	if (!port)
> > +		return -EINVAL;
> > +
> > +	linkcap = cxl_rcrb_to_linkcap(dev, dport->rcrb.base + SZ_4K);
> > +	return sysfs_emit(buf, "%x\n", linkcap);
> 
> This and the other ones should be using "%#x\n" so that the format of the
> number base is included.
> 

I will fix them. Thank you.

> > +}
> > +static DEVICE_ATTR_RO(rcd_link_cap);
> > +
> > +static u16 cxl_rcrb_to_linkctr(struct device *dev, resource_size_t
> > +rcrb) {
> > +	void __iomem *addr;
> > +	u8 offset;
> > +	u16 linkctrl;
> > +
> > +	if (WARN_ON_ONCE(rcrb == CXL_RESOURCE_NONE))
> > +		return 0;
> > +
> > +	if (!request_mem_region(rcrb, SZ_4K, dev_name(dev)))
> > +		return 0;
> 
> ...the other benefit of centralizing this code is that we do not end up with
> multiple copies of similar, but slightly different code.
> 

Are you saying that caching values simplifies the show function?
Then I think you're right. I will change that the value should be cached
in the same way as the component register.

> > +
> > +	addr = ioremap(rcrb, SZ_4K);
> > +	if (!addr)
> > +		goto out;
> > +
> > +	offset = cxl_rcrb_get_pcie_cap_offset(addr);
> > +	if (offset)
> > +		dev_dbg(dev, "found PCIe capability (0x%x)\n", offset);
> > +	else
> > +		goto out;
> > +
> > +	linkctrl = readw(addr + offset + PCI_EXP_LNKCTL);
> > +	iounmap(addr);
> > +out:
> > +	release_mem_region(rcrb, SZ_4K);
> > +
> > +	return linkctrl;
> > +}
> > +
> > +static ssize_t rcd_link_ctrl_show(struct device *dev,
> > +				   struct device_attribute *attr, char *buf) {
> > +	struct cxl_port *port;
> > +	struct cxl_dport *dport;
> > +	struct device *parent = dev->parent;
> > +	struct pci_dev *parent_pdev = to_pci_dev(parent);
> > +	u16 linkctrl;
> > +
> > +	port = cxl_pci_find_port(parent_pdev, &dport);
> > +	if (!port)
> > +		return -EINVAL;
> > +
> > +
> > +	linkctrl = cxl_rcrb_to_linkctr(dev, dport->rcrb.base + SZ_4K);
> > +
> > +	return sysfs_emit(buf, "%x\n", linkctrl); } static
> > +DEVICE_ATTR_RO(rcd_link_ctrl);
> > +
> > +static u16 cxl_rcrb_to_linkstatus(struct device *dev, resource_size_t
> > +rcrb) {
> > +	void __iomem *addr;
> > +	u8 offset;
> > +	u16 linksta;
> > +
> > +	if (WARN_ON_ONCE(rcrb == CXL_RESOURCE_NONE))
> > +		return 0;
> > +
> > +	if (!request_mem_region(rcrb, SZ_4K, dev_name(dev)))
> > +		return 0;
> > +
> > +	addr = ioremap(rcrb, SZ_4K);
> > +	if (!addr)
> > +		goto out;
> > +
> > +	offset = cxl_rcrb_get_pcie_cap_offset(addr);
> > +	if (offset)
> > +		dev_dbg(dev, "found PCIe capability (0x%x)\n", offset);
> > +	else
> > +		goto out;
> > +
> > +	linksta = readw(addr + offset + PCI_EXP_LNKSTA);
> > +	iounmap(addr);
> > +out:
> > +	release_mem_region(rcrb, SZ_4K);
> > +
> > +	return linksta;
> > +}
> > +
> > +static ssize_t rcd_link_status_show(struct device *dev,
> > +				   struct device_attribute *attr, char *buf) {
> > +	struct cxl_port *port;
> > +	struct cxl_dport *dport;
> > +	struct device *parent = dev->parent;
> > +	struct pci_dev *parent_pdev = to_pci_dev(parent);
> > +	u16 linkstatus;
> > +
> > +	port = cxl_pci_find_port(parent_pdev, &dport);
> > +	if (!port)
> > +		return -EINVAL;
> > +
> > +	linkstatus = cxl_rcrb_to_linkstatus(dev, dport->rcrb.base + SZ_4K);
> > +
> > +	return sysfs_emit(buf, "%x\n", linkstatus); } static
> > +DEVICE_ATTR_RO(rcd_link_status);
> > +
> > +static struct attribute *cxl_rcd_attrs[] = {
> > +		&dev_attr_rcd_link_cap.attr,
> > +		&dev_attr_rcd_link_ctrl.attr,
> > +		&dev_attr_rcd_link_status.attr,
> > +		NULL
> > +};
> > +
> > +static umode_t cxl_rcd_visible(struct kobject *kobj,
> > +					  struct attribute *a, int n)
> > +{
> > +	struct device *dev = kobj_to_dev(kobj);
> > +	struct pci_dev *pdev = to_pci_dev(dev);
> > +
> > +	if (is_cxl_restricted(pdev))
> > +		return a->mode;
> > +
> > +	return 0;
> > +}
> > +
> > +static struct attribute_group cxl_rcd_group = {
> > +		.attrs = cxl_rcd_attrs,
> > +		.is_visible = cxl_rcd_visible,
> > +};
> > +
> > +__ATTRIBUTE_GROUPS(cxl_rcd);
> > +
> >  static int cxl_pci_probe(struct pci_dev *pdev, const struct
> > pci_device_id *id)  {
> >  	struct pci_host_bridge *host_bridge =
> > pci_find_host_bridge(pdev->bus); @@ -806,6 +995,9 @@ static int
> cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> >  	if (IS_ERR(mds))
> >  		return PTR_ERR(mds);
> >  	cxlds = &mds->cxlds;
> > +	device_create_file(&pdev->dev, &dev_attr_rcd_link_cap);
> > +	device_create_file(&pdev->dev, &dev_attr_rcd_link_ctrl);
> > +	device_create_file(&pdev->dev, &dev_attr_rcd_link_status);
> 
> No need to manually call device_create_file() when the attribute group is
> already registered below. I am surprised you did not get duplicate sysfs file
> warnings when registering these files twice.
> 

Thank you for pointing it out. Remove these calls.

> >  	pci_set_drvdata(pdev, cxlds);
> >
> >  	cxlds->rcd = is_cxl_restricted(pdev); @@ -967,6 +1159,7 @@ static
> > struct pci_driver cxl_pci_driver = {
> >  	.err_handler		= &cxl_error_handlers,
> >  	.driver	= {
> >  		.probe_type	= PROBE_PREFER_ASYNCHRONOUS,
> > +		.dev_groups	= cxl_rcd_groups,
> >  	},
> >  };
> >
> > --
> > 2.43.0
> >
> >
> 






[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux