Page MenuHomeFreeBSD

hmp(4): introduce Heterogeneous MultiProcessing support
Needs RevisionPublic

Authored by mchoo on Apr 21 2026, 10:21 AM.
Tags
None
Referenced Files
F165676074: D56546.id176224.diff
Sun, Aug 9, 4:25 AM
F165676065: D56546.id177987.diff
Sun, Aug 9, 4:25 AM
Unknown Object (File)
Sat, Aug 8, 12:39 PM
Unknown Object (File)
Sat, Aug 8, 11:35 AM
Unknown Object (File)
Sat, Aug 8, 10:47 AM
Unknown Object (File)
Sat, Aug 8, 10:43 AM
Unknown Object (File)
Sat, Aug 8, 9:20 AM
Unknown Object (File)
Fri, Aug 7, 8:21 PM

Details

Reviewers
imp
olce
adrian
Summary

Add initial support for HMP.

The hmp(4) framework sits between scheduler and providers. It aims to
forward information from provider to scheduler to enable hybrid
scheduling.

Support for controlling hardware from scheduler (e.g. Arm SCMI) will be
added later.

For now, disable this option by default and enable when it becomes
stable enough.

Sponsored by: FreeBSD Foundation

Signed-off-by: Minsoo Choo <minsoochoo0122@proton.me>

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 73753
Build 70636: arc lint + arc unit

Event Timeline

Changes from D56547:

  • Moved from struct pcpu to DPCPU so we can avoid options HMP on pcpu.
  • Reworked scores system. Since scheduler only cares about perf and eff scores, we don't need other capabilities.
  • Capacities don't need atomic operation since it is written once on boot.
  • Added sysctls.

@koinec_yahoo.co.jp Could you please work on intelhfi based on this patch series?

Thank you very much for improving the HMP code and creating the manual.

Understood. I will proceed with supporting this version.

  • Remove dynamic and throttle flags as they are not needed anymore
  • Add hmp_lowest_capacity_cpu()
In D56546#1306730, @minsoochoo0122_proton.me wrote:
  • Remove dynamic and throttle flags as they are not needed anymore

Thank you very much for the improvements.
Is it possible to retain the flag field and the definition of the "Throttled" flag?

If the intention behind removing it is that it might not be immediately used in the ULE scheduler, I understand the reasoning. I am currently learning about the ULE scheduler while testing intelhfi. Since ULE focuses on distributing loads to the lowest-load CPUs while remaining cache-aware, I agree that the Throttled flag isn't strictly necessary for ULE itself.

However, I view HMP(4) as a forward-looking mechanism designed to share scores across heterogeneous multicore architectures, rather than something optimized solely for the ULE scheduler. Also, considering that the Intel Hardware Feedback Interface defines a "Throttled" state, I assume this flag was initially introduced with Arm architecture development in mind.
Therefore, notification of the "Throttled" state could become essential in the future for managing heterogeneous multicore environments. For this reason, I would like to request keeping the Throttled state configurable rather than removing it.

On the other hand, since "Capacity" represents a static capability, dynamic updates would be handled by a Provider that sets the "Score." Thus, I agree that the "Dynamic" flag can be safely removed.

Additionally, the Intel Hardware Feedback Interface defines a state for notifying power-efficient cores (such as LP-E cores) to aggregate all tasks. Specifically, when the HFI table's Efficiency score reaches its maximum value of 255, it requests aggregating all tasks to that specific core to maximize battery efficiency. This is conceptually the opposite of the "Throttled" state.
To support this behavior, would it be possible to add a "Consolidated" flag to the flag field?

In D56546#1306730, @minsoochoo0122_proton.me wrote:
  • Remove dynamic and throttle flags as they are not needed anymore

Thank you very much for the improvements.
Is it possible to retain the flag field and the definition of the "Throttled" flag?

If the intention behind removing it is that it might not be immediately used in the ULE scheduler, I understand the reasoning. I am currently learning about the ULE scheduler while testing intelhfi. Since ULE focuses on distributing loads to the lowest-load CPUs while remaining cache-aware, I agree that the Throttled flag isn't strictly necessary for ULE itself.

However, I view HMP(4) as a forward-looking mechanism designed to share scores across heterogeneous multicore architectures, rather than something optimized solely for the ULE scheduler. Also, considering that the Intel Hardware Feedback Interface defines a "Throttled" state, I assume this flag was initially introduced with Arm architecture development in mind.
Therefore, notification of the "Throttled" state could become essential in the future for managing heterogeneous multicore environments. For this reason, I would like to request keeping the Throttled state configurable rather than removing it.

Currently the sole goal for hmp(4) is integrating with the ULE scheduler (or any other future scheduler). When I added the throttled flag, I thought it would be useful for thread placement so the scheduler can avoid placing thread to throttled core. But this comes with two drawbacks:

  1. Assume we have two core arm64 board and buildworld (or do any heavy work) on it. Then both cores are likely to be flagged as throttled. The scheduler won't assign thread to ether core, but that's impossible. Otherwise, the scheduler will conclude that all cores are throttled and place the thread at a random core, but then we wasted O(n) time to make that conclusion. (O(n) for 2c board won't be a huge issue, but imagine this is happening to a 256c server)
  2. Throttled should be a part of thermal subsystem like cpufreq. For Arm specifically, I believe writing cpufreq_scmi driver would be better for handling throttled flag. You might ask what if we control the cores through hmp(4), but then we have two subsystems (cpufreq, hmp) for same work which introduces another layer of uncertainty.

Additionally, the Intel Hardware Feedback Interface defines a state for notifying power-efficient cores (such as LP-E cores) to aggregate all tasks. Specifically, when the HFI table's Efficiency score reaches its maximum value of 255, it requests aggregating all tasks to that specific core to maximize battery efficiency. This is conceptually the opposite of the "Throttled" state.
To support this behavior, would it be possible to add a "Consolidated" flag to the flag field?

hmp(4) subsystem works like a greatest common divisor. In other words, it only abstracts what seems to be common across many providers. But this seems to be intelhfi specific.

Moving a bulk of tasks at a time is generally avoided because thread replacement without reason is bad (e.g. cache miss, TLB miss, etc). And even without making a bulk replacement, if a core's efficiency score is high enough, the tasks should be balanced gradually over time according to the algorithm, so I don't see any reason why the aggregation needs to happen at the same time. But as you said, if it's good for battery efficiency, it's worth discussing with people interested in schedulers and laptop projects.

Thank you very much for the explanation. I understand it much better now.

I understand that "throttled" should indeed be handled by the thermal management mechanism and that it also affects performance.
Also, regarding "Consolidated" flag as you pointed out, I also recognize it as a feature specific to Intel HFI.

Based on your feedback, I have uploaded an intelhfi patch with the HMP flags removed. I would appreciate it if you could check it.

  • total_capacity should have type hmp_capacity_t
  • hmp_capacity_t is now uint32_t since total_capacity will overflow when there are 64 or more cores. hmp_score_t has also changed to uint32_t to reflect the change.
sys/sys/hmp.h
73

Is this type "hmp_capacity_t" ?

Fix missing hmp_capacity_t separation

(This may not be the appropriate place to post this, but please forgive me as I don't know of any other suitable location.)

As part of testing intelhfi(4), I attempted to modify sched_ule(4) to utilize hmp(4), and I was able to achieve a performance improvement of up to +10-15% in some cases. I plan to post the diff later.
However, incorporating this modification requires changes to the hmp(4) side as well. Specifically, I want to modify the following:

  • Capacity: I want to change the meaning of the value. I want the slowest core's capacity value to be 1024, and the other cores' capacity value to be 1024 divided by their performance ratio to the slowest core. For example, a core twice as fast would have a score of 512.
  • Score: I want to set a range of 0-255. (I understand that the range is not clearly defined, even in the manual. I want to clarify this.) A higher value simply means faster, and unlike capacity value, this should be an absolute value, not a performance ratio.
  • I want the search for the highest/lowest capacity value for each core to be performed by sched_ule(4), and I want to avoid using Scheduler Helper Functions in hmp(4). (Sorry, but I want to remove these functions.) This is because the existing functions in sched_ule(4) already explore the CPU topology and search for load.
  • Due to the above change in the meaning of the capacity value, displaying percentages in hmp(4) becomes difficult. Therefore, I want to remove this function. Sorry, too.

I apologize for suggesting that this would involve removing part of the wonderful patch you have so kindly created. Would you be able to forgive me?

All of the above modifications are synchronized with sched_ule(4), so I have also created a patch for hmp(4) addressing the above points. Therefore, I would like to post it together with this update.

Finally, I am currently dealing with the above post and the advice from intelhfi(4) in response to it, so please understand that it will take me a few weeks to complete everything.

(This may not be the appropriate place to post this, but please forgive me as I don't know of any other suitable location.)

You are always welcome to share hmp-related stuff here.

As part of testing intelhfi(4), I attempted to modify sched_ule(4) to utilize hmp(4), and I was able to achieve a performance improvement of up to +10-15% in some cases. I plan to post the diff later.

Thank you very much! I'm currently in school so I can't spend too much time working on this..... But if you are willing to help, I appreciate a lot:) Please feel free to take some time for posting the diff if you need to do so.

However, incorporating this modification requires changes to the hmp(4) side as well. Specifically, I want to modify the following:

  • Capacity: I want to change the meaning of the value. I want the slowest core's capacity value to be 1024, and the other cores' capacity value to be 1024 divided by their performance ratio to the slowest core. For example, a core twice as fast would have a score of 512.

The concept of capacity is actually from hardware and ACPI people, not from me. At least on device tree, it accurately represents the maximum wordload a processor can do (work done per Hz * maximum frequency). However, if the diff tells that there is some compelling reasons to do so, I'm okay with it.

  • Score: I want to set a range of 0-255. (I understand that the range is not clearly defined, even in the manual. I want to clarify this.) A higher value simply means faster, and unlike capacity value, this should be an absolute value, not a performance ratio.

amdhfi uses u32 for perf/eff scores. I can check the rationale behind this in your ULE patch.

  • I want the search for the highest/lowest capacity value for each core to be performed by sched_ule(4), and I want to avoid using Scheduler Helper Functions in hmp(4). (Sorry, but I want to remove these functions.) This is because the existing functions in sched_ule(4) already explore the CPU topology and search for load.

This was something I was worrying about in the design phase. I think the diff will clarify it.

  • Due to the above change in the meaning of the capacity value, displaying percentages in hmp(4) becomes difficult. Therefore, I want to remove this function. Sorry, too.

If that's what we should do, I'm happy to do so.

I apologize for suggesting that this would involve removing part of the wonderful patch you have so kindly created. Would you be able to forgive me?

Of course! Thank you for dedicating your time on this:)

All of the above modifications are synchronized with sched_ule(4), so I have also created a patch for hmp(4) addressing the above points. Therefore, I would like to post it together with this update.

Please don't overwrite this revision, but instead create a new one with hmp modification and add that as its child revision.

Finally, I am currently dealing with the above post and the advice from intelhfi(4) in response to it, so please understand that it will take me a few weeks to complete everything.

No worries.

olce requested changes to this revision.Sun, Jul 26, 1:49 PM

You should probably merge this revision with D56547. I have reviewed the merged result of this one, that one and D57939 (Koine Yuusuke's). Inline comments have been put in the relevant reviews as much as possible, but general comments are all listed below.

The current name, Heterogeneous MultiProcessing, looks misleading for multiple reasons. This is a framework, without drivers nor scheduler support, and by itself does not make any real user-visible difference, although the role it fulfills is certainly necessary. The current schedulers do work on heterogeneous cores, it's just that they will make poor decision by lack of knowledge (this is different, e.g., from the SMP option, that one actually enabling running on multiple cores; "SMP" itself could be seen as a misnomer, as it supports different cores provided they report the same functional capabilities (but not necessarily the same performance); the choice of "SMP" at the time it was introduced is understandable, probably there was nothing else on the horizon back then). As you said yourself at the EuroBSDCon presentation, its usefulness goes beyond heterogeneous multiprocessing: Even when all cores are the same, it's useful to have hardware feedback on which core to schedule preferentially because some of them are throttled for thermal (and perhaps other) reasons. Finally, the fate of hybrid general purpose CPU looks uncertain, and Intel has officially announced its intention to stop producing this kind of packages. So I would think about another, more generic name, even if less "catchy". Haven't given much thought to that, off the top of my head: Hardware Data for Scheduling (HDS), which feels much more to the point with respect to what this framework really is. Proposals welcome.

A priori, I would not enable the framework by default in GENERIC at the moment, that will depend a lot on how the reviews and testing go, but ultimately that's certainly the goal. By contrast, I'd put it in LINT right now.

I think we should aim for a linear range of possible capacity values, with finer-grain granularity. Using the inverse of a 0-1024 range with numerator 1024 for the performance factor gives ~0.1% precision per increment near 1024, but already ~0.5% around 512 (factor 2), ~1.5% around 256 (factor 4), and already ~6.5% around 128 (factor 8). Let's make things more uniform and future proof by just using a 64-bit integer instead (still a separate type, hmp_capacity_t) whose value's meaning completely depends on the capacity provider, which is free to treat this number as fixed point instead if need be (in a similar fashion as we do with fixpt_t, but the precise scale to be used would be provider-dependent, instead of the fixed FSCALE (2^11)); I expect frequencies could be stored in there by some providers (see below).

There is generally a lot of redundancy in information exported via sysctl knobs, and whether that brings much value is unclear. Also, I'd organize the tree differently, and clarify some of the exported data. All this is detailed in inline comments.

The functions selecting highest/lowest capacity or the best CPU are premature. ULE's policy to select CPU is to follow the highest load on the highest loaded path, or conversely the lowest load on the lowest loaded path, and those functions require more fine-grained information than the functions here can provide. In passing, as long as capacities are unchanging, having O(n) algorithms in the scheduler would be hard to accept when there are better options. Some Koine's later revision removes them, that should be merged here.

I'd like that we keep in mind that, even if it doesn't need to be the case right now (although it would likely help debug, see below), this framework should support adding/removing providers at runtime, or changing their parameters. Having atomic accessors is not enough for this to happen. The easiest way to do it, while retaining some abstraction, seems to be to have a transactional-like API, where basically you'd call some kind of prepare() function, which allocates an opaque structure on which you then call all necessary accessors, and then commit the new structure with a commit() function. The difficulty here is that the values are spread out into per-CPU structures, so a more clever strategy has to be used to have atomicity across all of them. The first coming to mind would be to have two instances of struct hmp_pcpu per-CPU, and a global index in a global variable that indicates which structure to use. prepare() would possibly initialize all the not-currently-used struct hmp_pcpu structures, setters would modify them and commit() would just update the global index. For maximal robustness, the actual scheme probably should also prevent ABA problems, if for some reason hardware updates are too fast, so what is described above is only a sketch (not going to detail it for now; you can think about what that entails and we can discuss that later).

Last (for now) but not least, I'd like to point out that you're considering capacity as fixed (as the maximum capacity), but it may not be the most appropriate design. (If we were to keep it, I'd rename capacity into the more explicit maximum_capacity.) The actual capacity itself is not fixed and varies over time, depending among others on power (through P-states/frequency control or CPPC) and thermal settings/conditions, and it is this number which ultimately matters for schedulers to make decisions. We should keep bridging the frequency control/CPPC information in mind when computing the current capacity. Having the schedulers factor in these things into the equation themselves looks quite unwise. Consequently, we need a way for performance-controlling drivers (such as hwpstate_amd(4) and hwpstate_intel(4)) to change the capacity. Whether applying a multiplier to preexisting fixed maximum capacities is the most appealing mechanism remains to be seen. If, e.g., we intend to use CPU CPPC, as Koine is already trying to do in intelhfi(4), then it seems we are going to be able to immediately get the current capacity without having to compute it from a maximum capacity (and actually, I don't see where maximum capacities would come from on amd64 platforms, other than trying to compute them with some dummy workload?). I only gave a small peek at Arm PSCA/SCMI, it looks like it reports CPPC information (it may report other ones, I haven't dug enough to know). Same if we are only considering processor frequencies (when all cores are the same), we would get immediately the current capacity, and computing a multiplier to the maximum capacity appears as an unnecessary detour.

Stopping here, as there is already a lot to process, and I'll be mostly AFK for a long while (~4 weeks). Feel free to ask for more details, to respond to exposed points, or perhaps raise other concerns or questions.

sys/kern/subr_hmp.c
1–8

SPDX goes just after Copyright, or before. I generally treat authorship sentences as not being part of the copyrights, but am not completely sure about the proper practice here.

We usually add a mail address after the name (no obligation AFAIK; can be added later).

17–21

total_capacity is removed in a later revision, and has_scores is redundant and not used for anything else than the kern.hmp.has_scores knob. I'd just remove struct hmp_state completely.

183–187

After suppressing has_scores, you can change this to a "proc" that just returns the result of hmp_active_score_provider != NULL.

sys/sys/hmp.h
89

There must be parentheses around the expression of each return statement (style(9)) (not repeating that for below occurrences).

This revision now requires changes to proceed.Sun, Jul 26, 1:49 PM