Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fbcon: Use delayed work for cursor

Allows us to delete a bunch of hand-rolled stuff using a timer plus a
separate work). Also to simplify the code we initialize the
cursor_work completely when we allocate the fbcon_ops structure,
instead of trying to cope with console re-initialization.

The motiviation here is that fbcon code stops using the fb_info.queue,
which helps with locking issues around cleanup and all that in a later
patch.

Also note that this allows us to ditch the hand-rolled work cleanup in
fbcon_exit - we already call fbcon_del_cursor_timer, which takes care
of everything. Plus this was racy anyway.

v2:
- Only INIT_DELAYED_WORK when kzalloc succeeded (Tetsuo)
- Explain that we replace both the timer and a work with the combined
delayed_work (Javier)

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Cc: Daniel Vetter <daniel@ffwll.ch>
Cc: Claudio Suarez <cssk@net-c.es>
Cc: Du Cheng <ducheng2@gmail.com>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Link: https://patchwork.freedesktop.org/patch/msgid/20220405210335.3434130-7-daniel.vetter@ffwll.ch

+36 -55
+34 -53
drivers/video/fbdev/core/fbcon.c
··· 350 350 351 351 static void fb_flashcursor(struct work_struct *work) 352 352 { 353 - struct fb_info *info = container_of(work, struct fb_info, queue); 354 - struct fbcon_ops *ops = info->fbcon_par; 353 + struct fbcon_ops *ops = container_of(work, struct fbcon_ops, cursor_work.work); 354 + struct fb_info *info; 355 355 struct vc_data *vc = NULL; 356 356 int c; 357 357 int mode; ··· 364 364 if (ret == 0) 365 365 return; 366 366 367 - if (ops && ops->currcon != -1) 367 + /* protected by console_lock */ 368 + info = ops->info; 369 + 370 + if (ops->currcon != -1) 368 371 vc = vc_cons[ops->currcon].d; 369 372 370 373 if (!vc || !con_is_visible(vc) || ··· 383 380 ops->cursor(vc, info, mode, get_color(vc, info, c, 1), 384 381 get_color(vc, info, c, 0)); 385 382 console_unlock(); 383 + 384 + queue_delayed_work(system_power_efficient_wq, &ops->cursor_work, 385 + ops->cur_blink_jiffies); 386 386 } 387 387 388 - static void cursor_timer_handler(struct timer_list *t) 389 - { 390 - struct fbcon_ops *ops = from_timer(ops, t, cursor_timer); 391 - struct fb_info *info = ops->info; 392 - 393 - queue_work(system_power_efficient_wq, &info->queue); 394 - mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies); 395 - } 396 - 397 - static void fbcon_add_cursor_timer(struct fb_info *info) 388 + static void fbcon_add_cursor_work(struct fb_info *info) 398 389 { 399 390 struct fbcon_ops *ops = info->fbcon_par; 400 391 401 - if ((!info->queue.func || info->queue.func == fb_flashcursor) && 402 - !(ops->flags & FBCON_FLAGS_CURSOR_TIMER) && 403 - !fbcon_cursor_noblink) { 404 - if (!info->queue.func) 405 - INIT_WORK(&info->queue, fb_flashcursor); 406 - 407 - timer_setup(&ops->cursor_timer, cursor_timer_handler, 0); 408 - mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies); 409 - ops->flags |= FBCON_FLAGS_CURSOR_TIMER; 410 - } 392 + if (!fbcon_cursor_noblink) 393 + queue_delayed_work(system_power_efficient_wq, &ops->cursor_work, 394 + ops->cur_blink_jiffies); 411 395 } 412 396 413 - static void fbcon_del_cursor_timer(struct fb_info *info) 397 + static void fbcon_del_cursor_work(struct fb_info *info) 414 398 { 415 399 struct fbcon_ops *ops = info->fbcon_par; 416 400 417 - if (info->queue.func == fb_flashcursor && 418 - ops->flags & FBCON_FLAGS_CURSOR_TIMER) { 419 - del_timer_sync(&ops->cursor_timer); 420 - ops->flags &= ~FBCON_FLAGS_CURSOR_TIMER; 421 - } 401 + cancel_delayed_work_sync(&ops->cursor_work); 422 402 } 423 403 424 404 #ifndef MODULE ··· 703 717 } 704 718 705 719 if (!err) { 720 + INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor); 721 + 706 722 ops->cur_blink_jiffies = HZ / 5; 707 723 ops->info = info; 708 724 info->fbcon_par = ops; ··· 739 751 } 740 752 741 753 if (!err) { 742 - fbcon_del_cursor_timer(oldinfo); 754 + fbcon_del_cursor_work(oldinfo); 743 755 kfree(ops->cursor_state.mask); 744 756 kfree(ops->cursor_data); 745 757 kfree(ops->cursor_src); ··· 855 867 logo_shown != FBCON_LOGO_DONTSHOW); 856 868 857 869 if (!found) 858 - fbcon_add_cursor_timer(info); 870 + fbcon_add_cursor_work(info); 859 871 con2fb_map_boot[unit] = newidx; 860 872 con2fb_init_display(vc, info, unit, show_logo); 861 873 } ··· 952 964 return NULL; 953 965 } 954 966 967 + INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor); 968 + 955 969 ops->currcon = -1; 956 970 ops->graphics = 1; 957 971 ops->cur_rotate = -1; ··· 996 1006 info->var.yres, 997 1007 info->var.bits_per_pixel); 998 1008 999 - fbcon_add_cursor_timer(info); 1009 + fbcon_add_cursor_work(info); 1000 1010 return display_desc; 1001 1011 } 1002 1012 ··· 1184 1194 goto finished; 1185 1195 1186 1196 if (con_is_visible(vc)) 1187 - fbcon_del_cursor_timer(info); 1197 + fbcon_del_cursor_work(info); 1188 1198 1189 1199 ops->flags &= ~FBCON_FLAGS_INIT; 1190 1200 finished: ··· 1310 1320 return; 1311 1321 1312 1322 if (vc->vc_cursor_type & CUR_SW) 1313 - fbcon_del_cursor_timer(info); 1323 + fbcon_del_cursor_work(info); 1314 1324 else 1315 - fbcon_add_cursor_timer(info); 1325 + fbcon_add_cursor_work(info); 1316 1326 1317 1327 ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; 1318 1328 ··· 2122 2132 } 2123 2133 2124 2134 if (old_info != info) 2125 - fbcon_del_cursor_timer(old_info); 2135 + fbcon_del_cursor_work(old_info); 2126 2136 } 2127 2137 2128 2138 if (fbcon_is_inactive(vc, info) || 2129 2139 ops->blank_state != FB_BLANK_UNBLANK) 2130 - fbcon_del_cursor_timer(info); 2140 + fbcon_del_cursor_work(info); 2131 2141 else 2132 - fbcon_add_cursor_timer(info); 2142 + fbcon_add_cursor_work(info); 2133 2143 2134 2144 set_blitting_type(vc, info); 2135 2145 ops->cursor_reset = 1; ··· 2237 2247 2238 2248 if (mode_switch || fbcon_is_inactive(vc, info) || 2239 2249 ops->blank_state != FB_BLANK_UNBLANK) 2240 - fbcon_del_cursor_timer(info); 2250 + fbcon_del_cursor_work(info); 2241 2251 else 2242 - fbcon_add_cursor_timer(info); 2252 + fbcon_add_cursor_work(info); 2243 2253 2244 2254 return 0; 2245 2255 } ··· 3171 3181 if (!ops) 3172 3182 goto err; 3173 3183 3174 - blink = (ops->flags & FBCON_FLAGS_CURSOR_TIMER) ? 1 : 0; 3184 + blink = delayed_work_pending(&ops->cursor_work); 3175 3185 err: 3176 3186 console_unlock(); 3177 3187 return snprintf(buf, PAGE_SIZE, "%d\n", blink); ··· 3200 3210 3201 3211 if (blink) { 3202 3212 fbcon_cursor_noblink = 0; 3203 - fbcon_add_cursor_timer(info); 3213 + fbcon_add_cursor_work(info); 3204 3214 } else { 3205 3215 fbcon_cursor_noblink = 1; 3206 - fbcon_del_cursor_timer(info); 3216 + fbcon_del_cursor_work(info); 3207 3217 } 3208 3218 3209 3219 err: ··· 3304 3314 #endif 3305 3315 3306 3316 for_each_registered_fb(i) { 3307 - int pending = 0; 3308 - 3309 3317 mapped = 0; 3310 3318 info = registered_fb[i]; 3311 - 3312 - if (info->queue.func) 3313 - pending = cancel_work_sync(&info->queue); 3314 - pr_debug("fbcon: %s pending work\n", (pending ? "canceled" : "no")); 3315 3319 3316 3320 for (j = first_fb_vc; j <= last_fb_vc; j++) { 3317 3321 if (con2fb_map[j] == i) { ··· 3322 3338 if (info->fbcon_par) { 3323 3339 struct fbcon_ops *ops = info->fbcon_par; 3324 3340 3325 - fbcon_del_cursor_timer(info); 3341 + fbcon_del_cursor_work(info); 3326 3342 kfree(ops->cursor_src); 3327 3343 kfree(ops->cursor_state.mask); 3328 3344 kfree(info->fbcon_par); 3329 3345 info->fbcon_par = NULL; 3330 3346 } 3331 - 3332 - if (info->queue.func == fb_flashcursor) 3333 - info->queue.func = NULL; 3334 3347 } 3335 3348 } 3336 3349 }
+2 -2
drivers/video/fbdev/core/fbcon.h
··· 14 14 #include <linux/types.h> 15 15 #include <linux/vt_buffer.h> 16 16 #include <linux/vt_kern.h> 17 + #include <linux/workqueue.h> 17 18 18 19 #include <asm/io.h> 19 20 20 21 #define FBCON_FLAGS_INIT 1 21 - #define FBCON_FLAGS_CURSOR_TIMER 2 22 22 23 23 /* 24 24 * This is the interface between the low-level console driver and the ··· 68 68 int (*update_start)(struct fb_info *info); 69 69 int (*rotate_font)(struct fb_info *info, struct vc_data *vc); 70 70 struct fb_var_screeninfo var; /* copy of the current fb_var_screeninfo */ 71 - struct timer_list cursor_timer; /* Cursor timer */ 71 + struct delayed_work cursor_work; /* Cursor timer */ 72 72 struct fb_cursor cursor_state; 73 73 struct fbcon_display *p; 74 74 struct fb_info *info;