diff --git a/sys/kern/subr_devstat.c b/sys/kern/subr_devstat.c --- a/sys/kern/subr_devstat.c +++ b/sys/kern/subr_devstat.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,22 @@ static STAILQ_HEAD(devstatlist, devstat) device_statq; /* + * Lock protecting the device_statq list and the devstat_num_devs / + * devstat_generation counters. devstat_add_entry() and + * devstat_remove_entry() take it exclusively; sysctl_devstat() takes it + * shared so a concurrent device detach cannot free a node out from under + * the world-readable sysctl walk (DF-0195). + */ +static struct lock devstat_lock; + +static void +devstat_lock_init(void *arg __unused) +{ + lockinit(&devstat_lock, "devstat", 0, 0); +} +SYSINIT(devstatlock, SI_SUB_CREATE_INIT, SI_ORDER_ANY, devstat_lock_init, NULL); + +/* * Take a malloced and zeroed devstat structure given to us, fill it in * and add it to the queue of devices. */ @@ -61,6 +78,8 @@ if (ds == NULL) return; + lockmgr(&devstat_lock, LK_EXCLUSIVE); + if (devstat_num_devs == 0) STAILQ_INIT(&device_statq); @@ -131,6 +150,8 @@ ds->device_type = device_type; ds->priority = priority; getmicrotime(&ds->dev_creation_time); + + lockmgr(&devstat_lock, LK_RELEASE); } /* @@ -144,6 +165,8 @@ if (ds == NULL) return; + lockmgr(&devstat_lock, LK_EXCLUSIVE); + devstat_generation++; devstat_num_devs--; @@ -151,6 +174,8 @@ /* Remove this entry from the devstat queue */ STAILQ_REMOVE(devstat_head, ds, devstat, dev_links); + + lockmgr(&devstat_lock, LK_RELEASE); } /* @@ -272,8 +297,12 @@ struct devstat *nds; struct devstatlist *devstat_head; - if (devstat_num_devs == 0) + lockmgr(&devstat_lock, LK_SHARED); + + if (devstat_num_devs == 0) { + lockmgr(&devstat_lock, LK_RELEASE); return(EINVAL); + } error = 0; devstat_head = &device_statq; @@ -291,6 +320,8 @@ nds = STAILQ_NEXT(nds, dev_links), i++) error = SYSCTL_OUT(req, nds, sizeof(struct devstat)); + lockmgr(&devstat_lock, LK_RELEASE); + return(error); }