Merge tag 'printk-for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/pmladek...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 9 Mar 2019 17:22:42 +0000 (09:22 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 9 Mar 2019 17:22:42 +0000 (09:22 -0800)
Pull printk updates from Petr Mladek:

 - Allow to sort mixed lines by an extra information about the caller

 - Remove no longer used LOG_PREFIX.

 - Some clean up and documentation update.

* tag 'printk-for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/pmladek/printk:
  printk/docs: Add extra integer types to printk-formats
  printk: Remove no longer used LOG_PREFIX.
  lib/vsprintf: Remove %pCr remnant in comment
  printk: Pass caller information to log_store().
  printk: Add caller information to printk() output.

1  2 
Documentation/core-api/printk-formats.rst
include/linux/printk.h
kernel/printk/printk.c
lib/Kconfig.debug
lib/vsprintf.c

index a7fae4538946702ecadd09558a043dffb0ab60e3,cd1b0e804b74d8f8ff055e75df20dd84e0e07620..c37ec7cd9c060c30c5dfd167c62faaf94c34a3ab
@@@ -13,6 -13,10 +13,10 @@@ Integer type
  
        If variable is of Type,         use printk format specifier:
        ------------------------------------------------------------
+               char                    %hhd or %hhx
+               unsigned char           %hhu or %hhx
+               short int               %hd or %hx
+               unsigned short int      %hu or %hx
                int                     %d or %x
                unsigned int            %u or %x
                long                    %ld or %lx
                unsigned long long      %llu or %llx
                size_t                  %zu or %zx
                ssize_t                 %zd or %zx
+               s8                      %hhd or %hhx
+               u8                      %hhu or %hhx
+               s16                     %hd or %hx
+               u16                     %hu or %hx
                s32                     %d or %x
                u32                     %u or %x
                s64                     %lld or %llx
@@@ -412,24 -420,6 +420,24 @@@ Examples:
  
  Passed by reference.
  
 +Time and date (struct rtc_time)
 +-------------------------------
 +
 +::
 +
 +      %ptR            YYYY-mm-ddTHH:MM:SS
 +      %ptRd           YYYY-mm-dd
 +      %ptRt           HH:MM:SS
 +      %ptR[dt][r]
 +
 +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.
 +
 +Passed by reference.
 +
  struct clk
  ----------
  
diff --combined include/linux/printk.h
index 02b5c115d89b275058a645fff873cedbe8affed3,97aa12c928d45f4aa89e489fa3c866c9a0ee1037..d7c77ed1a4cbf1b8a5ec7d3e986698e368d55666
@@@ -18,7 -18,6 +18,6 @@@ static inline int printk_get_level(cons
        if (buffer[0] == KERN_SOH_ASCII && buffer[1]) {
                switch (buffer[1]) {
                case '0' ... '7':
-               case 'd':       /* KERN_DEFAULT */
                case 'c':       /* KERN_CONT */
                        return buffer[1];
                }
@@@ -264,7 -263,7 +263,7 @@@ static inline void show_regs_print_info
  {
  }
  
 -static inline asmlinkage void dump_stack(void)
 +static inline void dump_stack(void)
  {
  }
  
@@@ -461,7 -460,7 +460,7 @@@ do {                                                                       
                                      DEFAULT_RATELIMIT_INTERVAL,       \
                                      DEFAULT_RATELIMIT_BURST);         \
        DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, pr_fmt(fmt));         \
 -      if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT) &&        \
 +      if (DYNAMIC_DEBUG_BRANCH(descriptor) &&                         \
            __ratelimit(&_rs))                                          \
                __dynamic_pr_debug(&descriptor, pr_fmt(fmt), ##__VA_ARGS__);    \
  } while (0)
diff --combined kernel/printk/printk.c
index d3d170374cebf884169c754071813e418336e188,9b6783c158f91b707994a4af7ca004b416c17a66..8eee85bb26877f407b43d7f6a0a7c806956b0075
@@@ -31,6 -31,7 +31,6 @@@
  #include <linux/delay.h>
  #include <linux/smp.h>
  #include <linux/security.h>
 -#include <linux/bootmem.h>
  #include <linux/memblock.h>
  #include <linux/syscalls.h>
  #include <linux/crash_core.h>
@@@ -344,7 -345,6 +344,6 @@@ static int console_msg_format = MSG_FOR
  
  enum log_flags {
        LOG_NEWLINE     = 2,    /* text ended with a newline */
-       LOG_PREFIX      = 4,    /* text started with a prefix */
        LOG_CONT        = 8,    /* text is a fragment of a continuation line */
  };
  
@@@ -356,6 -356,9 +355,9 @@@ struct printk_log 
        u8 facility;            /* syslog facility */
        u8 flags:5;             /* internal record flags */
        u8 level:3;             /* syslog level */
+ #ifdef CONFIG_PRINTK_CALLER
+       u32 caller_id;            /* thread id or processor id */
+ #endif
  }
  #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
  __packed __aligned(4)
@@@ -422,7 -425,11 +424,11 @@@ static u64 exclusive_console_stop_seq
  static u64 clear_seq;
  static u32 clear_idx;
  
+ #ifdef CONFIG_PRINTK_CALLER
+ #define PREFIX_MAX            48
+ #else
  #define PREFIX_MAX            32
+ #endif
  #define LOG_LINE_MAX          (1024 - PREFIX_MAX)
  
  #define LOG_LEVEL(v)          ((v) & 0x07)
@@@ -577,7 -584,7 +583,7 @@@ static u32 truncate_msg(u16 *text_len, 
  }
  
  /* insert record into the buffer, discard old ones, update heads */
- static int log_store(int facility, int level,
+ static int log_store(u32 caller_id, int facility, int level,
                     enum log_flags flags, u64 ts_nsec,
                     const char *dict, u16 dict_len,
                     const char *text, u16 text_len)
                msg->ts_nsec = ts_nsec;
        else
                msg->ts_nsec = local_clock();
+ #ifdef CONFIG_PRINTK_CALLER
+       msg->caller_id = caller_id;
+ #endif
        memset(log_dict(msg) + dict_len, 0, pad_len);
        msg->len = size;
  
@@@ -688,12 -698,21 +697,21 @@@ static ssize_t msg_print_ext_header(cha
                                    struct printk_log *msg, u64 seq)
  {
        u64 ts_usec = msg->ts_nsec;
+       char caller[20];
+ #ifdef CONFIG_PRINTK_CALLER
+       u32 id = msg->caller_id;
+       snprintf(caller, sizeof(caller), ",caller=%c%u",
+                id & 0x80000000 ? 'C' : 'T', id & ~0x80000000);
+ #else
+       caller[0] = '\0';
+ #endif
  
        do_div(ts_usec, 1000);
  
-       return scnprintf(buf, size, "%u,%llu,%llu,%c;",
-                      (msg->facility << 3) | msg->level, seq, ts_usec,
-                      msg->flags & LOG_CONT ? 'c' : '-');
+       return scnprintf(buf, size, "%u,%llu,%llu,%c%s;",
+                        (msg->facility << 3) | msg->level, seq, ts_usec,
+                        msg->flags & LOG_CONT ? 'c' : '-', caller);
  }
  
  static ssize_t msg_print_ext_body(char *buf, size_t size,
@@@ -1038,6 -1057,9 +1056,9 @@@ void log_buf_vmcoreinfo_setup(void
        VMCOREINFO_OFFSET(printk_log, len);
        VMCOREINFO_OFFSET(printk_log, text_len);
        VMCOREINFO_OFFSET(printk_log, dict_len);
+ #ifdef CONFIG_PRINTK_CALLER
+       VMCOREINFO_OFFSET(printk_log, caller_id);
+ #endif
  }
  #endif
  
@@@ -1124,9 -1146,9 +1145,9 @@@ void __init setup_log_buf(int early
  
        if (early) {
                new_log_buf =
 -                      memblock_virt_alloc(new_log_buf_len, LOG_ALIGN);
 +                      memblock_alloc(new_log_buf_len, LOG_ALIGN);
        } else {
 -              new_log_buf = memblock_virt_alloc_nopanic(new_log_buf_len,
 +              new_log_buf = memblock_alloc_nopanic(new_log_buf_len,
                                                          LOG_ALIGN);
        }
  
@@@ -1236,10 -1258,23 +1257,23 @@@ static size_t print_time(u64 ts, char *
  {
        unsigned long rem_nsec = do_div(ts, 1000000000);
  
-       return sprintf(buf, "[%5lu.%06lu] ",
+       return sprintf(buf, "[%5lu.%06lu]",
                       (unsigned long)ts, rem_nsec / 1000);
  }
  
+ #ifdef CONFIG_PRINTK_CALLER
+ static size_t print_caller(u32 id, char *buf)
+ {
+       char caller[12];
+       snprintf(caller, sizeof(caller), "%c%u",
+                id & 0x80000000 ? 'C' : 'T', id & ~0x80000000);
+       return sprintf(buf, "[%6s]", caller);
+ }
+ #else
+ #define print_caller(id, buf) 0
+ #endif
  static size_t print_prefix(const struct printk_log *msg, bool syslog,
                           bool time, char *buf)
  {
  
        if (syslog)
                len = print_syslog((msg->facility << 3) | msg->level, buf);
        if (time)
                len += print_time(msg->ts_nsec, buf + len);
+       len += print_caller(msg->caller_id, buf + len);
+       if (IS_ENABLED(CONFIG_PRINTK_CALLER) || time) {
+               buf[len++] = ' ';
+               buf[len] = '\0';
+       }
        return len;
  }
  
@@@ -1466,7 -1510,7 +1509,7 @@@ int do_syslog(int type, char __user *bu
                        return -EINVAL;
                if (!len)
                        return 0;
 -              if (!access_ok(VERIFY_WRITE, buf, len))
 +              if (!access_ok(buf, len))
                        return -EFAULT;
                error = wait_event_interruptible(log_wait,
                                                 syslog_seq != log_next_seq);
                        return -EINVAL;
                if (!len)
                        return 0;
 -              if (!access_ok(VERIFY_WRITE, buf, len))
 +              if (!access_ok(buf, len))
                        return -EFAULT;
                error = syslog_print_all(buf, len, clear);
                break;
@@@ -1752,6 -1796,12 +1795,12 @@@ static inline void printk_delay(void
        }
  }
  
+ static inline u32 printk_caller_id(void)
+ {
+       return in_task() ? task_pid_nr(current) :
+               0x80000000 + raw_smp_processor_id();
+ }
  /*
   * Continuation lines are buffered, and not committed to the record buffer
   * until the line is complete, or a race forces it. The line fragments
  static struct cont {
        char buf[LOG_LINE_MAX];
        size_t len;                     /* length == 0 means unused buffer */
-       struct task_struct *owner;      /* task of first print*/
+       u32 caller_id;                  /* printk_caller_id() of first print */
        u64 ts_nsec;                    /* time of first print */
        u8 level;                       /* log level of first message */
        u8 facility;                    /* log facility of first message */
@@@ -1773,12 -1823,13 +1822,13 @@@ static void cont_flush(void
        if (cont.len == 0)
                return;
  
-       log_store(cont.facility, cont.level, cont.flags, cont.ts_nsec,
-                 NULL, 0, cont.buf, cont.len);
+       log_store(cont.caller_id, cont.facility, cont.level, cont.flags,
+                 cont.ts_nsec, NULL, 0, cont.buf, cont.len);
        cont.len = 0;
  }
  
- static bool cont_add(int facility, int level, enum log_flags flags, const char *text, size_t len)
+ static bool cont_add(u32 caller_id, int facility, int level,
+                    enum log_flags flags, const char *text, size_t len)
  {
        /* If the line gets too long, split it up in separate records. */
        if (cont.len + len > sizeof(cont.buf)) {
        if (!cont.len) {
                cont.facility = facility;
                cont.level = level;
-               cont.owner = current;
+               cont.caller_id = caller_id;
                cont.ts_nsec = local_clock();
                cont.flags = flags;
        }
  
  static size_t log_output(int facility, int level, enum log_flags lflags, const char *dict, size_t dictlen, char *text, size_t text_len)
  {
+       const u32 caller_id = printk_caller_id();
        /*
         * If an earlier line was buffered, and we're a continuation
-        * write from the same process, try to add it to the buffer.
+        * write from the same context, try to add it to the buffer.
         */
        if (cont.len) {
-               if (cont.owner == current && (lflags & LOG_CONT)) {
-                       if (cont_add(facility, level, lflags, text, text_len))
+               if (cont.caller_id == caller_id && (lflags & LOG_CONT)) {
+                       if (cont_add(caller_id, facility, level, lflags, text, text_len))
                                return text_len;
                }
                /* Otherwise, make sure it's flushed */
  
        /* If it doesn't end in a newline, try to buffer the current line */
        if (!(lflags & LOG_NEWLINE)) {
-               if (cont_add(facility, level, lflags, text, text_len))
+               if (cont_add(caller_id, facility, level, lflags, text, text_len))
                        return text_len;
        }
  
        /* Store it in the record log */
-       return log_store(facility, level, lflags, 0, dict, dictlen, text, text_len);
+       return log_store(caller_id, facility, level, lflags, 0,
+                        dict, dictlen, text, text_len);
  }
  
  /* Must be called under logbuf_lock. */
@@@ -1867,9 -1921,6 +1920,6 @@@ int vprintk_store(int facility, int lev
                        case '0' ... '7':
                                if (level == LOGLEVEL_DEFAULT)
                                        level = kern_level - '0';
-                               /* fallthrough */
-                       case 'd':       /* KERN_DEFAULT */
-                               lflags |= LOG_PREFIX;
                                break;
                        case 'c':       /* KERN_CONT */
                                lflags |= LOG_CONT;
                level = default_message_loglevel;
  
        if (dict)
-               lflags |= LOG_PREFIX|LOG_NEWLINE;
+               lflags |= LOG_NEWLINE;
  
        return log_output(facility, level, lflags,
                          dict, dictlen, text, text_len);
diff --combined lib/Kconfig.debug
index e718487c97c3dd2b6a09ebcc4318218fc26ca1b1,89df6750b3651fbc61f076ade431ed94ef0f83f3..91ed81250fb3a3afd5441b5eee313b58285c70ed
@@@ -17,6 -17,23 +17,23 @@@ config PRINTK_TIM
          The behavior is also controlled by the kernel command line
          parameter printk.time=1. See Documentation/admin-guide/kernel-parameters.rst
  
+ config PRINTK_CALLER
+       bool "Show caller information on printks"
+       depends on PRINTK
+       help
+         Selecting this option causes printk() to add a caller "thread id" (if
+         in task context) or a caller "processor id" (if not in task context)
+         to every message.
+         This option is intended for environments where multiple threads
+         concurrently call printk() for many times, for it is difficult to
+         interpret without knowing where these lines (or sometimes individual
+         line which was divided into multiple lines due to race) came from.
+         Since toggling after boot makes the code racy, currently there is
+         no option to enable/disable at the kernel command line parameter or
+         sysfs interface.
  config CONSOLE_LOGLEVEL_DEFAULT
        int "Default console loglevel (1-15)"
        range 1 15
@@@ -222,6 -239,7 +239,6 @@@ config ENABLE_MUST_CHEC
  config FRAME_WARN
        int "Warn for stack frames larger than (needs gcc 4.4)"
        range 0 8192
 -      default 3072 if KASAN_EXTRA
        default 2048 if GCC_PLUGIN_LATENT_ENTROPY
        default 1280 if (!64BIT && PARISC)
        default 1024 if (!64BIT && !PARISC)
@@@ -265,6 -283,23 +282,6 @@@ config UNUSED_SYMBOL
          you really need it, and what the merge plan to the mainline kernel for
          your module is.
  
 -config PAGE_OWNER
 -      bool "Track page owner"
 -      depends on DEBUG_KERNEL && STACKTRACE_SUPPORT
 -      select DEBUG_FS
 -      select STACKTRACE
 -      select STACKDEPOT
 -      select PAGE_EXTENSION
 -      help
 -        This keeps track of what call chain is the owner of a page, may
 -        help to find bare alloc_page(s) leaks. Even if you include this
 -        feature on your build, it is disabled in default. You should pass
 -        "page_owner=on" to boot parameter in order to enable it. Eats
 -        a fair amount of memory if enabled. See tools/vm/page_owner_sort.c
 -        for user-space helper.
 -
 -        If unsure, say N.
 -
  config DEBUG_FS
        bool "Debug Filesystem"
        help
@@@ -421,7 -456,7 +438,7 @@@ config DEBUG_KERNE
  
  menu "Memory Debugging"
  
 -source mm/Kconfig.debug
 +source "mm/Kconfig.debug"
  
  config DEBUG_OBJECTS
        bool "Debug object operations"
@@@ -575,21 -610,6 +592,21 @@@ config DEBUG_KMEMLEAK_DEFAULT_OF
          Say Y here to disable kmemleak by default. It can then be enabled
          on the command line via kmemleak=on.
  
 +config DEBUG_KMEMLEAK_AUTO_SCAN
 +      bool "Enable kmemleak auto scan thread on boot up"
 +      default y
 +      depends on DEBUG_KMEMLEAK
 +      help
 +        Depending on the cpu, kmemleak scan may be cpu intensive and can
 +        stall user tasks at times. This option enables/disables automatic
 +        kmemleak scan at boot up.
 +
 +        Say N here to disable kmemleak auto scan thread to stop automatic
 +        scanning. Disabling this option disables automatic reporting of
 +        memory leaks.
 +
 +        If unsure, say Y.
 +
  config DEBUG_STACK_USAGE
        bool "Stack utilization instrumentation"
        depends on DEBUG_KERNEL && !IA64
@@@ -1289,7 -1309,7 +1306,7 @@@ config DEBUG_KOBJEC
        depends on DEBUG_KERNEL
        help
          If you say Y here, some extra kobject debugging messages will be sent
 -        to the syslog. 
 +        to the syslog.
  
  config DEBUG_KOBJECT_RELEASE
        bool "kobject release debugging"
@@@ -1606,7 -1626,7 +1623,7 @@@ config LATENCYTO
          Enable this option if you want to use the LatencyTOP tool
          to find out which userspace is blocking on what kernel operations.
  
 -source kernel/trace/Kconfig
 +source "kernel/trace/Kconfig"
  
  config PROVIDE_OHCI1394_DMA_INIT
        bool "Remote debugging over FireWire early on boot"
@@@ -1682,6 -1702,7 +1699,6 @@@ if RUNTIME_TESTING_MEN
  config LKDTM
        tristate "Linux Kernel Dump Test Tool Module"
        depends on DEBUG_FS
 -      depends on BLOCK
        help
        This module enables testing of the different dumping mechanisms by
        inducing system failures at predefined crash points.
@@@ -1809,9 -1830,6 +1826,9 @@@ config TEST_BITFIEL
  config TEST_UUID
        tristate "Test functions located in the uuid module at runtime"
  
 +config TEST_XARRAY
 +      tristate "Test the XArray code at runtime"
 +
  config TEST_OVERFLOW
        tristate "Test check_*_overflow() functions at runtime"
  
@@@ -1857,19 -1875,6 +1874,19 @@@ config TEST_LK
  
          If unsure, say N.
  
 +config TEST_VMALLOC
 +      tristate "Test module for stress/performance analysis of vmalloc allocator"
 +      default n
 +       depends on MMU
 +      depends on m
 +      help
 +        This builds the "test_vmalloc" module that should be used for
 +        stress and performance analysis. So, any new change for vmalloc
 +        subsystem can be evaluated from performance and stability point
 +        of view.
 +
 +        If unsure, say N.
 +
  config TEST_USER_COPY
        tristate "Test user/kernel boundary protections"
        depends on m
@@@ -1977,59 -1982,11 +1994,59 @@@ config TEST_DEBUG_VIRTUA
  
          If unsure, say N.
  
 +config TEST_MEMCAT_P
 +      tristate "Test memcat_p() helper function"
 +      help
 +        Test the memcat_p() helper for correctly merging two
 +        pointer arrays together.
 +
 +        If unsure, say N.
 +
 +config TEST_LIVEPATCH
 +      tristate "Test livepatching"
 +      default n
 +      depends on DYNAMIC_DEBUG
 +      depends on LIVEPATCH
 +      depends on m
 +      help
 +        Test kernel livepatching features for correctness.  The tests will
 +        load test modules that will be livepatched in various scenarios.
 +
 +        To run all the livepatching tests:
 +
 +        make -C tools/testing/selftests TARGETS=livepatch run_tests
 +
 +        Alternatively, individual tests may be invoked:
 +
 +        tools/testing/selftests/livepatch/test-callbacks.sh
 +        tools/testing/selftests/livepatch/test-livepatch.sh
 +        tools/testing/selftests/livepatch/test-shadow-vars.sh
 +
 +        If unsure, say N.
 +
 +config TEST_OBJAGG
 +      tristate "Perform selftest on object aggreration manager"
 +      default n
 +      depends on OBJAGG
 +      help
 +        Enable this option to test object aggregation manager on boot
 +        (or module load).
 +
 +
 +config TEST_STACKINIT
 +      tristate "Test level of stack variable initialization"
 +      help
 +        Test if the kernel is zero-initializing stack variables and
 +        padding. Coverage is controlled by compiler flags,
 +        CONFIG_GCC_PLUGIN_STRUCTLEAK, CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF,
 +        or CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL.
 +
 +        If unsure, say N.
 +
  endif # RUNTIME_TESTING_MENU
  
  config MEMTEST
        bool "Memtest"
 -      depends on HAVE_MEMBLOCK
        ---help---
          This option adds a kernel parameter 'memtest', which allows memtest
          to be set.
diff --combined lib/vsprintf.c
index 30b00de4f321ff57557d0dd0fc618b3fb4a745fb,458600f4fbc5c659c4204192e654f43e021eb2ee..791b6fa369051fe7cf4aad89e262c10e694de906
@@@ -17,7 -17,6 +17,7 @@@
   */
  
  #include <stdarg.h>
 +#include <linux/build_bug.h>
  #include <linux/clk.h>
  #include <linux/clk-provider.h>
  #include <linux/module.h>     /* for KSYM_SYMBOL_LEN */
@@@ -31,7 -30,6 +31,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>
@@@ -406,8 -404,6 +406,8 @@@ struct printf_spec 
        unsigned int    base:8;         /* number base, 8, 10 or 16 only */
        signed int      precision:16;   /* # of digits/chars */
  } __packed;
 +static_assert(sizeof(struct printf_spec) == 8);
 +
  #define FIELD_WIDTH_MAX ((1 << 23) - 1)
  #define PRECISION_MAX ((1 << 15) - 1)
  
@@@ -425,6 -421,8 +425,6 @@@ char *number(char *buf, char *end, unsi
        int field_width = spec.field_width;
        int precision = spec.precision;
  
 -      BUILD_BUG_ON(sizeof(struct printf_spec) != 8);
 -
        /* locase = 0 or 0x20. ORing digits or letters with 'locase'
         * produces same digits or (maybe lowercased) letters */
        locase = (spec.flags & SMALL);
@@@ -824,20 -822,6 +824,20 @@@ static const struct printf_spec default
        .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)
@@@ -1565,87 -1549,6 +1565,87 @@@ char *address_val(char *buf, char *end
        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 r)
 +{
 +      int year = tm->tm_year + (r ? 0 : 1900);
 +      int mon = tm->tm_mon + (r ? 0 : 1);
 +
 +      buf = number(buf, end, year, default_dec04_spec);
 +      if (buf < end)
 +              *buf = '-';
 +      buf++;
 +
 +      buf = number(buf, end, mon, default_dec02_spec);
 +      if (buf < end)
 +              *buf = '-';
 +      buf++;
 +
 +      return number(buf, end, tm->tm_mday, default_dec02_spec);
 +}
 +
 +static noinline_for_stack
 +char *time_str(char *buf, char *end, const struct rtc_time *tm, bool r)
 +{
 +      buf = number(buf, end, tm->tm_hour, default_dec02_spec);
 +      if (buf < end)
 +              *buf = ':';
 +      buf++;
 +
 +      buf = number(buf, end, tm->tm_min, default_dec02_spec);
 +      if (buf < end)
 +              *buf = ':';
 +      buf++;
 +
 +      return number(buf, end, tm->tm_sec, default_dec02_spec);
 +}
 +
 +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 raw = false;
 +      int count = 2;
 +
 +      switch (fmt[count]) {
 +      case 'd':
 +              have_t = false;
 +              count++;
 +              break;
 +      case 't':
 +              have_d = false;
 +              count++;
 +              break;
 +      }
 +
 +      raw = fmt[count] == 'r';
 +
 +      if (have_d)
 +              buf = date_str(buf, end, tm, raw);
 +      if (have_d && have_t) {
 +              /* Respect ISO 8601 */
 +              if (buf < end)
 +                      *buf = 'T';
 +              buf++;
 +      }
 +      if (have_t)
 +              buf = time_str(buf, end, tm, raw);
 +
 +      return buf;
 +}
 +
 +static noinline_for_stack
 +char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec,
 +                  const char *fmt)
 +{
 +      switch (fmt[1]) {
 +      case 'R':
 +              return rtc_str(buf, end, (const struct rtc_time *)ptr, fmt);
 +      default:
 +              return ptr_to_id(buf, end, ptr, spec);
 +      }
 +}
 +
  static noinline_for_stack
  char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec,
            const char *fmt)
@@@ -1781,7 -1684,6 +1781,7 @@@ char *device_node_string(char *buf, cha
                fmt = "f";
  
        for (pass = false; strspn(fmt,"fnpPFcC"); fmt++, pass = true) {
 +              int precision;
                if (pass) {
                        if (buf < end)
                                *buf = ':';
                        buf = device_node_gen_full_name(dn, buf, end);
                        break;
                case 'n':       /* name */
 -                      buf = string(buf, end, dn->name, str_spec);
 +                      p = kbasename(of_node_full_name(dn));
 +                      precision = str_spec.precision;
 +                      str_spec.precision = strchrnul(p, '@') - p;
 +                      buf = string(buf, end, p, str_spec);
 +                      str_spec.precision = precision;
                        break;
                case 'p':       /* phandle */
                        buf = number(buf, end, (unsigned int)dn->phandle, num_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][r]' 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
   *        (legacy clock framework) of the clock
-  * - 'Cr' For a clock, it prints the current rate of the clock
   * - 'G' For flags to be printed as a collection of symbolic strings that would
   *       construct the specific value. Supported flags given by option:
   *       p page flags (see struct page) given as pointer to unsigned long
@@@ -2051,8 -1946,6 +2050,8 @@@ char *pointer(const char *fmt, char *bu
                return address_val(buf, end, ptr, fmt);
        case 'd':
                return dentry_name(buf, end, ptr, spec, fmt);
 +      case 't':
 +              return time_and_date(buf, end, ptr, spec, fmt);
        case 'C':
                return clock(buf, end, ptr, spec, fmt);
        case 'D':