From af5333e0a02b2295304d4e029b15ee15a4fe2b3a Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 4 Nov 2013 11:33:00 +0100 Subject: Update to FreeBSD 8.4 --- freebsd/sys/kern/kern_sysctl.c | 294 ++++++++++++++++++++++++----------------- 1 file changed, 174 insertions(+), 120 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 750de376..fad7ec10 100644 --- a/freebsd/sys/kern/kern_sysctl.c +++ b/freebsd/sys/kern/kern_sysctl.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -53,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -87,13 +89,12 @@ static MALLOC_DEFINE(M_SYSCTLTMP, "sysctltmp", "sysctl temp output buffer"); static struct sx sysctllock; static struct sx sysctlmemlock; -#define SYSCTL_SLOCK() sx_slock(&sysctllock) -#define SYSCTL_SUNLOCK() sx_sunlock(&sysctllock) #define SYSCTL_XLOCK() sx_xlock(&sysctllock) #define SYSCTL_XUNLOCK() sx_xunlock(&sysctllock) #define SYSCTL_ASSERT_XLOCKED() sx_assert(&sysctllock, SA_XLOCKED) -#define SYSCTL_ASSERT_LOCKED() sx_assert(&sysctllock, SA_LOCKED) #define SYSCTL_INIT() sx_init(&sysctllock, "sysctl lock") +#define SYSCTL_SLEEP(ch, wmesg, timo) \ + sx_sleep(ch, &sysctllock, 0, wmesg, timo) static int sysctl_root(SYSCTL_HANDLER_ARGS); @@ -107,7 +108,7 @@ sysctl_find_oidname(const char *name, struct sysctl_oid_list *list) { struct sysctl_oid *oidp; - SYSCTL_ASSERT_LOCKED(); + SYSCTL_ASSERT_XLOCKED(); SLIST_FOREACH(oidp, list, oid_link) { if (strcmp(oidp->oid_name, name) == 0) { return (oidp); @@ -314,7 +315,7 @@ sysctl_ctx_entry_find(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp) { struct sysctl_ctx_entry *e; - SYSCTL_ASSERT_LOCKED(); + SYSCTL_ASSERT_XLOCKED(); if (clist == NULL || oidp == NULL) return(NULL); TAILQ_FOREACH(e, clist, link) { @@ -410,10 +411,20 @@ sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse) } sysctl_unregister_oid(oidp); if (del) { + /* + * Wait for all threads running the handler to drain. + * This preserves the previous behavior when the + * sysctl lock was held across a handler invocation, + * and is necessary for module unload correctness. + */ + while (oidp->oid_running > 0) { + oidp->oid_kind |= CTLFLAG_DYING; + SYSCTL_SLEEP(&oidp->oid_running, "oidrm", 0); + } if (oidp->oid_descr) - free((void *)(uintptr_t)(const void *)oidp->oid_descr, M_SYSCTLOID); - free((void *)(uintptr_t)(const void *)oidp->oid_name, - M_SYSCTLOID); + free(__DECONST(char *, oidp->oid_descr), + M_SYSCTLOID); + free(__DECONST(char *, oidp->oid_name), M_SYSCTLOID); free(oidp, M_SYSCTLOID); } } @@ -430,8 +441,6 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, int (*handler)(SYSCTL_HANDLER_ARGS), const char *fmt, const char *descr) { struct sysctl_oid *oidp; - ssize_t len; - char *newname; /* You have to hook up somewhere.. */ if (parent == NULL) @@ -458,11 +467,7 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, SLIST_NEXT(oidp, oid_link) = NULL; oidp->oid_number = number; oidp->oid_refcnt = 1; - len = strlen(name); - newname = malloc(len + 1, M_SYSCTLOID, M_WAITOK); - bcopy(name, newname, len + 1); - newname[len] = '\0'; - oidp->oid_name = newname; + oidp->oid_name = strdup(name, M_SYSCTLOID); oidp->oid_handler = handler; oidp->oid_kind = CTLFLAG_DYN | kind; if ((kind & CTLTYPE) == CTLTYPE_NODE) { @@ -475,12 +480,8 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, oidp->oid_arg2 = arg2; } oidp->oid_fmt = fmt; - if (descr) { - int len = strlen(descr) + 1; - oidp->oid_descr = malloc(len, M_SYSCTLOID, M_WAITOK); - if (oidp->oid_descr) - strcpy((char *)(uintptr_t)(const void *)oidp->oid_descr, descr); - } + if (descr) + oidp->oid_descr = strdup(descr, M_SYSCTLOID); /* Update the context, if used */ if (clist != NULL) sysctl_ctx_entry_add(clist, oidp); @@ -496,16 +497,12 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, void sysctl_rename_oid(struct sysctl_oid *oidp, const char *name) { - ssize_t len; char *newname; - void *oldname; + char *oldname; - len = strlen(name); - newname = malloc(len + 1, M_SYSCTLOID, M_WAITOK); - bcopy(name, newname, len + 1); - newname[len] = '\0'; + newname = strdup(name, M_SYSCTLOID); SYSCTL_XLOCK(); - oldname = (void *)(uintptr_t)(const void *)oidp->oid_name; + oldname = __DECONST(char *, oidp->oid_name); oidp->oid_name = newname; SYSCTL_XUNLOCK(); free(oldname, M_SYSCTLOID); @@ -582,7 +579,7 @@ sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i) int k; struct sysctl_oid *oidp; - SYSCTL_ASSERT_LOCKED(); + SYSCTL_ASSERT_XLOCKED(); SLIST_FOREACH(oidp, l, oid_link) { for (k=0; ktd, PRIV_SYSCTL_DEBUG); if (error) return (error); + SYSCTL_XLOCK(); sysctl_sysctl_debug_dump_node(&sysctl__children, 0); + SYSCTL_XUNLOCK(); return (ENOENT); } @@ -641,7 +640,7 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) struct sysctl_oid_list *lsp = &sysctl__children, *lsp2; char buf[10]; - SYSCTL_ASSERT_LOCKED(); + SYSCTL_XLOCK(); while (namelen) { if (!lsp) { snprintf(buf,sizeof(buf),"%d",*name); @@ -650,7 +649,7 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) if (!error) error = SYSCTL_OUT(req, buf, strlen(buf)); if (error) - return (error); + goto out; namelen--; name++; continue; @@ -666,7 +665,7 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) error = SYSCTL_OUT(req, oid->oid_name, strlen(oid->oid_name)); if (error) - return (error); + goto out; namelen--; name++; @@ -677,12 +676,15 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS) if (oid->oid_handler) break; - lsp2 = (struct sysctl_oid_list *)oid->oid_arg1; + lsp2 = SYSCTL_CHILDREN(oid); break; } lsp = lsp2; } - return (SYSCTL_OUT(req, "", 1)); + error = SYSCTL_OUT(req, "", 1); + out: + SYSCTL_XUNLOCK(); + return (error); } static SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD, sysctl_sysctl_name, ""); @@ -693,7 +695,7 @@ sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen, { struct sysctl_oid *oidp; - SYSCTL_ASSERT_LOCKED(); + SYSCTL_ASSERT_XLOCKED(); *len = level; SLIST_FOREACH(oidp, lsp, oid_link) { *next = oidp->oid_number; @@ -708,7 +710,7 @@ sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen, if (oidp->oid_handler) /* We really should call the handler here...*/ return (0); - lsp = (struct sysctl_oid_list *)oidp->oid_arg1; + lsp = SYSCTL_CHILDREN(oidp); if (!sysctl_sysctl_next_ls(lsp, 0, 0, next+1, len, level+1, oidpp)) return (0); @@ -723,7 +725,7 @@ sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen, return (0); if (oidp->oid_handler) return (0); - lsp = (struct sysctl_oid_list *)oidp->oid_arg1; + lsp = SYSCTL_CHILDREN(oidp); if (!sysctl_sysctl_next_ls(lsp, name+1, namelen-1, next+1, len, level+1, oidpp)) return (0); @@ -735,7 +737,7 @@ sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen, if (oidp->oid_handler) continue; - lsp = (struct sysctl_oid_list *)oidp->oid_arg1; + lsp = SYSCTL_CHILDREN(oidp); if (!sysctl_sysctl_next_ls(lsp, name+1, namelen-1, next+1, len, level+1, oidpp)) return (0); @@ -757,7 +759,9 @@ sysctl_sysctl_next(SYSCTL_HANDLER_ARGS) struct sysctl_oid_list *lsp = &sysctl__children; int newoid[CTL_MAXNAME]; + SYSCTL_XLOCK(); i = sysctl_sysctl_next_ls(lsp, name, namelen, newoid, &j, 1, &oid); + SYSCTL_XUNLOCK(); if (i) return (ENOENT); error = SYSCTL_OUT(req, newoid, j * sizeof (int)); @@ -769,39 +773,26 @@ static SYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD, sysctl_sysctl_next, ""); static int name2oid(char *name, int *oid, int *len, struct sysctl_oid **oidpp) { - int i; struct sysctl_oid *oidp; struct sysctl_oid_list *lsp = &sysctl__children; char *p; - SYSCTL_ASSERT_LOCKED(); - - if (!*name) - return (ENOENT); - - p = name + strlen(name) - 1 ; - if (*p == '.') - *p = '\0'; - - *len = 0; - - for (p = name; *p && *p != '.'; p++) - ; - i = *p; - if (i == '.') - *p = '\0'; + SYSCTL_ASSERT_XLOCKED(); - oidp = SLIST_FIRST(lsp); + for (*len = 0; *len < CTL_MAXNAME;) { + p = strsep(&name, "."); - while (oidp && *len < CTL_MAXNAME) { - if (strcmp(name, oidp->oid_name)) { - oidp = SLIST_NEXT(oidp, oid_link); - continue; + oidp = SLIST_FIRST(lsp); + for (;; oidp = SLIST_NEXT(oidp, oid_link)) { + if (oidp == NULL) + return (ENOENT); + if (strcmp(p, oidp->oid_name) == 0) + break; } *oid++ = oidp->oid_number; (*len)++; - if (!i) { + if (name == NULL || *name == '\0') { if (oidpp) *oidpp = oidp; return (0); @@ -813,14 +804,7 @@ name2oid(char *name, int *oid, int *len, struct sysctl_oid **oidpp) if (oidp->oid_handler) break; - lsp = (struct sysctl_oid_list *)oidp->oid_arg1; - oidp = SLIST_FIRST(lsp); - name = p+1; - for (p = name; *p && *p != '.'; p++) - ; - i = *p; - if (i == '.') - *p = '\0'; + lsp = SYSCTL_CHILDREN(oidp); } return (ENOENT); } @@ -832,8 +816,6 @@ sysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS) int error, oid[CTL_MAXNAME], len; struct sysctl_oid *op = 0; - SYSCTL_ASSERT_LOCKED(); - if (!req->newlen) return (ENOENT); if (req->newlen >= MAXPATHLEN) /* XXX arbitrary, undocumented */ @@ -848,8 +830,10 @@ sysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS) } p [req->newlen] = '\0'; - + len = 0; + SYSCTL_XLOCK(); error = name2oid(p, oid, &len, &op); + SYSCTL_XUNLOCK(); free(p, M_SYSCTL); @@ -869,16 +853,21 @@ sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS) struct sysctl_oid *oid; int error; + SYSCTL_XLOCK(); error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); if (error) - return (error); + goto out; - if (!oid->oid_fmt) - return (ENOENT); + if (oid->oid_fmt == NULL) { + error = ENOENT; + goto out; + } error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind)); if (error) - return (error); + goto out; error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1); + out: + SYSCTL_XUNLOCK(); return (error); } @@ -892,13 +881,18 @@ sysctl_sysctl_oiddescr(SYSCTL_HANDLER_ARGS) struct sysctl_oid *oid; int error; + SYSCTL_XLOCK(); error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); if (error) - return (error); + goto out; - if (!oid->oid_descr) - return (ENOENT); + if (oid->oid_descr == NULL) { + error = ENOENT; + goto out; + } error = SYSCTL_OUT(req, oid->oid_descr, strlen(oid->oid_descr) + 1); + out: + SYSCTL_XUNLOCK(); return (error); } @@ -966,7 +960,10 @@ sysctl_msec_to_ticks(SYSCTL_HANDLER_ARGS) /* - * Handle a long, signed or unsigned. arg1 points to it. + * Handle a long, signed or unsigned. + * Two cases: + * a variable: point arg1 at it. + * a constant: pass it in arg2. */ int @@ -981,9 +978,10 @@ sysctl_handle_long(SYSCTL_HANDLER_ARGS) /* * Attempt to get a coherent snapshot by making a copy of the data. */ - if (!arg1) - return (EINVAL); - tmplong = *(long *)arg1; + if (arg1) + tmplong = *(long *)arg1; + else + tmplong = arg2; #ifdef SCTL_MASK32 if (req->flags & SCTL_MASK32) { tmpint = tmplong; @@ -995,18 +993,24 @@ sysctl_handle_long(SYSCTL_HANDLER_ARGS) if (error || !req->newptr) return (error); + if (!arg1) + error = EPERM; #ifdef SCTL_MASK32 - if (req->flags & SCTL_MASK32) { + else if (req->flags & SCTL_MASK32) { error = SYSCTL_IN(req, &tmpint, sizeof(int)); *(long *)arg1 = (long)tmpint; - } else + } #endif + else error = SYSCTL_IN(req, arg1, sizeof(long)); return (error); } /* - * Handle a 64 bit int, signed or unsigned. arg1 points to it. + * Handle a 64 bit int, signed or unsigned. + * Two cases: + * a variable: point arg1 at it. + * a constant: pass it in arg2. */ int @@ -1018,15 +1022,19 @@ sysctl_handle_quad(SYSCTL_HANDLER_ARGS) /* * Attempt to get a coherent snapshot by making a copy of the data. */ - if (!arg1) - return (EINVAL); - tmpout = *(uint64_t *)arg1; + if (arg1) + tmpout = *(uint64_t *)arg1; + else + tmpout = arg2; error = SYSCTL_OUT(req, &tmpout, sizeof(uint64_t)); if (error || !req->newptr) return (error); - error = SYSCTL_IN(req, arg1, sizeof(uint64_t)); + if (!arg1) + error = EPERM; + else + error = SYSCTL_IN(req, arg1, sizeof(uint64_t)); return (error); } @@ -1192,9 +1200,9 @@ kernel_sysctl(struct thread *td, int *name, u_int namelen, void *old, req.newfunc = sysctl_new_kernel; req.lock = REQ_LOCKED; - SYSCTL_SLOCK(); + SYSCTL_XLOCK(); error = sysctl_root(0, name, namelen, &req); - SYSCTL_SUNLOCK(); + SYSCTL_XUNLOCK(); if (req.lock == REQ_WIRED && req.validlen > 0) vsunlock(req.oldptr, req.validlen); @@ -1241,8 +1249,8 @@ kernel_sysctlbyname(struct thread *td, char *name, void *old, size_t *oldlenp, static int sysctl_old_user(struct sysctl_req *req, const void *p, size_t l) { - int error = 0; size_t i, len, origidx; + int error; origidx = req->oldidx; req->oldidx += l; @@ -1263,10 +1271,14 @@ sysctl_old_user(struct sysctl_req *req, const void *p, size_t l) else { if (i > len - origidx) i = len - origidx; - error = copyout(p, (char *)req->oldptr + origidx, i); + if (req->lock == REQ_WIRED) { + error = copyout_nofault(p, (char *)req->oldptr + + origidx, i); + } else + error = copyout(p, (char *)req->oldptr + origidx, i); + if (error != 0) + return (error); } - if (error) - return (error); if (i < l) return (ENOMEM); return (0); @@ -1322,37 +1334,43 @@ int sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid, int *nindx, struct sysctl_req *req) { + struct sysctl_oid_list *lsp; struct sysctl_oid *oid; int indx; - SYSCTL_ASSERT_LOCKED(); - oid = SLIST_FIRST(&sysctl__children); + SYSCTL_ASSERT_XLOCKED(); + lsp = &sysctl__children; indx = 0; - while (oid && indx < CTL_MAXNAME) { - if (oid->oid_number == name[indx]) { - indx++; - if (oid->oid_kind & CTLFLAG_NOLOCK) - req->lock = REQ_UNLOCKED; - if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { - if (oid->oid_handler != NULL || - indx == namelen) { - *noid = oid; - if (nindx != NULL) - *nindx = indx; - return (0); - } - oid = SLIST_FIRST( - (struct sysctl_oid_list *)oid->oid_arg1); - } else if (indx == namelen) { + while (indx < CTL_MAXNAME) { + SLIST_FOREACH(oid, lsp, oid_link) { + if (oid->oid_number == name[indx]) + break; + } + if (oid == NULL) + return (ENOENT); + + indx++; + if (oid->oid_kind & CTLFLAG_NOLOCK) + req->lock = REQ_UNLOCKED; + if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { + if (oid->oid_handler != NULL || indx == namelen) { *noid = oid; if (nindx != NULL) *nindx = indx; + KASSERT((oid->oid_kind & CTLFLAG_DYING) == 0, + ("%s found DYING node %p", __func__, oid)); return (0); - } else { - return (ENOTDIR); } + lsp = SYSCTL_CHILDREN(oid); + } else if (indx == namelen) { + *noid = oid; + if (nindx != NULL) + *nindx = indx; + KASSERT((oid->oid_kind & CTLFLAG_DYING) == 0, + ("%s found DYING node %p", __func__, oid)); + return (0); } else { - oid = SLIST_NEXT(oid, oid_link); + return (ENOTDIR); } } return (ENOENT); @@ -1369,7 +1387,7 @@ sysctl_root(SYSCTL_HANDLER_ARGS) struct sysctl_oid *oid; int error, indx, lvl; - SYSCTL_ASSERT_LOCKED(); + SYSCTL_ASSERT_XLOCKED(); error = sysctl_find_oid(arg1, arg2, &oid, &indx, req); if (error) @@ -1437,12 +1455,23 @@ sysctl_root(SYSCTL_HANDLER_ARGS) if (error != 0) return (error); #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__ */ + + SYSCTL_XLOCK(); + oid->oid_running--; + if (oid->oid_running == 0 && (oid->oid_kind & CTLFLAG_DYING) != 0) + wakeup(&oid->oid_running); return (error); } @@ -1543,9 +1572,9 @@ userland_sysctl(struct thread *td, int *name, u_int namelen, void *old, for (;;) { req.oldidx = 0; req.newidx = 0; - SYSCTL_SLOCK(); + SYSCTL_XLOCK(); error = sysctl_root(0, name, namelen, &req); - SYSCTL_SUNLOCK(); + SYSCTL_XUNLOCK(); if (error != EAGAIN) break; uio_yield(); @@ -1569,4 +1598,29 @@ userland_sysctl(struct thread *td, int *name, u_int namelen, void *old, } return (error); } + +/* + * Drain into a sysctl struct. The user buffer should be wired if a page + * fault would cause issue. + */ +static int +sbuf_sysctl_drain(void *arg, const char *data, int len) +{ + struct sysctl_req *req = arg; + int error; + + error = SYSCTL_OUT(req, data, len); + KASSERT(error >= 0, ("Got unexpected negative value %d", error)); + return (error == 0 ? len : -error); +} + +struct sbuf * +sbuf_new_for_sysctl(struct sbuf *s, char *buf, int length, + struct sysctl_req *req) +{ + + s = sbuf_new(s, buf, length, SBUF_FIXEDLEN); + sbuf_set_drain(s, sbuf_sysctl_drain, req); + return (s); +} #endif /* __rtems__ */ -- cgit v1.2.3