Page MenuHomeFreeBSD

acpi_spmc: Add SPMC (system power management controller) driver
Needs ReviewPublic

Authored by obiwac on Jan 9 2025, 12:15 AM.
Tags
None
Referenced Files
F143073164: D48387.id170298.diff
Sun, Jan 25, 3:57 PM
F143068047: D48387.id170306.diff
Sun, Jan 25, 2:56 PM
F143057481: D48387.id170298.diff
Sun, Jan 25, 1:24 PM
F143056025: D48387.id170306.diff
Sun, Jan 25, 1:11 PM
F143054389: D48387.id170336.diff
Sun, Jan 25, 12:57 PM
F143051279: D48387.id170308.diff
Sun, Jan 25, 12:29 PM
F143033784: D48387.id170308.diff
Sun, Jan 25, 9:54 AM
F143003274: D48387.id170336.diff
Sun, Jan 25, 5:23 AM

Details

Reviewers
jkim
jhb
imp
olce
Summary

Add SPMC (system power management controller) driver as acpi_spmc. This is the device which provides the LPI device D-state constraints and allows for OSPM to send S0ix/modern standby entry/exit notifications. This supports the original Intel DSM (https://uefi.org/sites/default/files/resources/Intel_ACPI_Low_Power_S0_Idle.pdf, untested), the AMD DSM (tested), and the Microsoft DSM (partially tested, getting constraints seems to only work for the AMD DSM on my machine).

For entry/exit, all the notifications supported by the platform are called. I'm not sure of which ones are necessary and on which systems exactly, but my system needs at least one of the AMD or Microsoft regular entry notifications and the Microsoft modern standby one. I don't think it's necessarily an issue to do this, and Linux adopts the same strategy.

Before entry, acpi_spmc_check_constraints is called to notify of any violated power constraints. This will use acpi_pwr_get_state added in D48386 to get current device D-states, but the code for doing that is currently disabled because the D-state code changes had to be backed out.

This is a prerequisite to s2idle on AMD, as the EC (which we can't mask out the GPE's of because it notifies us of important wake events, such as the lid opening or the power button being pressed) is very noisy until the entry notifications are called: https://patchwork.kernel.org/project/linux-pm/patch/2279758.LZ0rCGQtcH@aspire.rjw.lan/ It still is very noisy (albeit less so) but hopefully that's something a driver for the AMD PMC will solve.

This is sort of a parallel to @bwidawsk 's D17676 revision, which went with device_post_suspend/resume device methods to call SPMC's entry/exit functions. Since we'll probably also need an AMD-specific PMC that runs after device_post_suspend in the near future, I decided against doing it this way and instead simply added acpi_spmc_enter/exit function pointers on the ACPI softc which acpi_spmc sets in probe to be called in a future patch before idling in acpi_EnterSleepState.

Another solution (what I believe Linux does after a cursory check) is to make acpi_spmc responsible for calling the entry/exit functions for any other PMC's there may be. My concern for doing this is if a system has an AMD PMC but no SPMC, if such a system exists.

Sponsored by: The FreeBSD Foundation

Test Plan

I have tested this on the Framework 13 AMD Ryzen 7040 series. I am able to get device constraints correctly and enter/exit from modern standby when idling the CPU and the device constraints are respected. Other devices (especially Intel-based) will need testing.

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 69065
Build 65948: arc lint + arc unit

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
sys/dev/acpica/acpi_spmc.c
117–118

This sounds like a good idea and a cleaner solution indeed. is the itf in dsm_itf as in interface?

for the revisions - I will change this to just use 0 on all platforms at the moment, and, when I get around to testing on intel, I can fix this if this needs fixing.

528–529

yeah you're right, I have all the correct ordering in a subsequent local commit which I hadn't squashed and updated this revision with yet.

538–547

I think this is to be expected

sys/dev/acpica/acpi_spmc.c
117–118

This sounds like a good idea and a cleaner solution indeed.

Just to be clear, you don't *absolutely* need to do it before commit, especially if you have already done a lot of tests, but if you are going to do more of them, then yes, would be nice to do that.

We should also probably add some knobs to control part of the process to ease testing and feedback if people have problems, but that can be done later while the code is in the tree (e.g., rule out some DSM, tweak the ordering, change the revisions, etc.).

is the itf in dsm_itf as in interface?

Yes. That was just an example name, I'm sure we can find a better one.

for the revisions - I will change this to just use 0 on all platforms at the moment, and, when I get around to testing on intel, I can fix this if this needs fixing.

That's the cleanest approach I think.

538–547

I'm not so sure, but if that works, then it's likely the ordering just doesn't matter (at least for now on tested machines). A debug knob tweaking that might be useful (for later).

obiwac marked 3 inline comments as done.

Remove rev_for_uuid for now and just use revision 0 everywhere.

olce requested changes to this revision.Mon, Jan 12, 8:57 PM

I'm pretty new to device management in FreeBSD, but it looks to me that allocating some memory + acpi_set_private() in DEVICE_PROBE() (here, acpi_spmc_probe()) and acpi_get_private() + deallocation in DEVICE_ATTACH() is a bad pattern, as it will cause memory leaks and in fact even panics in most cases if there are multiple candidate drivers. Since there is for now only a single driver, could you please instead fill up the acpi_spmc_softc directly and have acpi_spmc_probe() return 0 on success (which guarantees preservation of the softc up to DEVICE_ATTACH())? Let's also avoid possible bad pattern spreading by copy-pasting (incidentally, I see the same pattern in acpi_ec.c).

I'll finish the review tomorrow morning (didn't have time to check some stuff).

sys/dev/acpica/acpi_spmc.c
158–159

private can't be NULL on allocation with M_WAITOK.

257–263

Please check here that the revision is indeed zero, and just ignore the constraint if it is not (format unknown). You'll have to update sc->constraint_count to that end (tweaking the malloc() is most probably not worth the trouble).

359–368

Not too sure about the ordering here. If both AMD and MS are supported, wouldn't it be better to use MS as I assume it respects the spec? Did you do specific tests around these?

Err, in fact, looking at Microsoft's document, constraints are simply not supported. I'd just drop the DSM_SET_MS case here, unless you have experimental evidence that these are supported?

As a consequence, and contrarily to what I said in the first paragraph above, if you prefer to still keep the MS case, it may be better to use it only as a last resort.

374

(result.Pointer == NULL here is probably redundant, but that's fine.)

375–376

I'd print AMD/Intel/MS so that we immediately know which exact DSM failed.

428

Since you added #ifdef notyet, which kind of problems did you stumble on? E.g., are there cases where the message is printed but suspend works? Is this linked to not matching lpi_uid?

Ideally, we should do this match, at least to be able to print which deeper LPIT state we will not be able to enter, since that does not preclude from entering shallower ones. If some constraint is not fulfilled even for the shallower state, we might want to simple abort. These are just ideas, nothing to fix here at this point.

460
  • Style (please wrap comment line).
  • Possibly relax the check (failure controlled only by status), and just call AcpiOsFree() on result.Pointer if non-NULL. That would be slightly different than Linux's way but a priori seems to make more sense.
528–529

Exit notifications should be in reverse order compared to entry ones as hinted in Microsoft's document, in particular the diagram and the fact that DSM_EXIT_NOTIF is supposed to indicate exit from lowest power state (possibly transient indication) whereas DSM_MODERN_EXIT_NOTIF is a global exit from "suspend".

This revision now requires changes to proceed.Mon, Jan 12, 8:57 PM
obiwac added inline comments.
sys/dev/acpica/acpi_spmc.c
359–368

This was a while ago for me now but as I recall the AMD get device constraints DSM was necessary and the MS one didn't have this. I have no idea what the situation is like on Intel though so in the meantime I defaulted to the MS one.

428

The #ifdef notyet is because the necessary commits for acpi_pwr_get_state had to be reverted, as they were the D-state changes that broke older machines with working S3. I will add this function back at some point, but not a super high priority as this is just an extra debugging check which doesn't seem to affect S0i3 entry (at least on AMD machines).

We should also avoid the ugly pollution of struct acpi_softc (I completely understand this was done to get something working more quickly). Rather, let's define a new interface, called something like acpi_lpi (defined in a new acpi_lpi_if.m file), with two methods, acpi_lpi_enter() and acpi_lpi_exit(), which would be called before/after entering suspend-to-idle in acpi_EnterSleepState() (e.g., on all direct children of acpi; we'll see that in D48735). There are examples of interfaces under sys/dev/acpica, acpi_if.m and acpi_bus_if.m. The .m files are processed by sys/tools/makeobjops.awk. There's a bit more doc on kobj(9) (which device(9) relies on) in the Architecture Handbook.

That doesn't seem much complicated, but could be done as well as a separate revision.

sys/dev/acpica/acpi_spmc.c
359–368

That makes sense. But since their doc doesn't mention any function to get constraints, I still think we should put it last at least (and remove/disable it later entirely depending on testing feedback; it would be great to print if the MS DSM is used).

428

Yes, that doesn't affect functionality (on systems where S0i3 indeed works), so there's no urgency.

That said, this is a needed piece for debugging when S0i3 is not entered or if it is but in some degraded fashion that still consumes an unexpected amount of power.

In general, for power and suspend issues, I think we'll never be able to test things on enough hardware, so we'd better prepare for information gathering and steps tweaking in the field to be able to actually diagnose and even bypass problems.

It seems a good idea to keep the constraints parsing code enabled even if it is functionally completely useless as long as the #ifdef notyet stays, so that we can already have feedbacks on how it fares.

obiwac marked 5 inline comments as done.

Fix issues pointed out by olce@ and apply some suggestions. Main changes:

  • Change DSM order again to match Windows & Linux.
  • Hold DSM set info in new struct dsm_set, so we can hold revision ID and DSM name.
  • Print out DSM name & revision.
  • Tunable to change Intel DSM set revision ID to use for debugging.

fixed most of the issues highlighted. Will address the allocating-memory-in-probe-function pattern and add an interface for late suspend/early resume (or maybe call this something like pmc_enter and pmc_exit? then again maybe on some platforms the PMC isn't necessarily intended to be called after device suspend) in the future.

Let's also avoid possible bad pattern spreading by copy-pasting (incidentally, I see the same pattern in acpi_ec.c).

that's probably where I got this pattern from in fact. I will update acpi_ec.c to fix this later on

sys/dev/acpica/acpi_spmc.c
460

Actually I need to check if acpi_EvaluateDSMTyped() is already always freeing the result pointer if returning an error status. other uses of this function in FreeBSD don't do this, so those would need to be fixed too if not

497–502

updated this because I was told the exact ordering was important, at least for AMD. Not sure yet how true this is for Intel, but might aswell

528–529

Which diagram are you referring to actually? I don't think I've seen that one.

You're right, and in fact in order to be reflective of what Windows and Linux as of f198478 does, we should even be calling the AMD DSM first.

obiwac marked an inline comment as done.

Remove SPMC callbacks on struct acpi_softc. I will replace them with new pmc_suspend and pmc_resume methods in another revision. This leaves the acpi_spmc_suspend and acpi_spmc_resume (renamed from acpi_spmc_enter/exit) functions unused at the moment. Lmk if this is okay or if you'd rather I add these in a different revision too.

Fix issues pointed out by olce@ and apply some suggestions. Main changes:

  • Change DSM order again to match Windows & Linux.
  • Hold DSM set info in new struct dsm_set, so we can hold revision ID and DSM name.
  • Print out DSM name & revision.
  • Tunable to change Intel DSM set revision ID to use for debugging.

Great! Thanks.

fixed most of the issues highlighted. Will address the allocating-memory-in-probe-function pattern and add an interface for late suspend/early resume (or maybe call this something like pmc_enter and pmc_exit? then again maybe on some platforms the PMC isn't necessarily intended to be called after device suspend) in the future.

Let's also avoid possible bad pattern spreading by copy-pasting (incidentally, I see the same pattern in acpi_ec.c).

that's probably where I got this pattern from in fact. I will update acpi_ec.c to fix this later on

And thanks again.

Lmk if this is okay or if you'd rather I add these in a different revision too.

That's fine.

sys/dev/acpica/acpi_spmc.c
263–264

I'd add some comment before the loop saying that sc->constraint_count can be modified inside the loop (hence using object->Package.Count as a guard).

484–488

Comment stale now, can be removed too.

obiwac marked an inline comment as done.

Remove struct acpi_spmc_private and just set DSM set flags directly on softc + style(9) improvements.

Comment about changing sc->constraint_count and more style(9) improvements.

obiwac marked 3 inline comments as done.

Don't ever use Microsoft DSM UUID for getting constraints, as this isn't supported (checked on Intel acpidump).

obiwac added inline comments.
sys/dev/acpica/acpi_spmc.c
359–368

I just removed it entirely because I can confirm that on Intel the Microsoft DSM UUID doesn't contain that function anyway. See:

https://raw.githubusercontent.com/linuxhw/ACPI/refs/heads/master/Notebook/Framework/Laptop/Laptop%2013/717EDB7C4975

and search for 11e00d56-ce64-47ce-837b-1f898f9aa461.

460

it is

Make hw.acpi.spmc.intel_dsm_revision sysctl operate directly on Intel DSM set struct.

olce requested changes to this revision.Fri, Jan 23, 5:44 PM

Almost there! One bug (BUS_PROBE_DEFAULT) and one strongly suggested change (potential bug) around sc->dsm_sets |= DSM_SET_MS;, the rest is mostly cosmetic.

Also, there are occurrences of a space followed by a TAB, could you replace the space by a TAB there?

sys/dev/acpica/acpi_spmc.c
170

I'd use a symbolic function index here. I've put this one as an example, as it is 5 instead of 1 and a more crucial function (after all, we don't really care if DSM_GET_DEVICE_CONSTRAINTS (1) is not available, we could function without it).

172

The Microsoft's spec does not mention functions 1 & 2, so they could as well be absent, and we must avoid them.

I'd use something like DSM_MODERN_ENTRY_NOTIF (7), instead of 1.

174

Same reasoning as above.

183

You've set dsm_sets on the softc and expect it to survive afterwards, so you need to force attach (or duplicate the sets logic in acpi_spmc_attach()).

296–300

I'd keep the previous alignment here (letting just struct acpi_spmc_constraint *constraint be the odd man out), but up to you.

365

Same alignment remark as above.

376–379

I'd move this comment just before is_amd, suppressing the ", so use the Intel one." part.

This revision now requires changes to proceed.Fri, Jan 23, 5:44 PM
sys/dev/acpica/acpi_spmc.c
170

actually this is checking for enum functions (1 means bit 0 not DSM #1). I think this is the canonical way of checking if a DSM exists at all.

although we should probably (and might aswell) check that all the functions we necessarily need from a given DSM set are available here. Will do that in a bit

183

thanks, this was a misunderstanding of how this worked on my end :)

296–300

i tend to agree but i was previously told that style advised against this now and I do see in style(9) they don't align this

sys/dev/acpica/acpi_spmc.c
170

Ha ha... You're right. Too much wine at lunch :-p... Sorry for the noise.

Checking for the necessary functions would be great (and ideally printing some message if a DSM is present but does not have the expected function), up to you.

296–300

Yeah, style(9) does not require it, and effectively it's not done in the few examples there, but it does not explicitly discourage it either. (More of a note for myself to ask and possibly amend style(9).) Anyway, I do not insist.

obiwac marked 3 inline comments as done.
  • Add dsms_needed mask to each DSM set definition to check the result of enum functions (acpi_DSMQuery()) against.
  • Default AMD DSM set revision ID to 2 as my ASL suggests it should be.
  • Add debug.acpi.spmc.amd_dsm_revision sysctl so we can easily test emulating Linux's behaviour of using revision ID 0 (probably won't make a different irl - at least my ASL doesn't ever check for revision ID in DSM implementations).
  • Return 0 in probe instead of BUS_PROBE_DEFAULT.
  • Fix style/comment placement.
obiwac added inline comments.
sys/dev/acpica/acpi_spmc.c
170

in the meantime I made it not accept the DSM set if it doesn't have all of the expected DSM functions. Maybe i should accept it so long as enum functions exists, and warn if not all the expected DSMs are available. I don't think anything bad can happen if you call a DSM ID that doesn't really exist.

296–300

i will align it because it bothers me too to be honest :)

obiwac marked an inline comment as done.

acpi_spmc_check_dsm_set() function which enables DSM set so long as enum functions DSM is supported (bit 1), but warns if incomplete vs what we expect.

Align declarations.

I think we're completely done with this!!! I do just need to test this on my machine again since the most recent changes.

Newline after DSM set unexpected enum functions warning messages + fix DSM revision sysctl default values.