[PATCH v3 3/3] console/fbcon: Add support for deferred console takeover

Hans de Goede hdegoede at redhat.com
Thu Jun 28 08:58:42 UTC 2018


Hi,

On 28-06-18 10:37, Daniel Vetter wrote:
> On Thu, Jun 28, 2018 at 10:20 AM, Hans de Goede <hdegoede at redhat.com> wrote:
>> Hi,
>>
>> On 28-06-18 09:55, Daniel Vetter wrote:
>>>
>>> On Tue, Jun 26, 2018 at 08:36:12PM +0200, Hans de Goede wrote:
>>>>
>>>> Currently fbcon claims fbdevs as soon as they are registered and takes
>>>> over
>>>> the console as soon as the first fbdev gets registered.
>>>>
>>>> This behavior is undesirable in cases where a smooth graphical bootup is
>>>> desired, in such cases we typically want the contents of the framebuffer
>>>> (typically a vendor logo) to stay in place as is.
>>>>
>>>> The current solution for this problem (on embedded systems) is to not
>>>> enable fbcon.
>>>>
>>>> This commit adds a new FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER config
>>>> option,
>>>> which when enabled defers fbcon taking over the console from the dummy
>>>> console until the first text is displayed on the console. Together with
>>>> the
>>>> "quiet" kernel commandline option, this allows fbcon to still be used
>>>> together with a smooth graphical bootup, having it take over the console
>>>> as
>>>> soon as e.g. an error message is logged.
>>>>
>>>> Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch>
>>>> Signed-off-by: Hans de Goede <hdegoede at redhat.com>
>>>
>>>
>>> A bit a late comment, and feel free to reject: Have you considered
>>> registering the fbcon driver, but not doing any modesets until the very
>>> first real message shows up?
>>
>>
>> I have tried something like that, but it did not work out, this patch-set
>> actually is my 3th attempt at this (the other 2 were never posted
>> because they did not work).
>>
>> The fbcon code is woven quite tightly into the console code, so this was
>> the only clean way I could find to achieve what I want.
> 
> I assumed/feared as much. Would be good to cover that in the commit
> message, to justify your approach better.

Ok, v5 with an updated commit message (and without any code changes)
coming up.

Regards,

Hans



>>>> ---
>>>> Changes in v2:
>>>> -Check the whole string when checking for erases in putcs, instead of
>>>> just
>>>>    the first char
>>>> -Make dummycon_blank return 1, so that a redraw gets triggered and any
>>>> text
>>>>    rendered while blanked gets output so that it can trigger a deferred
>>>>    takeover if one is pending
>>>>
>>>> Changes in v3:
>>>> -Call WARN_CONSOLE_UNLOCKED() from fbcon_output_notifier()
>>>> -Unregister the notifier on fbcon_exit()
>>>> -Document the fbcon=nodefer commandline option in
>>>> Documentation/fb/fbcon.txt
>>>> ---
>>>>    Documentation/fb/fbcon.txt       |  7 ++++
>>>>    drivers/video/console/Kconfig    | 11 +++++
>>>>    drivers/video/console/dummycon.c | 67 +++++++++++++++++++++++++----
>>>>    drivers/video/fbdev/core/fbcon.c | 72 ++++++++++++++++++++++++++++++++
>>>>    include/linux/console.h          |  5 +++
>>>>    5 files changed, 154 insertions(+), 8 deletions(-)
>>>>
>>>> diff --git a/Documentation/fb/fbcon.txt b/Documentation/fb/fbcon.txt
>>>> index 79c22d096bbc..d4d642e1ce9c 100644
>>>> --- a/Documentation/fb/fbcon.txt
>>>> +++ b/Documentation/fb/fbcon.txt
>>>> @@ -155,6 +155,13 @@ C. Boot options
>>>>          used by text. By default, this area will be black. The 'color'
>>>> value
>>>>          is an integer number that depends on the framebuffer driver being
>>>> used.
>>>>    +6. fbcon=nodefer
>>>> +
>>>> +       If the kernel is compiled with deferred fbcon takeover support,
>>>> normally
>>>> +       the framebuffer contents, left in place by the
>>>> firmware/bootloader, will
>>>> +       be preserved until there actually is some text is output to the
>>>> console.
>>>> +       This option causes fbcon to bind immediately to the fbdev device.
>>>> +
>>>>    C. Attaching, Detaching and Unloading
>>>>      Before going on how to attach, detach and unload the framebuffer
>>>> console, an
>>>> diff --git a/drivers/video/console/Kconfig
>>>> b/drivers/video/console/Kconfig
>>>> index 4110ba7d7ca9..e91edef98633 100644
>>>> --- a/drivers/video/console/Kconfig
>>>> +++ b/drivers/video/console/Kconfig
>>>> @@ -150,6 +150,17 @@ config FRAMEBUFFER_CONSOLE_ROTATION
>>>>             such that other users of the framebuffer will remain normally
>>>>             oriented.
>>>>    +config FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
>>>> +       bool "Framebuffer Console Deferred Takeover"
>>>> +       depends on FRAMEBUFFER_CONSOLE=y && DUMMY_CONSOLE=y
>>>> +       help
>>>> +         If enabled this defers the framebuffer console taking over the
>>>> +         console from the dummy console until the first text is
>>>> displayed on
>>>> +         the console. This is useful in combination with the "quiet"
>>>> kernel
>>>> +         commandline option to keep the framebuffer contents initially
>>>> put up
>>>> +         by the firmware in place, rather then replacing the contents
>>>> with a
>>>> +         black screen as soon as fbcon loads.
>>>> +
>>>>    config STI_CONSOLE
>>>>            bool "STI text console"
>>>>          depends on PARISC && HAS_IOMEM
>>>> diff --git a/drivers/video/console/dummycon.c
>>>> b/drivers/video/console/dummycon.c
>>>> index f2eafe2ed980..45ad925ad5f8 100644
>>>> --- a/drivers/video/console/dummycon.c
>>>> +++ b/drivers/video/console/dummycon.c
>>>> @@ -26,6 +26,65 @@
>>>>    #define DUMMY_ROWS    CONFIG_DUMMY_CONSOLE_ROWS
>>>>    #endif
>>>>    +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
>>>> +/* These are both protected by the console_lock */
>>>> +static RAW_NOTIFIER_HEAD(dummycon_output_nh);
>>>> +static bool dummycon_putc_called;
>>>> +
>>>> +void dummycon_register_output_notifier(struct notifier_block *nb)
>>>> +{
>>>> +       raw_notifier_chain_register(&dummycon_output_nh, nb);
>>>> +
>>>> +       if (dummycon_putc_called)
>>>> +               nb->notifier_call(nb, 0, NULL);
>>>> +}
>>>> +
>>>> +void dummycon_unregister_output_notifier(struct notifier_block *nb)
>>>> +{
>>>> +       raw_notifier_chain_unregister(&dummycon_output_nh, nb);
>>>> +}
>>>> +
>>>> +static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos)
>>>> +{
>>>> +       dummycon_putc_called = true;
>>>> +       raw_notifier_call_chain(&dummycon_output_nh, 0, NULL);
>>>> +}
>>>> +
>>>> +static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
>>>> +                          int count, int ypos, int xpos)
>>>> +{
>>>> +       int i;
>>>> +
>>>> +       if (!dummycon_putc_called) {
>>>> +               /* Ignore erases */
>>>> +               for (i = 0 ; i < count; i++) {
>>>> +                       if (s[i] != vc->vc_video_erase_char)
>>>> +                               break;
>>>> +               }
>>>> +               if (i == count)
>>>> +                       return;
>>>> +
>>>> +               dummycon_putc_called = true;
>>>> +       }
>>>> +
>>>> +       raw_notifier_call_chain(&dummycon_output_nh, 0, NULL);
>>>> +}
>>>> +
>>>> +static int dummycon_blank(struct vc_data *vc, int blank, int
>>>> mode_switch)
>>>> +{
>>>> +       /* Redraw, so that we get putc(s) for output done while blanked
>>>> */
>>>> +       return 1;
>>>> +}
>>>> +#else
>>>> +static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos)
>>>> { }
>>>> +static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
>>>> +                          int count, int ypos, int xpos) { }
>>>> +static int dummycon_blank(struct vc_data *vc, int blank, int
>>>> mode_switch)
>>>> +{
>>>> +       return 0;
>>>> +}
>>>> +#endif
>>>> +
>>>>    static const char *dummycon_startup(void)
>>>>    {
>>>>        return "dummy device";
>>>> @@ -44,9 +103,6 @@ static void dummycon_init(struct vc_data *vc, int
>>>> init)
>>>>    static void dummycon_deinit(struct vc_data *vc) { }
>>>>    static void dummycon_clear(struct vc_data *vc, int sy, int sx, int
>>>> height,
>>>>                             int width) { }
>>>> -static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos)
>>>> { }
>>>> -static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
>>>> -                          int count, int ypos, int xpos) { }
>>>>    static void dummycon_cursor(struct vc_data *vc, int mode) { }
>>>>      static bool dummycon_scroll(struct vc_data *vc, unsigned int top,
>>>> @@ -61,11 +117,6 @@ static int dummycon_switch(struct vc_data *vc)
>>>>          return 0;
>>>>    }
>>>>    -static int dummycon_blank(struct vc_data *vc, int blank, int
>>>> mode_switch)
>>>> -{
>>>> -       return 0;
>>>> -}
>>>> -
>>>>    static int dummycon_font_set(struct vc_data *vc, struct console_font
>>>> *font,
>>>>                               unsigned int flags)
>>>>    {
>>>> diff --git a/drivers/video/fbdev/core/fbcon.c
>>>> b/drivers/video/fbdev/core/fbcon.c
>>>> index cd8d52a967aa..5fb156bdcf4e 100644
>>>> --- a/drivers/video/fbdev/core/fbcon.c
>>>> +++ b/drivers/video/fbdev/core/fbcon.c
>>>> @@ -129,6 +129,12 @@ static inline void fbcon_map_override(void)
>>>>    }
>>>>    #endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */
>>>>    +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
>>>> +static bool deferred_takeover = true;
>>>> +#else
>>>> +#define deferred_takeover false
>>>> +#endif
>>>> +
>>>>    /* font data */
>>>>    static char fontname[40];
>>>>    @@ -499,6 +505,12 @@ static int __init fb_console_setup(char *this_opt)
>>>>                                  margin_color = simple_strtoul(options,
>>>> &options, 0);
>>>>                          continue;
>>>>                  }
>>>> +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
>>>> +               if (!strcmp(options, "nodefer")) {
>>>> +                       deferred_takeover = false;
>>>> +                       continue;
>>>> +               }
>>>> +#endif
>>>>          }
>>>>          return 1;
>>>>    }
>>>> @@ -3100,6 +3112,9 @@ static int fbcon_fb_unregistered(struct fb_info
>>>> *info)
>>>>          WARN_CONSOLE_UNLOCKED();
>>>>    +     if (deferred_takeover)
>>>> +               return 0;
>>>> +
>>>>          idx = info->node;
>>>>          for (i = first_fb_vc; i <= last_fb_vc; i++) {
>>>>                  if (con2fb_map[i] == idx)
>>>> @@ -3140,6 +3155,13 @@ static void fbcon_remap_all(int idx)
>>>>          WARN_CONSOLE_UNLOCKED();
>>>>    +     if (deferred_takeover) {
>>>> +               for (i = first_fb_vc; i <= last_fb_vc; i++)
>>>> +                       con2fb_map_boot[i] = idx;
>>>> +               fbcon_map_override();
>>>> +               return;
>>>> +       }
>>>> +
>>>>          for (i = first_fb_vc; i <= last_fb_vc; i++)
>>>>                  set_con2fb_map(i, idx, 0);
>>>>    @@ -3191,6 +3213,11 @@ static int fbcon_fb_registered(struct fb_info
>>>> *info)
>>>>          idx = info->node;
>>>>          fbcon_select_primary(info);
>>>>    +     if (deferred_takeover) {
>>>> +               pr_info("fbcon: Deferring console take-over\n");
>>>> +               return 0;
>>>> +       }
>>>> +
>>>>          if (info_idx == -1) {
>>>>                  for (i = first_fb_vc; i <= last_fb_vc; i++) {
>>>>                          if (con2fb_map_boot[i] == idx) {
>>>> @@ -3566,8 +3593,46 @@ static int fbcon_init_device(void)
>>>>          return 0;
>>>>    }
>>>>    +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
>>>> +static struct notifier_block fbcon_output_nb;
>>>> +
>>>> +static int fbcon_output_notifier(struct notifier_block *nb,
>>>> +                                unsigned long action, void *data)
>>>> +{
>>>> +       int i;
>>>> +
>>>> +       WARN_CONSOLE_UNLOCKED();
>>>> +
>>>> +       pr_info("fbcon: Taking over console\n");
>>>> +
>>>> +       dummycon_unregister_output_notifier(&fbcon_output_nb);
>>>> +       deferred_takeover = false;
>>>> +       logo_shown = FBCON_LOGO_DONTSHOW;
>>>> +
>>>> +       for (i = 0; i < FB_MAX; i++) {
>>>> +               if (registered_fb[i])
>>>> +                       fbcon_fb_registered(registered_fb[i]);
>>>> +       }
>>>> +
>>>> +       return NOTIFY_OK;
>>>> +}
>>>> +
>>>> +static void fbcon_register_output_notifier(void)
>>>> +{
>>>> +       fbcon_output_nb.notifier_call = fbcon_output_notifier;
>>>> +       dummycon_register_output_notifier(&fbcon_output_nb);
>>>> +}
>>>> +#else
>>>> +static inline void fbcon_register_output_notifier(void) {}
>>>> +#endif
>>>> +
>>>>    static void fbcon_start(void)
>>>>    {
>>>> +       if (deferred_takeover) {
>>>> +               fbcon_register_output_notifier();
>>>> +               return;
>>>> +       }
>>>> +
>>>>          if (num_registered_fb) {
>>>>                  int i;
>>>>    @@ -3594,6 +3659,13 @@ static void fbcon_exit(void)
>>>>          if (fbcon_has_exited)
>>>>                  return;
>>>>    +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
>>>> +       if (deferred_takeover) {
>>>> +               dummycon_unregister_output_notifier(&fbcon_output_nb);
>>>> +               deferred_takeover = false;
>>>> +       }
>>>> +#endif
>>>> +
>>>>          kfree((void *)softback_buf);
>>>>          softback_buf = 0UL;
>>>>    diff --git a/include/linux/console.h b/include/linux/console.h
>>>> index dfd6b0e97855..f59f3dbca65c 100644
>>>> --- a/include/linux/console.h
>>>> +++ b/include/linux/console.h
>>>> @@ -21,6 +21,7 @@ struct console_font_op;
>>>>    struct console_font;
>>>>    struct module;
>>>>    struct tty_struct;
>>>> +struct notifier_block;
>>>>      /*
>>>>     * this is what the terminal answers to a ESC-Z or csi0c query.
>>>> @@ -220,4 +221,8 @@ static inline bool vgacon_text_force(void) { return
>>>> false; }
>>>>      extern void console_init(void);
>>>>    +/* For deferred console takeover */
>>>> +void dummycon_register_output_notifier(struct notifier_block *nb);
>>>> +void dummycon_unregister_output_notifier(struct notifier_block *nb);
>>>> +
>>>>    #endif /* _LINUX_CONSOLE_H */
>>>> --
>>>> 2.17.1
>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel at lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>
>>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
> 
> 


More information about the dri-devel mailing list