Switch to kmem(9).
(void *)pew is one way to get a struct work *, but let's write&pew->pew_work, instead. It is more defensive and persuasive. Make miscellaneous changes in support of tearing down arbitrary stacks of filesystems and devices during shutdown: 1 Move struct shutdown_state, shutdown_first(), and shutdown_next(), from kern_pmf.c to subr_autoconf.c. Rename detach_all() to config_detach_all(), and move it from kern_pmf.c to subr_autoconf.c. Export all of those routines. 2 In pmf_system_shutdown(), do not suspend user process scheduling, and do not detach all devices: I am going to do that in cpu_reboot(), instead. (Soon I will do it in an MI cpu_reboot() routine.) Do still call PMF shutdown hooks. 3 In config_detach(), add a DIAGNOSTIC assertion: if we're exiting config_detach() at the bottom, alldevs_nwrite had better not be 0, because config_detach() is a writer of the device list. 4 In deviter_release(), check to see if we're iterating the device list for reading, *first*, and if so, decrease the number of readers. Used to be that if we happened to be reading during shutdown, we ran the shutdown branch. Thus the number of writers reached 0, the number of readers remained > 0, and no writer could iterate again. Under certain circumstances that would cause a hang during shutdown.
This commit is contained in:
parent
57a3ffeae7
commit
9d9978e5a5
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: kern_pmf.c,v 1.26 2009/04/17 20:45:09 dyoung Exp $ */
|
||||
/* $NetBSD: kern_pmf.c,v 1.27 2009/06/26 19:30:45 dyoung Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
|
||||
|
@ -27,11 +27,11 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.26 2009/04/17 20:45:09 dyoung Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.27 2009/06/26 19:30:45 dyoung Exp $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/callout.h>
|
||||
#include <sys/kernel.h>
|
||||
|
@ -94,13 +94,7 @@ typedef struct pmf_event_workitem {
|
|||
device_t pew_device;
|
||||
} pmf_event_workitem_t;
|
||||
|
||||
struct shutdown_state {
|
||||
bool initialized;
|
||||
deviter_t di;
|
||||
};
|
||||
|
||||
static device_t shutdown_first(struct shutdown_state *);
|
||||
static device_t shutdown_next(struct shutdown_state *);
|
||||
|
||||
static bool pmf_device_resume_locked(device_t PMF_FN_PROTO);
|
||||
static bool pmf_device_suspend_locked(device_t PMF_FN_PROTO);
|
||||
|
@ -122,7 +116,7 @@ pmf_event_worker(struct work *wk, void *dummy)
|
|||
(*event->pmf_handler)(event->pmf_device);
|
||||
}
|
||||
|
||||
free(pew, M_TEMP);
|
||||
kmem_free(pew, sizeof(*pew));
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -270,52 +264,6 @@ pmf_system_suspend(PMF_FN_ARGS1)
|
|||
return true;
|
||||
}
|
||||
|
||||
static device_t
|
||||
shutdown_first(struct shutdown_state *s)
|
||||
{
|
||||
if (!s->initialized) {
|
||||
deviter_init(&s->di, DEVITER_F_SHUTDOWN|DEVITER_F_LEAVES_FIRST);
|
||||
s->initialized = true;
|
||||
}
|
||||
return shutdown_next(s);
|
||||
}
|
||||
|
||||
static device_t
|
||||
shutdown_next(struct shutdown_state *s)
|
||||
{
|
||||
device_t dv;
|
||||
|
||||
while ((dv = deviter_next(&s->di)) != NULL && !device_is_active(dv))
|
||||
;
|
||||
|
||||
if (dv == NULL)
|
||||
s->initialized = false;
|
||||
|
||||
return dv;
|
||||
}
|
||||
|
||||
static bool
|
||||
detach_all(int how)
|
||||
{
|
||||
static struct shutdown_state s;
|
||||
device_t curdev;
|
||||
bool progress = false;
|
||||
|
||||
if ((how & RB_NOSYNC) != 0)
|
||||
return false;
|
||||
|
||||
for (curdev = shutdown_first(&s); curdev != NULL;
|
||||
curdev = shutdown_next(&s)) {
|
||||
aprint_debug(" detaching %s, ", device_xname(curdev));
|
||||
if (config_detach(curdev, DETACH_SHUTDOWN) == 0) {
|
||||
progress = true;
|
||||
aprint_debug("success.");
|
||||
} else
|
||||
aprint_debug("failed.");
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
static bool
|
||||
shutdown_all(int how)
|
||||
{
|
||||
|
@ -348,11 +296,6 @@ void
|
|||
pmf_system_shutdown(int how)
|
||||
{
|
||||
aprint_debug("Shutting down devices:");
|
||||
suspendsched();
|
||||
|
||||
while (detach_all(how))
|
||||
;
|
||||
|
||||
shutdown_all(how);
|
||||
}
|
||||
|
||||
|
@ -612,7 +555,7 @@ pmf_event_inject(device_t dv, pmf_generic_event_t ev)
|
|||
{
|
||||
pmf_event_workitem_t *pew;
|
||||
|
||||
pew = malloc(sizeof(pmf_event_workitem_t), M_TEMP, M_NOWAIT);
|
||||
pew = kmem_alloc(sizeof(pmf_event_workitem_t), KM_NOSLEEP);
|
||||
if (pew == NULL) {
|
||||
PMF_EVENT_PRINTF(("%s: PMF event %d dropped (no memory)\n",
|
||||
dv ? device_xname(dv) : "<anonymous>", ev));
|
||||
|
@ -622,7 +565,7 @@ pmf_event_inject(device_t dv, pmf_generic_event_t ev)
|
|||
pew->pew_event = ev;
|
||||
pew->pew_device = dv;
|
||||
|
||||
workqueue_enqueue(pmf_event_workqueue, (void *)pew, NULL);
|
||||
workqueue_enqueue(pmf_event_workqueue, &pew->pew_work, NULL);
|
||||
PMF_EVENT_PRINTF(("%s: PMF event %d injected\n",
|
||||
dv ? device_xname(dv) : "<anonymous>", ev));
|
||||
|
||||
|
@ -635,7 +578,7 @@ pmf_event_register(device_t dv, pmf_generic_event_t ev,
|
|||
{
|
||||
pmf_event_handler_t *event;
|
||||
|
||||
event = malloc(sizeof(*event), M_DEVBUF, M_WAITOK);
|
||||
event = kmem_alloc(sizeof(*event), KM_SLEEP);
|
||||
event->pmf_event = ev;
|
||||
event->pmf_handler = handler;
|
||||
event->pmf_device = dv;
|
||||
|
@ -661,7 +604,7 @@ pmf_event_deregister(device_t dv, pmf_generic_event_t ev,
|
|||
if (event->pmf_handler != handler)
|
||||
continue;
|
||||
TAILQ_REMOVE(&pmf_all_events, event, pmf_link);
|
||||
free(event, M_DEVBUF);
|
||||
kmem_free(event, sizeof(*event));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -719,7 +662,7 @@ pmf_class_display_deregister(device_t dv)
|
|||
callout_stop(&global_idle_counter);
|
||||
splx(s);
|
||||
|
||||
free(sc, M_DEVBUF);
|
||||
kmem_free(sc, sizeof(*sc));
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -728,7 +671,7 @@ pmf_class_display_register(device_t dv)
|
|||
struct display_class_softc *sc;
|
||||
int s;
|
||||
|
||||
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK);
|
||||
sc = kmem_alloc(sizeof(*sc), KM_SLEEP);
|
||||
|
||||
s = splsoftclock();
|
||||
if (TAILQ_EMPTY(&all_displays))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: subr_autoconf.c,v 1.177 2009/05/29 23:27:08 dyoung Exp $ */
|
||||
/* $NetBSD: subr_autoconf.c,v 1.178 2009/06/26 19:30:45 dyoung Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1996, 2000 Christopher G. Demetriou
|
||||
|
@ -77,7 +77,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.177 2009/05/29 23:27:08 dyoung Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.178 2009/06/26 19:30:45 dyoung Exp $");
|
||||
|
||||
#include "opt_ddb.h"
|
||||
#include "drvctl.h"
|
||||
|
@ -1598,6 +1598,7 @@ config_detach(device_t dev, int flags)
|
|||
|
||||
out:
|
||||
mutex_enter(&alldevs_mtx);
|
||||
KASSERT(alldevs_nwrite != 0);
|
||||
if (--alldevs_nwrite == 0)
|
||||
alldevs_writer = NULL;
|
||||
cv_signal(&alldevs_cv);
|
||||
|
@ -1623,6 +1624,52 @@ config_detach_children(device_t parent, int flags)
|
|||
return error;
|
||||
}
|
||||
|
||||
device_t
|
||||
shutdown_first(struct shutdown_state *s)
|
||||
{
|
||||
if (!s->initialized) {
|
||||
deviter_init(&s->di, DEVITER_F_SHUTDOWN|DEVITER_F_LEAVES_FIRST);
|
||||
s->initialized = true;
|
||||
}
|
||||
return shutdown_next(s);
|
||||
}
|
||||
|
||||
device_t
|
||||
shutdown_next(struct shutdown_state *s)
|
||||
{
|
||||
device_t dv;
|
||||
|
||||
while ((dv = deviter_next(&s->di)) != NULL && !device_is_active(dv))
|
||||
;
|
||||
|
||||
if (dv == NULL)
|
||||
s->initialized = false;
|
||||
|
||||
return dv;
|
||||
}
|
||||
|
||||
bool
|
||||
config_detach_all(int how)
|
||||
{
|
||||
static struct shutdown_state s;
|
||||
device_t curdev;
|
||||
bool progress = false;
|
||||
|
||||
if ((how & RB_NOSYNC) != 0)
|
||||
return false;
|
||||
|
||||
for (curdev = shutdown_first(&s); curdev != NULL;
|
||||
curdev = shutdown_next(&s)) {
|
||||
aprint_debug(" detaching %s, ", device_xname(curdev));
|
||||
if (config_detach(curdev, DETACH_SHUTDOWN) == 0) {
|
||||
progress = true;
|
||||
aprint_debug("success.");
|
||||
} else
|
||||
aprint_debug("failed.");
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
int
|
||||
config_activate(device_t dev)
|
||||
{
|
||||
|
@ -2689,16 +2736,15 @@ deviter_release(deviter_t *di)
|
|||
bool rw = (di->di_flags & DEVITER_F_RW) != 0;
|
||||
|
||||
mutex_enter(&alldevs_mtx);
|
||||
if (alldevs_nwrite > 0 && alldevs_writer == NULL)
|
||||
--alldevs_nwrite;
|
||||
else {
|
||||
|
||||
if (rw) {
|
||||
if (--alldevs_nwrite == 0)
|
||||
alldevs_writer = NULL;
|
||||
} else
|
||||
--alldevs_nread;
|
||||
|
||||
if (!rw) {
|
||||
--alldevs_nread;
|
||||
cv_signal(&alldevs_cv);
|
||||
} else if (alldevs_nwrite > 0 && alldevs_writer == NULL) {
|
||||
--alldevs_nwrite; /* shutting down: do not signal */
|
||||
} else {
|
||||
KASSERT(alldevs_nwrite != 0);
|
||||
if (--alldevs_nwrite == 0)
|
||||
alldevs_writer = NULL;
|
||||
cv_signal(&alldevs_cv);
|
||||
}
|
||||
mutex_exit(&alldevs_mtx);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: device.h,v 1.119 2009/05/07 22:17:41 cegger Exp $ */
|
||||
/* $NetBSD: device.h,v 1.120 2009/06/26 19:30:45 dyoung Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1996, 2000 Christopher G. Demetriou
|
||||
|
@ -201,6 +201,11 @@ struct deviter {
|
|||
};
|
||||
|
||||
typedef struct deviter deviter_t;
|
||||
|
||||
struct shutdown_state {
|
||||
bool initialized;
|
||||
deviter_t di;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -446,6 +451,7 @@ device_t config_attach_pseudo(cfdata_t);
|
|||
|
||||
int config_detach(device_t, int);
|
||||
int config_detach_children(device_t, int flags);
|
||||
bool config_detach_all(int);
|
||||
int config_activate(device_t);
|
||||
int config_deactivate(device_t);
|
||||
void config_defer(device_t, void (*)(device_t));
|
||||
|
@ -540,6 +546,8 @@ void device_pmf_class_register(device_t, void *,
|
|||
void (*)(device_t));
|
||||
void device_pmf_class_deregister(device_t);
|
||||
|
||||
device_t shutdown_first(struct shutdown_state *);
|
||||
device_t shutdown_next(struct shutdown_state *);
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#endif /* !_SYS_DEVICE_H_ */
|
||||
|
|
Loading…
Reference in New Issue