]> www.infradead.org Git - users/hch/configfs.git/commitdiff
proc: give /proc/cmdline size
authorAlexey Dobriyan <adobriyan@gmail.com>
Thu, 8 Sep 2022 18:21:54 +0000 (21:21 +0300)
committerAndrew Morton <akpm@linux-foundation.org>
Fri, 18 Nov 2022 21:55:07 +0000 (13:55 -0800)
Most /proc files don't have length (in fstat sense).  This leads to
inefficiencies when reading such files with APIs commonly found in modern
programming languages.  They open file, then fstat descriptor, get st_size
== 0 and either assume file is empty or start reading without knowing
target size.

cat(1) does OK because it uses large enough buffer by default.  But naive
programs copy-pasted from SO aren't:

let mut f = std::fs::File::open("/proc/cmdline").unwrap();
let mut buf: Vec<u8> = Vec::new();
f.read_to_end(&mut buf).unwrap();

will result in

openat(AT_FDCWD, "/proc/cmdline", O_RDONLY|O_CLOEXEC) = 3
statx(0, NULL, AT_STATX_SYNC_AS_STAT, STATX_ALL, NULL) = -1 EFAULT (Bad address)
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0444, stx_size=0, ...}) = 0
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "BOOT_IMAGE=(hd3,gpt2)/vmlinuz-5.", 32) = 32
read(3, "19.6-100.fc35.x86_64 root=/dev/m", 32) = 32
read(3, "apper/fedora_localhost--live-roo"..., 64) = 64
read(3, "ocalhost--live-swap rd.lvm.lv=fe"..., 128) = 116
read(3, "", 12)

open/stat is OK, lseek looks silly but there are 3 unnecessary reads
because Rust starts with 32 bytes per Vec<u8> and grows from there.

In case of /proc/cmdline, the length is known precisely.

Make variables readonly while I'm at it.

P.S.: I tried to scp /proc/cpuinfo today and got empty file
but this is separate story.

Link: https://lkml.kernel.org/r/YxoywlbM73JJN3r+@localhost.localdomain
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/proc/cmdline.c
include/linux/init.h
init/main.c

index fa762c5fbcb28760dcea21953dc7a42938e2443a..91fe1597af7b44bf07daa1a3fbc18da139d1b1c1 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/init.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
+#include "internal.h"
 
 static int cmdline_proc_show(struct seq_file *m, void *v)
 {
@@ -13,7 +14,10 @@ static int cmdline_proc_show(struct seq_file *m, void *v)
 
 static int __init proc_cmdline_init(void)
 {
-       proc_create_single("cmdline", 0, NULL, cmdline_proc_show);
+       struct proc_dir_entry *pde;
+
+       pde = proc_create_single("cmdline", 0, NULL, cmdline_proc_show);
+       pde->size = saved_command_line_len + 1;
        return 0;
 }
 fs_initcall(proc_cmdline_init);
index 077d7f93b402f4e36ad2ed9079f163c0ac7c6d45..2e96756fe1ff223aee19465d068a133bbdb24b88 100644 (file)
@@ -143,6 +143,7 @@ struct file_system_type;
 extern int do_one_initcall(initcall_t fn);
 extern char __initdata boot_command_line[];
 extern char *saved_command_line;
+extern unsigned int saved_command_line_len;
 extern unsigned int reset_devices;
 
 /* used by init/main.c */
index aa21add5f7c54aa64d846b0d9f7fd3299412baf5..d213371cc067ba75d3d1ca3dbdd874e0cc9a9921 100644 (file)
@@ -145,7 +145,8 @@ void (*__initdata late_time_init)(void);
 /* Untouched command line saved by arch-specific code. */
 char __initdata boot_command_line[COMMAND_LINE_SIZE];
 /* Untouched saved command line (eg. for /proc) */
-char *saved_command_line;
+char *saved_command_line __ro_after_init;
+unsigned int saved_command_line_len __ro_after_init;
 /* Command line for parameter parsing */
 static char *static_command_line;
 /* Untouched extra command line */
@@ -667,6 +668,8 @@ static void __init setup_command_line(char *command_line)
                        strcpy(saved_command_line + len, extra_init_args);
                }
        }
+
+       saved_command_line_len = strlen(saved_command_line);
 }
 
 /*
@@ -1379,7 +1382,7 @@ static void __init do_initcall_level(int level, char *command_line)
 static void __init do_initcalls(void)
 {
        int level;
-       size_t len = strlen(saved_command_line) + 1;
+       size_t len = saved_command_line_len + 1;
        char *command_line;
 
        command_line = kzalloc(len, GFP_KERNEL);