Re: [RFC] kernel/sysctl: support setting sysctl parameters from kernel command line

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Mar 17, 2020 at 02:21:05PM +0100, Vlastimil Babka wrote:
> A recently proposed patch to add vm_swappiness command line parameter in
> addition to existing sysctl [1] made me wonder why we don't have a general
> support for passing sysctl parameters via command line. Googling found only
> somebody else wondering the same [2], but I haven't found any prior discussion
> with reasons why not to do this.

I'd like to see stuff like this (as you say, you've found some
redundancies here which could be cleaned up a bit). I think the reason
it hasn't happened before is that the answers have mostly revolved
around "just set it in your initramfs". :P

> [...]
> Hence, this patch adds a new parse_args() pass that looks for parameters
> prefixed by 'sysctl.' and searches for them in the sysctl ctl_tables. When
> found, the respective proc handler is invoked. The search is just a naive
> linear one, to avoid using the whole procfs layer. It should be acceptable,
> as the cost depends on number of sysctl. parameters passed.

I think this needs reconsidering: this RFC only searches 1 level deep,
but sysctls are a tree. For example:

kernel.yama.ptrace_scope
mm.transparent_hugepage.enabled
net.ipv4.conf.default.rp_filter
...etc

If this goes in, it'll need to do full traversal.

> The main limitation of avoiding the procfs layer is however that sysctls
> dynamically registered by register_sysctl_table() or register_sysctl_paths()
> cannot be set by this method.

Correct. And I like what you've done in the code: announce any unhandled
sysctls.

> The processing is hooked right before the init process is loaded, as some
> handlers might be more complicated than simple setters and might need some
> subsystems to be initialized. At the moment the init process can be started and
> eventually execute a process writing to /proc/sys/ then it should be also fine
> to do that from the kernel.

I agree about placement.

> 
> [1] https://lore.kernel.org/linux-doc/BL0PR02MB560167492CA4094C91589930E9FC0@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/
> [2] https://unix.stackexchange.com/questions/558802/how-to-set-sysctl-using-kernel-command-line-parameter
> 
> Signed-off-by: Vlastimil Babka <vbabka@xxxxxxx>
> ---
> Hi,
> 
> this is an early RFC so I can get feedback whether to pursue this idea further,
> before trying the more complicated stuff with dynamically registered sysctls.
> For those I have some unanswered questions:
> - Support them at all?

Maybe? It seems excessive for the initial version.

> - Do so by an internal procfs mount again, that was removed by 61a47c1ad3a4 ?
>   Or try to keep it simple.

I think you can walk the registered sysctl structures themselves, yes?

> - If sysctls are dynamically registered at module load, process the command
>   line sysctl arguments again? - this would be rather complicated I guess.

If it does get supported, perhaps saving them somewhere for
register_sysctl_table() to walk when it gets called?

I like the idea if just for having to build less boiler plate for
supporting things that I've had to plumb to both boot_params and sysctl.
:)

-Kees

> 
> Vlastimil
> 
>  include/linux/sysctl.h |  1 +
>  init/main.c            | 21 ++++++++++++++
>  kernel/sysctl.c        | 66 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 88 insertions(+)
> 
> diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
> index 02fa84493f23..62ae963a5c0c 100644
> --- a/include/linux/sysctl.h
> +++ b/include/linux/sysctl.h
> @@ -206,6 +206,7 @@ struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path,
>  void unregister_sysctl_table(struct ctl_table_header * table);
>  
>  extern int sysctl_init(void);
> +int process_sysctl_arg(char *param, char *val, const char *unused, void *arg);
>  
>  extern struct ctl_table sysctl_mount_point[];
>  
> diff --git a/init/main.c b/init/main.c
> index ee4947af823f..74a094c6b8b9 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -1345,6 +1345,25 @@ void __weak free_initmem(void)
>  	free_initmem_default(POISON_FREE_INITMEM);
>  }
>  
> +static void do_sysctl_args(void)
> +{
> +#ifdef CONFIG_SYSCTL
> +	size_t len = strlen(saved_command_line) + 1;
> +	char *command_line;
> +
> +	command_line = kzalloc(len, GFP_KERNEL);
> +	if (!command_line)
> +		panic("%s: Failed to allocate %zu bytes\n", __func__, len);
> +
> +	strcpy(command_line, saved_command_line);
> +
> +	parse_args("Setting sysctl args", command_line,
> +		   NULL, 0, -1, -1, NULL, process_sysctl_arg);
> +
> +	kfree(command_line);
> +#endif
> +}
> +
>  static int __ref kernel_init(void *unused)
>  {
>  	int ret;
> @@ -1367,6 +1386,8 @@ static int __ref kernel_init(void *unused)
>  
>  	rcu_end_inkernel_boot();
>  
> +	do_sysctl_args();
> +
>  	if (ramdisk_execute_command) {
>  		ret = run_init_process(ramdisk_execute_command);
>  		if (!ret)
> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> index ad5b88a53c5a..0444656c259d 100644
> --- a/kernel/sysctl.c
> +++ b/kernel/sysctl.c
> @@ -1980,6 +1980,72 @@ int __init sysctl_init(void)
>  	return 0;
>  }
>  
> +/* Set sysctl value passed on kernel command line. */
> +int process_sysctl_arg(char *param, char *val,
> +			       const char *unused, void *arg)
> +{
> +	size_t count;
> +	char *tmp;
> +	int err;
> +	loff_t ppos = 0;
> +	struct ctl_table *base, *child = NULL, *found = NULL;
> +
> +	if (strncmp(param, "sysctl.", sizeof("sysctl.") - 1))
> +		return 0;
> +
> +	param += (sizeof("sysctl.") - 1);
> +
> +	tmp = strchr(param, '.');
> +	if (!tmp) {
> +		pr_warn("Invalid sysctl param '%s' on command line", param);
> +		return 0;
> +	}
> +
> +	*tmp = '\0';
> +
> +	for (base = &sysctl_base_table[0]; base->procname != 0; base++) {
> +		if (strcmp(param, base->procname) == 0) {
> +			child = base->child;
> +			break;
> +		}
> +	}
> +
> +	if (!child) {
> +		pr_warn("Unknown sysctl prefix '%s' on command line", param);
> +		return 0;
> +	}
> +
> +	tmp++;
> +
> +	for (; child->procname != 0; child++) {
> +		if (strcmp(tmp, child->procname) == 0) {
> +			found = child;
> +			break;
> +		}
> +	}
> +
> +	if (!found) {
> +		pr_warn("Unknown sysctl param '%s.%s' on command line", param, tmp);
> +		return 0;
> +	}
> +
> +	if (!(found->mode & 0200)) {
> +		pr_warn("Cannot set sysctl '%s.%s=%s' from command line - not writable",
> +			param, tmp, val);
> +		return 0;
> +	}
> +
> +
> +	count = strlen(val);
> +	err = found->proc_handler(found, 1, val, &count, &ppos);
> +
> +	if (err)
> +		pr_warn("Error %d setting sysctl '%s.%s=%s' from command line",
> +			err, param, tmp, val);
> +
> +	return 0;
> +}
> +
>  #endif /* CONFIG_SYSCTL */
>  
>  /*
> -- 
> 2.25.1
> 

-- 
Kees Cook




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux