Merge lp:~kdub/powerd/fix-1258655 into lp:powerd

Proposed by Kevin DuBois
Status: Rejected
Rejected by: Kevin DuBois
Proposed branch: lp:~kdub/powerd/fix-1258655
Merge into: lp:powerd
Diff against target: 227 lines (+72/-47)
1 file modified
src/display.c (+72/-47)
To merge this branch: bzr merge lp:~kdub/powerd/fix-1258655
Reviewer Review Type Date Requested Status
Seth Forshee (community) Disapprove
PS Jenkins bot continuous-integration Approve
Review via email: mp+199582@code.launchpad.net

Commit message

1) eliminating fb_state as a global variable and locking actual_display_state properly.
2) for earlysuspend devices, the responsibility for changing state and turning on the display/compositor via dbus is in the thread-monitoring state. for non-earlysuspend devices, that responsibility remains in the same thread that it is in now.

fixes ( LP: #1258655 )

Description of the change

powerd must ensure that the system is in a usable state before triggering the calls to the display server.

The problem was that for earlysuspend devices, there were two threads that thought they had responsibility for turning the display on, when really only the thread monitoring the files in /sys/power had the right to do so. Furthermore, the variables concerned with display state "actual_display_state" and "fb_state" were accessed by both the file-monitoring thread and the the request-servicing thread, causing races around the actual state of the display.

This MP fixes these issues by:
1) eliminating fb_state as a global variable and locking actual_display_state properly.
2) for earlysuspend devices, the responsibility for changing state and turning on the display/compositor via dbus is in the thread-monitoring state. for non-earlysuspend devices, that responsibility remains in the same thread that it is in now.

fixes ( LP: #1258655 )

Also, please feel free to test by jamming on the power button repeatedly and quickly. I have manually tested on my nexus 4 (an early suspend device) and my nexus 10 (a non-early suspend device).

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Seth Forshee (sforshee) wrote :
Download full text (9.7 KiB)

I don't think this actually "fixes" anything, I think it probably just
shuffles timing around and ends up somehow hiding the problem.

On Wed, Dec 18, 2013 at 10:40:39PM -0000, Kevin DuBois wrote:
> Kevin DuBois has proposed merging lp:~kdub/powerd/fix-1258655 into lp:powerd.
>
> Commit message:
> 1) eliminating fb_state as a global variable and locking actual_display_state properly.

Previously all updates to actual_display_state happened on the main
loop. There were a couple of reads off the main loop related to power
button handling, but adding a lock doesn't eliminate any class of races
(i.e. the state changes just after the read). So actual_screen_state
doesn't *need* any locking until you start manipulating it from
wait_for_fb_thread(). Even then I find most of the locking to be
dubious, more on that later.

> powerd must ensure that the system is in a usable state before
> triggering the calls to the display server. The problem was that
> for earlysuspend devices, there were two threads that thought they had
> responsibility for turning the display on, when really only the thread
> monitoring the files in /sys/power had the right to do so. Furthermore,
> the variables concerned with display state "actual_display_state" and
> "fb_state" were accessed by both the file-monitoring thread and the the
> request-servicing thread, causing races around the actual state of the
> display.

Previously only the main thread ever turned the display on/off; the
monitoring thready only scheduled work on the main thread to do so. The
monitoring thread never checks actual_screen_state, only the work which
is scheduled on the main thread does this. Therefore there is no race in
accessing actual_screen_state, since it's always happening on the main
thread.

This change actually reduces serialization. Previously changes to the
display state and to the system power state were serialized relative to
each other by nature of both happening on the main thread. It _should_
be impossible to have earlysuspend enabled while the display was on due
the following:

 * Before the display was turned on update_display_state() requested the
   active state, which would synchronously disable earlysuspend before
   proceeding to turn on the display.
 * The display was turned off before releasing the active state request.
   Releasing this request may cause the system to be put into
   suspend/earlysuspend mode.
 * All of this happens on the main thread and is thus serialized

These changes move the display on/off to another thread, so the
guarantees of the display state being serialized wrt the system power
state are broken. There *are* races between turning the screen on on the
monitor thread and turning it off on the main thread with your changes.
Expanding the amount of code serialized by the mutex in the screen off
case could help some, but I'm not sure that it will ever be entirely
sufficient.

My guess would be that the actual problem is that unity's
setScreenPowerMode() doesn't complete synchronously wrt what powerd is
doing. So something like this might happen:

 * powerd takes an active state request to guarantee that earlysuspend
   is disabled, then calls setScre...

Read more...

Revision history for this message
Seth Forshee (sforshee) wrote :

Disapproving as per the comments I sent via email. I'm not convinced that the premise behind these changes is correct, but I'm open to being convinced otherwise. In any case I'm almost certain that the changes will introduce regressions.

review: Disapprove
Revision history for this message
Seth Forshee (sforshee) wrote :

I've been going back over the code, and as long as the dbus calls are handled on the main thread (which was my understanding, but I'm no expert about dbus) then all display on/off code should happen on the main thread. As far as I can tell there are adequate protections to prevent turning on the display when earlysuspend is enabled. I really don't think there are any problems there. In particular the display code holds an active request when unity's setScreenPowerMode() is called, and it's called on the main loop, so there should be absolutely no way that earlysuspend is active when it is called and there should be absolutely no way for earlysuspend to become active until the dbus call returns (and setScreenPowerMode() *is* called synchronously).

I did just remember though bug #1208433, which may be related. I think rsalveti and I chatted about that one once and we both thought that the root cause was probably something in the display driver. maguro and grouper are both earlysuspend devices and neither have the same problem with rapidly pressing the power key. Are you able to try either of those with unity to verify that's still the case?

Revision history for this message
Kevin DuBois (kdub) wrote :

unity-mir gets two on's from one button press, one from http://bazaar.launchpad.net/~phablet-team/powerd/trunk/view/head:/src/display.c#L415 and one from http://bazaar.launchpad.net/~phablet-team/powerd/trunk/view/head:/src/display.c#L263. The two on's don't happen everytime, they happen like races.

This isn't a dbus race, that code all checked out (best I can tell). Its that two requests are actually sent out in response to a button press. Sometimes the FB is not ready when one of these 'on' requests happens.

From mir's perspective, all the errors emanate from the FB device not being able to post/blank/etc. Best I can tell, the FB is sometimes not available from a power perspective when one of the on calls comes.

my theory about this problem goes something like this:

power button press
update_display_state()
request_sys_state_internal()
   (request to come out of suspend)
display_set_power_mode()
                                   fb comes out of suspend
                                   fb monitor thread calls update_display_state()

I can check the other devices

Revision history for this message
Seth Forshee (sforshee) wrote :

On Thu, Dec 19, 2013 at 05:54:59PM -0000, Kevin DuBois wrote:
> unity-mir gets two on's from one button press, one from http://bazaar.launchpad.net/~phablet-team/powerd/trunk/view/head:/src/display.c#L415 and one from http://bazaar.launchpad.net/~phablet-team/powerd/trunk/view/head:/src/display.c#L263. The two on's don't happen everytime, they happen like races.
>
> This isn't a dbus race, that code all checked out (best I can tell). Its that two requests are actually sent out in response to a button press. Sometimes the FB is not ready when one of these 'on' requests happens.
>
> >From mir's perspective, all the errors emanate from the FB device not being able to post/blank/etc. Best I can tell, the FB is sometimes not available from a power perspective when one of the on calls comes.

With reference to my comments on the bug, I suspect the first call comes
before the earlysuspend gets around to suspending the framebuffer, and
the second comes when it has been suspended and then resumed. It's a
race with the earlysuspend code in the kernel, which turns out to be
asynchronous with writing to /sys/power/state.

Revision history for this message
Kevin DuBois (kdub) wrote :

Seth came up with another fix in https://code.launchpad.net/~sforshee/powerd/fix-display-timing/+merge/200431
so moving to rejected.

Unmerged revisions

108. By Kevin DuBois

merge base

107. By Kevin DuBois

further cleanup, make sure non-early suspend devices still work

106. By Kevin DuBois

bit of cleanup in fb thread

105. By Kevin DuBois

change state handling to make more robust display interaction

104. By Kevin DuBois

add some locking to the variables accessed in both therads

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/display.c'
--- src/display.c 2013-10-10 15:35:12 +0000
+++ src/display.c 2013-12-18 22:40:08 +0000
@@ -54,6 +54,7 @@
54static gint target_brightness;54static gint target_brightness;
55static gint dim_brightness;55static gint dim_brightness;
56static gboolean running_mir = FALSE;56static gboolean running_mir = FALSE;
57static gboolean earlysuspend_used = FALSE;
5758
58/* Assume screen is off to start; we will force it on */59/* Assume screen is off to start; we will force it on */
59static uuid_t internal_request_cookie;60static uuid_t internal_request_cookie;
@@ -64,6 +65,8 @@
64 .flags = 0,65 .flags = 0,
65};66};
6667
68GMutex actual_screen_state_guard;
69
67/*70/*
68 * When using the proximity sensor, our actual state of the screen71 * When using the proximity sensor, our actual state of the screen
69 * may not match the requested state in internal_state. Therefore72 * may not match the requested state in internal_state. Therefore
@@ -77,19 +80,6 @@
77 */80 */
78static unsigned screen_off_overrides;81static unsigned screen_off_overrides;
7982
80enum fb_state {
81 FB_SLEEP,
82 FB_AWAKE,
83
84 NUM_FB_STATES
85};
86static enum fb_state fb_state = FB_AWAKE;
87
88static const char *fb_file_names[] = {
89 [FB_SLEEP] = "/sys/power/wait_for_fb_sleep",
90 [FB_AWAKE] = "/sys/power/wait_for_fb_wake"
91};
92
93static const char *display_state_strs[POWERD_NUM_DISPLAY_STATES] = {83static const char *display_state_strs[POWERD_NUM_DISPLAY_STATES] = {
94 [POWERD_DISPLAY_STATE_DONT_CARE] = "off",84 [POWERD_DISPLAY_STATE_DONT_CARE] = "off",
95 [POWERD_DISPLAY_STATE_ON] = "on",85 [POWERD_DISPLAY_STATE_ON] = "on",
@@ -107,6 +97,10 @@
107 return (flags & AB_ENABLED_MASK) == AB_ENABLED;97 return (flags & AB_ENABLED_MASK) == AB_ENABLED;
108}98}
10999
100
101/* the Display Server (mir or sf) is free to fire off a few more frames as
102 part of its shutdown process. powerd must guarantee the fb_state is FB_AWAKE,
103 and the system is not suspended until the request returns. */
110gboolean display_set_power_mode(int display, char *power_mode)104gboolean display_set_power_mode(int display, char *power_mode)
111{105{
112 GError *error = NULL;106 GError *error = NULL;
@@ -170,7 +164,10 @@
170164
171gboolean powerd_display_enabled(void)165gboolean powerd_display_enabled(void)
172{166{
173 return actual_screen_state == POWERD_DISPLAY_STATE_ON;167 g_mutex_lock(&actual_screen_state_guard);
168 gboolean ret = (actual_screen_state == POWERD_DISPLAY_STATE_ON);
169 g_mutex_unlock(&actual_screen_state_guard);
170 return ret;
174}171}
175172
176static int get_target_brightness(gboolean request_bright, int hw_value)173static int get_target_brightness(gboolean request_bright, int hw_value)
@@ -221,8 +218,8 @@
221218
222 switch (applied_state) {219 switch (applied_state) {
223 case DISPLAY_STATE_OFF:220 case DISPLAY_STATE_OFF:
224 if (actual_screen_state == DISPLAY_STATE_OFF) {221 if (!powerd_display_enabled()) {
225 /* Nothing to do */222 /* Nothing to do, display is alread off */
226 return;223 return;
227 }224 }
228225
@@ -230,11 +227,19 @@
230 if (using_ab)227 if (using_ab)
231 powerd_autobrightness_disable();228 powerd_autobrightness_disable();
232 powerd_set_brightness(0);229 powerd_set_brightness(0);
230
233 if (!display_set_power_mode(0, "off")) {231 if (!display_set_power_mode(0, "off")) {
234 powerd_warn("failed to set display power mode, not clearing state");232 powerd_warn("failed to set display power mode, not clearing state");
235 return;233 return;
236 }234 }
237235
236 if (!earlysuspend_used)
237 {
238 g_mutex_lock(&actual_screen_state_guard);
239 actual_screen_state = DISPLAY_STATE_OFF;
240 g_mutex_unlock(&actual_screen_state_guard);
241 }
242
238 powerd_debug("Releasing internal active state request");243 powerd_debug("Releasing internal active state request");
239 ret = clear_sys_state_internal(internal_request_cookie);244 ret = clear_sys_state_internal(internal_request_cookie);
240 if (!ret) {245 if (!ret) {
@@ -245,7 +250,8 @@
245 }250 }
246 break;251 break;
247 case POWERD_DISPLAY_STATE_ON:252 case POWERD_DISPLAY_STATE_ON:
248 if (actual_screen_state != POWERD_DISPLAY_STATE_ON) {253 if (!powerd_display_enabled()) {
254 /* display is off, must turn display on */
249 powerd_debug("Requesting active state internally");255 powerd_debug("Requesting active state internally");
250 ret = request_sys_state_internal("display-request",256 ret = request_sys_state_internal("display-request",
251 POWERD_SYS_STATE_ACTIVE,257 POWERD_SYS_STATE_ACTIVE,
@@ -253,16 +259,18 @@
253 if (!ret)259 if (!ret)
254 powerd_warn("Request for active state failed");260 powerd_warn("Request for active state failed");
255261
256 /*262 /* if the device uses earlysuspend, responsibility for turning
257 * If the current state is suspend we need to wait for263 the display server and changing actual_screen_state falls
258 * notification of leaving the active state, otherwise the264 to wait_for_fb_thread() */
259 * screen may fail to turn on for devices using earlysuspend.265 if (!earlysuspend_used)
260 * Otherwise we can turn on the screen right away.266 {
261 */
262 if (fb_state == FB_AWAKE)
263 turn_on_display(use_ab);267 turn_on_display(use_ab);
268 g_mutex_lock(&actual_screen_state_guard);
269 actual_screen_state = POWERD_DISPLAY_STATE_ON;
270 g_mutex_unlock(&actual_screen_state_guard);
271 }
264 } else {272 } else {
265 /* Only changing brightness */273 /* display is already on, must only changing brightness */
266 if (use_ab) {274 if (use_ab) {
267 if (!using_ab)275 if (!using_ab)
268 powerd_autobrightness_enable();276 powerd_autobrightness_enable();
@@ -281,8 +289,6 @@
281 powerd_warn("Invalid display state %d", applied_state);289 powerd_warn("Invalid display state %d", applied_state);
282 return;290 return;
283 }291 }
284
285 actual_screen_state = applied_state;
286}292}
287293
288static void update_flags(guint32 flags)294static void update_flags(guint32 flags)
@@ -389,7 +395,6 @@
389 powerd_run_mainloop_sync(apply_override_worker, &ovr_data);395 powerd_run_mainloop_sync(apply_override_worker, &ovr_data);
390}396}
391397
392
393static int wait_for_file(const char *fname)398static int wait_for_file(const char *fname)
394{399{
395 int fd, ret;400 int fd, ret;
@@ -407,31 +412,43 @@
407 return ret == -1 ? -errno : 0;412 return ret == -1 ? -errno : 0;
408}413}
409414
410static gboolean fb_state_changed(gpointer data)415enum fb_state {
411{416 FB_SLEEP,
412 fb_state = (enum fb_state)data;417 FB_AWAKE,
413 powerd_debug("fb state %s", fb_state == FB_SLEEP ? "sleep" : "awake");418
414 if (fb_state == FB_AWAKE && powerd_display_enabled())419 NUM_FB_STATES
415 turn_on_display(ab_enabled(internal_state.flags));420};
416 return FALSE;421
417}422static const char *fb_file_names[] = {
423 [FB_SLEEP] = "/sys/power/wait_for_fb_sleep",
424 [FB_AWAKE] = "/sys/power/wait_for_fb_wake"
425};
418426
419static gpointer wait_for_fb_thread(gpointer unused)427static gpointer wait_for_fb_thread(gpointer unused)
420{428{
421 unsigned long state = fb_state;429 enum fb_state fb_state = FB_AWAKE;
422
423 if (state >= NUM_FB_STATES) {
424 powerd_warn("Invalid FB state");
425 return NULL;
426 }
427
428 while (1) {430 while (1) {
429 state = (state == FB_SLEEP) ? FB_AWAKE : FB_SLEEP;431 enum fb_state next_state = (fb_state == FB_SLEEP) ? FB_AWAKE : FB_SLEEP;
430 if (wait_for_file(fb_file_names[state])) {432
431 powerd_warn("Error reading %s", fb_file_names[state]);433 if (wait_for_file(fb_file_names[next_state])) {
434 powerd_warn("Error reading %s", fb_file_names[next_state]);
432 return NULL;435 return NULL;
433 }436 }
434 g_timeout_add(0, fb_state_changed, (gpointer)state);437
438 fb_state = next_state;
439 powerd_debug("fb state %s", fb_state == FB_SLEEP ? "sleep" : "awake");
440
441 g_mutex_lock(&actual_screen_state_guard);
442 if (fb_state == FB_AWAKE)
443 {
444 turn_on_display(ab_enabled(internal_state.flags));
445 actual_screen_state = POWERD_DISPLAY_STATE_ON;
446 }
447 else if (fb_state == FB_SLEEP)
448 {
449 actual_screen_state = DISPLAY_STATE_OFF;
450 }
451 g_mutex_unlock(&actual_screen_state_guard);
435 }452 }
436 return NULL;453 return NULL;
437}454}
@@ -489,7 +506,15 @@
489506
490 /* If kernel supports earlysuspend, start thread to monitor fb state */507 /* If kernel supports earlysuspend, start thread to monitor fb state */
491 if (!access(fb_file_names[FB_SLEEP], F_OK))508 if (!access(fb_file_names[FB_SLEEP], F_OK))
509 {
510 powerd_debug("device uses earlysuspend");
511 earlysuspend_used = TRUE;
492 g_thread_new("powerd_fb_wake_monitor", wait_for_fb_thread, &error);512 g_thread_new("powerd_fb_wake_monitor", wait_for_fb_thread, &error);
513 } else
514 {
515 powerd_debug("device does not use earlysuspend");
516 earlysuspend_used = FALSE;
517 }
493518
494 return 0;519 return 0;
495}520}

Subscribers

People subscribed via source and target branches