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
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Test cases for sscanf facility.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/overflow.h>
#include <linux/printk.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "../tools/testing/selftests/kselftest_module.h"
#define BUF_SIZE 1024
KSTM_MODULE_GLOBALS();
static char *test_buffer __initdata;
static char *fmt_buffer __initdata;
static struct rnd_state rnd_state __initdata;
typedef int (*check_fn)(const void *check_data, const char *string,
const char *fmt, int n_args, va_list ap);
static void __scanf(4, 6) __init
_test(check_fn fn, const void *check_data, const char *string, const char *fmt,
int n_args, ...)
{
va_list ap, ap_copy;
int ret;
total_tests++;
va_start(ap, n_args);
va_copy(ap_copy, ap);
ret = vsscanf(string, fmt, ap_copy);
va_end(ap_copy);
if (ret != n_args) {
pr_warn("vsscanf(\"%s\", \"%s\", ...) returned %d expected %d\n",
string, fmt, ret, n_args);
goto fail;
}
ret = (*fn)(check_data, string, fmt, n_args, ap);
if (ret)
goto fail;
va_end(ap);
return;
fail:
failed_tests++;
va_end(ap);
}
#define _check_numbers_template(arg_fmt, expect, str, fmt, n_args, ap) \
do { \
pr_debug("\"%s\", \"%s\" ->\n", str, fmt); \
for (; n_args > 0; n_args--, expect++) { \
typeof(*expect) got = *va_arg(ap, typeof(expect)); \
pr_debug("\t" arg_fmt "\n", got); \
if (got != *expect) { \
pr_warn("vsscanf(\"%s\", \"%s\", ...) expected " arg_fmt " got " arg_fmt "\n", \
str, fmt, *expect, got); \
return 1; \
} \
} \
return 0; \
} while (0)
static int __init check_ull(const void *check_data, const char *string,
const char *fmt, int n_args, va_list ap)
{
const unsigned long long *pval = check_data;
_check_numbers_template("%llu", pval, string, fmt, n_args, ap);
}
static int __init check_ll(const void *check_data, const char *string,
const char *fmt, int n_args, va_list ap)
{
const long long *pval = check_data;
_check_numbers_template("%lld", pval, string, fmt, n_args, ap);
}
static int __init check_ulong(const void *check_data, const char *string,
const char *fmt, int n_args, va_list ap)
{
const unsigned long *pval = check_data;
_check_numbers_template("%lu", pval, string, fmt, n_args, ap);
}
static int __init check_long(const void *check_data, const char *string,
const char *fmt, int n_args, va_list ap)
{
const long *pval = check_data;
_check_numbers_template("%ld", pval, string, fmt, n_args, ap);
}
static int __init check_uint(const void *check_data, const char *string,
const char *fmt, int n_args, va_list ap)
{
const unsigned int *pval = check_data;
_check_numbers_template("%u", pval, string, fmt, n_args, ap);
}
static int __init check_int(const void *check_data, const char *string,
const char *fmt, int n_args, va_list ap)
{
const int *pval = check_data;
_check_numbers_template("%d", pval, string, fmt, n_args, ap);
}
static int __init check_ushort(const void *check_data, const char *string,
const char *fmt, int n_args, va_list ap)
{
const unsigned short *pval = check_data;
_check_numbers_template("%hu", pval, string, fmt, n_args, ap);
}
static int __init check_short(const void *check_data, const char *string,
const char *fmt, int n_args, va_list ap)
{
const short *pval = check_data;
_check_numbers_template("%hd", pval, string, fmt, n_args, ap);
}
static int __init check_uchar(const void *check_data, const char *string,
const char *fmt, int n_args, va_list ap)
{
const unsigned char *pval = check_data;
_check_numbers_template("%hhu", pval, string, fmt, n_args, ap);
}
static int __init check_char(const void *check_data, const char *string,
const char *fmt, int n_args, va_list ap)
{
const signed char *pval = check_data;
_check_numbers_template("%hhd", pval, string, fmt, n_args, ap);
}
/* Selection of interesting numbers to test, copied from test-kstrtox.c */
static const unsigned long long numbers[] __initconst = {
0x0ULL,
0x1ULL,
0x7fULL,
0x80ULL,
0x81ULL,
0xffULL,
0x100ULL,
0x101ULL,
0x7fffULL,
0x8000ULL,
0x8001ULL,
0xffffULL,
0x10000ULL,
0x10001ULL,
0x7fffffffULL,
0x80000000ULL,
0x80000001ULL,
0xffffffffULL,
0x100000000ULL,
0x100000001ULL,
0x7fffffffffffffffULL,
0x8000000000000000ULL,
0x8000000000000001ULL,
0xfffffffffffffffeULL,
0xffffffffffffffffULL,
};
#define value_representable_in_type(T, val) \
(is_signed_type(T) \
? ((long long)(val) >= type_min(T)) && ((long long)(val) <= type_max(T)) \
: ((unsigned long long)(val) <= type_max(T)))
#define test_one_number(T, gen_fmt, scan_fmt, val, fn) \
do { \
const T expect_val = (T)(val); \
T result = ~expect_val; /* should be overwritten */ \
\
snprintf(test_buffer, BUF_SIZE, gen_fmt, expect_val); \
_test(fn, &expect_val, test_buffer, "%" scan_fmt, 1, &result); \
} while (0)
#define simple_numbers_loop(T, gen_fmt, scan_fmt, fn) \
do { \
int i; \
\
for (i = 0; i < ARRAY_SIZE(numbers); i++) { \
if (value_representable_in_type(T, numbers[i])) \
test_one_number(T, gen_fmt, scan_fmt, \
numbers[i], fn); \
\
if (value_representable_in_type(T, -numbers[i])) \
test_one_number(T, gen_fmt, scan_fmt, \
-numbers[i], fn); \
} \
} while (0)
static void __init numbers_simple(void)
{
simple_numbers_loop(unsigned long long, "%llu", "llu", check_ull);
simple_numbers_loop(long long, "%lld", "lld", check_ll);
simple_numbers_loop(long long, "%lld", "lli", check_ll);
simple_numbers_loop(unsigned long long, "%llx", "llx", check_ull);
simple_numbers_loop(long long, "%llx", "llx", check_ll);
simple_numbers_loop(long long, "0x%llx", "lli", check_ll);
simple_numbers_loop(unsigned long long, "0x%llx", "llx", check_ull);
simple_numbers_loop(long long, "0x%llx", "llx", check_ll);
simple_numbers_loop(unsigned long, "%lu", "lu", check_ulong);
simple_numbers_loop(long, "%ld", "ld", check_long);
simple_numbers_loop(long, "%ld", "li", check_long);
simple_numbers_loop(unsigned long, "%lx", "lx", check_ulong);
simple_numbers_loop(long, "%lx", "lx", check_long);
simple_numbers_loop(long, "0x%lx", "li", check_long);
simple_numbers_loop(unsigned long, "0x%lx", "lx", check_ulong);
simple_numbers_loop(long, "0x%lx", "lx", check_long);
simple_numbers_loop(unsigned int, "%u", "u", check_uint);
simple_numbers_loop(int, "%d", "d", check_int);
simple_numbers_loop(int, "%d", "i", check_int);
simple_numbers_loop(unsigned int, "%x", "x", check_uint);
simple_numbers_loop(int, "%x", "x", check_int);
simple_numbers_loop(int, "0x%x", "i", check_int);
simple_numbers_loop(unsigned int, "0x%x", "x", check_uint);
simple_numbers_loop(int, "0x%x", "x", check_int);
simple_numbers_loop(unsigned short, "%hu", "hu", check_ushort);
simple_numbers_loop(short, "%hd", "hd", check_short);
simple_numbers_loop(short, "%hd", "hi", check_short);
simple_numbers_loop(unsigned short, "%hx", "hx", check_ushort);
simple_numbers_loop(short, "%hx", "hx", check_short);
simple_numbers_loop(short, "0x%hx", "hi", check_short);
simple_numbers_loop(unsigned short, "0x%hx", "hx", check_ushort);
simple_numbers_loop(short, "0x%hx", "hx", check_short);
simple_numbers_loop(unsigned char, "%hhu", "hhu", check_uchar);
simple_numbers_loop(signed char, "%hhd", "hhd", check_char);
simple_numbers_loop(signed char, "%hhd", "hhi", check_char);
simple_numbers_loop(unsigned char, "%hhx", "hhx", check_uchar);
simple_numbers_loop(signed char, "%hhx", "hhx", check_char);
simple_numbers_loop(signed char, "0x%hhx", "hhi", check_char);
simple_numbers_loop(unsigned char, "0x%hhx", "hhx", check_uchar);
simple_numbers_loop(signed char, "0x%hhx", "hhx", check_char);
}
/*
* This gives a better variety of number "lengths" in a small sample than
* the raw prandom*() functions (Not mathematically rigorous!!).
* Variabilty of length and value is more important than perfect randomness.
*/
static u32 __init next_test_random(u32 max_bits)
{
u32 n_bits = hweight32(prandom_u32_state(&rnd_state)) % (max_bits + 1);
return prandom_u32_state(&rnd_state) & (UINT_MAX >> (32 - n_bits));
}
static unsigned long long __init next_test_random_ull(void)
{
u32 rand1 = prandom_u32_state(&rnd_state);
u32 n_bits = (hweight32(rand1) * 3) % 64;
u64 val = (u64)prandom_u32_state(&rnd_state) * rand1;
return val & (ULLONG_MAX >> (64 - n_bits));
}
#define random_for_type(T) \
((T)(sizeof(T) <= sizeof(u32) \
? next_test_random(BITS_PER_TYPE(T)) \
: next_test_random_ull()))
/*
* Define a pattern of negative and positive numbers to ensure we get
* some of both within the small number of samples in a test string.
*/
#define NEGATIVES_PATTERN 0x3246 /* 00110010 01000110 */
#define fill_random_array(arr) \
do { \
unsigned int neg_pattern = NEGATIVES_PATTERN; \
int i; \
\
for (i = 0; i < ARRAY_SIZE(arr); i++, neg_pattern >>= 1) { \
(arr)[i] = random_for_type(typeof((arr)[0])); \
if (is_signed_type(typeof((arr)[0])) && (neg_pattern & 1)) \
(arr)[i] = -(arr)[i]; \
} \
} while (0)
/*
* Convenience wrapper around snprintf() to append at buf_pos in buf,
* updating buf_pos and returning the number of characters appended.
* On error buf_pos is not changed and return value is 0.
*/
static int __init __printf(4, 5)
append_fmt(char *buf, int *buf_pos, int buf_len, const char *val_fmt, ...)
{
va_list ap;
int field_len;
va_start(ap, val_fmt);
field_len = vsnprintf(buf + *buf_pos, buf_len - *buf_pos, val_fmt, ap);
va_end(ap);
if (field_len < 0)
field_len = 0;
*buf_pos += field_len;
return field_len;
}
/*
* Convenience function to append the field delimiter string
* to both the value string and format string buffers.
*/
static void __init append_delim(char *str_buf, int *str_buf_pos, int str_buf_len,
char *fmt_buf, int *fmt_buf_pos, int fmt_buf_len,
const char *delim_str)
{
append_fmt(str_buf, str_buf_pos, str_buf_len, delim_str);
append_fmt(fmt_buf, fmt_buf_pos, fmt_buf_len, delim_str);
}
#define test_array_8(fn, check_data, string, fmt, arr) \
do { \
BUILD_BUG_ON(ARRAY_SIZE(arr) != 8); \
_test(fn, check_data, string, fmt, 8, \
&(arr)[0], &(arr)[1], &(arr)[2], &(arr)[3], \
&(arr)[4], &(arr)[5], &(arr)[6], &(arr)[7]); \
} while (0)
#define numbers_list_8(T, gen_fmt, field_sep, scan_fmt, fn) \
do { \
int i, pos = 0, fmt_pos = 0; \
T expect[8], result[8]; \
\
fill_random_array(expect); \
\
for (i = 0; i < ARRAY_SIZE(expect); i++) { \
if (i != 0) \
append_delim(test_buffer, &pos, BUF_SIZE, \
fmt_buffer, &fmt_pos, BUF_SIZE, \
field_sep); \
\
append_fmt(test_buffer, &pos, BUF_SIZE, gen_fmt, expect[i]); \
append_fmt(fmt_buffer, &fmt_pos, BUF_SIZE, "%%%s", scan_fmt); \
} \
\
test_array_8(fn, expect, test_buffer, fmt_buffer, result); \
} while (0)
#define numbers_list_fix_width(T, gen_fmt, field_sep, width, scan_fmt, fn) \
do { \
char full_fmt[16]; \
\
snprintf(full_fmt, sizeof(full_fmt), "%u%s", width, scan_fmt); \
numbers_list_8(T, gen_fmt, field_sep, full_fmt, fn); \
} while (0)
#define numbers_list_val_width(T, gen_fmt, field_sep, scan_fmt, fn) \
do { \
int i, val_len, pos = 0, fmt_pos = 0; \
T expect[8], result[8]; \
\
fill_random_array(expect); \
\
for (i = 0; i < ARRAY_SIZE(expect); i++) { \
if (i != 0) \
append_delim(test_buffer, &pos, BUF_SIZE, \
fmt_buffer, &fmt_pos, BUF_SIZE, field_sep);\
\
val_len = append_fmt(test_buffer, &pos, BUF_SIZE, gen_fmt, \
expect[i]); \
append_fmt(fmt_buffer, &fmt_pos, BUF_SIZE, \
"%%%u%s", val_len, scan_fmt); \
} \
\
test_array_8(fn, expect, test_buffer, fmt_buffer, result); \
} while (0)
static void __init numbers_list(const char *delim)
{
numbers_list_8(unsigned long long, "%llu", delim, "llu", check_ull);
numbers_list_8(long long, "%lld", delim, "lld", check_ll);
numbers_list_8(long long, "%lld", delim, "lli", check_ll);
numbers_list_8(unsigned long long, "%llx", delim, "llx", check_ull);
numbers_list_8(unsigned long long, "0x%llx", delim, "llx", check_ull);
numbers_list_8(long long, "0x%llx", delim, "lli", check_ll);
numbers_list_8(unsigned long, "%lu", delim, "lu", check_ulong);
numbers_list_8(long, "%ld", delim, "ld", check_long);
numbers_list_8(long, "%ld", delim, "li", check_long);
numbers_list_8(unsigned long, "%lx", delim, "lx", check_ulong);
numbers_list_8(unsigned long, "0x%lx", delim, "lx", check_ulong);
numbers_list_8(long, "0x%lx", delim, "li", check_long);
numbers_list_8(unsigned int, "%u", delim, "u", check_uint);
numbers_list_8(int, "%d", delim, "d", check_int);
numbers_list_8(int, "%d", delim, "i", check_int);
numbers_list_8(unsigned int, "%x", delim, "x", check_uint);
numbers_list_8(unsigned int, "0x%x", delim, "x", check_uint);
numbers_list_8(int, "0x%x", delim, "i", check_int);
numbers_list_8(unsigned short, "%hu", delim, "hu", check_ushort);
numbers_list_8(short, "%hd", delim, "hd", check_short);
numbers_list_8(short, "%hd", delim, "hi", check_short);
numbers_list_8(unsigned short, "%hx", delim, "hx", check_ushort);
numbers_list_8(unsigned short, "0x%hx", delim, "hx", check_ushort);
numbers_list_8(short, "0x%hx", delim, "hi", check_short);
numbers_list_8(unsigned char, "%hhu", delim, "hhu", check_uchar);
numbers_list_8(signed char, "%hhd", delim, "hhd", check_char);
numbers_list_8(signed char, "%hhd", delim, "hhi", check_char);
numbers_list_8(unsigned char, "%hhx", delim, "hhx", check_uchar);
numbers_list_8(unsigned char, "0x%hhx", delim, "hhx", check_uchar);
numbers_list_8(signed char, "0x%hhx", delim, "hhi", check_char);
}
/*
* List of numbers separated by delim. Each field width specifier is the
* maximum possible digits for the given type and base.
*/
static void __init numbers_list_field_width_typemax(const char *delim)
{
numbers_list_fix_width(unsigned long long, "%llu", delim, 20, "llu", check_ull);
numbers_list_fix_width(long long, "%lld", delim, 20, "lld", check_ll);
numbers_list_fix_width(long long, "%lld", delim, 20, "lli", check_ll);
numbers_list_fix_width(unsigned long long, "%llx", delim, 16, "llx", check_ull);
numbers_list_fix_width(unsigned long long, "0x%llx", delim, 18, "llx", check_ull);
numbers_list_fix_width(long long, "0x%llx", delim, 18, "lli", check_ll);
#if BITS_PER_LONG == 64
numbers_list_fix_width(unsigned long, "%lu", delim, 20, "lu", check_ulong);
numbers_list_fix_width(long, "%ld", delim, 20, "ld", check_long);
numbers_list_fix_width(long, "%ld", delim, 20, "li", check_long);
numbers_list_fix_width(unsigned long, "%lx", delim, 16, "lx", check_ulong);
numbers_list_fix_width(unsigned long, "0x%lx", delim, 18, "lx", check_ulong);
numbers_list_fix_width(long, "0x%lx", delim, 18, "li", check_long);
#else
numbers_list_fix_width(unsigned long, "%lu", delim, 10, "lu", check_ulong);
numbers_list_fix_width(long, "%ld", delim, 11, "ld", check_long);
numbers_list_fix_width(long, "%ld", delim, 11, "li", check_long);
numbers_list_fix_width(unsigned long, "%lx", delim, 8, "lx", check_ulong);
numbers_list_fix_width(unsigned long, "0x%lx", delim, 10, "lx", check_ulong);
numbers_list_fix_width(long, "0x%lx", delim, 10, "li", check_long);
#endif
numbers_list_fix_width(unsigned int, "%u", delim, 10, "u", check_uint);
numbers_list_fix_width(int, "%d", delim, 11, "d", check_int);
numbers_list_fix_width(int, "%d", delim, 11, "i", check_int);
numbers_list_fix_width(unsigned int, "%x", delim, 8, "x", check_uint);
numbers_list_fix_width(unsigned int, "0x%x", delim, 10, "x", check_uint);
numbers_list_fix_width(int, "0x%x", delim, 10, "i", check_int);
numbers_list_fix_width(unsigned short, "%hu", delim, 5, "hu", check_ushort);
numbers_list_fix_width(short, "%hd", delim, 6, "hd", check_short);
numbers_list_fix_width(short, "%hd", delim, 6, "hi", check_short);
numbers_list_fix_width(unsigned short, "%hx", delim, 4, "hx", check_ushort);
numbers_list_fix_width(unsigned short, "0x%hx", delim, 6, "hx", check_ushort);
numbers_list_fix_width(short, "0x%hx", delim, 6, "hi", check_short);
numbers_list_fix_width(unsigned char, "%hhu", delim, 3, "hhu", check_uchar);
numbers_list_fix_width(signed char, "%hhd", delim, 4, "hhd", check_char);
numbers_list_fix_width(signed char, "%hhd", delim, 4, "hhi", check_char);
numbers_list_fix_width(unsigned char, "%hhx", delim, 2, "hhx", check_uchar);
numbers_list_fix_width(unsigned char, "0x%hhx", delim, 4, "hhx", check_uchar);
numbers_list_fix_width(signed char, "0x%hhx", delim, 4, "hhi", check_char);
}
/*
* List of numbers separated by delim. Each field width specifier is the
* exact length of the corresponding value digits in the string being scanned.
*/
static void __init numbers_list_field_width_val_width(const char *delim)
{
numbers_list_val_width(unsigned long long, "%llu", delim, "llu", check_ull);
numbers_list_val_width(long long, "%lld", delim, "lld", check_ll);
numbers_list_val_width(long long, "%lld", delim, "lli", check_ll);
numbers_list_val_width(unsigned long long, "%llx", delim, "llx", check_ull);
numbers_list_val_width(unsigned long long, "0x%llx", delim, "llx", check_ull);
numbers_list_val_width(long long, "0x%llx", delim, "lli", check_ll);
numbers_list_val_width(unsigned long, "%lu", delim, "lu", check_ulong);
numbers_list_val_width(long, "%ld", delim, "ld", check_long);
numbers_list_val_width(long, "%ld", delim, "li", check_long);
numbers_list_val_width(unsigned long, "%lx", delim, "lx", check_ulong);
numbers_list_val_width(unsigned long, "0x%lx", delim, "lx", check_ulong);
numbers_list_val_width(long, "0x%lx", delim, "li", check_long);
numbers_list_val_width(unsigned int, "%u", delim, "u", check_uint);
numbers_list_val_width(int, "%d", delim, "d", check_int);
numbers_list_val_width(int, "%d", delim, "i", check_int);
numbers_list_val_width(unsigned int, "%x", delim, "x", check_uint);
numbers_list_val_width(unsigned int, "0x%x", delim, "x", check_uint);
numbers_list_val_width(int, "0x%x", delim, "i", check_int);
numbers_list_val_width(unsigned short, "%hu", delim, "hu", check_ushort);
numbers_list_val_width(short, "%hd", delim, "hd", check_short);
numbers_list_val_width(short, "%hd", delim, "hi", check_short);
numbers_list_val_width(unsigned short, "%hx", delim, "hx", check_ushort);
numbers_list_val_width(unsigned short, "0x%hx", delim, "hx", check_ushort);
numbers_list_val_width(short, "0x%hx", delim, "hi", check_short);
numbers_list_val_width(unsigned char, "%hhu", delim, "hhu", check_uchar);
numbers_list_val_width(signed char, "%hhd", delim, "hhd", check_char);
numbers_list_val_width(signed char, "%hhd", delim, "hhi", check_char);
numbers_list_val_width(unsigned char, "%hhx", delim, "hhx", check_uchar);
numbers_list_val_width(unsigned char, "0x%hhx", delim, "hhx", check_uchar);
numbers_list_val_width(signed char, "0x%hhx", delim, "hhi", check_char);
}
/*
* Slice a continuous string of digits without field delimiters, containing
* numbers of varying length, using the field width to extract each group
* of digits. For example the hex values c0,3,bf01,303 would have a
* string representation of "c03bf01303" and extracted with "%2x%1x%4x%3x".
*/
static void __init numbers_slice(void)
{
numbers_list_field_width_val_width("");
}
#define test_number_prefix(T, str, scan_fmt, expect0, expect1, n_args, fn) \
do { \
const T expect[2] = { expect0, expect1 }; \
T result[2] = {~expect[0], ~expect[1]}; \
\
_test(fn, &expect, str, scan_fmt, n_args, &result[0], &result[1]); \
} while (0)
/*
* Number prefix is >= field width.
* Expected behaviour is derived from testing userland sscanf.
*/
static void __init numbers_prefix_overflow(void)
{
/*
* Negative decimal with a field of width 1, should quit scanning
* and return 0.
*/
test_number_prefix(long long, "-1 1", "%1lld %lld", 0, 0, 0, check_ll);
test_number_prefix(long, "-1 1", "%1ld %ld", 0, 0, 0, check_long);
test_number_prefix(int, "-1 1", "%1d %d", 0, 0, 0, check_int);
test_number_prefix(short, "-1 1", "%1hd %hd", 0, 0, 0, check_short);
test_number_prefix(signed char, "-1 1", "%1hhd %hhd", 0, 0, 0, check_char);
test_number_prefix(long long, "-1 1", "%1lli %lli", 0, 0, 0, check_ll);
test_number_prefix(long, "-1 1", "%1li %li", 0, 0, 0, check_long);
test_number_prefix(int, "-1 1", "%1i %i", 0, 0, 0, check_int);
test_number_prefix(short, "-1 1", "%1hi %hi", 0, 0, 0, check_short);
test_number_prefix(signed char, "-1 1", "%1hhi %hhi", 0, 0, 0, check_char);
/*
* 0x prefix in a field of width 1: 0 is a valid digit so should
* convert. Next field scan starts at the 'x' which isn't a digit so
* scan quits with one field converted.
*/
test_number_prefix(unsigned long long, "0xA7", "%1llx%llx", 0, 0, 1, check_ull);
test_number_prefix(unsigned long, "0xA7", "%1lx%lx", 0, 0, 1, check_ulong);
test_number_prefix(unsigned int, "0xA7", "%1x%x", 0, 0, 1, check_uint);
test_number_prefix(unsigned short, "0xA7", "%1hx%hx", 0, 0, 1, check_ushort);
test_number_prefix(unsigned char, "0xA7", "%1hhx%hhx", 0, 0, 1, check_uchar);
test_number_prefix(long long, "0xA7", "%1lli%llx", 0, 0, 1, check_ll);
test_number_prefix(long, "0xA7", "%1li%lx", 0, 0, 1, check_long);
test_number_prefix(int, "0xA7", "%1i%x", 0, 0, 1, check_int);
test_number_prefix(short, "0xA7", "%1hi%hx", 0, 0, 1, check_short);
test_number_prefix(char, "0xA7", "%1hhi%hhx", 0, 0, 1, check_char);
/*
* 0x prefix in a field of width 2 using %x conversion: first field
* converts to 0. Next field scan starts at the character after "0x".
* Both fields will convert.
*/
test_number_prefix(unsigned long long, "0xA7", "%2llx%llx", 0, 0xa7, 2, check_ull);
test_number_prefix(unsigned long, "0xA7", "%2lx%lx", 0, 0xa7, 2, check_ulong);
test_number_prefix(unsigned int, "0xA7", "%2x%x", 0, 0xa7, 2, check_uint);
test_number_prefix(unsigned short, "0xA7", "%2hx%hx", 0, 0xa7, 2, check_ushort);
test_number_prefix(unsigned char, "0xA7", "%2hhx%hhx", 0, 0xa7, 2, check_uchar);
/*
* 0x prefix in a field of width 2 using %i conversion: first field
* converts to 0. Next field scan starts at the character after "0x",
* which will convert if can be intepreted as decimal but will fail
* if it contains any hex digits (since no 0x prefix).
*/
test_number_prefix(long long, "0x67", "%2lli%lli", 0, 67, 2, check_ll);
test_number_prefix(long, "0x67", "%2li%li", 0, 67, 2, check_long);
test_number_prefix(int, "0x67", "%2i%i", 0, 67, 2, check_int);
test_number_prefix(short, "0x67", "%2hi%hi", 0, 67, 2, check_short);
test_number_prefix(char, "0x67", "%2hhi%hhi", 0, 67, 2, check_char);
test_number_prefix(long long, "0xA7", "%2lli%lli", 0, 0, 1, check_ll);
test_number_prefix(long, "0xA7", "%2li%li", 0, 0, 1, check_long);
test_number_prefix(int, "0xA7", "%2i%i", 0, 0, 1, check_int);
test_number_prefix(short, "0xA7", "%2hi%hi", 0, 0, 1, check_short);
test_number_prefix(char, "0xA7", "%2hhi%hhi", 0, 0, 1, check_char);
}
#define _test_simple_strtoxx(T, fn, gen_fmt, expect, base) \
do { \
T got; \
char *endp; \
int len; \
bool fail = false; \
\
total_tests++; \
len = snprintf(test_buffer, BUF_SIZE, gen_fmt, expect); \
got = (fn)(test_buffer, &endp, base); \
pr_debug(#fn "(\"%s\", %d) -> " gen_fmt "\n", test_buffer, base, got); \
if (got != (expect)) { \
fail = true; \
pr_warn(#fn "(\"%s\", %d): got " gen_fmt " expected " gen_fmt "\n", \
test_buffer, base, got, expect); \
} else if (endp != test_buffer + len) { \
fail = true; \
pr_warn(#fn "(\"%s\", %d) startp=0x%px got endp=0x%px expected 0x%px\n", \
test_buffer, base, test_buffer, \
test_buffer + len, endp); \
} \
\
if (fail) \
failed_tests++; \
} while (0)
#define test_simple_strtoxx(T, fn, gen_fmt, base) \
do { \
int i; \
\
for (i = 0; i < ARRAY_SIZE(numbers); i++) { \
_test_simple_strtoxx(T, fn, gen_fmt, (T)numbers[i], base); \
\
if (is_signed_type(T)) \
_test_simple_strtoxx(T, fn, gen_fmt, \
-(T)numbers[i], base); \
} \
} while (0)
static void __init test_simple_strtoull(void)
{
test_simple_strtoxx(unsigned long long, simple_strtoull, "%llu", 10);
test_simple_strtoxx(unsigned long long, simple_strtoull, "%llu", 0);
test_simple_strtoxx(unsigned long long, simple_strtoull, "%llx", 16);
test_simple_strtoxx(unsigned long long, simple_strtoull, "0x%llx", 16);
test_simple_strtoxx(unsigned long long, simple_strtoull, "0x%llx", 0);
}
static void __init test_simple_strtoll(void)
{
test_simple_strtoxx(long long, simple_strtoll, "%lld", 10);
test_simple_strtoxx(long long, simple_strtoll, "%lld", 0);
test_simple_strtoxx(long long, simple_strtoll, "%llx", 16);
test_simple_strtoxx(long long, simple_strtoll, "0x%llx", 16);
test_simple_strtoxx(long long, simple_strtoll, "0x%llx", 0);
}
static void __init test_simple_strtoul(void)
{
test_simple_strtoxx(unsigned long, simple_strtoul, "%lu", 10);
test_simple_strtoxx(unsigned long, simple_strtoul, "%lu", 0);
test_simple_strtoxx(unsigned long, simple_strtoul, "%lx", 16);
test_simple_strtoxx(unsigned long, simple_strtoul, "0x%lx", 16);
test_simple_strtoxx(unsigned long, simple_strtoul, "0x%lx", 0);
}
static void __init test_simple_strtol(void)
{
test_simple_strtoxx(long, simple_strtol, "%ld", 10);
test_simple_strtoxx(long, simple_strtol, "%ld", 0);
test_simple_strtoxx(long, simple_strtol, "%lx", 16);
test_simple_strtoxx(long, simple_strtol, "0x%lx", 16);
test_simple_strtoxx(long, simple_strtol, "0x%lx", 0);
}
/* Selection of common delimiters/separators between numbers in a string. */
static const char * const number_delimiters[] __initconst = {
" ", ":", ",", "-", "/",
};
static void __init test_numbers(void)
{
int i;
/* String containing only one number. */
numbers_simple();
/* String with multiple numbers separated by delimiter. */
for (i = 0; i < ARRAY_SIZE(number_delimiters); i++) {
numbers_list(number_delimiters[i]);
/* Field width may be longer than actual field digits. */
numbers_list_field_width_typemax(number_delimiters[i]);
/* Each field width exactly length of actual field digits. */
numbers_list_field_width_val_width(number_delimiters[i]);
}
/* Slice continuous sequence of digits using field widths. */
numbers_slice();
numbers_prefix_overflow();
}
static void __init selftest(void)
{
test_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!test_buffer)
return;
fmt_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!fmt_buffer) {
kfree(test_buffer);
return;
}
prandom_seed_state(&rnd_state, 3141592653589793238ULL);
test_numbers();
test_simple_strtoull();
test_simple_strtoll();
test_simple_strtoul();
test_simple_strtol();
kfree(fmt_buffer);
kfree(test_buffer);
}
KSTM_MODULE_LOADERS(test_scanf);
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("GPL v2");
...@@ -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