Page MenuHomeFreeBSD

D56547.diff
No OneTemporary

D56547.diff

diff --git a/sys/kern/subr_hmp.c b/sys/kern/subr_hmp.c
--- a/sys/kern/subr_hmp.c
+++ b/sys/kern/subr_hmp.c
@@ -23,14 +23,164 @@
/* Per-CPU HMP state */
DPCPU_DEFINE(struct hmp_pcpu, hmp_pcpu);
+/*
+ * Provider interfaces
+ *
+ * Capacity and score providers live on independent lists and are selected
+ * independently at boot. Selection runs in two phases (see hmp_init):
+ * capacity first, then scores.
+ */
+static SLIST_HEAD(, hmp_capacity_provider) hmp_cap_providers =
+ SLIST_HEAD_INITIALIZER(hmp_cap_providers);
+static SLIST_HEAD(, hmp_score_provider) hmp_score_providers =
+ SLIST_HEAD_INITIALIZER(hmp_score_providers);
+
+static struct hmp_capacity_provider *hmp_active_cap_provider;
+static struct hmp_score_provider *hmp_active_score_provider;
+
+void
+hmp_capacity_provider_register(struct hmp_capacity_provider *p)
+{
+ KASSERT(p->name != NULL, ("hmp: capacity provider missing name"));
+ KASSERT(p->probe != NULL && p->init != NULL,
+ ("hmp: capacity provider %s missing probe/init", p->name));
+
+ SLIST_INSERT_HEAD(&hmp_cap_providers, p, link);
+}
+
+void
+hmp_score_provider_register(struct hmp_score_provider *p)
+{
+ KASSERT(p->name != NULL, ("hmp: score provider missing name"));
+ KASSERT(p->probe != NULL && p->init != NULL,
+ ("hmp: score provider %s missing probe/init", p->name));
+
+ SLIST_INSERT_HEAD(&hmp_score_providers, p, link);
+}
+
+/*
+ * Default capacity provider.
+ *
+ * Always wins if nothing else probes: every CPU gets HMP_CAPACITY_DEFAULT,
+ * which collapses HMP decisions down to "any CPU is fine." There is no
+ * default score provider; absence of scores is a valid state.
+ */
+
+static int
+hmp_default_cap_probe(void)
+{
+ return (0);
+}
+
+static int
+hmp_default_cap_init(void)
+{
+ struct hmp_pcpu *hp;
+ int cpu;
+
+ CPU_FOREACH(cpu) {
+ hp = DPCPU_ID_PTR(cpu, hmp_pcpu);
+ hp->capacity = HMP_CAPACITY_DEFAULT;
+ }
+
+ return (0);
+}
+
+static struct hmp_capacity_provider hmp_default_cap_provider = {
+ .name = "default",
+ .priority = 0,
+ .probe = hmp_default_cap_probe, /* always true */
+ .init = hmp_default_cap_init,
+};
+HMP_CAPACITY_PROVIDER_DECLARE(default, hmp_default_cap_provider);
+
/*
* Initialization
*/
+static void
+hmp_set_total_capacity(void)
+{
+ struct hmp_pcpu *hp;
+ int cpu;
+
+ hmp_state.total_capacity = 0;
+
+ CPU_FOREACH(cpu) {
+ hp = DPCPU_ID_PTR(cpu, hmp_pcpu);
+ hmp_state.total_capacity += hp->capacity;
+ }
+}
+
+static void
+hmp_init_capacity(void)
+{
+ struct hmp_capacity_provider *p, *best;
+
+ SLIST_FOREACH(p, &hmp_cap_providers, link) {
+ if (p->probe() == 0)
+ p->probed = true;
+ }
+
+ for (;;) {
+ best = NULL;
+ SLIST_FOREACH(p, &hmp_cap_providers, link) {
+ if (!p->probed)
+ continue;
+ if (best == NULL || p->priority > best->priority)
+ best = p;
+ }
+ if (best == NULL)
+ break;
+ if (best->init() == 0)
+ break;
+ best->probed = false;
+ }
+
+ KASSERT(best != NULL,
+ ("hmp: no capacity provider init succeeded, not even default"));
+ best->active = true;
+ hmp_active_cap_provider = best;
+ hmp_set_total_capacity();
+}
+
+static void
+hmp_init_scores(void)
+{
+ struct hmp_score_provider *p, *best;
+
+ SLIST_FOREACH(p, &hmp_score_providers, link) {
+ if (p->probe() == 0)
+ p->probed = true;
+ }
+
+ for (;;) {
+ best = NULL;
+ SLIST_FOREACH(p, &hmp_score_providers, link) {
+ if (!p->probed)
+ continue;
+ if (best == NULL || p->priority > best->priority)
+ best = p;
+ }
+ if (best == NULL) {
+ hmp_state.has_scores = false;
+ return;
+ }
+ if (best->init() == 0)
+ break;
+ best->probed = false;
+ }
+
+ best->active = true;
+ hmp_active_score_provider = best;
+ hmp_state.has_scores = true;
+}
+
static void
hmp_init(void *arg __unused)
{
- /* Choose and call provider */
+ hmp_init_capacity();
+ hmp_init_scores();
}
SYSINIT(hmp, SI_SUB_SMP + 1, SI_ORDER_ANY, hmp_init, NULL);
@@ -175,6 +325,45 @@
return (sysctl_handle_int(oidp, &v, 0, req));
}
+static int
+hmp_sysctl_active_cap_provider(SYSCTL_HANDLER_ARGS)
+{
+ const char *name;
+
+ name = (hmp_active_cap_provider != NULL) ?
+ hmp_active_cap_provider->name : "none";
+ return (sysctl_handle_string(oidp, __DECONST(char *, name), 0, req));
+}
+
+static int
+hmp_sysctl_active_score_provider(SYSCTL_HANDLER_ARGS)
+{
+ const char *name;
+
+ name = (hmp_active_score_provider != NULL) ?
+ hmp_active_score_provider->name : "none";
+ return (sysctl_handle_string(oidp, __DECONST(char *, name), 0, req));
+}
+
+static void
+hmp_sysctl_add_provider(struct sysctl_oid_list *parent, const char *name,
+ int *priority, bool *probed, bool *active)
+{
+ struct sysctl_oid *node;
+
+ node = SYSCTL_ADD_NODE(NULL, parent, OID_AUTO, name,
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "HMP provider state");
+ if (node == NULL)
+ return;
+
+ SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node), OID_AUTO, "priority",
+ CTLFLAG_RD, priority, 0, "Provider priority (higher wins)");
+ SYSCTL_ADD_BOOL(NULL, SYSCTL_CHILDREN(node), OID_AUTO, "probed",
+ CTLFLAG_RD, probed, 0, "probe() returned true on this system");
+ SYSCTL_ADD_BOOL(NULL, SYSCTL_CHILDREN(node), OID_AUTO, "active",
+ CTLFLAG_RD, active, 0, "Selected as the active provider");
+}
+
static SYSCTL_NODE(_kern, OID_AUTO, hmp, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"Heterogeneous multi-processing state");
@@ -190,8 +379,11 @@
hmp_sysctl_init(void *arg __unused)
{
struct sysctl_oid *cpu_root, *cpu_node, *cpu_score_node;
+ struct sysctl_oid *provider_root, *cap_root, *score_root;
struct sysctl_oid_list *cpu_root_children, *cpu_children;
- struct sysctl_oid_list *cpu_score_children;
+ struct sysctl_oid_list *cpu_score_children, *provider_children;
+ struct hmp_capacity_provider *cp;
+ struct hmp_score_provider *sp;
char name[8];
int cpu;
@@ -245,5 +437,58 @@
"Current efficiency score (dynamic)");
}
}
+
+ /*
+ * Provider tree.
+ *
+ * kern.hmp.provider.active_capacity
+ * - name of the winning capacity provider (always set)
+ * kern.hmp.provider.active_score
+ * - name of the winning score provider, or "" if none
+ * kern.hmp.provider.capacity.<n>.{priority,probed,active}
+ * kern.hmp.provider.score.<n>.{priority,probed,active}
+ *
+ * Every registered provider is listed on each side, not just the
+ * winner, so operators can see which providers were available and
+ * why one was chosen over another.
+ */
+ provider_root = SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(_kern_hmp),
+ OID_AUTO, "provider", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
+ "Registered HMP providers");
+ if (provider_root == NULL)
+ return;
+ provider_children = SYSCTL_CHILDREN(provider_root);
+
+ SYSCTL_ADD_PROC(NULL, provider_children,
+ OID_AUTO, "active_capacity",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
+ hmp_sysctl_active_cap_provider, "A",
+ "Name of the active HMP capacity provider");
+
+ SYSCTL_ADD_PROC(NULL, provider_children,
+ OID_AUTO, "active_score",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
+ hmp_sysctl_active_score_provider, "A",
+ "Name of the active HMP score provider (empty if none)");
+
+ cap_root = SYSCTL_ADD_NODE(NULL, provider_children,
+ OID_AUTO, "capacity", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
+ "Registered HMP capacity providers");
+ if (cap_root != NULL) {
+ SLIST_FOREACH(cp, &hmp_cap_providers, link) {
+ hmp_sysctl_add_provider(SYSCTL_CHILDREN(cap_root),
+ cp->name, &cp->priority, &cp->probed, &cp->active);
+ }
+ }
+
+ score_root = SYSCTL_ADD_NODE(NULL, provider_children,
+ OID_AUTO, "score", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
+ "Registered HMP score providers");
+ if (score_root != NULL) {
+ SLIST_FOREACH(sp, &hmp_score_providers, link) {
+ hmp_sysctl_add_provider(SYSCTL_CHILDREN(score_root),
+ sp->name, &sp->priority, &sp->probed, &sp->active);
+ }
+ }
}
SYSINIT(hmp_sysctl, SI_SUB_SMP + 2, SI_ORDER_ANY, hmp_sysctl_init, NULL);
diff --git a/sys/sys/hmp.h b/sys/sys/hmp.h
--- a/sys/sys/hmp.h
+++ b/sys/sys/hmp.h
@@ -15,6 +15,7 @@
#include <sys/types.h>
#include <sys/pcpu.h>
+#include <sys/queue.h>
#include <machine/atomic.h>
@@ -58,6 +59,11 @@
/*
* System-wide CPU capability state - initialized on boot
+ *
+ * total_capacity is populated once the capacity provider finishes init().
+ * has_scores is set to true once a score provider has won selection and
+ * finished init(); when false, the scheduler must fall back to capacity-only
+ * placement decisions.
*/
struct hmp {
hmp_score_t total_capacity; /* Precalculated for scheduler */
@@ -92,6 +98,77 @@
atomic_store_rel_16(&hp->scores[st], score);
}
+/*
+ * Provider interfaces
+ *
+ * HMP is fed by two independent provider paths:
+ *
+ * probe() must not touch hmp_pcpu state.
+ * init() is called once, only on the winning provider of each path.
+ */
+
+/*
+ * Capacity provider
+ *
+ * Populate the static per-CPU capacity field and hmp_state.total_capacity.
+ * Exactly one capacity provider wins at boot (highest priority among
+ * those whose probe() returns true).
+ */
+struct hmp_capacity_provider {
+ const char *name;
+ int priority; /* higher wins */
+ int (*probe)(void);
+ int (*init)(void);
+
+ /* Filled in by registration; do not set manually. */
+ bool probed;
+ bool active;
+
+ SLIST_ENTRY(hmp_capacity_provider) link;
+};
+
+/*
+ * Score provider
+ *
+ * Populate and maintain per-CPU perf/eff scores and flags.
+ * Exactly one score provider wins at boot. Score provider can be absent
+ * (hmp_state.has_scores stays false and the scheduler uses capacity-only
+ * placement).
+ */
+struct hmp_score_provider {
+ const char *name;
+ int priority; /* higher wins */
+ int (*probe)(void);
+ int (*init)(void);
+
+ /* Filled in by registration; do not set manually. */
+ bool probed;
+ bool active;
+
+ SLIST_ENTRY(hmp_score_provider) link;
+};
+
+void hmp_capacity_provider_register(struct hmp_capacity_provider *p);
+void hmp_score_provider_register(struct hmp_score_provider *p);
+
+#define HMP_CAPACITY_PROVIDER_DECLARE(name, provider) \
+ DATA_SET(hmp_capacity_provider_set, provider); \
+ static void hmp_cap_provider_##name##_register(void *arg __unused) \
+ { \
+ hmp_capacity_provider_register(&(provider)); \
+ } \
+ SYSINIT(hmp_cap_provider_##name, SI_SUB_SMP, SI_ORDER_ANY, \
+ hmp_cap_provider_##name##_register, NULL)
+
+#define HMP_SCORE_PROVIDER_DECLARE(name, provider) \
+ DATA_SET(hmp_score_provider_set, provider); \
+ static void hmp_score_provider_##name##_register(void *arg __unused) \
+ { \
+ hmp_score_provider_register(&(provider)); \
+ } \
+ SYSINIT(hmp_score_provider_##name, SI_SUB_SMP, SI_ORDER_ANY, \
+ hmp_score_provider_##name##_register, NULL)
+
/*
* Helper functions for scheduler
*/

File Metadata

Mime Type
text/plain
Expires
Tue, May 19, 3:13 AM (12 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33289060
Default Alt Text
D56547.diff (10 KB)

Event Timeline