version 1.250, 2017/03/20 01:06:29 |
version 1.251, 2017/03/20 01:13:07 |
Line 217 static TAILQ_HEAD(, finalize_hook) confi |
|
Line 217 static TAILQ_HEAD(, finalize_hook) confi |
|
static int config_finalize_done; |
static int config_finalize_done; |
|
|
/* list of all devices */ |
/* list of all devices */ |
static struct devicelist alldevs = TAILQ_HEAD_INITIALIZER(alldevs); |
static struct { |
static kmutex_t alldevs_mtx; |
kmutex_t lock; |
static bool alldevs_garbage = false; |
struct devicelist list; |
static devgen_t alldevs_gen = 1; |
devgen_t gen; |
static int alldevs_nread = 0; |
int nread; |
static int alldevs_nwrite = 0; |
int nwrite; |
|
bool garbage; |
|
} alldevs __cacheline_aligned = { |
|
.list = TAILQ_HEAD_INITIALIZER(alldevs.list), |
|
.gen = 1, |
|
.nread = 0, |
|
.nwrite = 0, |
|
.garbage = false, |
|
}; |
|
|
static int config_pending; /* semaphore for mountroot */ |
static int config_pending; /* semaphore for mountroot */ |
static kmutex_t config_misc_lock; |
static kmutex_t config_misc_lock; |
Line 341 config_init(void) |
|
Line 349 config_init(void) |
|
|
|
KASSERT(config_initialized == false); |
KASSERT(config_initialized == false); |
|
|
mutex_init(&alldevs_mtx, MUTEX_DEFAULT, IPL_VM); |
mutex_init(&alldevs.lock, MUTEX_DEFAULT, IPL_VM); |
|
|
mutex_init(&config_misc_lock, MUTEX_DEFAULT, IPL_NONE); |
mutex_init(&config_misc_lock, MUTEX_DEFAULT, IPL_NONE); |
cv_init(&config_misc_cv, "cfgmisc"); |
cv_init(&config_misc_cv, "cfgmisc"); |
Line 1155 number(char *ep, int n) |
|
Line 1163 number(char *ep, int n) |
|
/* |
/* |
* Expand the size of the cd_devs array if necessary. |
* Expand the size of the cd_devs array if necessary. |
* |
* |
* The caller must hold alldevs_mtx. config_makeroom() may release and |
* The caller must hold alldevs.lock. config_makeroom() may release and |
* re-acquire alldevs_mtx, so callers should re-check conditions such |
* re-acquire alldevs.lock, so callers should re-check conditions such |
* as alldevs_nwrite == 0 and alldevs_nread == 0 when config_makeroom() |
* as alldevs.nwrite == 0 and alldevs.nread == 0 when config_makeroom() |
* returns. |
* returns. |
*/ |
*/ |
static void |
static void |
Line 1166 config_makeroom(int n, struct cfdriver * |
|
Line 1174 config_makeroom(int n, struct cfdriver * |
|
int ondevs, nndevs; |
int ondevs, nndevs; |
device_t *osp, *nsp; |
device_t *osp, *nsp; |
|
|
KASSERT(mutex_owned(&alldevs_mtx)); |
KASSERT(mutex_owned(&alldevs.lock)); |
alldevs_nwrite++; |
alldevs.nwrite++; |
|
|
for (nndevs = MAX(4, cd->cd_ndevs); nndevs <= n; nndevs += nndevs) |
for (nndevs = MAX(4, cd->cd_ndevs); nndevs <= n; nndevs += nndevs) |
; |
; |
Line 1179 config_makeroom(int n, struct cfdriver * |
|
Line 1187 config_makeroom(int n, struct cfdriver * |
|
ondevs = cd->cd_ndevs; |
ondevs = cd->cd_ndevs; |
osp = cd->cd_devs; |
osp = cd->cd_devs; |
|
|
/* Release alldevs_mtx around allocation, which may |
/* |
|
* Release alldevs.lock around allocation, which may |
* sleep. |
* sleep. |
*/ |
*/ |
mutex_exit(&alldevs_mtx); |
mutex_exit(&alldevs.lock); |
nsp = kmem_alloc(sizeof(device_t[nndevs]), KM_SLEEP); |
nsp = kmem_alloc(sizeof(device_t[nndevs]), KM_SLEEP); |
if (nsp == NULL) |
if (nsp == NULL) |
panic("%s: could not expand cd_devs", __func__); |
panic("%s: could not expand cd_devs", __func__); |
mutex_enter(&alldevs_mtx); |
mutex_enter(&alldevs.lock); |
|
|
/* If another thread moved the array while we did |
/* |
* not hold alldevs_mtx, try again. |
* If another thread moved the array while we did |
|
* not hold alldevs.lock, try again. |
*/ |
*/ |
if (cd->cd_devs != osp) { |
if (cd->cd_devs != osp) { |
mutex_exit(&alldevs_mtx); |
mutex_exit(&alldevs.lock); |
kmem_free(nsp, sizeof(device_t[nndevs])); |
kmem_free(nsp, sizeof(device_t[nndevs])); |
mutex_enter(&alldevs_mtx); |
mutex_enter(&alldevs.lock); |
continue; |
continue; |
} |
} |
|
|
Line 1205 config_makeroom(int n, struct cfdriver * |
|
Line 1215 config_makeroom(int n, struct cfdriver * |
|
cd->cd_ndevs = nndevs; |
cd->cd_ndevs = nndevs; |
cd->cd_devs = nsp; |
cd->cd_devs = nsp; |
if (ondevs != 0) { |
if (ondevs != 0) { |
mutex_exit(&alldevs_mtx); |
mutex_exit(&alldevs.lock); |
kmem_free(osp, sizeof(device_t[ondevs])); |
kmem_free(osp, sizeof(device_t[ondevs])); |
mutex_enter(&alldevs_mtx); |
mutex_enter(&alldevs.lock); |
} |
} |
} |
} |
KASSERT(mutex_owned(&alldevs_mtx)); |
KASSERT(mutex_owned(&alldevs.lock)); |
alldevs_nwrite--; |
alldevs.nwrite--; |
} |
} |
|
|
/* |
/* |
|
|
config_devlink(device_t dev) |
config_devlink(device_t dev) |
{ |
{ |
|
|
mutex_enter(&alldevs_mtx); |
mutex_enter(&alldevs.lock); |
|
|
KASSERT(device_cfdriver(dev)->cd_devs[dev->dv_unit] == dev); |
KASSERT(device_cfdriver(dev)->cd_devs[dev->dv_unit] == dev); |
|
|
dev->dv_add_gen = alldevs_gen; |
dev->dv_add_gen = alldevs.gen; |
/* It is safe to add a device to the tail of the list while |
/* It is safe to add a device to the tail of the list while |
* readers and writers are in the list. |
* readers and writers are in the list. |
*/ |
*/ |
TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); |
TAILQ_INSERT_TAIL(&alldevs.list, dev, dv_list); |
mutex_exit(&alldevs_mtx); |
mutex_exit(&alldevs.lock); |
} |
} |
|
|
static void |
static void |
Line 1245 config_devfree(device_t dev) |
|
Line 1255 config_devfree(device_t dev) |
|
} |
} |
|
|
/* |
/* |
* Caller must hold alldevs_mtx. |
* Caller must hold alldevs.lock. |
*/ |
*/ |
static void |
static void |
config_devunlink(device_t dev, struct devicelist *garbage) |
config_devunlink(device_t dev, struct devicelist *garbage) |
Line 1254 config_devunlink(device_t dev, struct de |
|
Line 1264 config_devunlink(device_t dev, struct de |
|
cfdriver_t cd = device_cfdriver(dev); |
cfdriver_t cd = device_cfdriver(dev); |
int i; |
int i; |
|
|
KASSERT(mutex_owned(&alldevs_mtx)); |
KASSERT(mutex_owned(&alldevs.lock)); |
|
|
/* Unlink from device list. Link to garbage list. */ |
/* Unlink from device list. Link to garbage list. */ |
TAILQ_REMOVE(&alldevs, dev, dv_list); |
TAILQ_REMOVE(&alldevs.list, dev, dv_list); |
TAILQ_INSERT_TAIL(garbage, dev, dv_list); |
TAILQ_INSERT_TAIL(garbage, dev, dv_list); |
|
|
/* Remove from cfdriver's array. */ |
/* Remove from cfdriver's array. */ |
Line 1660 config_attach_pseudo(cfdata_t cf) |
|
Line 1670 config_attach_pseudo(cfdata_t cf) |
|
} |
} |
|
|
/* |
/* |
* Caller must hold alldevs_mtx. |
* Caller must hold alldevs.lock. |
*/ |
*/ |
static void |
static void |
config_collect_garbage(struct devicelist *garbage) |
config_collect_garbage(struct devicelist *garbage) |
Line 1669 config_collect_garbage(struct devicelist |
|
Line 1679 config_collect_garbage(struct devicelist |
|
|
|
KASSERT(!cpu_intr_p()); |
KASSERT(!cpu_intr_p()); |
KASSERT(!cpu_softintr_p()); |
KASSERT(!cpu_softintr_p()); |
KASSERT(mutex_owned(&alldevs_mtx)); |
KASSERT(mutex_owned(&alldevs.lock)); |
|
|
while (alldevs_nwrite == 0 && alldevs_nread == 0 && alldevs_garbage) { |
while (alldevs.nwrite == 0 && alldevs.nread == 0 && alldevs.garbage) { |
TAILQ_FOREACH(dv, &alldevs, dv_list) { |
TAILQ_FOREACH(dv, &alldevs.list, dv_list) { |
if (dv->dv_del_gen != 0) |
if (dv->dv_del_gen != 0) |
break; |
break; |
} |
} |
if (dv == NULL) { |
if (dv == NULL) { |
alldevs_garbage = false; |
alldevs.garbage = false; |
break; |
break; |
} |
} |
config_devunlink(dv, garbage); |
config_devunlink(dv, garbage); |
} |
} |
KASSERT(mutex_owned(&alldevs_mtx)); |
KASSERT(mutex_owned(&alldevs.lock)); |
} |
} |
|
|
static void |
static void |
Line 1731 config_detach(device_t dev, int flags) |
|
Line 1741 config_detach(device_t dev, int flags) |
|
ca = dev->dv_cfattach; |
ca = dev->dv_cfattach; |
KASSERT(ca != NULL); |
KASSERT(ca != NULL); |
|
|
mutex_enter(&alldevs_mtx); |
mutex_enter(&alldevs.lock); |
if (dev->dv_del_gen != 0) { |
if (dev->dv_del_gen != 0) { |
mutex_exit(&alldevs_mtx); |
mutex_exit(&alldevs.lock); |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
printf("%s: %s is already detached\n", __func__, |
printf("%s: %s is already detached\n", __func__, |
device_xname(dev)); |
device_xname(dev)); |
#endif /* DIAGNOSTIC */ |
#endif /* DIAGNOSTIC */ |
return ENOENT; |
return ENOENT; |
} |
} |
alldevs_nwrite++; |
alldevs.nwrite++; |
mutex_exit(&alldevs_mtx); |
mutex_exit(&alldevs.lock); |
|
|
if (!detachall && |
if (!detachall && |
(flags & (DETACH_SHUTDOWN|DETACH_FORCE)) == DETACH_SHUTDOWN && |
(flags & (DETACH_SHUTDOWN|DETACH_FORCE)) == DETACH_SHUTDOWN && |
Line 1818 config_detach(device_t dev, int flags) |
|
Line 1828 config_detach(device_t dev, int flags) |
|
|
|
out: |
out: |
config_alldevs_enter(&af); |
config_alldevs_enter(&af); |
KASSERT(alldevs_nwrite != 0); |
KASSERT(alldevs.nwrite != 0); |
--alldevs_nwrite; |
--alldevs.nwrite; |
if (rv == 0 && dev->dv_del_gen == 0) { |
if (rv == 0 && dev->dv_del_gen == 0) { |
if (alldevs_nwrite == 0 && alldevs_nread == 0) |
if (alldevs.nwrite == 0 && alldevs.nread == 0) |
config_devunlink(dev, &af.af_garbage); |
config_devunlink(dev, &af.af_garbage); |
else { |
else { |
dev->dv_del_gen = alldevs_gen; |
dev->dv_del_gen = alldevs.gen; |
alldevs_garbage = true; |
alldevs.garbage = true; |
} |
} |
} |
} |
config_alldevs_exit(&af); |
config_alldevs_exit(&af); |
|
|
config_alldevs_enter(struct alldevs_foray *af) |
config_alldevs_enter(struct alldevs_foray *af) |
{ |
{ |
TAILQ_INIT(&af->af_garbage); |
TAILQ_INIT(&af->af_garbage); |
mutex_enter(&alldevs_mtx); |
mutex_enter(&alldevs.lock); |
config_collect_garbage(&af->af_garbage); |
config_collect_garbage(&af->af_garbage); |
} |
} |
|
|
static void |
static void |
config_alldevs_exit(struct alldevs_foray *af) |
config_alldevs_exit(struct alldevs_foray *af) |
{ |
{ |
mutex_exit(&alldevs_mtx); |
mutex_exit(&alldevs.lock); |
config_dump_garbage(&af->af_garbage); |
config_dump_garbage(&af->af_garbage); |
} |
} |
|
|
Line 2236 device_lookup(cfdriver_t cd, int unit) |
|
Line 2246 device_lookup(cfdriver_t cd, int unit) |
|
{ |
{ |
device_t dv; |
device_t dv; |
|
|
mutex_enter(&alldevs_mtx); |
mutex_enter(&alldevs.lock); |
if (unit < 0 || unit >= cd->cd_ndevs) |
if (unit < 0 || unit >= cd->cd_ndevs) |
dv = NULL; |
dv = NULL; |
else if ((dv = cd->cd_devs[unit]) != NULL && dv->dv_del_gen != 0) |
else if ((dv = cd->cd_devs[unit]) != NULL && dv->dv_del_gen != 0) |
dv = NULL; |
dv = NULL; |
mutex_exit(&alldevs_mtx); |
mutex_exit(&alldevs.lock); |
|
|
return dv; |
return dv; |
} |
} |
Line 2786 deviter_init(deviter_t *di, deviter_flag |
|
Line 2796 deviter_init(deviter_t *di, deviter_flag |
|
if ((flags & DEVITER_F_SHUTDOWN) != 0) |
if ((flags & DEVITER_F_SHUTDOWN) != 0) |
flags |= DEVITER_F_RW; |
flags |= DEVITER_F_RW; |
|
|
mutex_enter(&alldevs_mtx); |
mutex_enter(&alldevs.lock); |
if ((flags & DEVITER_F_RW) != 0) |
if ((flags & DEVITER_F_RW) != 0) |
alldevs_nwrite++; |
alldevs.nwrite++; |
else |
else |
alldevs_nread++; |
alldevs.nread++; |
di->di_gen = alldevs_gen++; |
di->di_gen = alldevs.gen++; |
di->di_flags = flags; |
di->di_flags = flags; |
|
|
switch (di->di_flags & (DEVITER_F_LEAVES_FIRST|DEVITER_F_ROOT_FIRST)) { |
switch (di->di_flags & (DEVITER_F_LEAVES_FIRST|DEVITER_F_ROOT_FIRST)) { |
case DEVITER_F_LEAVES_FIRST: |
case DEVITER_F_LEAVES_FIRST: |
TAILQ_FOREACH(dv, &alldevs, dv_list) { |
TAILQ_FOREACH(dv, &alldevs.list, dv_list) { |
if (!deviter_visits(di, dv)) |
if (!deviter_visits(di, dv)) |
continue; |
continue; |
di->di_curdepth = MAX(di->di_curdepth, dv->dv_depth); |
di->di_curdepth = MAX(di->di_curdepth, dv->dv_depth); |
} |
} |
break; |
break; |
case DEVITER_F_ROOT_FIRST: |
case DEVITER_F_ROOT_FIRST: |
TAILQ_FOREACH(dv, &alldevs, dv_list) { |
TAILQ_FOREACH(dv, &alldevs.list, dv_list) { |
if (!deviter_visits(di, dv)) |
if (!deviter_visits(di, dv)) |
continue; |
continue; |
di->di_maxdepth = MAX(di->di_maxdepth, dv->dv_depth); |
di->di_maxdepth = MAX(di->di_maxdepth, dv->dv_depth); |
Line 2814 deviter_init(deviter_t *di, deviter_flag |
|
Line 2824 deviter_init(deviter_t *di, deviter_flag |
|
} |
} |
|
|
deviter_reinit(di); |
deviter_reinit(di); |
mutex_exit(&alldevs_mtx); |
mutex_exit(&alldevs.lock); |
} |
} |
|
|
static void |
static void |
deviter_reinit(deviter_t *di) |
deviter_reinit(deviter_t *di) |
{ |
{ |
|
|
KASSERT(mutex_owned(&alldevs_mtx)); |
KASSERT(mutex_owned(&alldevs.lock)); |
if ((di->di_flags & DEVITER_F_RW) != 0) |
if ((di->di_flags & DEVITER_F_RW) != 0) |
di->di_prev = TAILQ_LAST(&alldevs, devicelist); |
di->di_prev = TAILQ_LAST(&alldevs.list, devicelist); |
else |
else |
di->di_prev = TAILQ_FIRST(&alldevs); |
di->di_prev = TAILQ_FIRST(&alldevs.list); |
} |
} |
|
|
device_t |
device_t |
Line 2841 deviter_next2(deviter_t *di) |
|
Line 2851 deviter_next2(deviter_t *di) |
|
{ |
{ |
device_t dv; |
device_t dv; |
|
|
KASSERT(mutex_owned(&alldevs_mtx)); |
KASSERT(mutex_owned(&alldevs.lock)); |
|
|
dv = di->di_prev; |
dv = di->di_prev; |
|
|
Line 2861 deviter_next1(deviter_t *di) |
|
Line 2871 deviter_next1(deviter_t *di) |
|
{ |
{ |
device_t dv; |
device_t dv; |
|
|
KASSERT(mutex_owned(&alldevs_mtx)); |
KASSERT(mutex_owned(&alldevs.lock)); |
|
|
do { |
do { |
dv = deviter_next2(di); |
dv = deviter_next2(di); |
Line 2875 deviter_next(deviter_t *di) |
|
Line 2885 deviter_next(deviter_t *di) |
|
{ |
{ |
device_t dv = NULL; |
device_t dv = NULL; |
|
|
mutex_enter(&alldevs_mtx); |
mutex_enter(&alldevs.lock); |
switch (di->di_flags & (DEVITER_F_LEAVES_FIRST|DEVITER_F_ROOT_FIRST)) { |
switch (di->di_flags & (DEVITER_F_LEAVES_FIRST|DEVITER_F_ROOT_FIRST)) { |
case 0: |
case 0: |
dv = deviter_next1(di); |
dv = deviter_next1(di); |
Line 2901 deviter_next(deviter_t *di) |
|
Line 2911 deviter_next(deviter_t *di) |
|
default: |
default: |
break; |
break; |
} |
} |
mutex_exit(&alldevs_mtx); |
mutex_exit(&alldevs.lock); |
|
|
return dv; |
return dv; |
} |
} |
Line 2911 deviter_release(deviter_t *di) |
|
Line 2921 deviter_release(deviter_t *di) |
|
{ |
{ |
bool rw = (di->di_flags & DEVITER_F_RW) != 0; |
bool rw = (di->di_flags & DEVITER_F_RW) != 0; |
|
|
mutex_enter(&alldevs_mtx); |
mutex_enter(&alldevs.lock); |
if (rw) |
if (rw) |
--alldevs_nwrite; |
--alldevs.nwrite; |
else |
else |
--alldevs_nread; |
--alldevs.nread; |
/* XXX wake a garbage-collection thread */ |
/* XXX wake a garbage-collection thread */ |
mutex_exit(&alldevs_mtx); |
mutex_exit(&alldevs.lock); |
} |
} |
|
|
const char * |
const char * |