Fix O(n^2) behavior in sysctl
Sysctl OIDs were internally stored in linked lists, triggering O(n^2) behavior when userland iterates over many of them. The slowdown is noticeable for MIBs that have > 100 children (for example, vm.uma). But it's unignorable for kstat.zfs when a pool has > 1000 datasets. Convert the linked lists into RB trees. This produces a ~25x speedup for listing kstat.zfs with 4100 datasets, and no measurable penalty for small dataset counts. Bump __FreeBSD_version for the KPI change. Sponsored by: Axcient Reviewed by: mjg Differential Revision: https://reviews.freebsd.org/D36500
This commit is contained in:
parent
cee4fc7cad
commit
d3f96f6610
@ -246,7 +246,7 @@ sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp)
|
|||||||
struct attribute **attr;
|
struct attribute **attr;
|
||||||
struct sysctl_oid *oidp;
|
struct sysctl_oid *oidp;
|
||||||
|
|
||||||
SLIST_FOREACH(oidp, SYSCTL_CHILDREN(kobj->oidp), oid_link) {
|
RB_FOREACH(oidp, sysctl_oid_list, SYSCTL_CHILDREN(kobj->oidp)) {
|
||||||
if (strcmp(oidp->oid_name, grp->name) != 0)
|
if (strcmp(oidp->oid_name, grp->name) != 0)
|
||||||
continue;
|
continue;
|
||||||
for (attr = grp->attrs; *attr != NULL; attr++) {
|
for (attr = grp->attrs; *attr != NULL; attr++) {
|
||||||
|
@ -84,6 +84,8 @@ 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
|
||||||
@ -120,7 +122,7 @@ static struct sx sysctlstringlock;
|
|||||||
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,
|
||||||
@ -134,7 +136,7 @@ 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);
|
||||||
}
|
}
|
||||||
@ -356,11 +358,14 @@ sysctl_search_oid(struct sysctl_oid **nodes, struct sysctl_oid *needle)
|
|||||||
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);
|
||||||
@ -425,8 +430,7 @@ 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;
|
||||||
|
|
||||||
@ -476,25 +480,21 @@ sysctl_register_oid(struct sysctl_oid *oidp)
|
|||||||
* 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++;
|
}
|
||||||
}
|
|
||||||
} 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 &&
|
||||||
@ -504,10 +504,7 @@ sysctl_register_oid(struct sysctl_oid *oidp)
|
|||||||
}
|
}
|
||||||
/* 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
|
||||||
@ -556,7 +553,6 @@ sysctl_enable_oid(struct sysctl_oid *oidp)
|
|||||||
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();
|
||||||
@ -564,14 +560,8 @@ sysctl_unregister_oid(struct sysctl_oid *oidp)
|
|||||||
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) {
|
error = 0;
|
||||||
SLIST_REMOVE(oidp->oid_parent, oidp,
|
|
||||||
sysctl_oid, oid_link);
|
|
||||||
error = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -732,17 +722,14 @@ 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);
|
||||||
@ -811,14 +798,16 @@ sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse)
|
|||||||
*/
|
*/
|
||||||
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);
|
||||||
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)
|
||||||
@ -895,7 +884,7 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent,
|
|||||||
}
|
}
|
||||||
oidp = malloc(sizeof(struct sysctl_oid), M_SYSCTLOID, M_WAITOK|M_ZERO);
|
oidp = malloc(sizeof(struct sysctl_oid), M_SYSCTLOID, M_WAITOK|M_ZERO);
|
||||||
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;
|
||||||
@ -1016,7 +1005,7 @@ sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i)
|
|||||||
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(" ");
|
||||||
|
|
||||||
@ -1081,7 +1070,7 @@ 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];
|
||||||
@ -1105,10 +1094,9 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS)
|
|||||||
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)
|
||||||
@ -1120,14 +1108,9 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS)
|
|||||||
namelen--;
|
namelen--;
|
||||||
name++;
|
name++;
|
||||||
|
|
||||||
if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE)
|
if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE &&
|
||||||
break;
|
!oid->oid_handler)
|
||||||
|
lsp2 = SYSCTL_CHILDREN(oid);
|
||||||
if (oid->oid_handler)
|
|
||||||
break;
|
|
||||||
|
|
||||||
lsp2 = SYSCTL_CHILDREN(oid);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
lsp = lsp2;
|
lsp = lsp2;
|
||||||
}
|
}
|
||||||
@ -1239,13 +1222,25 @@ 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) {
|
||||||
@ -1254,13 +1249,13 @@ sysctl_sysctl_next_action(struct sysctl_oid_list *lsp, int *name, u_int namelen,
|
|||||||
}
|
}
|
||||||
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) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1332,13 +1327,12 @@ name2oid(char *name, int *oid, int *len, struct sysctl_oid **oidpp)
|
|||||||
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) {
|
||||||
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)++;
|
||||||
|
|
||||||
@ -2162,16 +2156,15 @@ sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid,
|
|||||||
{
|
{
|
||||||
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);
|
||||||
|
|
||||||
|
@ -525,7 +525,7 @@ vfs_register(struct vfsconf *vfc)
|
|||||||
* number.
|
* number.
|
||||||
*/
|
*/
|
||||||
sysctl_wlock();
|
sysctl_wlock();
|
||||||
SLIST_FOREACH(oidp, SYSCTL_CHILDREN(&sysctl___vfs), oid_link) {
|
RB_FOREACH(oidp, sysctl_oid_list, SYSCTL_CHILDREN(&sysctl___vfs)) {
|
||||||
if (strcmp(oidp->oid_name, vfc->vfc_name) == 0) {
|
if (strcmp(oidp->oid_name, vfc->vfc_name) == 0) {
|
||||||
sysctl_unregister_oid(oidp);
|
sysctl_unregister_oid(oidp);
|
||||||
oidp->oid_number = vfc->vfc_typenum;
|
oidp->oid_number = vfc->vfc_typenum;
|
||||||
|
@ -76,7 +76,7 @@
|
|||||||
* cannot include sys/param.h and should only be updated here.
|
* cannot include sys/param.h and should only be updated here.
|
||||||
*/
|
*/
|
||||||
#undef __FreeBSD_version
|
#undef __FreeBSD_version
|
||||||
#define __FreeBSD_version 1400070
|
#define __FreeBSD_version 1400071
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,
|
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,
|
||||||
|
@ -39,7 +39,8 @@
|
|||||||
#define _SYS_SYSCTL_H_
|
#define _SYS_SYSCTL_H_
|
||||||
|
|
||||||
#ifdef _KERNEL
|
#ifdef _KERNEL
|
||||||
#include <sys/queue.h>
|
#include <sys/tree.h>
|
||||||
|
#include <sys/systm.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -173,20 +174,25 @@ struct sysctl_req {
|
|||||||
int flags;
|
int flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
SLIST_HEAD(sysctl_oid_list, sysctl_oid);
|
struct sysctl_oid;
|
||||||
|
|
||||||
|
/* RB Tree handling */
|
||||||
|
RB_HEAD(sysctl_oid_list, sysctl_oid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This describes one "oid" in the MIB tree. Potentially more nodes can
|
* This describes one "oid" in the MIB tree. Potentially more nodes can
|
||||||
* be hidden behind it, expanded by the handler.
|
* be hidden behind it, expanded by the handler.
|
||||||
*/
|
*/
|
||||||
struct sysctl_oid {
|
struct sysctl_oid {
|
||||||
struct sysctl_oid_list oid_children;
|
struct sysctl_oid_list oid_children;
|
||||||
struct sysctl_oid_list *oid_parent;
|
struct sysctl_oid_list* oid_parent;
|
||||||
SLIST_ENTRY(sysctl_oid) oid_link;
|
RB_ENTRY(sysctl_oid) oid_link;
|
||||||
|
/* Sort key for all siblings, and lookup key for userland */
|
||||||
int oid_number;
|
int oid_number;
|
||||||
u_int oid_kind;
|
u_int oid_kind;
|
||||||
void *oid_arg1;
|
void *oid_arg1;
|
||||||
intmax_t oid_arg2;
|
intmax_t oid_arg2;
|
||||||
|
/* Must be unique amongst all siblings. */
|
||||||
const char *oid_name;
|
const char *oid_name;
|
||||||
int (*oid_handler)(SYSCTL_HANDLER_ARGS);
|
int (*oid_handler)(SYSCTL_HANDLER_ARGS);
|
||||||
const char *oid_fmt;
|
const char *oid_fmt;
|
||||||
@ -196,6 +202,19 @@ struct sysctl_oid {
|
|||||||
const char *oid_label;
|
const char *oid_label;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
cmp_sysctl_oid(struct sysctl_oid *a, struct sysctl_oid *b)
|
||||||
|
{
|
||||||
|
if (a->oid_number > b->oid_number)
|
||||||
|
return (1);
|
||||||
|
else if (a->oid_number < b->oid_number)
|
||||||
|
return (-1);
|
||||||
|
else
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_PROTOTYPE(sysctl_oid_list, sysctl_oid, oid_link, cmp_sysctl_oid);
|
||||||
|
|
||||||
#define SYSCTL_IN(r, p, l) (r->newfunc)(r, p, l)
|
#define SYSCTL_IN(r, p, l) (r->newfunc)(r, p, l)
|
||||||
#define SYSCTL_OUT(r, p, l) (r->oldfunc)(r, p, l)
|
#define SYSCTL_OUT(r, p, l) (r->oldfunc)(r, p, l)
|
||||||
#define SYSCTL_OUT_STR(r, p) (r->oldfunc)(r, p, strlen(p) + 1)
|
#define SYSCTL_OUT_STR(r, p) (r->oldfunc)(r, p, strlen(p) + 1)
|
||||||
@ -275,7 +294,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
|
|||||||
#define SYSCTL_OID_RAW(id, parent_child_head, nbr, name, kind, a1, a2, handler, fmt, descr, label) \
|
#define SYSCTL_OID_RAW(id, parent_child_head, nbr, name, kind, a1, a2, handler, fmt, descr, label) \
|
||||||
struct sysctl_oid id = { \
|
struct sysctl_oid id = { \
|
||||||
.oid_parent = (parent_child_head), \
|
.oid_parent = (parent_child_head), \
|
||||||
.oid_children = SLIST_HEAD_INITIALIZER(&id.oid_children), \
|
.oid_children = RB_INITIALIZER(&id.oid_children), \
|
||||||
.oid_number = (nbr), \
|
.oid_number = (nbr), \
|
||||||
.oid_kind = (kind), \
|
.oid_kind = (kind), \
|
||||||
.oid_arg1 = (a1), \
|
.oid_arg1 = (a1), \
|
||||||
|
Loading…
Reference in New Issue
Block a user