diff mbox series

[02/12] PCI/ACPI: Add PERST# Assertion Delay _DSM method

Message ID 20250401153225.96379-3-anshuman.gupta@intel.com (mailing list archive)
State New
Headers show
Series VRAM Self Refresh | expand

Commit Message

Anshuman Gupta April 1, 2025, 3:32 p.m. UTC
Implement _DSM Method 11 as per PCI firmware specs
section 4.6.11 Rev 3.3.

Signed-off-by: Anshuman Gupta <anshuman.gupta@intel.com>
---
 drivers/pci/pci-acpi.c   | 53 ++++++++++++++++++++++++++++++++++++++++
 include/linux/pci-acpi.h |  7 ++++++
 2 files changed, 60 insertions(+)

Comments

Bjorn Helgaas April 1, 2025, 6:30 p.m. UTC | #1
On Tue, Apr 01, 2025 at 09:02:15PM +0530, Anshuman Gupta wrote:
> Implement _DSM Method 11 as per PCI firmware specs
> section 4.6.11 Rev 3.3.

"PCI Firmware r3.3, sec 4.6.11" so the citation is major to minor.

"0xb" or "0Bh" to match spec usage.

> Signed-off-by: Anshuman Gupta <anshuman.gupta@intel.com>
> ---
>  drivers/pci/pci-acpi.c   | 53 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/pci-acpi.h |  7 ++++++
>  2 files changed, 60 insertions(+)
> 
> diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
> index ebd49e43457e..04149f037664 100644
> --- a/drivers/pci/pci-acpi.c
> +++ b/drivers/pci/pci-acpi.c
> @@ -1499,6 +1499,59 @@ int pci_acpi_request_d3cold_aux_power(struct pci_dev *dev, u32 requested_power)
>  }
>  EXPORT_SYMBOL(pci_acpi_request_d3cold_aux_power);
>  
> +/**
> + * pci_acpi_add_perst_assertion_delay - Request PERST# delay via ACPI DSM
> + * @dev: PCI device instance
> + * @delay_us: Requested delay_us
> + *
> + * This function sends a request to the host BIOS via ACPI _DSM to grant the
> + * required PERST# delay for the specified PCI device. It evaluates the _DSM
> + * to request the PERST# delay and handles the response accordingly.
> + *
> + * Return: returns 0 on success and errno on failure.
> + */
> +int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us)
> +{
> +	union acpi_object in_obj = {
> +		.integer.type = ACPI_TYPE_INTEGER,
> +		.integer.value = delay_us,
> +	};
> +
> +	union acpi_object *out_obj;
> +	acpi_handle handle;
> +	int result, ret = -EINVAL;
> +
> +	if (!dev || !ACPI_HANDLE(&dev->dev))
> +		return -EINVAL;
> +
> +	handle = ACPI_HANDLE(&dev->dev);

acpi_check_dsm().

> +	out_obj = acpi_evaluate_dsm_typed(handle, &pci_acpi_dsm_guid, 4,
> +					  DSM_PCI_PERST_ASSERTION_DELAY,
> +					  &in_obj, ACPI_TYPE_INTEGER);
> +	if (!out_obj)
> +		return -EINVAL;
> +
> +	result = out_obj->integer.value;
> +
> +	if (result == delay_us) {
> +		dev_info(&dev->dev, "PERST# Assertion Delay set to"
> +			 "%u microseconds\n", delay_us);

pci_info().

Join these into a single string, even though they won't fit in a line
without wrapping.  This is to make them easier to grep for when a user
reports seeing the message.  (Do this on the previous patch too, where
I forgot to mention it.)

> +		ret = 0;
> +	} else if (result == 0) {
> +		dev_warn(&dev->dev, "PERST# Assertion Delay request failed,"
> +			 "no previous valid request\n");
> +	} else {
> +		dev_warn(&dev->dev,
> +			 "PERST# Assertion Delay request failed"
> +			 "Previous valid delay: %u microseconds\n", result);
> +	}
> +
> +	ACPI_FREE(out_obj);
> +	return ret;
> +}
> +EXPORT_SYMBOL(pci_acpi_add_perst_assertion_delay);
Rafael J. Wysocki April 2, 2025, 11:06 a.m. UTC | #2
On Tue, Apr 1, 2025 at 5:36 PM Anshuman Gupta <anshuman.gupta@intel.com> wrote:
>
> Implement _DSM Method 11 as per PCI firmware specs
> section 4.6.11 Rev 3.3.
>
> Signed-off-by: Anshuman Gupta <anshuman.gupta@intel.com>

I have basically the same comments as for the previous patch (in
addition to Bjorn's comments).

> ---
>  drivers/pci/pci-acpi.c   | 53 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/pci-acpi.h |  7 ++++++
>  2 files changed, 60 insertions(+)
>
> diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
> index ebd49e43457e..04149f037664 100644
> --- a/drivers/pci/pci-acpi.c
> +++ b/drivers/pci/pci-acpi.c
> @@ -1499,6 +1499,59 @@ int pci_acpi_request_d3cold_aux_power(struct pci_dev *dev, u32 requested_power)
>  }
>  EXPORT_SYMBOL(pci_acpi_request_d3cold_aux_power);
>
> +/**
> + * pci_acpi_add_perst_assertion_delay - Request PERST# delay via ACPI DSM
> + * @dev: PCI device instance
> + * @delay_us: Requested delay_us

How's the caller supposed to find out what value to use here?

> + *
> + * This function sends a request to the host BIOS via ACPI _DSM to grant the
> + * required PERST# delay for the specified PCI device. It evaluates the _DSM
> + * to request the PERST# delay and handles the response accordingly.
> + *
> + * Return: returns 0 on success and errno on failure.
> + */
> +int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us)
> +{
> +       union acpi_object in_obj = {
> +               .integer.type = ACPI_TYPE_INTEGER,
> +               .integer.value = delay_us,
> +       };
> +
> +       union acpi_object *out_obj;
> +       acpi_handle handle;
> +       int result, ret = -EINVAL;
> +
> +       if (!dev || !ACPI_HANDLE(&dev->dev))
> +               return -EINVAL;
> +
> +       handle = ACPI_HANDLE(&dev->dev);

Please use ACPI_HANDLE() once.

> +
> +       out_obj = acpi_evaluate_dsm_typed(handle, &pci_acpi_dsm_guid, 4,

This is something I haven't noticed in the previous patch, but also
applies to it.

Why is rev 4 of the interface hard-coded here?

> +                                         DSM_PCI_PERST_ASSERTION_DELAY,
> +                                         &in_obj, ACPI_TYPE_INTEGER);
> +       if (!out_obj)
> +               return -EINVAL;
> +
> +       result = out_obj->integer.value;
> +
> +       if (result == delay_us) {
> +               dev_info(&dev->dev, "PERST# Assertion Delay set to"
> +                        "%u microseconds\n", delay_us);
> +               ret = 0;
> +       } else if (result == 0) {
> +               dev_warn(&dev->dev, "PERST# Assertion Delay request failed,"
> +                        "no previous valid request\n");
> +       } else {
> +               dev_warn(&dev->dev,
> +                        "PERST# Assertion Delay request failed"
> +                        "Previous valid delay: %u microseconds\n", result);
> +       }
> +
> +       ACPI_FREE(out_obj);
> +       return ret;
> +}
> +EXPORT_SYMBOL(pci_acpi_add_perst_assertion_delay);

EXPORT_SYMBOL_GPL()?

> +
>  static void pci_acpi_set_external_facing(struct pci_dev *dev)
>  {
>         u8 val;
> diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
> index dbc4b0ed433c..4b7373f91a9a 100644
> --- a/include/linux/pci-acpi.h
> +++ b/include/linux/pci-acpi.h
> @@ -122,6 +122,7 @@ extern const guid_t pci_acpi_dsm_guid;
>  #define DSM_PCI_POWER_ON_RESET_DELAY           0x08
>  #define DSM_PCI_DEVICE_READINESS_DURATIONS     0x09
>  #define DSM_PCI_D3COLD_AUX_POWER_LIMIT         0x0A
> +#define DSM_PCI_PERST_ASSERTION_DELAY          0x0B
>
>  #ifdef CONFIG_PCIE_EDR
>  void pci_acpi_add_edr_notifier(struct pci_dev *pdev);
> @@ -134,6 +135,7 @@ static inline void pci_acpi_remove_edr_notifier(struct pci_dev *pdev) { }
>  int pci_acpi_set_companion_lookup_hook(struct acpi_device *(*func)(struct pci_dev *));
>  void pci_acpi_clear_companion_lookup_hook(void);
>  int pci_acpi_request_d3cold_aux_power(struct pci_dev *dev, u32 requested_power);
> +int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us);
>
>  #else  /* CONFIG_ACPI */
>  static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
> @@ -142,6 +144,11 @@ int pci_acpi_request_d3cold_aux_power(struct pci_dev *dev, u32 requested_power)
>  {
>         return -EOPNOTSUPP;
>  }
> +
> +int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us)
> +{
> +       return -EOPNOTSUPP;
> +}
>  #endif /* CONFIG_ACPI */
>
>  #endif /* _PCI_ACPI_H_ */
> --
Bjorn Helgaas April 2, 2025, 2:21 p.m. UTC | #3
On Wed, Apr 02, 2025 at 01:06:42PM +0200, Rafael J. Wysocki wrote:
> On Tue, Apr 1, 2025 at 5:36 PM Anshuman Gupta <anshuman.gupta@intel.com> wrote:
> >
> > Implement _DSM Method 11 as per PCI firmware specs
> > section 4.6.11 Rev 3.3.

> > +int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us)
> > +{
> > +       union acpi_object in_obj = {
> > +               .integer.type = ACPI_TYPE_INTEGER,
> > +               .integer.value = delay_us,
> > +       };
> > +
> > +       union acpi_object *out_obj;
> > +       acpi_handle handle;
> > +       int result, ret = -EINVAL;
> > +
> > +       if (!dev || !ACPI_HANDLE(&dev->dev))
> > +               return -EINVAL;
> > +
> > +       handle = ACPI_HANDLE(&dev->dev);
> > +
> > +       out_obj = acpi_evaluate_dsm_typed(handle, &pci_acpi_dsm_guid, 4,
> 
> This is something I haven't noticed in the previous patch, but also
> applies to it.
> 
> Why is rev 4 of the interface hard-coded here?

Thanks for asking this because it's related to the whole _DSM revision
question that I don't understand.

If we didn't use rev 4 here, what should we use?  The PCI Firmware
spec, r3.3, sec 4.6.11, documents this interface and says "lowest
valid Revision ID value is 4", so that's the source of the 4.

My argument is that the spec documents rev 4, the kernel code was
tested with rev 4, so what would be the benefit of using a different
revision here?

> > +                                         DSM_PCI_PERST_ASSERTION_DELAY,
> > +                                         &in_obj, ACPI_TYPE_INTEGER);
Rafael J. Wysocki April 2, 2025, 2:52 p.m. UTC | #4
On Wed, Apr 2, 2025 at 4:21 PM Bjorn Helgaas <helgaas@kernel.org> wrote:
>
> On Wed, Apr 02, 2025 at 01:06:42PM +0200, Rafael J. Wysocki wrote:
> > On Tue, Apr 1, 2025 at 5:36 PM Anshuman Gupta <anshuman.gupta@intel.com> wrote:
> > >
> > > Implement _DSM Method 11 as per PCI firmware specs
> > > section 4.6.11 Rev 3.3.
>
> > > +int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us)
> > > +{
> > > +       union acpi_object in_obj = {
> > > +               .integer.type = ACPI_TYPE_INTEGER,
> > > +               .integer.value = delay_us,
> > > +       };
> > > +
> > > +       union acpi_object *out_obj;
> > > +       acpi_handle handle;
> > > +       int result, ret = -EINVAL;
> > > +
> > > +       if (!dev || !ACPI_HANDLE(&dev->dev))
> > > +               return -EINVAL;
> > > +
> > > +       handle = ACPI_HANDLE(&dev->dev);
> > > +
> > > +       out_obj = acpi_evaluate_dsm_typed(handle, &pci_acpi_dsm_guid, 4,
> >
> > This is something I haven't noticed in the previous patch, but also
> > applies to it.
> >
> > Why is rev 4 of the interface hard-coded here?
>
> Thanks for asking this because it's related to the whole _DSM revision
> question that I don't understand.
>
> If we didn't use rev 4 here, what should we use?  The PCI Firmware
> spec, r3.3, sec 4.6.11, documents this interface and says "lowest
> valid Revision ID value is 4", so that's the source of the 4.

Well, the "lowest vaild Revision ID" does not generally mean the "only
valid Revision ID".

> My argument is that the spec documents rev 4, the kernel code was
> tested with rev 4, so what would be the benefit of using a different
> revision here?

I'm talking about using a symbol to represent the number 4, not about
possibly using a different number, along the lines of using, say,
ACPI_FADT_LOW_POWER_S0 instead of putting BIT(21) directly into the
code.

The value is not likely to change, but using a symbol for representing
it has merit (it can be meaningfully used in searches, it can be
documented etc.).

Now, I'm not sure how likely it is for the PCI Firmware spec to bump
up the revision of this interface (I suppose that it will do so if a
new function is defined), but even if it does so, the kernel will have
to check both the new revision and rev 4 anyway, in case the firmware
doesn't know about the new revision.
Bjorn Helgaas April 2, 2025, 3:50 p.m. UTC | #5
On Wed, Apr 02, 2025 at 04:52:55PM +0200, Rafael J. Wysocki wrote:
> On Wed, Apr 2, 2025 at 4:21 PM Bjorn Helgaas <helgaas@kernel.org> wrote:
> > On Wed, Apr 02, 2025 at 01:06:42PM +0200, Rafael J. Wysocki wrote:
> > > On Tue, Apr 1, 2025 at 5:36 PM Anshuman Gupta <anshuman.gupta@intel.com> wrote:
> > > >
> > > > Implement _DSM Method 11 as per PCI firmware specs
> > > > section 4.6.11 Rev 3.3.
> >
> > > > +int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us)
> > > > +{
> > > > +       union acpi_object in_obj = {
> > > > +               .integer.type = ACPI_TYPE_INTEGER,
> > > > +               .integer.value = delay_us,
> > > > +       };
> > > > +
> > > > +       union acpi_object *out_obj;
> > > > +       acpi_handle handle;
> > > > +       int result, ret = -EINVAL;
> > > > +
> > > > +       if (!dev || !ACPI_HANDLE(&dev->dev))
> > > > +               return -EINVAL;
> > > > +
> > > > +       handle = ACPI_HANDLE(&dev->dev);
> > > > +
> > > > +       out_obj = acpi_evaluate_dsm_typed(handle, &pci_acpi_dsm_guid, 4,
> > >
> > > This is something I haven't noticed in the previous patch, but also
> > > applies to it.
> > >
> > > Why is rev 4 of the interface hard-coded here?
> >
> > Thanks for asking this because it's related to the whole _DSM revision
> > question that I don't understand.
> >
> > If we didn't use rev 4 here, what should we use?  The PCI Firmware
> > spec, r3.3, sec 4.6.11, documents this interface and says "lowest
> > valid Revision ID value is 4", so that's the source of the 4.
> 
> Well, the "lowest valid Revision ID" does not generally mean the "only
> valid Revision ID".

True, but why should the kernel change from using the tested revision
ID to some other revision ID?  What would be the benefit of using rev
5?

PCI Firmware r3.3 does contain a statement that "OSPM must invoke all
Functions other than Function 0 with the same Revision ID value," but
IMO that was a mistake, and we just approved an ECR to remove it.

> > My argument is that the spec documents rev 4, the kernel code was
> > tested with rev 4, so what would be the benefit of using a different
> > revision here?
> 
> I'm talking about using a symbol to represent the number 4, not about
> possibly using a different number, along the lines of using, say,
> ACPI_FADT_LOW_POWER_S0 instead of putting BIT(21) directly into the
> code.
>
> The value is not likely to change, but using a symbol for representing
> it has merit (it can be meaningfully used in searches, it can be
> documented etc.).

DSM_PCI_PERST_ASSERTION_DELAY (the function number) is a great thing
to search for.  I doubt a symbol for "4" would really add anything.
At least in PCI, changes to a particular _DSM function are relatively
rare.

> Now, I'm not sure how likely it is for the PCI Firmware spec to bump
> up the revision of this interface (I suppose that it will do so if a
> new function is defined), but even if it does so, the kernel will have
> to check both the new revision and rev 4 anyway, in case the firmware
> doesn't know about the new revision.

I guess the reason the OS would check both rev 5 and rev 4 would be
that a new platform might implement DSM_PCI_PERST_ASSERTION_DELAY only
at rev 5?  I think this would be a real mistake in terms of
implementing something to the spec.  

The spec documents DSM_PCI_PERST_ASSERTION_DELAY rev 4.  Some
platforms implemented and tested DSM_PCI_PERST_ASSERTION_DELAY rev 4.
Linux added and tested support for DSM_PCI_PERST_ASSERTION_DELAY rev
4.  I propose new platforms should also implement and test
DSM_PCI_PERST_ASSERTION_DELAY rev 4.

If a new platform implements only DSM_PCI_PERST_ASSERTION_DELAY rev 5,
there is no actual documentation for that rev other than the spec
assertion that "rev 5 must support all functions defined by previous
revs of the UUID."  IMO this is nonsense.  The platform might have no
need to implement all the functions defined for the UUID.

Even if the platform makes all its functions valid for "the lowest
valid Revision ID" through the "current Revision ID", there's nothing
other than the spec assertion to guarantee that they all behave the
same.  And dealing with multiple revisions that are "supposed" to be
the same just makes work and risk for the OS.

Bjorn
Rafael J. Wysocki April 2, 2025, 5:51 p.m. UTC | #6
On Wed, Apr 2, 2025 at 5:50 PM Bjorn Helgaas <helgaas@kernel.org> wrote:
>
> On Wed, Apr 02, 2025 at 04:52:55PM +0200, Rafael J. Wysocki wrote:
> > On Wed, Apr 2, 2025 at 4:21 PM Bjorn Helgaas <helgaas@kernel.org> wrote:
> > > On Wed, Apr 02, 2025 at 01:06:42PM +0200, Rafael J. Wysocki wrote:
> > > > On Tue, Apr 1, 2025 at 5:36 PM Anshuman Gupta <anshuman.gupta@intel.com> wrote:
> > > > >
> > > > > Implement _DSM Method 11 as per PCI firmware specs
> > > > > section 4.6.11 Rev 3.3.
> > >
> > > > > +int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us)
> > > > > +{
> > > > > +       union acpi_object in_obj = {
> > > > > +               .integer.type = ACPI_TYPE_INTEGER,
> > > > > +               .integer.value = delay_us,
> > > > > +       };
> > > > > +
> > > > > +       union acpi_object *out_obj;
> > > > > +       acpi_handle handle;
> > > > > +       int result, ret = -EINVAL;
> > > > > +
> > > > > +       if (!dev || !ACPI_HANDLE(&dev->dev))
> > > > > +               return -EINVAL;
> > > > > +
> > > > > +       handle = ACPI_HANDLE(&dev->dev);
> > > > > +
> > > > > +       out_obj = acpi_evaluate_dsm_typed(handle, &pci_acpi_dsm_guid, 4,
> > > >
> > > > This is something I haven't noticed in the previous patch, but also
> > > > applies to it.
> > > >
> > > > Why is rev 4 of the interface hard-coded here?
> > >
> > > Thanks for asking this because it's related to the whole _DSM revision
> > > question that I don't understand.
> > >
> > > If we didn't use rev 4 here, what should we use?  The PCI Firmware
> > > spec, r3.3, sec 4.6.11, documents this interface and says "lowest
> > > valid Revision ID value is 4", so that's the source of the 4.
> >
> > Well, the "lowest valid Revision ID" does not generally mean the "only
> > valid Revision ID".
>
> True, but why should the kernel change from using the tested revision
> ID to some other revision ID?  What would be the benefit of using rev
> 5?

TBH I'm not exactly buying the "tested revision ID" concept because
what does it really mean?

If a given _DSM interface has been tested on one platform, does it
necessarily mean that it will work on all of the other platforms
implementing it?

> PCI Firmware r3.3 does contain a statement that "OSPM must invoke all
> Functions other than Function 0 with the same Revision ID value," but
> IMO that was a mistake, and we just approved an ECR to remove it.
>
> > > My argument is that the spec documents rev 4, the kernel code was
> > > tested with rev 4, so what would be the benefit of using a different
> > > revision here?
> >
> > I'm talking about using a symbol to represent the number 4, not about
> > possibly using a different number, along the lines of using, say,
> > ACPI_FADT_LOW_POWER_S0 instead of putting BIT(21) directly into the
> > code.
> >
> > The value is not likely to change, but using a symbol for representing
> > it has merit (it can be meaningfully used in searches, it can be
> > documented etc.).
>
> DSM_PCI_PERST_ASSERTION_DELAY (the function number) is a great thing
> to search for.  I doubt a symbol for "4" would really add anything.
> At least in PCI, changes to a particular _DSM function are relatively
> rare.
>
> > Now, I'm not sure how likely it is for the PCI Firmware spec to bump
> > up the revision of this interface (I suppose that it will do so if a
> > new function is defined), but even if it does so, the kernel will have
> > to check both the new revision and rev 4 anyway, in case the firmware
> > doesn't know about the new revision.
>
> I guess the reason the OS would check both rev 5 and rev 4 would be
> that a new platform might implement DSM_PCI_PERST_ASSERTION_DELAY only
> at rev 5?  I think this would be a real mistake in terms of
> implementing something to the spec.

This is a real possibility, however, because there's nothing to
prevent this kind of spec interpretation.

I didn't mean this, though.

Say the kernel wants to use a function that is only defined at rev 5.
It will invoke function 0 at rev 5 to see if this function is
available, but then it may as well see if the other functions it wants
to use are available at rev 5 because it will get this information
from the same _DSM call.  If the platform firmware responds that they
all are implemented, why won't the kernel use them all at rev 5?

> The spec documents DSM_PCI_PERST_ASSERTION_DELAY rev 4.  Some
> platforms implemented and tested DSM_PCI_PERST_ASSERTION_DELAY rev 4.
> Linux added and tested support for DSM_PCI_PERST_ASSERTION_DELAY rev
> 4.  I propose new platforms should also implement and test
> DSM_PCI_PERST_ASSERTION_DELAY rev 4.
>
> If a new platform implements only DSM_PCI_PERST_ASSERTION_DELAY rev 5,
> there is no actual documentation for that rev other than the spec
> assertion that "rev 5 must support all functions defined by previous
> revs of the UUID."  IMO this is nonsense.  The platform might have no
> need to implement all the functions defined for the UUID.

Sure.  That's why function 0 should always be used.

> Even if the platform makes all its functions valid for "the lowest
> valid Revision ID" through the "current Revision ID", there's nothing
> other than the spec assertion to guarantee that they all behave the
> same.  And dealing with multiple revisions that are "supposed" to be
> the same just makes work and risk for the OS.

You seem to be questioning the interface versioning at large by saying
that there is no real guarantee of backwards compatibility between
consecutive interface revisions on the same platform.

Presumably, though, the interface is adhering to some specification
which defines the behavior of the functions (and the set of available
functions in the first place) for all of the valid revisions of it.
The OS and the platform firmware both follow the same spec and so they
should be mutually compatible.  If this particular spec defines rev 5
to be an exact superset of rev 4, there is no reason to expect
anything else.
Bjorn Helgaas April 2, 2025, 6:48 p.m. UTC | #7
On Wed, Apr 02, 2025 at 07:51:29PM +0200, Rafael J. Wysocki wrote:
> On Wed, Apr 2, 2025 at 5:50 PM Bjorn Helgaas <helgaas@kernel.org> wrote:
> > On Wed, Apr 02, 2025 at 04:52:55PM +0200, Rafael J. Wysocki wrote:
> > > On Wed, Apr 2, 2025 at 4:21 PM Bjorn Helgaas <helgaas@kernel.org> wrote:
> > > > On Wed, Apr 02, 2025 at 01:06:42PM +0200, Rafael J. Wysocki wrote:
> > > > > On Tue, Apr 1, 2025 at 5:36 PM Anshuman Gupta <anshuman.gupta@intel.com> wrote:
> > > > > >
> > > > > > Implement _DSM Method 11 as per PCI firmware specs
> > > > > > section 4.6.11 Rev 3.3.
> > > >
> > > > > > +int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us)
> > > > > > +{
> > > > > > +       union acpi_object in_obj = {
> > > > > > +               .integer.type = ACPI_TYPE_INTEGER,
> > > > > > +               .integer.value = delay_us,
> > > > > > +       };
> > > > > > +
> > > > > > +       union acpi_object *out_obj;
> > > > > > +       acpi_handle handle;
> > > > > > +       int result, ret = -EINVAL;
> > > > > > +
> > > > > > +       if (!dev || !ACPI_HANDLE(&dev->dev))
> > > > > > +               return -EINVAL;
> > > > > > +
> > > > > > +       handle = ACPI_HANDLE(&dev->dev);
> > > > > > +
> > > > > > +       out_obj = acpi_evaluate_dsm_typed(handle, &pci_acpi_dsm_guid, 4,
> > > > >
> > > > > This is something I haven't noticed in the previous patch, but also
> > > > > applies to it.
> > > > >
> > > > > Why is rev 4 of the interface hard-coded here?
> > > >
> > > > Thanks for asking this because it's related to the whole _DSM revision
> > > > question that I don't understand.
> > > >
> > > > If we didn't use rev 4 here, what should we use?  The PCI Firmware
> > > > spec, r3.3, sec 4.6.11, documents this interface and says "lowest
> > > > valid Revision ID value is 4", so that's the source of the 4.
> > >
> > > Well, the "lowest valid Revision ID" does not generally mean the "only
> > > valid Revision ID".
> >
> > True, but why should the kernel change from using the tested revision
> > ID to some other revision ID?  What would be the benefit of using rev
> > 5?
> 
> TBH I'm not exactly buying the "tested revision ID" concept because
> what does it really mean?
> 
> If a given _DSM interface has been tested on one platform, does it
> necessarily mean that it will work on all of the other platforms
> implementing it?

No, all we can say is that this OS rev 4 code works on the platforms
we've tested.  Other platforms might have their own defects.

My point is that this OS code was written to the rev 4 spec and has
been tested, and we shouldn't change the code or the revision it uses
unless we're fixing something.

> > > Now, I'm not sure how likely it is for the PCI Firmware spec to
> > > bump up the revision of this interface (I suppose that it will
> > > do so if a new function is defined), but even if it does so, the
> > > kernel will have to check both the new revision and rev 4
> > > anyway, in case the firmware doesn't know about the new
> > > revision.
> >
> > I guess the reason the OS would check both rev 5 and rev 4 would
> > be that a new platform might implement
> > DSM_PCI_PERST_ASSERTION_DELAY only at rev 5?  I think this would
> > be a real mistake in terms of implementing something to the spec.
> 
> This is a real possibility, however, because there's nothing to
> prevent this kind of spec interpretation.
> 
> I didn't mean this, though.
> 
> Say the kernel wants to use a function that is only defined at rev
> 5.  It will invoke function 0 at rev 5 to see if this function is
> available, but then it may as well see if the other functions it
> wants to use are available at rev 5 because it will get this
> information from the same _DSM call.  If the platform firmware
> responds that they all are implemented, why won't the kernel use
> them all at rev 5?

This is an unnecessary change, since the OS tested its rev 4 code and
now we're saying the OS should use rev 5 instead, which the OS did not
test.  The reason rev 5 exists at all is probably that some completely
unrelated new function was added, and somebody decided it should be
rev 5.

I guess function 0 *could* have been defined to answer "yes/no" about
whether a single (function, revision) is implemented.  Then we
probably wouldn't be having this conversation.

But we do get an entire bitmask of implemented functions back from
function 0, which allows the possibility of using the same revision
for all the functions.  I suppose we could have a boot-time function
that calls function 0 with rev 0, 1, 2, ..., until it gets an empty
bitmask, and saves the highest rev with a non-empty mask.

In PCI we don't do that; instead, we call acpi_check_dsm() before
every acpi_evaluate_dsm().  The main reason is that I don't think it's
safe to change the function X rev just because function Y was added
later with a higher revision.

> > The spec documents DSM_PCI_PERST_ASSERTION_DELAY rev 4.  Some
> > platforms implemented and tested DSM_PCI_PERST_ASSERTION_DELAY rev
> > 4.  Linux added and tested support for
> > DSM_PCI_PERST_ASSERTION_DELAY rev 4.  I propose new platforms
> > should also implement and test DSM_PCI_PERST_ASSERTION_DELAY rev
> > 4.
> >
> > If a new platform implements only DSM_PCI_PERST_ASSERTION_DELAY
> > rev 5, there is no actual documentation for that rev other than
> > the spec assertion that "rev 5 must support all functions defined
> > by previous revs of the UUID."  IMO this is nonsense.  The
> > platform might have no need to implement all the functions defined
> > for the UUID.
> 
> Sure.  That's why function 0 should always be used.

Yes.  But the requirement that rev 5 must support all functions ever
previously documented for the UUID still makes no sense to me.  I
don't think there's any requirement that a platform implement ALL of
the documented PCI functions.

Maybe the intent is that this only applies to a given platform, i.e.,
that new firmware for that platform must continue to support all
functions and revisions that were ever supported on that platform?
That seems obvious to me and hardly worth mentioning.

> > Even if the platform makes all its functions valid for "the lowest
> > valid Revision ID" through the "current Revision ID", there's
> > nothing other than the spec assertion to guarantee that they all
> > behave the same.  And dealing with multiple revisions that are
> > "supposed" to be the same just makes work and risk for the OS.
> 
> You seem to be questioning the interface versioning at large by
> saying that there is no real guarantee of backwards compatibility
> between consecutive interface revisions on the same platform.

That's exactly what I'm saying, although I think problems are more
likely across different platforms.  There's the requirement in the
spec, but that's just words on a page.  There's no way to enforce or
validate it.

> Presumably, though, the interface is adhering to some specification
> which defines the behavior of the functions (and the set of
> available functions in the first place) for all of the valid
> revisions of it.  The OS and the platform firmware both follow the
> same spec and so they should be mutually compatible.  If this
> particular spec defines rev 5 to be an exact superset of rev 4,
> there is no reason to expect anything else.

I don't *expect* rev 5 to be different.  But as a user of it, why
would I change working, tested code that is not broken?

The PCI DPC function 0xc is an example where rev 5 (per ECN) and rev 6
(per r3.3) are not compatible.

If the OS implemented rev 5, then learns via function 0 that function
0xc is also supported at rev 6, and starts using the same OS code with
rev 6, the OS is broken.

Bjorn
Rafael J. Wysocki April 2, 2025, 7:36 p.m. UTC | #8
On Wed, Apr 2, 2025 at 8:48 PM Bjorn Helgaas <helgaas@kernel.org> wrote:
>
> On Wed, Apr 02, 2025 at 07:51:29PM +0200, Rafael J. Wysocki wrote:
> > On Wed, Apr 2, 2025 at 5:50 PM Bjorn Helgaas <helgaas@kernel.org> wrote:
> > > On Wed, Apr 02, 2025 at 04:52:55PM +0200, Rafael J. Wysocki wrote:
> > > > On Wed, Apr 2, 2025 at 4:21 PM Bjorn Helgaas <helgaas@kernel.org> wrote:
> > > > > On Wed, Apr 02, 2025 at 01:06:42PM +0200, Rafael J. Wysocki wrote:
> > > > > > On Tue, Apr 1, 2025 at 5:36 PM Anshuman Gupta <anshuman.gupta@intel.com> wrote:
> > > > > > >
> > > > > > > Implement _DSM Method 11 as per PCI firmware specs
> > > > > > > section 4.6.11 Rev 3.3.
> > > > >
> > > > > > > +int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us)
> > > > > > > +{
> > > > > > > +       union acpi_object in_obj = {
> > > > > > > +               .integer.type = ACPI_TYPE_INTEGER,
> > > > > > > +               .integer.value = delay_us,
> > > > > > > +       };
> > > > > > > +
> > > > > > > +       union acpi_object *out_obj;
> > > > > > > +       acpi_handle handle;
> > > > > > > +       int result, ret = -EINVAL;
> > > > > > > +
> > > > > > > +       if (!dev || !ACPI_HANDLE(&dev->dev))
> > > > > > > +               return -EINVAL;
> > > > > > > +
> > > > > > > +       handle = ACPI_HANDLE(&dev->dev);
> > > > > > > +
> > > > > > > +       out_obj = acpi_evaluate_dsm_typed(handle, &pci_acpi_dsm_guid, 4,
> > > > > >
> > > > > > This is something I haven't noticed in the previous patch, but also
> > > > > > applies to it.
> > > > > >
> > > > > > Why is rev 4 of the interface hard-coded here?
> > > > >
> > > > > Thanks for asking this because it's related to the whole _DSM revision
> > > > > question that I don't understand.
> > > > >
> > > > > If we didn't use rev 4 here, what should we use?  The PCI Firmware
> > > > > spec, r3.3, sec 4.6.11, documents this interface and says "lowest
> > > > > valid Revision ID value is 4", so that's the source of the 4.
> > > >
> > > > Well, the "lowest valid Revision ID" does not generally mean the "only
> > > > valid Revision ID".
> > >
> > > True, but why should the kernel change from using the tested revision
> > > ID to some other revision ID?  What would be the benefit of using rev
> > > 5?
> >
> > TBH I'm not exactly buying the "tested revision ID" concept because
> > what does it really mean?
> >
> > If a given _DSM interface has been tested on one platform, does it
> > necessarily mean that it will work on all of the other platforms
> > implementing it?
>
> No, all we can say is that this OS rev 4 code works on the platforms
> we've tested.  Other platforms might have their own defects.
>
> My point is that this OS code was written to the rev 4 spec and has
> been tested, and we shouldn't change the code or the revision it uses
> unless we're fixing something.

Well, if the spec says "rev 5 is the same as rev 4 except for function
Y which is new in rev 5", then arguably the same OS code is also
compliant with rev 5 (it will not use function Y, but that's fine).

If the platform firmware follows the same spec, then the rev 5
implementation in it can be expected to work exactly as its rev 4
implementation either.

I see no problem with using rev 5 instead of rev 4 in that case and nn
new platforms, rev 5 is just as risky as rev 4.

> > > > Now, I'm not sure how likely it is for the PCI Firmware spec to
> > > > bump up the revision of this interface (I suppose that it will
> > > > do so if a new function is defined), but even if it does so, the
> > > > kernel will have to check both the new revision and rev 4
> > > > anyway, in case the firmware doesn't know about the new
> > > > revision.
> > >
> > > I guess the reason the OS would check both rev 5 and rev 4 would
> > > be that a new platform might implement
> > > DSM_PCI_PERST_ASSERTION_DELAY only at rev 5?  I think this would
> > > be a real mistake in terms of implementing something to the spec.
> >
> > This is a real possibility, however, because there's nothing to
> > prevent this kind of spec interpretation.
> >
> > I didn't mean this, though.
> >
> > Say the kernel wants to use a function that is only defined at rev
> > 5.  It will invoke function 0 at rev 5 to see if this function is
> > available, but then it may as well see if the other functions it
> > wants to use are available at rev 5 because it will get this
> > information from the same _DSM call.  If the platform firmware
> > responds that they all are implemented, why won't the kernel use
> > them all at rev 5?
>
> This is an unnecessary change, since the OS tested its rev 4 code and
> now we're saying the OS should use rev 5 instead, which the OS did not
> test.

Yes, it did.  It is the same code, as per the above.

> The reason rev 5 exists at all is probably that some completely
> unrelated new function was added, and somebody decided it should be
> rev 5.
>
> I guess function 0 *could* have been defined to answer "yes/no" about
> whether a single (function, revision) is implemented.  Then we
> probably wouldn't be having this conversation.
>
> But we do get an entire bitmask of implemented functions back from
> function 0, which allows the possibility of using the same revision
> for all the functions.  I suppose we could have a boot-time function
> that calls function 0 with rev 0, 1, 2, ..., until it gets an empty
> bitmask, and saves the highest rev with a non-empty mask.
>
> In PCI we don't do that; instead, we call acpi_check_dsm() before
> every acpi_evaluate_dsm().  The main reason is that I don't think it's
> safe to change the function X rev just because function Y was added
> later with a higher revision.

So what if there is a platform that only implements revisions greater than X?

Presumably, there is another OS targeted by this platform that only
uses revisions X+1 and forward.

> > > The spec documents DSM_PCI_PERST_ASSERTION_DELAY rev 4.  Some
> > > platforms implemented and tested DSM_PCI_PERST_ASSERTION_DELAY rev
> > > 4.  Linux added and tested support for
> > > DSM_PCI_PERST_ASSERTION_DELAY rev 4.  I propose new platforms
> > > should also implement and test DSM_PCI_PERST_ASSERTION_DELAY rev
> > > 4.
> > >
> > > If a new platform implements only DSM_PCI_PERST_ASSERTION_DELAY
> > > rev 5, there is no actual documentation for that rev other than
> > > the spec assertion that "rev 5 must support all functions defined
> > > by previous revs of the UUID."  IMO this is nonsense.  The
> > > platform might have no need to implement all the functions defined
> > > for the UUID.
> >
> > Sure.  That's why function 0 should always be used.
>
> Yes.  But the requirement that rev 5 must support all functions ever
> previously documented for the UUID still makes no sense to me.  I
> don't think there's any requirement that a platform implement ALL of
> the documented PCI functions.

I think that there is some confusion in the spec language that seems
to be conflating platforms with interface definitions.

It ostensibly wants the definitions of subsequent revisions of the
same interface to be consistent with each other, which is not a bad
thing in principle IMV, but at the same time it ostensibly allows
platforms to choose which functions to implement, and it doesn't say
anything about the expected behavior when the OS attempts to use a
function that is not implemented for a given rev.

You are right that the backward compatibility language in the _DSM
definition is not enforceable, but it can be regarded as a guidance:
Do not change the interface that you have once defined arbitrarily
between revisions.  After all, if you don't like this guidance, you
can just allocate a new UUID and define a new interface based on it.

Anyway, I'm not the author of this piece of the specification, so I
don't really know the original intent, and it is not sufficiently
clear.

> Maybe the intent is that this only applies to a given platform, i.e.,
> that new firmware for that platform must continue to support all
> functions and revisions that were ever supported on that platform?
> That seems obvious to me and hardly worth mentioning.
>
> > > Even if the platform makes all its functions valid for "the lowest
> > > valid Revision ID" through the "current Revision ID", there's
> > > nothing other than the spec assertion to guarantee that they all
> > > behave the same.  And dealing with multiple revisions that are
> > > "supposed" to be the same just makes work and risk for the OS.
> >
> > You seem to be questioning the interface versioning at large by
> > saying that there is no real guarantee of backwards compatibility
> > between consecutive interface revisions on the same platform.
>
> That's exactly what I'm saying, although I think problems are more
> likely across different platforms.  There's the requirement in the
> spec, but that's just words on a page.  There's no way to enforce or
> validate it.
>
> > Presumably, though, the interface is adhering to some specification
> > which defines the behavior of the functions (and the set of
> > available functions in the first place) for all of the valid
> > revisions of it.  The OS and the platform firmware both follow the
> > same spec and so they should be mutually compatible.  If this
> > particular spec defines rev 5 to be an exact superset of rev 4,
> > there is no reason to expect anything else.
>
> I don't *expect* rev 5 to be different.  But as a user of it, why
> would I change working, tested code that is not broken?
>
> The PCI DPC function 0xc is an example where rev 5 (per ECN) and rev 6
> (per r3.3) are not compatible.
>
> If the OS implemented rev 5, then learns via function 0 that function
> 0xc is also supported at rev 6, and starts using the same OS code with
> rev 6, the OS is broken.

Yes, in this case the backward compatibility language in the _DSM
definition is obviously not followed.
diff mbox series

Patch

diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index ebd49e43457e..04149f037664 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -1499,6 +1499,59 @@  int pci_acpi_request_d3cold_aux_power(struct pci_dev *dev, u32 requested_power)
 }
 EXPORT_SYMBOL(pci_acpi_request_d3cold_aux_power);
 
+/**
+ * pci_acpi_add_perst_assertion_delay - Request PERST# delay via ACPI DSM
+ * @dev: PCI device instance
+ * @delay_us: Requested delay_us
+ *
+ * This function sends a request to the host BIOS via ACPI _DSM to grant the
+ * required PERST# delay for the specified PCI device. It evaluates the _DSM
+ * to request the PERST# delay and handles the response accordingly.
+ *
+ * Return: returns 0 on success and errno on failure.
+ */
+int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us)
+{
+	union acpi_object in_obj = {
+		.integer.type = ACPI_TYPE_INTEGER,
+		.integer.value = delay_us,
+	};
+
+	union acpi_object *out_obj;
+	acpi_handle handle;
+	int result, ret = -EINVAL;
+
+	if (!dev || !ACPI_HANDLE(&dev->dev))
+		return -EINVAL;
+
+	handle = ACPI_HANDLE(&dev->dev);
+
+	out_obj = acpi_evaluate_dsm_typed(handle, &pci_acpi_dsm_guid, 4,
+					  DSM_PCI_PERST_ASSERTION_DELAY,
+					  &in_obj, ACPI_TYPE_INTEGER);
+	if (!out_obj)
+		return -EINVAL;
+
+	result = out_obj->integer.value;
+
+	if (result == delay_us) {
+		dev_info(&dev->dev, "PERST# Assertion Delay set to"
+			 "%u microseconds\n", delay_us);
+		ret = 0;
+	} else if (result == 0) {
+		dev_warn(&dev->dev, "PERST# Assertion Delay request failed,"
+			 "no previous valid request\n");
+	} else {
+		dev_warn(&dev->dev,
+			 "PERST# Assertion Delay request failed"
+			 "Previous valid delay: %u microseconds\n", result);
+	}
+
+	ACPI_FREE(out_obj);
+	return ret;
+}
+EXPORT_SYMBOL(pci_acpi_add_perst_assertion_delay);
+
 static void pci_acpi_set_external_facing(struct pci_dev *dev)
 {
 	u8 val;
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index dbc4b0ed433c..4b7373f91a9a 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -122,6 +122,7 @@  extern const guid_t pci_acpi_dsm_guid;
 #define DSM_PCI_POWER_ON_RESET_DELAY		0x08
 #define DSM_PCI_DEVICE_READINESS_DURATIONS	0x09
 #define DSM_PCI_D3COLD_AUX_POWER_LIMIT		0x0A
+#define DSM_PCI_PERST_ASSERTION_DELAY		0x0B
 
 #ifdef CONFIG_PCIE_EDR
 void pci_acpi_add_edr_notifier(struct pci_dev *pdev);
@@ -134,6 +135,7 @@  static inline void pci_acpi_remove_edr_notifier(struct pci_dev *pdev) { }
 int pci_acpi_set_companion_lookup_hook(struct acpi_device *(*func)(struct pci_dev *));
 void pci_acpi_clear_companion_lookup_hook(void);
 int pci_acpi_request_d3cold_aux_power(struct pci_dev *dev, u32 requested_power);
+int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us);
 
 #else	/* CONFIG_ACPI */
 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
@@ -142,6 +144,11 @@  int pci_acpi_request_d3cold_aux_power(struct pci_dev *dev, u32 requested_power)
 {
 	return -EOPNOTSUPP;
 }
+
+int pci_acpi_add_perst_assertion_delay(struct pci_dev *dev, u32 delay_us)
+{
+	return -EOPNOTSUPP;
+}
 #endif	/* CONFIG_ACPI */
 
 #endif	/* _PCI_ACPI_H_ */