diff options
Diffstat (limited to 'freebsd/sys/kern/subr_hints.c')
-rw-r--r-- | freebsd/sys/kern/subr_hints.c | 353 |
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) |