Tracing updates:
Change in API of bootconfig (before it comes live in a release) - Have a magic value "BOOTCONFIG" in initrd to know a bootconfig exists - Set CONFIG_BOOT_CONFIG to 'n' by default - Show error if "bootconfig" on cmdline but not compiled in - Prevent redefining the same value - Have a way to append values - Added a SELECT BLK_DEV_INITRD to fix a build failure Synthetic event fixes: - Switch to raw_smp_processor_id() for recording CPU value in preempt section. (No care for what the value actually is) - Fix samples always recording u64 values - Fix endianess - Check number of values matches number of fields - Fix a printing bug Fix of trace_printk() breaking postponed start up tests Make a function static that is only used in a single file. -----BEGIN PGP SIGNATURE----- iIoEABYIADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCXlW4vxQccm9zdGVkdEBn b29kbWlzLm9yZwAKCRAp5XQQmuv6qtioAP0WLEm3dWO0z3321h/a0DSshC+Bslu3 HDPTsGVGrXmvggEA/lr1ikRHd8PsO7zW8BfaZMxoXaTqXiuSrzEWxnMlFw0= =O8PM -----END PGP SIGNATURE----- Merge tag 'trace-v5.6-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace Pull tracing and bootconfig updates: "Fixes and changes to bootconfig before it goes live in a release. Change in API of bootconfig (before it comes live in a release): - Have a magic value "BOOTCONFIG" in initrd to know a bootconfig exists - Set CONFIG_BOOT_CONFIG to 'n' by default - Show error if "bootconfig" on cmdline but not compiled in - Prevent redefining the same value - Have a way to append values - Added a SELECT BLK_DEV_INITRD to fix a build failure Synthetic event fixes: - Switch to raw_smp_processor_id() for recording CPU value in preempt section. (No care for what the value actually is) - Fix samples always recording u64 values - Fix endianess - Check number of values matches number of fields - Fix a printing bug Fix of trace_printk() breaking postponed start up tests Make a function static that is only used in a single file" * tag 'trace-v5.6-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: bootconfig: Fix CONFIG_BOOTTIME_TRACING dependency issue bootconfig: Add append value operator support bootconfig: Prohibit re-defining value on same key bootconfig: Print array as multiple commands for legacy command line bootconfig: Reject subkey and value on same parent key tools/bootconfig: Remove unneeded error message silencer bootconfig: Add bootconfig magic word for indicating bootconfig explicitly bootconfig: Set CONFIG_BOOT_CONFIG=n by default tracing: Clear trace_state when starting trace bootconfig: Mark boot_config_checksum() static tracing: Disable trace_printk() on post poned tests tracing: Have synthetic event test use raw_smp_processor_id() tracing: Fix number printing bug in print_synth_event() tracing: Check that number of vals matches number of synth event fields tracing: Make synth_event trace functions endian-correct tracing: Make sure synth_event_trace() example always uses u64
This commit is contained in:
Коммит
91ad64a84e
|
@ -62,6 +62,30 @@ Or more shorter, written as following::
|
|||
In both styles, same key words are automatically merged when parsing it
|
||||
at boot time. So you can append similar trees or key-values.
|
||||
|
||||
Same-key Values
|
||||
---------------
|
||||
|
||||
It is prohibited that two or more values or arrays share a same-key.
|
||||
For example,::
|
||||
|
||||
foo = bar, baz
|
||||
foo = qux # !ERROR! we can not re-define same key
|
||||
|
||||
If you want to append the value to existing key as an array member,
|
||||
you can use ``+=`` operator. For example::
|
||||
|
||||
foo = bar, baz
|
||||
foo += qux
|
||||
|
||||
In this case, the key ``foo`` has ``bar``, ``baz`` and ``qux``.
|
||||
|
||||
However, a sub-key and a value can not co-exist under a parent key.
|
||||
For example, following config is NOT allowed.::
|
||||
|
||||
foo = value1
|
||||
foo.bar = value2 # !ERROR! subkey "bar" and value "value1" can NOT co-exist
|
||||
|
||||
|
||||
Comments
|
||||
--------
|
||||
|
||||
|
@ -102,9 +126,13 @@ Boot Kernel With a Boot Config
|
|||
==============================
|
||||
|
||||
Since the boot configuration file is loaded with initrd, it will be added
|
||||
to the end of the initrd (initramfs) image file. The Linux kernel decodes
|
||||
the last part of the initrd image in memory to get the boot configuration
|
||||
data.
|
||||
to the end of the initrd (initramfs) image file with size, checksum and
|
||||
12-byte magic word as below.
|
||||
|
||||
[initrd][bootconfig][size(u32)][checksum(u32)][#BOOTCONFIG\n]
|
||||
|
||||
The Linux kernel decodes the last part of the initrd image in memory to
|
||||
get the boot configuration data.
|
||||
Because of this "piggyback" method, there is no need to change or
|
||||
update the boot loader and the kernel image itself.
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BOOTCONFIG_MAGIC "#BOOTCONFIG\n"
|
||||
#define BOOTCONFIG_MAGIC_LEN 12
|
||||
|
||||
/* XBC tree node */
|
||||
struct xbc_node {
|
||||
u16 next;
|
||||
|
|
|
@ -1226,13 +1226,12 @@ endif
|
|||
|
||||
config BOOT_CONFIG
|
||||
bool "Boot config support"
|
||||
depends on BLK_DEV_INITRD
|
||||
default y
|
||||
select BLK_DEV_INITRD
|
||||
help
|
||||
Extra boot config allows system admin to pass a config file as
|
||||
complemental extension of kernel cmdline when booting.
|
||||
The boot config file must be attached at the end of initramfs
|
||||
with checksum and size.
|
||||
with checksum, size and magic word.
|
||||
See <file:Documentation/admin-guide/bootconfig.rst> for details.
|
||||
|
||||
If unsure, say Y.
|
||||
|
|
42
init/main.c
42
init/main.c
|
@ -268,7 +268,6 @@ static int __init xbc_snprint_cmdline(char *buf, size_t size,
|
|||
{
|
||||
struct xbc_node *knode, *vnode;
|
||||
char *end = buf + size;
|
||||
char c = '\"';
|
||||
const char *val;
|
||||
int ret;
|
||||
|
||||
|
@ -279,25 +278,20 @@ static int __init xbc_snprint_cmdline(char *buf, size_t size,
|
|||
return ret;
|
||||
|
||||
vnode = xbc_node_get_child(knode);
|
||||
ret = snprintf(buf, rest(buf, end), "%s%c", xbc_namebuf,
|
||||
vnode ? '=' : ' ');
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
buf += ret;
|
||||
if (!vnode)
|
||||
continue;
|
||||
|
||||
c = '\"';
|
||||
xbc_array_for_each_value(vnode, val) {
|
||||
ret = snprintf(buf, rest(buf, end), "%c%s", c, val);
|
||||
if (!vnode) {
|
||||
ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
buf += ret;
|
||||
continue;
|
||||
}
|
||||
xbc_array_for_each_value(vnode, val) {
|
||||
ret = snprintf(buf, rest(buf, end), "%s=\"%s\" ",
|
||||
xbc_namebuf, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
buf += ret;
|
||||
c = ',';
|
||||
}
|
||||
if (rest(buf, end) > 2)
|
||||
strcpy(buf, "\" ");
|
||||
buf += 2;
|
||||
}
|
||||
|
||||
return buf - (end - size);
|
||||
|
@ -335,7 +329,7 @@ static char * __init xbc_make_cmdline(const char *key)
|
|||
return new_cmdline;
|
||||
}
|
||||
|
||||
u32 boot_config_checksum(unsigned char *p, u32 size)
|
||||
static u32 boot_config_checksum(unsigned char *p, u32 size)
|
||||
{
|
||||
u32 ret = 0;
|
||||
|
||||
|
@ -374,7 +368,11 @@ static void __init setup_boot_config(const char *cmdline)
|
|||
if (!initrd_end)
|
||||
goto not_found;
|
||||
|
||||
hdr = (u32 *)(initrd_end - 8);
|
||||
data = (char *)initrd_end - BOOTCONFIG_MAGIC_LEN;
|
||||
if (memcmp(data, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN))
|
||||
goto not_found;
|
||||
|
||||
hdr = (u32 *)(data - 8);
|
||||
size = hdr[0];
|
||||
csum = hdr[1];
|
||||
|
||||
|
@ -418,6 +416,14 @@ not_found:
|
|||
}
|
||||
#else
|
||||
#define setup_boot_config(cmdline) do { } while (0)
|
||||
|
||||
static int __init warn_bootconfig(char *str)
|
||||
{
|
||||
pr_warn("WARNING: 'bootconfig' found on the kernel command line but CONFIG_BOOTCONFIG is not set.\n");
|
||||
return 0;
|
||||
}
|
||||
early_param("bootconfig", warn_bootconfig);
|
||||
|
||||
#endif
|
||||
|
||||
/* Change NUL term back to "=", to make "param" the whole string. */
|
||||
|
|
|
@ -143,8 +143,8 @@ if FTRACE
|
|||
|
||||
config BOOTTIME_TRACING
|
||||
bool "Boot-time Tracing support"
|
||||
depends on BOOT_CONFIG && TRACING
|
||||
default y
|
||||
depends on TRACING
|
||||
select BOOT_CONFIG
|
||||
help
|
||||
Enable developer to setup ftrace subsystem via supplemental
|
||||
kernel cmdline at boot time for debugging (tracing) driver
|
||||
|
|
|
@ -111,11 +111,11 @@ static int __init test_gen_synth_cmd(void)
|
|||
/* Create some bogus values just for testing */
|
||||
|
||||
vals[0] = 777; /* next_pid_field */
|
||||
vals[1] = (u64)"hula hoops"; /* next_comm_field */
|
||||
vals[1] = (u64)(long)"hula hoops"; /* next_comm_field */
|
||||
vals[2] = 1000000; /* ts_ns */
|
||||
vals[3] = 1000; /* ts_ms */
|
||||
vals[4] = smp_processor_id(); /* cpu */
|
||||
vals[5] = (u64)"thneed"; /* my_string_field */
|
||||
vals[4] = raw_smp_processor_id(); /* cpu */
|
||||
vals[5] = (u64)(long)"thneed"; /* my_string_field */
|
||||
vals[6] = 598; /* my_int_field */
|
||||
|
||||
/* Now generate a gen_synth_test event */
|
||||
|
@ -218,11 +218,11 @@ static int __init test_empty_synth_event(void)
|
|||
/* Create some bogus values just for testing */
|
||||
|
||||
vals[0] = 777; /* next_pid_field */
|
||||
vals[1] = (u64)"tiddlywinks"; /* next_comm_field */
|
||||
vals[1] = (u64)(long)"tiddlywinks"; /* next_comm_field */
|
||||
vals[2] = 1000000; /* ts_ns */
|
||||
vals[3] = 1000; /* ts_ms */
|
||||
vals[4] = smp_processor_id(); /* cpu */
|
||||
vals[5] = (u64)"thneed_2.0"; /* my_string_field */
|
||||
vals[4] = raw_smp_processor_id(); /* cpu */
|
||||
vals[5] = (u64)(long)"thneed_2.0"; /* my_string_field */
|
||||
vals[6] = 399; /* my_int_field */
|
||||
|
||||
/* Now trace an empty_synth_test event */
|
||||
|
@ -290,11 +290,11 @@ static int __init test_create_synth_event(void)
|
|||
/* Create some bogus values just for testing */
|
||||
|
||||
vals[0] = 777; /* next_pid_field */
|
||||
vals[1] = (u64)"tiddlywinks"; /* next_comm_field */
|
||||
vals[1] = (u64)(long)"tiddlywinks"; /* next_comm_field */
|
||||
vals[2] = 1000000; /* ts_ns */
|
||||
vals[3] = 1000; /* ts_ms */
|
||||
vals[4] = smp_processor_id(); /* cpu */
|
||||
vals[5] = (u64)"thneed"; /* my_string_field */
|
||||
vals[4] = raw_smp_processor_id(); /* cpu */
|
||||
vals[5] = (u64)(long)"thneed"; /* my_string_field */
|
||||
vals[6] = 398; /* my_int_field */
|
||||
|
||||
/* Now generate a create_synth_test event */
|
||||
|
@ -330,7 +330,7 @@ static int __init test_add_next_synth_val(void)
|
|||
goto out;
|
||||
|
||||
/* next_comm_field */
|
||||
ret = synth_event_add_next_val((u64)"slinky", &trace_state);
|
||||
ret = synth_event_add_next_val((u64)(long)"slinky", &trace_state);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -345,12 +345,12 @@ static int __init test_add_next_synth_val(void)
|
|||
goto out;
|
||||
|
||||
/* cpu */
|
||||
ret = synth_event_add_next_val(smp_processor_id(), &trace_state);
|
||||
ret = synth_event_add_next_val(raw_smp_processor_id(), &trace_state);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* my_string_field */
|
||||
ret = synth_event_add_next_val((u64)"thneed_2.01", &trace_state);
|
||||
ret = synth_event_add_next_val((u64)(long)"thneed_2.01", &trace_state);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -388,7 +388,7 @@ static int __init test_add_synth_val(void)
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = synth_event_add_val("cpu", smp_processor_id(), &trace_state);
|
||||
ret = synth_event_add_val("cpu", raw_smp_processor_id(), &trace_state);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -396,12 +396,12 @@ static int __init test_add_synth_val(void)
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = synth_event_add_val("next_comm_field", (u64)"silly putty",
|
||||
ret = synth_event_add_val("next_comm_field", (u64)(long)"silly putty",
|
||||
&trace_state);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = synth_event_add_val("my_string_field", (u64)"thneed_9",
|
||||
ret = synth_event_add_val("my_string_field", (u64)(long)"thneed_9",
|
||||
&trace_state);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
@ -423,13 +423,13 @@ static int __init test_trace_synth_event(void)
|
|||
|
||||
/* Trace some bogus values just for testing */
|
||||
ret = synth_event_trace(create_synth_test, 7, /* number of values */
|
||||
444, /* next_pid_field */
|
||||
(u64)"clackers", /* next_comm_field */
|
||||
1000000, /* ts_ns */
|
||||
1000, /* ts_ms */
|
||||
smp_processor_id(), /* cpu */
|
||||
(u64)"Thneed", /* my_string_field */
|
||||
999); /* my_int_field */
|
||||
(u64)444, /* next_pid_field */
|
||||
(u64)(long)"clackers", /* next_comm_field */
|
||||
(u64)1000000, /* ts_ns */
|
||||
(u64)1000, /* ts_ms */
|
||||
(u64)raw_smp_processor_id(), /* cpu */
|
||||
(u64)(long)"Thneed", /* my_string_field */
|
||||
(u64)999); /* my_int_field */
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -1837,6 +1837,7 @@ static __init int init_trace_selftests(void)
|
|||
|
||||
pr_info("Running postponed tracer tests:\n");
|
||||
|
||||
tracing_selftest_running = true;
|
||||
list_for_each_entry_safe(p, n, &postponed_selftests, list) {
|
||||
/* This loop can take minutes when sanitizers are enabled, so
|
||||
* lets make sure we allow RCU processing.
|
||||
|
@ -1859,6 +1860,7 @@ static __init int init_trace_selftests(void)
|
|||
list_del(&p->list);
|
||||
kfree(p);
|
||||
}
|
||||
tracing_selftest_running = false;
|
||||
|
||||
out:
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
|
|
@ -821,6 +821,29 @@ static const char *synth_field_fmt(char *type)
|
|||
return fmt;
|
||||
}
|
||||
|
||||
static void print_synth_event_num_val(struct trace_seq *s,
|
||||
char *print_fmt, char *name,
|
||||
int size, u64 val, char *space)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
trace_seq_printf(s, print_fmt, name, (u8)val, space);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
trace_seq_printf(s, print_fmt, name, (u16)val, space);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
trace_seq_printf(s, print_fmt, name, (u32)val, space);
|
||||
break;
|
||||
|
||||
default:
|
||||
trace_seq_printf(s, print_fmt, name, val, space);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static enum print_line_t print_synth_event(struct trace_iterator *iter,
|
||||
int flags,
|
||||
struct trace_event *event)
|
||||
|
@ -859,10 +882,13 @@ static enum print_line_t print_synth_event(struct trace_iterator *iter,
|
|||
} else {
|
||||
struct trace_print_flags __flags[] = {
|
||||
__def_gfpflag_names, {-1, NULL} };
|
||||
char *space = (i == se->n_fields - 1 ? "" : " ");
|
||||
|
||||
trace_seq_printf(s, print_fmt, se->fields[i]->name,
|
||||
entry->fields[n_u64],
|
||||
i == se->n_fields - 1 ? "" : " ");
|
||||
print_synth_event_num_val(s, print_fmt,
|
||||
se->fields[i]->name,
|
||||
se->fields[i]->size,
|
||||
entry->fields[n_u64],
|
||||
space);
|
||||
|
||||
if (strcmp(se->fields[i]->type, "gfp_t") == 0) {
|
||||
trace_seq_puts(s, " (");
|
||||
|
@ -1805,6 +1831,8 @@ __synth_event_trace_start(struct trace_event_file *file,
|
|||
int entry_size, fields_size = 0;
|
||||
int ret = 0;
|
||||
|
||||
memset(trace_state, '\0', sizeof(*trace_state));
|
||||
|
||||
/*
|
||||
* Normal event tracing doesn't get called at all unless the
|
||||
* ENABLED bit is set (which attaches the probe thus allowing
|
||||
|
@ -1885,6 +1913,11 @@ int synth_event_trace(struct trace_event_file *file, unsigned int n_vals, ...)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (n_vals != state.event->n_fields) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
va_start(args, n_vals);
|
||||
for (i = 0, n_u64 = 0; i < state.event->n_fields; i++) {
|
||||
u64 val;
|
||||
|
@ -1898,12 +1931,30 @@ int synth_event_trace(struct trace_event_file *file, unsigned int n_vals, ...)
|
|||
strscpy(str_field, str_val, STR_VAR_LEN_MAX);
|
||||
n_u64 += STR_VAR_LEN_MAX / sizeof(u64);
|
||||
} else {
|
||||
state.entry->fields[n_u64] = val;
|
||||
struct synth_field *field = state.event->fields[i];
|
||||
|
||||
switch (field->size) {
|
||||
case 1:
|
||||
*(u8 *)&state.entry->fields[n_u64] = (u8)val;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*(u16 *)&state.entry->fields[n_u64] = (u16)val;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
*(u32 *)&state.entry->fields[n_u64] = (u32)val;
|
||||
break;
|
||||
|
||||
default:
|
||||
state.entry->fields[n_u64] = val;
|
||||
break;
|
||||
}
|
||||
n_u64++;
|
||||
}
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
out:
|
||||
__synth_event_trace_end(&state);
|
||||
|
||||
return ret;
|
||||
|
@ -1942,6 +1993,11 @@ int synth_event_trace_array(struct trace_event_file *file, u64 *vals,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (n_vals != state.event->n_fields) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0, n_u64 = 0; i < state.event->n_fields; i++) {
|
||||
if (state.event->fields[i]->is_string) {
|
||||
char *str_val = (char *)(long)vals[i];
|
||||
|
@ -1950,11 +2006,30 @@ int synth_event_trace_array(struct trace_event_file *file, u64 *vals,
|
|||
strscpy(str_field, str_val, STR_VAR_LEN_MAX);
|
||||
n_u64 += STR_VAR_LEN_MAX / sizeof(u64);
|
||||
} else {
|
||||
state.entry->fields[n_u64] = vals[i];
|
||||
struct synth_field *field = state.event->fields[i];
|
||||
u64 val = vals[i];
|
||||
|
||||
switch (field->size) {
|
||||
case 1:
|
||||
*(u8 *)&state.entry->fields[n_u64] = (u8)val;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*(u16 *)&state.entry->fields[n_u64] = (u16)val;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
*(u32 *)&state.entry->fields[n_u64] = (u32)val;
|
||||
break;
|
||||
|
||||
default:
|
||||
state.entry->fields[n_u64] = val;
|
||||
break;
|
||||
}
|
||||
n_u64++;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
__synth_event_trace_end(&state);
|
||||
|
||||
return ret;
|
||||
|
@ -1997,8 +2072,6 @@ int synth_event_trace_start(struct trace_event_file *file,
|
|||
if (!trace_state)
|
||||
return -EINVAL;
|
||||
|
||||
memset(trace_state, '\0', sizeof(*trace_state));
|
||||
|
||||
ret = __synth_event_trace_start(file, trace_state);
|
||||
if (ret == -ENOENT)
|
||||
ret = 0; /* just disabled, not really an error */
|
||||
|
@ -2069,8 +2142,25 @@ static int __synth_event_add_val(const char *field_name, u64 val,
|
|||
|
||||
str_field = (char *)&entry->fields[field->offset];
|
||||
strscpy(str_field, str_val, STR_VAR_LEN_MAX);
|
||||
} else
|
||||
entry->fields[field->offset] = val;
|
||||
} else {
|
||||
switch (field->size) {
|
||||
case 1:
|
||||
*(u8 *)&trace_state->entry->fields[field->offset] = (u8)val;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*(u16 *)&trace_state->entry->fields[field->offset] = (u16)val;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
*(u32 *)&trace_state->entry->fields[field->offset] = (u32)val;
|
||||
break;
|
||||
|
||||
default:
|
||||
trace_state->entry->fields[field->offset] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -533,7 +533,7 @@ struct xbc_node *find_match_node(struct xbc_node *node, char *k)
|
|||
|
||||
static int __init __xbc_add_key(char *k)
|
||||
{
|
||||
struct xbc_node *node;
|
||||
struct xbc_node *node, *child;
|
||||
|
||||
if (!xbc_valid_keyword(k))
|
||||
return xbc_parse_error("Invalid keyword", k);
|
||||
|
@ -543,8 +543,12 @@ static int __init __xbc_add_key(char *k)
|
|||
|
||||
if (!last_parent) /* the first level */
|
||||
node = find_match_node(xbc_nodes, k);
|
||||
else
|
||||
node = find_match_node(xbc_node_get_child(last_parent), k);
|
||||
else {
|
||||
child = xbc_node_get_child(last_parent);
|
||||
if (child && xbc_node_is_value(child))
|
||||
return xbc_parse_error("Subkey is mixed with value", k);
|
||||
node = find_match_node(child, k);
|
||||
}
|
||||
|
||||
if (node)
|
||||
last_parent = node;
|
||||
|
@ -574,10 +578,10 @@ static int __init __xbc_parse_keys(char *k)
|
|||
return __xbc_add_key(k);
|
||||
}
|
||||
|
||||
static int __init xbc_parse_kv(char **k, char *v)
|
||||
static int __init xbc_parse_kv(char **k, char *v, int op)
|
||||
{
|
||||
struct xbc_node *prev_parent = last_parent;
|
||||
struct xbc_node *node;
|
||||
struct xbc_node *child;
|
||||
char *next;
|
||||
int c, ret;
|
||||
|
||||
|
@ -585,12 +589,19 @@ static int __init xbc_parse_kv(char **k, char *v)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
child = xbc_node_get_child(last_parent);
|
||||
if (child) {
|
||||
if (xbc_node_is_key(child))
|
||||
return xbc_parse_error("Value is mixed with subkey", v);
|
||||
else if (op == '=')
|
||||
return xbc_parse_error("Value is redefined", v);
|
||||
}
|
||||
|
||||
c = __xbc_parse_value(&v, &next);
|
||||
if (c < 0)
|
||||
return c;
|
||||
|
||||
node = xbc_add_sibling(v, XBC_VALUE);
|
||||
if (!node)
|
||||
if (!xbc_add_sibling(v, XBC_VALUE))
|
||||
return -ENOMEM;
|
||||
|
||||
if (c == ',') { /* Array */
|
||||
|
@ -763,7 +774,7 @@ int __init xbc_init(char *buf)
|
|||
|
||||
p = buf;
|
||||
do {
|
||||
q = strpbrk(p, "{}=;\n#");
|
||||
q = strpbrk(p, "{}=+;\n#");
|
||||
if (!q) {
|
||||
p = skip_spaces(p);
|
||||
if (*p != '\0')
|
||||
|
@ -774,8 +785,15 @@ int __init xbc_init(char *buf)
|
|||
c = *q;
|
||||
*q++ = '\0';
|
||||
switch (c) {
|
||||
case '+':
|
||||
if (*q++ != '=') {
|
||||
ret = xbc_parse_error("Wrong '+' operator",
|
||||
q - 2);
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
case '=':
|
||||
ret = xbc_parse_kv(&p, q);
|
||||
ret = xbc_parse_kv(&p, q, c);
|
||||
break;
|
||||
case '{':
|
||||
ret = xbc_open_brace(&p, q);
|
||||
|
|
|
@ -4,10 +4,7 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
/* controllable printf */
|
||||
extern int pr_output;
|
||||
#define printk(fmt, ...) \
|
||||
(pr_output ? printf(fmt, ##__VA_ARGS__) : 0)
|
||||
#define printk(fmt, ...) printf(fmt, ##__VA_ARGS__)
|
||||
|
||||
#define pr_err printk
|
||||
#define pr_warn printk
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/bootconfig.h>
|
||||
|
||||
int pr_output = 1;
|
||||
|
||||
static int xbc_show_array(struct xbc_node *node)
|
||||
{
|
||||
const char *val;
|
||||
|
@ -131,15 +129,26 @@ int load_xbc_from_initrd(int fd, char **buf)
|
|||
struct stat stat;
|
||||
int ret;
|
||||
u32 size = 0, csum = 0, rcsum;
|
||||
char magic[BOOTCONFIG_MAGIC_LEN];
|
||||
|
||||
ret = fstat(fd, &stat);
|
||||
if (ret < 0)
|
||||
return -errno;
|
||||
|
||||
if (stat.st_size < 8)
|
||||
if (stat.st_size < 8 + BOOTCONFIG_MAGIC_LEN)
|
||||
return 0;
|
||||
|
||||
if (lseek(fd, -8, SEEK_END) < 0) {
|
||||
if (lseek(fd, -BOOTCONFIG_MAGIC_LEN, SEEK_END) < 0) {
|
||||
pr_err("Failed to lseek: %d\n", -errno);
|
||||
return -errno;
|
||||
}
|
||||
if (read(fd, magic, BOOTCONFIG_MAGIC_LEN) < 0)
|
||||
return -errno;
|
||||
/* Check the bootconfig magic bytes */
|
||||
if (memcmp(magic, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN) != 0)
|
||||
return 0;
|
||||
|
||||
if (lseek(fd, -(8 + BOOTCONFIG_MAGIC_LEN), SEEK_END) < 0) {
|
||||
pr_err("Failed to lseek: %d\n", -errno);
|
||||
return -errno;
|
||||
}
|
||||
|
@ -150,11 +159,14 @@ int load_xbc_from_initrd(int fd, char **buf)
|
|||
if (read(fd, &csum, sizeof(u32)) < 0)
|
||||
return -errno;
|
||||
|
||||
/* Wrong size, maybe no boot config here */
|
||||
if (stat.st_size < size + 8)
|
||||
return 0;
|
||||
/* Wrong size error */
|
||||
if (stat.st_size < size + 8 + BOOTCONFIG_MAGIC_LEN) {
|
||||
pr_err("bootconfig size is too big\n");
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
if (lseek(fd, stat.st_size - 8 - size, SEEK_SET) < 0) {
|
||||
if (lseek(fd, stat.st_size - (size + 8 + BOOTCONFIG_MAGIC_LEN),
|
||||
SEEK_SET) < 0) {
|
||||
pr_err("Failed to lseek: %d\n", -errno);
|
||||
return -errno;
|
||||
}
|
||||
|
@ -163,17 +175,17 @@ int load_xbc_from_initrd(int fd, char **buf)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Wrong Checksum, maybe no boot config here */
|
||||
/* Wrong Checksum */
|
||||
rcsum = checksum((unsigned char *)*buf, size);
|
||||
if (csum != rcsum) {
|
||||
pr_err("checksum error: %d != %d\n", csum, rcsum);
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = xbc_init(*buf);
|
||||
/* Wrong data, maybe no boot config here */
|
||||
/* Wrong data */
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
@ -213,20 +225,15 @@ int delete_xbc(const char *path)
|
|||
return -errno;
|
||||
}
|
||||
|
||||
/*
|
||||
* Suppress error messages in xbc_init() because it can be just a
|
||||
* data which concidentally matches the size and checksum footer.
|
||||
*/
|
||||
pr_output = 0;
|
||||
size = load_xbc_from_initrd(fd, &buf);
|
||||
pr_output = 1;
|
||||
if (size < 0) {
|
||||
ret = size;
|
||||
pr_err("Failed to load a boot config from initrd: %d\n", ret);
|
||||
} else if (size > 0) {
|
||||
ret = fstat(fd, &stat);
|
||||
if (!ret)
|
||||
ret = ftruncate(fd, stat.st_size - size - 8);
|
||||
ret = ftruncate(fd, stat.st_size
|
||||
- size - 8 - BOOTCONFIG_MAGIC_LEN);
|
||||
if (ret)
|
||||
ret = -errno;
|
||||
} /* Ignore if there is no boot config in initrd */
|
||||
|
@ -295,6 +302,12 @@ int apply_xbc(const char *path, const char *xbc_path)
|
|||
pr_err("Failed to apply a boot config: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
/* Write a magic word of the bootconfig */
|
||||
ret = write(fd, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to apply a boot config magic: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
close(fd);
|
||||
free(data);
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# value -> subkey pattern
|
||||
key = value
|
||||
key.subkey = another-value
|
|
@ -0,0 +1,3 @@
|
|||
# subkey -> value pattern
|
||||
key.subkey = value
|
||||
key = another-value
|
|
@ -0,0 +1,6 @@
|
|||
# Same key value is not allowed
|
||||
key {
|
||||
foo = value
|
||||
bar = value2
|
||||
}
|
||||
key.foo = value
|
|
@ -9,7 +9,7 @@ TEMPCONF=`mktemp temp-XXXX.bconf`
|
|||
NG=0
|
||||
|
||||
cleanup() {
|
||||
rm -f $INITRD $TEMPCONF
|
||||
rm -f $INITRD $TEMPCONF $OUTFILE
|
||||
exit $NG
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ xpass $BOOTCONF -a $TEMPCONF $INITRD
|
|||
new_size=$(stat -c %s $INITRD)
|
||||
|
||||
echo "File size check"
|
||||
xpass test $new_size -eq $(expr $bconf_size + $initrd_size + 9)
|
||||
xpass test $new_size -eq $(expr $bconf_size + $initrd_size + 9 + 12)
|
||||
|
||||
echo "Apply command repeat test"
|
||||
xpass $BOOTCONF -a $TEMPCONF $INITRD
|
||||
|
@ -71,7 +71,6 @@ printf " \0\0\0 \0\0\0" >> $INITRD
|
|||
$BOOTCONF -a $TEMPCONF $INITRD > $OUTFILE 2>&1
|
||||
xfail grep -i "failed" $OUTFILE
|
||||
xfail grep -i "error" $OUTFILE
|
||||
rm $OUTFILE
|
||||
|
||||
echo "Max node number check"
|
||||
|
||||
|
@ -96,6 +95,19 @@ truncate -s 32764 $TEMPCONF
|
|||
echo "\"" >> $TEMPCONF # add 2 bytes + terminal ('\"\n\0')
|
||||
xpass $BOOTCONF -a $TEMPCONF $INITRD
|
||||
|
||||
echo "Adding same-key values"
|
||||
cat > $TEMPCONF << EOF
|
||||
key = bar, baz
|
||||
key += qux
|
||||
EOF
|
||||
echo > $INITRD
|
||||
|
||||
xpass $BOOTCONF -a $TEMPCONF $INITRD
|
||||
$BOOTCONF $INITRD > $OUTFILE
|
||||
xpass grep -q "bar" $OUTFILE
|
||||
xpass grep -q "baz" $OUTFILE
|
||||
xpass grep -q "qux" $OUTFILE
|
||||
|
||||
echo "=== expected failure cases ==="
|
||||
for i in samples/bad-* ; do
|
||||
xfail $BOOTCONF -a $i $INITRD
|
||||
|
|
Загрузка…
Ссылка в новой задаче