Skip to content

Commit dc5bdb6

Browse files
committed
drm/fb-helper: Fix vt restore
In the past we had a pile of hacks to orchestrate access between fbdev emulation and native kms clients. We've tried to streamline this, by always preferring the kms side above fbdev calls when a drm master exists, because drm master controls access to the display resources. Unfortunately this breaks existing userspace, specifically Xorg. When exiting Xorg first restores the console to text mode using the KDSET ioctl on the vt. This does nothing, because a drm master is still around. Then it drops the drm master status, which again does nothing, because logind is keeping additional drm fd open to be able to orchestrate vt switches. In the past this is the point where fbdev was restored, as part of the ->lastclose hook on the drm side. Now to fix this regression we don't want to go back to letting fbdev restore things whenever it feels like, or to the pile of hacks we've had before. Instead try and go with a minimal exception to make the KDSET case work again, and nothing else. This means that if userspace does a KDSET call when switching between graphical compositors, there will be some flickering with fbcon showing up for a bit. But a) that's not a regression and b) userspace can fix it by improving the vt switching dance - logind should have all the information it needs. While pondering all this I'm also wondering wheter we should have a SWITCH_MASTER ioctl to allow race-free master status handover. But that's for another day. v2: Somehow forgot to cc all the fbdev people. v3: Fix typo Alex spotted. Reviewed-by: Alex Deucher <[email protected]> Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=208179 Cc: [email protected] Reported-and-Tested-by: [email protected] Cc: Michel Dänzer <[email protected]> Fixes: 64914da ("drm/fbdev-helper: don't force restores") Cc: Noralf Trønnes <[email protected]> Cc: Thomas Zimmermann <[email protected]> Cc: Daniel Vetter <[email protected]> Cc: Maarten Lankhorst <[email protected]> Cc: Maxime Ripard <[email protected]> Cc: David Airlie <[email protected]> Cc: Daniel Vetter <[email protected]> Cc: [email protected] Cc: <[email protected]> # v5.7+ Cc: Bartlomiej Zolnierkiewicz <[email protected]> Cc: Geert Uytterhoeven <[email protected]> Cc: Nathan Chancellor <[email protected]> Cc: Qiujun Huang <[email protected]> Cc: Peter Rosin <[email protected]> Cc: [email protected] Signed-off-by: Daniel Vetter <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 842ec61 commit dc5bdb6

File tree

3 files changed

+52
-15
lines changed

3 files changed

+52
-15
lines changed

drivers/gpu/drm/drm_fb_helper.c

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -227,18 +227,9 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
227227
}
228228
EXPORT_SYMBOL(drm_fb_helper_debug_leave);
229229

230-
/**
231-
* drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
232-
* @fb_helper: driver-allocated fbdev helper, can be NULL
233-
*
234-
* This should be called from driver's drm &drm_driver.lastclose callback
235-
* when implementing an fbcon on top of kms using this helper. This ensures that
236-
* the user isn't greeted with a black screen when e.g. X dies.
237-
*
238-
* RETURNS:
239-
* Zero if everything went ok, negative error code otherwise.
240-
*/
241-
int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
230+
static int
231+
__drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper,
232+
bool force)
242233
{
243234
bool do_delayed;
244235
int ret;
@@ -250,7 +241,16 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
250241
return 0;
251242

252243
mutex_lock(&fb_helper->lock);
253-
ret = drm_client_modeset_commit(&fb_helper->client);
244+
if (force) {
245+
/*
246+
* Yes this is the _locked version which expects the master lock
247+
* to be held. But for forced restores we're intentionally
248+
* racing here, see drm_fb_helper_set_par().
249+
*/
250+
ret = drm_client_modeset_commit_locked(&fb_helper->client);
251+
} else {
252+
ret = drm_client_modeset_commit(&fb_helper->client);
253+
}
254254

255255
do_delayed = fb_helper->delayed_hotplug;
256256
if (do_delayed)
@@ -262,6 +262,22 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
262262

263263
return ret;
264264
}
265+
266+
/**
267+
* drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
268+
* @fb_helper: driver-allocated fbdev helper, can be NULL
269+
*
270+
* This should be called from driver's drm &drm_driver.lastclose callback
271+
* when implementing an fbcon on top of kms using this helper. This ensures that
272+
* the user isn't greeted with a black screen when e.g. X dies.
273+
*
274+
* RETURNS:
275+
* Zero if everything went ok, negative error code otherwise.
276+
*/
277+
int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
278+
{
279+
return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, false);
280+
}
265281
EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
266282

267283
#ifdef CONFIG_MAGIC_SYSRQ
@@ -1318,6 +1334,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
13181334
{
13191335
struct drm_fb_helper *fb_helper = info->par;
13201336
struct fb_var_screeninfo *var = &info->var;
1337+
bool force;
13211338

13221339
if (oops_in_progress)
13231340
return -EBUSY;
@@ -1327,7 +1344,25 @@ int drm_fb_helper_set_par(struct fb_info *info)
13271344
return -EINVAL;
13281345
}
13291346

1330-
drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
1347+
/*
1348+
* Normally we want to make sure that a kms master takes precedence over
1349+
* fbdev, to avoid fbdev flickering and occasionally stealing the
1350+
* display status. But Xorg first sets the vt back to text mode using
1351+
* the KDSET IOCTL with KD_TEXT, and only after that drops the master
1352+
* status when exiting.
1353+
*
1354+
* In the past this was caught by drm_fb_helper_lastclose(), but on
1355+
* modern systems where logind always keeps a drm fd open to orchestrate
1356+
* the vt switching, this doesn't work.
1357+
*
1358+
* To not break the userspace ABI we have this special case here, which
1359+
* is only used for the above case. Everything else uses the normal
1360+
* commit function, which ensures that we never steal the display from
1361+
* an active drm master.
1362+
*/
1363+
force = var->activate & FB_ACTIVATE_KD_TEXT;
1364+
1365+
__drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
13311366

13321367
return 0;
13331368
}

drivers/video/fbdev/core/fbcon.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2402,7 +2402,8 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
24022402
ops->graphics = 1;
24032403

24042404
if (!blank) {
2405-
var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
2405+
var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE |
2406+
FB_ACTIVATE_KD_TEXT;
24062407
fb_set_var(info, &var);
24072408
ops->graphics = 0;
24082409
ops->var = info->var;

include/uapi/linux/fb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ struct fb_bitfield {
205205
#define FB_ACTIVATE_ALL 64 /* change all VCs on this fb */
206206
#define FB_ACTIVATE_FORCE 128 /* force apply even when no change*/
207207
#define FB_ACTIVATE_INV_MODE 256 /* invalidate videomode */
208+
#define FB_ACTIVATE_KD_TEXT 512 /* for KDSET vt ioctl */
208209

209210
#define FB_ACCELF_TEXT 1 /* (OBSOLETE) see fb_info.flags and vc_mode */
210211

0 commit comments

Comments
 (0)