Changeset View
Standalone View
sys/kern/kern_sysctl.c
Show First 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | |||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/vm_extern.h> | #include <vm/vm_extern.h> | ||||
static MALLOC_DEFINE(M_SYSCTL, "sysctl", "sysctl internal magic"); | static MALLOC_DEFINE(M_SYSCTL, "sysctl", "sysctl internal magic"); | ||||
static MALLOC_DEFINE(M_SYSCTLOID, "sysctloid", "sysctl dynamic oids"); | static MALLOC_DEFINE(M_SYSCTLOID, "sysctloid", "sysctl dynamic oids"); | ||||
static MALLOC_DEFINE(M_SYSCTLTMP, "sysctltmp", "sysctl temp output buffer"); | static MALLOC_DEFINE(M_SYSCTLTMP, "sysctltmp", "sysctl temp output buffer"); | ||||
RB_GENERATE(sysctl_oid_list, sysctl_oid, oid_link, cmp_sysctl_oid); | |||||
/* | /* | ||||
* The sysctllock protects the MIB tree. It also protects sysctl | * The sysctllock protects the MIB tree. It also protects sysctl | ||||
* contexts used with dynamic sysctls. The sysctl_register_oid() and | * contexts used with dynamic sysctls. The sysctl_register_oid() and | ||||
* sysctl_unregister_oid() routines require the sysctllock to already | * sysctl_unregister_oid() routines require the sysctllock to already | ||||
* be held, so the sysctl_wlock() and sysctl_wunlock() routines are | * be held, so the sysctl_wlock() and sysctl_wunlock() routines are | ||||
* provided for the few places in the kernel which need to use that | * provided for the few places in the kernel which need to use that | ||||
* API rather than using the dynamic API. Use of the dynamic API is | * API rather than using the dynamic API. Use of the dynamic API is | ||||
* strongly encouraged for most code. | * strongly encouraged for most code. | ||||
Show All 20 Lines | |||||
#define SYSCTL_INIT() rm_init_flags(&sysctllock, "sysctl lock", \ | #define SYSCTL_INIT() rm_init_flags(&sysctllock, "sysctl lock", \ | ||||
RM_SLEEPABLE) | RM_SLEEPABLE) | ||||
#define SYSCTL_SLEEP(ch, wmesg, timo) \ | #define SYSCTL_SLEEP(ch, wmesg, timo) \ | ||||
rm_sleep(ch, &sysctllock, 0, wmesg, timo) | rm_sleep(ch, &sysctllock, 0, wmesg, timo) | ||||
static int sysctl_root(SYSCTL_HANDLER_ARGS); | static int sysctl_root(SYSCTL_HANDLER_ARGS); | ||||
/* Root list */ | /* Root list */ | ||||
struct sysctl_oid_list sysctl__children = SLIST_HEAD_INITIALIZER(&sysctl__children); | struct sysctl_oid_list sysctl__children = RB_INITIALIZER(&sysctl__children); | ||||
static char* sysctl_escape_name(const char*); | static char* sysctl_escape_name(const char*); | ||||
static int sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, | static int sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, | ||||
int recurse); | int recurse); | ||||
static int sysctl_old_kernel(struct sysctl_req *, const void *, size_t); | static int sysctl_old_kernel(struct sysctl_req *, const void *, size_t); | ||||
static int sysctl_new_kernel(struct sysctl_req *, void *, size_t); | static int sysctl_new_kernel(struct sysctl_req *, void *, size_t); | ||||
static struct sysctl_oid * | static struct sysctl_oid * | ||||
sysctl_find_oidname(const char *name, struct sysctl_oid_list *list) | sysctl_find_oidname(const char *name, struct sysctl_oid_list *list) | ||||
{ | { | ||||
struct sysctl_oid *oidp; | struct sysctl_oid *oidp; | ||||
SYSCTL_ASSERT_LOCKED(); | SYSCTL_ASSERT_LOCKED(); | ||||
SLIST_FOREACH(oidp, list, oid_link) { | RB_FOREACH(oidp, sysctl_oid_list, list) { | ||||
if (strcmp(oidp->oid_name, name) == 0) { | if (strcmp(oidp->oid_name, name) == 0) { | ||||
return (oidp); | return (oidp); | ||||
} | } | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 205 Lines • ▼ Show 20 Lines | |||||
sysctl_search_oid(struct sysctl_oid **nodes, struct sysctl_oid *needle) | sysctl_search_oid(struct sysctl_oid **nodes, struct sysctl_oid *needle) | ||||
{ | { | ||||
int indx; | int indx; | ||||
SYSCTL_ASSERT_LOCKED(); | SYSCTL_ASSERT_LOCKED(); | ||||
indx = 0; | indx = 0; | ||||
while (indx < CTL_MAXNAME && indx >= 0) { | while (indx < CTL_MAXNAME && indx >= 0) { | ||||
if (nodes[indx] == NULL && indx == 0) | if (nodes[indx] == NULL && indx == 0) | ||||
nodes[indx] = SLIST_FIRST(&sysctl__children); | nodes[indx] = RB_MIN(sysctl_oid_list, | ||||
&sysctl__children); | |||||
else if (nodes[indx] == NULL) | else if (nodes[indx] == NULL) | ||||
nodes[indx] = SLIST_FIRST(&nodes[indx - 1]->oid_children); | nodes[indx] = RB_MIN(sysctl_oid_list, | ||||
&nodes[indx - 1]->oid_children); | |||||
else | else | ||||
nodes[indx] = SLIST_NEXT(nodes[indx], oid_link); | nodes[indx] = RB_NEXT(sysctl_oid_list, | ||||
&nodes[indx - 1]->oid_children, nodes[indx]); | |||||
if (nodes[indx] == needle) | if (nodes[indx] == needle) | ||||
return (indx + 1); | return (indx + 1); | ||||
if (nodes[indx] == NULL) { | if (nodes[indx] == NULL) { | ||||
indx--; | indx--; | ||||
continue; | continue; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | SYSCTL_PROC(_sysctl, OID_AUTO, reuse_test, | ||||
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 0, sysctl_reuse_test, "-", | CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 0, sysctl_reuse_test, "-", | ||||
""); | ""); | ||||
#endif | #endif | ||||
void | void | ||||
sysctl_register_oid(struct sysctl_oid *oidp) | sysctl_register_oid(struct sysctl_oid *oidp) | ||||
{ | { | ||||
struct sysctl_oid_list *parent = oidp->oid_parent; | struct sysctl_oid_list *parent = oidp->oid_parent; | ||||
struct sysctl_oid *p; | struct sysctl_oid *p, key; | ||||
struct sysctl_oid *q; | |||||
int oid_number; | int oid_number; | ||||
int timeout = 2; | int timeout = 2; | ||||
/* | /* | ||||
* First check if another oid with the same name already | * First check if another oid with the same name already | ||||
* exists in the parent's list. | * exists in the parent's list. | ||||
*/ | */ | ||||
SYSCTL_ASSERT_WLOCKED(); | SYSCTL_ASSERT_WLOCKED(); | ||||
Show All 33 Lines | if (oid_number < 0) { | ||||
oid_number = newoid; | oid_number = newoid; | ||||
} | } | ||||
/* | /* | ||||
* Insert the OID into the parent's list sorted by OID number. | * Insert the OID into the parent's list sorted by OID number. | ||||
*/ | */ | ||||
retry: | retry: | ||||
q = NULL; | key.oid_number = oid_number; | ||||
SLIST_FOREACH(p, parent, oid_link) { | p = RB_FIND(sysctl_oid_list, parent, &key); | ||||
/* check if the current OID number is in use */ | if (p) { | ||||
if (oid_number == p->oid_number) { | |||||
/* get the next valid OID number */ | /* get the next valid OID number */ | ||||
if (oid_number < CTL_AUTO_START || | if (oid_number < CTL_AUTO_START || | ||||
oid_number == 0x7fffffff) { | oid_number == 0x7fffffff) { | ||||
/* wraparound - restart */ | /* wraparound - restart */ | ||||
oid_number = CTL_AUTO_START; | oid_number = CTL_AUTO_START; | ||||
/* don't loop forever */ | /* don't loop forever */ | ||||
if (!timeout--) | if (!timeout--) | ||||
panic("sysctl: Out of OID numbers\n"); | panic("sysctl: Out of OID numbers\n"); | ||||
goto retry; | goto retry; | ||||
} else { | } else { | ||||
oid_number++; | oid_number++; | ||||
dougm: In the old code, the incremented 'old_number' gets tested in another iteration of the… | |||||
} | } | ||||
} else if (oid_number < p->oid_number) | |||||
break; | |||||
q = p; | |||||
} | } | ||||
/* check for non-auto OID number collision */ | /* check for non-auto OID number collision */ | ||||
if (oidp->oid_number >= 0 && oidp->oid_number < CTL_AUTO_START && | if (oidp->oid_number >= 0 && oidp->oid_number < CTL_AUTO_START && | ||||
oid_number >= CTL_AUTO_START) { | oid_number >= CTL_AUTO_START) { | ||||
printf("sysctl: OID number(%d) is already in use for '%s'\n", | printf("sysctl: OID number(%d) is already in use for '%s'\n", | ||||
oidp->oid_number, oidp->oid_name); | oidp->oid_number, oidp->oid_name); | ||||
} | } | ||||
/* update the OID number, if any */ | /* update the OID number, if any */ | ||||
oidp->oid_number = oid_number; | oidp->oid_number = oid_number; | ||||
if (q != NULL) | RB_INSERT(sysctl_oid_list, parent, oidp); | ||||
SLIST_INSERT_AFTER(q, oidp, oid_link); | |||||
else | |||||
SLIST_INSERT_HEAD(parent, oidp, oid_link); | |||||
if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE && | if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE && | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
(oidp->oid_kind & CTLFLAG_VNET) == 0 && | (oidp->oid_kind & CTLFLAG_VNET) == 0 && | ||||
#endif | #endif | ||||
(oidp->oid_kind & CTLFLAG_TUN) != 0 && | (oidp->oid_kind & CTLFLAG_TUN) != 0 && | ||||
(oidp->oid_kind & CTLFLAG_NOFETCH) == 0) { | (oidp->oid_kind & CTLFLAG_NOFETCH) == 0) { | ||||
/* only fetch value once */ | /* only fetch value once */ | ||||
Show All 32 Lines | sysctl_enable_oid(struct sysctl_oid *oidp) | ||||
KASSERT((oidp->oid_kind & CTLFLAG_DORMANT) != 0, | KASSERT((oidp->oid_kind & CTLFLAG_DORMANT) != 0, | ||||
("enabling already enabled sysctl oid")); | ("enabling already enabled sysctl oid")); | ||||
oidp->oid_kind &= ~CTLFLAG_DORMANT; | oidp->oid_kind &= ~CTLFLAG_DORMANT; | ||||
} | } | ||||
void | void | ||||
sysctl_unregister_oid(struct sysctl_oid *oidp) | sysctl_unregister_oid(struct sysctl_oid *oidp) | ||||
{ | { | ||||
struct sysctl_oid *p; | |||||
int error; | int error; | ||||
SYSCTL_ASSERT_WLOCKED(); | SYSCTL_ASSERT_WLOCKED(); | ||||
if (oidp->oid_number == OID_AUTO) { | if (oidp->oid_number == OID_AUTO) { | ||||
error = EINVAL; | error = EINVAL; | ||||
} else { | } else { | ||||
error = ENOENT; | error = ENOENT; | ||||
SLIST_FOREACH(p, oidp->oid_parent, oid_link) { | if (RB_REMOVE(sysctl_oid_list, oidp->oid_parent, oidp)) | ||||
if (p == oidp) { | |||||
SLIST_REMOVE(oidp->oid_parent, oidp, | |||||
sysctl_oid, oid_link); | |||||
error = 0; | error = 0; | ||||
break; | |||||
} | } | ||||
} | |||||
} | |||||
/* | /* | ||||
* This can happen when a module fails to register and is | * This can happen when a module fails to register and is | ||||
* being unloaded afterwards. It should not be a panic() | * being unloaded afterwards. It should not be a panic() | ||||
* for normal use. | * for normal use. | ||||
*/ | */ | ||||
if (error) { | if (error) { | ||||
printf("%s: failed(%d) to unregister sysctl(%s)\n", | printf("%s: failed(%d) to unregister sysctl(%s)\n", | ||||
▲ Show 20 Lines • Show All 143 Lines • ▼ Show 20 Lines | sysctl_remove_oid(struct sysctl_oid *oidp, int del, int recurse) | ||||
SYSCTL_WUNLOCK(); | SYSCTL_WUNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
sysctl_remove_name(struct sysctl_oid *parent, const char *name, | sysctl_remove_name(struct sysctl_oid *parent, const char *name, | ||||
int del, int recurse) | int del, int recurse) | ||||
{ | { | ||||
struct sysctl_oid *p, *tmp; | struct sysctl_oid *p; | ||||
int error; | int error; | ||||
error = ENOENT; | error = ENOENT; | ||||
SYSCTL_WLOCK(); | SYSCTL_WLOCK(); | ||||
SLIST_FOREACH_SAFE(p, SYSCTL_CHILDREN(parent), oid_link, tmp) { | p = sysctl_find_oidname(name, &parent->oid_children); | ||||
if (strcmp(p->oid_name, name) == 0) { | if (p) | ||||
error = sysctl_remove_oid_locked(p, del, recurse); | error = sysctl_remove_oid_locked(p, del, recurse); | ||||
break; | |||||
} | |||||
} | |||||
SYSCTL_WUNLOCK(); | SYSCTL_WUNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Duplicate the provided string, escaping any illegal characters. The result | * Duplicate the provided string, escaping any illegal characters. The result | ||||
* must be freed when no longer in use. | * must be freed when no longer in use. | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse) | ||||
* WARNING: normal method to do this should be through | * WARNING: normal method to do this should be through | ||||
* sysctl_ctx_free(). Use recursing as the last resort | * sysctl_ctx_free(). Use recursing as the last resort | ||||
* method to purge your sysctl tree of leftovers... | * method to purge your sysctl tree of leftovers... | ||||
* However, if some other code still references these nodes, | * However, if some other code still references these nodes, | ||||
* it will panic. | * it will panic. | ||||
*/ | */ | ||||
if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | ||||
if (oidp->oid_refcnt == 1) { | if (oidp->oid_refcnt == 1) { | ||||
SLIST_FOREACH_SAFE(p, | for(p = RB_MIN(sysctl_oid_list, &oidp->oid_children); | ||||
dougmUnsubmitted Not Done Inline ActionsRB_FOREACH_SAFE(p, sysctl_oid_list, &oidp->old_children, tmp) dougm: RB_FOREACH_SAFE(p, sysctl_oid_list, &oidp->old_children, tmp)
applies here. | |||||
SYSCTL_CHILDREN(oidp), oid_link, tmp) { | p != NULL; p = tmp) { | ||||
if (!recurse) { | if (!recurse) { | ||||
printf("Warning: failed attempt to " | printf("Warning: failed attempt to " | ||||
"remove oid %s with child %s\n", | "remove oid %s with child %s\n", | ||||
oidp->oid_name, p->oid_name); | oidp->oid_name, p->oid_name); | ||||
return (ENOTEMPTY); | return (ENOTEMPTY); | ||||
} | } | ||||
tmp = RB_NEXT(sysctl_oid_list, | |||||
&oidp->oid_children, p); | |||||
error = sysctl_remove_oid_locked(p, del, | error = sysctl_remove_oid_locked(p, del, | ||||
recurse); | recurse); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (oidp->oid_refcnt > 1 ) { | if (oidp->oid_refcnt > 1 ) { | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | ||||
SYSCTL_WUNLOCK(); | SYSCTL_WUNLOCK(); | ||||
return (oidp); | return (oidp); | ||||
} else { | } else { | ||||
sysctl_warn_reuse(__func__, oidp); | sysctl_warn_reuse(__func__, oidp); | ||||
SYSCTL_WUNLOCK(); | SYSCTL_WUNLOCK(); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
} | } | ||||
oidp = malloc(sizeof(struct sysctl_oid), M_SYSCTLOID, M_WAITOK|M_ZERO); | oidp = malloc(sizeof(struct sysctl_oid), M_SYSCTLOID, M_WAITOK|M_ZERO); | ||||
Not Done Inline Actionsif this is the only spot where you need sleeping behavior then perhaps it can be moved up prior to locking mjg: if this is the only spot where you need sleeping behavior then perhaps it can be moved up prior… | |||||
Done Inline ActionsIt isn't. For example, there's a malloc in sysctl_ctx_entry_add, which is entered with the lock already held. I think that custom sysctl handlers can also sleep, too. asomers: It isn't. For example, there's a malloc in sysctl_ctx_entry_add, which is entered with the… | |||||
oidp->oid_parent = parent; | oidp->oid_parent = parent; | ||||
SLIST_INIT(&oidp->oid_children); | RB_INIT(&oidp->oid_children); | ||||
oidp->oid_number = number; | oidp->oid_number = number; | ||||
oidp->oid_refcnt = 1; | oidp->oid_refcnt = 1; | ||||
oidp->oid_name = escaped; | oidp->oid_name = escaped; | ||||
oidp->oid_handler = handler; | oidp->oid_handler = handler; | ||||
oidp->oid_kind = CTLFLAG_DYN | kind; | oidp->oid_kind = CTLFLAG_DYN | kind; | ||||
oidp->oid_arg1 = arg1; | oidp->oid_arg1 = arg1; | ||||
oidp->oid_arg2 = arg2; | oidp->oid_arg2 = arg2; | ||||
oidp->oid_fmt = fmt; | oidp->oid_fmt = fmt; | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | |||||
#ifdef SYSCTL_DEBUG | #ifdef SYSCTL_DEBUG | ||||
static void | static void | ||||
sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i) | sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i) | ||||
{ | { | ||||
int k; | int k; | ||||
struct sysctl_oid *oidp; | struct sysctl_oid *oidp; | ||||
SYSCTL_ASSERT_LOCKED(); | SYSCTL_ASSERT_LOCKED(); | ||||
SLIST_FOREACH(oidp, l, oid_link) { | RB_FOREACH(oidp, sysctl_oid_list, l) { | ||||
for (k=0; k<i; k++) | for (k=0; k<i; k++) | ||||
printf(" "); | printf(" "); | ||||
printf("%d %s ", oidp->oid_number, oidp->oid_name); | printf("%d %s ", oidp->oid_number, oidp->oid_name); | ||||
printf("%c%c", | printf("%c%c", | ||||
oidp->oid_kind & CTLFLAG_RD ? 'R':' ', | oidp->oid_kind & CTLFLAG_RD ? 'R':' ', | ||||
oidp->oid_kind & CTLFLAG_WR ? 'W':' '); | oidp->oid_kind & CTLFLAG_WR ? 'W':' '); | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
static int | static int | ||||
sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) | sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int *name = (int *) arg1; | int *name = (int *) arg1; | ||||
u_int namelen = arg2; | u_int namelen = arg2; | ||||
int error; | int error; | ||||
struct sysctl_oid *oid; | struct sysctl_oid *oid, key; | ||||
struct sysctl_oid_list *lsp = &sysctl__children, *lsp2; | struct sysctl_oid_list *lsp = &sysctl__children, *lsp2; | ||||
struct rm_priotracker tracker; | struct rm_priotracker tracker; | ||||
char buf[10]; | char buf[10]; | ||||
error = sysctl_wire_old_buffer(req, 0); | error = sysctl_wire_old_buffer(req, 0); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
SYSCTL_RLOCK(&tracker); | SYSCTL_RLOCK(&tracker); | ||||
while (namelen) { | while (namelen) { | ||||
if (!lsp) { | if (!lsp) { | ||||
snprintf(buf,sizeof(buf),"%d",*name); | snprintf(buf,sizeof(buf),"%d",*name); | ||||
if (req->oldidx) | if (req->oldidx) | ||||
error = SYSCTL_OUT(req, ".", 1); | error = SYSCTL_OUT(req, ".", 1); | ||||
if (!error) | if (!error) | ||||
error = SYSCTL_OUT(req, buf, strlen(buf)); | error = SYSCTL_OUT(req, buf, strlen(buf)); | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
namelen--; | namelen--; | ||||
name++; | name++; | ||||
continue; | continue; | ||||
} | } | ||||
lsp2 = NULL; | lsp2 = NULL; | ||||
SLIST_FOREACH(oid, lsp, oid_link) { | key.oid_number = *name; | ||||
if (oid->oid_number != *name) | oid = RB_FIND(sysctl_oid_list, lsp, &key); | ||||
continue; | if (oid) { | ||||
if (req->oldidx) | if (req->oldidx) | ||||
error = SYSCTL_OUT(req, ".", 1); | error = SYSCTL_OUT(req, ".", 1); | ||||
if (!error) | if (!error) | ||||
error = SYSCTL_OUT(req, oid->oid_name, | error = SYSCTL_OUT(req, oid->oid_name, | ||||
strlen(oid->oid_name)); | strlen(oid->oid_name)); | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
namelen--; | namelen--; | ||||
name++; | name++; | ||||
if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE) | if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE && | ||||
break; | !oid->oid_handler) | ||||
if (oid->oid_handler) | |||||
break; | |||||
lsp2 = SYSCTL_CHILDREN(oid); | lsp2 = SYSCTL_CHILDREN(oid); | ||||
break; | |||||
} | } | ||||
lsp = lsp2; | lsp = lsp2; | ||||
} | } | ||||
error = SYSCTL_OUT(req, "", 1); | error = SYSCTL_OUT(req, "", 1); | ||||
out: | out: | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_RUNLOCK(&tracker); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 95 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Recursively walk the sysctl subtree at lsp until we find the given name. | * Recursively walk the sysctl subtree at lsp until we find the given name. | ||||
* Returns true and fills in next oid data in @next and @len if oid is found. | * Returns true and fills in next oid data in @next and @len if oid is found. | ||||
*/ | */ | ||||
static bool | static bool | ||||
sysctl_sysctl_next_action(struct sysctl_oid_list *lsp, int *name, u_int namelen, | sysctl_sysctl_next_action(struct sysctl_oid_list *lsp, int *name, u_int namelen, | ||||
int *next, int *len, int level, bool honor_skip) | int *next, int *len, int level, bool honor_skip) | ||||
{ | { | ||||
struct sysctl_oid *oidp; | struct sysctl_oid_list *next_lsp; | ||||
struct sysctl_oid *oidp = NULL, key; | |||||
bool success = false; | bool success = false; | ||||
enum sysctl_iter_action action; | enum sysctl_iter_action action; | ||||
SYSCTL_ASSERT_LOCKED(); | SYSCTL_ASSERT_LOCKED(); | ||||
SLIST_FOREACH(oidp, lsp, oid_link) { | /* | ||||
action = sysctl_sysctl_next_node(oidp, name, namelen, honor_skip); | * Start the search at the requested oid. But if not found, then scan | ||||
* through all children. | |||||
*/ | |||||
if (namelen > 0) { | |||||
key.oid_number = *name; | |||||
oidp = RB_FIND(sysctl_oid_list, lsp, &key); | |||||
} | |||||
if (!oidp) | |||||
oidp = RB_MIN(sysctl_oid_list, lsp); | |||||
for(; oidp != NULL; oidp = RB_NEXT(sysctl_oid_list, lsp, oidp)) { | |||||
action = sysctl_sysctl_next_node(oidp, name, namelen, | |||||
honor_skip); | |||||
if (action == ITER_SIBLINGS) | if (action == ITER_SIBLINGS) | ||||
continue; | continue; | ||||
if (action == ITER_FOUND) { | if (action == ITER_FOUND) { | ||||
success = true; | success = true; | ||||
break; | break; | ||||
} | } | ||||
KASSERT((action== ITER_CHILDREN), ("ret(%d)!=ITER_CHILDREN", action)); | KASSERT((action== ITER_CHILDREN), ("ret(%d)!=ITER_CHILDREN", action)); | ||||
lsp = SYSCTL_CHILDREN(oidp); | next_lsp = SYSCTL_CHILDREN(oidp); | ||||
if (namelen == 0) { | if (namelen == 0) { | ||||
success = sysctl_sysctl_next_action(lsp, NULL, 0, | success = sysctl_sysctl_next_action(next_lsp, NULL, 0, | ||||
next + 1, len, level + 1, honor_skip); | next + 1, len, level + 1, honor_skip); | ||||
} else { | } else { | ||||
success = sysctl_sysctl_next_action(lsp, name + 1, namelen - 1, | success = sysctl_sysctl_next_action(next_lsp, name + 1, | ||||
next + 1, len, level + 1, honor_skip); | namelen - 1, next + 1, len, level + 1, honor_skip); | ||||
if (!success) { | if (!success) { | ||||
/* | /* | ||||
* We maintain the invariant that current node oid | * We maintain the invariant that current node oid | ||||
* is >= the oid provided in @name. | * is >= the oid provided in @name. | ||||
* As there are no usable children at this node, | * As there are no usable children at this node, | ||||
* current node oid is strictly > than the requested | * current node oid is strictly > than the requested | ||||
* oid. | * oid. | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | name2oid(char *name, int *oid, int *len, struct sysctl_oid **oidpp) | ||||
struct sysctl_oid_list *lsp = &sysctl__children; | struct sysctl_oid_list *lsp = &sysctl__children; | ||||
char *p; | char *p; | ||||
SYSCTL_ASSERT_LOCKED(); | SYSCTL_ASSERT_LOCKED(); | ||||
for (*len = 0; *len < CTL_MAXNAME;) { | for (*len = 0; *len < CTL_MAXNAME;) { | ||||
p = strsep(&name, "."); | p = strsep(&name, "."); | ||||
oidp = SLIST_FIRST(lsp); | RB_FOREACH(oidp, sysctl_oid_list, lsp) { | ||||
Not Done Inline Actionsname2oid is called *a lot* as well. *another* tree which sorts by name would be in order here. mjg: name2oid is called *a lot* as well. *another* tree which sorts by name would be in order here. | |||||
Done Inline ActionsSomewhat surprisingly, dtrace doesn't show any hits for name2oid or sysctl_sysctl_name during sysctl kstat.zfs. So I don't think it's worth optimizing it, at least not at this point. asomers: Somewhat surprisingly, dtrace doesn't show any hits for `name2oid` or `sysctl_sysctl_name`… | |||||
Not Done Inline Actionseveryone doing sysctbyname runs into it. mjg: everyone doing sysctbyname runs into it. | |||||
Done Inline ActionsMaybe it got inlined then. Still, a regression analysis of the time to run sysctl kstat.zfs.testpool shows perfectly linear behavior up to 8000 file systems. Since name2oid is obviously quadratic, it can't be getting called very often. Maybe it only gets called for the initial kstat.zfs.testpool lookup but not during the subsequent iteration over all file systems. asomers: Maybe it got inlined then. Still, a regression analysis of the time to run `sysctl kstat.zfs. | |||||
Not Done Inline Actionsit is called by everything doing sysctlbyname and I gave you examples above of actual users mjg: it is called by everything doing sysctlbyname and I gave you examples above of actual users | |||||
Done Inline ActionsOh sure. I know that lots of programs call sysctlbyname. I'm just saying that none of them, as far as I know, call it over and over for the same parent mib. I verified that both ztop and sysctl kstat.zfs only do it once each. That's why it doesn't lead to any O(n^2) problems. And as a result, I consider it premature optimization to worry overmuch about name2oid. asomers: Oh sure. I know that lots of programs call sysctlbyname. I'm just saying that none of them… | |||||
for (;; oidp = SLIST_NEXT(oidp, oid_link)) { | |||||
if (oidp == NULL) | |||||
return (ENOENT); | |||||
if (strcmp(p, oidp->oid_name) == 0) | if (strcmp(p, oidp->oid_name) == 0) | ||||
break; | break; | ||||
} | } | ||||
if (oidp == NULL) | |||||
return (ENOENT); | |||||
*oid++ = oidp->oid_number; | *oid++ = oidp->oid_number; | ||||
(*len)++; | (*len)++; | ||||
if (name == NULL || *name == '\0') { | if (name == NULL || *name == '\0') { | ||||
if (oidpp) | if (oidpp) | ||||
*oidpp = oidp; | *oidpp = oidp; | ||||
return (0); | return (0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 807 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
int | int | ||||
sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid, | sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid, | ||||
int *nindx, struct sysctl_req *req) | int *nindx, struct sysctl_req *req) | ||||
{ | { | ||||
struct sysctl_oid_list *lsp; | struct sysctl_oid_list *lsp; | ||||
struct sysctl_oid *oid; | struct sysctl_oid *oid; | ||||
struct sysctl_oid key; | |||||
int indx; | int indx; | ||||
SYSCTL_ASSERT_LOCKED(); | SYSCTL_ASSERT_LOCKED(); | ||||
lsp = &sysctl__children; | lsp = &sysctl__children; | ||||
indx = 0; | indx = 0; | ||||
while (indx < CTL_MAXNAME) { | while (indx < CTL_MAXNAME) { | ||||
SLIST_FOREACH(oid, lsp, oid_link) { | key.oid_number = name[indx]; | ||||
if (oid->oid_number == name[indx]) | oid = RB_FIND(sysctl_oid_list, lsp, &key); | ||||
break; | |||||
} | |||||
if (oid == NULL) | if (oid == NULL) | ||||
return (ENOENT); | return (ENOENT); | ||||
indx++; | indx++; | ||||
if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | ||||
if (oid->oid_handler != NULL || indx == namelen) { | if (oid->oid_handler != NULL || indx == namelen) { | ||||
*noid = oid; | *noid = oid; | ||||
if (nindx != NULL) | if (nindx != NULL) | ||||
▲ Show 20 Lines • Show All 856 Lines • Show Last 20 Lines |
In the old code, the incremented 'old_number' gets tested in another iteration of the SLIST_FOREACH loop. Here, the incremented 'old_number' is assumed to be valid.
I think you need to 'goto retry' here too. Or, better, make a loop.