A handful drivers want to print a content of the struct timespec64
in a format of %lld:%09ld. In order to make their lives easier, add
the respecting specifier directly to the printf() implementation.

Signed-off-by: Andy Shevchenko <[email protected]>
---
 Documentation/core-api/printk-formats.rst | 11 +++++++--
 lib/tests/printf_kunit.c                  |  4 ++++
 lib/vsprintf.c                            | 28 ++++++++++++++++++++++-
 3 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/Documentation/core-api/printk-formats.rst 
b/Documentation/core-api/printk-formats.rst
index 7f2f11b48286..c0b1b6089307 100644
--- a/Documentation/core-api/printk-formats.rst
+++ b/Documentation/core-api/printk-formats.rst
@@ -547,11 +547,13 @@ Time and date
        %pt[RT]s                YYYY-mm-dd HH:MM:SS
        %pt[RT]d                YYYY-mm-dd
        %pt[RT]t                HH:MM:SS
-       %pt[RT][dt][r][s]
+       %ptSp                   <seconds>.<nanoseconds>
+       %pt[RST][dt][r][s]
 
 For printing date and time as represented by::
 
-       R  struct rtc_time structure
+       R  content of struct rtc_time
+       S  content of struct timespec64
        T  time64_t type
 
 in human readable format.
@@ -563,6 +565,11 @@ The %pt[RT]s (space) will override ISO 8601 separator by 
using ' ' (space)
 instead of 'T' (Capital T) between date and time. It won't have any effect
 when date or time is omitted.
 
+The %ptSp is equivalent to %lld.%09ld for the content of the struct timespec64.
+When the other specifiers are given, it becomes the respective equivalent of
+%ptT[dt][r][s].%09ld. In other words, the seconds are being printed in
+the human readable format followed by a dot and nanoseconds.
+
 Passed by reference.
 
 struct clk
diff --git a/lib/tests/printf_kunit.c b/lib/tests/printf_kunit.c
index bc54cca2d7a6..7617e5b8b02c 100644
--- a/lib/tests/printf_kunit.c
+++ b/lib/tests/printf_kunit.c
@@ -504,6 +504,7 @@ time_and_date(struct kunit *kunittest)
        };
        /* 2019-01-04T15:32:23 */
        time64_t t = 1546615943;
+       struct timespec64 ts = { .tv_sec = t, .tv_nsec = 11235813 };
 
        test("(%pt?)", "%pt", &tm);
        test("2018-11-26T05:35:43", "%ptR", &tm);
@@ -522,6 +523,9 @@ time_and_date(struct kunit *kunittest)
        test("0119-00-04 15:32:23", "%ptTsr", &t);
        test("15:32:23|2019-01-04", "%ptTts|%ptTds", &t, &t);
        test("15:32:23|0119-00-04", "%ptTtrs|%ptTdrs", &t, &t);
+
+       test("2019-01-04T15:32:23.011235813", "%ptS", &ts);
+       test("1546615943.011235813", "%ptSp", &ts);
 }
 
 static void
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 11dbf1023391..51a88b3f5b52 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1983,6 +1983,28 @@ char *time64_str(char *buf, char *end, const time64_t 
time,
        return rtc_str(buf, end, &rtc_time, spec, fmt);
 }
 
+static noinline_for_stack
+char *timespec64_str(char *buf, char *end, const struct timespec64 *ts,
+                    struct printf_spec spec, const char *fmt)
+{
+       static const struct printf_spec default_dec09_spec = {
+               .base = 10,
+               .field_width = 9,
+               .precision = -1,
+               .flags = ZEROPAD,
+       };
+
+       if (fmt[2] == 'p')
+               buf = number(buf, end, ts->tv_sec, default_dec_spec);
+       else
+               buf = time64_str(buf, end, ts->tv_sec, spec, fmt);
+       if (buf < end)
+               *buf = '.';
+       buf++;
+
+       return number(buf, end, ts->tv_nsec, default_dec09_spec);
+}
+
 static noinline_for_stack
 char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec,
                    const char *fmt)
@@ -1993,6 +2015,8 @@ char *time_and_date(char *buf, char *end, void *ptr, 
struct printf_spec spec,
        switch (fmt[1]) {
        case 'R':
                return rtc_str(buf, end, (const struct rtc_time *)ptr, spec, 
fmt);
+       case 'S':
+               return timespec64_str(buf, end, (const struct timespec64 *)ptr, 
spec, fmt);
        case 'T':
                return time64_str(buf, end, *(const time64_t *)ptr, spec, fmt);
        default:
@@ -2456,9 +2480,11 @@ early_param("no_hash_pointers", no_hash_pointers_enable);
  * - '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[RT][dt][r][s]' For time and date as represented by:
+ * - 't[RST][dt][r][s]' For time and date as represented by:
  *      R    struct rtc_time
+ *      S    struct timespec64
  *      T    time64_t
+ * - 'tSp' For time represented by struct timespec64 printed as 
<seconds>.<nanoseconds>
  * - 'C' For a clock, it prints the name (Common Clock Framework) or address
  *       (legacy clock framework) of the clock
  * - 'G' For flags to be printed as a collection of symbolic strings that would
-- 
2.50.1

Reply via email to