summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/kern/subr_hints.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/kern/subr_hints.c')
-rw-r--r--freebsd/sys/kern/subr_hints.c353
1 files changed, 195 insertions, 158 deletions
diff --git a/freebsd/sys/kern/subr_hints.c b/freebsd/sys/kern/subr_hints.c
index 982059c3..78ab1b4e 100644
--- a/freebsd/sys/kern/subr_hints.c
+++ b/freebsd/sys/kern/subr_hints.c
@@ -33,228 +33,257 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/lock.h>
+#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/bus.h>
-/*
- * Access functions for device resources.
- */
-
#ifndef __rtems__
-static int checkmethod = 1;
-static int use_kenv;
-static char *hintp;
-#else /* __rtems__ */
-#define hintmode 1
-#define hintp static_hints
-#define use_kenv 0
-static char __used default_static_hints[] = "";
-__weak_reference(default_static_hints, static_hints);
-#endif /* __rtems__ */
+#define FBACK_MDENV 0 /* MD env (e.g. loader.conf) */
+#define FBACK_STENV 1 /* Static env */
+#define FBACK_STATIC 2 /* static_hints */
-#ifndef __rtems__
/*
- * Define kern.hintmode sysctl, which only accept value 2, that cause to
- * switch from Static KENV mode to Dynamic KENV. So systems that have hints
- * compiled into kernel will be able to see/modify KENV (and hints too).
+ * We'll use hintenv_merged to indicate that the dynamic environment has been
+ * properly prepared for hint usage. This implies that the dynamic environment
+ * has already been setup (dynamic_kenv) and that we have added any supplied
+ * static_hints to the dynamic environment.
+ */
+static bool hintenv_merged;
+/* Static environment and static hints cannot change, so we'll skip known bad */
+static bool stenv_skip;
+static bool sthints_skip;
+/*
+ * Access functions for device resources.
*/
-static int
-sysctl_hintmode(SYSCTL_HANDLER_ARGS)
+static void
+static_hints_to_env(void *data __unused)
{
const char *cp;
char *line, *eq;
- int eqidx, error, from_kenv, i, value;
-
- from_kenv = 0;
- cp = kern_envp;
- value = hintmode;
-
- /* Fetch candidate for new hintmode value */
- error = sysctl_handle_int(oidp, &value, 0, req);
- if (error || req->newptr == NULL)
- return (error);
-
- if (value != 2)
- /* Only accept swithing to hintmode 2 */
- return (EINVAL);
-
- /* Migrate from static to dynamic hints */
- switch (hintmode) {
- case 0:
- if (dynamic_kenv) {
- /*
- * Already here. But assign hintmode to 2, to not
- * check it in the future.
- */
- hintmode = 2;
- return (0);
- }
- from_kenv = 1;
- cp = kern_envp;
- break;
- case 1:
- cp = static_hints;
- break;
- case 2:
- /* Nothing to do, hintmode already 2 */
- return (0);
- }
+ int eqidx, i;
- while (cp) {
- i = strlen(cp);
- if (i == 0)
- break;
- if (from_kenv) {
- if (strncmp(cp, "hint.", 5) != 0)
- /* kenv can have not only hints */
- continue;
- }
+ cp = static_hints;
+ while (cp && *cp != '\0') {
eq = strchr(cp, '=');
if (eq == NULL)
/* Bad hint value */
continue;
eqidx = eq - cp;
- line = malloc(i+1, M_TEMP, M_WAITOK);
+ i = strlen(cp);
+ line = malloc(i + 1, M_TEMP, M_WAITOK);
strcpy(line, cp);
- line[eqidx] = '\0';
- kern_setenv(line, line + eqidx + 1);
+ line[eqidx] = line[i] = '\0';
+ /*
+ * Before adding a hint to the dynamic environment, check if
+ * another value for said hint has already been added. This is
+ * needed because static environment overrides static hints and
+ * dynamic environment overrides all.
+ */
+ if (testenv(line) == 0)
+ kern_setenv(line, line + eqidx + 1);
free(line, M_TEMP);
cp += i + 1;
}
-
- hintmode = value;
- use_kenv = 1;
- return (0);
+ hintenv_merged = true;
}
-SYSCTL_PROC(_kern, OID_AUTO, hintmode, CTLTYPE_INT|CTLFLAG_RW,
- &hintmode, 0, sysctl_hintmode, "I", "Get/set current hintmode");
+/* Any time after dynamic env is setup */
+SYSINIT(hintenv, SI_SUB_KMEM + 1, SI_ORDER_SECOND, static_hints_to_env, NULL);
+#else /* __rtems__ */
+#define sthints_skip false
+
+static char __used default_static_hints[] = "";
+__weak_reference(default_static_hints, static_hints);
#endif /* __rtems__ */
/*
+ * Checks the environment to see if we even have any hints. If it has no hints,
+ * then res_find can take the hint that there's no point in searching it and
+ * either move on to the next environment or fail early.
+ */
+static bool
+_res_checkenv(char *envp)
+{
+ char *cp;
+
+ cp = envp;
+ while (cp) {
+ if (strncmp(cp, "hint.", 5) == 0)
+ return (true);
+ while (*cp != '\0')
+ cp++;
+ cp++;
+ if (*cp == '\0')
+ break;
+ }
+ return (false);
+}
+
+/*
* Evil wildcarding resource string lookup.
* This walks the supplied env string table and returns a match.
* The start point can be remembered for incremental searches.
*/
static int
-res_find(int *line, int *startln,
+res_find(char **hintp_cookie, int *line, int *startln,
const char *name, int *unit, const char *resname, const char *value,
const char **ret_name, int *ret_namelen, int *ret_unit,
const char **ret_resname, int *ret_resnamelen, const char **ret_value)
{
- int n = 0, hit, i = 0;
+#ifndef __rtems__
+ int fbacklvl = FBACK_MDENV, i = 0, n = 0;
+#else /* __rtems__ */
+ int n = 0;
+#endif /* __rtems__ */
char r_name[32];
int r_unit;
char r_resname[32];
char r_value[128];
const char *s, *cp;
- char *p;
-
+ char *hintp, *p;
#ifndef __rtems__
- if (checkmethod) {
- hintp = NULL;
+ bool dyn_used = false;
- switch (hintmode) {
- case 0: /* loader hints in environment only */
- break;
- case 1: /* static hints only */
- hintp = static_hints;
- checkmethod = 0;
- break;
- case 2: /* fallback mode */
- if (dynamic_kenv) {
- mtx_lock(&kenv_lock);
- cp = kenvp[0];
- for (i = 0; cp != NULL; cp = kenvp[++i]) {
- if (!strncmp(cp, "hint.", 5)) {
- use_kenv = 1;
- checkmethod = 0;
- break;
- }
+
+ /*
+ * We are expecting that the caller will pass us a hintp_cookie that
+ * they are tracking. Upon entry, if *hintp_cookie is *not* set, this
+ * indicates to us that we should be figuring out based on the current
+ * environment where to search. This keeps us sane throughout the
+ * entirety of a single search.
+ */
+ if (*hintp_cookie == NULL) {
+ hintp = NULL;
+ if (hintenv_merged) {
+ /*
+ * static_hints, if it was previously used, has
+ * already been folded in to the environment
+ * by this point.
+ */
+ mtx_lock(&kenv_lock);
+ cp = kenvp[0];
+ for (i = 0; cp != NULL; cp = kenvp[++i]) {
+ if (!strncmp(cp, "hint.", 5)) {
+ hintp = kenvp[0];
+ break;
}
- mtx_unlock(&kenv_lock);
- } else {
- cp = kern_envp;
- while (cp) {
- if (strncmp(cp, "hint.", 5) == 0) {
- cp = NULL;
- hintp = kern_envp;
- break;
- }
- while (*cp != '\0')
- cp++;
- cp++;
- if (*cp == '\0') {
- cp = NULL;
- hintp = static_hints;
- break;
- }
+ }
+ mtx_unlock(&kenv_lock);
+ dyn_used = true;
+ } else {
+ /*
+ * We'll have a chance to keep coming back here until
+ * we've actually exhausted all of our possibilities.
+ * We might have chosen the MD/Static env because it
+ * had some kind of hints, but perhaps it didn't have
+ * the hint we are looking for. We don't provide any
+ * fallback when searching the dynamic environment.
+ */
+fallback:
+ if (dyn_used || fbacklvl >= FBACK_STATIC)
+ return (ENOENT);
+
+ switch (fbacklvl) {
+ case FBACK_MDENV:
+ fbacklvl++;
+ if (_res_checkenv(md_envp)) {
+ hintp = md_envp;
+ break;
}
+
+ /* FALLTHROUGH */
+ case FBACK_STENV:
+ fbacklvl++;
+ if (!stenv_skip && _res_checkenv(kern_envp)) {
+ hintp = kern_envp;
+ break;
+ } else
+ stenv_skip = true;
+
+ /* FALLTHROUGH */
+ case FBACK_STATIC:
+ fbacklvl++;
+#else /* __rtems__ */
+ hintp = NULL;
+#endif /* __rtems__ */
+ /* We'll fallback to static_hints if needed/can */
+ if (!sthints_skip &&
+ _res_checkenv(static_hints))
+ hintp = static_hints;
+#ifndef __rtems__
+ else
+ sthints_skip = true;
+
+ break;
+ default:
+ return (ENOENT);
}
- break;
- default:
- break;
- }
- if (hintp == NULL) {
- if (dynamic_kenv) {
- use_kenv = 1;
- checkmethod = 0;
- } else
- hintp = kern_envp;
}
+#endif /* __rtems__ */
+
+ if (hintp == NULL)
+ return (ENOENT);
+ *hintp_cookie = hintp;
+#ifndef __rtems__
+ } else {
+ hintp = *hintp_cookie;
+ if (hintenv_merged && hintp == kenvp[0])
+ dyn_used = true;
+ else
+ /*
+ * If we aren't using the dynamic environment, we need
+ * to run through the proper fallback procedure again.
+ * This is so that we do continuations right if we're
+ * working with *line and *startln.
+ */
+ goto fallback;
}
- if (use_kenv) {
+ if (dyn_used) {
mtx_lock(&kenv_lock);
i = 0;
- cp = kenvp[0];
- if (cp == NULL) {
- mtx_unlock(&kenv_lock);
- return (ENOENT);
- }
- } else
+ }
#endif /* __rtems__ */
- cp = hintp;
+
+ cp = hintp;
while (cp) {
- hit = 1;
(*line)++;
if (strncmp(cp, "hint.", 5) != 0)
- hit = 0;
- else
- n = sscanf(cp, "hint.%32[^.].%d.%32[^=]=%127s",
- r_name, &r_unit, r_resname, r_value);
- if (hit && n != 4) {
+ goto nexthint;
+ n = sscanf(cp, "hint.%32[^.].%d.%32[^=]=%127s", r_name, &r_unit,
+ r_resname, r_value);
+ if (n != 4) {
printf("CONFIG: invalid hint '%s'\n", cp);
p = strchr(cp, 'h');
*p = 'H';
- hit = 0;
+ goto nexthint;
}
- if (hit && startln && *startln >= 0 && *line < *startln)
- hit = 0;
- if (hit && name && strcmp(name, r_name) != 0)
- hit = 0;
- if (hit && unit && *unit != r_unit)
- hit = 0;
- if (hit && resname && strcmp(resname, r_resname) != 0)
- hit = 0;
- if (hit && value && strcmp(value, r_value) != 0)
- hit = 0;
- if (hit)
- break;
- if (use_kenv) {
+ if (startln && *startln >= 0 && *line < *startln)
+ goto nexthint;
+ if (name && strcmp(name, r_name) != 0)
+ goto nexthint;
+ if (unit && *unit != r_unit)
+ goto nexthint;
+ if (resname && strcmp(resname, r_resname) != 0)
+ goto nexthint;
+ if (value && strcmp(value, r_value) != 0)
+ goto nexthint;
+ /* Successfully found a hint matching all criteria */
+ break;
+nexthint:
#ifndef __rtems__
+ if (dyn_used) {
cp = kenvp[++i];
if (cp == NULL)
break;
-#else /* __rtems__ */
(void) i;
-#endif /* __rtems__ */
} else {
+#endif /* __rtems__ */
while (*cp != '\0')
cp++;
cp++;
@@ -262,14 +291,20 @@ res_find(int *line, int *startln,
cp = NULL;
break;
}
+#ifndef __rtems__
}
+#endif /* __rtems__ */
}
#ifndef __rtems__
- if (use_kenv)
+ if (dyn_used)
mtx_unlock(&kenv_lock);
#endif /* __rtems__ */
if (cp == NULL)
- return ENOENT;
+#ifndef __rtems__
+ goto fallback;
+#else /* __rtems__ */
+ return (ENOENT);
+#endif /* __rtems__ */
s = cp;
/* This is a bit of a hack, but at least is reentrant */
@@ -307,11 +342,13 @@ resource_find(int *line, int *startln,
{
int i;
int un;
+ char *hintp;
*line = 0;
+ hintp = NULL;
/* Search for exact unit matches first */
- i = res_find(line, startln, name, unit, resname, value,
+ i = res_find(&hintp, line, startln, name, unit, resname, value,
ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
ret_value);
if (i == 0)
@@ -320,7 +357,7 @@ resource_find(int *line, int *startln,
return ENOENT;
/* If we are still here, search for wildcard matches */
un = -1;
- i = res_find(line, startln, name, &un, resname, value,
+ i = res_find(&hintp, line, startln, name, &un, resname, value,
ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
ret_value);
if (i == 0)