diff options
author | Owain Ainsworth <oga@cvs.openbsd.org> | 2010-03-30 17:40:56 +0000 |
---|---|---|
committer | Owain Ainsworth <oga@cvs.openbsd.org> | 2010-03-30 17:40:56 +0000 |
commit | 17f79ce6ab989e13c40b1b05780f81a5d5f2fe05 (patch) | |
tree | dd13f9c70165296d96946a2c3d66f8b4709ae64c /sys | |
parent | f5972d809904e97326ca314c087612469e208db2 (diff) |
Prevent the apmd/x races for good.
When we hit suspend time, go through all wsdisplays on the system. if
they are in mode MAPPED, but not MODE_DUMBFB then if possible do a full
vt switch to a !mapped vt, and prevent switching back until resume time.
This has to be called from MD code because this involves userland
running so that X can run the vt switch signal handler. This way, any
case where we are using the "poke registers from userland" model, we
will not be on the hardware when we go down, so the kernel can actually
handle thing properly.
Tested on several acpi laptops (by kettenis@ and ian@), x40 (me and
beck@ at LEAST) and zaurus (me). Maybe others, but if so I forgot who at
this time..
Idea from deraadt somewhere over the Faroe Islands (I thought of a
similar thing myself a while ago). Much prompting from him. Ok and
comments miod@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/arm/xscale/pxa2x0_apm.c | 11 | ||||
-rw-r--r-- | sys/arch/i386/i386/apm.c | 14 | ||||
-rw-r--r-- | sys/arch/zaurus/dev/zaurus_apm.c | 14 | ||||
-rw-r--r-- | sys/dev/acpi/acpi.c | 14 | ||||
-rw-r--r-- | sys/dev/wscons/wsdisplay.c | 112 | ||||
-rw-r--r-- | sys/dev/wscons/wsdisplayvar.h | 4 |
6 files changed, 163 insertions, 6 deletions
diff --git a/sys/arch/arm/xscale/pxa2x0_apm.c b/sys/arch/arm/xscale/pxa2x0_apm.c index 9d2e5bc2343..a009324dc8d 100644 --- a/sys/arch/arm/xscale/pxa2x0_apm.c +++ b/sys/arch/arm/xscale/pxa2x0_apm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pxa2x0_apm.c,v 1.31 2009/03/27 16:01:37 oga Exp $ */ +/* $OpenBSD: pxa2x0_apm.c,v 1.32 2010/03/30 17:40:55 oga Exp $ */ /*- * Copyright (c) 2001 Alexander Guy. All rights reserved. @@ -55,6 +55,9 @@ #include <arm/xscale/pxa2x0var.h> #include <arm/xscale/pxa2x0_apm.h> #include <arm/xscale/pxa2x0_gpio.h> +#include <dev/wscons/wsdisplayvar.h> + +#include "wsdisplay.h" #if defined(APMDEBUG) #define DPRINTF(x) printf x @@ -302,6 +305,9 @@ apm_power_info(struct pxa2x0_apm_softc *sc, void apm_suspend(struct pxa2x0_apm_softc *sc) { +#if NWSDISPLAY > 0 + wsdisplay_suspend(); +#endif /* NWSDISPLAY > 0 */ resettodr(); @@ -332,6 +338,9 @@ apm_resume(struct pxa2x0_apm_softc *sc) */ /* XXX ifdef NPXAUDC > 0 */ bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PSSR, PSSR_OTGPH); +#if NWSDISPLAY > 0 + wsdisplay_resume(); +#endif /* NWSDISPLAY > 0 */ } int diff --git a/sys/arch/i386/i386/apm.c b/sys/arch/i386/i386/apm.c index 8ec835e69ef..b2ce1ab4dfe 100644 --- a/sys/arch/i386/i386/apm.c +++ b/sys/arch/i386/i386/apm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: apm.c,v 1.84 2009/06/24 13:54:42 deraadt Exp $ */ +/* $OpenBSD: apm.c,v 1.85 2010/03/30 17:40:55 oga Exp $ */ /*- * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved. @@ -61,11 +61,14 @@ #include <i386/isa/isa_machdep.h> #include <i386/isa/nvram.h> #include <dev/isa/isavar.h> +#include <dev/wscons/wsdisplayvar.h> #include <machine/acpiapm.h> #include <machine/biosvar.h> #include <machine/apmvar.h> +#include "wsdisplay.h" + #if defined(APMDEBUG) #define DPRINTF(x) printf x #else @@ -316,6 +319,9 @@ apm_power_print(struct apm_softc *sc, struct apmregs *regs) void apm_suspend() { +#if NWSDISPLAY > 0 + wsdisplay_suspend(); +#endif /* NWSDISPLAY > 0 */ dopowerhooks(PWR_SUSPEND); if (cold) @@ -327,6 +333,9 @@ apm_suspend() void apm_standby() { +#if NWSDISPLAY > 0 + wsdisplay_suspend(); +#endif /* NWSDISPLAY > 0 */ dopowerhooks(PWR_STANDBY); if (cold) @@ -356,6 +365,9 @@ apm_resume(struct apm_softc *sc, struct apmregs *regs) /* restore hw.setperf */ if (cpu_setperf != NULL) cpu_setperf(perflevel); +#if NWSDISPLAY > 0 + wsdisplay_resume(); +#endif /* NWSDISPLAY > 0 */ } int diff --git a/sys/arch/zaurus/dev/zaurus_apm.c b/sys/arch/zaurus/dev/zaurus_apm.c index 18049edf1cb..75785f2de6b 100644 --- a/sys/arch/zaurus/dev/zaurus_apm.c +++ b/sys/arch/zaurus/dev/zaurus_apm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: zaurus_apm.c,v 1.13 2006/12/12 23:14:28 dim Exp $ */ +/* $OpenBSD: zaurus_apm.c,v 1.14 2010/03/30 17:40:55 oga Exp $ */ /* * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de> @@ -34,6 +34,10 @@ void zssp_init(void); /* XXX */ #include <zaurus/dev/zaurus_apm.h> +#include <dev/wscons/wsdisplayvar.h> + +#include "wsdisplay.h" + #if defined(APMDEBUG) #define DPRINTF(x) printf x #else @@ -637,6 +641,10 @@ zapm_poweroff(void) KASSERT(apm_cd.cd_ndevs > 0 && apm_cd.cd_devs[0] != NULL); sc = apm_cd.cd_devs[0]; +#if NWSDISPLAY > 0 + wsdisplay_suspend(); +#endif /* NWSDISPLAY > 0 */ + dopowerhooks(PWR_SUSPEND); /* XXX enable charging during suspend */ @@ -660,6 +668,10 @@ zapm_poweroff(void) /* NOTREACHED */ dopowerhooks(PWR_RESUME); + +#if NWSDISPLAY > 0 + wsdisplay_resume(); +#endif /* NWSDISPLAY > 0 */ } /* diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c index 909e1db7124..3d7361a2ddf 100644 --- a/sys/dev/acpi/acpi.c +++ b/sys/dev/acpi/acpi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi.c,v 1.153 2010/03/25 23:00:20 oga Exp $ */ +/* $OpenBSD: acpi.c,v 1.154 2010/03/30 17:40:55 oga Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com> * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org> @@ -38,6 +38,7 @@ #include <dev/acpi/amltypes.h> #include <dev/acpi/acpidev.h> #include <dev/acpi/dsdt.h> +#include <dev/wscons/wsdisplayvar.h> #include <dev/pci/pciidereg.h> #include <dev/pci/pciidevar.h> @@ -48,6 +49,8 @@ #define APMDEV_NORMAL 0 #define APMDEV_CTL 8 +#include "wsdisplay.h" + #ifdef ACPI_DEBUG int acpi_debug = 16; #endif @@ -1956,6 +1959,10 @@ acpi_resume(struct acpi_softc *sc, int state) env.v_integer = ACPI_SST_WORKING; aml_evalnode(sc, sc->sc_sst, 1, &env, NULL); } + +#if NWSDISPLAY > 0 + wsdisplay_resume(); +#endif /* NWSDISPLAY > 0 */ } #endif /* ! SMALL_KERNEL */ @@ -2015,6 +2022,11 @@ acpi_prepare_sleep_state(struct acpi_softc *sc, int state) return (ENXIO); } +#if NWSDISPLAY > 0 + if (state == ACPI_STATE_S3) + wsdisplay_suspend(); +#endif /* NWSDISPLAY > 0 */ + acpi_saved_spl = splhigh(); disable_intr(); cold = 1; diff --git a/sys/dev/wscons/wsdisplay.c b/sys/dev/wscons/wsdisplay.c index ae36fbc7258..70153570124 100644 --- a/sys/dev/wscons/wsdisplay.c +++ b/sys/dev/wscons/wsdisplay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wsdisplay.c,v 1.97 2009/11/09 17:53:39 nicm Exp $ */ +/* $OpenBSD: wsdisplay.c,v 1.98 2010/03/30 17:40:55 oga Exp $ */ /* $NetBSD: wsdisplay.c,v 1.82 2005/02/27 00:27:52 perry Exp $ */ /* @@ -154,6 +154,8 @@ int wsdisplay_addscreen(struct wsdisplay_softc *, int, const char *, const char *); int wsdisplay_getscreen(struct wsdisplay_softc *, struct wsdisplay_addscreendata *); +void wsdisplay_resume_device(struct device *); +void wsdisplay_suspend_device(struct device *); void wsdisplay_shutdownhook(void *); void wsdisplay_addscreen_print(struct wsdisplay_softc *, int, int); void wsdisplay_closescreen(struct wsdisplay_softc *, struct wsscreen *); @@ -189,6 +191,7 @@ struct wsdisplay_softc { #define SC_SWITCHPENDING 0x01 #define SC_PASTE_AVAIL 0x02 int sc_screenwanted, sc_oldscreen; /* valid with SC_SWITCHPENDING */ + int sc_resumescreen; /* if set, can't switch until resume. */ #if NWSKBD > 0 struct wsevsrc *sc_input; @@ -683,6 +686,7 @@ wsdisplay_common_attach(struct wsdisplay_softc *sc, int console, int kbdmux, #endif /* NWSKBD > 0 */ sc->sc_isconsole = console; + sc->sc_resumescreen = WSDISPLAY_NULLSCREEN; if (console) { KASSERT(wsdisplay_console_initted); @@ -1798,6 +1802,13 @@ wsdisplay_switch(struct device *dev, int no, int waitok) s = spltty(); + while (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN && res == 0) + res = tsleep(&sc->sc_resumescreen, PCATCH, "wsrestore", 0); + if (res) { + splx(s); + return (res); + } + if ((sc->sc_focus && no == sc->sc_focusidx) || (sc->sc_focus == NULL && no == WSDISPLAY_NULLSCREEN)) { splx(s); @@ -2152,6 +2163,105 @@ wsdisplay_switchtoconsole() } } +/* + * Deal with the xserver doing driver in userland and thus screwing up suspend + * and resume by switching away from it at suspend/resume time. + * + * these functions must be called from the MD suspend callback, since we may + * need to sleep if we have a user (probably an X server) on a vt. therefore + * this can't be a config_suspend() hook. + */ +void +wsdisplay_suspend(void) +{ + int i; + + for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) + if (wsdisplay_cd.cd_devs[i] != NULL) + wsdisplay_suspend_device(wsdisplay_cd.cd_devs[i]); +} + +void +wsdisplay_suspend_device(struct device *dev) +{ + struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; + struct wsscreen *scr; + int active, idx, ret = 0, s; + + if ((active = wsdisplay_getactivescreen(sc)) == WSDISPLAY_NULLSCREEN) + return; + + scr = sc->sc_scr[active]; + /* + * We want to switch out of graphics mode for the suspend, but + * only if we're in WSDISPLAY_MODE_MAPPED. + */ +retry: + idx = WSDISPLAY_MAXSCREEN; + if (scr->scr_flags & SCR_GRAPHICS && + (scr->scr_flags & SCR_DUMBFB) == 0) { + for (idx = 0; idx < WSDISPLAY_MAXSCREEN; idx++) { + if (sc->sc_scr[idx] == NULL || sc->sc_scr[idx] == scr) + continue; + + if ((sc->sc_scr[idx]->scr_flags & SCR_GRAPHICS) == 0) + break; + } + } + + /* if we don't have anything to switch to, we can't do anything */ + if (idx == WSDISPLAY_MAXSCREEN) + return; + + /* + * we do a lot of magic here because we need to know that the + * switch has completed before we return + */ + ret = wsdisplay_switch((struct device *)sc, idx, 1); + if (ret == EBUSY) { + /* XXX sleep on what's going on */ + goto retry; + } else if (ret) + return; + + s = spltty(); + sc->sc_resumescreen = active; /* block other vt switches until resume */ + splx(s); + /* + * This will either return ENXIO (invalid (shouldn't happen) or + * wsdisplay disappeared (problem solved)), or EINTR/ERESTART. + * Not much we can do about the latter since we can't return to + * userland. + */ + (void)wsscreen_switchwait(sc, idx); +} + +void +wsdisplay_resume(void) +{ + int i; + + for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) + if (wsdisplay_cd.cd_devs[i] != NULL) + wsdisplay_resume_device(wsdisplay_cd.cd_devs[i]); +} + +void +wsdisplay_resume_device(struct device *dev) +{ + struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; + int idx, s; + + if (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN) { + s = spltty(); + idx = sc->sc_resumescreen; + sc->sc_resumescreen = WSDISPLAY_NULLSCREEN; + wakeup(&sc->sc_resumescreen); + splx(s); + (void)wsdisplay_switch((struct device *)sc, idx, 1); + } +} + #ifdef SCROLLBACK_SUPPORT void wsscrollback(void *arg, int op) diff --git a/sys/dev/wscons/wsdisplayvar.h b/sys/dev/wscons/wsdisplayvar.h index 9f9a09ebe89..d7038a12b40 100644 --- a/sys/dev/wscons/wsdisplayvar.h +++ b/sys/dev/wscons/wsdisplayvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: wsdisplayvar.h,v 1.23 2009/09/05 14:09:35 miod Exp $ */ +/* $OpenBSD: wsdisplayvar.h,v 1.24 2010/03/30 17:40:55 oga Exp $ */ /* $NetBSD: wsdisplayvar.h,v 1.30 2005/02/04 02:10:49 perry Exp $ */ /* @@ -209,6 +209,8 @@ int wsdisplay_cfg_ioctl(struct wsdisplay_softc *sc, */ #define WSDISPLAY_NULLSCREEN -1 void wsdisplay_switchtoconsole(void); +void wsdisplay_suspend(void); +void wsdisplay_resume(void); const struct wsscreen_descr * wsdisplay_screentype_pick(const struct wsscreen_list *, const char *); |