Commit d20a4dca47d2cd027ed58a13f91b424affd1f449

Authored by Johannes Berg
Committed by Andi Kleen
1 parent 741438b500

APM emulation: Notify about all suspend events, not just APM invoked ones (v2)

This revamps the apm-emulation code to get suspend notifications
regardless of what way pm_suspend() was invoked, whether via the
apm ioctl or via /sys/power/state. Also do some code cleanup and
add comments while at it.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Len Brown <len.brown@intel.com>
Signed-off-by: Andi Kleen <ak@linux.intel.com>

Showing 1 changed file with 207 additions and 139 deletions Side-by-side Diff

drivers/char/apm-emulation.c
... ... @@ -59,6 +59,55 @@
59 59 };
60 60  
61 61 /*
  62 + * thread states (for threads using a writable /dev/apm_bios fd):
  63 + *
  64 + * SUSPEND_NONE: nothing happening
  65 + * SUSPEND_PENDING: suspend event queued for thread and pending to be read
  66 + * SUSPEND_READ: suspend event read, pending acknowledgement
  67 + * SUSPEND_ACKED: acknowledgement received from thread (via ioctl),
  68 + * waiting for resume
  69 + * SUSPEND_ACKTO: acknowledgement timeout
  70 + * SUSPEND_DONE: thread had acked suspend and is now notified of
  71 + * resume
  72 + *
  73 + * SUSPEND_WAIT: this thread invoked suspend and is waiting for resume
  74 + *
  75 + * A thread migrates in one of three paths:
  76 + * NONE -1-> PENDING -2-> READ -3-> ACKED -4-> DONE -5-> NONE
  77 + * -6-> ACKTO -7-> NONE
  78 + * NONE -8-> WAIT -9-> NONE
  79 + *
  80 + * While in PENDING or READ, the thread is accounted for in the
  81 + * suspend_acks_pending counter.
  82 + *
  83 + * The transitions are invoked as follows:
  84 + * 1: suspend event is signalled from the core PM code
  85 + * 2: the suspend event is read from the fd by the userspace thread
  86 + * 3: userspace thread issues the APM_IOC_SUSPEND ioctl (as ack)
  87 + * 4: core PM code signals that we have resumed
  88 + * 5: APM_IOC_SUSPEND ioctl returns
  89 + *
  90 + * 6: the notifier invoked from the core PM code timed out waiting
  91 + * for all relevant threds to enter ACKED state and puts those
  92 + * that haven't into ACKTO
  93 + * 7: those threads issue APM_IOC_SUSPEND ioctl too late,
  94 + * get an error
  95 + *
  96 + * 8: userspace thread issues the APM_IOC_SUSPEND ioctl (to suspend),
  97 + * ioctl code invokes pm_suspend()
  98 + * 9: pm_suspend() returns indicating resume
  99 + */
  100 +enum apm_suspend_state {
  101 + SUSPEND_NONE,
  102 + SUSPEND_PENDING,
  103 + SUSPEND_READ,
  104 + SUSPEND_ACKED,
  105 + SUSPEND_ACKTO,
  106 + SUSPEND_WAIT,
  107 + SUSPEND_DONE,
  108 +};
  109 +
  110 +/*
62 111 * The per-file APM data
63 112 */
64 113 struct apm_user {
... ... @@ -69,13 +118,7 @@
69 118 unsigned int reader: 1;
70 119  
71 120 int suspend_result;
72   - unsigned int suspend_state;
73   -#define SUSPEND_NONE 0 /* no suspend pending */
74   -#define SUSPEND_PENDING 1 /* suspend pending read */
75   -#define SUSPEND_READ 2 /* suspend read, pending ack */
76   -#define SUSPEND_ACKED 3 /* suspend acked */
77   -#define SUSPEND_WAIT 4 /* waiting for suspend */
78   -#define SUSPEND_DONE 5 /* suspend completed */
  121 + enum apm_suspend_state suspend_state;
79 122  
80 123 struct apm_queue queue;
81 124 };
... ... @@ -83,7 +126,8 @@
83 126 /*
84 127 * Local variables
85 128 */
86   -static int suspends_pending;
  129 +static atomic_t suspend_acks_pending = ATOMIC_INIT(0);
  130 +static atomic_t userspace_notification_inhibit = ATOMIC_INIT(0);
87 131 static int apm_disabled;
88 132 static struct task_struct *kapmd_tsk;
89 133  
... ... @@ -166,78 +210,6 @@
166 210 wake_up_interruptible(&apm_waitqueue);
167 211 }
168 212  
169   -/*
170   - * queue_suspend_event - queue an APM suspend event.
171   - *
172   - * Check that we're in a state where we can suspend. If not,
173   - * return -EBUSY. Otherwise, queue an event to all "writer"
174   - * users. If there are no "writer" users, return '1' to
175   - * indicate that we can immediately suspend.
176   - */
177   -static int queue_suspend_event(apm_event_t event, struct apm_user *sender)
178   -{
179   - struct apm_user *as;
180   - int ret = 1;
181   -
182   - mutex_lock(&state_lock);
183   - down_read(&user_list_lock);
184   -
185   - /*
186   - * If a thread is still processing, we can't suspend, so reject
187   - * the request.
188   - */
189   - list_for_each_entry(as, &apm_user_list, list) {
190   - if (as != sender && as->reader && as->writer && as->suser &&
191   - as->suspend_state != SUSPEND_NONE) {
192   - ret = -EBUSY;
193   - goto out;
194   - }
195   - }
196   -
197   - list_for_each_entry(as, &apm_user_list, list) {
198   - if (as != sender && as->reader && as->writer && as->suser) {
199   - as->suspend_state = SUSPEND_PENDING;
200   - suspends_pending++;
201   - queue_add_event(&as->queue, event);
202   - ret = 0;
203   - }
204   - }
205   - out:
206   - up_read(&user_list_lock);
207   - mutex_unlock(&state_lock);
208   - wake_up_interruptible(&apm_waitqueue);
209   - return ret;
210   -}
211   -
212   -static void apm_suspend(void)
213   -{
214   - struct apm_user *as;
215   - int err = pm_suspend(PM_SUSPEND_MEM);
216   -
217   - /*
218   - * Anyone on the APM queues will think we're still suspended.
219   - * Send a message so everyone knows we're now awake again.
220   - */
221   - queue_event(APM_NORMAL_RESUME);
222   -
223   - /*
224   - * Finally, wake up anyone who is sleeping on the suspend.
225   - */
226   - mutex_lock(&state_lock);
227   - down_read(&user_list_lock);
228   - list_for_each_entry(as, &apm_user_list, list) {
229   - if (as->suspend_state == SUSPEND_WAIT ||
230   - as->suspend_state == SUSPEND_ACKED) {
231   - as->suspend_result = err;
232   - as->suspend_state = SUSPEND_DONE;
233   - }
234   - }
235   - up_read(&user_list_lock);
236   - mutex_unlock(&state_lock);
237   -
238   - wake_up(&apm_suspend_waitqueue);
239   -}
240   -
241 213 static ssize_t apm_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
242 214 {
243 215 struct apm_user *as = fp->private_data;
244 216  
245 217  
246 218  
... ... @@ -308,25 +280,22 @@
308 280  
309 281 as->suspend_result = -EINTR;
310 282  
311   - if (as->suspend_state == SUSPEND_READ) {
312   - int pending;
313   -
  283 + switch (as->suspend_state) {
  284 + case SUSPEND_READ:
314 285 /*
315 286 * If we read a suspend command from /dev/apm_bios,
316 287 * then the corresponding APM_IOC_SUSPEND ioctl is
317 288 * interpreted as an acknowledge.
318 289 */
319 290 as->suspend_state = SUSPEND_ACKED;
320   - suspends_pending--;
321   - pending = suspends_pending == 0;
  291 + atomic_dec(&suspend_acks_pending);
322 292 mutex_unlock(&state_lock);
323 293  
324 294 /*
325   - * If there are no further acknowledges required,
326   - * suspend the system.
  295 + * suspend_acks_pending changed, the notifier needs to
  296 + * be woken up for this
327 297 */
328   - if (pending)
329   - apm_suspend();
  298 + wake_up(&apm_suspend_waitqueue);
330 299  
331 300 /*
332 301 * Wait for the suspend/resume to complete. If there
333 302  
334 303  
... ... @@ -342,35 +311,21 @@
342 311 * try_to_freeze() in freezer_count() will not trigger
343 312 */
344 313 freezer_count();
345   - } else {
  314 + break;
  315 + case SUSPEND_ACKTO:
  316 + as->suspend_result = -ETIMEDOUT;
  317 + mutex_unlock(&state_lock);
  318 + break;
  319 + default:
346 320 as->suspend_state = SUSPEND_WAIT;
347 321 mutex_unlock(&state_lock);
348 322  
349 323 /*
350 324 * Otherwise it is a request to suspend the system.
351   - * Queue an event for all readers, and expect an
352   - * acknowledge from all writers who haven't already
353   - * acknowledged.
  325 + * Just invoke pm_suspend(), we'll handle it from
  326 + * there via the notifier.
354 327 */
355   - err = queue_suspend_event(APM_USER_SUSPEND, as);
356   - if (err < 0) {
357   - /*
358   - * Avoid taking the lock here - this
359   - * should be fine.
360   - */
361   - as->suspend_state = SUSPEND_NONE;
362   - break;
363   - }
364   -
365   - if (err > 0)
366   - apm_suspend();
367   -
368   - /*
369   - * Wait for the suspend/resume to complete. If there
370   - * are pending acknowledges, we wait here for them.
371   - */
372   - wait_event_freezable(apm_suspend_waitqueue,
373   - as->suspend_state == SUSPEND_DONE);
  328 + as->suspend_result = pm_suspend(PM_SUSPEND_MEM);
374 329 }
375 330  
376 331 mutex_lock(&state_lock);
... ... @@ -386,7 +341,6 @@
386 341 static int apm_release(struct inode * inode, struct file * filp)
387 342 {
388 343 struct apm_user *as = filp->private_data;
389   - int pending = 0;
390 344  
391 345 filp->private_data = NULL;
392 346  
393 347  
394 348  
395 349  
... ... @@ -396,19 +350,16 @@
396 350  
397 351 /*
398 352 * We are now unhooked from the chain. As far as new
399   - * events are concerned, we no longer exist. However, we
400   - * need to balance suspends_pending, which means the
401   - * possibility of sleeping.
  353 + * events are concerned, we no longer exist.
402 354 */
403 355 mutex_lock(&state_lock);
404   - if (as->suspend_state != SUSPEND_NONE) {
405   - suspends_pending -= 1;
406   - pending = suspends_pending == 0;
407   - }
  356 + if (as->suspend_state == SUSPEND_PENDING ||
  357 + as->suspend_state == SUSPEND_READ)
  358 + atomic_dec(&suspend_acks_pending);
408 359 mutex_unlock(&state_lock);
409   - if (pending)
410   - apm_suspend();
411 360  
  361 + wake_up(&apm_suspend_waitqueue);
  362 +
412 363 kfree(as);
413 364 return 0;
414 365 }
... ... @@ -545,7 +496,6 @@
545 496 {
546 497 do {
547 498 apm_event_t event;
548   - int ret;
549 499  
550 500 wait_event_interruptible(kapmd_wait,
551 501 !queue_empty(&kapmd_queue) || kthread_should_stop());
552 502  
... ... @@ -570,20 +520,13 @@
570 520  
571 521 case APM_USER_SUSPEND:
572 522 case APM_SYS_SUSPEND:
573   - ret = queue_suspend_event(event, NULL);
574   - if (ret < 0) {
575   - /*
576   - * We were busy. Try again in 50ms.
577   - */
578   - queue_add_event(&kapmd_queue, event);
579   - msleep(50);
580   - }
581   - if (ret > 0)
582   - apm_suspend();
  523 + pm_suspend(PM_SUSPEND_MEM);
583 524 break;
584 525  
585 526 case APM_CRITICAL_SUSPEND:
586   - apm_suspend();
  527 + atomic_inc(&userspace_notification_inhibit);
  528 + pm_suspend(PM_SUSPEND_MEM);
  529 + atomic_dec(&userspace_notification_inhibit);
587 530 break;
588 531 }
589 532 } while (1);
... ... @@ -591,6 +534,120 @@
591 534 return 0;
592 535 }
593 536  
  537 +static int apm_suspend_notifier(struct notifier_block *nb,
  538 + unsigned long event,
  539 + void *dummy)
  540 +{
  541 + struct apm_user *as;
  542 + int err;
  543 +
  544 + /* short-cut emergency suspends */
  545 + if (atomic_read(&userspace_notification_inhibit))
  546 + return NOTIFY_DONE;
  547 +
  548 + switch (event) {
  549 + case PM_SUSPEND_PREPARE:
  550 + /*
  551 + * Queue an event to all "writer" users that we want
  552 + * to suspend and need their ack.
  553 + */
  554 + mutex_lock(&state_lock);
  555 + down_read(&user_list_lock);
  556 +
  557 + list_for_each_entry(as, &apm_user_list, list) {
  558 + if (as->suspend_state != SUSPEND_WAIT && as->reader &&
  559 + as->writer && as->suser) {
  560 + as->suspend_state = SUSPEND_PENDING;
  561 + atomic_inc(&suspend_acks_pending);
  562 + queue_add_event(&as->queue, APM_USER_SUSPEND);
  563 + }
  564 + }
  565 +
  566 + up_read(&user_list_lock);
  567 + mutex_unlock(&state_lock);
  568 + wake_up_interruptible(&apm_waitqueue);
  569 +
  570 + /*
  571 + * Wait for the the suspend_acks_pending variable to drop to
  572 + * zero, meaning everybody acked the suspend event (or the
  573 + * process was killed.)
  574 + *
  575 + * If the app won't answer within a short while we assume it
  576 + * locked up and ignore it.
  577 + */
  578 + err = wait_event_interruptible_timeout(
  579 + apm_suspend_waitqueue,
  580 + atomic_read(&suspend_acks_pending) == 0,
  581 + 5*HZ);
  582 +
  583 + /* timed out */
  584 + if (err == 0) {
  585 + /*
  586 + * Move anybody who timed out to "ack timeout" state.
  587 + *
  588 + * We could time out and the userspace does the ACK
  589 + * right after we time out but before we enter the
  590 + * locked section here, but that's fine.
  591 + */
  592 + mutex_lock(&state_lock);
  593 + down_read(&user_list_lock);
  594 + list_for_each_entry(as, &apm_user_list, list) {
  595 + if (as->suspend_state == SUSPEND_PENDING ||
  596 + as->suspend_state == SUSPEND_READ) {
  597 + as->suspend_state = SUSPEND_ACKTO;
  598 + atomic_dec(&suspend_acks_pending);
  599 + }
  600 + }
  601 + up_read(&user_list_lock);
  602 + mutex_unlock(&state_lock);
  603 + }
  604 +
  605 + /* let suspend proceed */
  606 + if (err >= 0)
  607 + return NOTIFY_OK;
  608 +
  609 + /* interrupted by signal */
  610 + return NOTIFY_BAD;
  611 +
  612 + case PM_POST_SUSPEND:
  613 + /*
  614 + * Anyone on the APM queues will think we're still suspended.
  615 + * Send a message so everyone knows we're now awake again.
  616 + */
  617 + queue_event(APM_NORMAL_RESUME);
  618 +
  619 + /*
  620 + * Finally, wake up anyone who is sleeping on the suspend.
  621 + */
  622 + mutex_lock(&state_lock);
  623 + down_read(&user_list_lock);
  624 + list_for_each_entry(as, &apm_user_list, list) {
  625 + if (as->suspend_state == SUSPEND_ACKED) {
  626 + /*
  627 + * TODO: maybe grab error code, needs core
  628 + * changes to push the error to the notifier
  629 + * chain (could use the second parameter if
  630 + * implemented)
  631 + */
  632 + as->suspend_result = 0;
  633 + as->suspend_state = SUSPEND_DONE;
  634 + }
  635 + }
  636 + up_read(&user_list_lock);
  637 + mutex_unlock(&state_lock);
  638 +
  639 + wake_up(&apm_suspend_waitqueue);
  640 + return NOTIFY_OK;
  641 +
  642 + default:
  643 + return NOTIFY_DONE;
  644 + }
  645 +}
  646 +
  647 +static struct notifier_block apm_notif_block = {
  648 + .notifier_call = apm_suspend_notifier,
  649 +};
  650 +
594 651 static int __init apm_init(void)
595 652 {
596 653 int ret;
... ... @@ -604,7 +661,7 @@
604 661 if (IS_ERR(kapmd_tsk)) {
605 662 ret = PTR_ERR(kapmd_tsk);
606 663 kapmd_tsk = NULL;
607   - return ret;
  664 + goto out;
608 665 }
609 666 wake_up_process(kapmd_tsk);
610 667  
611 668  
612 669  
... ... @@ -613,16 +670,27 @@
613 670 #endif
614 671  
615 672 ret = misc_register(&apm_device);
616   - if (ret != 0) {
617   - remove_proc_entry("apm", NULL);
618   - kthread_stop(kapmd_tsk);
619   - }
  673 + if (ret)
  674 + goto out_stop;
620 675  
  676 + ret = register_pm_notifier(&apm_notif_block);
  677 + if (ret)
  678 + goto out_unregister;
  679 +
  680 + return 0;
  681 +
  682 + out_unregister:
  683 + misc_deregister(&apm_device);
  684 + out_stop:
  685 + remove_proc_entry("apm", NULL);
  686 + kthread_stop(kapmd_tsk);
  687 + out:
621 688 return ret;
622 689 }
623 690  
624 691 static void __exit apm_exit(void)
625 692 {
  693 + unregister_pm_notifier(&apm_notif_block);
626 694 misc_deregister(&apm_device);
627 695 remove_proc_entry("apm", NULL);
628 696