On Thu, Jan 10, 2019 at 05:11:27PM -0600, Alexandru Gagniuc wrote: > _HPX Type 3 is intended to be more generic and allow configuration of > settings not possible with Type 2 tables. For example, FW could ensure > that the completion timeout value is set accordingly throughout the PCI > tree; some switches require very special handling. > > Type 3 can come in an arbitrary number of ACPI packages. With the older > types we only ever had one descriptor. We could get lazy and store it > on the stack. The current flow is to parse the HPX table > --pci_get_hp_params()-- and then program the PCIe device registers. > > In the current flow, we'll need to malloc(). Here's what I tried: > 1) devm_kmalloc() on the pci_dev > This ended up giving me NULL dereference at boot time. Yeah, I don't think devm_*() is going to work because that's part of the driver binding model; this is in the PCI core and this use is not related to the lifetime of a driver's binding to a device. > 2) Add a cleanup function to be called after writing the PCIe registers > > This RFC implements method 2. The HPX3 stuff is still NDA'd, but the > housekeeping parts are in here. Is this a good way to do things? I'm not > too sure about the need to call cleanup() on a stack variable. But if > not that, then what else? pci_get_hp_params() is currently exported, but only used inside drivers/pci, so I think it could be unexported. I don't remember if there's a reason why things are split between pci_get_hp_params(), which runs _HPX or _HPP, and the program_hpp_type*() functions. If we could get rid of that split, we could get rid of struct hotplug_params and do the configuration directly in the pci_get_hp_params() path. I think that would simplify the memory management. Bjorn > --- > drivers/pci/pci-acpi.c | 88 +++++++++++++++++++++++++++++++++++++ > drivers/pci/probe.c | 29 ++++++++++++ > include/linux/pci_hotplug.h | 17 +++++++ > 3 files changed, 134 insertions(+) > > diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c > index e1949f7efd9c..2ce1b68ce688 100644 > --- a/drivers/pci/pci-acpi.c > +++ b/drivers/pci/pci-acpi.c > @@ -219,6 +219,65 @@ static acpi_status decode_type2_hpx_record(union acpi_object *record, > return AE_OK; > } > > +static acpi_status parse_hpx3_register(struct hpp_type3 *hpx3, > + union acpi_object *reg_fields) > +{ > + struct hpp_type3_register *hpx3_reg; > + > + hpx3_reg = kzalloc(sizeof(*hpx3_reg), GFP_KERNEL); > + if (!hpx3_reg) > + return AE_NO_MEMORY; > + > + /* Parse fields blurb */ > + > + list_add_tail(&hpx3_reg->list, &hpx3->regs); > + return AE_OK; > +} > + > +static acpi_status decode_type3_hpx_record(union acpi_object *record, > + struct hotplug_params *hpx) > +{ > + union acpi_object *fields = record->package.elements; > + u32 desc_count, expected_length, revision; > + union acpi_object *reg_fields; > + acpi_status ret; > + int i; > + > + revision = fields[1].integer.value; > + switch (revision) { > + case 1: > + desc_count = fields[2].integer.value; > + expected_length = 3 + desc_count * 14; > + > + if (record->package.count != expected_length) > + return AE_ERROR; > + > + for (i = 2; i < expected_length; i++) > + if (fields[i].type != ACPI_TYPE_INTEGER) > + return AE_ERROR; > + > + if (!hpx->t3) { > + INIT_LIST_HEAD(&hpx->type3_data.regs); > + hpx->t3 = &hpx->type3_data; > + } > + > + for (i = 0; i < desc_count; i++) { > + reg_fields = fields + 3 + i * 14; > + ret = parse_hpx3_register(hpx->t3, reg_fields); > + if (ret != AE_OK) > + return ret; > + } > + > + break; > + default: > + printk(KERN_WARNING > + "%s: Type 3 Revision %d record not supported\n", > + __func__, revision); > + return AE_ERROR; > + } > + return AE_OK; > +} > + > static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) > { > acpi_status status; > @@ -271,6 +330,11 @@ static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) > if (ACPI_FAILURE(status)) > goto exit; > break; > + case 3: > + status = decode_type3_hpx_record(record, hpx); > + if (ACPI_FAILURE(status)) > + goto exit; > + break; > default: > printk(KERN_ERR "%s: Type %d record not supported\n", > __func__, type); > @@ -368,6 +432,30 @@ int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp) > } > EXPORT_SYMBOL_GPL(pci_get_hp_params); > > +static int cleantup_type3_hpx_record(struct hpp_type3 *hpx3) > +{ > + struct hpp_type3_register *reg; > + struct list_head *entry, *temp; > + > + list_for_each_safe(entry, temp, &hpp->t3->regs){ > + reg = list_entry(entry, typeof(*reg), list); > + list_del(entry); > + kfree(reg); > + } > +} > + > +/* pci_cleanup_hp_params > + * > + * @hpp - allocated by the caller > + */ > +void pci_cleanup_hp_params(struct hotplug_params *hpp) > +{ > + > + if (hpp->t3) > + cleanup_type3_hpx_record(hpp->t3); > +} > +EXPORT_SYMBOL_GPL(pci_cleanup_hp_params); > + > /** > * pciehp_is_native - Check whether a hotplug port is handled by the OS > * @bridge: Hotplug port to check > diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c > index 257b9f6f2ebb..35ef7d1f4f3b 100644 > --- a/drivers/pci/probe.c > +++ b/drivers/pci/probe.c > @@ -1979,6 +1979,32 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) > */ > } > > +static void program_hpp_type3_register(struct pci_dev *dev, > + const struct hpp_type3_register *reg) > +{ > + /* Complicated ACPI-mandated blurb */ > +} > + > +static void program_hpp_type3(struct pci_dev *dev, struct hpp_type3 *hpp) > +{ > + struct hpp_type3_register *reg; > + > + if (!hpp) > + return; > + > + if (!pci_is_pcie(dev)) > + return; > + > + if (hpp->revision > 1) { > + pci_warn(dev, "PCIe settings rev %d not supported\n", > + hpp->revision); > + return; > + } > + > + list_for_each_entry(reg, &hpp->regs, list) > + program_hpp_type3_register(dev, reg); > +} > + > int pci_configure_extended_tags(struct pci_dev *dev, void *ign) > { > struct pci_host_bridge *host; > @@ -2145,9 +2171,12 @@ static void pci_configure_device(struct pci_dev *dev) > if (ret) > return; > > + program_hpp_type3(dev, hpp.t3); > program_hpp_type2(dev, hpp.t2); > program_hpp_type1(dev, hpp.t1); > program_hpp_type0(dev, hpp.t0); > + > + pci_cleanup_hp_params(&hpp); > } > > 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..479da87a3774 100644 > --- a/include/linux/pci_hotplug.h > +++ b/include/linux/pci_hotplug.h > @@ -124,18 +124,35 @@ struct hpp_type2 { > u32 sec_unc_err_mask_or; > }; > > +/* > + * PCI Express Setting Record (Type 3) > + * The ACPI overlords can never get enough > + */ > +struct hpp_type3_register { > + u32 crap_describing_entry_contents; > + struct list_head list; > +}; > + > +struct hpp_type3 { > + u32 revision; > + struct list_head regs; > +}; > + > 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_type3 *t3; /* Type3: NULL if not available */ > struct hpp_type0 type0_data; > struct hpp_type1 type1_data; > struct hpp_type2 type2_data; > + struct hpp_type3 type3_data; > }; > > #ifdef CONFIG_ACPI > #include <linux/acpi.h> > int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp); > +void pci_cleanup_hp_params(struct hotplug_params *hpp); > 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); > -- > 2.19.2 >