Commit e563592c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'printk-for-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux

Pull printk updates from Petr Mladek:

 - Add %pt[RT]s modifier to vsprintf(). It overrides ISO 8601 separator
   by using ' ' (space). It produces "YYYY-mm-dd HH:MM:SS" instead of
   "YYYY-mm-ddTHH:MM:SS".

 - Correctly parse long row of numbers by sscanf() when using the field
   width. Add extensive sscanf() selftest.

 - Generalize re-entrant CPU lock that has already been used to
   serialize dump_stack() output. It is part of the ongoing printk
   rework. It will allow to remove the obsoleted printk_safe buffers and
   introduce atomic consoles.

 - Some code clean up and sparse warning fixes.

* tag 'printk-for-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux:
  printk: fix cpu lock ordering
  lib/dump_stack: move cpu lock to printk.c
  printk: Remove trailing semicolon in macros
  random32: Fix implicit truncation warning in prandom_seed_state()
  lib: test_scanf: Remove pointless use of type_min() with unsigned types
  selftests: lib: Add wrapper script for test_scanf
  lib: test_scanf: Add tests for sscanf number conversion
  lib: vsprintf: Fix handling of number field widths in vsscanf
  lib: vsprintf: scanf: Negative number must have field width > 1
  usb: host: xhci-tegra: Switch to use %ptTs
  nilfs2: Switch to use %ptTs
  kdb: Switch to use %ptTs
  lib/vsprintf: Allow to override ISO 8601 date and time separator
parents b694011a 94f2be50
...@@ -513,9 +513,10 @@ Time and date ...@@ -513,9 +513,10 @@ Time and date
:: ::
%pt[RT] YYYY-mm-ddTHH:MM:SS %pt[RT] YYYY-mm-ddTHH:MM:SS
%pt[RT]s YYYY-mm-dd HH:MM:SS
%pt[RT]d YYYY-mm-dd %pt[RT]d YYYY-mm-dd
%pt[RT]t HH:MM:SS %pt[RT]t HH:MM:SS
%pt[RT][dt][r] %pt[RT][dt][r][s]
For printing date and time as represented by:: For printing date and time as represented by::
...@@ -527,6 +528,10 @@ in human readable format. ...@@ -527,6 +528,10 @@ in human readable format.
By default year will be incremented by 1900 and month by 1. By default year will be incremented by 1900 and month by 1.
Use %pt[RT]r (raw) to suppress this behaviour. Use %pt[RT]r (raw) to suppress this behaviour.
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.
Passed by reference. Passed by reference.
struct clk struct clk
......
...@@ -19629,6 +19629,7 @@ S: Maintained ...@@ -19629,6 +19629,7 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pmladek/printk.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/pmladek/printk.git
F: Documentation/core-api/printk-formats.rst F: Documentation/core-api/printk-formats.rst
F: lib/test_printf.c F: lib/test_printf.c
F: lib/test_scanf.c
F: lib/vsprintf.c F: lib/vsprintf.c
VT1211 HARDWARE MONITOR DRIVER VT1211 HARDWARE MONITOR DRIVER
......
...@@ -917,7 +917,6 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) ...@@ -917,7 +917,6 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
struct xhci_op_regs __iomem *op; struct xhci_op_regs __iomem *op;
unsigned long timeout; unsigned long timeout;
time64_t timestamp; time64_t timestamp;
struct tm time;
u64 address; u64 address;
u32 value; u32 value;
int err; int err;
...@@ -1014,11 +1013,8 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) ...@@ -1014,11 +1013,8 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
} }
timestamp = le32_to_cpu(header->fwimg_created_time); timestamp = le32_to_cpu(header->fwimg_created_time);
time64_to_tm(timestamp, 0, &time);
dev_info(dev, "Firmware timestamp: %ld-%02d-%02d %02d:%02d:%02d UTC\n", dev_info(dev, "Firmware timestamp: %ptTs UTC\n", &timestamp);
time.tm_year + 1900, time.tm_mon + 1, time.tm_mday,
time.tm_hour, time.tm_min, time.tm_sec);
return 0; return 0;
} }
......
...@@ -19,19 +19,6 @@ ...@@ -19,19 +19,6 @@
/* /sys/fs/<nilfs>/ */ /* /sys/fs/<nilfs>/ */
static struct kset *nilfs_kset; static struct kset *nilfs_kset;
#define NILFS_SHOW_TIME(time_t_val, buf) ({ \
struct tm res; \
int count = 0; \
time64_to_tm(time_t_val, 0, &res); \
res.tm_year += 1900; \
res.tm_mon += 1; \
count = scnprintf(buf, PAGE_SIZE, \
"%ld-%.2d-%.2d %.2d:%.2d:%.2d\n", \
res.tm_year, res.tm_mon, res.tm_mday, \
res.tm_hour, res.tm_min, res.tm_sec);\
count; \
})
#define NILFS_DEV_INT_GROUP_OPS(name, parent_name) \ #define NILFS_DEV_INT_GROUP_OPS(name, parent_name) \
static ssize_t nilfs_##name##_attr_show(struct kobject *kobj, \ static ssize_t nilfs_##name##_attr_show(struct kobject *kobj, \
struct attribute *attr, char *buf) \ struct attribute *attr, char *buf) \
...@@ -576,7 +563,7 @@ nilfs_segctor_last_seg_write_time_show(struct nilfs_segctor_attr *attr, ...@@ -576,7 +563,7 @@ nilfs_segctor_last_seg_write_time_show(struct nilfs_segctor_attr *attr,
ctime = nilfs->ns_ctime; ctime = nilfs->ns_ctime;
up_read(&nilfs->ns_segctor_sem); up_read(&nilfs->ns_segctor_sem);
return NILFS_SHOW_TIME(ctime, buf); return sysfs_emit(buf, "%ptTs\n", &ctime);
} }
static ssize_t static ssize_t
...@@ -604,7 +591,7 @@ nilfs_segctor_last_nongc_write_time_show(struct nilfs_segctor_attr *attr, ...@@ -604,7 +591,7 @@ nilfs_segctor_last_nongc_write_time_show(struct nilfs_segctor_attr *attr,
nongc_ctime = nilfs->ns_nongc_ctime; nongc_ctime = nilfs->ns_nongc_ctime;
up_read(&nilfs->ns_segctor_sem); up_read(&nilfs->ns_segctor_sem);
return NILFS_SHOW_TIME(nongc_ctime, buf); return sysfs_emit(buf, "%ptTs\n", &nongc_ctime);
} }
static ssize_t static ssize_t
...@@ -724,7 +711,7 @@ nilfs_superblock_sb_write_time_show(struct nilfs_superblock_attr *attr, ...@@ -724,7 +711,7 @@ nilfs_superblock_sb_write_time_show(struct nilfs_superblock_attr *attr,
sbwtime = nilfs->ns_sbwtime; sbwtime = nilfs->ns_sbwtime;
up_read(&nilfs->ns_sem); up_read(&nilfs->ns_sem);
return NILFS_SHOW_TIME(sbwtime, buf); return sysfs_emit(buf, "%ptTs\n", &sbwtime);
} }
static ssize_t static ssize_t
......
...@@ -236,7 +236,7 @@ do { \ ...@@ -236,7 +236,7 @@ do { \
* using WARN/WARN_ONCE to include file/line information and a backtrace. * using WARN/WARN_ONCE to include file/line information and a backtrace.
*/ */
#define dev_WARN(dev, format, arg...) \ #define dev_WARN(dev, format, arg...) \
WARN(1, "%s %s: " format, dev_driver_string(dev), dev_name(dev), ## arg); WARN(1, "%s %s: " format, dev_driver_string(dev), dev_name(dev), ## arg)
#define dev_WARN_ONCE(dev, condition, format, arg...) \ #define dev_WARN_ONCE(dev, condition, format, arg...) \
WARN_ONCE(condition, "%s %s: " format, \ WARN_ONCE(condition, "%s %s: " format, \
......
...@@ -111,7 +111,7 @@ static inline u32 __seed(u32 x, u32 m) ...@@ -111,7 +111,7 @@ static inline u32 __seed(u32 x, u32 m)
*/ */
static inline void prandom_seed_state(struct rnd_state *state, u64 seed) static inline void prandom_seed_state(struct rnd_state *state, u64 seed)
{ {
u32 i = (seed >> 32) ^ (seed << 10) ^ seed; u32 i = ((seed >> 32) ^ (seed << 10) ^ seed) & 0xffffffffUL;
state->s1 = __seed(i, 2U); state->s1 = __seed(i, 2U);
state->s2 = __seed(i, 8U); state->s2 = __seed(i, 8U);
......
...@@ -282,6 +282,47 @@ static inline void printk_safe_flush_on_panic(void) ...@@ -282,6 +282,47 @@ static inline void printk_safe_flush_on_panic(void)
} }
#endif #endif
#ifdef CONFIG_SMP
extern int __printk_cpu_trylock(void);
extern void __printk_wait_on_cpu_lock(void);
extern void __printk_cpu_unlock(void);
/**
* printk_cpu_lock_irqsave() - Acquire the printk cpu-reentrant spinning
* lock and disable interrupts.
* @flags: Stack-allocated storage for saving local interrupt state,
* to be passed to printk_cpu_unlock_irqrestore().
*
* If the lock is owned by another CPU, spin until it becomes available.
* Interrupts are restored while spinning.
*/
#define printk_cpu_lock_irqsave(flags) \
for (;;) { \
local_irq_save(flags); \
if (__printk_cpu_trylock()) \
break; \
local_irq_restore(flags); \
__printk_wait_on_cpu_lock(); \
}
/**
* printk_cpu_unlock_irqrestore() - Release the printk cpu-reentrant spinning
* lock and restore interrupts.
* @flags: Caller's saved interrupt state, from printk_cpu_lock_irqsave().
*/
#define printk_cpu_unlock_irqrestore(flags) \
do { \
__printk_cpu_unlock(); \
local_irq_restore(flags); \
} while (0) \
#else
#define printk_cpu_lock_irqsave(flags) ((void)flags)
#define printk_cpu_unlock_irqrestore(flags) ((void)flags)
#endif /* CONFIG_SMP */
extern int kptr_restrict; extern int kptr_restrict;
/** /**
......
...@@ -2488,7 +2488,6 @@ static void kdb_sysinfo(struct sysinfo *val) ...@@ -2488,7 +2488,6 @@ static void kdb_sysinfo(struct sysinfo *val)
static int kdb_summary(int argc, const char **argv) static int kdb_summary(int argc, const char **argv)
{ {
time64_t now; time64_t now;
struct tm tm;
struct sysinfo val; struct sysinfo val;
if (argc) if (argc)
...@@ -2502,13 +2501,7 @@ static int kdb_summary(int argc, const char **argv) ...@@ -2502,13 +2501,7 @@ static int kdb_summary(int argc, const char **argv)
kdb_printf("domainname %s\n", init_uts_ns.name.domainname); kdb_printf("domainname %s\n", init_uts_ns.name.domainname);
now = __ktime_get_real_seconds(); now = __ktime_get_real_seconds();
time64_to_tm(now, 0, &tm); kdb_printf("date %ptTs tz_minuteswest %d\n", &now, sys_tz.tz_minuteswest);
kdb_printf("date %04ld-%02d-%02d %02d:%02d:%02d "
"tz_minuteswest %d\n",
1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
sys_tz.tz_minuteswest);
kdb_sysinfo(&val); kdb_sysinfo(&val);
kdb_printf("uptime "); kdb_printf("uptime ");
if (val.uptime > (24*60*60)) { if (val.uptime > (24*60*60)) {
......
...@@ -3531,3 +3531,119 @@ void kmsg_dump_rewind(struct kmsg_dump_iter *iter) ...@@ -3531,3 +3531,119 @@ void kmsg_dump_rewind(struct kmsg_dump_iter *iter)
EXPORT_SYMBOL_GPL(kmsg_dump_rewind); EXPORT_SYMBOL_GPL(kmsg_dump_rewind);
#endif #endif
#ifdef CONFIG_SMP
static atomic_t printk_cpulock_owner = ATOMIC_INIT(-1);
static atomic_t printk_cpulock_nested = ATOMIC_INIT(0);
/**
* __printk_wait_on_cpu_lock() - Busy wait until the printk cpu-reentrant
* spinning lock is not owned by any CPU.
*
* Context: Any context.
*/
void __printk_wait_on_cpu_lock(void)
{
do {
cpu_relax();
} while (atomic_read(&printk_cpulock_owner) != -1);
}
EXPORT_SYMBOL(__printk_wait_on_cpu_lock);
/**
* __printk_cpu_trylock() - Try to acquire the printk cpu-reentrant
* spinning lock.
*
* If no processor has the lock, the calling processor takes the lock and
* becomes the owner. If the calling processor is already the owner of the
* lock, this function succeeds immediately.
*
* Context: Any context. Expects interrupts to be disabled.
* Return: 1 on success, otherwise 0.
*/
int __printk_cpu_trylock(void)
{
int cpu;
int old;
cpu = smp_processor_id();
/*
* Guarantee loads and stores from this CPU when it is the lock owner
* are _not_ visible to the previous lock owner. This pairs with
* __printk_cpu_unlock:B.
*
* Memory barrier involvement:
*
* If __printk_cpu_trylock:A reads from __printk_cpu_unlock:B, then
* __printk_cpu_unlock:A can never read from __printk_cpu_trylock:B.
*
* Relies on:
*
* RELEASE from __printk_cpu_unlock:A to __printk_cpu_unlock:B
* of the previous CPU
* matching
* ACQUIRE from __printk_cpu_trylock:A to __printk_cpu_trylock:B
* of this CPU
*/
old = atomic_cmpxchg_acquire(&printk_cpulock_owner, -1,
cpu); /* LMM(__printk_cpu_trylock:A) */
if (old == -1) {
/*
* This CPU is now the owner and begins loading/storing
* data: LMM(__printk_cpu_trylock:B)
*/
return 1;
} else if (old == cpu) {
/* This CPU is already the owner. */
atomic_inc(&printk_cpulock_nested);
return 1;
}
return 0;
}
EXPORT_SYMBOL(__printk_cpu_trylock);
/**
* __printk_cpu_unlock() - Release the printk cpu-reentrant spinning lock.
*
* The calling processor must be the owner of the lock.
*
* Context: Any context. Expects interrupts to be disabled.
*/
void __printk_cpu_unlock(void)
{
if (atomic_read(&printk_cpulock_nested)) {
atomic_dec(&printk_cpulock_nested);
return;
}
/*
* This CPU is finished loading/storing data:
* LMM(__printk_cpu_unlock:A)
*/
/*
* Guarantee loads and stores from this CPU when it was the
* lock owner are visible to the next lock owner. This pairs
* with __printk_cpu_trylock:A.
*
* Memory barrier involvement:
*
* If __printk_cpu_trylock:A reads from __printk_cpu_unlock:B,
* then __printk_cpu_trylock:B reads from __printk_cpu_unlock:A.
*
* Relies on:
*
* RELEASE from __printk_cpu_unlock:A to __printk_cpu_unlock:B
* of this CPU
* matching
* ACQUIRE from __printk_cpu_trylock:A to __printk_cpu_trylock:B
* of the next CPU
*/
atomic_set_release(&printk_cpulock_owner,
-1); /* LMM(__printk_cpu_unlock:B) */
}
EXPORT_SYMBOL(__printk_cpu_unlock);
#endif /* CONFIG_SMP */
...@@ -2180,6 +2180,9 @@ config TEST_KSTRTOX ...@@ -2180,6 +2180,9 @@ config TEST_KSTRTOX
config TEST_PRINTF config TEST_PRINTF
tristate "Test printf() family of functions at runtime" tristate "Test printf() family of functions at runtime"
config TEST_SCANF
tristate "Test scanf() family of functions at runtime"
config TEST_BITMAP config TEST_BITMAP
tristate "Test bitmap_*() family of functions at runtime" tristate "Test bitmap_*() family of functions at runtime"
help help
......
...@@ -83,6 +83,7 @@ obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o ...@@ -83,6 +83,7 @@ obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_keys.o obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_keys.o
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o
obj-$(CONFIG_TEST_PRINTF) += test_printf.o obj-$(CONFIG_TEST_PRINTF) += test_printf.o
obj-$(CONFIG_TEST_SCANF) += test_scanf.o
obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o
obj-$(CONFIG_TEST_STRSCPY) += test_strscpy.o obj-$(CONFIG_TEST_STRSCPY) += test_strscpy.o
obj-$(CONFIG_TEST_UUID) += test_uuid.o obj-$(CONFIG_TEST_UUID) += test_uuid.o
......
...@@ -84,50 +84,16 @@ static void __dump_stack(void) ...@@ -84,50 +84,16 @@ static void __dump_stack(void)
* *
* Architectures can override this implementation by implementing its own. * Architectures can override this implementation by implementing its own.
*/ */
#ifdef CONFIG_SMP
static atomic_t dump_lock = ATOMIC_INIT(-1);
asmlinkage __visible void dump_stack(void) asmlinkage __visible void dump_stack(void)
{ {
unsigned long flags; unsigned long flags;
int was_locked;
int old;
int cpu;
/* /*
* Permit this cpu to perform nested stack dumps while serialising * Permit this cpu to perform nested stack dumps while serialising
* against other CPUs * against other CPUs
*/ */
retry: printk_cpu_lock_irqsave(flags);
local_irq_save(flags);
cpu = smp_processor_id();
old = atomic_cmpxchg(&dump_lock, -1, cpu);
if (old == -1) {
was_locked = 0;
} else if (old == cpu) {
was_locked = 1;
} else {
local_irq_restore(flags);
/*
* Wait for the lock to release before jumping to
* atomic_cmpxchg() in order to mitigate the thundering herd
* problem.
*/
do { cpu_relax(); } while (atomic_read(&dump_lock) != -1);
goto retry;
}
__dump_stack();
if (!was_locked)
atomic_set(&dump_lock, -1);
local_irq_restore(flags);
}
#else
asmlinkage __visible void dump_stack(void)
{
__dump_stack(); __dump_stack();
printk_cpu_unlock_irqrestore(flags);
} }
#endif
EXPORT_SYMBOL(dump_stack); EXPORT_SYMBOL(dump_stack);
...@@ -39,20 +39,22 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) ...@@ -39,20 +39,22 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
/* /*
* Convert non-negative integer string representation in explicitly given radix * Convert non-negative integer string representation in explicitly given radix
* to an integer. * to an integer. A maximum of max_chars characters will be converted.
*
* Return number of characters consumed maybe or-ed with overflow bit. * Return number of characters consumed maybe or-ed with overflow bit.
* If overflow occurs, result integer (incorrect) is still returned. * If overflow occurs, result integer (incorrect) is still returned.
* *
* Don't you dare use this function. * Don't you dare use this function.
*/ */
unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p) unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned long long *p,
size_t max_chars)
{ {
unsigned long long res; unsigned long long res;
unsigned int rv; unsigned int rv;
res = 0; res = 0;
rv = 0; rv = 0;
while (1) { while (max_chars--) {
unsigned int c = *s; unsigned int c = *s;
unsigned int lc = c | 0x20; /* don't tolower() this line */ unsigned int lc = c | 0x20; /* don't tolower() this line */
unsigned int val; unsigned int val;
...@@ -82,6 +84,11 @@ unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long ...@@ -82,6 +84,11 @@ unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long
return rv; return rv;
} }
unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p)
{
return _parse_integer_limit(s, base, p, INT_MAX);
}
static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
{ {
unsigned long long _res; unsigned long long _res;
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#define KSTRTOX_OVERFLOW (1U << 31) #define KSTRTOX_OVERFLOW (1U << 31)
const char *_parse_integer_fixup_radix(const char *s, unsigned int *base); const char *_parse_integer_fixup_radix(const char *s, unsigned int *base);
unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned long long *res,
size_t max_chars);
unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res); unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res);
#endif #endif
...@@ -528,6 +528,11 @@ time_and_date(void) ...@@ -528,6 +528,11 @@ time_and_date(void)
test("0119-00-04T15:32:23", "%ptTr", &t); test("0119-00-04T15:32:23", "%ptTr", &t);
test("15:32:23|2019-01-04", "%ptTt|%ptTd", &t, &t); test("15:32:23|2019-01-04", "%ptTt|%ptTd", &t, &t);
test("15:32:23|0119-00-04", "%ptTtr|%ptTdr", &t, &t); test("15:32:23|0119-00-04", "%ptTtr|%ptTdr", &t, &t);
test("2019-01-04 15:32:23", "%ptTs", &t);
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);
} }
static void __init static void __init
......
This diff is collapsed.
...@@ -53,6 +53,31 @@ ...@@ -53,6 +53,31 @@
#include <linux/string_helpers.h> #include <linux/string_helpers.h>
#include "kstrtox.h" #include "kstrtox.h"
static unsigned long long simple_strntoull(const char *startp, size_t max_chars,
char **endp, unsigned int base)
{
const char *cp;
unsigned long long result = 0ULL;
size_t prefix_chars;
unsigned int rv;
cp = _parse_integer_fixup_radix(startp, &base);
prefix_chars = cp - startp;
if (prefix_chars < max_chars) {
rv = _parse_integer_limit(cp, base, &result, max_chars - prefix_chars);
/* FIXME */
cp += (rv & ~KSTRTOX_OVERFLOW);
} else {
/* Field too short for prefix + digit, skip over without converting */
cp = startp + max_chars;
}
if (endp)
*endp = (char *)cp;
return result;
}
/** /**
* simple_strtoull - convert a string to an unsigned long long * simple_strtoull - convert a string to an unsigned long long
* @cp: The start of the string * @cp: The start of the string
...@@ -63,18 +88,7 @@ ...@@ -63,18 +88,7 @@
*/ */
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base) unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
{ {
unsigned long long result; return simple_strntoull(cp, INT_MAX, endp, base);
unsigned int rv;
cp = _parse_integer_fixup_radix(cp, &base);
rv = _parse_integer(cp, base, &result);
/* FIXME */
cp += (rv & ~KSTRTOX_OVERFLOW);
if (endp)
*endp = (char *)cp;
return result;
} }
EXPORT_SYMBOL(simple_strtoull); EXPORT_SYMBOL(simple_strtoull);
...@@ -109,6 +123,21 @@ long simple_strtol(const char *cp, char **endp, unsigned int base) ...@@ -109,6 +123,21 @@ long simple_strtol(const char *cp, char **endp, unsigned int base)
} }
EXPORT_SYMBOL(simple_strtol); EXPORT_SYMBOL(simple_strtol);
static long long simple_strntoll(const char *cp, size_t max_chars, char **endp,
unsigned int base)
{
/*
* simple_strntoull() safely handles receiving max_chars==0 in the
* case cp[0] == '-' && max_chars == 1.
* If max_chars == 0 we can drop through and pass it to simple_strntoull()
* and the content of *cp is irrelevant.
*/
if (*cp == '-' && max_chars > 0)
return -simple_strntoull(cp + 1, max_chars - 1, endp, base);
return simple_strntoull(cp, max_chars, endp, base);
}
/** /**
* simple_strtoll - convert a string to a signed long long * simple_strtoll - convert a string to a signed long long
* @cp: The start of the string * @cp: The start of the string
...@@ -119,10 +148,7 @@ EXPORT_SYMBOL(simple_strtol); ...@@ -119,10 +148,7 @@ EXPORT_SYMBOL(simple_strtol);
*/ */
long long simple_strtoll(const char *cp, char **endp, unsigned int base) long long simple_strtoll(const char *cp, char **endp, unsigned int base)
{ {
if (*cp == '-') return simple_strntoll(cp, INT_MAX, endp, base);
return -simple_strtoull(cp + 1, endp, base);
return simple_strtoull(cp, endp, base);
} }
EXPORT_SYMBOL(simple_strtoll); EXPORT_SYMBOL(simple_strtoll);
...@@ -1834,7 +1860,8 @@ char *rtc_str(char *buf, char *end, const struct rtc_time *tm, ...@@ -1834,7 +1860,8 @@ char *rtc_str(char *buf, char *end, const struct rtc_time *tm,
struct printf_spec spec, const char *fmt) struct printf_spec spec, const char *fmt)
{ {
bool have_t = true, have_d = true; bool have_t = true, have_d = true;
bool raw = false; bool raw = false, iso8601_separator = true;
bool found = true;
int count = 2; int count = 2;
if (check_pointer(&buf, end, tm, spec)) if (check_pointer(&buf, end, tm, spec))
...@@ -1851,14 +1878,25 @@ char *rtc_str(char *buf, char *end, const struct rtc_time *tm, ...@@ -1851,14 +1878,25 @@ char *rtc_str(char *buf, char *end, const struct rtc_time *tm,
break; break;
} }
raw = fmt[count] == 'r'; do {
switch (fmt[count++]) {
case 'r':
raw = true;
break;
case 's':
iso8601_separator = false;
break;
default:
found = false;
break;
}
} while (found);
if (have_d) if (have_d)
buf = date_str(buf, end, tm, raw); buf = date_str(buf, end, tm, raw);
if (have_d && have_t) { if (have_d && have_t) {
/* Respect ISO 8601 */
if (buf < end) if (buf < end)
*buf = 'T'; *buf = iso8601_separator ? 'T' : ' ';
buf++; buf++;
} }
if (have_t) if (have_t)
...@@ -2298,7 +2336,7 @@ early_param("no_hash_pointers", no_hash_pointers_enable); ...@@ -2298,7 +2336,7 @@ early_param("no_hash_pointers", no_hash_pointers_enable);
* - 'd[234]' For a dentry name (optionally 2-4 last components) * - 'd[234]' For a dentry name (optionally 2-4 last components)
* - 'D[234]' Same as 'd' but for a struct file * - 'D[234]' Same as 'd' but for a struct file
* - 'g' For block_device name (gendisk + partition number) * - 'g' For block_device name (gendisk + partition number)
* - 't[RT][dt][r]' For time and date as represented by: * - 't[RT][dt][r][s]' For time and date as represented by:
* R struct rtc_time * R struct rtc_time
* T time64_t * T time64_t
* - 'C' For a clock, it prints the name (Common Clock Framework) or address * - 'C' For a clock, it prints the name (Common Clock Framework) or address
...@@ -3565,8 +3603,12 @@ int vsscanf(const char *buf, const char *fmt, va_list args) ...@@ -3565,8 +3603,12 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
str = skip_spaces(str); str = skip_spaces(str);
digit = *str; digit = *str;
if (is_sign && digit == '-') if (is_sign && digit == '-') {
if (field_width == 1)
break;
digit = *(str + 1); digit = *(str + 1);
}
if (!digit if (!digit
|| (base == 16 && !isxdigit(digit)) || (base == 16 && !isxdigit(digit))
...@@ -3576,25 +3618,13 @@ int vsscanf(const char *buf, const char *fmt, va_list args) ...@@ -3576,25 +3618,13 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
break; break;
if (is_sign) if (is_sign)
val.s = qualifier != 'L' ? val.s = simple_strntoll(str,
simple_strtol(str, &next, base) : field_width >= 0 ? field_width : INT_MAX,
simple_strtoll(str, &next, base); &next, base);
else else
val.u = qualifier != 'L' ? val.u = simple_strntoull(str,
simple_strtoul(str, &next, base) : field_width >= 0 ? field_width : INT_MAX,
simple_strtoull(str, &next, base); &next, base);
if (field_width > 0 && next - str > field_width) {
if (base == 0)
_parse_integer_fixup_radix(str, &base);
while (next - str > field_width) {
if (is_sign)
val.s = div_s64(val.s, base);
else
val.u = div_u64(val.u, base);
--next;
}
}
switch (qualifier) { switch (qualifier) {
case 'H': /* that's 'hh' in format */ case 'H': /* that's 'hh' in format */
......
...@@ -4,6 +4,6 @@ ...@@ -4,6 +4,6 @@
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests" # No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
all: all:
TEST_PROGS := printf.sh bitmap.sh prime_numbers.sh strscpy.sh TEST_PROGS := printf.sh bitmap.sh prime_numbers.sh scanf.sh strscpy.sh
include ../lib.mk include ../lib.mk
CONFIG_TEST_PRINTF=m CONFIG_TEST_PRINTF=m
CONFIG_TEST_SCANF=m
CONFIG_TEST_BITMAP=m CONFIG_TEST_BITMAP=m
CONFIG_PRIME_NUMBERS=m CONFIG_PRIME_NUMBERS=m
CONFIG_TEST_STRSCPY=m CONFIG_TEST_STRSCPY=m
......
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Tests the scanf infrastructure using test_scanf kernel module.
$(dirname $0)/../kselftest/module.sh "scanf" test_scanf
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment