Message ID | 20240531213841.3246055-7-wei.huang2@amd.com (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Bjorn Helgaas |
Headers | show |
Series | PCIe TPH and cache direct injection support | expand |
On Fri, May 31, 2024 at 04:38:38PM -0500, Wei Huang wrote: > According to PCI SIG ECN, calling the _DSM firmware method for a given > CPU_UID returns the steering tags for different types of memory > (volatile, non-volatile). These tags are supposed to be used in ST > table entry for optimal results. > > Co-developed-by: Eric Van Tassell <Eric.VanTassell@amd.com> > Signed-off-by: Eric Van Tassell <Eric.VanTassell@amd.com> > Signed-off-by: Wei Huang <wei.huang2@amd.com> > Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com> > Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com> > Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com> ... > diff --git a/drivers/pci/pcie/tph.c b/drivers/pci/pcie/tph.c ... > +static bool invoke_dsm(acpi_handle handle, u32 cpu_uid, u8 ph, > + u8 target_type, bool cache_ref_valid, > + u64 cache_ref, union st_info *st_out) > +{ > + union acpi_object in_obj, in_buf[3], *out_obj; > + > + in_buf[0].integer.type = ACPI_TYPE_INTEGER; > + in_buf[0].integer.value = 0; /* 0 => processor cache steering tags */ > + > + in_buf[1].integer.type = ACPI_TYPE_INTEGER; > + in_buf[1].integer.value = cpu_uid; > + > + in_buf[2].integer.type = ACPI_TYPE_INTEGER; > + in_buf[2].integer.value = ph & 3; > + in_buf[2].integer.value |= (target_type & 1) << 2; > + in_buf[2].integer.value |= (cache_ref_valid & 1) << 3; > + in_buf[2].integer.value |= (cache_ref << 32); > + > + in_obj.type = ACPI_TYPE_PACKAGE; > + in_obj.package.count = ARRAY_SIZE(in_buf); > + in_obj.package.elements = in_buf; > + > + out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV, > + ST_DSM_FUNC_INDEX, &in_obj); Hi Wei Huang, Eric, all, This seems to break builds on ARM (32bit) with multi_v7_defconfig. .../tph.c:221:39: error: use of undeclared identifier 'pci_acpi_dsm_guid' 221 | out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV, | I suspect a dependency on ACPI in Kconfig is appropriate. > + > + if (!out_obj) > + return false; > + > + if (out_obj->type != ACPI_TYPE_BUFFER) { > + pr_err("invalid return type %d from TPH _DSM\n", > + out_obj->type); > + ACPI_FREE(out_obj); > + return false; > + } ...
On 6/4/24 10:30, Simon Horman wrote: > On Fri, May 31, 2024 at 04:38:38PM -0500, Wei Huang wrote: >> According to PCI SIG ECN, calling the _DSM firmware method for a given >> CPU_UID returns the steering tags for different types of memory >> (volatile, non-volatile). These tags are supposed to be used in ST >> table entry for optimal results. >> >> Co-developed-by: Eric Van Tassell <Eric.VanTassell@amd.com> >> Signed-off-by: Eric Van Tassell <Eric.VanTassell@amd.com> >> Signed-off-by: Wei Huang <wei.huang2@amd.com> >> Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com> >> Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com> >> Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com> > > ... > >> diff --git a/drivers/pci/pcie/tph.c b/drivers/pci/pcie/tph.c > > ... > >> +static bool invoke_dsm(acpi_handle handle, u32 cpu_uid, u8 ph, >> + u8 target_type, bool cache_ref_valid, >> + u64 cache_ref, union st_info *st_out) >> +{ >> + union acpi_object in_obj, in_buf[3], *out_obj; >> + >> + in_buf[0].integer.type = ACPI_TYPE_INTEGER; >> + in_buf[0].integer.value = 0; /* 0 => processor cache steering tags */ >> + >> + in_buf[1].integer.type = ACPI_TYPE_INTEGER; >> + in_buf[1].integer.value = cpu_uid; >> + >> + in_buf[2].integer.type = ACPI_TYPE_INTEGER; >> + in_buf[2].integer.value = ph & 3; >> + in_buf[2].integer.value |= (target_type & 1) << 2; >> + in_buf[2].integer.value |= (cache_ref_valid & 1) << 3; >> + in_buf[2].integer.value |= (cache_ref << 32); >> + >> + in_obj.type = ACPI_TYPE_PACKAGE; >> + in_obj.package.count = ARRAY_SIZE(in_buf); >> + in_obj.package.elements = in_buf; >> + >> + out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV, >> + ST_DSM_FUNC_INDEX, &in_obj); > > Hi Wei Huang, Eric, all, > > This seems to break builds on ARM (32bit) with multi_v7_defconfig. > > .../tph.c:221:39: error: use of undeclared identifier 'pci_acpi_dsm_guid' > 221 | out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV, > | Thanks for pointing it out. I will add "depends on ACPI" in Kconfig which solves the problem: $ make ARCH=arm CROSS_COMPILE=arm-linux-gnu- multi_v7_defconfig zImage modules CALL scripts/checksyscalls.sh Kernel: arch/arm/boot/Image is ready Kernel: arch/arm/boot/zImage is ready > > I suspect a dependency on ACPI in Kconfig is appropriate. > >> + >> + if (!out_obj) >> + return false; >> + >> + if (out_obj->type != ACPI_TYPE_BUFFER) { >> + pr_err("invalid return type %d from TPH _DSM\n", >> + out_obj->type); >> + ACPI_FREE(out_obj); >> + return false; >> + } > > ...
On Fri, 31 May 2024 16:38:38 -0500 Wei Huang <wei.huang2@amd.com> wrote: > According to PCI SIG ECN, calling the _DSM firmware method for a given > CPU_UID returns the steering tags for different types of memory > (volatile, non-volatile). These tags are supposed to be used in ST > table entry for optimal results. > > Co-developed-by: Eric Van Tassell <Eric.VanTassell@amd.com> > Signed-off-by: Eric Van Tassell <Eric.VanTassell@amd.com> > Signed-off-by: Wei Huang <wei.huang2@amd.com> > Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com> > Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com> > Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com> Took a very quick look at this only due to lack of time.. > --- > drivers/pci/pcie/tph.c | 103 +++++++++++++++++++++++++++++++++++++++- > include/linux/pci-tph.h | 34 +++++++++++++ > 2 files changed, 136 insertions(+), 1 deletion(-) > > diff --git a/drivers/pci/pcie/tph.c b/drivers/pci/pcie/tph.c > index 320b99c60365..425935a14b62 100644 > --- a/drivers/pci/pcie/tph.c > +++ b/drivers/pci/pcie/tph.c > @@ -158,6 +158,98 @@ static int tph_get_table_location(struct pci_dev *dev, u8 *loc_out) > return 0; > } > > +static u16 tph_extract_tag(enum tph_mem_type mem_type, u8 req_type, > + union st_info *st_tag) > +{ > + switch (req_type) { > + case PCI_TPH_REQ_TPH_ONLY: /* 8 bit tags */ > + switch (mem_type) { > + case TPH_MEM_TYPE_VM: > + if (st_tag->vm_st_valid) > + return st_tag->vm_st; > + break; > + case TPH_MEM_TYPE_PM: > + if (st_tag->pm_st_valid) > + return st_tag->pm_st; > + break; > + } > + break; > + case PCI_TPH_REQ_EXT_TPH: /* 16 bit tags */ > + switch (mem_type) { > + case TPH_MEM_TYPE_VM: > + if (st_tag->vm_xst_valid) > + return st_tag->vm_xst; > + break; > + case TPH_MEM_TYPE_PM: > + if (st_tag->pm_xst_valid) > + return st_tag->pm_xst; > + break; > + } > + break; > + default: > + pr_err("invalid steering tag in ACPI _DSM\n"); > + return 0; Not an error code? If so need to explain why 0 is the right thing to return. > + } > + > + return 0; > +} > + > +#define MIN_ST_DSM_REV 7 > +#define ST_DSM_FUNC_INDEX 0xf > +static bool invoke_dsm(acpi_handle handle, u32 cpu_uid, u8 ph, give that a pci / tph prefix of some type as it's a very generic name so potential future name clashes likely. > + u8 target_type, bool cache_ref_valid, > + u64 cache_ref, union st_info *st_out) > +{ > + union acpi_object in_obj, in_buf[3], *out_obj; I'm out of time for the day, so not checked this. Will look more closely in v3. > + > + in_buf[0].integer.type = ACPI_TYPE_INTEGER; > + in_buf[0].integer.value = 0; /* 0 => processor cache steering tags */ > + > + in_buf[1].integer.type = ACPI_TYPE_INTEGER; > + in_buf[1].integer.value = cpu_uid; > + > + in_buf[2].integer.type = ACPI_TYPE_INTEGER; > + in_buf[2].integer.value = ph & 3; > + in_buf[2].integer.value |= (target_type & 1) << 2; > + in_buf[2].integer.value |= (cache_ref_valid & 1) << 3; > + in_buf[2].integer.value |= (cache_ref << 32); > + > + in_obj.type = ACPI_TYPE_PACKAGE; > + in_obj.package.count = ARRAY_SIZE(in_buf); > + in_obj.package.elements = in_buf; > + > + out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV, > + ST_DSM_FUNC_INDEX, &in_obj); > + > + if (!out_obj) > + return false; > + > + if (out_obj->type != ACPI_TYPE_BUFFER) { > + pr_err("invalid return type %d from TPH _DSM\n", > + out_obj->type); > + ACPI_FREE(out_obj); > + return false; > + } > + > + st_out->value = *((u64 *)(out_obj->buffer.pointer)); > + > + ACPI_FREE(out_obj); > + > + return true; > +} > + > static bool msix_nr_in_bounds(struct pci_dev *dev, int msix_nr) > { > u16 tbl_sz; > @@ -441,7 +533,16 @@ bool pcie_tph_get_st(struct pci_dev *dev, unsigned int cpu, > enum tph_mem_type mem_type, u8 req_type, > u16 *tag) Add this function in this patch as it's not used before here and all the logic is about the _DSM Note name needs to change though. > { > - *tag = 0; > + union st_info info; > + > + if (!invoke_dsm(root_complex_acpi_handle(dev), cpu, 0, 0, false, 0, > + &info)) { > + *tag = 0; > + return false; > + } > + > + *tag = tph_extract_tag(mem_type, req_type, &info); > + pr_debug("%s: cpu=%d tag=%d\n", __func__, cpu, *tag); > > return true; > } > diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h > index 4fbd1e2fd98c..79533c6254c2 100644 > --- a/include/linux/pci-tph.h > +++ b/include/linux/pci-tph.h > @@ -14,6 +14,40 @@ enum tph_mem_type { > TPH_MEM_TYPE_PM /* persistent memory type */ > }; > > +/* > + * The st_info struct defines the steering tag returned by the firmware _DSM > + * method defined in PCI SIG ECN. The specification is available at: > + * https://members.pcisig.com/wg/PCI-SIG/document/15470. > + > + * @vm_st_valid: 8 bit tag for volatile memory is valid > + * @vm_xst_valid: 16 bit tag for volatile memory is valid > + * @vm_ignore: 1 => was and will be ignored, 0 => ph should be supplied > + * @vm_st: 8 bit steering tag for volatile mem > + * @vm_xst: 16 bit steering tag for volatile mem > + * @pm_st_valid: 8 bit tag for persistent memory is valid > + * @pm_xst_valid: 16 bit tag for persistent memory is valid > + * @pm_ignore: 1 => was and will be ignore, 0 => ph should be supplied pm_ph_ignore > + * @pm_st: 8 bit steering tag for persistent mem > + * @pm_xst: 16 bit steering tag for persistent mem > + */ > +union st_info { > + struct { > + u64 vm_st_valid:1, > + vm_xst_valid:1, > + vm_ph_ignore:1, > + rsvd1:5, > + vm_st:8, > + vm_xst:16, > + pm_st_valid:1, > + pm_xst_valid:1, > + pm_ph_ignore:1, > + rsvd2:5, > + pm_st:8, > + pm_xst:16; > + }; > + u64 value; > +}; Firstly why in a header? If it did want to be then pci-acpi.h might be reasonable. > + > #ifdef CONFIG_PCIE_TPH > int pcie_tph_disable(struct pci_dev *dev); > int tph_set_dev_nostmode(struct pci_dev *dev);
On Fri, May 31, 2024 at 04:38:38PM -0500, Wei Huang wrote: > According to PCI SIG ECN, calling the _DSM firmware method for a given > CPU_UID returns the steering tags for different types of memory > (volatile, non-volatile). These tags are supposed to be used in ST > table entry for optimal results. Cite PCI Firmware spec if possible. If it hasn't been incorporated yet, at least include the exact name of the ECN and the date it was approved. Say what the patch does in the commit log (in addition to the subject line). > +#define MIN_ST_DSM_REV 7 No useful value in this #define. If the value ever changes, code changes will be required too. > +#define ST_DSM_FUNC_INDEX 0xf Move to the list in pci-acpi.h with name similar to others. > +static bool invoke_dsm(acpi_handle handle, u32 cpu_uid, u8 ph, > + u8 target_type, bool cache_ref_valid, > + u64 cache_ref, union st_info *st_out) > +{ Return 0 or -errno. "invoke_dsm" is not a predicate with an obvious true/false meaning. > + union acpi_object in_obj, in_buf[3], *out_obj; > + > + in_buf[0].integer.type = ACPI_TYPE_INTEGER; > + in_buf[0].integer.value = 0; /* 0 => processor cache steering tags */ > + > + in_buf[1].integer.type = ACPI_TYPE_INTEGER; > + in_buf[1].integer.value = cpu_uid; > + > + in_buf[2].integer.type = ACPI_TYPE_INTEGER; > + in_buf[2].integer.value = ph & 3; > + in_buf[2].integer.value |= (target_type & 1) << 2; > + in_buf[2].integer.value |= (cache_ref_valid & 1) << 3; > + in_buf[2].integer.value |= (cache_ref << 32); > + > + in_obj.type = ACPI_TYPE_PACKAGE; > + in_obj.package.count = ARRAY_SIZE(in_buf); > + in_obj.package.elements = in_buf; Must check whether this _DSM function is implemented first, e.g., see acpi_enable_dpc(). > + out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV, > + ST_DSM_FUNC_INDEX, &in_obj); > + > + if (!out_obj) > + return false; > + > + if (out_obj->type != ACPI_TYPE_BUFFER) { > + pr_err("invalid return type %d from TPH _DSM\n", > + out_obj->type); > + ACPI_FREE(out_obj); > + return false; > + } > + > + st_out->value = *((u64 *)(out_obj->buffer.pointer)); > + > + ACPI_FREE(out_obj); > + > + return true; > +}
diff --git a/drivers/pci/pcie/tph.c b/drivers/pci/pcie/tph.c index 320b99c60365..425935a14b62 100644 --- a/drivers/pci/pcie/tph.c +++ b/drivers/pci/pcie/tph.c @@ -158,6 +158,98 @@ static int tph_get_table_location(struct pci_dev *dev, u8 *loc_out) return 0; } +static u16 tph_extract_tag(enum tph_mem_type mem_type, u8 req_type, + union st_info *st_tag) +{ + switch (req_type) { + case PCI_TPH_REQ_TPH_ONLY: /* 8 bit tags */ + switch (mem_type) { + case TPH_MEM_TYPE_VM: + if (st_tag->vm_st_valid) + return st_tag->vm_st; + break; + case TPH_MEM_TYPE_PM: + if (st_tag->pm_st_valid) + return st_tag->pm_st; + break; + } + break; + case PCI_TPH_REQ_EXT_TPH: /* 16 bit tags */ + switch (mem_type) { + case TPH_MEM_TYPE_VM: + if (st_tag->vm_xst_valid) + return st_tag->vm_xst; + break; + case TPH_MEM_TYPE_PM: + if (st_tag->pm_xst_valid) + return st_tag->pm_xst; + break; + } + break; + default: + pr_err("invalid steering tag in ACPI _DSM\n"); + return 0; + } + + return 0; +} + +#define MIN_ST_DSM_REV 7 +#define ST_DSM_FUNC_INDEX 0xf +static bool invoke_dsm(acpi_handle handle, u32 cpu_uid, u8 ph, + u8 target_type, bool cache_ref_valid, + u64 cache_ref, union st_info *st_out) +{ + union acpi_object in_obj, in_buf[3], *out_obj; + + in_buf[0].integer.type = ACPI_TYPE_INTEGER; + in_buf[0].integer.value = 0; /* 0 => processor cache steering tags */ + + in_buf[1].integer.type = ACPI_TYPE_INTEGER; + in_buf[1].integer.value = cpu_uid; + + in_buf[2].integer.type = ACPI_TYPE_INTEGER; + in_buf[2].integer.value = ph & 3; + in_buf[2].integer.value |= (target_type & 1) << 2; + in_buf[2].integer.value |= (cache_ref_valid & 1) << 3; + in_buf[2].integer.value |= (cache_ref << 32); + + in_obj.type = ACPI_TYPE_PACKAGE; + in_obj.package.count = ARRAY_SIZE(in_buf); + in_obj.package.elements = in_buf; + + out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, MIN_ST_DSM_REV, + ST_DSM_FUNC_INDEX, &in_obj); + + if (!out_obj) + return false; + + if (out_obj->type != ACPI_TYPE_BUFFER) { + pr_err("invalid return type %d from TPH _DSM\n", + out_obj->type); + ACPI_FREE(out_obj); + return false; + } + + st_out->value = *((u64 *)(out_obj->buffer.pointer)); + + ACPI_FREE(out_obj); + + return true; +} + +static acpi_handle root_complex_acpi_handle(struct pci_dev *dev) +{ + struct pci_dev *root_port; + + root_port = pcie_find_root_port(dev); + + if (!root_port || !root_port->bus || !root_port->bus->bridge) + return NULL; + + return ACPI_HANDLE(root_port->bus->bridge); +} + static bool msix_nr_in_bounds(struct pci_dev *dev, int msix_nr) { u16 tbl_sz; @@ -441,7 +533,16 @@ bool pcie_tph_get_st(struct pci_dev *dev, unsigned int cpu, enum tph_mem_type mem_type, u8 req_type, u16 *tag) { - *tag = 0; + union st_info info; + + if (!invoke_dsm(root_complex_acpi_handle(dev), cpu, 0, 0, false, 0, + &info)) { + *tag = 0; + return false; + } + + *tag = tph_extract_tag(mem_type, req_type, &info); + pr_debug("%s: cpu=%d tag=%d\n", __func__, cpu, *tag); return true; } diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h index 4fbd1e2fd98c..79533c6254c2 100644 --- a/include/linux/pci-tph.h +++ b/include/linux/pci-tph.h @@ -14,6 +14,40 @@ enum tph_mem_type { TPH_MEM_TYPE_PM /* persistent memory type */ }; +/* + * The st_info struct defines the steering tag returned by the firmware _DSM + * method defined in PCI SIG ECN. The specification is available at: + * https://members.pcisig.com/wg/PCI-SIG/document/15470. + + * @vm_st_valid: 8 bit tag for volatile memory is valid + * @vm_xst_valid: 16 bit tag for volatile memory is valid + * @vm_ignore: 1 => was and will be ignored, 0 => ph should be supplied + * @vm_st: 8 bit steering tag for volatile mem + * @vm_xst: 16 bit steering tag for volatile mem + * @pm_st_valid: 8 bit tag for persistent memory is valid + * @pm_xst_valid: 16 bit tag for persistent memory is valid + * @pm_ignore: 1 => was and will be ignore, 0 => ph should be supplied + * @pm_st: 8 bit steering tag for persistent mem + * @pm_xst: 16 bit steering tag for persistent mem + */ +union st_info { + struct { + u64 vm_st_valid:1, + vm_xst_valid:1, + vm_ph_ignore:1, + rsvd1:5, + vm_st:8, + vm_xst:16, + pm_st_valid:1, + pm_xst_valid:1, + pm_ph_ignore:1, + rsvd2:5, + pm_st:8, + pm_xst:16; + }; + u64 value; +}; + #ifdef CONFIG_PCIE_TPH int pcie_tph_disable(struct pci_dev *dev); int tph_set_dev_nostmode(struct pci_dev *dev);