Edit

IABSD.fr/src/sys/kern/subr_suspend.c

Branch :

  • Show log

    Commit

  • Author : kettenis
    Date : 2025-06-16 20:21:33
    Hash : ca139715
    Message : Introduce a generic powerbutton_event() function that does everything we expect from a power button event in a consistent manner. This gets rid of some duplicated code and means all drivers now prevent shutdown within the first 10 seconds after resume. tested by hastings@, jca@ ok landry@, deraadt@

  • sys/kern/subr_suspend.c
  • /* $OpenBSD: subr_suspend.c,v 1.19 2025/06/16 20:21:33 kettenis Exp $ */
    /*
     * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com>
     * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    #include <sys/param.h>
    #include <sys/systm.h>
    #include <sys/buf.h>
    #include <sys/clockintr.h>
    #include <sys/reboot.h>
    #include <sys/sensors.h>
    #include <sys/sysctl.h>
    #include <sys/mount.h>
    #include <sys/syscallargs.h>
    #include <dev/wscons/wsdisplayvar.h>
    #ifdef GPROF
    #include <sys/gmon.h>
    #endif
    #ifdef HIBERNATE
    #include <sys/hibernate.h>
    #endif
    
    #include "softraid.h"
    #include "wsdisplay.h"
    
    /* Number of (active) wakeup devices in the system. */
    u_int wakeup_devices;
    
    /* Uptime of last resume. */
    time_t resume_time;
    
    void
    device_register_wakeup(struct device *dev)
    {
    	wakeup_devices++;
    }
    
    int
    sleep_state(void *v, int sleepmode)
    {
    	int error, s;
    	extern int perflevel;
    	size_t rndbuflen;
    	char *rndbuf;
    #ifdef GPROF
    	int gmon_state;
    #endif
    #if NSOFTRAID > 0
    	extern void sr_quiesce(void);
    #endif
    
    top:
    	error = ENXIO;
    	rndbuf = NULL;
    	rndbuflen = 0;
    
    	if (sleepmode == SLEEP_SUSPEND && wakeup_devices == 0)
    		return EOPNOTSUPP;
    
    	if (sleep_showstate(v, sleepmode))
    		return EOPNOTSUPP;
    #if NWSDISPLAY > 0
    	wsdisplay_suspend();
    #endif
    	stop_periodic_resettodr();
    
    #ifdef HIBERNATE
    	if (sleepmode == SLEEP_HIBERNATE) {
    		/*
    		 * Discard useless memory to reduce fragmentation,
    		 * and attempt to create a hibernate work area
    		 */
    		hibernate_suspend_bufcache();
    		uvmpd_hibernate();
    		if (hibernate_alloc()) {
    			printf("failed to allocate hibernate memory\n");
    			error = ENOMEM;
    			goto fail_hiballoc;
    		}
    	}
    #endif /* HIBERNATE */
    
    	sensor_quiesce();
    	if (config_suspend_all(DVACT_QUIESCE)) {
    		error = EIO;
    		goto fail_quiesce;
    	}
    
    	vfs_stall(curproc, 1);
    #if NSOFTRAID > 0
    	sr_quiesce();
    #endif
    	bufq_quiesce();
    #ifdef MULTIPROCESSOR
    	sched_stop_secondary_cpus();
    	KASSERT(CPU_IS_PRIMARY(curcpu()));
    #endif
    #ifdef GPROF
    	gmon_state = gmoninit;
    	gmoninit = 0;
    #endif
    #ifdef MULTIPROCESSOR
    	sleep_mp();
    #endif
    
    #ifdef HIBERNATE
    	if (sleepmode == SLEEP_HIBERNATE) {
    		/*
    		 * We've just done various forms of syncing to disk
    		 * churned lots of memory dirty.  We don't need to
    		 * save that dirty memory to hibernate, so release it.
    		 */
    		hibernate_suspend_bufcache();
    		uvmpd_hibernate();
    	}
    #endif /* HIBERNATE */
    
    	resettodr();
    
    	s = splhigh();
    	intr_disable();	/* PSL_I for resume; PIC/APIC broken until repair */
    	cold = 2;	/* Force other code to delay() instead of tsleep() */
    	intr_enable_wakeup();
    
    	if (config_suspend_all(DVACT_SUSPEND) != 0) {
    		error = EDEADLK;
    		goto fail_suspend;
    	}
    	suspend_randomness();
    	if (sleep_setstate(v)) {
    		error = ENOTBLK;
    		goto fail_pts;
    	}
    
    	if (sleepmode == SLEEP_SUSPEND) {
    		/*
    		 * XXX
    		 * Flag to disk drivers that they should "power down" the disk
    		 * when we get to DVACT_POWERDOWN.
    		 */
    		boothowto |= RB_POWERDOWN;
    		config_suspend_all(DVACT_POWERDOWN);
    		boothowto &= ~RB_POWERDOWN;
    
    		if (cpu_setperf != NULL)
    			cpu_setperf(0);
    	}
    
    	error = gosleep(v);
    
    #ifdef HIBERNATE
    	if (sleepmode == SLEEP_HIBERNATE) {
    		uvm_pmr_dirty_everything();
    		hib_getentropy(&rndbuf, &rndbuflen);
    	}
    #endif /* HIBERNATE */
    
    fail_pts:
    	config_suspend_all(DVACT_RESUME);
    
    fail_suspend:
    	intr_disable_wakeup();
    	cold = 0;
    	intr_enable();
    	splx(s);
    
    	inittodr(gettime());
    	clockintr_cpu_init(NULL);
    	clockintr_trigger();
    
    	resume_time = getuptime();
    
    	sleep_resume(v);
    	resume_randomness(rndbuf, rndbuflen);
    #ifdef MULTIPROCESSOR
    	resume_mp();
    #endif
    #ifdef GPROF
    	gmoninit = gmon_state;
    #endif
    #ifdef MULTIPROCESSOR
    	sched_start_secondary_cpus();
    #endif
    	vfs_stall(curproc, 0);
    	bufq_restart();
    
    fail_quiesce:
    	config_suspend_all(DVACT_WAKEUP);
    	sensor_restart();
    
    #ifdef HIBERNATE
    	if (sleepmode == SLEEP_HIBERNATE) {
    		hibernate_free();
    fail_hiballoc:
    		hibernate_resume_bufcache();
    	}
    #endif /* HIBERNATE */
    
    	start_periodic_resettodr();
    #if NWSDISPLAY > 0
    	wsdisplay_resume();
    #endif
    	sys_sync(curproc, NULL, NULL);
    	if (cpu_setperf != NULL)
    		cpu_setperf(perflevel);	/* Restore hw.setperf */
    	if (suspend_finish(v) == EAGAIN)
    		goto top;
    	return (error);
    }
    
    int
    resuming(void)
    {
    	return (getuptime() < resume_time + 10);
    }