Index: src/sys/ufs/ffs/ffs_alloc.c =================================================================== --- src/sys/ufs/ffs/ffs_alloc.c (.../current) (revision 81) +++ src/sys/ufs/ffs/ffs_alloc.c (.../sysctl_sx) (revision 128) @@ -2242,17 +2242,22 @@ return (error); if (cmd.version != FFS_CMD_VERSION) return (ERPCMISMATCH); + + mtx_lock(&Giant); + if ((error = getvnode(curproc->p_fd, cmd.handle, &fp)) != 0) return (error); vn_start_write(fp->f_data, &mp, V_WAIT); if (mp == 0 || strncmp(mp->mnt_stat.f_fstypename, "ufs", MFSNAMELEN)) { vn_finished_write(mp); fdrop(fp, curthread); + mtx_unlock(&Giant); return (EINVAL); } if (mp->mnt_flag & MNT_RDONLY) { vn_finished_write(mp); fdrop(fp, curthread); + mtx_unlock(&Giant); return (EROFS); } ump = VFSTOUFS(mp); @@ -2379,5 +2384,6 @@ } fdrop(fp, curthread); vn_finished_write(mp); + mtx_unlock(&Giant); return (error); } Index: src/sys/kern/kern_jail.c =================================================================== --- src/sys/kern/kern_jail.c (.../current) (revision 81) +++ src/sys/kern/kern_jail.c (.../sysctl_sx) (revision 128) @@ -451,18 +451,18 @@ { struct xprison *xp, *sxp; struct prison *pr; - int count, error; + int count, error = 0; - mtx_assert(&Giant, MA_OWNED); + mtx_lock(&Giant); if (jailed(req->td->td_ucred)) - return (0); + goto done; retry: mtx_lock(&allprison_mtx); count = prisoncount; mtx_unlock(&allprison_mtx); if (count == 0) - return (0); + goto done; sxp = xp = malloc(sizeof(*xp) * count, M_TEMP, M_WAITOK | M_ZERO); mtx_lock(&allprison_mtx); @@ -483,9 +483,11 @@ xp++; } mtx_unlock(&allprison_mtx); - + error = SYSCTL_OUT(req, sxp, sizeof(*sxp) * count); free(sxp, M_TEMP); +done: + mtx_unlock(&Giant); if (error) return (error); return (0); Index: src/sys/kern/kern_sx.c =================================================================== --- src/sys/kern/kern_sx.c (.../current) (revision 81) +++ src/sys/kern/kern_sx.c (.../sysctl_sx) (revision 128) @@ -367,3 +367,15 @@ } } #endif /* INVARIANT_SUPPORT */ + +int +sx_xlocked(struct sx *sx) +{ + mtx_lock(sx->sx_lock); + if (sx->sx_xholder == curthread) { + mtx_unlock(sx->sx_lock); + return (1); + } + mtx_unlock(sx->sx_lock); + return (0); +} Index: src/sys/kern/kern_sysctl.c =================================================================== --- src/sys/kern/kern_sysctl.c (.../current) (revision 81) +++ src/sys/kern/kern_sysctl.c (.../sysctl_sx) (revision 128) @@ -62,12 +62,21 @@ /* * Locking - this locks the sysctl tree in memory. */ -static struct sx sysctllock; +static struct mtx sysctl_ctx_mtx; +static struct sx sysctl_sx; -#define SYSCTL_LOCK() sx_xlock(&sysctllock) -#define SYSCTL_UNLOCK() sx_xunlock(&sysctllock) -#define SYSCTL_INIT() sx_init(&sysctllock, "sysctl lock") +#define SYSCTL_XLOCK() sx_xlock(&sysctl_sx) +#define SYSCTL_SLOCK() sx_slock(&sysctl_sx) +#define SYSCTL_XUNLOCK() sx_xunlock(&sysctl_sx) +#define SYSCTL_SUNLOCK() sx_sunlock(&sysctl_sx) +#define SYSCTL_INIT() sx_init(&sysctl_sx, "sysctl sx lock") +#define SYSCTL_CTX_LOCK() mtx_lock(&sysctl_ctx_mtx) +#define SYSCTL_CTX_UNLOCK() mtx_unlock(&sysctl_ctx_mtx) +#define SYSCTL_CTX_INIT() mtx_init(&sysctl_ctx_mtx, \ + "sysctl context lock", \ + NULL, MTX_DEF) + static int sysctl_root(SYSCTL_HANDLER_ARGS); struct sysctl_oid_list sysctl__children; /* root list */ @@ -77,6 +86,8 @@ { struct sysctl_oid *oidp; + sx_assert(&sysctl_sx, SX_LOCKED); + SLIST_FOREACH(oidp, list, oid_link) { if (strcmp(oidp->oid_name, name) == 0) { return (oidp); @@ -91,25 +102,39 @@ * Order by number in each list. */ -void +int sysctl_register_oid(struct sysctl_oid *oidp) { struct sysctl_oid_list *parent = oidp->oid_parent; struct sysctl_oid *p; struct sysctl_oid *q; + int error; + int locked; + locked = sx_xlocked(&sysctl_sx); + if (!locked) + SYSCTL_XLOCK(); + /* * First check if another oid with the same name already * exists in the parent's list. */ + + if (parent == NULL) { /* The oid has no parent! */ + error = 1; + goto done; + } + p = sysctl_find_oidname(oidp->oid_name, parent); if (p != NULL) { if ((p->oid_kind & CTLTYPE) == CTLTYPE_NODE) { p->oid_refcnt++; - return; + error = 1; + goto done; } else { printf("can't re-use a leaf (%s)!\n", p->oid_name); - return; + error = 1; + goto done; } } /* @@ -146,19 +171,30 @@ SLIST_INSERT_AFTER(q, oidp, oid_link); else SLIST_INSERT_HEAD(parent, oidp, oid_link); + error = 0; +done: + if (!locked) + SYSCTL_XUNLOCK(); + return (error); } void sysctl_unregister_oid(struct sysctl_oid *oidp) { struct sysctl_oid *p; + struct sysctl_oid_list *parent = oidp->oid_parent; int error; + int locked; + locked = sx_xlocked(&sysctl_sx); + if (!locked) + SYSCTL_XLOCK(); + error = ENOENT; if (oidp->oid_number == OID_AUTO) { error = EINVAL; } else { - SLIST_FOREACH(p, oidp->oid_parent, oid_link) { + SLIST_FOREACH(p, parent, oid_link) { if (p == oidp) { SLIST_REMOVE(oidp->oid_parent, oidp, sysctl_oid, oid_link); @@ -167,7 +203,8 @@ } } } - + if (!locked) + SYSCTL_XUNLOCK(); /* * This can happen when a module fails to register and is * being unloaded afterwards. It should not be a panic() @@ -203,6 +240,8 @@ * XXX This algorithm is a hack. But I don't know any * XXX better solution for now... */ + + SYSCTL_CTX_LOCK(); TAILQ_FOREACH(e, clist, link) { error = sysctl_remove_oid(e->entry, 0, 0); if (error) @@ -221,8 +260,10 @@ sysctl_register_oid(e1->entry); e1 = TAILQ_PREV(e1, sysctl_ctx_list, link); } - if (error) + if (error) { + SYSCTL_CTX_UNLOCK(); return(EBUSY); + } /* Now really delete the entries */ e = TAILQ_FIRST(clist); while (e != NULL) { @@ -234,6 +275,7 @@ free(e, M_SYSCTLOID); e = e1; } + SYSCTL_CTX_UNLOCK(); return (error); } @@ -247,7 +289,9 @@ return(NULL); e = malloc(sizeof(struct sysctl_ctx_entry), M_SYSCTLOID, M_WAITOK); e->entry = oidp; + SYSCTL_CTX_LOCK(); TAILQ_INSERT_HEAD(clist, e, link); + SYSCTL_CTX_UNLOCK(); return (e); } @@ -278,17 +322,21 @@ if (clist == NULL || oidp == NULL) return (EINVAL); + SYSCTL_CTX_LOCK(); e = sysctl_ctx_entry_find(clist, oidp); if (e != NULL) { TAILQ_REMOVE(clist, e, link); free(e, M_SYSCTLOID); + SYSCTL_CTX_UNLOCK(); return (0); - } else + } else { + SYSCTL_CTX_UNLOCK(); return (ENOENT); + } } /* - * Remove dynamically created sysctl trees. + * Really remove dynamically created sysctl trees. * oidp - top of the tree to be removed * del - if 0 - just deregister, otherwise free up entries as well * recurse - if != 0 traverse the subtree to be deleted @@ -298,12 +346,23 @@ { struct sysctl_oid *p; int error; + int locked; if (oidp == NULL) return(EINVAL); + + /* + * Only aquire the exclusive lock if we are really going to delete a + * node, and we don't already hold it. + */ + locked = sx_xlocked(&sysctl_sx); + if (del && (!locked)) + SYSCTL_XLOCK(); + if ((oidp->oid_kind & CTLFLAG_DYN) == 0) { printf("can't remove non-dynamic nodes!\n"); - return (EINVAL); + error = EINVAL; + goto done; } /* * WARNING: normal method to do this should be through @@ -315,23 +374,26 @@ if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { if (oidp->oid_refcnt == 1) { SLIST_FOREACH(p, SYSCTL_CHILDREN(oidp), oid_link) { - if (!recurse) - return (ENOTEMPTY); + if (!recurse) { + error = ENOTEMPTY; + goto done; + } error = sysctl_remove_oid(p, del, recurse); if (error) - return (error); + goto done; } if (del) free(SYSCTL_CHILDREN(oidp), M_SYSCTLOID); } } - if (oidp->oid_refcnt > 1 ) { + if (oidp->oid_refcnt > 1) { oidp->oid_refcnt--; } else { if (oidp->oid_refcnt == 0) { printf("Warning: bad oid_refcnt=%u (%s)!\n", oidp->oid_refcnt, oidp->oid_name); - return (EINVAL); + error = EINVAL; + goto done; } sysctl_unregister_oid(oidp); if (del) { @@ -342,7 +404,11 @@ free(oidp, M_SYSCTLOID); } } - return (0); + error = 0; +done: + if (del && (!locked)) + SYSCTL_XUNLOCK(); + return (error); } /* @@ -352,7 +418,8 @@ 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, int arg2, - int (*handler)(SYSCTL_HANDLER_ARGS), const char *fmt, const char *descr) + int (*handler)(SYSCTL_HANDLER_ARGS), const char *fmt, const char *descr, + struct mtx *mtx) { struct sysctl_oid *oidp; ssize_t len; @@ -361,6 +428,8 @@ /* You have to hook up somewhere.. */ if (parent == NULL) return(NULL); + + SYSCTL_XLOCK(); /* Check if the node already exists, otherwise create it */ oidp = sysctl_find_oidname(name, parent); if (oidp != NULL) { @@ -369,9 +438,11 @@ /* Update the context */ if (clist != NULL) sysctl_ctx_entry_add(clist, oidp); + SYSCTL_XUNLOCK(); return (oidp); } else { printf("can't re-use a leaf (%s)!\n", name); + SYSCTL_XUNLOCK(); return (NULL); } } @@ -397,6 +468,7 @@ oidp->oid_arg2 = arg2; } oidp->oid_fmt = fmt; + oidp->oid_mtx = mtx; if (descr) { int len = strlen(descr) + 1; oidp->descr = malloc(len, M_SYSCTLOID, M_WAITOK); @@ -406,8 +478,20 @@ /* Update the context, if used */ if (clist != NULL) sysctl_ctx_entry_add(clist, oidp); + /* Register this oid */ - sysctl_register_oid(oidp); + if (sysctl_register_oid(oidp)) { + free((void *)(uintptr_t)(const void *)oidp->descr, + M_SYSCTLOID); + free((void *)(uintptr_t)(const void *)oidp->oid_name, + M_SYSCTLOID); + free(oidp->oid_arg1, M_SYSCTLOID); /* Children */ + free(newname, M_SYSCTLOID); + free(oidp, M_SYSCTLOID); + return (NULL); + } + + SYSCTL_XUNLOCK(); return (oidp); } @@ -421,13 +505,17 @@ if (oid->oid_parent == parent) return (0); + SYSCTL_XLOCK(); oidp = sysctl_find_oidname(oid->oid_name, parent); - if (oidp != NULL) + if (oidp != NULL) { + SYSCTL_XUNLOCK(); return (EEXIST); + } sysctl_unregister_oid(oid); oid->oid_parent = parent; oid->oid_number = OID_AUTO; sysctl_register_oid(oid); + SYSCTL_XUNLOCK(); return (0); } @@ -441,11 +529,12 @@ { struct sysctl_oid **oidp; + SYSCTL_CTX_INIT(); SYSCTL_INIT(); SET_FOREACH(oidp, sysctl_set) sysctl_register_oid(*oidp); } -SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_all, 0); +SYSINIT(sysctl, SI_SUB_LOCK, SI_ORDER_ANY, sysctl_register_all, 0); /* * "Staff-functions" @@ -513,7 +602,9 @@ error = suser(req->td); if (error) return error; + SYSCTL_SLOCK(); sysctl_sysctl_debug_dump_node(&sysctl__children, 0); + SYSCTL_SUNLOCK(); return ENOENT; } @@ -531,6 +622,7 @@ struct sysctl_oid_list *lsp = &sysctl__children, *lsp2; char buf[10]; + SYSCTL_SLOCK(); while (namelen) { if (!lsp) { snprintf(buf,sizeof(buf),"%d",*name); @@ -553,14 +645,14 @@ error = SYSCTL_OUT(req, ".", 1); if (!error) error = SYSCTL_OUT(req, oid->oid_name, - strlen(oid->oid_name)); + strlen(oid->oid_name)); if (error) return (error); namelen--; name++; - if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE) + if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE) break; if (oid->oid_handler) @@ -571,7 +663,9 @@ } lsp = lsp2; } - return (SYSCTL_OUT(req, "", 1)); + error = SYSCTL_OUT(req, "", 1); + SYSCTL_SUNLOCK(); + return (error); } SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD, sysctl_sysctl_name, ""); @@ -583,23 +677,28 @@ struct sysctl_oid *oidp; *len = level; + SYSCTL_SLOCK(); SLIST_FOREACH(oidp, lsp, oid_link) { *next = oidp->oid_number; *oidpp = oidp; - if (oidp->oid_kind & CTLFLAG_SKIP) continue; - if (!namelen) { - if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) { + SYSCTL_SUNLOCK(); return 0; - if (oidp->oid_handler) + } + if (oidp->oid_handler) { /* We really should call the handler here...*/ + SYSCTL_SUNLOCK(); return 0; + } lsp = (struct sysctl_oid_list *)oidp->oid_arg1; if (!sysctl_sysctl_next_ls(lsp, 0, 0, next+1, - len, level+1, oidpp)) + len, level+1, oidpp)) { + SYSCTL_SUNLOCK(); return 0; + } goto emptynode; } @@ -607,14 +706,20 @@ continue; if (oidp->oid_number > *name) { - if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) { + SYSCTL_SUNLOCK(); return 0; - if (oidp->oid_handler) + } + if (oidp->oid_handler) { + SYSCTL_SUNLOCK(); return 0; + } lsp = (struct sysctl_oid_list *)oidp->oid_arg1; if (!sysctl_sysctl_next_ls(lsp, name+1, namelen-1, - next+1, len, level+1, oidpp)) + next+1, len, level+1, oidpp)) { + SYSCTL_SUNLOCK(); return (0); + } goto next; } if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) @@ -625,13 +730,17 @@ lsp = (struct sysctl_oid_list *)oidp->oid_arg1; if (!sysctl_sysctl_next_ls(lsp, name+1, namelen-1, next+1, - len, level+1, oidpp)) + len, level+1, oidpp)) { + SYSCTL_SUNLOCK(); return (0); + } + next: namelen = 1; emptynode: *len = level; } + SYSCTL_SUNLOCK(); return 1; } @@ -644,7 +753,7 @@ struct sysctl_oid *oid; struct sysctl_oid_list *lsp = &sysctl__children; int newoid[CTL_MAXNAME]; - + i = sysctl_sysctl_next_ls(lsp, name, namelen, newoid, &j, 1, &oid); if (i) return ENOENT; @@ -677,6 +786,7 @@ if (i == '.') *p = '\0'; + SYSCTL_SLOCK(); oidp = SLIST_FIRST(lsp); while (oidp && *len < CTL_MAXNAME) { @@ -690,6 +800,7 @@ if (!i) { if (oidpp) *oidpp = oidp; + SYSCTL_SUNLOCK(); return (0); } @@ -708,6 +819,7 @@ if (i == '.') *p = '\0'; } + SYSCTL_SUNLOCK(); return ENOENT; } @@ -753,16 +865,21 @@ struct sysctl_oid *oid; int error; + SYSCTL_SLOCK(); error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); if (error) - return (error); + goto done; - if (!oid->oid_fmt) - return (ENOENT); + if (!oid->oid_fmt) { + error = ENOENT; + goto done; + } error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind)); if (error) - return (error); + goto done; error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1); +done: + SYSCTL_SUNLOCK(); return (error); } @@ -774,14 +891,20 @@ { struct sysctl_oid *oid; int error; - + + SYSCTL_SLOCK(); error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); - if (error) + if (error) { + SYSCTL_SUNLOCK(); return (error); + } - if (!oid->descr) + if (!oid->descr) { + SYSCTL_SUNLOCK(); return (ENOENT); + } error = SYSCTL_OUT(req, oid->descr, strlen(oid->descr) + 1); + SYSCTL_SUNLOCK(); return (error); } @@ -806,10 +929,14 @@ /* * Attempt to get a coherent snapshot by making a copy of the data. */ + if (oidp->oid_mtx) + mtx_lock(oidp->oid_mtx); if (arg1) tmpout = *(int *)arg1; else tmpout = arg2; + if (oidp->oid_mtx) + mtx_unlock(oidp->oid_mtx); error = SYSCTL_OUT(req, &tmpout, sizeof(int)); if (error || !req->newptr) @@ -840,7 +967,11 @@ */ if (!arg1) return (EINVAL); + if (oidp->oid_mtx) + mtx_lock(oidp->oid_mtx); tmplong = *(long *)arg1; + if (oidp->oid_mtx) + mtx_unlock(oidp->oid_mtx); #ifdef SCTL_MASK32 if (req->flags & SCTL_MASK32) { tmpint = tmplong; @@ -884,10 +1015,14 @@ outlen = strlen((char *)arg1)+1; tmparg = malloc(outlen, M_SYSCTLTMP, M_WAITOK); + if (oidp->oid_mtx) + mtx_lock(oidp->oid_mtx); if (strlcpy(tmparg, (char *)arg1, outlen) >= outlen) { free(tmparg, M_SYSCTLTMP); goto retry; } + if (oidp->oid_mtx) + mtx_unlock(oidp->oid_mtx); error = SYSCTL_OUT(req, tmparg, outlen); free(tmparg, M_SYSCTLTMP); @@ -917,29 +1052,46 @@ int error, tries; u_int generation; struct sysctl_req req2; + void *tmp; - /* - * Attempt to get a coherent snapshot, by using the thread - * pre-emption counter updated from within mi_switch() to - * determine if we were pre-empted during a bcopy() or - * copyout(). Make 3 attempts at doing this before giving up. - * If we encounter an error, stop immediately. - */ - tries = 0; - req2 = *req; -retry: - generation = curthread->td_generation; - error = SYSCTL_OUT(req, arg1, arg2); - if (error) - return (error); - tries++; - if (generation != curthread->td_generation && tries < 3) { - *req = req2; - goto retry; + /* XXX */ + if ((oidp->oid_mtx == &Giant) || (oidp->oid_mtx == NULL)) { + /* + * Attempt to get a coherent snapshot, by using the thread + * pre-emption counter updated from within mi_switch() to + * determine if we were pre-empted during a bcopy() or + * copyout(). Make 3 attempts at doing this before giving up. + * If we encounter an error, stop immediately. + */ + tries = 0; + req2 = *req; + retry: + generation = curthread->td_generation; + error = SYSCTL_OUT(req, arg1, arg2); + if (error) + return (error); + tries++; + if (generation != curthread->td_generation && tries < 3) { + *req = req2; + goto retry; + } + error = SYSCTL_IN(req, arg1, arg2); + } else { + tmp = malloc(arg2, M_SYSCTLTMP, M_WAITOK); + + mtx_lock(oidp->oid_mtx); + memcpy(tmp, arg1, arg2); + mtx_unlock(oidp->oid_mtx); + + error = SYSCTL_OUT(req, tmp, arg2); + if (error) { + free(tmp, M_SYSCTLTMP); + return (error); + } + error = SYSCTL_IN(req, tmp, arg2); + free(tmp, M_SYSCTLTMP); } - error = SYSCTL_IN(req, arg1, arg2); - return (error); } @@ -1010,15 +1162,11 @@ req.newfunc = sysctl_new_kernel; req.lock = REQ_LOCKED; - SYSCTL_LOCK(); - error = sysctl_root(0, name, namelen, &req); if (req.lock == REQ_WIRED && req.validlen > 0) vsunlock(req.oldptr, req.validlen); - SYSCTL_UNLOCK(); - if (error && error != ENOMEM) return (error); @@ -1136,9 +1284,13 @@ int *nindx, struct sysctl_req *req) { struct sysctl_oid *oid; + struct sysctl_oid_list *lsp = &sysctl__children; + struct sysctl_oid_list *parent; int indx; - oid = SLIST_FIRST(&sysctl__children); + sx_assert(&sysctl_sx, SX_LOCKED); + + oid = SLIST_FIRST(lsp); indx = 0; while (oid && indx < CTL_MAXNAME) { if (oid->oid_number == name[indx]) { @@ -1153,8 +1305,8 @@ *nindx = indx; return (0); } - oid = SLIST_FIRST( - (struct sysctl_oid_list *)oid->oid_arg1); + lsp = (struct sysctl_oid_list *)oid->oid_arg1; + oid = SLIST_FIRST(lsp); } else if (indx == namelen) { *noid = oid; if (nindx != NULL) @@ -1164,7 +1316,10 @@ return (ENOTDIR); } } else { + parent = oid->oid_parent; oid = SLIST_NEXT(oid, oid_link); + if (!oid) + return (ENOENT); } } return (ENOENT); @@ -1180,10 +1335,11 @@ { struct sysctl_oid *oid; int error, indx, lvl; - + + SYSCTL_SLOCK(); error = sysctl_find_oid(arg1, arg2, &oid, &indx, req); if (error) - return (error); + goto done; if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { /* @@ -1191,13 +1347,17 @@ * 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 done; + } } /* 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 done; + } KASSERT(req->td != NULL, ("sysctl_root(): req->td == NULL")); @@ -1206,7 +1366,7 @@ lvl = (oid->oid_kind & CTLMASK_SECURE) >> CTLSHIFT_SECURE; error = securelevel_gt(req->td->td_ucred, lvl); if (error) - return (error); + goto done; } /* Is this sysctl writable by only privileged users? */ @@ -1219,11 +1379,13 @@ flags = 0; error = suser_cred(req->td->td_ucred, flags); if (error) - return (error); + goto done; } - if (!oid->oid_handler) - return EINVAL; + if (!oid->oid_handler) { + error = EINVAL; + goto done; + } if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { arg1 = (int *)arg1 + indx; @@ -1236,10 +1398,12 @@ error = mac_check_system_sysctl(req->td->td_ucred, oid, arg1, arg2, req); if (error != 0) - return (error); + goto done; + #endif error = oid->oid_handler(oid, arg1, arg2, req); - +done: + SYSCTL_SUNLOCK(); return (error); } @@ -1270,8 +1434,6 @@ if (error) return (error); - mtx_lock(&Giant); - error = userland_sysctl(td, name, uap->namelen, uap->old, uap->oldlenp, 0, uap->new, uap->newlen, &j, 0); @@ -1283,7 +1445,6 @@ error = i; } done2: - mtx_unlock(&Giant); return (error); } @@ -1332,8 +1493,6 @@ req.newfunc = sysctl_new_user; req.lock = REQ_LOCKED; - SYSCTL_LOCK(); - do { req.oldidx = 0; req.newidx = 0; @@ -1343,8 +1502,6 @@ if (req.lock == REQ_WIRED && req.validlen > 0) vsunlock(req.oldptr, req.validlen); - SYSCTL_UNLOCK(); - if (error && error != ENOMEM) return (error); Index: src/sys/vm/vm_meter.c =================================================================== --- src/sys/vm/vm_meter.c (.../current) (revision 81) +++ src/sys/vm/vm_meter.c (.../sysctl_sx) (revision 128) @@ -118,10 +118,12 @@ totalp = &total; bzero(totalp, sizeof *totalp); + + mtx_lock(&Giant); + /* * Mark all objects as inactive. */ - GIANT_REQUIRED; mtx_lock(&vm_object_list_mtx); TAILQ_FOREACH(object, &vm_object_list, object_list) { if (!VM_OBJECT_TRYLOCK(object)) { @@ -237,6 +239,7 @@ } mtx_unlock(&vm_object_list_mtx); totalp->t_free = cnt.v_free_count + cnt.v_cache_count; + mtx_unlock(&Giant); return (sysctl_handle_opaque(oidp, totalp, sizeof total, req)); } Index: src/sys/sys/sysctl.h =================================================================== --- src/sys/sys/sysctl.h (.../current) (revision 81) +++ src/sys/sys/sysctl.h (.../sysctl_sx) (revision 128) @@ -37,6 +37,9 @@ #define _SYS_SYSCTL_H_ #include +#include +#include +#include /* For Giant */ struct thread; /* @@ -162,6 +165,7 @@ const char *oid_fmt; int oid_refcnt; const char *descr; + struct mtx *oid_mtx; }; #define SYSCTL_IN(r, p, l) (r->newfunc)(r, p, l) @@ -176,7 +180,7 @@ /* * These functions are used to add/remove an oid from the mib. */ -void sysctl_register_oid(struct sysctl_oid *oidp); +int sysctl_register_oid(struct sysctl_oid *oidp); void sysctl_unregister_oid(struct sysctl_oid *oidp); /* Declare a static oid to allow child oids to be added to it. */ @@ -208,96 +212,181 @@ #define SYSCTL_OID(parent, nbr, name, kind, a1, a2, handler, fmt, descr) \ static struct sysctl_oid sysctl__##parent##_##name = { \ &sysctl_##parent##_children, { 0 }, \ - nbr, kind, a1, a2, #name, handler, fmt, 0, descr }; \ + nbr, kind, a1, a2, #name, handler, fmt, 0, descr, &Giant }; \ DATA_SET(sysctl_set, sysctl__##parent##_##name) +#define SYSCTL_OID_MTX(parent, nbr, name, kind, a1, a2, handler, fmt, descr, mtx) \ + static struct sysctl_oid sysctl__##parent##_##name = { \ + &sysctl_##parent##_children, { 0 }, \ + nbr, kind, a1, a2, #name, handler, fmt, 0, descr, mtx }; \ + DATA_SET(sysctl_set, sysctl__##parent##_##name) + + #define SYSCTL_ADD_OID(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, descr) \ - sysctl_add_oid(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, descr) + sysctl_add_oid(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, descr, &Giant) +#define SYSCTL_ADD_OID_MTX(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, descr, mtx) \ + sysctl_add_oid(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, descr, mtx) + /* This constructs a node from which other oids can hang. */ -#define SYSCTL_NODE(parent, nbr, name, access, handler, descr) \ +#define SYSCTL_NODE(parent, nbr, name, access, handler, descr) \ struct sysctl_oid_list SYSCTL_NODE_CHILDREN(parent, name); \ SYSCTL_OID(parent, nbr, name, CTLTYPE_NODE|(access), \ (void*)&SYSCTL_NODE_CHILDREN(parent, name), 0, handler, \ "N", descr) -#define SYSCTL_ADD_NODE(ctx, parent, nbr, name, access, handler, descr) \ - sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_NODE|(access), \ - 0, 0, handler, "N", descr) +#define SYSCTL_NODE_MTX(parent, nbr, name, access, handler, descr, mtx) \ + struct sysctl_oid_list SYSCTL_NODE_CHILDREN(parent, name); \ + SYSCTL_OID_MTX(parent, nbr, name, CTLTYPE_NODE|(access), \ + (void*)&SYSCTL_NODE_CHILDREN(parent, name), 0, handler, \ + "N", descr, mtx) +#define SYSCTL_ADD_NODE(ctx, parent, nbr, name, access, handler, descr) \ + sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_NODE|(access), \ + 0, 0, handler, "N", descr, &Giant) + +#define SYSCTL_ADD_NODE_MTX(ctx, parent, nbr, name, access, handler, descr, mtx) \ + sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_NODE|(access), \ + 0, 0, handler, "N", descr, mtx) + /* Oid for a string. len can be 0 to indicate '\0' termination. */ #define SYSCTL_STRING(parent, nbr, name, access, arg, len, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_STRING|(access), \ arg, len, sysctl_handle_string, "A", descr) +#define SYSCTL_STRING_MTX(parent, nbr, name, access, arg, len, descr, mtx) \ + SYSCTL_OID_MTX(parent, nbr, name, CTLTYPE_STRING|(access), \ + arg, len, sysctl_handle_string, "A", descr, mtx) + #define SYSCTL_ADD_STRING(ctx, parent, nbr, name, access, arg, len, descr) \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_STRING|(access), \ - arg, len, sysctl_handle_string, "A", descr) + arg, len, sysctl_handle_string, "A", descr, &Giant) +#define SYSCTL_ADD_STRING_MTX(ctx, parent, nbr, name, access, arg, len, descr, mtx) \ + sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_STRING|(access), \ + arg, len, sysctl_handle_string, "A", descr, mtx) + /* Oid for an int. If ptr is NULL, val is returned. */ #define SYSCTL_INT(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_INT|(access), \ ptr, val, sysctl_handle_int, "I", descr) +#define SYSCTL_INT_MTX(parent, nbr, name, access, ptr, val, descr, mtx) \ + SYSCTL_OID_MTX(parent, nbr, name, CTLTYPE_INT|(access), \ + ptr, val, sysctl_handle_int, "I", descr, mtx) + #define SYSCTL_ADD_INT(ctx, parent, nbr, name, access, ptr, val, descr) \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_INT|(access), \ - ptr, val, sysctl_handle_int, "I", descr) + ptr, val, sysctl_handle_int, "I", descr, &Giant) +#define SYSCTL_ADD_INT_MTX(ctx, parent, nbr, name, access, ptr, val, descr, mtx) \ + sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_INT|(access), \ + ptr, val, sysctl_handle_int, "I", descr, mtx) + /* Oid for an unsigned int. If ptr is NULL, val is returned. */ #define SYSCTL_UINT(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_UINT|(access), \ ptr, val, sysctl_handle_int, "IU", descr) +#define SYSCTL_UINT_MTX(parent, nbr, name, access, ptr, val, descr, mtx) \ + SYSCTL_OID_MTX(parent, nbr, name, CTLTYPE_UINT|(access), \ + ptr, val, sysctl_handle_int, "IU", descr, mtx) + #define SYSCTL_ADD_UINT(ctx, parent, nbr, name, access, ptr, val, descr) \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_UINT|(access), \ - ptr, val, sysctl_handle_int, "IU", descr) + ptr, val, sysctl_handle_int, "IU", descr, &Giant) +#define SYSCTL_ADD_UINT_MTX(ctx, parent, nbr, name, access, ptr, val, descr, mtx) \ + sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_UINT|(access), \ + ptr, val, sysctl_handle_int, "IU", descr, mtx) + /* Oid for a long. The pointer must be non NULL. */ #define SYSCTL_LONG(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_LONG|(access), \ ptr, val, sysctl_handle_long, "L", descr) +#define SYSCTL_LONG_MTX(parent, nbr, name, access, ptr, val, descr, mtx) \ + SYSCTL_OID_MTX(parent, nbr, name, CTLTYPE_LONG|(access), \ + ptr, val, sysctl_handle_long, "L", descr, mtx) + #define SYSCTL_ADD_LONG(ctx, parent, nbr, name, access, ptr, descr) \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_LONG|(access), \ - ptr, 0, sysctl_handle_long, "L", descr) + ptr, 0, sysctl_handle_long, "L", descr, &Giant) +#define SYSCTL_ADD_LONG_MTX(ctx, parent, nbr, name, access, ptr, descr, mtx) \ + sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_LONG|(access), \ + ptr, 0, sysctl_handle_long, "L", descr, mtx) + /* Oid for an unsigned long. The pointer must be non NULL. */ #define SYSCTL_ULONG(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_ULONG|(access), \ ptr, val, sysctl_handle_long, "LU", descr) +#define SYSCTL_ULONG_MTX(parent, nbr, name, access, ptr, val, descr, mtx) \ + SYSCTL_OID_MTX(parent, nbr, name, CTLTYPE_ULONG|(access), \ + ptr, val, sysctl_handle_long, "LU", descr, mtx) + #define SYSCTL_ADD_ULONG(ctx, parent, nbr, name, access, ptr, descr) \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_ULONG|(access), \ - ptr, 0, sysctl_handle_long, "LU", descr) + ptr, 0, sysctl_handle_long, "LU", descr, &Giant) +#define SYSCTL_ADD_ULONG_MTX(ctx, parent, nbr, name, access, ptr, descr, mtx) \ + sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_ULONG|(access), \ + ptr, 0, sysctl_handle_long, "LU", descr, mtx) + /* Oid for an opaque object. Specified by a pointer and a length. */ #define SYSCTL_OPAQUE(parent, nbr, name, access, ptr, len, fmt, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_OPAQUE|(access), \ ptr, len, sysctl_handle_opaque, fmt, descr) +#define SYSCTL_OPAQUE_MTX(parent, nbr, name, access, ptr, len, fmt, descr, mtx) \ + SYSCTL_OID_MTX(parent, nbr, name, CTLTYPE_OPAQUE|(access), \ + ptr, len, sysctl_handle_opaque, fmt, descr, mtx) + #define SYSCTL_ADD_OPAQUE(ctx, parent, nbr, name, access, ptr, len, fmt, descr)\ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_OPAQUE|(access), \ - ptr, len, sysctl_handle_opaque, fmt, descr) + ptr, len, sysctl_handle_opaque, fmt, descr, &Giant) +#define SYSCTL_ADD_OPAQUE_MTX(ctx, parent, nbr, name, access, ptr, len, fmt, descr, mtx)\ + sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_OPAQUE|(access), \ + ptr, len, sysctl_handle_opaque, fmt, descr, mtx) + /* Oid for a struct. Specified by a pointer and a type. */ #define SYSCTL_STRUCT(parent, nbr, name, access, ptr, type, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_OPAQUE|(access), \ ptr, sizeof(struct type), sysctl_handle_opaque, \ "S," #type, descr) +#define SYSCTL_STRUCT_MTX(parent, nbr, name, access, ptr, type, descr, mtx) \ + SYSCTL_OID_MTX(parent, nbr, name, CTLTYPE_OPAQUE|(access), \ + ptr, sizeof(struct type), sysctl_handle_opaque, \ + "S," #type, descr, mtx) + #define SYSCTL_ADD_STRUCT(ctx, parent, nbr, name, access, ptr, type, descr) \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_OPAQUE|(access), \ - ptr, sizeof(struct type), sysctl_handle_opaque, "S," #type, descr) + ptr, sizeof(struct type), sysctl_handle_opaque, "S," #type, descr, &Giant) +#define SYSCTL_ADD_STRUCT_MTX(ctx, parent, nbr, name, access, ptr, type, descr, mtx) \ + sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_OPAQUE|(access), \ + ptr, sizeof(struct type), sysctl_handle_opaque, "S," #type, descr, mtx) + /* Oid for a procedure. Specified by a pointer and an arg. */ #define SYSCTL_PROC(parent, nbr, name, access, ptr, arg, handler, fmt, descr) \ SYSCTL_OID(parent, nbr, name, (access), \ ptr, arg, handler, fmt, descr) +#define SYSCTL_PROC_MTX(parent, nbr, name, access, ptr, arg, handler, fmt, descr, mtx) \ + SYSCTL_OID_MTX(parent, nbr, name, (access), \ + ptr, arg, handler, fmt, descr, mtx) + #define SYSCTL_ADD_PROC(ctx, parent, nbr, name, access, ptr, arg, handler, fmt, descr) \ - sysctl_add_oid(ctx, parent, nbr, name, (access), \ - ptr, arg, handler, fmt, descr) + sysctl_add_oid(ctx, parent, nbr, name, (access), \ + ptr, arg, handler, fmt, descr, &Giant) +#define SYSCTL_ADD_PROC_MTX(ctx, parent, nbr, name, access, ptr, arg, handler, fmt, descr, mtx) \ + sysctl_add_oid(ctx, parent, nbr, name, (access), \ + ptr, arg, handler, fmt, descr, mtx) + #endif /* _KERNEL */ /* @@ -608,7 +697,7 @@ struct sysctl_oid_list *parent, int nbr, const char *name, int kind, void *arg1, int arg2, int (*handler) (SYSCTL_HANDLER_ARGS), - const char *fmt, const char *descr); + const char *fmt, const char *descr, struct mtx *mtx); int sysctl_move_oid(struct sysctl_oid *oidp, struct sysctl_oid_list *parent); int sysctl_remove_oid(struct sysctl_oid *oidp, int del, int recurse);