Revert r285125 until rmlocks get fixed.

Right now there is a chance that sysctl unregister will cause reader to
block on the sx lock associated with sysctl rmlock, in which case kernels
with debug enabled will panic.
This commit is contained in:
Mateusz Guzik 2015-07-30 19:52:43 +00:00
parent 4ed2d460bd
commit 4ae1e3c752
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=286094
4 changed files with 111 additions and 109 deletions

View File

@ -292,10 +292,10 @@ linker_file_register_sysctls(linker_file_t lf)
return;
sx_xunlock(&kld_sx);
sysctl_wlock();
sysctl_xlock();
for (oidp = start; oidp < stop; oidp++)
sysctl_register_oid(*oidp);
sysctl_wunlock();
sysctl_xunlock();
sx_xlock(&kld_sx);
}
@ -313,10 +313,10 @@ linker_file_unregister_sysctls(linker_file_t lf)
return;
sx_xunlock(&kld_sx);
sysctl_wlock();
sysctl_xlock();
for (oidp = start; oidp < stop; oidp++)
sysctl_unregister_oid(*oidp);
sysctl_wunlock();
sysctl_xunlock();
sx_xlock(&kld_sx);
}

View File

@ -54,7 +54,6 @@ __FBSDID("$FreeBSD$");
#include <sys/jail.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/rmlock.h>
#include <sys/sbuf.h>
#include <sys/sx.h>
#include <sys/sysproto.h>
@ -78,7 +77,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_wlock() and sysctl_wunlock() routines are
* be held, so the sysctl_xlock() and sysctl_xunlock() 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.
@ -87,21 +86,20 @@ 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 rmlock sysctllock;
static struct sx sysctllock;
static struct sx sysctlmemlock;
#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_XLOCK() sx_xlock(&sysctllock)
#define SYSCTL_XUNLOCK() sx_xunlock(&sysctllock)
#define SYSCTL_SLOCK() sx_slock(&sysctllock)
#define SYSCTL_SUNLOCK() sx_sunlock(&sysctllock)
#define SYSCTL_XLOCKED() sx_xlocked(&sysctllock)
#define SYSCTL_ASSERT_LOCKED() sx_assert(&sysctllock, SA_LOCKED)
#define SYSCTL_ASSERT_XLOCKED() sx_assert(&sysctllock, SA_XLOCKED)
#define SYSCTL_ASSERT_SLOCKED() sx_assert(&sysctllock, SA_SLOCKED)
#define SYSCTL_INIT() sx_init(&sysctllock, "sysctl lock")
#define SYSCTL_SLEEP(ch, wmesg, timo) \
rm_sleep(ch, &sysctllock, 0, wmesg, timo)
sx_sleep(ch, &sysctllock, 0, wmesg, timo)
static int sysctl_root(SYSCTL_HANDLER_ARGS);
@ -113,6 +111,29 @@ static int sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del,
static int sysctl_old_kernel(struct sysctl_req *, const void *, size_t);
static int sysctl_new_kernel(struct sysctl_req *, void *, size_t);
static void
sysctl_lock(bool xlock)
{
if (xlock)
SYSCTL_XLOCK();
else
SYSCTL_SLOCK();
}
static bool
sysctl_unlock(void)
{
bool xlocked;
xlocked = SYSCTL_XLOCKED();
if (xlocked)
SYSCTL_XUNLOCK();
else
SYSCTL_SUNLOCK();
return (xlocked);
}
static struct sysctl_oid *
sysctl_find_oidname(const char *name, struct sysctl_oid_list *list)
{
@ -133,32 +154,29 @@ sysctl_find_oidname(const char *name, struct sysctl_oid_list *list)
* Order by number in each list.
*/
void
sysctl_wlock(void)
sysctl_xlock(void)
{
SYSCTL_WLOCK();
SYSCTL_XLOCK();
}
void
sysctl_wunlock(void)
sysctl_xunlock(void)
{
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
}
static int
sysctl_root_handler_locked(struct sysctl_oid *oid, void *arg1, intptr_t arg2,
struct sysctl_req *req, struct rm_priotracker *tracker)
struct sysctl_req *req)
{
int error;
bool xlocked;
if (oid->oid_kind & CTLFLAG_DYN)
atomic_add_int(&oid->oid_running, 1);
if (tracker != NULL)
SYSCTL_RUNLOCK(tracker);
else
SYSCTL_WUNLOCK();
xlocked = sysctl_unlock();
if (!(oid->oid_kind & CTLFLAG_MPSAFE))
mtx_lock(&Giant);
@ -166,11 +184,7 @@ sysctl_root_handler_locked(struct sysctl_oid *oid, void *arg1, intptr_t arg2,
if (!(oid->oid_kind & CTLFLAG_MPSAFE))
mtx_unlock(&Giant);
if (tracker != NULL)
SYSCTL_RLOCK(tracker);
else
SYSCTL_WLOCK();
sysctl_lock(xlocked);
if (oid->oid_kind & CTLFLAG_DYN) {
if (atomic_fetchadd_int(&oid->oid_running, -1) == 1 &&
(oid->oid_kind & CTLFLAG_DYING) != 0)
@ -269,7 +283,7 @@ sysctl_load_tunable_by_oid_locked(struct sysctl_oid *oidp)
return;
}
error = sysctl_root_handler_locked(oidp, oidp->oid_arg1,
oidp->oid_arg2, &req, NULL);
oidp->oid_arg2, &req);
if (error != 0)
printf("Setting sysctl %s failed: %d\n", path + rem, error);
if (penv != NULL)
@ -289,7 +303,7 @@ sysctl_register_oid(struct sysctl_oid *oidp)
* First check if another oid with the same name already
* exists in the parent's list.
*/
SYSCTL_ASSERT_WLOCKED();
SYSCTL_ASSERT_XLOCKED();
p = sysctl_find_oidname(oidp->oid_name, parent);
if (p != NULL) {
if ((p->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
@ -383,7 +397,7 @@ sysctl_unregister_oid(struct sysctl_oid *oidp)
struct sysctl_oid *p;
int error;
SYSCTL_ASSERT_WLOCKED();
SYSCTL_ASSERT_XLOCKED();
error = ENOENT;
if (oidp->oid_number == OID_AUTO) {
error = EINVAL;
@ -439,7 +453,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_WLOCK();
SYSCTL_XLOCK();
TAILQ_FOREACH(e, clist, link) {
error = sysctl_remove_oid_locked(e->entry, 0, 0);
if (error)
@ -459,7 +473,7 @@ sysctl_ctx_free(struct sysctl_ctx_list *clist)
e1 = TAILQ_PREV(e1, sysctl_ctx_list, link);
}
if (error) {
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
return(EBUSY);
}
/* Now really delete the entries */
@ -473,7 +487,7 @@ sysctl_ctx_free(struct sysctl_ctx_list *clist)
free(e, M_SYSCTLOID);
e = e1;
}
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
return (error);
}
@ -483,7 +497,7 @@ sysctl_ctx_entry_add(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp)
{
struct sysctl_ctx_entry *e;
SYSCTL_ASSERT_WLOCKED();
SYSCTL_ASSERT_XLOCKED();
if (clist == NULL || oidp == NULL)
return(NULL);
e = malloc(sizeof(struct sysctl_ctx_entry), M_SYSCTLOID, M_WAITOK);
@ -498,7 +512,7 @@ sysctl_ctx_entry_find(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp)
{
struct sysctl_ctx_entry *e;
SYSCTL_ASSERT_WLOCKED();
SYSCTL_ASSERT_XLOCKED();
if (clist == NULL || oidp == NULL)
return(NULL);
TAILQ_FOREACH(e, clist, link) {
@ -520,15 +534,15 @@ sysctl_ctx_entry_del(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp)
if (clist == NULL || oidp == NULL)
return (EINVAL);
SYSCTL_WLOCK();
SYSCTL_XLOCK();
e = sysctl_ctx_entry_find(clist, oidp);
if (e != NULL) {
TAILQ_REMOVE(clist, e, link);
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
free(e, M_SYSCTLOID);
return (0);
} else {
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
return (ENOENT);
}
}
@ -544,9 +558,9 @@ sysctl_remove_oid(struct sysctl_oid *oidp, int del, int recurse)
{
int error;
SYSCTL_WLOCK();
SYSCTL_XLOCK();
error = sysctl_remove_oid_locked(oidp, del, recurse);
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
return (error);
}
@ -558,14 +572,14 @@ sysctl_remove_name(struct sysctl_oid *parent, const char *name,
int error;
error = ENOENT;
SYSCTL_WLOCK();
SYSCTL_XLOCK();
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_WUNLOCK();
SYSCTL_XUNLOCK();
return (error);
}
@ -577,7 +591,7 @@ sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse)
struct sysctl_oid *p, *tmp;
int error;
SYSCTL_ASSERT_WLOCKED();
SYSCTL_ASSERT_XLOCKED();
if (oidp == NULL)
return(EINVAL);
if ((oidp->oid_kind & CTLFLAG_DYN) == 0) {
@ -652,7 +666,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_WLOCK();
SYSCTL_XLOCK();
oidp = sysctl_find_oidname(name, parent);
if (oidp != NULL) {
if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
@ -660,10 +674,10 @@ 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_WUNLOCK();
SYSCTL_XUNLOCK();
return (oidp);
} else {
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
printf("can't re-use a leaf (%s)!\n", name);
return (NULL);
}
@ -686,7 +700,7 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent,
sysctl_ctx_entry_add(clist, oidp);
/* Register this oid */
sysctl_register_oid(oidp);
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
return (oidp);
}
@ -700,10 +714,10 @@ sysctl_rename_oid(struct sysctl_oid *oidp, const char *name)
char *oldname;
newname = strdup(name, M_SYSCTLOID);
SYSCTL_WLOCK();
SYSCTL_XLOCK();
oldname = __DECONST(char *, oidp->oid_name);
oidp->oid_name = newname;
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
free(oldname, M_SYSCTLOID);
}
@ -715,21 +729,21 @@ sysctl_move_oid(struct sysctl_oid *oid, struct sysctl_oid_list *parent)
{
struct sysctl_oid *oidp;
SYSCTL_WLOCK();
SYSCTL_XLOCK();
if (oid->oid_parent == parent) {
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
return (0);
}
oidp = sysctl_find_oidname(oid->oid_name, parent);
if (oidp != NULL) {
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
return (EEXIST);
}
sysctl_unregister_oid(oid);
oid->oid_parent = parent;
oid->oid_number = OID_AUTO;
sysctl_register_oid(oid);
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
return (0);
}
@ -745,10 +759,10 @@ sysctl_register_all(void *arg)
sx_init(&sysctlmemlock, "sysctl mem");
SYSCTL_INIT();
SYSCTL_WLOCK();
SYSCTL_XLOCK();
SET_FOREACH(oidp, sysctl_set)
sysctl_register_oid(*oidp);
SYSCTL_WUNLOCK();
SYSCTL_XUNLOCK();
}
SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_FIRST, sysctl_register_all, 0);
@ -818,15 +832,14 @@ 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_RLOCK(&tracker);
SYSCTL_SLOCK();
sysctl_sysctl_debug_dump_node(&sysctl__children, 0);
SYSCTL_RUNLOCK(&tracker);
SYSCTL_SUNLOCK();
return (ENOENT);
}
@ -842,10 +855,9 @@ 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_RLOCK(&tracker);
SYSCTL_SLOCK();
while (namelen) {
if (!lsp) {
snprintf(buf,sizeof(buf),"%d",*name);
@ -888,7 +900,7 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS)
}
error = SYSCTL_OUT(req, "", 1);
out:
SYSCTL_RUNLOCK(&tracker);
SYSCTL_SUNLOCK();
return (error);
}
@ -967,12 +979,11 @@ 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_RLOCK(&tracker);
SYSCTL_SLOCK();
i = sysctl_sysctl_next_ls(lsp, name, namelen, newoid, &j, 1, &oid);
SYSCTL_RUNLOCK(&tracker);
SYSCTL_SUNLOCK();
if (i)
return (ENOENT);
error = SYSCTL_OUT(req, newoid, j * sizeof (int));
@ -1031,7 +1042,6 @@ sysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS)
char *p;
int error, oid[CTL_MAXNAME], len = 0;
struct sysctl_oid *op = 0;
struct rm_priotracker tracker;
if (!req->newlen)
return (ENOENT);
@ -1048,9 +1058,9 @@ sysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS)
p [req->newlen] = '\0';
SYSCTL_RLOCK(&tracker);
SYSCTL_SLOCK();
error = name2oid(p, oid, &len, &op);
SYSCTL_RUNLOCK(&tracker);
SYSCTL_SUNLOCK();
free(p, M_SYSCTL);
@ -1073,10 +1083,9 @@ static int
sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS)
{
struct sysctl_oid *oid;
struct rm_priotracker tracker;
int error;
SYSCTL_RLOCK(&tracker);
SYSCTL_SLOCK();
error = sysctl_find_oid(arg1, arg2, &oid, NULL, req);
if (error)
goto out;
@ -1090,7 +1099,7 @@ sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS)
goto out;
error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1);
out:
SYSCTL_RUNLOCK(&tracker);
SYSCTL_SUNLOCK();
return (error);
}
@ -1102,10 +1111,9 @@ static int
sysctl_sysctl_oiddescr(SYSCTL_HANDLER_ARGS)
{
struct sysctl_oid *oid;
struct rm_priotracker tracker;
int error;
SYSCTL_RLOCK(&tracker);
SYSCTL_SLOCK();
error = sysctl_find_oid(arg1, arg2, &oid, NULL, req);
if (error)
goto out;
@ -1116,7 +1124,7 @@ sysctl_sysctl_oiddescr(SYSCTL_HANDLER_ARGS)
}
error = SYSCTL_OUT(req, oid->oid_descr, strlen(oid->oid_descr) + 1);
out:
SYSCTL_RUNLOCK(&tracker);
SYSCTL_SUNLOCK();
return (error);
}
@ -1421,7 +1429,9 @@ kernel_sysctl(struct thread *td, int *name, u_int namelen, void *old,
req.newfunc = sysctl_new_kernel;
req.lock = REQ_UNWIRED;
SYSCTL_SLOCK();
error = sysctl_root(0, name, namelen, &req);
SYSCTL_SUNLOCK();
if (req.lock == REQ_WIRED && req.validlen > 0)
vsunlock(req.oldptr, req.validlen);
@ -1598,14 +1608,13 @@ static int
sysctl_root(SYSCTL_HANDLER_ARGS)
{
struct sysctl_oid *oid;
struct rm_priotracker tracker;
int error, indx, lvl;
SYSCTL_RLOCK(&tracker);
SYSCTL_ASSERT_SLOCKED();
error = sysctl_find_oid(arg1, arg2, &oid, &indx, req);
if (error)
goto out;
return (error);
if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
/*
@ -1613,17 +1622,13 @@ 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) {
error = EISDIR;
goto out;
}
if (oid->oid_handler == NULL)
return (EISDIR);
}
/* Is this sysctl writable? */
if (req->newptr && !(oid->oid_kind & CTLFLAG_WR)) {
error = EPERM;
goto out;
}
if (req->newptr && !(oid->oid_kind & CTLFLAG_WR))
return (EPERM);
KASSERT(req->td != NULL, ("sysctl_root(): req->td == NULL"));
@ -1633,11 +1638,10 @@ 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)) ||
(req->newptr && !(oid->oid_kind & CTLFLAG_CAPWR))) {
error = EPERM;
goto out;
}
if (req->oldptr && !(oid->oid_kind & CTLFLAG_CAPRD))
return (EPERM);
if (req->newptr && !(oid->oid_kind & CTLFLAG_CAPWR))
return (EPERM);
}
#endif
@ -1646,7 +1650,7 @@ sysctl_root(SYSCTL_HANDLER_ARGS)
lvl = (oid->oid_kind & CTLMASK_SECURE) >> CTLSHIFT_SECURE;
error = securelevel_gt(req->td->td_ucred, lvl);
if (error)
goto out;
return (error);
}
/* Is this sysctl writable by only privileged users? */
@ -1664,13 +1668,11 @@ sysctl_root(SYSCTL_HANDLER_ARGS)
priv = PRIV_SYSCTL_WRITE;
error = priv_check(req->td, priv);
if (error)
goto out;
return (error);
}
if (!oid->oid_handler) {
error = EINVAL;
goto out;
}
if (!oid->oid_handler)
return (EINVAL);
if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
arg1 = (int *)arg1 + indx;
@ -1683,18 +1685,16 @@ sysctl_root(SYSCTL_HANDLER_ARGS)
error = mac_system_check_sysctl(req->td->td_ucred, oid, arg1, arg2,
req);
if (error != 0)
goto out;
return (error);
#endif
#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);
error = sysctl_root_handler_locked(oid, arg1, arg2, req);
KFAIL_POINT_ERROR(_debug_fail_point, sysctl_running, error);
out:
SYSCTL_RUNLOCK(&tracker);
return (error);
}
@ -1794,7 +1794,9 @@ userland_sysctl(struct thread *td, int *name, u_int namelen, void *old,
for (;;) {
req.oldidx = 0;
req.newidx = 0;
SYSCTL_SLOCK();
error = sysctl_root(0, name, namelen, &req);
SYSCTL_SUNLOCK();
if (error != EAGAIN)
break;
kern_yield(PRI_USER);

View File

@ -291,7 +291,7 @@ vfs_register(struct vfsconf *vfc)
* preserved by re-registering the oid after modifying its
* number.
*/
sysctl_wlock();
sysctl_xlock();
SLIST_FOREACH(oidp, SYSCTL_CHILDREN(&sysctl___vfs), oid_link) {
if (strcmp(oidp->oid_name, vfc->vfc_name) == 0) {
sysctl_unregister_oid(oidp);
@ -300,7 +300,7 @@ vfs_register(struct vfsconf *vfc)
break;
}
}
sysctl_wunlock();
sysctl_xunlock();
return (0);
}

View File

@ -807,8 +807,8 @@ int userland_sysctl(struct thread *td, int *name, u_int namelen, void *old,
size_t *retval, int flags);
int sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid,
int *nindx, struct sysctl_req *req);
void sysctl_wlock(void);
void sysctl_wunlock(void);
void sysctl_xlock(void);
void sysctl_xunlock(void);
int sysctl_wire_old_buffer(struct sysctl_req *req, size_t len);
struct sbuf;