Merge patch series "initrd: remove half of classic initrd support"

Askar Safin <safinaskar@gmail.com> says:

This patchset will not affect anyone, who showed up in these lists.
See [5] for details.

Intro
====
This patchset removes half of classic initrd (initial RAM disk) support,
i. e. linuxrc code path, which was deprecated in 2020.
Initramfs still stays, RAM disk itself (brd) still stays.
And other half of initrd stays, too.
init/do_mounts* are listed in VFS entry in
MAINTAINERS, so I think this patchset should go through VFS tree.
I tested the patchset on 8 (!!!) archs in Qemu (see details below).
If you still use initrd, see below for workaround.

In 2020 deprecation notice was put to linuxrc initrd code path.
In v1 I tried to remove initrd
fully, but Nicolas Schichan reported that he still uses
other code path (root=/dev/ram0 one) on million devices [4].
root=/dev/ram0 code path did not contain deprecation notice.

So, in this version of patchset I remove deprecated code path,
i. e. linuxrc one, while keeping other, i. e. root=/dev/ram0 one.

Also I put deprecation notice to remaining code path, i. e. to
root=/dev/ram0 one. I plan to send patches for full removal
of initrd after one year, i. e. in January 2027 (of course,
initramfs will still work).

Also, I tried to make this patchset small to make sure it
can be reverted easily. I plan to send cleanups later.

Details
====
Other user-visible changes:

- Removed kernel command line parameters "load_ramdisk" and
"prompt_ramdisk", which did nothing and were deprecated
- Removed /proc/sys/kernel/real-root-dev . It was used
for initrd only
- Command line parameters "noinitrd" and "ramdisk_start=" are deprecated

Testing
====
I tested my patchset on many architectures in Qemu using my Rust
program, heavily based on mkroot [1].

I used the following cross-compilers:

aarch64-linux-musleabi
armv4l-linux-musleabihf
armv5l-linux-musleabihf
armv7l-linux-musleabihf
i486-linux-musl
i686-linux-musl
mips-linux-musl
mips64-linux-musl
mipsel-linux-musl
powerpc-linux-musl
powerpc64-linux-musl
powerpc64le-linux-musl
riscv32-linux-musl
riscv64-linux-musl
s390x-linux-musl
sh4-linux-musl
sh4eb-linux-musl
x86_64-linux-musl

taken from this directory [2].

So, as you can see, there are 18 triplets, which correspond to 8 subdirs in arch/.

For every triplet I tested that:
- Initramfs still works (both builtin and external)
- Direct boot from disk still works
- Remaining initrd code path (root=/dev/ram0) still works

Workaround
====
If "retain_initrd" is passed to kernel, then initramfs/initrd,
passed by bootloader, is retained and becomes available after boot
as read-only magic file /sys/firmware/initrd [3].

No copies are involved. I. e. /sys/firmware/initrd is simply
a reference to original blob passed by bootloader.

This works even if initrd/initramfs is not recognized by kernel
in any way, i. e. even if it is not valid cpio archive, nor
a fs image supported by classic initrd.

This works both with my patchset and without it.

This means that you can emulate classic initrd so:
link builtin initramfs to kernel; in /init in this initramfs
copy /sys/firmware/initrd to some file in / and loop-mount it.

This is even better than classic initrd, because:
- You can use fs not supported by classic initrd, for example erofs
- One copy is involved (from /sys/firmware/initrd to some file in /)
as opposed to two when using classic initrd

Still, I don't recommend using this workaround, because
I want everyone to migrate to proper modern initramfs.
But still you can use this workaround if you want.

Also: it is not possible to directly loop-mount
/sys/firmware/initrd . Theoretically kernel can be changed
to allow this (and/or to make it writable), but I think nobody needs this.
And I don't want to implement this.

On Qemu's -initrd and GRUB's initrd
====
Don't panic, this patchset doesn't remove initramfs
(which is used by nearly all Linux distros). And I don't
have plans to remove it.

Qemu's -initrd option and GRUB's initrd command refer
to initrd bootloader mechanism, which is used to
load both initrd and (external) initramfs.

So, if you use Qemu's -initrd or GRUB's initrd,
then you likely use them to pass initramfs, and thus
you are safe.

v1: https://lore.kernel.org/lkml/20250913003842.41944-1-safinaskar@gmail.com/

v1 -> v2 changes:
- A lot. I removed most patches, see cover letter for details

v2: https://lore.kernel.org/lkml/20251010094047.3111495-1-safinaskar@gmail.com/

v2 -> v3 changes:
- Commit messages
- Expanded docs for "noinitrd"
- Added link to /sys/firmware/initrd workaround to pr_warn

v3: https://lore.kernel.org/lkml/20251017060956.1151347-1-safinaskar@gmail.com/

v3 -> v4 changes:
- Changed "September 2026" to "January 2027" (i. e. after 2026 LTS release)

[1] https://github.com/landley/toybox/tree/master/mkroot
[2] https://landley.net/toybox/downloads/binaries/toolchains/latest
[3] https://lore.kernel.org/all/20231207235654.16622-1-graf@amazon.com/
[4] https://lore.kernel.org/lkml/20250918152830.438554-1-nschichan@freebox.fr/
[5] https://lore.kernel.org/lkml/20251022082604.25437-1-safinaskar@gmail.com/

* patches from https://patch.msgid.link/20251119222407.3333257-1-safinaskar@gmail.com:
  init: remove /proc/sys/kernel/real-root-dev
  initrd: remove deprecated code path (linuxrc)
  init: remove deprecated "load_ramdisk" and "prompt_ramdisk" command line parameters

Link: https://patch.msgid.link/20251119222407.3333257-1-safinaskar@gmail.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2025-11-25 10:40:50 +01:00
commit ef12d0573a
No known key found for this signature in database
GPG key ID: 91C61BC06578DCA2
9 changed files with 23 additions and 160 deletions

View file

@ -3437,8 +3437,6 @@ Kernel parameters
If there are multiple matching configurations changing
the same attribute, the last one is used.
load_ramdisk= [RAM] [Deprecated]
lockd.nlm_grace_period=P [NFS] Assign grace period.
Format: <integer>
@ -4444,8 +4442,10 @@ Kernel parameters
Note that this argument takes precedence over
the CONFIG_RCU_NOCB_CPU_DEFAULT_ALL option.
noinitrd [RAM] Tells the kernel not to load any configured
initial RAM disk.
noinitrd [Deprecated,RAM] Tells the kernel not to load any configured
initial RAM disk. Currently this parameter applies to
initrd only, not to initramfs. But it applies to both
in EFI mode.
nointremap [X86-64,Intel-IOMMU,EARLY] Do not enable interrupt
remapping.
@ -5402,8 +5402,6 @@ Kernel parameters
Param: <number> - step/bucket size as a power of 2 for
statistical time based profiling.
prompt_ramdisk= [RAM] [Deprecated]
prot_virt= [S390] enable hosting protected virtual machines
isolated from the hypervisor (if hardware supports
that). If enabled, the default kernel base address
@ -5460,7 +5458,7 @@ Kernel parameters
ramdisk_size= [RAM] Sizes of RAM disks in kilobytes
See Documentation/admin-guide/blockdev/ramdisk.rst.
ramdisk_start= [RAM] RAM disk image start address
ramdisk_start= [Deprecated,RAM] RAM disk image start address
random.trust_cpu=off
[KNL,EARLY] Disable trusting the use of the CPU's

View file

@ -1235,12 +1235,6 @@ that support this feature.
== ===========================================================================
real-root-dev
=============
See Documentation/admin-guide/initrd.rst.
reboot-cmd (SPARC only)
=======================

View file

@ -9,7 +9,7 @@ CONFIG_ASSABET_NEPONSET=y
CONFIG_ZBOOT_ROM_TEXT=0x80000
CONFIG_ZBOOT_ROM_BSS=0xc1000000
CONFIG_ZBOOT_ROM=y
CONFIG_CMDLINE="console=ttySA0,38400n8 cpufreq=221200 rw root=/dev/mtdblock2 mtdparts=sa1100:512K(boot),1M(kernel),2560K(initrd),4M(root) load_ramdisk=1 prompt_ramdisk=0 mem=32M noinitrd initrd=0xc0800000,3M"
CONFIG_CMDLINE="console=ttySA0,38400n8 cpufreq=221200 rw root=/dev/mtdblock2 mtdparts=sa1100:512K(boot),1M(kernel),2560K(initrd),4M(root) mem=32M noinitrd initrd=0xc0800000,3M"
CONFIG_FPE_NWFPE=y
CONFIG_PM=y
CONFIG_MODULES=y

View file

@ -3,8 +3,6 @@
#ifndef __LINUX_INITRD_H
#define __LINUX_INITRD_H
#define INITRD_MINOR 250 /* shouldn't collide with /dev/ram* too soon ... */
/* starting block # of image */
extern int rd_image_start;

View file

@ -92,7 +92,6 @@ enum
KERN_DOMAINNAME=8, /* string: domainname */
KERN_PANIC=15, /* int: panic timeout */
KERN_REALROOTDEV=16, /* real root device to mount after initrd */
KERN_SPARC_REBOOT=21, /* reboot command on Sparc */
KERN_CTLALTDEL=22, /* int: allow ctl-alt-del to reboot */

View file

@ -34,13 +34,6 @@ static int root_wait;
dev_t ROOT_DEV;
static int __init load_ramdisk(char *str)
{
pr_warn("ignoring the deprecated load_ramdisk= option\n");
return 1;
}
__setup("load_ramdisk=", load_ramdisk);
static int __init readonly(char *str)
{
if (*str)
@ -484,13 +477,11 @@ void __init prepare_namespace(void)
if (saved_root_name[0])
ROOT_DEV = parse_root_device(saved_root_name);
if (initrd_load(saved_root_name))
goto out;
initrd_load();
if (root_wait)
wait_for_root(saved_root_name);
mount_root(saved_root_name);
out:
devtmpfs_mount();
init_mount(".", "/", NULL, MS_MOVE, NULL);
init_chroot(".");

View file

@ -23,25 +23,15 @@ static inline __init int create_dev(char *name, dev_t dev)
}
#ifdef CONFIG_BLK_DEV_RAM
int __init rd_load_disk(int n);
int __init rd_load_image(char *from);
int __init rd_load_image(void);
#else
static inline int rd_load_disk(int n) { return 0; }
static inline int rd_load_image(char *from) { return 0; }
static inline int rd_load_image(void) { return 0; }
#endif
#ifdef CONFIG_BLK_DEV_INITRD
bool __init initrd_load(char *root_device_name);
void __init initrd_load(void);
#else
static inline bool initrd_load(char *root_device_name)
{
return false;
}
static inline void initrd_load(void) { }
#endif
/* Ensure that async file closing finished to prevent spurious errors. */

View file

@ -2,45 +2,20 @@
#include <linux/unistd.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/minix_fs.h>
#include <linux/romfs_fs.h>
#include <linux/initrd.h>
#include <linux/sched.h>
#include <linux/freezer.h>
#include <linux/kmod.h>
#include <uapi/linux/mount.h>
#include "do_mounts.h"
unsigned long initrd_start, initrd_end;
int initrd_below_start_ok;
static unsigned int real_root_dev; /* do_proc_dointvec cannot handle kdev_t */
static int __initdata mount_initrd = 1;
phys_addr_t phys_initrd_start __initdata;
unsigned long phys_initrd_size __initdata;
#ifdef CONFIG_SYSCTL
static const struct ctl_table kern_do_mounts_initrd_table[] = {
{
.procname = "real-root-dev",
.data = &real_root_dev,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
};
static __init int kernel_do_mounts_initrd_sysctls_init(void)
{
register_sysctl_init("kernel", kern_do_mounts_initrd_table);
return 0;
}
late_initcall(kernel_do_mounts_initrd_sysctls_init);
#endif /* CONFIG_SYSCTL */
static int __init no_initrd(char *str)
{
pr_warn("noinitrd option is deprecated and will be removed soon\n");
mount_initrd = 0;
return 1;
}
@ -70,85 +45,19 @@ static int __init early_initrd(char *p)
}
early_param("initrd", early_initrd);
static int __init init_linuxrc(struct subprocess_info *info, struct cred *new)
{
ksys_unshare(CLONE_FS | CLONE_FILES);
console_on_rootfs();
/* move initrd over / and chdir/chroot in initrd root */
init_chdir("/root");
init_mount(".", "/", NULL, MS_MOVE, NULL);
init_chroot(".");
ksys_setsid();
return 0;
}
static void __init handle_initrd(char *root_device_name)
{
struct subprocess_info *info;
static char *argv[] = { "linuxrc", NULL, };
extern char *envp_init[];
int error;
pr_warn("using deprecated initrd support, will be removed soon.\n");
real_root_dev = new_encode_dev(ROOT_DEV);
create_dev("/dev/root.old", Root_RAM0);
/* mount initrd on rootfs' /root */
mount_root_generic("/dev/root.old", root_device_name,
root_mountflags & ~MS_RDONLY);
init_mkdir("/old", 0700);
init_chdir("/old");
info = call_usermodehelper_setup("/linuxrc", argv, envp_init,
GFP_KERNEL, init_linuxrc, NULL, NULL);
if (!info)
return;
call_usermodehelper_exec(info, UMH_WAIT_PROC|UMH_FREEZABLE);
/* move initrd to rootfs' /old */
init_mount("..", ".", NULL, MS_MOVE, NULL);
/* switch root and cwd back to / of rootfs */
init_chroot("..");
if (new_decode_dev(real_root_dev) == Root_RAM0) {
init_chdir("/old");
return;
}
init_chdir("/");
ROOT_DEV = new_decode_dev(real_root_dev);
mount_root(root_device_name);
printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
error = init_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
if (!error)
printk("okay\n");
else {
if (error == -ENOENT)
printk("/initrd does not exist. Ignored.\n");
else
printk("failed\n");
printk(KERN_NOTICE "Unmounting old root\n");
init_umount("/old", MNT_DETACH);
}
}
bool __init initrd_load(char *root_device_name)
void __init initrd_load(void)
{
if (mount_initrd) {
create_dev("/dev/ram", Root_RAM0);
/*
* Load the initrd data into /dev/ram0. Execute it as initrd
* unless /dev/ram0 is supposed to be our actual root device,
* in that case the ram disk is just set up here, and gets
* mounted in the normal path.
* Load the initrd data into /dev/ram0.
*/
if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
init_unlink("/initrd.image");
handle_initrd(root_device_name);
return true;
if (rd_load_image()) {
pr_warn("using deprecated initrd support, will be removed in January 2027; "
"use initramfs instead or (as a last resort) /sys/firmware/initrd; "
"see section \"Workaround\" in "
"https://lore.kernel.org/lkml/20251010094047.3111495-1-safinaskar@gmail.com\n");
}
}
init_unlink("/initrd.image");
return false;
}

View file

@ -18,17 +18,11 @@
static struct file *in_file, *out_file;
static loff_t in_pos, out_pos;
static int __init prompt_ramdisk(char *str)
{
pr_warn("ignoring the deprecated prompt_ramdisk= option\n");
return 1;
}
__setup("prompt_ramdisk=", prompt_ramdisk);
int __initdata rd_image_start; /* starting block # of image */
static int __init ramdisk_start_setup(char *str)
{
pr_warn("ramdisk_start= option is deprecated and will be removed soon\n");
return kstrtoint(str, 0, &rd_image_start) == 0;
}
__setup("ramdisk_start=", ramdisk_start_setup);
@ -183,7 +177,7 @@ static unsigned long nr_blocks(struct file *file)
return i_size_read(inode) >> 10;
}
int __init rd_load_image(char *from)
int __init rd_load_image(void)
{
int res = 0;
unsigned long rd_blocks, devblocks, nr_disks;
@ -197,7 +191,7 @@ int __init rd_load_image(char *from)
if (IS_ERR(out_file))
goto out;
in_file = filp_open(from, O_RDONLY, 0);
in_file = filp_open("/initrd.image", O_RDONLY, 0);
if (IS_ERR(in_file))
goto noclose_input;
@ -226,10 +220,7 @@ int __init rd_load_image(char *from)
/*
* OK, time to copy in the data
*/
if (strcmp(from, "/initrd.image") == 0)
devblocks = nblocks;
else
devblocks = nr_blocks(in_file);
devblocks = nblocks;
if (devblocks == 0) {
printk(KERN_ERR "RAMDISK: could not determine device size\n");
@ -273,13 +264,6 @@ out:
return res;
}
int __init rd_load_disk(int n)
{
create_dev("/dev/root", ROOT_DEV);
create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, n));
return rd_load_image("/dev/root");
}
static int exit_code;
static int decompress_error;