Changeset View
Standalone View
sys/kern/kern_sysctl.c
Show First 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/jail.h> | #include <sys/jail.h> | ||||
#include <sys/kdb.h> | #include <sys/kdb.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/rmlock.h> | |||||
#include <sys/sbuf.h> | #include <sys/sbuf.h> | ||||
#include <sys/sx.h> | #include <sys/sx.h> | ||||
#include <sys/sysproto.h> | #include <sys/sysproto.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#ifdef KTRACE | #ifdef KTRACE | ||||
#include <sys/ktrace.h> | #include <sys/ktrace.h> | ||||
#endif | #endif | ||||
#ifdef DDB | #ifdef DDB | ||||
#include <ddb/ddb.h> | #include <ddb/ddb.h> | ||||
#include <ddb/db_lex.h> | #include <ddb/db_lex.h> | ||||
#endif | #endif | ||||
#include <net/vnet.h> | #include <net/vnet.h> | ||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
#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"); | ||||
SPLAY_GENERATE(sysctl_oid_list, sysctl_oid, oid_link, cmp_splay_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. | ||||
* | * | ||||
* The sysctlmemlock is used to limit the amount of user memory wired for | * The sysctlmemlock is used to limit the amount of user memory wired for | ||||
* sysctl requests. This is implemented by serializing any userland | * sysctl requests. This is implemented by serializing any userland | ||||
* sysctl requests larger than a single page via an exclusive lock. | * sysctl requests larger than a single page via an exclusive lock. | ||||
* | * | ||||
* The sysctlstringlock is used to protect concurrent access to writable | * The sysctlstringlock is used to protect concurrent access to writable | ||||
* string nodes in sysctl_handle_string(). | * string nodes in sysctl_handle_string(). | ||||
*/ | */ | ||||
static struct rmlock sysctllock; | /* Even though we never lock it for reading, sysctllock needs to be an sx lock | ||||
* instead of a mutex to support sleeping. | |||||
*/ | |||||
static struct sx sysctllock; | |||||
static struct sx __exclusive_cache_line sysctlmemlock; | static struct sx __exclusive_cache_line sysctlmemlock; | ||||
static struct sx sysctlstringlock; | static struct sx sysctlstringlock; | ||||
#define SYSCTL_WLOCK() rm_wlock(&sysctllock) | #define SYSCTL_LOCK() sx_xlock(&sysctllock) | ||||
#define SYSCTL_WUNLOCK() rm_wunlock(&sysctllock) | #define SYSCTL_UNLOCK() sx_xunlock(&sysctllock) | ||||
#define SYSCTL_RLOCK(tracker) rm_rlock(&sysctllock, (tracker)) | #define SYSCTL_RLOCK(tracker) sx_xlock(&sysctllock) | ||||
#define SYSCTL_RUNLOCK(tracker) rm_runlock(&sysctllock, (tracker)) | #define SYSCTL_RUNLOCK(tracker) sx_xunlock(&sysctllock) | ||||
#define SYSCTL_WLOCKED() rm_wowned(&sysctllock) | #define SYSCTL_ASSERT_LOCKED() sx_assert(&sysctllock, SA_XLOCKED) | ||||
#define SYSCTL_ASSERT_LOCKED() rm_assert(&sysctllock, RA_LOCKED) | #define SYSCTL_INIT() sx_init(&sysctllock, "sysctl lock") | ||||
#define SYSCTL_ASSERT_WLOCKED() rm_assert(&sysctllock, RA_WLOCKED) | |||||
#define SYSCTL_ASSERT_RLOCKED() rm_assert(&sysctllock, RA_RLOCKED) | |||||
#define SYSCTL_INIT() rm_init_flags(&sysctllock, "sysctl lock", \ | |||||
RM_SLEEPABLE) | |||||
#define SYSCTL_SLEEP(ch, wmesg, timo) \ | #define SYSCTL_SLEEP(ch, wmesg, timo) \ | ||||
rm_sleep(ch, &sysctllock, 0, wmesg, timo) | sx_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 = SPLAY_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) { | SPLAY_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); | ||||
} | } | ||||
/* | /* | ||||
* Initialization of the MIB tree. | * Initialization of the MIB tree. | ||||
* | * | ||||
* Order by number in each list. | * Order by number in each list. | ||||
*/ | */ | ||||
void | void | ||||
sysctl_wlock(void) | sysctl_wlock(void) | ||||
{ | { | ||||
SYSCTL_WLOCK(); | SYSCTL_LOCK(); | ||||
} | } | ||||
void | void | ||||
sysctl_wunlock(void) | sysctl_wunlock(void) | ||||
{ | { | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
} | } | ||||
static int | static int | ||||
sysctl_root_handler_locked(struct sysctl_oid *oid, void *arg1, intmax_t arg2, | sysctl_root_handler_locked(struct sysctl_oid *oid, void *arg1, intmax_t arg2, | ||||
struct sysctl_req *req, struct rm_priotracker *tracker) | struct sysctl_req *req) | ||||
{ | { | ||||
int error; | int error; | ||||
if (oid->oid_kind & CTLFLAG_DYN) | if (oid->oid_kind & CTLFLAG_DYN) | ||||
atomic_add_int(&oid->oid_running, 1); | atomic_add_int(&oid->oid_running, 1); | ||||
if (tracker != NULL) | SYSCTL_UNLOCK(); | ||||
SYSCTL_RUNLOCK(tracker); | |||||
else | |||||
SYSCTL_WUNLOCK(); | |||||
/* | /* | ||||
* Treat set CTLFLAG_NEEDGIANT and unset CTLFLAG_MPSAFE flags the same, | * Treat set CTLFLAG_NEEDGIANT and unset CTLFLAG_MPSAFE flags the same, | ||||
* untill we're ready to remove all traces of Giant from sysctl(9). | * untill we're ready to remove all traces of Giant from sysctl(9). | ||||
*/ | */ | ||||
if ((oid->oid_kind & CTLFLAG_NEEDGIANT) || | if ((oid->oid_kind & CTLFLAG_NEEDGIANT) || | ||||
(!(oid->oid_kind & CTLFLAG_MPSAFE))) | (!(oid->oid_kind & CTLFLAG_MPSAFE))) | ||||
mtx_lock(&Giant); | mtx_lock(&Giant); | ||||
error = oid->oid_handler(oid, arg1, arg2, req); | error = oid->oid_handler(oid, arg1, arg2, req); | ||||
if ((oid->oid_kind & CTLFLAG_NEEDGIANT) || | if ((oid->oid_kind & CTLFLAG_NEEDGIANT) || | ||||
(!(oid->oid_kind & CTLFLAG_MPSAFE))) | (!(oid->oid_kind & CTLFLAG_MPSAFE))) | ||||
mtx_unlock(&Giant); | mtx_unlock(&Giant); | ||||
KFAIL_POINT_ERROR(_debug_fail_point, sysctl_running, error); | KFAIL_POINT_ERROR(_debug_fail_point, sysctl_running, error); | ||||
if (tracker != NULL) | SYSCTL_LOCK(); | ||||
SYSCTL_RLOCK(tracker); | |||||
else | |||||
SYSCTL_WLOCK(); | |||||
if (oid->oid_kind & CTLFLAG_DYN) { | if (oid->oid_kind & CTLFLAG_DYN) { | ||||
if (atomic_fetchadd_int(&oid->oid_running, -1) == 1 && | if (atomic_fetchadd_int(&oid->oid_running, -1) == 1 && | ||||
(oid->oid_kind & CTLFLAG_DYING) != 0) | (oid->oid_kind & CTLFLAG_DYING) != 0) | ||||
wakeup(&oid->oid_running); | wakeup(&oid->oid_running); | ||||
} | } | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 126 Lines • ▼ Show 20 Lines | if (penv == NULL) | ||||
return; | return; | ||||
req.newlen = strlen(penv); | req.newlen = strlen(penv); | ||||
req.newptr = penv; | req.newptr = penv; | ||||
break; | break; | ||||
default: | default: | ||||
return; | return; | ||||
} | } | ||||
error = sysctl_root_handler_locked(oidp, oidp->oid_arg1, | error = sysctl_root_handler_locked(oidp, oidp->oid_arg1, | ||||
oidp->oid_arg2, &req, NULL); | oidp->oid_arg2, &req); | ||||
if (error != 0) | if (error != 0) | ||||
printf("Setting sysctl %s failed: %d\n", path + rem, error); | printf("Setting sysctl %s failed: %d\n", path + rem, error); | ||||
if (penv != NULL) | if (penv != NULL) | ||||
freeenv(penv); | freeenv(penv); | ||||
} | } | ||||
/* | /* | ||||
* Locate the path to a given oid. Returns the length of the resulting path, | * Locate the path to a given oid. Returns the length of the resulting path, | ||||
* or -1 if the oid was not found. nodes must have room for CTL_MAXNAME | * or -1 if the oid was not found. nodes must have room for CTL_MAXNAME | ||||
* elements and be NULL initialized. | * elements and be NULL initialized. | ||||
*/ | */ | ||||
static int | static int | ||||
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] = SPLAY_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] = SPLAY_MIN(sysctl_oid_list, | ||||
&nodes[indx - 1]->oid_children); | |||||
else | else | ||||
nodes[indx] = SLIST_NEXT(nodes[indx], oid_link); | nodes[indx] = SPLAY_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 All 32 Lines | sysctl_warn_reuse(const char *func, struct sysctl_oid *leaf) | ||||
(void)sbuf_finish(&sb); | (void)sbuf_finish(&sb); | ||||
} | } | ||||
#ifdef SYSCTL_DEBUG | #ifdef SYSCTL_DEBUG | ||||
static int | static int | ||||
sysctl_reuse_test(SYSCTL_HANDLER_ARGS) | sysctl_reuse_test(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct rm_priotracker tracker; | SYSCTL_LOCK(); | ||||
SYSCTL_RLOCK(&tracker); | |||||
sysctl_warn_reuse(__func__, oidp); | sysctl_warn_reuse(__func__, oidp); | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_UNLOCK(); | ||||
return (0); | return (0); | ||||
} | } | ||||
SYSCTL_PROC(_sysctl, OID_AUTO, reuse_test, | 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_LOCKED(); | ||||
p = sysctl_find_oidname(oidp->oid_name, parent); | p = sysctl_find_oidname(oidp->oid_name, parent); | ||||
if (p != NULL) { | if (p != NULL) { | ||||
if ((p->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | if ((p->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | ||||
p->oid_refcnt++; | p->oid_refcnt++; | ||||
return; | return; | ||||
} else { | } else { | ||||
sysctl_warn_reuse(__func__, p); | sysctl_warn_reuse(__func__, p); | ||||
return; | return; | ||||
Show All 25 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 = SPLAY_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) | SPLAY_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 18 Lines | if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) | ||||
oidp->oid_kind |= CTLFLAG_DORMANT; | oidp->oid_kind |= CTLFLAG_DORMANT; | ||||
sysctl_register_oid(oidp); | sysctl_register_oid(oidp); | ||||
} | } | ||||
void | void | ||||
sysctl_enable_oid(struct sysctl_oid *oidp) | sysctl_enable_oid(struct sysctl_oid *oidp) | ||||
{ | { | ||||
SYSCTL_ASSERT_WLOCKED(); | SYSCTL_ASSERT_LOCKED(); | ||||
if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | ||||
KASSERT((oidp->oid_kind & CTLFLAG_DORMANT) == 0, | KASSERT((oidp->oid_kind & CTLFLAG_DORMANT) == 0, | ||||
("sysctl node is marked as dormant")); | ("sysctl node is marked as dormant")); | ||||
return; | return; | ||||
} | } | ||||
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_LOCKED(); | ||||
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 (SPLAY_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 All 28 Lines | sysctl_ctx_free(struct sysctl_ctx_list *clist) | ||||
error = 0; | error = 0; | ||||
/* | /* | ||||
* First perform a "dry run" to check if it's ok to remove oids. | * First perform a "dry run" to check if it's ok to remove oids. | ||||
* XXX FIXME | * XXX FIXME | ||||
* XXX This algorithm is a hack. But I don't know any | * XXX This algorithm is a hack. But I don't know any | ||||
* XXX better solution for now... | * XXX better solution for now... | ||||
*/ | */ | ||||
SYSCTL_WLOCK(); | SYSCTL_LOCK(); | ||||
TAILQ_FOREACH(e, clist, link) { | TAILQ_FOREACH(e, clist, link) { | ||||
error = sysctl_remove_oid_locked(e->entry, 0, 0); | error = sysctl_remove_oid_locked(e->entry, 0, 0); | ||||
if (error) | if (error) | ||||
break; | break; | ||||
} | } | ||||
/* | /* | ||||
* Restore deregistered entries, either from the end, | * Restore deregistered entries, either from the end, | ||||
* or from the place where error occurred. | * or from the place where error occurred. | ||||
* e contains the entry that was not unregistered | * e contains the entry that was not unregistered | ||||
*/ | */ | ||||
if (error) | if (error) | ||||
e1 = TAILQ_PREV(e, sysctl_ctx_list, link); | e1 = TAILQ_PREV(e, sysctl_ctx_list, link); | ||||
else | else | ||||
e1 = TAILQ_LAST(clist, sysctl_ctx_list); | e1 = TAILQ_LAST(clist, sysctl_ctx_list); | ||||
while (e1 != NULL) { | while (e1 != NULL) { | ||||
sysctl_register_oid(e1->entry); | sysctl_register_oid(e1->entry); | ||||
e1 = TAILQ_PREV(e1, sysctl_ctx_list, link); | e1 = TAILQ_PREV(e1, sysctl_ctx_list, link); | ||||
} | } | ||||
if (error) { | if (error) { | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
return(EBUSY); | return(EBUSY); | ||||
} | } | ||||
/* Now really delete the entries */ | /* Now really delete the entries */ | ||||
e = TAILQ_FIRST(clist); | e = TAILQ_FIRST(clist); | ||||
while (e != NULL) { | while (e != NULL) { | ||||
e1 = TAILQ_NEXT(e, link); | e1 = TAILQ_NEXT(e, link); | ||||
error = sysctl_remove_oid_locked(e->entry, 1, 0); | error = sysctl_remove_oid_locked(e->entry, 1, 0); | ||||
if (error) | if (error) | ||||
panic("sysctl_remove_oid: corrupt tree, entry: %s", | panic("sysctl_remove_oid: corrupt tree, entry: %s", | ||||
e->entry->oid_name); | e->entry->oid_name); | ||||
free(e, M_SYSCTLOID); | free(e, M_SYSCTLOID); | ||||
e = e1; | e = e1; | ||||
} | } | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* Add an entry to the context */ | /* Add an entry to the context */ | ||||
struct sysctl_ctx_entry * | struct sysctl_ctx_entry * | ||||
sysctl_ctx_entry_add(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) | sysctl_ctx_entry_add(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) | ||||
{ | { | ||||
struct sysctl_ctx_entry *e; | struct sysctl_ctx_entry *e; | ||||
SYSCTL_ASSERT_WLOCKED(); | SYSCTL_ASSERT_LOCKED(); | ||||
if (clist == NULL || oidp == NULL) | if (clist == NULL || oidp == NULL) | ||||
return(NULL); | return(NULL); | ||||
e = malloc(sizeof(struct sysctl_ctx_entry), M_SYSCTLOID, M_WAITOK); | e = malloc(sizeof(struct sysctl_ctx_entry), M_SYSCTLOID, M_WAITOK); | ||||
e->entry = oidp; | e->entry = oidp; | ||||
TAILQ_INSERT_HEAD(clist, e, link); | TAILQ_INSERT_HEAD(clist, e, link); | ||||
return (e); | return (e); | ||||
} | } | ||||
/* Find an entry in the context */ | /* Find an entry in the context */ | ||||
struct sysctl_ctx_entry * | struct sysctl_ctx_entry * | ||||
sysctl_ctx_entry_find(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) | sysctl_ctx_entry_find(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) | ||||
{ | { | ||||
struct sysctl_ctx_entry *e; | struct sysctl_ctx_entry *e; | ||||
SYSCTL_ASSERT_WLOCKED(); | SYSCTL_ASSERT_LOCKED(); | ||||
if (clist == NULL || oidp == NULL) | if (clist == NULL || oidp == NULL) | ||||
return(NULL); | return(NULL); | ||||
TAILQ_FOREACH(e, clist, link) { | TAILQ_FOREACH(e, clist, link) { | ||||
if (e->entry == oidp) | if (e->entry == oidp) | ||||
return(e); | return(e); | ||||
} | } | ||||
return (e); | return (e); | ||||
} | } | ||||
/* | /* | ||||
* Delete an entry from the context. | * Delete an entry from the context. | ||||
* NOTE: this function doesn't free oidp! You have to remove it | * NOTE: this function doesn't free oidp! You have to remove it | ||||
* with sysctl_remove_oid(). | * with sysctl_remove_oid(). | ||||
*/ | */ | ||||
int | int | ||||
sysctl_ctx_entry_del(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) | sysctl_ctx_entry_del(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) | ||||
{ | { | ||||
struct sysctl_ctx_entry *e; | struct sysctl_ctx_entry *e; | ||||
if (clist == NULL || oidp == NULL) | if (clist == NULL || oidp == NULL) | ||||
return (EINVAL); | return (EINVAL); | ||||
SYSCTL_WLOCK(); | SYSCTL_LOCK(); | ||||
e = sysctl_ctx_entry_find(clist, oidp); | e = sysctl_ctx_entry_find(clist, oidp); | ||||
if (e != NULL) { | if (e != NULL) { | ||||
TAILQ_REMOVE(clist, e, link); | TAILQ_REMOVE(clist, e, link); | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
free(e, M_SYSCTLOID); | free(e, M_SYSCTLOID); | ||||
return (0); | return (0); | ||||
} else { | } else { | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Remove dynamically created sysctl trees. | * Remove dynamically created sysctl trees. | ||||
* oidp - top of the tree to be removed | * oidp - top of the tree to be removed | ||||
* del - if 0 - just deregister, otherwise free up entries as well | * del - if 0 - just deregister, otherwise free up entries as well | ||||
* recurse - if != 0 traverse the subtree to be deleted | * recurse - if != 0 traverse the subtree to be deleted | ||||
*/ | */ | ||||
int | int | ||||
sysctl_remove_oid(struct sysctl_oid *oidp, int del, int recurse) | sysctl_remove_oid(struct sysctl_oid *oidp, int del, int recurse) | ||||
{ | { | ||||
int error; | int error; | ||||
SYSCTL_WLOCK(); | SYSCTL_LOCK(); | ||||
error = sysctl_remove_oid_locked(oidp, del, recurse); | error = sysctl_remove_oid_locked(oidp, del, recurse); | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
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_LOCK(); | ||||
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_UNLOCK(); | ||||
} | |||||
} | |||||
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 All 34 Lines | |||||
} | } | ||||
static int | static int | ||||
sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse) | sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse) | ||||
{ | { | ||||
struct sysctl_oid *p, *tmp; | struct sysctl_oid *p, *tmp; | ||||
int error; | int error; | ||||
SYSCTL_ASSERT_WLOCKED(); | SYSCTL_ASSERT_LOCKED(); | ||||
if (oidp == NULL) | if (oidp == NULL) | ||||
return(EINVAL); | return(EINVAL); | ||||
if ((oidp->oid_kind & CTLFLAG_DYN) == 0) { | if ((oidp->oid_kind & CTLFLAG_DYN) == 0) { | ||||
printf("Warning: can't remove non-dynamic nodes (%s)!\n", | printf("Warning: can't remove non-dynamic nodes (%s)!\n", | ||||
oidp->oid_name); | oidp->oid_name); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* | /* | ||||
* 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 = SPLAY_MIN(sysctl_oid_list, &oidp->oid_children); | ||||
SYSCTL_CHILDREN(oidp), oid_link, tmp) { | p != NULL; p = tmp) { | ||||
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. | |||||
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 = SPLAY_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 41 Lines • ▼ Show 20 Lines | sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, | ||||
struct sysctl_oid *oidp; | struct sysctl_oid *oidp; | ||||
char *escaped; | char *escaped; | ||||
/* You have to hook up somewhere.. */ | /* You have to hook up somewhere.. */ | ||||
if (parent == NULL) | if (parent == NULL) | ||||
return(NULL); | return(NULL); | ||||
escaped = sysctl_escape_name(name); | escaped = sysctl_escape_name(name); | ||||
/* Check if the node already exists, otherwise create it */ | /* Check if the node already exists, otherwise create it */ | ||||
SYSCTL_WLOCK(); | SYSCTL_LOCK(); | ||||
oidp = sysctl_find_oidname(escaped, parent); | oidp = sysctl_find_oidname(escaped, parent); | ||||
if (oidp != NULL) { | if (oidp != NULL) { | ||||
free(escaped, M_SYSCTLOID); | free(escaped, M_SYSCTLOID); | ||||
if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | ||||
oidp->oid_refcnt++; | oidp->oid_refcnt++; | ||||
/* Update the context */ | /* Update the context */ | ||||
if (clist != NULL) | if (clist != NULL) | ||||
sysctl_ctx_entry_add(clist, oidp); | sysctl_ctx_entry_add(clist, oidp); | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
return (oidp); | return (oidp); | ||||
} else { | } else { | ||||
sysctl_warn_reuse(__func__, oidp); | sysctl_warn_reuse(__func__, oidp); | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
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); | ||||
mjgUnsubmitted 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… | |||||
asomersAuthorUnsubmitted 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); | SPLAY_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; | ||||
if (descr != NULL) | if (descr != NULL) | ||||
oidp->oid_descr = strdup(descr, M_SYSCTLOID); | oidp->oid_descr = strdup(descr, M_SYSCTLOID); | ||||
if (label != NULL) | if (label != NULL) | ||||
oidp->oid_label = strdup(label, M_SYSCTLOID); | oidp->oid_label = strdup(label, M_SYSCTLOID); | ||||
/* Update the context, if used */ | /* Update the context, if used */ | ||||
if (clist != NULL) | if (clist != NULL) | ||||
sysctl_ctx_entry_add(clist, oidp); | sysctl_ctx_entry_add(clist, oidp); | ||||
/* Register this oid */ | /* Register this oid */ | ||||
sysctl_register_oid(oidp); | sysctl_register_oid(oidp); | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
return (oidp); | return (oidp); | ||||
} | } | ||||
/* | /* | ||||
* Rename an existing oid. | * Rename an existing oid. | ||||
*/ | */ | ||||
void | void | ||||
sysctl_rename_oid(struct sysctl_oid *oidp, const char *name) | sysctl_rename_oid(struct sysctl_oid *oidp, const char *name) | ||||
{ | { | ||||
char *newname; | char *newname; | ||||
char *oldname; | char *oldname; | ||||
newname = strdup(name, M_SYSCTLOID); | newname = strdup(name, M_SYSCTLOID); | ||||
SYSCTL_WLOCK(); | SYSCTL_LOCK(); | ||||
oldname = __DECONST(char *, oidp->oid_name); | oldname = __DECONST(char *, oidp->oid_name); | ||||
oidp->oid_name = newname; | oidp->oid_name = newname; | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
free(oldname, M_SYSCTLOID); | free(oldname, M_SYSCTLOID); | ||||
} | } | ||||
/* | /* | ||||
* Reparent an existing oid. | * Reparent an existing oid. | ||||
*/ | */ | ||||
int | int | ||||
sysctl_move_oid(struct sysctl_oid *oid, struct sysctl_oid_list *parent) | sysctl_move_oid(struct sysctl_oid *oid, struct sysctl_oid_list *parent) | ||||
{ | { | ||||
struct sysctl_oid *oidp; | struct sysctl_oid *oidp; | ||||
SYSCTL_WLOCK(); | SYSCTL_LOCK(); | ||||
if (oid->oid_parent == parent) { | if (oid->oid_parent == parent) { | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
return (0); | return (0); | ||||
} | } | ||||
oidp = sysctl_find_oidname(oid->oid_name, parent); | oidp = sysctl_find_oidname(oid->oid_name, parent); | ||||
if (oidp != NULL) { | if (oidp != NULL) { | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
return (EEXIST); | return (EEXIST); | ||||
} | } | ||||
sysctl_unregister_oid(oid); | sysctl_unregister_oid(oid); | ||||
oid->oid_parent = parent; | oid->oid_parent = parent; | ||||
oid->oid_number = OID_AUTO; | oid->oid_number = OID_AUTO; | ||||
sysctl_register_oid(oid); | sysctl_register_oid(oid); | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Register the kernel's oids on startup. | * Register the kernel's oids on startup. | ||||
*/ | */ | ||||
SET_DECLARE(sysctl_set, struct sysctl_oid); | SET_DECLARE(sysctl_set, struct sysctl_oid); | ||||
static void | static void | ||||
sysctl_register_all(void *arg) | sysctl_register_all(void *arg) | ||||
{ | { | ||||
struct sysctl_oid **oidp; | struct sysctl_oid **oidp; | ||||
sx_init(&sysctlmemlock, "sysctl mem"); | sx_init(&sysctlmemlock, "sysctl mem"); | ||||
sx_init(&sysctlstringlock, "sysctl string handler"); | sx_init(&sysctlstringlock, "sysctl string handler"); | ||||
SYSCTL_INIT(); | SYSCTL_INIT(); | ||||
SYSCTL_WLOCK(); | SYSCTL_LOCK(); | ||||
SET_FOREACH(oidp, sysctl_set) | SET_FOREACH(oidp, sysctl_set) | ||||
sysctl_register_oid(*oidp); | sysctl_register_oid(*oidp); | ||||
SYSCTL_WUNLOCK(); | SYSCTL_UNLOCK(); | ||||
} | } | ||||
SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_FIRST, sysctl_register_all, NULL); | SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_FIRST, sysctl_register_all, NULL); | ||||
/* | /* | ||||
* "Staff-functions" | * "Staff-functions" | ||||
* | * | ||||
* These functions implement a presently undocumented interface | * These functions implement a presently undocumented interface | ||||
* used by the sysctl program to walk the tree, and get the type | * used by the sysctl program to walk the tree, and get the type | ||||
Show All 23 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) { | SPLAY_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 All 26 Lines | switch (oidp->oid_kind & CTLTYPE) { | ||||
default: printf("\n"); | default: printf("\n"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
sysctl_sysctl_debug(SYSCTL_HANDLER_ARGS) | sysctl_sysctl_debug(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct rm_priotracker tracker; | |||||
int error; | int error; | ||||
error = priv_check(req->td, PRIV_SYSCTL_DEBUG); | error = priv_check(req->td, PRIV_SYSCTL_DEBUG); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
SYSCTL_RLOCK(&tracker); | SYSCTL_LOCK(); | ||||
sysctl_sysctl_debug_dump_node(&sysctl__children, 0); | sysctl_sysctl_debug_dump_node(&sysctl__children, 0); | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_UNLOCK(); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
SYSCTL_PROC(_sysctl, CTL_SYSCTL_DEBUG, debug, CTLTYPE_STRING | CTLFLAG_RD | | SYSCTL_PROC(_sysctl, CTL_SYSCTL_DEBUG, debug, CTLTYPE_STRING | CTLFLAG_RD | | ||||
CTLFLAG_MPSAFE, 0, 0, sysctl_sysctl_debug, "-", ""); | CTLFLAG_MPSAFE, 0, 0, sysctl_sysctl_debug, "-", ""); | ||||
#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; | |||||
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_LOCK(); | ||||
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 = SPLAY_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_UNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* XXXRW/JA: Shouldn't return name data for nodes that we don't permit in | * XXXRW/JA: Shouldn't return name data for nodes that we don't permit in | ||||
* capability mode. | * capability mode. | ||||
*/ | */ | ||||
static SYSCTL_NODE(_sysctl, CTL_SYSCTL_NAME, name, CTLFLAG_RD | | static SYSCTL_NODE(_sysctl, CTL_SYSCTL_NAME, name, CTLFLAG_RD | | ||||
▲ Show 20 Lines • Show All 89 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 = SPLAY_FIND(sysctl_oid_list, lsp, &key); | |||||
} | |||||
if (!oidp) | |||||
oidp = SPLAY_MIN(sysctl_oid_list, lsp); | |||||
for(; oidp != NULL; oidp = SPLAY_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 All 19 Lines | |||||
static int | static int | ||||
sysctl_sysctl_next(SYSCTL_HANDLER_ARGS) | sysctl_sysctl_next(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int *name = (int *) arg1; | int *name = (int *) arg1; | ||||
u_int namelen = arg2; | u_int namelen = arg2; | ||||
int len, error; | int len, error; | ||||
bool success; | bool success; | ||||
struct sysctl_oid_list *lsp = &sysctl__children; | struct sysctl_oid_list *lsp = &sysctl__children; | ||||
struct rm_priotracker tracker; | |||||
int next[CTL_MAXNAME]; | int next[CTL_MAXNAME]; | ||||
len = 0; | len = 0; | ||||
SYSCTL_RLOCK(&tracker); | SYSCTL_LOCK(); | ||||
success = sysctl_sysctl_next_action(lsp, name, namelen, next, &len, 1, | success = sysctl_sysctl_next_action(lsp, name, namelen, next, &len, 1, | ||||
oidp->oid_number == CTL_SYSCTL_NEXT); | oidp->oid_number == CTL_SYSCTL_NEXT); | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_UNLOCK(); | ||||
if (!success) | if (!success) | ||||
return (ENOENT); | return (ENOENT); | ||||
error = SYSCTL_OUT(req, next, len * sizeof (int)); | error = SYSCTL_OUT(req, next, len * sizeof (int)); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* XXXRW/JA: Shouldn't return next data for nodes that we don't permit in | * XXXRW/JA: Shouldn't return next data for nodes that we don't permit in | ||||
Show All 12 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); | SPLAY_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 All 10 Lines | |||||
} | } | ||||
static int | static int | ||||
sysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS) | sysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
char *p; | char *p; | ||||
int error, oid[CTL_MAXNAME], len = 0; | int error, oid[CTL_MAXNAME], len = 0; | ||||
struct sysctl_oid *op = NULL; | struct sysctl_oid *op = NULL; | ||||
struct rm_priotracker tracker; | |||||
char buf[32]; | char buf[32]; | ||||
if (!req->newlen) | if (!req->newlen) | ||||
return (ENOENT); | return (ENOENT); | ||||
if (req->newlen >= MAXPATHLEN) /* XXX arbitrary, undocumented */ | if (req->newlen >= MAXPATHLEN) /* XXX arbitrary, undocumented */ | ||||
return (ENAMETOOLONG); | return (ENAMETOOLONG); | ||||
p = buf; | p = buf; | ||||
if (req->newlen >= sizeof(buf)) | if (req->newlen >= sizeof(buf)) | ||||
p = malloc(req->newlen+1, M_SYSCTL, M_WAITOK); | p = malloc(req->newlen+1, M_SYSCTL, M_WAITOK); | ||||
error = SYSCTL_IN(req, p, req->newlen); | error = SYSCTL_IN(req, p, req->newlen); | ||||
if (error) { | if (error) { | ||||
if (p != buf) | if (p != buf) | ||||
free(p, M_SYSCTL); | free(p, M_SYSCTL); | ||||
return (error); | return (error); | ||||
} | } | ||||
p [req->newlen] = '\0'; | p [req->newlen] = '\0'; | ||||
SYSCTL_RLOCK(&tracker); | SYSCTL_LOCK(); | ||||
error = name2oid(p, oid, &len, &op); | error = name2oid(p, oid, &len, &op); | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_UNLOCK(); | ||||
if (p != buf) | if (p != buf) | ||||
free(p, M_SYSCTL); | free(p, M_SYSCTL); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
error = SYSCTL_OUT(req, oid, len * sizeof *oid); | error = SYSCTL_OUT(req, oid, len * sizeof *oid); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* XXXRW/JA: Shouldn't return name2oid data for nodes that we don't permit in | * XXXRW/JA: Shouldn't return name2oid data for nodes that we don't permit in | ||||
* capability mode. | * capability mode. | ||||
*/ | */ | ||||
SYSCTL_PROC(_sysctl, CTL_SYSCTL_NAME2OID, name2oid, CTLTYPE_INT | CTLFLAG_RW | | SYSCTL_PROC(_sysctl, CTL_SYSCTL_NAME2OID, name2oid, CTLTYPE_INT | CTLFLAG_RW | | ||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, 0, 0, | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, 0, 0, | ||||
sysctl_sysctl_name2oid, "I", ""); | sysctl_sysctl_name2oid, "I", ""); | ||||
static int | static int | ||||
sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS) | sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct sysctl_oid *oid; | struct sysctl_oid *oid; | ||||
struct rm_priotracker tracker; | |||||
int error; | int error; | ||||
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_LOCK(); | ||||
error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); | error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
if (oid->oid_fmt == NULL) { | if (oid->oid_fmt == NULL) { | ||||
error = ENOENT; | error = ENOENT; | ||||
goto out; | goto out; | ||||
} | } | ||||
error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind)); | error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind)); | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1); | error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1); | ||||
out: | out: | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_UNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
static SYSCTL_NODE(_sysctl, CTL_SYSCTL_OIDFMT, oidfmt, CTLFLAG_RD | | static SYSCTL_NODE(_sysctl, CTL_SYSCTL_OIDFMT, oidfmt, CTLFLAG_RD | | ||||
CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_oidfmt, ""); | CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_oidfmt, ""); | ||||
static int | static int | ||||
sysctl_sysctl_oiddescr(SYSCTL_HANDLER_ARGS) | sysctl_sysctl_oiddescr(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct sysctl_oid *oid; | struct sysctl_oid *oid; | ||||
struct rm_priotracker tracker; | |||||
int error; | int error; | ||||
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_LOCK(); | ||||
error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); | error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
if (oid->oid_descr == NULL) { | if (oid->oid_descr == NULL) { | ||||
error = ENOENT; | error = ENOENT; | ||||
goto out; | goto out; | ||||
} | } | ||||
error = SYSCTL_OUT(req, oid->oid_descr, strlen(oid->oid_descr) + 1); | error = SYSCTL_OUT(req, oid->oid_descr, strlen(oid->oid_descr) + 1); | ||||
out: | out: | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_UNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
static SYSCTL_NODE(_sysctl, CTL_SYSCTL_OIDDESCR, oiddescr, CTLFLAG_RD | | static SYSCTL_NODE(_sysctl, CTL_SYSCTL_OIDDESCR, oiddescr, CTLFLAG_RD | | ||||
CTLFLAG_MPSAFE|CTLFLAG_CAPRD, sysctl_sysctl_oiddescr, ""); | CTLFLAG_MPSAFE|CTLFLAG_CAPRD, sysctl_sysctl_oiddescr, ""); | ||||
static int | static int | ||||
sysctl_sysctl_oidlabel(SYSCTL_HANDLER_ARGS) | sysctl_sysctl_oidlabel(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct sysctl_oid *oid; | struct sysctl_oid *oid; | ||||
struct rm_priotracker tracker; | |||||
int error; | int error; | ||||
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_LOCK(); | ||||
error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); | error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
if (oid->oid_label == NULL) { | if (oid->oid_label == NULL) { | ||||
error = ENOENT; | error = ENOENT; | ||||
goto out; | goto out; | ||||
} | } | ||||
error = SYSCTL_OUT(req, oid->oid_label, strlen(oid->oid_label) + 1); | error = SYSCTL_OUT(req, oid->oid_label, strlen(oid->oid_label) + 1); | ||||
out: | out: | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_UNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
static SYSCTL_NODE(_sysctl, CTL_SYSCTL_OIDLABEL, oidlabel, CTLFLAG_RD | | static SYSCTL_NODE(_sysctl, CTL_SYSCTL_OIDLABEL, oidlabel, CTLFLAG_RD | | ||||
CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_oidlabel, ""); | CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_oidlabel, ""); | ||||
/* | /* | ||||
* Default "handler" functions. | * Default "handler" functions. | ||||
▲ Show 20 Lines • Show All 654 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 = SPLAY_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 All 23 Lines | |||||
* Traverse our tree, and find the right node, execute whatever it points | * Traverse our tree, and find the right node, execute whatever it points | ||||
* to, and return the resulting error code. | * to, and return the resulting error code. | ||||
*/ | */ | ||||
static int | static int | ||||
sysctl_root(SYSCTL_HANDLER_ARGS) | sysctl_root(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct sysctl_oid *oid; | struct sysctl_oid *oid; | ||||
struct rm_priotracker tracker; | |||||
int error, indx, lvl; | int error, indx, lvl; | ||||
SYSCTL_RLOCK(&tracker); | SYSCTL_LOCK(); | ||||
error = sysctl_find_oid(arg1, arg2, &oid, &indx, req); | error = sysctl_find_oid(arg1, arg2, &oid, &indx, req); | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { | ||||
/* | /* | ||||
* You can't call a sysctl when it's a node, but has | * You can't call a sysctl when it's a node, but has | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | error = mac_system_check_sysctl(req->td->td_ucred, oid, arg1, arg2, | ||||
req); | req); | ||||
if (error != 0) | if (error != 0) | ||||
goto out; | goto out; | ||||
#endif | #endif | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
if ((oid->oid_kind & CTLFLAG_VNET) && arg1 != NULL) | if ((oid->oid_kind & CTLFLAG_VNET) && arg1 != NULL) | ||||
arg1 = (void *)(curvnet->vnet_data_base + (uintptr_t)arg1); | arg1 = (void *)(curvnet->vnet_data_base + (uintptr_t)arg1); | ||||
#endif | #endif | ||||
error = sysctl_root_handler_locked(oid, arg1, arg2, req, &tracker); | error = sysctl_root_handler_locked(oid, arg1, arg2, req); | ||||
out: | out: | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_UNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct __sysctl_args { | struct __sysctl_args { | ||||
int *name; | int *name; | ||||
u_int namelen; | u_int namelen; | ||||
void *old; | void *old; | ||||
▲ Show 20 Lines • Show All 722 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.