Add an in-memory filesystem: pkernfs. Memory is donated to pkernfs by carving it out of the normal System RAM range with the memmap= cmdline parameter and then giving that same physical range to pkernfs with the pkernfs= cmdline parameter. A new filesystem is added; so far it doesn't do much except persist a super block at the start of the donated memory and allows itself to be mounted. --- fs/Kconfig | 1 + fs/Makefile | 3 ++ fs/pkernfs/Kconfig | 9 ++++ fs/pkernfs/Makefile | 6 +++ fs/pkernfs/pkernfs.c | 99 ++++++++++++++++++++++++++++++++++++++++++++ fs/pkernfs/pkernfs.h | 6 +++ 6 files changed, 124 insertions(+) create mode 100644 fs/pkernfs/Kconfig create mode 100644 fs/pkernfs/Makefile create mode 100644 fs/pkernfs/pkernfs.c create mode 100644 fs/pkernfs/pkernfs.h diff --git a/fs/Kconfig b/fs/Kconfig index aa7e03cc1941..33a9770ae657 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -331,6 +331,7 @@ source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/erofs/Kconfig" source "fs/vboxsf/Kconfig" +source "fs/pkernfs/Kconfig" endif # MISC_FILESYSTEMS diff --git a/fs/Makefile b/fs/Makefile index f9541f40be4e..1af35b494b5d 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -19,6 +19,9 @@ obj-y := open.o read_write.o file_table.o super.o \ obj-$(CONFIG_BUFFER_HEAD) += buffer.o mpage.o obj-$(CONFIG_PROC_FS) += proc_namespace.o + +obj-y += pkernfs/ + obj-$(CONFIG_LEGACY_DIRECT_IO) += direct-io.o obj-y += notify/ obj-$(CONFIG_EPOLL) += eventpoll.o diff --git a/fs/pkernfs/Kconfig b/fs/pkernfs/Kconfig new file mode 100644 index 000000000000..59621a1d9aef --- /dev/null +++ b/fs/pkernfs/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config PKERNFS_FS + bool "Persistent Kernel filesystem (pkernfs)" + help + An in-memory filesystem on top of reserved memory specified via + pkernfs= cmdline argument. Used for storing kernel state and + userspace memory which is preserved across kexec to support + live update. diff --git a/fs/pkernfs/Makefile b/fs/pkernfs/Makefile new file mode 100644 index 000000000000..17258cb77f58 --- /dev/null +++ b/fs/pkernfs/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for persistent kernel filesystem +# + +obj-$(CONFIG_PKERNFS_FS) += pkernfs.o diff --git a/fs/pkernfs/pkernfs.c b/fs/pkernfs/pkernfs.c new file mode 100644 index 000000000000..4c476ddc35b6 --- /dev/null +++ b/fs/pkernfs/pkernfs.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "pkernfs.h" +#include <linux/dcache.h> +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/fs_context.h> +#include <linux/io.h> + +static phys_addr_t pkernfs_base, pkernfs_size; +static void *pkernfs_mem; +static const struct super_operations pkernfs_super_ops = { }; + +static int pkernfs_fill_super(struct super_block *sb, struct fs_context *fc) +{ + struct inode *inode; + struct dentry *dentry; + struct pkernfs_sb *psb; + + pkernfs_mem = memremap(pkernfs_base, pkernfs_size, MEMREMAP_WB); + psb = (struct pkernfs_sb *) pkernfs_mem; + + if (psb->magic_number == PKERNFS_MAGIC_NUMBER) { + pr_info("pkernfs: Restoring from super block\n"); + } else { + pr_info("pkernfs: Clean super block; initialising\n"); + psb->magic_number = PKERNFS_MAGIC_NUMBER; + } + + sb->s_op = &pkernfs_super_ops; + + inode = new_inode(sb); + if (!inode) + return -ENOMEM; + + inode->i_ino = 1; + inode->i_mode = S_IFDIR; + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + inode->i_atime = inode->i_mtime = current_time(inode); + inode_set_ctime_current(inode); + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); + + dentry = d_make_root(inode); + if (!dentry) + return -ENOMEM; + sb->s_root = dentry; + + return 0; +} + +static int pkernfs_get_tree(struct fs_context *fc) +{ + return get_tree_nodev(fc, pkernfs_fill_super); +} + +static const struct fs_context_operations pkernfs_context_ops = { + .get_tree = pkernfs_get_tree, +}; + +static int pkernfs_init_fs_context(struct fs_context *const fc) +{ + fc->ops = &pkernfs_context_ops; + return 0; +} + +static struct file_system_type pkernfs_fs_type = { + .owner = THIS_MODULE, + .name = "pkernfs", + .init_fs_context = pkernfs_init_fs_context, + .kill_sb = kill_litter_super, + .fs_flags = FS_USERNS_MOUNT, +}; + +static int __init pkernfs_init(void) +{ + int ret; + + ret = register_filesystem(&pkernfs_fs_type); + return ret; +} + +/** + * Format: pkernfs=<size>:<base> + * Just like: memmap=nn[KMG]!ss[KMG] + */ +static int __init parse_pkernfs_extents(char *p) +{ + pkernfs_size = memparse(p, &p); + p++; /* Skip over ! char */ + pkernfs_base = memparse(p, &p); + return 0; +} + +early_param("pkernfs", parse_pkernfs_extents); + +MODULE_ALIAS_FS("pkernfs"); +module_init(pkernfs_init); diff --git a/fs/pkernfs/pkernfs.h b/fs/pkernfs/pkernfs.h new file mode 100644 index 000000000000..bd1e2a6fd336 --- /dev/null +++ b/fs/pkernfs/pkernfs.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#define PKERNFS_MAGIC_NUMBER 0x706b65726e6673 +struct pkernfs_sb { + unsigned long magic_number; +}; -- 2.40.1