Re: [PATCH v3 28/40] cxl/pci: Retrieve CXL DVSEC memory info

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

 



On Mon, Jan 31, 2022 at 10:25 AM Jonathan Cameron
<Jonathan.Cameron@xxxxxxxxxx> wrote:
>
> On Sun, 23 Jan 2022 16:31:08 -0800
> Dan Williams <dan.j.williams@xxxxxxxxx> wrote:
>
> > From: Ben Widawsky <ben.widawsky@xxxxxxxxx>
> >
> > Before CXL 2.0 HDM Decoder Capability mechanisms can be utilized in a
> > device the driver must determine that the device is ready for CXL.mem
> > operation and that platform firmware, or some other agent, has
> > established an active decode via the legacy CXL 1.1 decoder mechanism.
> >
> > This legacy mechanism is defined in the CXL DVSEC as a set of range
> > registers and status bits that take time to settle after a reset.
> >
> > Validate the CXL memory decode setup via the DVSEC and cache it for
> > later consideration by the cxl_mem driver (to be added). Failure to
> > validate is not fatal to the cxl_pci driver since that is only providing
> > CXL command support over PCI.mmio, and might be needed to rectify CXL
> > DVSEC validation problems.
> >
> > Any potential ranges that the device is already claiming via DVSEC need
> > to be reconciled with the dynamic provisioning ranges provided by
> > platform firmware (like ACPI CEDT.CFMWS). Leave that reconciliation to
> > the cxl_mem driver.
> >
> > Reported-by: kernel test robot <lkp@xxxxxxxxx>
> > Signed-off-by: Ben Widawsky <ben.widawsky@xxxxxxxxx>
> > [djbw: clarify changelog]
> > [djbw: shorten defines]
> > [djbw: change precise spin wait to generous msleep]
> > Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx>
>
> The name change from previous patch wants cleaning up and a few
> more trivial suggestions inline.
>
> Thanks,
>
> Jonathan
>
> > ---
> >  drivers/cxl/cxlmem.h |   18 +++++++-
> >  drivers/cxl/cxlpci.h |   15 ++++++
> >  drivers/cxl/pci.c    |  116 ++++++++++++++++++++++++++++++++++++++++++++++++--
> >  3 files changed, 142 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> > index cedc6d3c0448..00f55f4066b9 100644
> > --- a/drivers/cxl/cxlmem.h
> > +++ b/drivers/cxl/cxlmem.h
> > @@ -89,6 +89,18 @@ struct cxl_mbox_cmd {
> >   */
> >  #define CXL_CAPACITY_MULTIPLIER SZ_256M
> >
> > +/**
> > + * struct cxl_endpoint_dvsec_info - Cached DVSEC info
> > + * @mem_enabled: cached value of mem_enabled in the DVSEC, PCIE_DEVICE
> > + * @ranges: Number of active HDM ranges this device uses.
> > + * @dvsec_range: cached attributes of the ranges in the DVSEC, PCIE_DEVICE
> > + */
> > +struct cxl_endpoint_dvsec_info {
> > +     bool mem_enabled;
> > +     int ranges;
> > +     struct range dvsec_range[2];
> > +};
> > +
> >  /**
> >   * struct cxl_dev_state - The driver device state
> >   *
> > @@ -98,7 +110,7 @@ struct cxl_mbox_cmd {
> >   *
> >   * @dev: The device associated with this CXL state
> >   * @regs: Parsed register blocks
> > - * @device_dvsec: Offset to the PCIe device DVSEC
> > + * @cxl_dvsec: Offset to the PCIe device DVSEC
>
> So soon?  Call it this in the previous patch!

Whoops, yes, rebase mistake.


>
> >   * @payload_size: Size of space for payload
> >   *                (CXL 2.0 8.2.8.4.3 Mailbox Capabilities Register)
> >   * @lsa_size: Size of Label Storage Area
> > @@ -118,6 +130,7 @@ struct cxl_mbox_cmd {
> >   * @next_volatile_bytes: volatile capacity change pending device reset
> >   * @next_persistent_bytes: persistent capacity change pending device reset
> >   * @component_reg_phys: register base of component registers
> > + * @info: Cached DVSEC information about the device.
> >   * @mbox_send: @dev specific transport for transmitting mailbox commands
> >   *
> >   * See section 8.2.9.5.2 Capacity Configuration and Label Storage for
> > @@ -127,7 +140,7 @@ struct cxl_dev_state {
> >       struct device *dev;
> >
> >       struct cxl_regs regs;
> > -     int device_dvsec;
> > +     int cxl_dvsec;
> >
> >       size_t payload_size;
> >       size_t lsa_size;
> > @@ -149,6 +162,7 @@ struct cxl_dev_state {
> >       u64 next_persistent_bytes;
> >
> >       resource_size_t component_reg_phys;
> > +     struct cxl_endpoint_dvsec_info info;
> >
> >       int (*mbox_send)(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd);
> >  };
> > diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h
> > index 766de340c4ce..2c29d26af7f8 100644
> > --- a/drivers/cxl/cxlpci.h
> > +++ b/drivers/cxl/cxlpci.h
> > @@ -16,7 +16,20 @@
> >  #define PCI_DVSEC_VENDOR_ID_CXL              0x1E98
> >
> >  /* CXL 2.0 8.1.3: PCIe DVSEC for CXL Device */
> > -#define CXL_DVSEC_PCIE_DEVICE                                        0
> > +#define CXL_DVSEC                    0
> > +#define   CXL_DVSEC_CAP_OFFSET               0xA
> > +#define     CXL_DVSEC_MEM_CAPABLE    BIT(2)
> > +#define     CXL_DVSEC_HDM_COUNT_MASK GENMASK(5, 4)
> > +#define   CXL_DVSEC_CTRL_OFFSET              0xC
> > +#define     CXL_DVSEC_MEM_ENABLE     BIT(2)
> > +#define   CXL_DVSEC_RANGE_SIZE_HIGH(i)       (0x18 + (i * 0x10))
> > +#define   CXL_DVSEC_RANGE_SIZE_LOW(i)        (0x1C + (i * 0x10))
> > +#define     CXL_DVSEC_MEM_INFO_VALID BIT(0)
> > +#define     CXL_DVSEC_MEM_ACTIVE     BIT(1)
> > +#define     CXL_DVSEC_MEM_SIZE_LOW_MASK      GENMASK(31, 28)
> > +#define   CXL_DVSEC_RANGE_BASE_HIGH(i)       (0x20 + (i * 0x10))
> > +#define   CXL_DVSEC_RANGE_BASE_LOW(i)        (0x24 + (i * 0x10))
> > +#define     CXL_DVSEC_MEM_BASE_LOW_MASK      GENMASK(31, 28)
> >
> >  /* CXL 2.0 8.1.4: Non-CXL Function Map DVSEC */
> >  #define CXL_DVSEC_FUNCTION_MAP                                       2
> > diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> > index 76de39b90351..5c43886dc2af 100644
> > --- a/drivers/cxl/pci.c
> > +++ b/drivers/cxl/pci.c
> > @@ -386,6 +386,110 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
> >       return rc;
> >  }
> >
> > +static int wait_for_valid(struct cxl_dev_state *cxlds)
> > +{
> > +     struct pci_dev *pdev = to_pci_dev(cxlds->dev);
> > +     int d = cxlds->cxl_dvsec, rc;
> > +     u32 val;
> > +
> > +     /*
> > +      * Memory_Info_Valid: When set, indicates that the CXL Range 1 Size high
> > +      * and Size Low registers are valid. Must be set within 1 second of
> > +      * deassertion of reset to CXL device. Likely it is already set by the
> > +      * time this runs, but otherwise give a 1.5 second timeout in case of
> > +      * clock skew.
> > +      */
> > +     rc = pci_read_config_dword(pdev, d + CXL_DVSEC_RANGE_SIZE_LOW(0), &val);
> > +     if (rc)
> > +             return rc;
> > +
> > +     if (val & CXL_DVSEC_MEM_INFO_VALID)
> > +             return 0;
> > +
> > +     msleep(1500);
> > +
> > +     rc = pci_read_config_dword(pdev, d + CXL_DVSEC_RANGE_SIZE_LOW(0), &val);
> > +     if (rc)
> > +             return rc;
> > +
> > +     if (val & CXL_DVSEC_MEM_INFO_VALID)
> > +             return 0;
>
> Prefer a blank line here.

Sure.

>
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static int cxl_dvsec_ranges(struct cxl_dev_state *cxlds)
> > +{
> > +     struct cxl_endpoint_dvsec_info *info = &cxlds->info;
> > +     struct pci_dev *pdev = to_pci_dev(cxlds->dev);
> > +     int d = cxlds->cxl_dvsec;
> > +     int hdm_count, rc, i;
> > +     u16 cap, ctrl;
> > +
> > +     rc = pci_read_config_word(pdev, d + CXL_DVSEC_CAP_OFFSET, &cap);
> > +     if (rc)
> > +             return rc;
>
> trivial but I'd like a blank line here as I find that slightly easier
> to parse after to many code reviews...

Done.

>
> > +     rc = pci_read_config_word(pdev, d + CXL_DVSEC_CTRL_OFFSET, &ctrl);
> > +     if (rc)
> > +             return rc;
> > +
> > +     if (!(cap & CXL_DVSEC_MEM_CAPABLE))
> > +             return -ENXIO;
> > +
> > +     /*
> > +      * It is not allowed by spec for MEM.capable to be set and have 0 HDM
> > +      * decoders. As this driver is for a spec defined class code which must
> > +      * be CXL.mem capable, there is no point in continuing.
>
> Comment should probably also talk about why > 2 not allowed.

Changed to:

        /*
         * It is not allowed by spec for MEM.capable to be set and have 0 legacy
         * HDM decoders (values > 2 are also undefined as of CXL 2.0). As this
         * driver is for a spec defined class code which must be CXL.mem
         * capable, there is no point in continuing to enable CXL.mem.
         */

>
> > +      */
> > +     hdm_count = FIELD_GET(CXL_DVSEC_HDM_COUNT_MASK, cap);
> > +     if (!hdm_count || hdm_count > 2)
> > +             return -EINVAL;
> > +
> > +     rc = wait_for_valid(cxlds);
> > +     if (rc)
> > +             return rc;
> > +
> > +     info->mem_enabled = FIELD_GET(CXL_DVSEC_MEM_ENABLE, ctrl);
> > +
> > +     for (i = 0; i < hdm_count; i++) {
> > +             u64 base, size;
> > +             u32 temp;
> > +
> > +             rc = pci_read_config_dword(
> > +                     pdev, d + CXL_DVSEC_RANGE_SIZE_HIGH(i), &temp);
> > +             if (rc)
> > +                     break;
>
> return rc; would be cleaner for these than break.
> Saves the minor review effort of going to look for what is done in the
> exit path (nothing :)

Done.

I had considered just dropping the error checking altogether since the
PCI core is not this paranoid, but might as well keep it at this
point.

>
> > +             size = (u64)temp << 32;
> > +
> > +             rc = pci_read_config_dword(
> > +                     pdev, d + CXL_DVSEC_RANGE_SIZE_LOW(i), &temp);
> > +             if (rc)
> > +                     break;
> > +             size |= temp & CXL_DVSEC_MEM_SIZE_LOW_MASK;
> > +
> > +             rc = pci_read_config_dword(
> > +                     pdev, d + CXL_DVSEC_RANGE_BASE_HIGH(i), &temp);
> > +             if (rc)
> > +                     break;
> > +             base = (u64)temp << 32;
> > +
> > +             rc = pci_read_config_dword(
> > +                     pdev, d + CXL_DVSEC_RANGE_BASE_LOW(i), &temp);
> > +             if (rc)
> > +                     break;
> > +             base |= temp & CXL_DVSEC_MEM_BASE_LOW_MASK;
> > +
> > +             info->dvsec_range[i] = (struct range) {
> > +                     .start = base,
> > +                     .end = base + size - 1
> > +             };
> > +
> > +             if (size)
> > +                     info->ranges++;
> > +     }
> > +
> > +     return rc;
> > +}
> > +
> >  static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> >  {
> >       struct cxl_register_map map;
> > @@ -408,10 +512,9 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> >       if (IS_ERR(cxlds))
> >               return PTR_ERR(cxlds);
> >
> > -     cxlds->device_dvsec = pci_find_dvsec_capability(pdev,
> > -                                                     PCI_DVSEC_VENDOR_ID_CXL,
> > -                                                     CXL_DVSEC_PCIE_DEVICE);
> > -     if (!cxlds->device_dvsec) {
> > +     cxlds->cxl_dvsec = pci_find_dvsec_capability(
> > +             pdev, PCI_DVSEC_VENDOR_ID_CXL, CXL_DVSEC);
> > +     if (!cxlds->cxl_dvsec) {
>
> I'm guessing a rebase went astray given this only came in one patch earlier.

Yes, sorry about that.

>
> >               dev_err(&pdev->dev,
> >                       "Device DVSEC not present. Expect limited functionality.\n");
> >               return -ENXIO;
> > @@ -452,6 +555,11 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> >       if (rc)
> >               return rc;
> >
> > +     rc = cxl_dvsec_ranges(cxlds);
> > +     if (rc)
> > +             dev_err(&pdev->dev,
> > +                     "Failed to get DVSEC range information (%d)\n", rc);
> > +
> >       cxlmd = devm_cxl_add_memdev(cxlds);
> >       if (IS_ERR(cxlmd))
> >               return PTR_ERR(cxlmd);
> >
>



[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