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:
dyoung 2009-06-26 19:30:45 +00:00
parent 57a3ffeae7
commit 9d9978e5a5
3 changed files with 77 additions and 80 deletions

View File

@ -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))

View File

@ -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);

View File

@ -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_ */