On Fri, Feb 08, 2019 at 10:24:12AM -0600, Alexandru Gagniuc wrote: > We used to first parse all the _HPP and _HPX tables before using the > information to program registers of PCIe devices. Up until HPX type 2, > there was only one structure of each type, so we could cheat and store > it on the stack. > > With HPX type 3 we get an arbitrary number of entries, so the above > model doesn't scale that well. Instead of parsing all tables at once, > parse and program each entry separately. For _HPP and _HPX 0 thru 2, > this is functionally equivalent. The change enables the upcoming _HPX3 > to integrate more easily. I think this is tremendous! It's going to simplify this code dramatically. Two comments below. > Signed-off-by: Alexandru Gagniuc <mr.nuke.me@xxxxxxxxx> > --- > drivers/pci/pci-acpi.c | 108 +++++++++++++++++++----------------- > drivers/pci/probe.c | 16 ++---- > include/linux/pci_hotplug.h | 18 +++--- > 3 files changed, 72 insertions(+), 70 deletions(-) > > diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c > index b25e5fa9d1c9..95f4f86d2f34 100644 > --- a/drivers/pci/pci-acpi.c > +++ b/drivers/pci/pci-acpi.c > @@ -119,7 +119,7 @@ phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) > } > > static acpi_status decode_type0_hpx_record(union acpi_object *record, > - struct hotplug_params *hpx) > + struct hpp_type0 *hpx0) > { > int i; > union acpi_object *fields = record->package.elements; > @@ -132,12 +132,11 @@ static acpi_status decode_type0_hpx_record(union acpi_object *record, > for (i = 2; i < 6; i++) > if (fields[i].type != ACPI_TYPE_INTEGER) > return AE_ERROR; > - hpx->t0 = &hpx->type0_data; > - hpx->t0->revision = revision; > - hpx->t0->cache_line_size = fields[2].integer.value; > - hpx->t0->latency_timer = fields[3].integer.value; > - hpx->t0->enable_serr = fields[4].integer.value; > - hpx->t0->enable_perr = fields[5].integer.value; > + hpx0->revision = revision; > + hpx0->cache_line_size = fields[2].integer.value; > + hpx0->latency_timer = fields[3].integer.value; > + hpx0->enable_serr = fields[4].integer.value; > + hpx0->enable_perr = fields[5].integer.value; > break; > default: > printk(KERN_WARNING > @@ -149,7 +148,7 @@ static acpi_status decode_type0_hpx_record(union acpi_object *record, > } > > static acpi_status decode_type1_hpx_record(union acpi_object *record, > - struct hotplug_params *hpx) > + struct hpp_type1 *hpx1) > { > int i; > union acpi_object *fields = record->package.elements; > @@ -162,11 +161,10 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record, > for (i = 2; i < 5; i++) > if (fields[i].type != ACPI_TYPE_INTEGER) > return AE_ERROR; > - hpx->t1 = &hpx->type1_data; > - hpx->t1->revision = revision; > - hpx->t1->max_mem_read = fields[2].integer.value; > - hpx->t1->avg_max_split = fields[3].integer.value; > - hpx->t1->tot_max_split = fields[4].integer.value; > + hpx1->revision = revision; > + hpx1->max_mem_read = fields[2].integer.value; > + hpx1->avg_max_split = fields[3].integer.value; > + hpx1->tot_max_split = fields[4].integer.value; > break; > default: > printk(KERN_WARNING > @@ -178,7 +176,7 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record, > } > > static acpi_status decode_type2_hpx_record(union acpi_object *record, > - struct hotplug_params *hpx) > + struct hpp_type2 *hpx2) > { > int i; > union acpi_object *fields = record->package.elements; > @@ -191,24 +189,23 @@ static acpi_status decode_type2_hpx_record(union acpi_object *record, > for (i = 2; i < 18; i++) > if (fields[i].type != ACPI_TYPE_INTEGER) > return AE_ERROR; > - hpx->t2 = &hpx->type2_data; > - hpx->t2->revision = revision; > - hpx->t2->unc_err_mask_and = fields[2].integer.value; > - hpx->t2->unc_err_mask_or = fields[3].integer.value; > - hpx->t2->unc_err_sever_and = fields[4].integer.value; > - hpx->t2->unc_err_sever_or = fields[5].integer.value; > - hpx->t2->cor_err_mask_and = fields[6].integer.value; > - hpx->t2->cor_err_mask_or = fields[7].integer.value; > - hpx->t2->adv_err_cap_and = fields[8].integer.value; > - hpx->t2->adv_err_cap_or = fields[9].integer.value; > - hpx->t2->pci_exp_devctl_and = fields[10].integer.value; > - hpx->t2->pci_exp_devctl_or = fields[11].integer.value; > - hpx->t2->pci_exp_lnkctl_and = fields[12].integer.value; > - hpx->t2->pci_exp_lnkctl_or = fields[13].integer.value; > - hpx->t2->sec_unc_err_sever_and = fields[14].integer.value; > - hpx->t2->sec_unc_err_sever_or = fields[15].integer.value; > - hpx->t2->sec_unc_err_mask_and = fields[16].integer.value; > - hpx->t2->sec_unc_err_mask_or = fields[17].integer.value; > + hpx2->revision = revision; > + hpx2->unc_err_mask_and = fields[2].integer.value; > + hpx2->unc_err_mask_or = fields[3].integer.value; > + hpx2->unc_err_sever_and = fields[4].integer.value; > + hpx2->unc_err_sever_or = fields[5].integer.value; > + hpx2->cor_err_mask_and = fields[6].integer.value; > + hpx2->cor_err_mask_or = fields[7].integer.value; > + hpx2->adv_err_cap_and = fields[8].integer.value; > + hpx2->adv_err_cap_or = fields[9].integer.value; > + hpx2->pci_exp_devctl_and = fields[10].integer.value; > + hpx2->pci_exp_devctl_or = fields[11].integer.value; > + hpx2->pci_exp_lnkctl_and = fields[12].integer.value; > + hpx2->pci_exp_lnkctl_or = fields[13].integer.value; > + hpx2->sec_unc_err_sever_and = fields[14].integer.value; > + hpx2->sec_unc_err_sever_or = fields[15].integer.value; > + hpx2->sec_unc_err_mask_and = fields[16].integer.value; > + hpx2->sec_unc_err_mask_or = fields[17].integer.value; > break; > default: > printk(KERN_WARNING > @@ -219,17 +216,18 @@ static acpi_status decode_type2_hpx_record(union acpi_object *record, > return AE_OK; > } > > -static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) > +static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle, > + const struct hotplug_program_ops *hp_ops) > { > acpi_status status; > struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; > union acpi_object *package, *record, *fields; > + struct hpp_type0 hpx0; > + struct hpp_type1 hpx1; > + struct hpp_type2 hpx2; > u32 type; > int i; > > - /* Clear the return buffer with zeros */ > - memset(hpx, 0, sizeof(struct hotplug_params)); > - > status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer); > if (ACPI_FAILURE(status)) > return status; > @@ -257,19 +255,25 @@ static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) > type = fields[0].integer.value; > switch (type) { > case 0: > - status = decode_type0_hpx_record(record, hpx); > + memset(&hpx0, 0, sizeof(hpx0)); > + status = decode_type0_hpx_record(record, &hpx0); > if (ACPI_FAILURE(status)) > goto exit; > + hp_ops->program_type0(dev, &hpx0); > break; > case 1: > - status = decode_type1_hpx_record(record, hpx); > + memset(&hpx1, 0, sizeof(hpx1)); > + status = decode_type1_hpx_record(record, &hpx1); > if (ACPI_FAILURE(status)) > goto exit; > + hp_ops->program_type1(dev, &hpx1); > break; > case 2: > - status = decode_type2_hpx_record(record, hpx); > + memset(&hpx2, 0, sizeof(hpx2)); > + status = decode_type2_hpx_record(record, &hpx2); > if (ACPI_FAILURE(status)) > goto exit; > + hp_ops->program_type2(dev, &hpx2); > break; > default: > printk(KERN_ERR "%s: Type %d record not supported\n", > @@ -283,14 +287,16 @@ static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) > return status; > } > > -static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp) > +static acpi_status acpi_run_hpp(struct pci_dev *dev, acpi_handle handle, > + const struct hotplug_program_ops *hp_ops) > { > acpi_status status; > struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; > union acpi_object *package, *fields; > + struct hpp_type0 hpp0; > int i; > > - memset(hpp, 0, sizeof(struct hotplug_params)); > + memset(&hpp0, 0, sizeof(hpp0)); > > status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer); > if (ACPI_FAILURE(status)) > @@ -311,12 +317,13 @@ static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp) > } > } > > - hpp->t0 = &hpp->type0_data; > - hpp->t0->revision = 1; > - hpp->t0->cache_line_size = fields[0].integer.value; > - hpp->t0->latency_timer = fields[1].integer.value; > - hpp->t0->enable_serr = fields[2].integer.value; > - hpp->t0->enable_perr = fields[3].integer.value; > + hpp0.revision = 1; > + hpp0.cache_line_size = fields[0].integer.value; > + hpp0.latency_timer = fields[1].integer.value; > + hpp0.enable_serr = fields[2].integer.value; > + hpp0.enable_perr = fields[3].integer.value; > + > + hp_ops->program_type0(dev, &hpp0); > > exit: > kfree(buffer.pointer); > @@ -328,7 +335,8 @@ static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp) > * @dev - the pci_dev for which we want parameters > * @hpp - allocated by the caller > */ > -int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp) > +int pci_acpi_program_hp_params(struct pci_dev *dev, > + const struct hotplug_program_ops *hp_ops) > { > acpi_status status; > acpi_handle handle, phandle; > @@ -351,10 +359,10 @@ int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp) > * this pci dev. > */ > while (handle) { > - status = acpi_run_hpx(handle, hpp); > + status = acpi_run_hpx(dev, handle, hp_ops); > if (ACPI_SUCCESS(status)) > return 0; > - status = acpi_run_hpp(handle, hpp); > + status = acpi_run_hpp(dev, handle, hp_ops); > if (ACPI_SUCCESS(status)) > return 0; > if (acpi_is_root_bridge(handle)) > diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c > index 257b9f6f2ebb..527c209f0c94 100644 > --- a/drivers/pci/probe.c > +++ b/drivers/pci/probe.c > @@ -2131,8 +2131,11 @@ static void pci_configure_eetlp_prefix(struct pci_dev *dev) > > static void pci_configure_device(struct pci_dev *dev) > { > - struct hotplug_params hpp; > - int ret; > + static const struct hotplug_program_ops hp_ops = { > + .program_type0 = program_hpp_type0, > + .program_type1 = program_hpp_type1, > + .program_type2 = program_hpp_type2, > + }; What if we just moved program_hpp_type0(), etc from probe.c to pci-acpi.c? The only reason I see to have it in probe.c is for pci_default_type0, and I think that is a pretty obtuse way of doing default configuration. I would have no problem at all just hardcoding those defaults in probe.c and then potentially having them overwritten by _HPP/_HPX. If we moved program_hpp_type0(), etc to pci-acpi.c, we could get rid of the function pointers and all the ACPI-related code would be in one place. > pci_configure_mps(dev); > pci_configure_extended_tags(dev, NULL); > @@ -2140,14 +2143,7 @@ static void pci_configure_device(struct pci_dev *dev) > pci_configure_ltr(dev); > pci_configure_eetlp_prefix(dev); > > - memset(&hpp, 0, sizeof(hpp)); > - ret = pci_get_hp_params(dev, &hpp); > - if (ret) > - return; > - > - program_hpp_type2(dev, hpp.t2); > - program_hpp_type1(dev, hpp.t1); > - program_hpp_type0(dev, hpp.t0); > + pci_acpi_program_hp_params(dev, &hp_ops); > } > > static void pci_release_capabilities(struct pci_dev *dev) > diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h > index 7acc9f91e72b..c85378edf235 100644 > --- a/include/linux/pci_hotplug.h > +++ b/include/linux/pci_hotplug.h > @@ -124,26 +124,24 @@ struct hpp_type2 { > u32 sec_unc_err_mask_or; > }; > > -struct hotplug_params { > - struct hpp_type0 *t0; /* Type0: NULL if not available */ > - struct hpp_type1 *t1; /* Type1: NULL if not available */ > - struct hpp_type2 *t2; /* Type2: NULL if not available */ > - struct hpp_type0 type0_data; > - struct hpp_type1 type1_data; > - struct hpp_type2 type2_data; > +struct hotplug_program_ops { > + void (*program_type0)(struct pci_dev *dev, struct hpp_type0 *hpp); > + void (*program_type1)(struct pci_dev *dev, struct hpp_type1 *hpp); > + void (*program_type2)(struct pci_dev *dev, struct hpp_type2 *hpp); > }; I think a lot of this stuff is only used in drivers/pci, so it could be moved from include/linux/pci_hotplug.h to drivers/pci/pci.h. > #ifdef CONFIG_ACPI > #include <linux/acpi.h> > -int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp); > +int pci_acpi_program_hp_params(struct pci_dev *dev, > + const struct hotplug_program_ops *hp_ops); > bool pciehp_is_native(struct pci_dev *bridge); > int acpi_get_hp_hw_control_from_firmware(struct pci_dev *bridge); > bool shpchp_is_native(struct pci_dev *bridge); > int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle); > int acpi_pci_detect_ejectable(acpi_handle handle); > #else > -static inline int pci_get_hp_params(struct pci_dev *dev, > - struct hotplug_params *hpp) > +int pci_acpi_program_hp_params(struct pci_dev *dev, > + const struct hotplug_program_ops *hp_ops); > { > return -ENODEV; > } > -- > 2.19.2 >