On Mon, 2011-11-28 at 00:31 +0000, Rusty Russell wrote: > Off the top of my head, this makes me think of the way initcalls are > ordered. We could put a parameter parsing initcall at the start of each > initcall level in include/asm-generic/vmlinux.lds.h's INITCALLS macro. > > Then we steal four bits from struct kernel_param's flags to indicate the > level of the initcall (-1 == existing ones, otherwise N == before level > N initcalls). Yes, this was my initial idea as well. The only problem I faced is the fact that there is no "between levels"... It's easy to add parameters parsing _at_ any particular level, but hard to do this _after_ level A and _before_ level B. The initcalls section simply contains all the calls, ordered by the level - the only "separated" level is the pre-SMP early one. And order within one level is determined by the link order, so I can't guarantee parsing the parameters as the first call of a level (nor as the last call of the previous level). I've came up with a simple prototype (below, only relevant fragments), doing exactly what I want at the "late" level (maybe that's all we actually need? ;-) Of course adding all other levels is possible, probably some clever "generalization" will be useful. And of course the level would be simply a number in the flags, but what to do with "_sync" levels (like "arch_initcall_sync", which is "3s" not just "3"). If it makes any sense I'll carry on, any feedback will be useful. Cheers! Paweł --- diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 7317dc2..22e6196 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -443,6 +489,122 @@ static int __devexit virtio_mmio_remove(struct platform_device *pdev) +/* Devices list parameter */ + +#if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES) + +static struct device vm_cmdline_parent = { + .init_name = "virtio-mmio-cmdline", +}; + +static int vm_cmdline_parent_registered; +static int vm_cmdline_id; + +static int vm_cmdline_set(const char *device, + const struct kernel_param *kp) +{ + int err; + struct resource resources[2] = {}; + char *str; + long long int base; + int processed, consumed = 0; + struct platform_device *pdev; + + resources[0].flags = IORESOURCE_MEM; + resources[1].flags = IORESOURCE_IRQ; + + resources[0].end = memparse(device, &str) - 1; + + processed = sscanf(str, "@%lli:%u%n:%d%n", + &base, &resources[1].start, &consumed, + &vm_cmdline_id, &consumed); + + if (processed < 2 || processed > 3 || str[consumed]) + return -EINVAL; + + resources[0].start = base; + resources[0].end += base; + resources[1].end = resources[1].start; + + if (!vm_cmdline_parent_registered) { + err = device_register(&vm_cmdline_parent); + if (err) { + pr_err("Failed to register parent device!\n"); + return err; + } + vm_cmdline_parent_registered = 1; + } + + pr_info("Registering device virtio-mmio.%d at 0x%llx-0x%llx, IRQ %d.\n", + vm_cmdline_id++, + (unsigned long long)resources[0].start, + (unsigned long long)resources[0].end, + (int)resources[1].start); + + pdev = platform_device_register_resndata(&vm_cmdline_parent, + "virtio-mmio", vm_cmdline_id++, + resources, ARRAY_SIZE(resources), NULL, 0); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + return 0; +} + +static int vm_cmdline_get_device(struct device *dev, void *data) +{ + char *buffer = data; + unsigned int len = strlen(buffer); + struct platform_device *pdev = to_platform_device(dev); + + snprintf(buffer + len, PAGE_SIZE - len, "0x%llx@0x%llx:%llu:%d\n", + pdev->resource[0].end - pdev->resource[0].start + 1ULL, + (unsigned long long)pdev->resource[0].start, + (unsigned long long)pdev->resource[1].start, + pdev->id); + return 0; +} + +static int vm_cmdline_get(char *buffer, const struct kernel_param *kp) +{ + buffer[0] = '\0'; + device_for_each_child(&vm_cmdline_parent, buffer, + vm_cmdline_get_device); + return strlen(buffer) + 1; +} + +static struct kernel_param_ops vm_cmdline_param_ops = { + .set = vm_cmdline_set, + .get = vm_cmdline_get, +}; + +module_param_cb_late(device, &vm_cmdline_param_ops, NULL, S_IRUSR); + +static int vm_unregister_cmdline_device(struct device *dev, + void *data) +{ + platform_device_unregister(to_platform_device(dev)); + + return 0; +} + +static void vm_unregister_cmdline_devices(void) +{ + if (vm_cmdline_parent_registered) { + device_for_each_child(&vm_cmdline_parent, NULL, + vm_unregister_cmdline_device); + device_unregister(&vm_cmdline_parent); + vm_cmdline_parent_registered = 0; + } +} + +#else + +static void virtio_mmio_unregister_cmdline_devices(void) +{ +} + +#endif + /* Platform driver */ static struct of_device_id virtio_mmio_match[] = { @@ -469,6 +631,7 @@ static int __init virtio_mmio_init(void) static void __exit virtio_mmio_exit(void) { platform_driver_unregister(&virtio_mmio_driver); + vm_unregister_cmdline_devices(); } module_init(virtio_mmio_init); diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index 7939f63..fccf0cb 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -48,7 +48,8 @@ struct kernel_param_ops { }; /* Flag bits for kernel_param.flags */ -#define KPARAM_ISBOOL 2 +#define KPARAM_ISBOOL (1 << 1) +#define KPARAM_LATE (1 << 2) struct kernel_param { const char *name; @@ -132,7 +133,13 @@ struct kparam_array */ #define module_param_cb(name, ops, arg, perm) \ __module_param_call(MODULE_PARAM_PREFIX, \ - name, ops, arg, __same_type((arg), bool *), perm) + name, ops, arg, \ + __same_type((arg), bool *), 0, perm) + +#define module_param_cb_late(name, ops, arg, perm) \ + __module_param_call(MODULE_PARAM_PREFIX, \ + name, ops, arg, \ + __same_type((arg), bool *), 1, perm) /* On alpha, ia64 and ppc64 relocations to global data cannot go into read-only sections (which is part of respective UNIX ABI on these @@ -146,7 +153,7 @@ struct kparam_array /* This is the fundamental function for registering boot/module parameters. */ -#define __module_param_call(prefix, name, ops, arg, isbool, perm) \ +#define __module_param_call(prefix, name, ops, arg, isbool, late, perm) \ /* Default value instead of permissions? */ \ static int __param_perm_check_##name __attribute__((unused)) = \ BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2)) \ @@ -155,7 +162,8 @@ struct kparam_array static struct kernel_param __moduleparam_const __param_##name \ __used \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ - = { __param_str_##name, ops, perm, isbool ? KPARAM_ISBOOL : 0, \ + = { __param_str_##name, ops, perm, \ + (isbool ? KPARAM_ISBOOL : 0) | (late ? KPARAM_LATE : 0), \ { arg } } /* Obsolete - use module_param_cb() */ @@ -165,6 +173,7 @@ struct kparam_array __module_param_call(MODULE_PARAM_PREFIX, \ name, &__param_ops_##name, arg, \ __same_type(arg, bool *), \ + 0, \ (perm) + sizeof(__check_old_set_param(set))*0) /* We don't get oldget: it's often a new-style param_get_uint, etc. */ @@ -246,7 +255,7 @@ static inline void __kernel_param_unlock(void) #define core_param(name, var, type, perm) \ param_check_##type(name, &(var)); \ __module_param_call("", name, ¶m_ops_##type, \ - &var, __same_type(var, bool), perm) + &var, __same_type(var, bool), 0, perm) #endif /* !MODULE */ /** @@ -264,7 +273,7 @@ static inline void __kernel_param_unlock(void) = { len, string }; \ __module_param_call(MODULE_PARAM_PREFIX, name, \ ¶m_ops_string, \ - .str = &__param_string_##name, 0, perm); \ + .str = &__param_string_##name, 0, 0, perm); \ __MODULE_PARM_TYPE(name, "string") /** @@ -292,6 +301,8 @@ extern int parse_args(const char *name, char *args, const struct kernel_param *params, unsigned num, + u16 flags_mask, + u16 flags, int (*unknown)(char *param, char *val)); /* Called by module remove. */ @@ -402,7 +413,7 @@ extern int param_get_invbool(char *buffer, const struct kernel_param *kp); __module_param_call(MODULE_PARAM_PREFIX, name, \ ¶m_array_ops, \ .arr = &__param_arr_##name, \ - __same_type(array[0], bool), perm); \ + __same_type(array[0], bool), 0, perm); \ __MODULE_PARM_TYPE(name, "array of " #type) extern struct kernel_param_ops param_array_ops; diff --git a/init/main.c b/init/main.c index 217ed23..ce89a53 100644 --- a/init/main.c +++ b/init/main.c @@ -407,7 +407,7 @@ static int __init do_early_param(char *param, char *val) void __init parse_early_options(char *cmdline) { - parse_args("early options", cmdline, NULL, 0, do_early_param); + parse_args("early options", cmdline, NULL, 0, 0, 0, do_early_param); } /* Arch code calls this early on, or if not, just before other parsing. */ @@ -511,7 +511,7 @@ asmlinkage void __init start_kernel(void) parse_early_param(); parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, - &unknown_bootoption); + KPARAM_LATE, 0, &unknown_bootoption); jump_label_init(); @@ -737,6 +737,22 @@ static void __init do_basic_setup(void) do_initcalls(); } +static int __init ignore_unknown_bootoption(char *param, char *val) +{ + return 0; +} + +static int __init parse_late_args(void) +{ + extern const struct kernel_param __start___param[], __stop___param[]; + + strcpy(static_command_line, saved_command_line); + parse_args("Late parameters", static_command_line, __start___param, + __stop___param - __start___param, + KPARAM_LATE, KPARAM_LATE, ignore_unknown_bootoption); +} +late_initcall(parse_late_args); + static void __init do_pre_smp_initcalls(void) { initcall_t *fn; diff --git a/kernel/module.c b/kernel/module.c index 178333c..66d3e75 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2893,7 +2893,8 @@ static struct module *load_module(void __user *umod, mutex_unlock(&module_mutex); /* Module is ready to execute: parsing args may do that. */ - err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, NULL); + err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, + 0, 0, NULL); if (err < 0) goto unlink; diff --git a/kernel/params.c b/kernel/params.c index 65aae11..560345c 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -94,6 +94,8 @@ static int parse_one(char *param, char *val, const struct kernel_param *params, unsigned num_params, + int flags_mask, + int flags, int (*handle_unknown)(char *param, char *val)) { unsigned int i; @@ -102,6 +104,8 @@ static int parse_one(char *param, /* Find parameter */ for (i = 0; i < num_params; i++) { if (parameq(param, params[i].name)) { + if ((params[i].flags & flags_mask) != flags) + return 0; /* No one handled NULL, so do it here. */ if (!val && params[i].ops->set != param_set_bool) return -EINVAL; @@ -180,6 +184,8 @@ int parse_args(const char *name, char *args, const struct kernel_param *params, unsigned num, + u16 flags_mask, + u16 flags, int (*unknown)(char *param, char *val)) { char *param, *val; @@ -195,7 +201,8 @@ int parse_args(const char *name, args = next_arg(args, ¶m, &val); irq_was_disabled = irqs_disabled(); - ret = parse_one(param, val, params, num, unknown); + ret = parse_one(param, val, params, num, + flags_mask, flags, unknown); if (irq_was_disabled && !irqs_disabled()) { printk(KERN_WARNING "parse_args(): option '%s' enabled " "irq's!\n", param); _______________________________________________ Virtualization mailing list Virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linuxfoundation.org/mailman/listinfo/virtualization