params: <level>_initcall-like kernel parameters
authorPawel Moll <pawel.moll@arm.com>
Mon, 26 Mar 2012 02:20:51 +0000 (12:50 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Mon, 26 Mar 2012 02:20:51 +0000 (12:50 +1030)
This patch adds a set of macros that can be used to declare
kernel parameters to be parsed _before_ initcalls at a chosen
level are executed.  We rename the now-unused "flags" field of
struct kernel_param as the level.  It's signed, for when we
use this for early params as well, in future.

Linker macro collating init calls had to be modified in order
to add additional symbols between levels that are later used
by the init code to split the calls into blocks.

Signed-off-by: Pawel Moll <pawel.moll@arm.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>

arch/powerpc/mm/hugetlbpage.c
include/asm-generic/vmlinux.lds.h
include/linux/moduleparam.h
init/main.c
kernel/module.c
kernel/params.c

index 57c7465..a3e6287 100644 (file)
@@ -310,7 +310,8 @@ void __init reserve_hugetlb_gpages(void)
        int i;
 
        strlcpy(cmdline, boot_command_line, COMMAND_LINE_SIZE);
-       parse_args("hugetlb gpages", cmdline, NULL, 0, &do_gpage_early_setup);
+       parse_args("hugetlb gpages", cmdline, NULL, 0, 0, 0,
+                       &do_gpage_early_setup);
 
        /*
         * Walk gpage list in reverse, allocating larger page sizes first.
index 798603e..8aeadf6 100644 (file)
                *(.init.setup)                                          \
                VMLINUX_SYMBOL(__setup_end) = .;
 
-#define INITCALLS                                                      \
-       *(.initcallearly.init)                                          \
-       VMLINUX_SYMBOL(__early_initcall_end) = .;                       \
-       *(.initcall0.init)                                              \
-       *(.initcall0s.init)                                             \
-       *(.initcall1.init)                                              \
-       *(.initcall1s.init)                                             \
-       *(.initcall2.init)                                              \
-       *(.initcall2s.init)                                             \
-       *(.initcall3.init)                                              \
-       *(.initcall3s.init)                                             \
-       *(.initcall4.init)                                              \
-       *(.initcall4s.init)                                             \
-       *(.initcall5.init)                                              \
-       *(.initcall5s.init)                                             \
-       *(.initcallrootfs.init)                                         \
-       *(.initcall6.init)                                              \
-       *(.initcall6s.init)                                             \
-       *(.initcall7.init)                                              \
-       *(.initcall7s.init)
+#define INIT_CALLS_LEVEL(level)                                                \
+               VMLINUX_SYMBOL(__initcall##level##_start) = .;          \
+               *(.initcall##level##.init)                              \
+               *(.initcall##level##s.init)                             \
 
 #define INIT_CALLS                                                     \
                VMLINUX_SYMBOL(__initcall_start) = .;                   \
-               INITCALLS                                               \
+               *(.initcallearly.init)                                  \
+               INIT_CALLS_LEVEL(0)                                     \
+               INIT_CALLS_LEVEL(1)                                     \
+               INIT_CALLS_LEVEL(2)                                     \
+               INIT_CALLS_LEVEL(3)                                     \
+               INIT_CALLS_LEVEL(4)                                     \
+               INIT_CALLS_LEVEL(5)                                     \
+               INIT_CALLS_LEVEL(rootfs)                                \
+               INIT_CALLS_LEVEL(6)                                     \
+               INIT_CALLS_LEVEL(7)                                     \
                VMLINUX_SYMBOL(__initcall_end) = .;
 
 #define CON_INITCALL                                                   \
index 4daa895..ea36486 100644 (file)
@@ -51,7 +51,7 @@ struct kernel_param {
        const char *name;
        const struct kernel_param_ops *ops;
        u16 perm;
-       u16 flags;
+       s16 level;
        union {
                void *arg;
                const struct kparam_string *str;
@@ -128,7 +128,40 @@ struct kparam_array
  * The ops can have NULL set or get functions.
  */
 #define module_param_cb(name, ops, arg, perm)                                \
-       __module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm)
+       __module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, 0)
+
+/**
+ * <level>_param_cb - general callback for a module/cmdline parameter
+ *                    to be evaluated before certain initcall level
+ * @name: a valid C identifier which is the parameter name.
+ * @ops: the set & get operations for this parameter.
+ * @perm: visibility in sysfs.
+ *
+ * The ops can have NULL set or get functions.
+ */
+#define __level_param_cb(name, ops, arg, perm, level)                  \
+       __module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, level)
+
+#define core_param_cb(name, ops, arg, perm)            \
+       __level_param_cb(name, ops, arg, perm, 1)
+
+#define postcore_param_cb(name, ops, arg, perm)                \
+       __level_param_cb(name, ops, arg, perm, 2)
+
+#define arch_param_cb(name, ops, arg, perm)            \
+       __level_param_cb(name, ops, arg, perm, 3)
+
+#define subsys_param_cb(name, ops, arg, perm)          \
+       __level_param_cb(name, ops, arg, perm, 4)
+
+#define fs_param_cb(name, ops, arg, perm)              \
+       __level_param_cb(name, ops, arg, perm, 5)
+
+#define device_param_cb(name, ops, arg, perm)          \
+       __level_param_cb(name, ops, arg, perm, 6)
+
+#define late_param_cb(name, ops, arg, perm)            \
+       __level_param_cb(name, ops, arg, perm, 7)
 
 /* On alpha, ia64 and ppc64 relocations to global data cannot go into
    read-only sections (which is part of respective UNIX ABI on these
@@ -142,7 +175,7 @@ struct kparam_array
 
 /* This is the fundamental function for registering boot/module
    parameters. */
-#define __module_param_call(prefix, name, ops, arg, perm)              \
+#define __module_param_call(prefix, name, ops, arg, perm, level)       \
        /* Default value instead of permissions? */                     \
        static int __param_perm_check_##name __attribute__((unused)) =  \
        BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2))  \
@@ -151,7 +184,7 @@ struct kparam_array
        static struct kernel_param __moduleparam_const __param_##name   \
        __used                                                          \
     __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
-       = { __param_str_##name, ops, perm, 0, { arg } }
+       = { __param_str_##name, ops, perm, level, { arg } }
 
 /* Obsolete - use module_param_cb() */
 #define module_param_call(name, set, get, arg, perm)                   \
@@ -159,7 +192,7 @@ struct kparam_array
                 { (void *)set, (void *)get };                          \
        __module_param_call(MODULE_PARAM_PREFIX,                        \
                            name, &__param_ops_##name, arg,             \
-                           (perm) + sizeof(__check_old_set_param(set))*0)
+                           (perm) + sizeof(__check_old_set_param(set))*0, 0)
 
 /* We don't get oldget: it's often a new-style param_get_uint, etc. */
 static inline int
@@ -239,7 +272,7 @@ static inline void __kernel_param_unlock(void)
  */
 #define core_param(name, var, type, perm)                              \
        param_check_##type(name, &(var));                               \
-       __module_param_call("", name, &param_ops_##type, &var, perm)
+       __module_param_call("", name, &param_ops_##type, &var, perm, 0)
 #endif /* !MODULE */
 
 /**
@@ -257,7 +290,7 @@ static inline void __kernel_param_unlock(void)
                = { len, string };                                      \
        __module_param_call(MODULE_PARAM_PREFIX, name,                  \
                            &param_ops_string,                          \
-                           .str = &__param_string_##name, perm);       \
+                           .str = &__param_string_##name, perm, 0);    \
        __MODULE_PARM_TYPE(name, "string")
 
 /**
@@ -285,6 +318,8 @@ extern int parse_args(const char *name,
                      char *args,
                      const struct kernel_param *params,
                      unsigned num,
+                     s16 level_min,
+                     s16 level_max,
                      int (*unknown)(char *param, char *val));
 
 /* Called by module remove. */
@@ -396,7 +431,7 @@ extern int param_set_bint(const char *val, const struct kernel_param *kp);
        __module_param_call(MODULE_PARAM_PREFIX, name,                  \
                            &param_array_ops,                           \
                            .arr = &__param_arr_##name,                 \
-                           perm);                                      \
+                           perm, 0);                                   \
        __MODULE_PARM_TYPE(name, "array of " #type)
 
 extern struct kernel_param_ops param_array_ops;
index c24805c..4397158 100644 (file)
@@ -400,7 +400,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. */
@@ -503,7 +503,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);
+                  0, 0, &unknown_bootoption);
 
        jump_label_init();
 
@@ -699,16 +699,69 @@ int __init_or_module do_one_initcall(initcall_t fn)
 }
 
 
-extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
+extern initcall_t __initcall_start[];
+extern initcall_t __initcall0_start[];
+extern initcall_t __initcall1_start[];
+extern initcall_t __initcall2_start[];
+extern initcall_t __initcall3_start[];
+extern initcall_t __initcall4_start[];
+extern initcall_t __initcall5_start[];
+extern initcall_t __initcall6_start[];
+extern initcall_t __initcall7_start[];
+extern initcall_t __initcall_end[];
+
+static initcall_t *initcall_levels[] __initdata = {
+       __initcall0_start,
+       __initcall1_start,
+       __initcall2_start,
+       __initcall3_start,
+       __initcall4_start,
+       __initcall5_start,
+       __initcall6_start,
+       __initcall7_start,
+       __initcall_end,
+};
+
+static char *initcall_level_names[] __initdata = {
+       "early parameters",
+       "core parameters",
+       "postcore parameters",
+       "arch parameters",
+       "subsys parameters",
+       "fs parameters",
+       "device parameters",
+       "late parameters",
+};
+
+static int __init ignore_unknown_bootoption(char *param, char *val)
+{
+       return 0;
+}
 
-static void __init do_initcalls(void)
+static void __init do_initcall_level(int level)
 {
+       extern const struct kernel_param __start___param[], __stop___param[];
        initcall_t *fn;
 
-       for (fn = __early_initcall_end; fn < __initcall_end; fn++)
+       strcpy(static_command_line, saved_command_line);
+       parse_args(initcall_level_names[level],
+                  static_command_line, __start___param,
+                  __stop___param - __start___param,
+                  level, level,
+                  ignore_unknown_bootoption);
+
+       for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
                do_one_initcall(*fn);
 }
 
+static void __init do_initcalls(void)
+{
+       int level;
+
+       for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
+               do_initcall_level(level);
+}
+
 /*
  * Ok, the machine is now initialized. None of the devices
  * have been touched yet, but the CPU subsystem is up and
@@ -732,7 +785,7 @@ static void __init do_pre_smp_initcalls(void)
 {
        initcall_t *fn;
 
-       for (fn = __initcall_start; fn < __early_initcall_end; fn++)
+       for (fn = __initcall_start; fn < __initcall0_start; fn++)
                do_one_initcall(*fn);
 }
 
index 7e31da9..6f6651a 100644 (file)
@@ -2923,7 +2923,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,
+                        -32768, 32767, NULL);
        if (err < 0)
                goto unlink;
 
index 508828a..f37d826 100644 (file)
@@ -87,6 +87,8 @@ static int parse_one(char *param,
                     char *val,
                     const struct kernel_param *params,
                     unsigned num_params,
+                    s16 min_level,
+                    s16 max_level,
                     int (*handle_unknown)(char *param, char *val))
 {
        unsigned int i;
@@ -95,6 +97,9 @@ static int parse_one(char *param,
        /* Find parameter */
        for (i = 0; i < num_params; i++) {
                if (parameq(param, params[i].name)) {
+                       if (params[i].level < min_level
+                           || params[i].level > max_level)
+                               return 0;
                        /* No one handled NULL, so do it here. */
                        if (!val && params[i].ops->set != param_set_bool
                            && params[i].ops->set != param_set_bint)
@@ -174,6 +179,8 @@ int parse_args(const char *name,
               char *args,
               const struct kernel_param *params,
               unsigned num,
+              s16 min_level,
+              s16 max_level,
               int (*unknown)(char *param, char *val))
 {
        char *param, *val;
@@ -189,7 +196,8 @@ int parse_args(const char *name,
 
                args = next_arg(args, &param, &val);
                irq_was_disabled = irqs_disabled();
-               ret = parse_one(param, val, params, num, unknown);
+               ret = parse_one(param, val, params, num,
+                               min_level, max_level, unknown);
                if (irq_was_disabled && !irqs_disabled()) {
                        printk(KERN_WARNING "parse_args(): option '%s' enabled "
                                        "irq's!\n", param);
@@ -374,7 +382,7 @@ static int param_array(const char *name,
                       unsigned int min, unsigned int max,
                       void *elem, int elemsize,
                       int (*set)(const char *, const struct kernel_param *kp),
-                      u16 flags,
+                      s16 level,
                       unsigned int *num)
 {
        int ret;
@@ -384,7 +392,7 @@ static int param_array(const char *name,
        /* Get the name right for errors. */
        kp.name = name;
        kp.arg = elem;
-       kp.flags = flags;
+       kp.level = level;
 
        *num = 0;
        /* We expect a comma-separated list of values. */
@@ -425,7 +433,7 @@ static int param_array_set(const char *val, const struct kernel_param *kp)
        unsigned int temp_num;
 
        return param_array(kp->name, val, 1, arr->max, arr->elem,
-                          arr->elemsize, arr->ops->set, kp->flags,
+                          arr->elemsize, arr->ops->set, kp->level,
                           arr->num ?: &temp_num);
 }