diff mbox series

[v2,01/21] lib/vsprintf: Print time and date in human readable format via %pt

Message ID 20180220214400.66749-2-andriy.shevchenko@linux.intel.com
State Awaiting Upstream
Headers show
Series lib, rtc: Print rtc_time via %ptR[dt][rv] | expand

Commit Message

Andy Shevchenko Feb. 20, 2018, 9:43 p.m. UTC
There are users which print time and date represented by content of
struct rtc_time in human readable format.

Instead of open coding that each time introduce %ptR[dt][rv] specifier.

Note, users have to select PRINTK_PEXT_TIMEDATE option in a Kconfig.

Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Guan Xuetao <gxt@mprc.pku.edu.cn>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jason Wessel <jason.wessel@windriver.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Jonathan Hunter <jonathanh@nvidia.com>
Cc: Krzysztof Kozlowski <krzk@kernel.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 Documentation/core-api/printk-formats.rst |  31 +++++++
 lib/Kconfig                               |   8 ++
 lib/test_printf.c                         |   6 ++
 lib/vsprintf.c                            | 145 ++++++++++++++++++++++++++++++
 4 files changed, 190 insertions(+)

Comments

Joe Perches Feb. 20, 2018, 11:55 p.m. UTC | #1
On Tue, 2018-02-20 at 23:43 +0200, Andy Shevchenko wrote:
> There are users which print time and date represented by content of
> struct rtc_time in human readable format.
> 
> Instead of open coding that each time introduce %ptR[dt][rv] specifier.
> 
> Note, users have to select PRINTK_PEXT_TIMEDATE option in a Kconfig.

Not sure this is a great option.
Not just the name, the need to select it.

> diff --git a/lib/vsprintf.c b/lib/vsprintf.c
[]
> +static noinline_for_stack
> +char *date_str(char *buf, char *end, const struct rtc_time *tm, bool v, bool r)
> +{
> +	int year = tm->tm_year + (r ? 0 : 1900);
> +	int mon = tm->tm_mon + (r ? 0 : 1);

What happens with negative values?
Perhaps these temporaries should be unsigned int.

> +
> +	if (unlikely(v && (unsigned int)tm->tm_year > 200))
> +		buf = string(buf, end, "****", default_str_spec);
> +	else
> +		buf = number(buf, end, year, default_dec04_spec);
> +
> +	if (buf < end)
> +		*buf = '-';
> +	buf++;
> +
> +	if (unlikely(v && (unsigned int)tm->tm_mon > 11))
> +		buf = string(buf, end, "**", default_str_spec);
> +	else
> +		buf = number(buf, end, mon, default_dec02_spec);
> +
> +	if (buf < end)
> +		*buf = '-';
> +	buf++;
> +
> +	if (unlikely(v && (unsigned int)tm->tm_mday > 31))
> +		buf = string(buf, end, "**", default_str_spec);
> +	else
> +		buf = number(buf, end, tm->tm_mday, default_dec02_spec);
> +
> +	return buf;
> +}
> +
> +static noinline_for_stack
> +char *time_str(char *buf, char *end, const struct rtc_time *tm, bool v, bool r)
> +{

Maybe use unsigned int temporaries here too for hour, min, sec

> +	if (unlikely(v && (unsigned int)tm->tm_hour > 24))
> +		buf = string(buf, end, "**", default_str_spec);
> +	else
> +		buf = number(buf, end, tm->tm_hour, default_dec02_spec);
> +
> +	if (buf < end)
> +		*buf = ':';
> +	buf++;
> +
> +	if (unlikely(v && (unsigned int)tm->tm_min > 59))

leap seconds are allowed in the struct

> +		buf = string(buf, end, "**", default_str_spec);
> +	else
> +		buf = number(buf, end, tm->tm_min, default_dec02_spec);
> +
> +	if (buf < end)
> +		*buf = ':';
> +	buf++;
> +
> +	if (unlikely(v && (unsigned int)tm->tm_sec > 59))
> +		buf = string(buf, end, "**", default_str_spec);
> +	else
> +		buf = number(buf, end, tm->tm_sec, default_dec02_spec);
> +
> +	return buf;
> +}
>
Rasmus Villemoes Feb. 21, 2018, 7:38 a.m. UTC | #2
On 2018-02-21 00:55, Joe Perches wrote:
> On Tue, 2018-02-20 at 23:43 +0200, Andy Shevchenko wrote:
>> There are users which print time and date represented by content of
>> struct rtc_time in human readable format.
>>
>> Instead of open coding that each time introduce %ptR[dt][rv] specifier.
>>
>> Note, users have to select PRINTK_PEXT_TIMEDATE option in a Kconfig.
> 
> Not sure this is a great option.
> Not just the name, the need to select it.

Bikeshedding first: If you do keep the config option, please use PRINTF,
not PRINTK - vsprintf can be and is used by lots of code other than printk.

Well, on the one hand, I like to reduce the size of the kernel when
possible and ideally make all new functionality guarded by config
options, but OTOH, how much does compiling out the datetime formatters
really save? Also, I agree with Joe's concern about the need to select
it. Maybe if we had a gcc plugin that did %pFOO validation it could also
warn about %pBAR being used without a corresponding config option being
set. But we don't have that currently...

Rasmus
Alexandre Belloni Feb. 21, 2018, 9:16 a.m. UTC | #3
On 20/02/2018 at 15:55:07 -0800, Joe Perches wrote:
> On Tue, 2018-02-20 at 23:43 +0200, Andy Shevchenko wrote:
> > +static noinline_for_stack
> > +char *time_str(char *buf, char *end, const struct rtc_time *tm, bool v, bool r)
> > +{
> 
> Maybe use unsigned int temporaries here too for hour, min, sec
> 
> > +	if (unlikely(v && (unsigned int)tm->tm_hour > 24))
> > +		buf = string(buf, end, "**", default_str_spec);
> > +	else
> > +		buf = number(buf, end, tm->tm_hour, default_dec02_spec);
> > +
> > +	if (buf < end)
> > +		*buf = ':';
> > +	buf++;
> > +
> > +	if (unlikely(v && (unsigned int)tm->tm_min > 59))
> 
> leap seconds are allowed in the struct
> 

No, they are not:
https://elixir.bootlin.com/linux/v4.15.4/source/drivers/rtc/rtc-lib.c#L108
Geert Uytterhoeven Feb. 21, 2018, 9:33 a.m. UTC | #4
Hi Andy,

On Tue, Feb 20, 2018 at 10:43 PM, Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
> There are users which print time and date represented by content of
> struct rtc_time in human readable format.
>
> Instead of open coding that each time introduce %ptR[dt][rv] specifier.

Thanks for your patch!

> Note, users have to select PRINTK_PEXT_TIMEDATE option in a Kconfig.

Is it worthwhile making this an option?

> --- a/Documentation/core-api/printk-formats.rst
> +++ b/Documentation/core-api/printk-formats.rst
> @@ -412,6 +412,37 @@ Examples::
>
>  Passed by reference.
>
> +Time and date
> +-------------
> +
> +::
> +
> +       %pt[R]          YYYY-mm-dd HH:MM:SS
> +       %pt[R]d         YYYY-mm-dd
> +       %pt[R]t         HH:MM:SS

[R] suggests the "R" is optional?
But if it's missing, it prints the hex pointer value?

> +       %pt[R][dt]

What's the purpose of this?

> +
> +  R for struct rtc_time
> +
> +Note, users have to select PRINTK_PEXT_TIMEDATE option in a Kconfig.
> +
> +struct rtc_time
> +~~~~~~~~~~~~~~~
> +
> +::
> +
> +       %ptR[dt][rv]

What's the purpose of this paragraph, compared to the previous one?

> +
> +For printing date and time as represented by struct rtc_time structure in
> +human readable format.

> @@ -1443,6 +1458,132 @@ char *address_val(char *buf, char *end, const void *addr, const char *fmt)
>         return special_hex_number(buf, end, num, size);
>  }
>
> +static noinline_for_stack
> +char *date_str(char *buf, char *end, const struct rtc_time *tm, bool v, bool r)
> +{
> +       int year = tm->tm_year + (r ? 0 : 1900);
> +       int mon = tm->tm_mon + (r ? 0 : 1);
> +
> +       if (unlikely(v && (unsigned int)tm->tm_year > 200))
> +               buf = string(buf, end, "****", default_str_spec);
> +       else
> +               buf = number(buf, end, year, default_dec04_spec);
> +
> +       if (buf < end)
> +               *buf = '-';

Instead of all these checks to avoid overflowing the passed buffer, it
may be simpler to format everything in a fixed-size buffer on the stack,
and copy whatever will fit in the target buffer at the end.

> +       buf++;
> +
> +       if (unlikely(v && (unsigned int)tm->tm_mon > 11))
> +               buf = string(buf, end, "**", default_str_spec);
> +       else
> +               buf = number(buf, end, mon, default_dec02_spec);
> +
> +       if (buf < end)
> +               *buf = '-';
> +       buf++;
> +
> +       if (unlikely(v && (unsigned int)tm->tm_mday > 31))
> +               buf = string(buf, end, "**", default_str_spec);
> +       else
> +               buf = number(buf, end, tm->tm_mday, default_dec02_spec);
> +
> +       return buf;
> +}
> +
> +static noinline_for_stack
> +char *time_str(char *buf, char *end, const struct rtc_time *tm, bool v, bool r)
> +{
> +       if (unlikely(v && (unsigned int)tm->tm_hour > 24))
> +               buf = string(buf, end, "**", default_str_spec);
> +       else
> +               buf = number(buf, end, tm->tm_hour, default_dec02_spec);
> +
> +       if (buf < end)
> +               *buf = ':';

Likewise.

> +       buf++;
> +
> +       if (unlikely(v && (unsigned int)tm->tm_min > 59))
> +               buf = string(buf, end, "**", default_str_spec);
> +       else
> +               buf = number(buf, end, tm->tm_min, default_dec02_spec);
> +
> +       if (buf < end)
> +               *buf = ':';
> +       buf++;
> +
> +       if (unlikely(v && (unsigned int)tm->tm_sec > 59))
> +               buf = string(buf, end, "**", default_str_spec);
> +       else
> +               buf = number(buf, end, tm->tm_sec, default_dec02_spec);
> +
> +       return buf;
> +}
> +
> +static noinline_for_stack
> +char *rtc_str(char *buf, char *end, const struct rtc_time *tm, const char *fmt)
> +{
> +       bool have_t = true, have_d = true;
> +       bool validate = false;
> +       bool raw = false;
> +       int count = 1;
> +       bool found;
> +
> +       switch (fmt[++count]) {
> +       case 'd':
> +               have_t = false;
> +               break;
> +       case 't':
> +               have_d = false;
> +               break;
> +       }
> +
> +       /* No %pt[dt] supplied */
> +       if (have_d && have_t)
> +               --count;

First increment count, then rollback.
What about:

    switch (fmt[count]) {
    case 'd':
            have_t = false;
            count++;
            break;
    case 't':
            have_d = false;
            count++;
            break;
    }

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Andy Shevchenko Feb. 21, 2018, 1:19 p.m. UTC | #5
On Tue, 2018-02-20 at 15:55 -0800, Joe Perches wrote:
> On Tue, 2018-02-20 at 23:43 +0200, Andy Shevchenko wrote:
> > There are users which print time and date represented by content of
> > struct rtc_time in human readable format.
> > 
> > Instead of open coding that each time introduce %ptR[dt][rv]
> > specifier.
> > 
> > Note, users have to select PRINTK_PEXT_TIMEDATE option in a Kconfig.
> 
> Not sure this is a great option.
> Not just the name, the need to select it.

kbuildbot and some people complained about + text size.
https://lists.01.org/pipermail/kbuild-all/2017-June/034950.html

I would really like to compile it always.

> > +	int year = tm->tm_year + (r ? 0 : 1900);
> > +	int mon = tm->tm_mon + (r ? 0 : 1);
> 
> What happens with negative values?

Same as before.

> Perhaps these temporaries should be unsigned int.

No, the type of them is int, so, I'll keep it int.

> > +	if (unlikely(v && (unsigned int)tm->tm_min > 59))
> 
> leap seconds are allowed in the struct

Alexandre answered already, but I would add that this is part of
existing ABI, so, I wouldn't go to change this.
Andy Shevchenko Feb. 21, 2018, 1:23 p.m. UTC | #6
On Wed, 2018-02-21 at 08:38 +0100, Rasmus Villemoes wrote:
> On 2018-02-21 00:55, Joe Perches wrote:
> > On Tue, 2018-02-20 at 23:43 +0200, Andy Shevchenko wrote:
> > > There are users which print time and date represented by content
> > > of
> > > struct rtc_time in human readable format.
> > > 
> > > Instead of open coding that each time introduce %ptR[dt][rv]
> > > specifier.
> > > 
> > > Note, users have to select PRINTK_PEXT_TIMEDATE option in a
> > > Kconfig.
> > 
> > Not sure this is a great option.
> > Not just the name, the need to select it.
> 
> Bikeshedding first: If you do keep the config option, please use
> PRINTF,
> not PRINTK - vsprintf can be and is used by lots of code other than
> printk.

OK.

> Well, on the one hand, I like to reduce the size of the kernel when
> possible and ideally make all new functionality guarded by config
> options, but OTOH, how much does compiling out the datetime formatters
> really save?

https://lists.01.org/pipermail/kbuild-all/2017-June/034950.html

I understand that half a year time allows us to increase kernel text
size by 750+ bytes unconditionally.

I would really like to not use any option.

>  Also, I agree with Joe's concern about the need to select
> it.

So, what exactly you are proposing?

>  Maybe if we had a gcc plugin that did %pFOO validation it could also
> warn about %pBAR being used without a corresponding config option
> being
> set. But we don't have that currently...

We have not, so, it's out of scope. If it's a big impediment, then I'm
not the guy who will do the job.
Andy Shevchenko Feb. 21, 2018, 2:02 p.m. UTC | #7
On Wed, 2018-02-21 at 10:33 +0100, Geert Uytterhoeven wrote:
> Hi Andy,
> 
> On Tue, Feb 20, 2018 at 10:43 PM, Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
> > There are users which print time and date represented by content of
> > struct rtc_time in human readable format.
> > 
> > Instead of open coding that each time introduce %ptR[dt][rv]
> > specifier.
> 
> Thanks for your patch!
> 
> > Note, users have to select PRINTK_PEXT_TIMEDATE option in a Kconfig.
> 
> Is it worthwhile making this an option?

People were complaining before

https://lists.01.org/pipermail/kbuild-all/2017-June/034950.html

> > --- a/Documentation/core-api/printk-formats.rst
> > +++ b/Documentation/core-api/printk-formats.rst
> > @@ -412,6 +412,37 @@ Examples::
> > 
> >  Passed by reference.
> > 
> > +Time and date
> > +-------------
> > +
> > +::
> > +
> > +       %pt[R]          YYYY-mm-dd HH:MM:SS
> > +       %pt[R]d         YYYY-mm-dd
> > +       %pt[R]t         HH:MM:SS
> 
> [R] suggests the "R" is optional?
> But if it's missing, it prints the hex pointer value?

Yes.

> > +       %pt[R][dt]
> 
> What's the purpose of this?

A place holder to extend.

> > +
> > +  R for struct rtc_time
> > +
> > +Note, users have to select PRINTK_PEXT_TIMEDATE option in a
> > Kconfig.
> > +
> > +struct rtc_time
> > +~~~~~~~~~~~~~~~
> > +
> > +::
> > +
> > +       %ptR[dt][rv]
> 
> What's the purpose of this paragraph, compared to the previous one?

This is first batch to make it working for struct rtc_time. We have
several users (and I have some local patches WIP) to print time64_t /
timespec64 which would use different letters and paragraphs to explain.

I could remove it and return like it was in v1 (with the exception for
new R letter added).

TBH, I don't see much consensus among developers on this topic.
I wouldn't like to send a new version until it would be a consensus.

> > +static noinline_for_stack
> > +char *date_str(char *buf, char *end, const struct rtc_time *tm,
> > bool v, bool r)
> > +{
> > +       int year = tm->tm_year + (r ? 0 : 1900);
> > +       int mon = tm->tm_mon + (r ? 0 : 1);
> > +
> > +       if (unlikely(v && (unsigned int)tm->tm_year > 200))
> > +               buf = string(buf, end, "****", default_str_spec);
> > +       else
> > +               buf = number(buf, end, year, default_dec04_spec);
> > +
> > +       if (buf < end)
> > +               *buf = '-';
> 
> Instead of all these checks to avoid overflowing the passed buffer, it
> may be simpler to format everything in a fixed-size buffer on the
> stack,
> and copy whatever will fit in the target buffer at the end.

I dropped that idea since the most heavier call is number().
We still need to do several of them one way or the other.

So, I really don't see much benefit of doing your way.

> > +static noinline_for_stack
> > +char *rtc_str(char *buf, char *end, const struct rtc_time *tm,
> > const char *fmt)
> > +{
> > +       bool have_t = true, have_d = true;
> > +       bool validate = false;
> > +       bool raw = false;
> > +       int count = 1;
> > +       bool found;
> > +
> > +       switch (fmt[++count]) {
> > +       case 'd':
> > +               have_t = false;
> > +               break;
> > +       case 't':
> > +               have_d = false;
> > +               break;
> > +       }
> > +
> > +       /* No %pt[dt] supplied */
> > +       if (have_d && have_t)
> > +               --count;
> 
> First increment count, then rollback.
> What about:
> 
>     switch (fmt[count]) {
>     case 'd':
>             have_t = false;
>             count++;
>             break;
>     case 't':
>             have_d = false;
>             count++;
>             break;
>     }

Or simple:

 default:
  --count;
  break;

?

I really need to come up with the next pile for time64_t which I suppose
will require rethinking of format parsing and printing functions here.
Andy Shevchenko Feb. 21, 2018, 2:05 p.m. UTC | #8
> TBH, I don't see much consensus among developers on this topic.
> I wouldn't like to send a new version until it would be a consensus.

I would love to see the comments followed by an agreement from majority
of people who are in Cc list here.
Geert Uytterhoeven Feb. 21, 2018, 2:19 p.m. UTC | #9
On Wed, Feb 21, 2018 at 2:23 PM, Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
> On Wed, 2018-02-21 at 08:38 +0100, Rasmus Villemoes wrote:
>> On 2018-02-21 00:55, Joe Perches wrote:
>> > On Tue, 2018-02-20 at 23:43 +0200, Andy Shevchenko wrote:
>> > > There are users which print time and date represented by content
>> > > of
>> > > struct rtc_time in human readable format.
>> > >
>> > > Instead of open coding that each time introduce %ptR[dt][rv]
>> > > specifier.
>> > >
>> > > Note, users have to select PRINTK_PEXT_TIMEDATE option in a
>> > > Kconfig.
>> >
>> > Not sure this is a great option.
>> > Not just the name, the need to select it.
>>
>> Bikeshedding first: If you do keep the config option, please use
>> PRINTF,
>> not PRINTK - vsprintf can be and is used by lots of code other than
>> printk.
>
> OK.
>
>> Well, on the one hand, I like to reduce the size of the kernel when
>> possible and ideally make all new functionality guarded by config
>> options, but OTOH, how much does compiling out the datetime formatters
>> really save?
>
> https://lists.01.org/pipermail/kbuild-all/2017-June/034950.html
>
> I understand that half a year time allows us to increase kernel text
> size by 750+ bytes unconditionally.
>
> I would really like to not use any option.

Agreed.

FTR, growth of my atari_defconfig kernel between v4.7 and v4.15:

add/remove: 351/155 grow/shrink: 691/429 up/down: 63095/-38665 (24430)
add/remove: 394/156 grow/shrink: 595/709 up/down: 61173/-31092 (30081)
add/remove: 1315/711 grow/shrink: 1269/442 up/down: 172871/-92075 (80796)
add/remove: 525/266 grow/shrink: 914/510 up/down: 116115/-46240 (69875)
add/remove: 443/222 grow/shrink: 906/456 up/down: 77807/-40657 (37150)
add/remove: 536/296 grow/shrink: 1043/652 up/down: 97366/-65459 (31907)
add/remove: 413/176 grow/shrink: 711/479 up/down: 75678/-41356 (34322)
add/remove: 311/145 grow/shrink: 898/438 up/down: 51655/-26851 (24804)

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Andy Shevchenko Feb. 22, 2018, 12:46 p.m. UTC | #10
On Wed, 2018-02-21 at 15:19 +0100, Geert Uytterhoeven wrote:
> On Wed, Feb 21, 2018 at 2:23 PM, Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
> > On Wed, 2018-02-21 at 08:38 +0100, Rasmus Villemoes wrote:
> > > On 2018-02-21 00:55, Joe Perches wrote:

> > > Well, on the one hand, I like to reduce the size of the kernel
> > > when
> > > possible and ideally make all new functionality guarded by config
> > > options, but OTOH, how much does compiling out the datetime
> > > formatters
> > > really save?
> > 
> > https://lists.01.org/pipermail/kbuild-all/2017-June/034950.html
> > 
> > I understand that half a year time allows us to increase kernel text
> > size by 750+ bytes unconditionally.
> > 
> > I would really like to not use any option.
> 
> Agreed.
> 
> FTR, growth of my atari_defconfig kernel between v4.7 and v4.15:
> 
> add/remove: 351/155 grow/shrink: 691/429 up/down: 63095/-38665 (24430)
> add/remove: 394/156 grow/shrink: 595/709 up/down: 61173/-31092 (30081)
> add/remove: 1315/711 grow/shrink: 1269/442 up/down: 172871/-92075
> (80796)
> add/remove: 525/266 grow/shrink: 914/510 up/down: 116115/-46240
> (69875)
> add/remove: 443/222 grow/shrink: 906/456 up/down: 77807/-40657 (37150)
> add/remove: 536/296 grow/shrink: 1043/652 up/down: 97366/-65459
> (31907)
> add/remove: 413/176 grow/shrink: 711/479 up/down: 75678/-41356 (34322)
> add/remove: 311/145 grow/shrink: 898/438 up/down: 51655/-26851 (24804)

The concern was emitted from one who cares about tiny config, i.e.
i386_tiny_defconfig.
Dmitry Torokhov March 14, 2018, 4:54 p.m. UTC | #11
On Wed, Feb 21, 2018 at 04:05:53PM +0200, Andy Shevchenko wrote:
> > TBH, I don't see much consensus among developers on this topic.
> > I wouldn't like to send a new version until it would be a consensus.
> 
> I would love to see the comments followed by an agreement from majority
> of people who are in Cc list here.

Not sure why I am on the CC list, but if we add it at all I do not think
this should be guarded by a config option. Otherwise let's add options
for %d and %s as well.

Thanks.
diff mbox series

Patch

diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst
index 934559b3c130..9aaf0858ddd4 100644
--- a/Documentation/core-api/printk-formats.rst
+++ b/Documentation/core-api/printk-formats.rst
@@ -412,6 +412,37 @@  Examples::
 
 Passed by reference.
 
+Time and date
+-------------
+
+::
+
+	%pt[R]		YYYY-mm-dd HH:MM:SS
+	%pt[R]d		YYYY-mm-dd
+	%pt[R]t		HH:MM:SS
+	%pt[R][dt]
+
+  R for struct rtc_time
+
+Note, users have to select PRINTK_PEXT_TIMEDATE option in a Kconfig.
+
+struct rtc_time
+~~~~~~~~~~~~~~~
+
+::
+
+	%ptR[dt][rv]
+
+For printing date and time as represented by struct rtc_time structure in
+human readable format.
+
+By default year will be incremented by 1900 and month by 1. Use %ptRr (raw)
+to suppress this behaviour. On the other hand when %ptRv is applied
+validation mechanism will be in use, i.e. numbers out of range will be
+replaced by ** or ****.
+
+Passed by reference.
+
 struct clk
 ----------
 
diff --git a/lib/Kconfig b/lib/Kconfig
index e96089499371..e48cb7ba7beb 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -605,3 +605,11 @@  config GENERIC_CMPDI2
 
 config GENERIC_UCMPDI2
 	bool
+
+#
+# %p optional extensions for vsnprintf().
+# Should be selected by users.
+#
+
+config PRINTK_PEXT_TIMEDATE
+	bool
diff --git a/lib/test_printf.c b/lib/test_printf.c
index cea592f402ed..54f8960ea531 100644
--- a/lib/test_printf.c
+++ b/lib/test_printf.c
@@ -398,6 +398,11 @@  struct_va_format(void)
 {
 }
 
+static void __init
+struct_rtc_time(void)
+{
+}
+
 static void __init
 struct_clk(void)
 {
@@ -509,6 +514,7 @@  test_pointer(void)
 	uuid();
 	dentry();
 	struct_va_format();
+	struct_rtc_time();
 	struct_clk();
 	bitmap();
 	netdev_features();
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 28d7aca6a805..90ea28aefc26 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -30,6 +30,7 @@ 
 #include <linux/ioport.h>
 #include <linux/dcache.h>
 #include <linux/cred.h>
+#include <linux/rtc.h>
 #include <linux/uuid.h>
 #include <linux/of.h>
 #include <net/addrconf.h>
@@ -709,6 +710,20 @@  static const struct printf_spec default_dec_spec = {
 	.precision = -1,
 };
 
+static const struct printf_spec default_dec02_spec = {
+	.base = 10,
+	.field_width = 2,
+	.precision = -1,
+	.flags = ZEROPAD,
+};
+
+static const struct printf_spec default_dec04_spec = {
+	.base = 10,
+	.field_width = 4,
+	.precision = -1,
+	.flags = ZEROPAD,
+};
+
 static noinline_for_stack
 char *resource_string(char *buf, char *end, struct resource *res,
 		      struct printf_spec spec, const char *fmt)
@@ -1443,6 +1458,132 @@  char *address_val(char *buf, char *end, const void *addr, const char *fmt)
 	return special_hex_number(buf, end, num, size);
 }
 
+static noinline_for_stack
+char *date_str(char *buf, char *end, const struct rtc_time *tm, bool v, bool r)
+{
+	int year = tm->tm_year + (r ? 0 : 1900);
+	int mon = tm->tm_mon + (r ? 0 : 1);
+
+	if (unlikely(v && (unsigned int)tm->tm_year > 200))
+		buf = string(buf, end, "****", default_str_spec);
+	else
+		buf = number(buf, end, year, default_dec04_spec);
+
+	if (buf < end)
+		*buf = '-';
+	buf++;
+
+	if (unlikely(v && (unsigned int)tm->tm_mon > 11))
+		buf = string(buf, end, "**", default_str_spec);
+	else
+		buf = number(buf, end, mon, default_dec02_spec);
+
+	if (buf < end)
+		*buf = '-';
+	buf++;
+
+	if (unlikely(v && (unsigned int)tm->tm_mday > 31))
+		buf = string(buf, end, "**", default_str_spec);
+	else
+		buf = number(buf, end, tm->tm_mday, default_dec02_spec);
+
+	return buf;
+}
+
+static noinline_for_stack
+char *time_str(char *buf, char *end, const struct rtc_time *tm, bool v, bool r)
+{
+	if (unlikely(v && (unsigned int)tm->tm_hour > 24))
+		buf = string(buf, end, "**", default_str_spec);
+	else
+		buf = number(buf, end, tm->tm_hour, default_dec02_spec);
+
+	if (buf < end)
+		*buf = ':';
+	buf++;
+
+	if (unlikely(v && (unsigned int)tm->tm_min > 59))
+		buf = string(buf, end, "**", default_str_spec);
+	else
+		buf = number(buf, end, tm->tm_min, default_dec02_spec);
+
+	if (buf < end)
+		*buf = ':';
+	buf++;
+
+	if (unlikely(v && (unsigned int)tm->tm_sec > 59))
+		buf = string(buf, end, "**", default_str_spec);
+	else
+		buf = number(buf, end, tm->tm_sec, default_dec02_spec);
+
+	return buf;
+}
+
+static noinline_for_stack
+char *rtc_str(char *buf, char *end, const struct rtc_time *tm, const char *fmt)
+{
+	bool have_t = true, have_d = true;
+	bool validate = false;
+	bool raw = false;
+	int count = 1;
+	bool found;
+
+	switch (fmt[++count]) {
+	case 'd':
+		have_t = false;
+		break;
+	case 't':
+		have_d = false;
+		break;
+	}
+
+	/* No %pt[dt] supplied */
+	if (have_d && have_t)
+		--count;
+
+	found = true;
+	do {
+		switch (fmt[++count]) {
+		case 'r':
+			raw = true;
+			break;
+		case 'v':
+			validate = true;
+			break;
+		default:
+			found = false;
+			break;
+		}
+	} while (found);
+
+	if (have_d)
+		buf = date_str(buf, end, tm, validate, raw);
+	if (have_d && have_t) {
+		if (buf < end)
+			*buf = ' ';
+		buf++;
+	}
+	if (have_t)
+		buf = time_str(buf, end, tm, validate, raw);
+
+	return buf;
+}
+
+static noinline_for_stack
+char *timeanddate(char *buf, char *end, void *ptr, const char *fmt)
+{
+	if (!IS_ENABLED(CONFIG_PRINTK_PEXT_TIMEDATE))
+		return string(buf, end, NULL, default_str_spec);
+
+	switch (fmt[1]) {
+	case 'R':
+		return rtc_str(buf, end, (const struct rtc_time *)ptr, fmt);
+	default:
+		break;
+	}
+	return special_hex_number(buf, end, (unsigned long)ptr, sizeof(void *));
+}
+
 static noinline_for_stack
 char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec,
 	    const char *fmt)
@@ -1776,6 +1917,8 @@  static char *ptr_to_id(char *buf, char *end, void *ptr, struct printf_spec spec)
  * - 'd[234]' For a dentry name (optionally 2-4 last components)
  * - 'D[234]' Same as 'd' but for a struct file
  * - 'g' For block_device name (gendisk + partition number)
+ * - 't[R][dt][rv]' For time and date as represented:
+ *      R    struct rtc_time
  * - 'C' For a clock, it prints the name (Common Clock Framework) or address
  *       (legacy clock framework) of the clock
  * - 'Cn' For a clock, it prints the name (Common Clock Framework) or address
@@ -1906,6 +2049,8 @@  char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 		return address_val(buf, end, ptr, fmt);
 	case 'd':
 		return dentry_name(buf, end, ptr, spec, fmt);
+	case 't':
+		return timeanddate(buf, end, ptr, fmt);
 	case 'C':
 		return clock(buf, end, ptr, spec, fmt);
 	case 'D':