PMF: synchronize device suspension and resumption.
This commit is contained in:
parent
954c0a60d9
commit
8705e09ae4
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue