From c40e45b75eb76d79a05c7fa85c1fa9b5c728a12f Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 7 Oct 2016 15:10:20 +0200 Subject: Update to FreeBSD head 2016-08-23 Git mirror commit 9fe7c416e6abb28b1398fd3e5687099846800cfd. --- freebsd/sys/kern/kern_sysctl.c | 682 ++++++++++++++++++++++++++++++++--------- 1 file changed, 531 insertions(+), 151 deletions(-) (limited to 'freebsd/sys/kern/kern_sysctl.c') diff --git a/freebsd/sys/kern/kern_sysctl.c b/freebsd/sys/kern/kern_sysctl.c index 38201cd3..3bcf6688 100644 --- a/freebsd/sys/kern/kern_sysctl.c +++ b/freebsd/sys/kern/kern_sysctl.c @@ -47,7 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include +#include #include #include #include @@ -56,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -79,7 +80,7 @@ static MALLOC_DEFINE(M_SYSCTLTMP, "sysctltmp", "sysctl temp output buffer"); * The sysctllock protects the MIB tree. It also protects sysctl * contexts used with dynamic sysctls. The sysctl_register_oid() and * sysctl_unregister_oid() routines require the sysctllock to already - * be held, so the sysctl_lock() and sysctl_unlock() 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 * API rather than using the dynamic API. Use of the dynamic API is * strongly encouraged for most code. @@ -88,29 +89,38 @@ static MALLOC_DEFINE(M_SYSCTLTMP, "sysctltmp", "sysctl temp output buffer"); * sysctl requests. This is implemented by serializing any userland * sysctl requests larger than a single page via an exclusive lock. */ -static struct sx sysctllock; +static struct rmlock sysctllock; static struct sx sysctlmemlock; -#define SYSCTL_XLOCK() sx_xlock(&sysctllock) -#define SYSCTL_XUNLOCK() sx_xunlock(&sysctllock) -#define SYSCTL_ASSERT_XLOCKED() sx_assert(&sysctllock, SA_XLOCKED) -#define SYSCTL_INIT() sx_init(&sysctllock, "sysctl lock") +#define SYSCTL_WLOCK() rm_wlock(&sysctllock) +#define SYSCTL_WUNLOCK() rm_wunlock(&sysctllock) +#define SYSCTL_RLOCK(tracker) rm_rlock(&sysctllock, (tracker)) +#define SYSCTL_RUNLOCK(tracker) rm_runlock(&sysctllock, (tracker)) +#define SYSCTL_WLOCKED() rm_wowned(&sysctllock) +#define SYSCTL_ASSERT_LOCKED() rm_assert(&sysctllock, RA_LOCKED) +#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) \ - sx_sleep(ch, &sysctllock, 0, wmesg, timo) + rm_sleep(ch, &sysctllock, 0, wmesg, timo) static int sysctl_root(SYSCTL_HANDLER_ARGS); -struct sysctl_oid_list sysctl__children; /* root list */ +/* Root list */ +struct sysctl_oid_list sysctl__children = SLIST_HEAD_INITIALIZER(&sysctl__children); static int sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse); +static int sysctl_old_kernel(struct sysctl_req *, const void *, size_t); +static int sysctl_new_kernel(struct sysctl_req *, void *, size_t); static struct sysctl_oid * sysctl_find_oidname(const char *name, struct sysctl_oid_list *list) { struct sysctl_oid *oidp; - SYSCTL_ASSERT_XLOCKED(); + SYSCTL_ASSERT_LOCKED(); SLIST_FOREACH(oidp, list, oid_link) { if (strcmp(oidp->oid_name, name) == 0) { return (oidp); @@ -125,31 +135,212 @@ sysctl_find_oidname(const char *name, struct sysctl_oid_list *list) * Order by number in each list. */ void -sysctl_lock(void) +sysctl_wlock(void) { - SYSCTL_XLOCK(); + SYSCTL_WLOCK(); } void -sysctl_unlock(void) +sysctl_wunlock(void) { - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); } +static int +sysctl_root_handler_locked(struct sysctl_oid *oid, void *arg1, intmax_t arg2, + struct sysctl_req *req, struct rm_priotracker *tracker) +{ + int error; + + if (oid->oid_kind & CTLFLAG_DYN) + atomic_add_int(&oid->oid_running, 1); + + if (tracker != NULL) + SYSCTL_RUNLOCK(tracker); + else + SYSCTL_WUNLOCK(); + + if (!(oid->oid_kind & CTLFLAG_MPSAFE)) + mtx_lock(&Giant); + error = oid->oid_handler(oid, arg1, arg2, req); + if (!(oid->oid_kind & CTLFLAG_MPSAFE)) + mtx_unlock(&Giant); + + KFAIL_POINT_ERROR(_debug_fail_point, sysctl_running, error); + + if (tracker != NULL) + SYSCTL_RLOCK(tracker); + else + SYSCTL_WLOCK(); + + if (oid->oid_kind & CTLFLAG_DYN) { + if (atomic_fetchadd_int(&oid->oid_running, -1) == 1 && + (oid->oid_kind & CTLFLAG_DYING) != 0) + wakeup(&oid->oid_running); + } + + return (error); +} + +#ifndef __rtems__ +static void +sysctl_load_tunable_by_oid_locked(struct sysctl_oid *oidp) +{ + struct sysctl_req req; + struct sysctl_oid *curr; + char *penv = NULL; + char path[64]; + ssize_t rem = sizeof(path); + ssize_t len; + uint8_t val_8; + uint16_t val_16; + uint32_t val_32; + int val_int; + long val_long; + int64_t val_64; + quad_t val_quad; + int error; + + path[--rem] = 0; + + for (curr = oidp; curr != NULL; curr = SYSCTL_PARENT(curr)) { + len = strlen(curr->oid_name); + rem -= len; + if (curr != oidp) + rem -= 1; + if (rem < 0) { + printf("OID path exceeds %d bytes\n", (int)sizeof(path)); + return; + } + memcpy(path + rem, curr->oid_name, len); + if (curr != oidp) + path[rem + len] = '.'; + } + + memset(&req, 0, sizeof(req)); + + req.td = curthread; + req.oldfunc = sysctl_old_kernel; + req.newfunc = sysctl_new_kernel; + req.lock = REQ_UNWIRED; + + switch (oidp->oid_kind & CTLTYPE) { + case CTLTYPE_INT: + if (getenv_int(path + rem, &val_int) == 0) + return; + req.newlen = sizeof(val_int); + req.newptr = &val_int; + break; + case CTLTYPE_UINT: + if (getenv_uint(path + rem, (unsigned int *)&val_int) == 0) + return; + req.newlen = sizeof(val_int); + req.newptr = &val_int; + break; + case CTLTYPE_LONG: + if (getenv_long(path + rem, &val_long) == 0) + return; + req.newlen = sizeof(val_long); + req.newptr = &val_long; + break; + case CTLTYPE_ULONG: + if (getenv_ulong(path + rem, (unsigned long *)&val_long) == 0) + return; + req.newlen = sizeof(val_long); + req.newptr = &val_long; + break; + case CTLTYPE_S8: + if (getenv_int(path + rem, &val_int) == 0) + return; + val_8 = val_int; + req.newlen = sizeof(val_8); + req.newptr = &val_8; + break; + case CTLTYPE_S16: + if (getenv_int(path + rem, &val_int) == 0) + return; + val_16 = val_int; + req.newlen = sizeof(val_16); + req.newptr = &val_16; + break; + case CTLTYPE_S32: + if (getenv_long(path + rem, &val_long) == 0) + return; + val_32 = val_long; + req.newlen = sizeof(val_32); + req.newptr = &val_32; + break; + case CTLTYPE_S64: + if (getenv_quad(path + rem, &val_quad) == 0) + return; + val_64 = val_quad; + req.newlen = sizeof(val_64); + req.newptr = &val_64; + break; + case CTLTYPE_U8: + if (getenv_uint(path + rem, (unsigned int *)&val_int) == 0) + return; + val_8 = val_int; + req.newlen = sizeof(val_8); + req.newptr = &val_8; + break; + case CTLTYPE_U16: + if (getenv_uint(path + rem, (unsigned int *)&val_int) == 0) + return; + val_16 = val_int; + req.newlen = sizeof(val_16); + req.newptr = &val_16; + break; + case CTLTYPE_U32: + if (getenv_ulong(path + rem, (unsigned long *)&val_long) == 0) + return; + val_32 = val_long; + req.newlen = sizeof(val_32); + req.newptr = &val_32; + break; + case CTLTYPE_U64: + /* XXX there is no getenv_uquad() */ + if (getenv_quad(path + rem, &val_quad) == 0) + return; + val_64 = val_quad; + req.newlen = sizeof(val_64); + req.newptr = &val_64; + break; + case CTLTYPE_STRING: + penv = kern_getenv(path + rem); + if (penv == NULL) + return; + req.newlen = strlen(penv); + req.newptr = penv; + break; + default: + return; + } + error = sysctl_root_handler_locked(oidp, oidp->oid_arg1, + oidp->oid_arg2, &req, NULL); + if (error != 0) + printf("Setting sysctl %s failed: %d\n", path + rem, error); + if (penv != NULL) + freeenv(penv); +} +#endif /* __rtems__ */ + void sysctl_register_oid(struct sysctl_oid *oidp) { struct sysctl_oid_list *parent = oidp->oid_parent; struct sysctl_oid *p; struct sysctl_oid *q; + int oid_number; + int timeout = 2; /* * First check if another oid with the same name already * exists in the parent's list. */ - SYSCTL_ASSERT_XLOCKED(); + SYSCTL_ASSERT_WLOCKED(); p = sysctl_find_oidname(oidp->oid_name, parent); if (p != NULL) { if ((p->oid_kind & CTLTYPE) == CTLTYPE_NODE) { @@ -160,40 +351,83 @@ sysctl_register_oid(struct sysctl_oid *oidp) return; } } + /* get current OID number */ + oid_number = oidp->oid_number; + +#if (OID_AUTO >= 0) +#error "OID_AUTO is expected to be a negative value" +#endif /* - * If this oid has a number OID_AUTO, give it a number which - * is greater than any current oid. + * Any negative OID number qualifies as OID_AUTO. Valid OID + * numbers should always be positive. + * * NOTE: DO NOT change the starting value here, change it in * , and make sure it is at least 256 to - * accomodate e.g. net.inet.raw as a static sysctl node. + * accommodate e.g. net.inet.raw as a static sysctl node. */ - if (oidp->oid_number == OID_AUTO) { - static int newoid = CTL_AUTO_START; + if (oid_number < 0) { + static int newoid; - oidp->oid_number = newoid++; - if (newoid == 0x7fffffff) - panic("out of oids"); - } -#if 0 - else if (oidp->oid_number >= CTL_AUTO_START) { - /* do not panic; this happens when unregistering sysctl sets */ - printf("static sysctl oid too high: %d", oidp->oid_number); + /* + * By decrementing the next OID number we spend less + * time inserting the OIDs into a sorted list. + */ + if (--newoid < CTL_AUTO_START) + newoid = 0x7fffffff; + + oid_number = newoid; } -#endif /* - * Insert the oid into the parent's list in order. + * Insert the OID into the parent's list sorted by OID number. */ +retry: q = NULL; SLIST_FOREACH(p, parent, oid_link) { - if (oidp->oid_number < p->oid_number) + /* check if the current OID number is in use */ + if (oid_number == p->oid_number) { + /* get the next valid OID number */ + if (oid_number < CTL_AUTO_START || + oid_number == 0x7fffffff) { + /* wraparound - restart */ + oid_number = CTL_AUTO_START; + /* don't loop forever */ + if (!timeout--) + panic("sysctl: Out of OID numbers\n"); + goto retry; + } else { + oid_number++; + } + } else if (oid_number < p->oid_number) break; q = p; } - if (q) + /* check for non-auto OID number collision */ + if (oidp->oid_number >= 0 && oidp->oid_number < CTL_AUTO_START && + oid_number >= CTL_AUTO_START) { + printf("sysctl: OID number(%d) is already in use for '%s'\n", + oidp->oid_number, oidp->oid_name); + } + /* update the OID number, if any */ + oidp->oid_number = oid_number; + if (q != NULL) SLIST_INSERT_AFTER(q, oidp, oid_link); else SLIST_INSERT_HEAD(parent, oidp, oid_link); + + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE && +#ifdef VIMAGE + (oidp->oid_kind & CTLFLAG_VNET) == 0 && +#endif + (oidp->oid_kind & CTLFLAG_TUN) != 0 && + (oidp->oid_kind & CTLFLAG_NOFETCH) == 0) { + /* only fetch value once */ + oidp->oid_kind |= CTLFLAG_NOFETCH; +#ifndef __rtems__ + /* try to fetch value from kernel environment */ + sysctl_load_tunable_by_oid_locked(oidp); +#endif /* __rtems__ */ + } } void @@ -202,7 +436,7 @@ sysctl_unregister_oid(struct sysctl_oid *oidp) struct sysctl_oid *p; int error; - SYSCTL_ASSERT_XLOCKED(); + SYSCTL_ASSERT_WLOCKED(); error = ENOENT; if (oidp->oid_number == OID_AUTO) { error = EINVAL; @@ -258,7 +492,7 @@ sysctl_ctx_free(struct sysctl_ctx_list *clist) * XXX This algorithm is a hack. But I don't know any * XXX better solution for now... */ - SYSCTL_XLOCK(); + SYSCTL_WLOCK(); TAILQ_FOREACH(e, clist, link) { error = sysctl_remove_oid_locked(e->entry, 0, 0); if (error) @@ -266,7 +500,7 @@ sysctl_ctx_free(struct sysctl_ctx_list *clist) } /* * Restore deregistered entries, either from the end, - * or from the place where error occured. + * or from the place where error occurred. * e contains the entry that was not unregistered */ if (error) @@ -278,7 +512,7 @@ sysctl_ctx_free(struct sysctl_ctx_list *clist) e1 = TAILQ_PREV(e1, sysctl_ctx_list, link); } if (error) { - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); return(EBUSY); } /* Now really delete the entries */ @@ -292,7 +526,7 @@ sysctl_ctx_free(struct sysctl_ctx_list *clist) free(e, M_SYSCTLOID); e = e1; } - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); return (error); } @@ -302,7 +536,7 @@ sysctl_ctx_entry_add(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) { struct sysctl_ctx_entry *e; - SYSCTL_ASSERT_XLOCKED(); + SYSCTL_ASSERT_WLOCKED(); if (clist == NULL || oidp == NULL) return(NULL); e = malloc(sizeof(struct sysctl_ctx_entry), M_SYSCTLOID, M_WAITOK); @@ -317,7 +551,7 @@ sysctl_ctx_entry_find(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) { struct sysctl_ctx_entry *e; - SYSCTL_ASSERT_XLOCKED(); + SYSCTL_ASSERT_WLOCKED(); if (clist == NULL || oidp == NULL) return(NULL); TAILQ_FOREACH(e, clist, link) { @@ -339,15 +573,15 @@ sysctl_ctx_entry_del(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) if (clist == NULL || oidp == NULL) return (EINVAL); - SYSCTL_XLOCK(); + SYSCTL_WLOCK(); e = sysctl_ctx_entry_find(clist, oidp); if (e != NULL) { TAILQ_REMOVE(clist, e, link); - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); free(e, M_SYSCTLOID); return (0); } else { - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); return (ENOENT); } } @@ -363,9 +597,9 @@ sysctl_remove_oid(struct sysctl_oid *oidp, int del, int recurse) { int error; - SYSCTL_XLOCK(); + SYSCTL_WLOCK(); error = sysctl_remove_oid_locked(oidp, del, recurse); - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); return (error); } @@ -377,14 +611,14 @@ sysctl_remove_name(struct sysctl_oid *parent, const char *name, int error; error = ENOENT; - SYSCTL_XLOCK(); + SYSCTL_WLOCK(); SLIST_FOREACH_SAFE(p, SYSCTL_CHILDREN(parent), oid_link, tmp) { if (strcmp(p->oid_name, name) == 0) { error = sysctl_remove_oid_locked(p, del, recurse); break; } } - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); return (error); } @@ -396,7 +630,7 @@ sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse) struct sysctl_oid *p, *tmp; int error; - SYSCTL_ASSERT_XLOCKED(); + SYSCTL_ASSERT_WLOCKED(); if (oidp == NULL) return(EINVAL); if ((oidp->oid_kind & CTLFLAG_DYN) == 0) { @@ -414,15 +648,17 @@ sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse) if (oidp->oid_refcnt == 1) { SLIST_FOREACH_SAFE(p, SYSCTL_CHILDREN(oidp), oid_link, tmp) { - if (!recurse) + if (!recurse) { + printf("Warning: failed attempt to " + "remove oid %s with child %s\n", + oidp->oid_name, p->oid_name); return (ENOTEMPTY); + } error = sysctl_remove_oid_locked(p, del, recurse); if (error) return (error); } - if (del) - free(SYSCTL_CHILDREN(oidp), M_SYSCTLOID); } } if (oidp->oid_refcnt > 1 ) { @@ -460,7 +696,7 @@ sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse) */ struct sysctl_oid * sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, - int number, const char *name, int kind, void *arg1, intptr_t arg2, + int number, const char *name, int kind, void *arg1, intmax_t arg2, int (*handler)(SYSCTL_HANDLER_ARGS), const char *fmt, const char *descr) { struct sysctl_oid *oidp; @@ -469,7 +705,7 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, if (parent == NULL) return(NULL); /* Check if the node already exists, otherwise create it */ - SYSCTL_XLOCK(); + SYSCTL_WLOCK(); oidp = sysctl_find_oidname(name, parent); if (oidp != NULL) { if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { @@ -477,41 +713,33 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, /* Update the context */ if (clist != NULL) sysctl_ctx_entry_add(clist, oidp); - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); return (oidp); } else { - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); printf("can't re-use a leaf (%s)!\n", name); return (NULL); } } oidp = malloc(sizeof(struct sysctl_oid), M_SYSCTLOID, M_WAITOK|M_ZERO); oidp->oid_parent = parent; - SLIST_NEXT(oidp, oid_link) = NULL; + SLIST_INIT(&oidp->oid_children); oidp->oid_number = number; oidp->oid_refcnt = 1; oidp->oid_name = strdup(name, M_SYSCTLOID); oidp->oid_handler = handler; oidp->oid_kind = CTLFLAG_DYN | kind; - if ((kind & CTLTYPE) == CTLTYPE_NODE) { - /* Allocate space for children */ - SYSCTL_CHILDREN_SET(oidp, malloc(sizeof(struct sysctl_oid_list), - M_SYSCTLOID, M_WAITOK)); - SLIST_INIT(SYSCTL_CHILDREN(oidp)); - oidp->oid_arg2 = arg2; - } else { - oidp->oid_arg1 = arg1; - oidp->oid_arg2 = arg2; - } + oidp->oid_arg1 = arg1; + oidp->oid_arg2 = arg2; oidp->oid_fmt = fmt; - if (descr) + if (descr != NULL) oidp->oid_descr = strdup(descr, M_SYSCTLOID); /* Update the context, if used */ if (clist != NULL) sysctl_ctx_entry_add(clist, oidp); /* Register this oid */ sysctl_register_oid(oidp); - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); return (oidp); } @@ -525,10 +753,10 @@ sysctl_rename_oid(struct sysctl_oid *oidp, const char *name) char *oldname; newname = strdup(name, M_SYSCTLOID); - SYSCTL_XLOCK(); + SYSCTL_WLOCK(); oldname = __DECONST(char *, oidp->oid_name); oidp->oid_name = newname; - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); free(oldname, M_SYSCTLOID); } @@ -540,21 +768,21 @@ sysctl_move_oid(struct sysctl_oid *oid, struct sysctl_oid_list *parent) { struct sysctl_oid *oidp; - SYSCTL_XLOCK(); + SYSCTL_WLOCK(); if (oid->oid_parent == parent) { - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); return (0); } oidp = sysctl_find_oidname(oid->oid_name, parent); if (oidp != NULL) { - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); return (EEXIST); } sysctl_unregister_oid(oid); oid->oid_parent = parent; oid->oid_number = OID_AUTO; sysctl_register_oid(oid); - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); return (0); } @@ -570,12 +798,12 @@ sysctl_register_all(void *arg) sx_init(&sysctlmemlock, "sysctl mem"); SYSCTL_INIT(); - SYSCTL_XLOCK(); + SYSCTL_WLOCK(); SET_FOREACH(oidp, sysctl_set) sysctl_register_oid(*oidp); - SYSCTL_XUNLOCK(); + SYSCTL_WUNLOCK(); } -SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_all, 0); +SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_FIRST, sysctl_register_all, 0); /* * "Staff-functions" @@ -603,7 +831,7 @@ sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i) int k; struct sysctl_oid *oidp; - SYSCTL_ASSERT_XLOCKED(); + SYSCTL_ASSERT_LOCKED(); SLIST_FOREACH(oidp, l, oid_link) { for (k=0; koid_handler) { sysctl_sysctl_debug_dump_node( - oidp->oid_arg1, i+2); + SYSCTL_CHILDREN(oidp), i + 2); } break; case CTLTYPE_INT: printf(" Int\n"); break; @@ -631,8 +859,14 @@ sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i) case CTLTYPE_LONG: printf(" Long\n"); break; case CTLTYPE_ULONG: printf(" u_long\n"); break; case CTLTYPE_STRING: printf(" String\n"); break; - case CTLTYPE_U64: printf(" uint64_t\n"); break; + case CTLTYPE_S8: printf(" int8_t\n"); break; + case CTLTYPE_S16: printf(" int16_t\n"); break; + case CTLTYPE_S32: printf(" int32_t\n"); break; case CTLTYPE_S64: printf(" int64_t\n"); break; + case CTLTYPE_U8: printf(" uint8_t\n"); break; + case CTLTYPE_U16: printf(" uint16_t\n"); break; + case CTLTYPE_U32: printf(" uint32_t\n"); break; + case CTLTYPE_U64: printf(" uint64_t\n"); break; case CTLTYPE_OPAQUE: printf(" Opaque/struct\n"); break; default: printf("\n"); } @@ -643,18 +877,19 @@ sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i) static int sysctl_sysctl_debug(SYSCTL_HANDLER_ARGS) { + struct rm_priotracker tracker; int error; error = priv_check(req->td, PRIV_SYSCTL_DEBUG); if (error) return (error); - SYSCTL_XLOCK(); + SYSCTL_RLOCK(&tracker); sysctl_sysctl_debug_dump_node(&sysctl__children, 0); - SYSCTL_XUNLOCK(); + SYSCTL_RUNLOCK(&tracker); return (ENOENT); } -SYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD, +SYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_MPSAFE, 0, 0, sysctl_sysctl_debug, "-", ""); #endif @@ -666,9 +901,10 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) int error = 0; struct sysctl_oid *oid; struct sysctl_oid_list *lsp = &sysctl__children, *lsp2; + struct rm_priotracker tracker; char buf[10]; - SYSCTL_XLOCK(); + SYSCTL_RLOCK(&tracker); while (namelen) { if (!lsp) { snprintf(buf,sizeof(buf),"%d",*name); @@ -682,7 +918,7 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) name++; continue; } - lsp2 = 0; + lsp2 = NULL; SLIST_FOREACH(oid, lsp, oid_link) { if (oid->oid_number != *name) continue; @@ -711,7 +947,7 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) } error = SYSCTL_OUT(req, "", 1); out: - SYSCTL_XUNLOCK(); + SYSCTL_RUNLOCK(&tracker); return (error); } @@ -719,7 +955,7 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) * XXXRW/JA: Shouldn't return name data for nodes that we don't permit in * capability mode. */ -static SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD | CTLFLAG_CAPRD, +static SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD | CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_name, ""); static int @@ -728,7 +964,7 @@ sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen, { struct sysctl_oid *oidp; - SYSCTL_ASSERT_XLOCKED(); + SYSCTL_ASSERT_LOCKED(); *len = level; SLIST_FOREACH(oidp, lsp, oid_link) { *next = oidp->oid_number; @@ -790,11 +1026,12 @@ sysctl_sysctl_next(SYSCTL_HANDLER_ARGS) int i, j, error; struct sysctl_oid *oid; struct sysctl_oid_list *lsp = &sysctl__children; + struct rm_priotracker tracker; int newoid[CTL_MAXNAME]; - SYSCTL_XLOCK(); + SYSCTL_RLOCK(&tracker); i = sysctl_sysctl_next_ls(lsp, name, namelen, newoid, &j, 1, &oid); - SYSCTL_XUNLOCK(); + SYSCTL_RUNLOCK(&tracker); if (i) return (ENOENT); error = SYSCTL_OUT(req, newoid, j * sizeof (int)); @@ -805,7 +1042,7 @@ sysctl_sysctl_next(SYSCTL_HANDLER_ARGS) * XXXRW/JA: Shouldn't return next data for nodes that we don't permit in * capability mode. */ -static SYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD | CTLFLAG_CAPRD, +static SYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD | CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_next, ""); static int @@ -815,7 +1052,7 @@ name2oid(char *name, int *oid, int *len, struct sysctl_oid **oidpp) struct sysctl_oid_list *lsp = &sysctl__children; char *p; - SYSCTL_ASSERT_XLOCKED(); + SYSCTL_ASSERT_LOCKED(); for (*len = 0; *len < CTL_MAXNAME;) { p = strsep(&name, "."); @@ -852,7 +1089,8 @@ sysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS) { char *p; int error, oid[CTL_MAXNAME], len = 0; - struct sysctl_oid *op = 0; + struct sysctl_oid *op = NULL; + struct rm_priotracker tracker; if (!req->newlen) return (ENOENT); @@ -869,9 +1107,9 @@ sysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS) p [req->newlen] = '\0'; - SYSCTL_XLOCK(); + SYSCTL_RLOCK(&tracker); error = name2oid(p, oid, &len, &op); - SYSCTL_XUNLOCK(); + SYSCTL_RUNLOCK(&tracker); free(p, M_SYSCTL); @@ -894,9 +1132,10 @@ static int sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS) { struct sysctl_oid *oid; + struct rm_priotracker tracker; int error; - SYSCTL_XLOCK(); + SYSCTL_RLOCK(&tracker); error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); if (error) goto out; @@ -910,7 +1149,7 @@ sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS) goto out; error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1); out: - SYSCTL_XUNLOCK(); + SYSCTL_RUNLOCK(&tracker); return (error); } @@ -922,9 +1161,10 @@ static int sysctl_sysctl_oiddescr(SYSCTL_HANDLER_ARGS) { struct sysctl_oid *oid; + struct rm_priotracker tracker; int error; - SYSCTL_XLOCK(); + SYSCTL_RLOCK(&tracker); error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); if (error) goto out; @@ -935,17 +1175,148 @@ sysctl_sysctl_oiddescr(SYSCTL_HANDLER_ARGS) } error = SYSCTL_OUT(req, oid->oid_descr, strlen(oid->oid_descr) + 1); out: - SYSCTL_XUNLOCK(); + SYSCTL_RUNLOCK(&tracker); return (error); } -static SYSCTL_NODE(_sysctl, 5, oiddescr, CTLFLAG_RD|CTLFLAG_CAPRD, +static SYSCTL_NODE(_sysctl, 5, oiddescr, CTLFLAG_RD|CTLFLAG_MPSAFE|CTLFLAG_CAPRD, sysctl_sysctl_oiddescr, ""); /* * Default "handler" functions. */ +/* + * Handle a bool. + * Two cases: + * a variable: point arg1 at it. + * a constant: pass it in arg2. + */ + +int +sysctl_handle_bool(SYSCTL_HANDLER_ARGS) +{ + uint8_t temp; + int error; + + /* + * Attempt to get a coherent snapshot by making a copy of the data. + */ + if (arg1) + temp = *(bool *)arg1 ? 1 : 0; + else + temp = arg2 ? 1 : 0; + + error = SYSCTL_OUT(req, &temp, sizeof(temp)); + if (error || !req->newptr) + return (error); + + if (!arg1) + error = EPERM; + else { + error = SYSCTL_IN(req, &temp, sizeof(temp)); + if (!error) + *(bool *)arg1 = temp ? 1 : 0; + } + return (error); +} + +/* + * Handle an int8_t, signed or unsigned. + * Two cases: + * a variable: point arg1 at it. + * a constant: pass it in arg2. + */ + +int +sysctl_handle_8(SYSCTL_HANDLER_ARGS) +{ + int8_t tmpout; + int error = 0; + + /* + * Attempt to get a coherent snapshot by making a copy of the data. + */ + if (arg1) + tmpout = *(int8_t *)arg1; + else + tmpout = arg2; + error = SYSCTL_OUT(req, &tmpout, sizeof(tmpout)); + + if (error || !req->newptr) + return (error); + + if (!arg1) + error = EPERM; + else + error = SYSCTL_IN(req, arg1, sizeof(tmpout)); + return (error); +} + +/* + * Handle an int16_t, signed or unsigned. + * Two cases: + * a variable: point arg1 at it. + * a constant: pass it in arg2. + */ + +int +sysctl_handle_16(SYSCTL_HANDLER_ARGS) +{ + int16_t tmpout; + int error = 0; + + /* + * Attempt to get a coherent snapshot by making a copy of the data. + */ + if (arg1) + tmpout = *(int16_t *)arg1; + else + tmpout = arg2; + error = SYSCTL_OUT(req, &tmpout, sizeof(tmpout)); + + if (error || !req->newptr) + return (error); + + if (!arg1) + error = EPERM; + else + error = SYSCTL_IN(req, arg1, sizeof(tmpout)); + return (error); +} + +/* + * Handle an int32_t, signed or unsigned. + * Two cases: + * a variable: point arg1 at it. + * a constant: pass it in arg2. + */ + +int +sysctl_handle_32(SYSCTL_HANDLER_ARGS) +{ + int32_t tmpout; + int error = 0; + + /* + * Attempt to get a coherent snapshot by making a copy of the data. + */ + if (arg1) + tmpout = *(int32_t *)arg1; + else + tmpout = arg2; + error = SYSCTL_OUT(req, &tmpout, sizeof(tmpout)); + + if (error || !req->newptr) + return (error); + + if (!arg1) + error = EPERM; + else + error = SYSCTL_IN(req, arg1, sizeof(tmpout)); + return (error); +} + /* * Handle an int, signed or unsigned. * Two cases: @@ -1091,26 +1462,38 @@ sysctl_handle_64(SYSCTL_HANDLER_ARGS) int sysctl_handle_string(SYSCTL_HANDLER_ARGS) { - int error=0; - char *tmparg; size_t outlen; + int error = 0, ro_string = 0; /* - * Attempt to get a coherent snapshot by copying to a - * temporary kernel buffer. + * A zero-length buffer indicates a fixed size read-only + * string: */ -retry: - outlen = strlen((char *)arg1)+1; - tmparg = malloc(outlen, M_SYSCTLTMP, M_WAITOK); - - if (strlcpy(tmparg, (char *)arg1, outlen) >= outlen) { - free(tmparg, M_SYSCTLTMP); - goto retry; + if (arg2 == 0) { + arg2 = strlen((char *)arg1) + 1; + ro_string = 1; } - error = SYSCTL_OUT(req, tmparg, outlen); - free(tmparg, M_SYSCTLTMP); + if (req->oldptr != NULL) { + char *tmparg; + if (ro_string) { + tmparg = arg1; + } else { + /* try to make a coherent snapshot of the string */ + tmparg = malloc(arg2, M_SYSCTLTMP, M_WAITOK); + memcpy(tmparg, arg1, arg2); + } + + outlen = strnlen(tmparg, arg2 - 1) + 1; + error = SYSCTL_OUT(req, tmparg, outlen); + + if (!ro_string) + free(tmparg, M_SYSCTLTMP); + } else { + outlen = strnlen((char *)arg1, arg2 - 1) + 1; + error = SYSCTL_OUT(req, NULL, outlen); + } if (error || !req->newptr) return (error); @@ -1121,7 +1504,6 @@ retry: error = SYSCTL_IN(req, arg1, arg2); ((char *)arg1)[arg2] = '\0'; } - return (error); } @@ -1243,9 +1625,7 @@ kernel_sysctl(struct thread *td, int *name, u_int namelen, void *old, req.newfunc = sysctl_new_kernel; req.lock = REQ_UNWIRED; - SYSCTL_XLOCK(); error = sysctl_root(0, name, namelen, &req); - SYSCTL_XUNLOCK(); if (req.lock == REQ_WIRED && req.validlen > 0) vsunlock(req.oldptr, req.validlen); @@ -1381,7 +1761,7 @@ sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid, struct sysctl_oid *oid; int indx; - SYSCTL_ASSERT_XLOCKED(); + SYSCTL_ASSERT_LOCKED(); lsp = &sysctl__children; indx = 0; while (indx < CTL_MAXNAME) { @@ -1426,13 +1806,14 @@ static int sysctl_root(SYSCTL_HANDLER_ARGS) { struct sysctl_oid *oid; + struct rm_priotracker tracker; int error, indx, lvl; - SYSCTL_ASSERT_XLOCKED(); + SYSCTL_RLOCK(&tracker); error = sysctl_find_oid(arg1, arg2, &oid, &indx, req); if (error) - return (error); + goto out; if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { /* @@ -1440,13 +1821,17 @@ sysctl_root(SYSCTL_HANDLER_ARGS) * no handler. Inform the user that it's a node. * The indx may or may not be the same as namelen. */ - if (oid->oid_handler == NULL) - return (EISDIR); + if (oid->oid_handler == NULL) { + error = EISDIR; + goto out; + } } /* Is this sysctl writable? */ - if (req->newptr && !(oid->oid_kind & CTLFLAG_WR)) - return (EPERM); + if (req->newptr && !(oid->oid_kind & CTLFLAG_WR)) { + error = EPERM; + goto out; + } #ifndef __rtems__ KASSERT(req->td != NULL, ("sysctl_root(): req->td == NULL")); @@ -1457,10 +1842,11 @@ sysctl_root(SYSCTL_HANDLER_ARGS) * writing unless specifically granted for the node. */ if (IN_CAPABILITY_MODE(req->td)) { - if (req->oldptr && !(oid->oid_kind & CTLFLAG_CAPRD)) - return (EPERM); - if (req->newptr && !(oid->oid_kind & CTLFLAG_CAPWR)) - return (EPERM); + if ((req->oldptr && !(oid->oid_kind & CTLFLAG_CAPRD)) || + (req->newptr && !(oid->oid_kind & CTLFLAG_CAPWR))) { + error = EPERM; + goto out; + } } #endif @@ -1469,7 +1855,7 @@ sysctl_root(SYSCTL_HANDLER_ARGS) lvl = (oid->oid_kind & CTLMASK_SECURE) >> CTLSHIFT_SECURE; error = securelevel_gt(req->td->td_ucred, lvl); if (error) - return (error); + goto out; } /* Is this sysctl writable by only privileged users? */ @@ -1487,14 +1873,16 @@ sysctl_root(SYSCTL_HANDLER_ARGS) priv = PRIV_SYSCTL_WRITE; error = priv_check(req->td, priv); if (error) - return (error); + goto out; } #else /* __rtems__ */ (void) lvl; #endif /* __rtems__ */ - if (!oid->oid_handler) - return (EINVAL); + if (!oid->oid_handler) { + error = EINVAL; + goto out; + } if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { arg1 = (int *)arg1 + indx; @@ -1507,25 +1895,16 @@ sysctl_root(SYSCTL_HANDLER_ARGS) error = mac_system_check_sysctl(req->td->td_ucred, oid, arg1, arg2, req); if (error != 0) - return (error); + goto out; #endif - oid->oid_running++; - SYSCTL_XUNLOCK(); - - if (!(oid->oid_kind & CTLFLAG_MPSAFE)) - mtx_lock(&Giant); - error = oid->oid_handler(oid, arg1, arg2, req); - if (!(oid->oid_kind & CTLFLAG_MPSAFE)) - mtx_unlock(&Giant); - -#ifndef __rtems__ - KFAIL_POINT_ERROR(_debug_fail_point, sysctl_running, error); -#endif /* __rtems__ */ +#ifdef VIMAGE + if ((oid->oid_kind & CTLFLAG_VNET) && arg1 != NULL) + arg1 = (void *)(curvnet->vnet_data_base + (uintptr_t)arg1); +#endif + error = sysctl_root_handler_locked(oid, arg1, arg2, req, &tracker); - SYSCTL_XLOCK(); - oid->oid_running--; - if (oid->oid_running == 0 && (oid->oid_kind & CTLFLAG_DYING) != 0) - wakeup(&oid->oid_running); +out: + SYSCTL_RUNLOCK(&tracker); return (error); } @@ -1616,7 +1995,7 @@ userland_sysctl(struct thread *td, int *name, u_int namelen, void *old, ktrsysctl(name, namelen); #endif - if (req.oldlen > PAGE_SIZE) { + if (req.oldptr && req.oldlen > PAGE_SIZE) { memlocked = 1; sx_xlock(&sysctlmemlock); } else @@ -1626,9 +2005,7 @@ userland_sysctl(struct thread *td, int *name, u_int namelen, void *old, for (;;) { req.oldidx = 0; req.newidx = 0; - SYSCTL_XLOCK(); error = sysctl_root(0, name, namelen, &req); - SYSCTL_XUNLOCK(); if (error != EAGAIN) break; kern_yield(PRI_USER); @@ -1674,7 +2051,10 @@ sbuf_new_for_sysctl(struct sbuf *s, char *buf, int length, struct sysctl_req *req) { - s = sbuf_new(s, buf, length, SBUF_FIXEDLEN); + /* Supply a default buffer size if none given. */ + if (buf == NULL && length == 0) + length = 64; + s = sbuf_new(s, buf, length, SBUF_FIXEDLEN | SBUF_INCLUDENUL); sbuf_set_drain(s, sbuf_sysctl_drain, req); return (s); } -- cgit v1.2.3