PMF: synchronize device suspension and resumption.

This commit is contained in:
dyoung 2008-03-07 07:03:06 +00:00
parent 954c0a60d9
commit 8705e09ae4
4 changed files with 184 additions and 8 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_pmf.c,v 1.15 2008/03/05 07:09:18 dyoung Exp $ */
/* $NetBSD: kern_pmf.c,v 1.16 2008/03/07 07:03:06 dyoung Exp $ */
/*-
* Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
@ -33,7 +33,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.15 2008/03/05 07:09:18 dyoung Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.16 2008/03/07 07:03:06 dyoung Exp $");
#include <sys/types.h>
#include <sys/param.h>
@ -107,6 +107,9 @@ struct shutdown_state {
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);
static void
pmf_event_worker(struct work *wk, void *dummy)
{
@ -375,9 +378,26 @@ pmf_device_deregister(device_t dev)
bool
pmf_device_suspend(device_t dev PMF_FN_ARGS)
{
bool rc;
PMF_TRANSITION_PRINTF(("%s: suspend enter\n", device_xname(dev)));
if (!device_pmf_is_registered(dev))
return false;
if (!device_pmf_lock(dev PMF_FN_CALL))
return false;
rc = pmf_device_suspend_locked(dev PMF_FN_CALL);
device_pmf_unlock(dev PMF_FN_CALL);
PMF_TRANSITION_PRINTF(("%s: suspend exit\n", device_xname(dev)));
return rc;
}
static bool
pmf_device_suspend_locked(device_t dev PMF_FN_ARGS)
{
PMF_TRANSITION_PRINTF2(1, ("%s: class suspend\n", device_xname(dev)));
if (!device_pmf_class_suspend(dev PMF_FN_CALL))
return false;
@ -387,16 +407,33 @@ pmf_device_suspend(device_t dev PMF_FN_ARGS)
PMF_TRANSITION_PRINTF2(1, ("%s: bus suspend\n", device_xname(dev)));
if (!device_pmf_bus_suspend(dev PMF_FN_CALL))
return false;
PMF_TRANSITION_PRINTF(("%s: suspend exit\n", device_xname(dev)));
return true;
}
bool
pmf_device_resume(device_t dev PMF_FN_ARGS)
{
bool rc;
PMF_TRANSITION_PRINTF(("%s: resume enter\n", device_xname(dev)));
if (!device_pmf_is_registered(dev))
return false;
if (!device_pmf_lock(dev PMF_FN_CALL))
return false;
rc = pmf_device_resume_locked(dev PMF_FN_CALL);
device_pmf_unlock(dev PMF_FN_CALL);
PMF_TRANSITION_PRINTF(("%s: resume exit\n", device_xname(dev)));
return rc;
}
static bool
pmf_device_resume_locked(device_t dev PMF_FN_ARGS)
{
PMF_TRANSITION_PRINTF2(1, ("%s: bus resume\n", device_xname(dev)));
if (!device_pmf_bus_resume(dev PMF_FN_CALL))
return false;
@ -406,7 +443,6 @@ pmf_device_resume(device_t dev PMF_FN_ARGS)
PMF_TRANSITION_PRINTF2(1, ("%s: class resume\n", device_xname(dev)));
if (!device_pmf_class_resume(dev PMF_FN_CALL))
return false;
PMF_TRANSITION_PRINTF(("%s: resume exit\n", device_xname(dev)));
return true;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: subr_autoconf.c,v 1.138 2008/03/07 06:29:20 dyoung Exp $ */
/* $NetBSD: subr_autoconf.c,v 1.139 2008/03/07 07:03:06 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.138 2008/03/07 06:29:20 dyoung Exp $");
__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.139 2008/03/07 07:03:06 dyoung Exp $");
#include "opt_multiprocessor.h"
#include "opt_ddb.h"
@ -127,6 +127,14 @@ extern struct splash_progress *splash_progress_state;
* Autoconfiguration subroutines.
*/
typedef struct pmf_private {
int pp_nwait;
int pp_nlock;
lwp_t *pp_holder;
kmutex_t pp_mtx;
kcondvar_t pp_cv;
} pmf_private_t;
/*
* ioconf.c exports exactly two names: cfdata and cfroots. All system
* devices and drivers are found via these tables.
@ -172,6 +180,9 @@ static void config_makeroom(int, struct cfdriver *);
static void config_devlink(device_t);
static void config_devunlink(device_t);
static void pmflock_debug(device_t, const char *, int);
static void pmflock_debug_with_flags(device_t, const char *, int PMF_FN_PROTO);
static device_t deviter_next1(deviter_t *);
static void deviter_reinit(deviter_t *);
@ -217,6 +228,8 @@ static int config_initialized; /* config_init() has been called. */
static int config_do_twiddle;
MALLOC_DEFINE(M_PMFPRIV, "pmfpriv", "device pmf private storage");
struct vnode *
opendisk(struct device *dv)
{
@ -1944,6 +1957,14 @@ device_pmf_driver_register(device_t dev,
bool (*resume)(device_t PMF_FN_PROTO),
bool (*shutdown)(device_t, int))
{
pmf_private_t *pp;
if ((pp = malloc(sizeof(*pp), M_PMFPRIV, M_NOWAIT|M_ZERO)) == NULL)
return false;
mutex_init(&pp->pp_mtx, MUTEX_DEFAULT, IPL_NONE);
cv_init(&pp->pp_cv, "pmfsusp");
dev->dv_pmf_private = pp;
dev->dv_driver_suspend = suspend;
dev->dv_driver_resume = resume;
dev->dv_driver_shutdown = shutdown;
@ -1951,12 +1972,43 @@ device_pmf_driver_register(device_t dev,
return true;
}
static const char *
curlwp_name(void)
{
if (curlwp->l_name != NULL)
return curlwp->l_name;
else
return curlwp->l_proc->p_comm;
}
void
device_pmf_driver_deregister(device_t dev)
{
pmf_private_t *pp = dev->dv_pmf_private;
dev->dv_driver_suspend = NULL;
dev->dv_driver_resume = NULL;
dev->dv_pmf_private = NULL;
mutex_enter(&pp->pp_mtx);
dev->dv_flags &= ~DVF_POWER_HANDLERS;
while (pp->pp_nlock > 0 || pp->pp_nwait > 0) {
/* Wake a thread that waits for the lock. That
* thread will fail to acquire the lock, and then
* it will wake the next thread that waits for the
* lock, or else it will wake us.
*/
cv_signal(&pp->pp_cv);
pmflock_debug(dev, __func__, __LINE__);
cv_wait(&pp->pp_cv, &pp->pp_mtx);
pmflock_debug(dev, __func__, __LINE__);
}
mutex_exit(&pp->pp_mtx);
cv_destroy(&pp->pp_cv);
mutex_destroy(&pp->pp_mtx);
free(pp, M_PMFPRIV);
}
bool
@ -1976,6 +2028,86 @@ device_pmf_driver_set_child_register(device_t dev,
dev->dv_driver_child_register = child_register;
}
static void
pmflock_debug(device_t dev, const char *func, int line)
{
pmf_private_t *pp = device_pmf_private(dev);
aprint_debug_dev(dev, "%s.%d, %s pp_nlock %d pp_nwait %d dv_flags %x\n",
func, line, curlwp_name(), pp->pp_nlock, pp->pp_nwait,
dev->dv_flags);
}
static void
pmflock_debug_with_flags(device_t dev, const char *func, int line PMF_FN_ARGS)
{
pmf_private_t *pp = device_pmf_private(dev);
aprint_debug_dev(dev, "%s.%d, %s pp_nlock %d pp_nwait %d dv_flags %x "
"flags " PMF_FLAGS_FMT "\n", func, line, curlwp_name(),
pp->pp_nlock, pp->pp_nwait, dev->dv_flags PMF_FN_CALL);
}
static bool
device_pmf_lock1(device_t dev PMF_FN_ARGS)
{
pmf_private_t *pp = device_pmf_private(dev);
while (pp->pp_nlock > 0 && pp->pp_holder != curlwp &&
device_pmf_is_registered(dev)) {
pp->pp_nwait++;
pmflock_debug_with_flags(dev, __func__, __LINE__ PMF_FN_CALL);
cv_wait(&pp->pp_cv, &pp->pp_mtx);
pmflock_debug_with_flags(dev, __func__, __LINE__ PMF_FN_CALL);
pp->pp_nwait--;
}
if (!device_pmf_is_registered(dev)) {
pmflock_debug_with_flags(dev, __func__, __LINE__ PMF_FN_CALL);
/* We could not acquire the lock, but some other thread may
* wait for it, also. Wake that thread.
*/
cv_signal(&pp->pp_cv);
return false;
}
pp->pp_nlock++;
pp->pp_holder = curlwp;
pmflock_debug_with_flags(dev, __func__, __LINE__ PMF_FN_CALL);
return true;
}
bool
device_pmf_lock(device_t dev PMF_FN_ARGS)
{
bool rc;
pmf_private_t *pp = device_pmf_private(dev);
mutex_enter(&pp->pp_mtx);
rc = device_pmf_lock1(dev PMF_FN_CALL);
mutex_exit(&pp->pp_mtx);
return rc;
}
void
device_pmf_unlock(device_t dev PMF_FN_ARGS)
{
pmf_private_t *pp = device_pmf_private(dev);
KASSERT(pp->pp_nlock > 0);
mutex_enter(&pp->pp_mtx);
if (--pp->pp_nlock == 0)
pp->pp_holder = NULL;
cv_signal(&pp->pp_cv);
pmflock_debug_with_flags(dev, __func__, __LINE__ PMF_FN_CALL);
mutex_exit(&pp->pp_mtx);
}
void *
device_pmf_private(device_t dev)
{
return dev->dv_pmf_private;
}
void *
device_pmf_bus_private(device_t dev)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: device.h,v 1.106 2008/03/05 07:09:18 dyoung Exp $ */
/* $NetBSD: device.h,v 1.107 2008/03/07 07:03:06 dyoung Exp $ */
/*
* Copyright (c) 1996, 2000 Christopher G. Demetriou
@ -155,6 +155,8 @@ struct device {
bool (*dv_class_suspend)(device_t PMF_FN_PROTO);
bool (*dv_class_resume)(device_t PMF_FN_PROTO);
void (*dv_class_deregister)(device_t);
void *dv_pmf_private;
};
/* dv_flags */
@ -492,6 +494,10 @@ bool device_pmf_bus_suspend(device_t PMF_FN_PROTO);
bool device_pmf_bus_resume(device_t PMF_FN_PROTO);
bool device_pmf_bus_shutdown(device_t, int);
void *device_pmf_private(device_t);
void device_pmf_unlock(device_t PMF_FN_PROTO);
bool device_pmf_lock(device_t PMF_FN_PROTO);
void device_pmf_bus_register(device_t, void *,
bool (*)(device_t PMF_FN_PROTO),
bool (*)(device_t PMF_FN_PROTO),

View File

@ -1,4 +1,4 @@
/* $NetBSD: pmf.h,v 1.8 2008/03/05 04:54:24 dyoung Exp $ */
/* $NetBSD: pmf.h,v 1.9 2008/03/07 07:03:06 dyoung Exp $ */
/*-
* Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
@ -61,6 +61,8 @@ typedef enum {
#define PMF_FN_ARGS /* , pmf_flags_t flags */
#define PMF_FN_CALL /* , flags */
#define PMF_FLAGS_FMT "n/a" /* "%x" */
void pmf_init(void);
bool pmf_event_inject(device_t, pmf_generic_event_t);