Skip to contentSkip to conversation

Unified power management userspace policy

PATCH John Fremlin 2 participants 4 messages Jan 8, 2001 – Jan 9, 2001

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

Hi all!

At the moment there are two power management drivers in the linux kernel (AFAIK). They each have different userspace interfaces -- /proc/apm and /dev/apmctl and /proc/sys/acpi/events or something. This is not altogether bad, but as they do the same thing, it might be nice to unify (part) of the interface. In fact this is already done for the in kernel interface with pm_send_all.

The kernel patch below extends the pm_send_all idea to ask userspace if the event causing it should be allowed to go ahead or not. This functionality is useful to me, because my power button sends an APM user suspend to the kernel, which I wish to convert into a clean shutdown -p now. The functionality is only hooked up to the APM driver at the moment (because that was the only caller of pm_send_all that I could find and ACPI doesn't seem to do anything useful on my box).

The kernel will run /sbin/powermanager (proc configurable) when it receives a rejectable PM event. This is to my mind preferable to having a power daemon because a PM event might occur even when the daemon isn't started yet, you waste a process entry the whole time, I'd have to implement a bunch of special file ops and binary interfaces, and to parallel the hotplug system.

There is an APM specific patch floating around that would permit apmd to reject suspend events. I played around with it for a bit updating it to the late 2.4.0-test12 but I didn't like it because it did suspend() directly after receiving the event from the BIOS, so my box would suspend briefly before rejecting the event.

An example /sbin/powermanager should be being uploaded to my homepage as I write:

http://www.penguinpowered.com/~vii/programs/linux/offbutton

or
http://john.snoop.dk/programs/linux/offbutton

The patch has been working fine for me in various forms since 2.4.0-prerelease came out, though I'm hardly a power user (pun).

It is against linux 2.4.0.

arch/i386/kernel/apm.c +91 -20
diff -u --recursive linux-clean/arch/i386/kernel/apm.c linux-hacked-pmpolicy/arch/i386/kernel/apm.c
@@ -148,6 +148,8 @@
  *   1.14: Make connection version persist across module unload/load.
  *         Enable and engage power management earlier.
  *         Disengage power management on module unload.
+ *   ---   Modified to support new pm_event API (John Fremlin
+ *         <[email protected]>)
  *
  * APM 1.1 Reference:
  *
@@ -884,30 +886,65 @@
 #endif
 }
 
+static int soft_suspend()
+{
+	struct pm_event pe;
+	memset(&pe,0,sizeof pe);
+
+	pe.pe_state = PM_STATE_SLEEP;
+	pe.pe_source = PME_SRC_SOFT;
+	pe.pe_flags = PME_FLAGS_REJECTABLE;
+
+	return pm_request_event(&pe);
+}
+
 static int send_event(apm_event_t event)
 {
+	struct pm_event pe;
+	memset(&pe,0,sizeof pe);
+
 	switch (event) {
 	case APM_SYS_SUSPEND:
+		pe.pe_state = PM_STATE_SLEEP;
+		pe.pe_source = PME_SRC_SYS;
+		pe.pe_flags = PME_FLAGS_REJECTABLE;
+		break;
+		
 	case APM_CRITICAL_SUSPEND:
+		pe.pe_state = PM_STATE_SLEEP;
+		pe.pe_source = PME_SRC_SYS;
+		break;
+			
 	case APM_USER_SUSPEND:
-		/* map all suspends to ACPI D3 */
-		if (pm_send_all(PM_SUSPEND, (void *)3)) {
-			if (event == APM_CRITICAL_SUSPEND) {
-				printk(KERN_CRIT "apm: Critical suspend was vetoed, expect armageddon\n" );
-				return 0;
-			}
-			if (apm_info.connection_version > 0x100)
-				apm_set_power_state(APM_STATE_REJECT);
-			return 0;
-		}
+		pe.pe_state = PM_STATE_SLEEP;
+		pe.pe_source = PME_SRC_HWUSER;
+		pe.pe_flags = PME_FLAGS_REJECTABLE;
 		break;
-	case APM_NORMAL_RESUME:
+
 	case APM_CRITICAL_RESUME:
-		/* map all resumes to ACPI D0 */
-		(void) pm_send_all(PM_RESUME, (void *)0);
+		pe.pe_state = PM_STATE_WAKEFUL;
+		pe.pe_source = PME_SRC_SYS;
+		break;
+		
+	case APM_NORMAL_RESUME:
+		pe.pe_state = PM_STATE_WAKEFUL;
+		pe.pe_source = PME_SRC_HWUSER;
 		break;
+		
+	default:
+		return 1;
 	}
 
+	if(pm_prepare_for_event(&pe))
+	{
+		if (event == APM_CRITICAL_SUSPEND) {
+			printk(KERN_CRIT "apm: critical suspend was vetoed, expect armageddon\n" );
+			return 0;
+		}
+		if (apm_info.connection_version > 0x100)
+			apm_set_power_state(APM_STATE_REJECT);
+		return 0;
+	}
 	return 1;
 }
 
@@ -925,8 +962,10 @@
 		err = APM_SUCCESS;
 	if (err != APM_SUCCESS)
 		apm_error("suspend", err);
-	send_event(APM_NORMAL_RESUME);
 	sti();
+	/* We mustn't pm_event_lock because whoever is waiting for the
+	 * end of this suspend is holding the lock */
+	send_event(APM_NORMAL_RESUME);
 	queue_event(APM_NORMAL_RESUME, NULL);
 	for (as = user_list; as != NULL; as = as->next) {
 		as->suspend_wait = 0;
@@ -947,6 +986,21 @@
 		apm_error("standby", err);
 }
 
+static int apm_enter_state(pm_state_t*ps)
+{
+	switch(*ps){
+	case PM_STATE_SLEEP:
+		return suspend();
+	case PM_STATE_WAKEFUL:
+		return 0;
+	case PM_STATE_POWEROFF:
+		apm_power_off();
+		return 2; /* it didn't work, did it? */
+	default:
+		return 1;
+	}
+}
+
 static apm_event_t get_event(void)
 {
 	int		error;
@@ -1019,12 +1073,17 @@
 			 */
 			if (waiting_for_resume)
 				return;
+			
+			pm_event_lock();
+			
 			if (send_event(event)) {
 				queue_event(event, NULL);
 				waiting_for_resume = 1;
 				if (suspends_pending <= 0)
 					(void) suspend();
 			}
+			pm_event_unlock();
+
 			break;
 
 		case APM_NORMAL_RESUME:
@@ -1036,7 +1095,10 @@
 			if ((event != APM_NORMAL_RESUME)
 			    || (ignore_normal_resume == 0)) {
 				set_time();
+				pm_event_lock();
 				send_event(event);
+				pm_event_unlock();
+				
 				queue_event(event, NULL);
 			}
 			break;
@@ -1053,12 +1115,16 @@
 			break;
 
 		case APM_CRITICAL_SUSPEND:
+			pm_event_lock();
 			send_event(event);
 			/*
 			 * We can only hope it worked - we are not allowed
 			 * to reject a critical suspend.
 			 */
 			(void) suspend();
+			
+			pm_event_unlock();
+			
 			break;
 		}
 	}
@@ -1240,14 +1306,16 @@
 			as->suspends_read--;
 			as->suspends_pending--;
 			suspends_pending--;
-		} else if (!send_event(APM_USER_SUSPEND))
-			return -EAGAIN;
-		else
-			queue_event(APM_USER_SUSPEND, as);
+		}
+		
 		if (suspends_pending <= 0) {
-			if (suspend() != APM_SUCCESS)
+			if (soft_suspend() != 0)
 				return -EIO;
+
+			queue_event(APM_USER_SUSPEND, as);
 		} else {
+			queue_event(APM_USER_SUSPEND, as);
+			
 			as->suspend_wait = 1;
 			add_wait_queue(&apm_suspend_waitqueue, &wait);
 			while (1) {
@@ -1285,7 +1353,7 @@
 	if (as->suspends_pending > 0) {
 		suspends_pending -= as->suspends_pending;
 		if (suspends_pending <= 0)
-			(void) suspend();
+			(void) soft_suspend();
 	}
 	if (user_list == as)
 		user_list = as->next;
@@ -1526,6 +1594,9 @@
 #ifdef CONFIG_MAGIC_SYSRQ
 	sysrq_power_off = apm_power_off;
 #endif
+
+	pm_enter_state = apm_enter_state;
+
 	if (smp_num_cpus == 1) {
 #if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
 		console_blank_hook = apm_console_blank;
include/linux/pm.h +96 -0

124 lines not on this page · raw message

include/linux/sysctl.h +1 -0
diff -u --recursive linux-clean/include/linux/sysctl.h linux-hacked-pmpolicy/include/linux/sysctl.h
@@ -117,6 +117,7 @@
 	KERN_OVERFLOWGID=47,	/* int: overflow GID */
 	KERN_SHMPATH=48,	/* string: path to shm fs */
 	KERN_HOTPLUG=49,	/* string: path to hotplug policy agent */
+	KERN_POWERMANAGER=50,   /* string: path to PM policy agent */
 };
 
 
kernel/pm.c +245 -2

278 lines not on this page · raw message

kernel/sysctl.c +7 -0
diff -u --recursive linux-clean/kernel/sysctl.c linux-hacked-pmpolicy/kernel/sysctl.c
@@ -58,6 +58,9 @@
 #ifdef CONFIG_HOTPLUG
 extern char hotplug_path[];
 #endif
+#ifdef CONFIG_PM
+extern char pm_policyhelper_path[];
+#endif
 #ifdef CONFIG_CHR_DEV_SG
 extern int sg_big_buff;
 #endif
@@ -195,6 +198,10 @@
 	{KERN_HOTPLUG, "hotplug", &hotplug_path, 256,
 	 0644, NULL, &proc_dostring, &sysctl_string },
 #endif
+#ifdef CONFIG_PM
+	{KERN_POWERMANAGER, "powermanager",&pm_policyhelper_path, 256,
+	 0644, NULL, &proc_dostring, &sysctl_string },
+#endif	
 #ifdef CONFIG_CHR_DEV_SG
 	{KERN_SG_BIG_BUFF, "sg-big-buff", &sg_big_buff, sizeof (int),
 	 0444, NULL, &proc_dointvec},
Signature · 3 lines
-- 

	http://www.penguinpowered.com/~vii
#1 lore raw
Quoted 10 lines · John Fremlin

Hi all!

At the moment there are two power management drivers in the linux kernel (AFAIK). They each have different userspace interfaces -- /proc/apm and /dev/apmctl and /proc/sys/acpi/events or something. This is not altogether bad, but as they do the same thing, it might be nice to unify (part) of the interface. In fact this is already done for the in kernel interface with pm_send_all.

John,

Could you please use call_usermodehelper() in this patch rather than exec_usermodehelper()? I want to kill exec_usermodehelper() sometime.

Plus your code will be simpler - no need to create
your own kernel thread.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#2 lore raw
In reply to John Fremlin

Hi!

Quoted 3 lines · Andrew Morton

Could you please use call_usermodehelper() in this patch rather than exec_usermodehelper()? I want to kill exec_usermodehelper() sometime.

The reason I used exec_usermodehelper is that I wanted to waitpid on the process to see how it exited. Am I still allowed to do that if it runs as a child of keventd?

[...]

Signature · 5 lines
	http://www.penguinpowered.com/~vii
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/
#3 lore raw
In reply to John Fremlin
Quoted 5 lines · John Fremlin

Hi!

Andrew Morton <[email protected]> writes:

Quoted 7 lines
Quoted 3 lines

Could you please use call_usermodehelper() in this patch rather than exec_usermodehelper()? I want to kill exec_usermodehelper() sometime.

The reason I used exec_usermodehelper is that I wanted to waitpid on the process to see how it exited. Am I still allowed to do that if it runs as a child of keventd?

Oh foo. I missed that.

In the patch-which-didn't-make-it, yes, it can be called synchronously. Or you can be called back with the exit code when the subprocess exits. It does all the waitpid stuff, the signal management, handles chrootedness, etc. But that's vapourware now.

In the current implementation of call_usermodehelper(), it looks like the commentary is incorrect - it returns a negative error code or the subprocess's pid, but you can't wait on that because it's parented by keventd.

Sorry for the noise - stick with what you have now.

-
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/