Linux-Fsdevel Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 1/2] fs:regfs: add register easy filesystem
@ 2020-10-20  6:30 Zou Cao
  2020-10-20  6:30 ` [PATCH 2/2] fs:regfs: add panic notifier callback for saving regs Zou Cao
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Zou Cao @ 2020-10-20  6:30 UTC (permalink / raw)
  To: viro; +Cc: linux-kernel, linux-fsdevel

register filesystem is mapping the register into file dentry, it
will use the io readio to get the register val. DBT file is use
to decript the register tree, you can use it as follow:

	mount -t regfs -o dtb=test.dtb none /mnt

	test.dts:
	/ {

	compatible = "hisilicon,hi6220-hikey", "hisilicon,hi6220";
	#address-cells = <0x2>;
	#size-cells = <0x2>;
	model = "HiKey Development Board";

	gic-v3-dist{
		reg = <0x0 0x8000000 0x0 0x10000>;
		GIC_CTRL {
			offset = <0x0>;
		};
		GICD_TYPER {
			offset = <0x4>;
		};
	   };
	};

it will create all regiter dentry file in /mnt

Signed-off-by: Zou Cao <zoucao@linux.alibaba.com>
---
 fs/Kconfig             |   1 +
 fs/Makefile            |   1 +
 fs/regfs/Kconfig       |   7 +
 fs/regfs/Makefile      |   8 ++
 fs/regfs/file.c        | 107 +++++++++++++++
 fs/regfs/inode.c       | 354 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/regfs/internal.h    |  32 +++++
 fs/regfs/regfs_inode.h |  32 +++++
 fs/regfs/supper.c      |  71 ++++++++++
 9 files changed, 613 insertions(+)
 create mode 100644 fs/regfs/Kconfig
 create mode 100644 fs/regfs/Makefile
 create mode 100644 fs/regfs/file.c
 create mode 100644 fs/regfs/inode.c
 create mode 100644 fs/regfs/internal.h
 create mode 100644 fs/regfs/regfs_inode.h
 create mode 100644 fs/regfs/supper.c

diff --git a/fs/Kconfig b/fs/Kconfig
index a88aa3a..d95acaf 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -324,6 +324,7 @@ endif # NETWORK_FILESYSTEMS
 source "fs/nls/Kconfig"
 source "fs/dlm/Kconfig"
 source "fs/unicode/Kconfig"
+source "fs/regfs/Kconfig"
 
 config IO_WQ
 	bool
diff --git a/fs/Makefile b/fs/Makefile
index 2ce5112..24f3878 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -136,3 +136,4 @@ obj-$(CONFIG_EFIVAR_FS)		+= efivarfs/
 obj-$(CONFIG_EROFS_FS)		+= erofs/
 obj-$(CONFIG_VBOXSF_FS)		+= vboxsf/
 obj-$(CONFIG_ZONEFS_FS)		+= zonefs/
+obj-$(CONFIG_REGFS_FS)		+= zonefs/
diff --git a/fs/regfs/Kconfig b/fs/regfs/Kconfig
new file mode 100644
index 0000000..74ba85b
--- /dev/null
+++ b/fs/regfs/Kconfig
@@ -0,0 +1,7 @@
+config REGFS_FS
+	tristate "registers filesystem support"
+	depends on ARM64
+	help
+	  regfs support the read and write register of device resource by
+	  dentry filesystem, it is more easy to support bsp debug. it also
+	  support to printk the register val when panic
diff --git a/fs/regfs/Makefile b/fs/regfs/Makefile
new file mode 100644
index 0000000..26d5eef
--- /dev/null
+++ b/fs/regfs/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+#Makefile for the linux ramfs routines.
+#
+
+obj-y += regfs.o
+
+regfs-objs += inode.o file.o supper.o
diff --git a/fs/regfs/file.c b/fs/regfs/file.c
new file mode 100644
index 0000000..6cd9f3d
--- /dev/null
+++ b/fs/regfs/file.c
@@ -0,0 +1,107 @@
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/mpage.h>
+#include <linux/writeback.h>
+#include <linux/buffer_head.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/memcontrol.h>
+#include <linux/iomap.h>
+#include <linux/pagemap.h>
+#include <linux/uio.h>
+#include <linux/buffer_head.h>
+#include <linux/dax.h>
+#include <linux/writeback.h>
+#include <linux/swap.h>
+#include <linux/bio.h>
+#include <linux/sched/signal.h>
+#include <linux/migrate.h>
+
+#include "regfs_inode.h"
+#include "internal.h"
+
+ssize_t regfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+	struct file *file = iocb->ki_filp;
+	struct inode *inode = file->f_mapping->host;
+	ssize_t ret;
+
+	inode_lock(inode);
+	ret = generic_write_checks(iocb, from);
+	if (ret > 0)
+		ret = __generic_file_write_iter(iocb, from);
+	inode_unlock(inode);
+
+	if (ret > 0)
+		ret = generic_write_sync(iocb, ret);
+	return ret;
+}
+
+static ssize_t regfs_file_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
+{
+	struct address_space *mapping = file->f_mapping;
+	struct regfs_inode_info  *info = REGFS_I(mapping->host);
+	char str[64];
+	unsigned long val;
+
+	val = readl_relaxed(info->base + info->offset);
+
+	loc_debug("name:%s base:%p val:%lx\n"
+			, file->f_path.dentry->d_iname
+			, info->base + info->offset
+			, val);
+
+	snprintf(str, 64, "%lx", val);
+
+	return simple_read_from_buffer(buf, len, ppos, str, strlen(str));
+}
+
+static ssize_t regfs_file_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos)
+{
+	struct address_space *mapping = file->f_mapping;
+	struct regfs_inode_info  *info = REGFS_I(mapping->host);
+	char str[67];
+	unsigned long val = 0;
+	loff_t pos = *ppos;
+	size_t res;
+
+	if (pos < 0)
+		return -EINVAL;
+	if (pos >= len || len > 66)
+		return 0;
+
+	res = copy_from_user(str, buf, len);
+	if (res)
+		return -EFAULT;
+	str[len] = 0;
+
+	if (kstrtoul(str, 16, &val) < 0)
+		return -EINVAL;
+
+	writel_relaxed(val, info->base + info->offset);
+
+	loc_debug("name:%s base:%p val:%lx\n"
+			, file->f_path.dentry->d_iname
+			, info->base + info->offset
+			, val);
+
+	return len;
+}
+
+const struct file_operations regfs_file_operations = {
+	.read = regfs_file_read,
+	.write = regfs_file_write,
+};
+
+const struct inode_operations regfs_file_inode_operations = {
+	.setattr	= simple_setattr,
+	.getattr	= simple_getattr,
+};
+
+const struct address_space_operations regfs_aops = {
+	.readpage   = simple_readpage,
+	.write_begin    = simple_write_begin,
+	.write_end  = simple_write_end,
+	.set_page_dirty = __set_page_dirty_buffers,
+};
+
diff --git a/fs/regfs/inode.c b/fs/regfs/inode.c
new file mode 100644
index 0000000..1643fcd
--- /dev/null
+++ b/fs/regfs/inode.c
@@ -0,0 +1,354 @@
+/*
+ * Resizable simple ram filesystem for Linux.
+ *
+ * Copyright (C) 2000 Linus Torvalds.
+ *               2000 Transmeta Corp.
+ *
+ * Usage limits added by David Gibson, Linuxcare Australia.
+ * This file is released under the GPL.
+ */
+
+/*
+ * NOTE! This filesystem is probably most useful
+ * not as a real filesystem, but as an example of
+ * how virtual filesystems can be written.
+ *
+ * It doesn't get much simpler than this. Consider
+ * that this file implements the full semantics of
+ * a POSIX-compliant read-write filesystem.
+ *
+ * Note in particular how the filesystem does not
+ * need to implement any data structures of its own
+ * to keep track of the virtual data: using the VFS
+ * caches is sufficient.
+ */
+
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/backing-dev.h>
+#include <linux/sched.h>
+#include <linux/parser.h>
+#include <linux/magic.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/libfdt.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include "regfs_inode.h"
+#include "internal.h"
+
+static LIST_HEAD(regfs_head);
+
+static const struct inode_operations regfs_dir_inode_operations;
+int regfs_debug;
+module_param(regfs_debug, int, S_IRUGO);
+MODULE_PARM_DESC(regfs_debug, "enable regfs debug mode");
+
+struct inode *regfs_get_inode(struct super_block *sb, const struct inode *dir, umode_t mode, dev_t dev)
+{
+	struct inode *inode = new_inode(sb);
+
+	if (inode) {
+		inode->i_ino = get_next_ino();
+		inode_init_owner(inode, dir, mode);
+		inode->i_mapping->a_ops = &regfs_aops;
+		//inode->i_mapping->backing_dev_info = &regfs_backing_dev_info;
+		mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
+		mapping_set_unevictable(inode->i_mapping);
+		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
+		switch (mode & S_IFMT) {
+		default:
+			init_special_inode(inode, mode, dev);
+			break;
+		case S_IFREG:
+			inode->i_op = &regfs_file_inode_operations;
+			inode->i_fop = &regfs_file_operations;
+			break;
+		case S_IFDIR:
+			inode->i_op = &regfs_dir_inode_operations;
+			inode->i_fop = &simple_dir_operations;
+
+			/* directory inodes start off with i_nlink == 2 (for "." entry) */
+			inc_nlink(inode);
+			break;
+		case S_IFLNK:
+			inode->i_op = &page_symlink_inode_operations;
+			break;
+		}
+	}
+
+	return inode;
+}
+
+static const struct inode_operations regfs_dir_inode_operations = {
+	.lookup		= simple_lookup,
+};
+
+static struct dentry *new_dentry_create(struct super_block *sb, struct dentry *parent,
+		 const char *name, bool is_dir, struct res_data *res)
+{
+	struct dentry *dentry;
+	struct inode *inode;
+	struct regfs_inode_info *ei;
+	struct regfs_fs_info *fsi = sb->s_fs_info;
+
+	dentry = d_alloc_name(parent, name);
+	if (!dentry)
+		return NULL;
+
+	inode = new_inode(sb);
+	if (!inode)
+		goto out;
+
+	ei = REGFS_I(inode);
+	inode->i_ino = get_next_ino();;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+	inode->i_uid =  GLOBAL_ROOT_UID;
+	inode->i_gid =  GLOBAL_ROOT_GID;
+	if (is_dir) {
+		inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR;
+		inode->i_op = &regfs_dir_inode_operations;
+		inode->i_fop = &simple_dir_operations;
+		list_add(&ei->list, &fsi->list);
+	} else {
+		inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
+		inode->i_op = &regfs_file_inode_operations;
+		inode->i_fop = &regfs_file_operations;
+		inc_nlink(inode);
+	}
+	ei->base = (void *)res->base;
+	ei->offset = res->offset;
+	ei->type = res->type;
+
+	d_add(dentry, inode);
+
+	loc_debug("new dentry io base:%llx offset:%llx ei:%llx\n", (u64)ei->base, (u64)ei->offset, (u64)ei);
+	return dentry;
+out:
+	dput(dentry);
+	return NULL;
+}
+
+static void node_transfer_dentry(struct super_block *sb)
+{
+	struct regfs_fs_info *fsi = sb->s_fs_info;
+	void *blob = fsi->dtb_buf;
+	const char *pathp;
+	int node_offset, depth = -1;
+	struct dentry *parent = NULL;
+	u64 parent_base;
+
+	for (node_offset = fdt_next_node(blob, -1, &depth);
+		node_offset >= 0 && depth >= 0;
+		node_offset = fdt_next_node(blob, node_offset, &depth)) {
+
+		const struct fdt_property *prop;
+		struct res_data res;
+
+		pathp = fdt_get_name(blob, node_offset, NULL);
+		prop = (void *)fdt_getprop(blob, node_offset, "reg", NULL);
+
+		if (prop) {
+			unsigned long phys;
+
+			phys = fdt32_to_cpu(((const __be32 *)prop)[1]);
+			res.type = RES_TYPE_RANGE;
+			res.offset = fdt32_to_cpu(((const __be32 *)prop)[3]);
+			res.base = (u64)ioremap(phys, res.offset);
+
+			if (!res.base) {
+				parent = NULL;
+				parent_base = 0;
+				continue;
+			}
+
+			loc_debug("%s reg:%lx size:%lx map:%llx\n\n", pathp
+				 , (unsigned long) fdt32_to_cpu(((const __be32 *)prop)[1])
+				 , (unsigned long) fdt32_to_cpu(((const __be32 *)prop)[3])
+				 , (u64)res.base);
+
+			parent = new_dentry_create(sb, sb->s_root, (const char *)pathp, true, &res);
+			parent_base = res.base;
+
+		} else {
+			// parent dentry is create failed, igonre all child dentry
+			if (!parent)
+				continue;
+
+			prop = (void *)fdt_getprop(blob, node_offset, "offset", NULL);
+			if (prop) {
+
+				res.offset = fdt32_to_cpu(*(const __be32 *)prop);
+				res.base = parent_base;
+				res.type = RES_TYPE_ITEM;
+
+				new_dentry_create(sb, parent, (const char *) pathp, false, &res);
+				loc_debug("%s offset:%lx\n", pathp, (unsigned long)fdt32_to_cpu(*(const __be32 *)prop));
+			}
+		}
+	}
+}
+
+static int parse_options(char *options, struct super_block *sb)
+{
+	char *p;
+	int ret = -EINVAL;
+	struct regfs_fs_info *fsi;
+	size_t msize = INT_MAX;
+
+	fsi = sb->s_fs_info;
+
+	if (!options)
+		return -EINVAL;
+
+	while ((p = strsep(&options, ",")) != NULL) {
+		char *name, *name_val;
+
+		name = strsep(&p, "=");
+		if (name == NULL)
+			goto failed;
+
+		name_val = strsep(&p, "=");
+		if (name_val == NULL)
+			goto failed;
+
+		//get resource address
+		if (!strcmp(name, "dtb")) {
+			ret = kernel_read_file_from_path(name_val, &fsi->dtb_buf, &fsi->dtb_len, msize, READING_UNKNOWN);
+			if (ret) {
+				pr_err("load %s failed\n", name_val);
+				goto failed;
+			}
+		} else
+			goto failed;
+	};
+
+	return 0;
+
+failed:
+	return ret;
+}
+
+int regfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct regfs_fs_info *fsi;
+	struct inode *inode;
+	int err;
+
+	fsi = kzalloc(sizeof(struct regfs_fs_info), GFP_KERNEL);
+	if (!fsi)
+		return -ENOMEM;
+
+	sb->s_fs_info = fsi;
+	fsi->sb = sb;
+
+	err = parse_options((char *)data, sb);
+	if (err)
+		goto out;
+
+	sb->s_maxbytes		= MAX_LFS_FILESIZE;
+	sb->s_blocksize		= PAGE_SIZE;
+	sb->s_blocksize_bits	= PAGE_SHIFT;
+	sb->s_magic		= RAMFS_MAGIC;
+	sb->s_op		= &regfs_ops;
+	sb->s_time_gran		= 1;
+
+	inode = regfs_get_inode(sb, NULL, S_IFDIR, 0);
+	sb->s_root = d_make_root(inode);
+	if (!sb->s_root)
+		goto out;
+
+	INIT_LIST_HEAD(&fsi->list);
+	INIT_LIST_HEAD(&fsi->regfs_head);
+	list_add(&fsi->regfs_head, &regfs_head);
+
+	return 0;
+
+out:
+	if (fsi)
+		kfree(fsi);
+
+	return err;
+}
+
+struct dentry *regfs_mount(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	struct dentry *root_dentry;
+	struct super_block *sb;
+
+	root_dentry = mount_nodev(fs_type, flags, data, regfs_fill_super);
+
+	sb = root_dentry->d_sb;
+
+	if (sb->s_root) {
+		node_transfer_dentry(sb);
+	} else
+		return NULL;
+
+	return root_dentry;
+}
+
+static void regfs_kill_sb(struct super_block *sb)
+{
+	struct regfs_fs_info *fsi = sb->s_fs_info;
+	struct regfs_inode_info *info_res;
+
+	list_for_each_entry(info_res,  &fsi->list, list)
+		iounmap(info_res->base);
+
+	if (fsi) {
+		if (fsi->dtb_buf)
+			vfree(fsi->dtb_buf);
+		list_del(&fsi->regfs_head);
+		kfree(sb->s_fs_info);
+	}
+	kill_litter_super(sb);
+}
+
+static struct file_system_type regfs_fs_type = {
+	.name		= "regfs",
+	.mount		= regfs_mount,
+	.kill_sb	= regfs_kill_sb,
+	.fs_flags	= FS_USERNS_MOUNT,
+};
+
+static void init_once(void *foo)
+{
+	struct regfs_inode_info *ei = (struct regfs_inode_info *) foo;
+
+	inode_init_once(&ei->vfs_inode);
+}
+
+static int __init init_regfs_fs(void)
+{
+
+	regfs_inode_cachep = kmem_cache_create_usercopy("regfs_inode_cache",
+				sizeof(struct regfs_inode_info), 0,
+				(SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD | SLAB_ACCOUNT),
+				0, 0, init_once);
+
+	if (!regfs_inode_cachep)
+		return -ENOMEM;
+
+	return  register_filesystem(&regfs_fs_type);
+}
+
+static void __exit exit_regfs_fs(void)
+{
+	unregister_filesystem(&regfs_fs_type);
+	rcu_barrier();
+	kmem_cache_destroy(regfs_inode_cachep);
+}
+
+module_init(init_regfs_fs);
+module_exit(exit_regfs_fs);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Zou Cao<zoucaox@linux.alibaba.com>");
diff --git a/fs/regfs/internal.h b/fs/regfs/internal.h
new file mode 100644
index 0000000..61577bb
--- /dev/null
+++ b/fs/regfs/internal.h
@@ -0,0 +1,32 @@
+#ifndef __INTERNAL_H__
+#define __INTERNAL_H__
+
+#define loc_debug(fmt, ...)   \
+	do {                       \
+		if (regfs_debug)   \
+			printk(fmt, ##__VA_ARGS__); \
+	} while (0)
+
+struct regfs_fs_info {
+	struct super_block *sb;
+	void   *dtb_buf;
+	u64  	dtb_len;
+	u64  	iomem;
+	u64  size;
+	// supper ifs list
+	struct list_head regfs_head;
+	// io map list of inode
+	struct list_head list;
+};
+
+#define RAMFS_DEFAULT_MODE	0755
+
+extern int regfs_debug;
+extern const struct address_space_operations regfs_aops;
+extern const struct inode_operations regfs_file_inode_operations;
+extern const struct file_operations regfs_file_operations;
+extern const struct super_operations regfs_ops;
+extern struct kmem_cache *regfs_inode_cachep;
+int regfs_supper_init(void);
+
+#endif
diff --git a/fs/regfs/regfs_inode.h b/fs/regfs/regfs_inode.h
new file mode 100644
index 0000000..0883e05
--- /dev/null
+++ b/fs/regfs/regfs_inode.h
@@ -0,0 +1,32 @@
+#ifndef __REGFS_INODE_H__
+#define __REGFS_INODE_H__
+
+enum res_type {
+	RES_TYPE_NONE = 0,
+	RES_TYPE_RANGE,
+	RES_TYPE_ITEM,
+};
+
+struct regfs_inode_info {
+	unsigned long flag;
+	struct inode vfs_inode;
+	void __iomem *base;
+	u64  offset;
+	u64  val;  //for panic save
+	struct list_head list;  //inode list
+	enum res_type type;
+};
+
+struct res_data {
+	enum res_type type;
+	u64  base;
+	u64  offset;
+};
+
+static inline struct regfs_inode_info *REGFS_I(struct inode *inode)
+{
+	return container_of(inode, struct regfs_inode_info, vfs_inode);
+}
+
+#endif
+
diff --git a/fs/regfs/supper.c b/fs/regfs/supper.c
new file mode 100644
index 0000000..35733b6
--- /dev/null
+++ b/fs/regfs/supper.c
@@ -0,0 +1,71 @@
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/backing-dev.h>
+#include <linux/sched.h>
+#include <linux/parser.h>
+#include <linux/magic.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/iversion.h>
+#include "regfs_inode.h"
+#include "internal.h"
+
+struct kmem_cache *regfs_inode_cachep;
+
+/*
+ * Display the mount options in /proc/mounts.
+ */
+
+static struct inode *regfs_alloc_inode(struct super_block *sb)
+{
+	struct regfs_inode_info *ei;
+
+	ei = kmem_cache_alloc(regfs_inode_cachep, GFP_NOFS);
+	if (!ei)
+		return NULL;
+
+	inode_set_iversion(&ei->vfs_inode, 1);
+	ei->type = RES_TYPE_NONE;
+
+	return &ei->vfs_inode;
+}
+
+static void regfs_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+
+	kmem_cache_free(regfs_inode_cachep, REGFS_I(inode));
+}
+
+static void regfs_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, regfs_i_callback);
+}
+
+static int regfs_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+	return 0;
+}
+
+static int regfs_drop_inode(struct inode *inode)
+{
+	return generic_drop_inode(inode);
+}
+
+const struct super_operations regfs_ops = {
+	.alloc_inode    = regfs_alloc_inode,
+	.destroy_inode  = regfs_destroy_inode,
+	.write_inode    = regfs_write_inode,
+	.drop_inode = regfs_drop_inode,
+};
+
+int regfs_supper_init(void)
+{
+	return 0;
+}
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/2] fs:regfs: add panic notifier callback for saving regs
  2020-10-20  6:30 [PATCH 1/2] fs:regfs: add register easy filesystem Zou Cao
@ 2020-10-20  6:30 ` Zou Cao
  2020-10-23  2:57 ` [PATCH 1/2] fs:regfs: add register easy filesystem zc
  2020-10-29  2:42 ` Al Viro
  2 siblings, 0 replies; 8+ messages in thread
From: Zou Cao @ 2020-10-20  6:30 UTC (permalink / raw)
  To: viro; +Cc: linux-kernel, linux-fsdevel

register panic notifier callback for saveing regs, add a module
param regfs_panic to enable the show reg info when panic.

Signed-off-by: Zou Cao <zoucao@linux.alibaba.com>
---
 fs/regfs/inode.c | 39 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/fs/regfs/inode.c b/fs/regfs/inode.c
index 1643fcd..6c79f73 100644
--- a/fs/regfs/inode.c
+++ b/fs/regfs/inode.c
@@ -46,10 +46,41 @@
 static LIST_HEAD(regfs_head);
 
 static const struct inode_operations regfs_dir_inode_operations;
-int regfs_debug;
+int regfs_debug = 1;
 module_param(regfs_debug, int, S_IRUGO);
 MODULE_PARM_DESC(regfs_debug, "enable regfs debug mode");
 
+static int regfs_panic = 1;
+module_param(regfs_panic, int, S_IRUGO);
+MODULE_PARM_DESC(regfs_debug, "printk the register when panic");
+
+//save all register val when panic
+static int regfs_panic_event(struct notifier_block *self,
+		 unsigned long val, void *data)
+{
+	struct regfs_fs_info *fsi;
+	struct inode *inode, *next;
+
+
+	list_for_each_entry(fsi, &regfs_head, regfs_head) {
+		list_for_each_entry_safe(inode, next, &fsi->sb->s_inodes, i_sb_list) {
+			struct regfs_inode_info *info =  REGFS_I(inode);;
+			//save the regs val
+			if (info->type == RES_TYPE_ITEM) {
+				info->val = readl_relaxed(info->base + info->offset);
+				if (regfs_panic)
+					printk("%llx:%llx\n", (u64)(info->base + info->offset), info->val);
+			}
+		}
+	}
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block regfs_panic_event_nb = {
+	.notifier_call   = regfs_panic_event,
+};
+
 struct inode *regfs_get_inode(struct super_block *sb, const struct inode *dir, umode_t mode, dev_t dev)
 {
 	struct inode *inode = new_inode(sb);
@@ -328,6 +359,7 @@ static void init_once(void *foo)
 
 static int __init init_regfs_fs(void)
 {
+	int ret;
 
 	regfs_inode_cachep = kmem_cache_create_usercopy("regfs_inode_cache",
 				sizeof(struct regfs_inode_info), 0,
@@ -337,11 +369,16 @@ static int __init init_regfs_fs(void)
 	if (!regfs_inode_cachep)
 		return -ENOMEM;
 
+	ret = atomic_notifier_chain_register(&panic_notifier_list, &regfs_panic_event_nb);
+	if (ret)
+		pr_warn("regfs regiter panic notifier failed\n");
+
 	return  register_filesystem(&regfs_fs_type);
 }
 
 static void __exit exit_regfs_fs(void)
 {
+	atomic_notifier_chain_unregister(&panic_notifier_list, &regfs_panic_event_nb);
 	unregister_filesystem(&regfs_fs_type);
 	rcu_barrier();
 	kmem_cache_destroy(regfs_inode_cachep);
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] fs:regfs: add register easy filesystem
  2020-10-20  6:30 [PATCH 1/2] fs:regfs: add register easy filesystem Zou Cao
  2020-10-20  6:30 ` [PATCH 2/2] fs:regfs: add panic notifier callback for saving regs Zou Cao
@ 2020-10-23  2:57 ` zc
  2020-10-27  9:16   ` zc
       [not found]   ` <fe9f0382-da87-77ab-75ab-5a4bec0a9a21@linux.alibaba.com>
  2020-10-29  2:42 ` Al Viro
  2 siblings, 2 replies; 8+ messages in thread
From: zc @ 2020-10-23  2:57 UTC (permalink / raw)
  To: viro; +Cc: linux-kernel, linux-fsdevel, zoucao

Hi viro:

   Through regfs is very sample and easy,  but i think it is a Interest 
,  could give  some suggestions?


Regards,

zc

在 2020/10/20 下午2:30, Zou Cao 写道:
> register filesystem is mapping the register into file dentry, it
> will use the io readio to get the register val. DBT file is use
> to decript the register tree, you can use it as follow:
>
> 	mount -t regfs -o dtb=test.dtb none /mnt
>
> 	test.dts:
> 	/ {
>
> 	compatible = "hisilicon,hi6220-hikey", "hisilicon,hi6220";
> 	#address-cells = <0x2>;
> 	#size-cells = <0x2>;
> 	model = "HiKey Development Board";
>
> 	gic-v3-dist{
> 		reg = <0x0 0x8000000 0x0 0x10000>;
> 		GIC_CTRL {
> 			offset = <0x0>;
> 		};
> 		GICD_TYPER {
> 			offset = <0x4>;
> 		};
> 	   };
> 	};
>
> it will create all regiter dentry file in /mnt
>
> Signed-off-by: Zou Cao <zoucao@linux.alibaba.com>
> ---
>   fs/Kconfig             |   1 +
>   fs/Makefile            |   1 +
>   fs/regfs/Kconfig       |   7 +
>   fs/regfs/Makefile      |   8 ++
>   fs/regfs/file.c        | 107 +++++++++++++++
>   fs/regfs/inode.c       | 354 +++++++++++++++++++++++++++++++++++++++++++++++++
>   fs/regfs/internal.h    |  32 +++++
>   fs/regfs/regfs_inode.h |  32 +++++
>   fs/regfs/supper.c      |  71 ++++++++++
>   9 files changed, 613 insertions(+)
>   create mode 100644 fs/regfs/Kconfig
>   create mode 100644 fs/regfs/Makefile
>   create mode 100644 fs/regfs/file.c
>   create mode 100644 fs/regfs/inode.c
>   create mode 100644 fs/regfs/internal.h
>   create mode 100644 fs/regfs/regfs_inode.h
>   create mode 100644 fs/regfs/supper.c
>
> diff --git a/fs/Kconfig b/fs/Kconfig
> index a88aa3a..d95acaf 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -324,6 +324,7 @@ endif # NETWORK_FILESYSTEMS
>   source "fs/nls/Kconfig"
>   source "fs/dlm/Kconfig"
>   source "fs/unicode/Kconfig"
> +source "fs/regfs/Kconfig"
>   
>   config IO_WQ
>   	bool
> diff --git a/fs/Makefile b/fs/Makefile
> index 2ce5112..24f3878 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -136,3 +136,4 @@ obj-$(CONFIG_EFIVAR_FS)		+= efivarfs/
>   obj-$(CONFIG_EROFS_FS)		+= erofs/
>   obj-$(CONFIG_VBOXSF_FS)		+= vboxsf/
>   obj-$(CONFIG_ZONEFS_FS)		+= zonefs/
> +obj-$(CONFIG_REGFS_FS)		+= zonefs/
> diff --git a/fs/regfs/Kconfig b/fs/regfs/Kconfig
> new file mode 100644
> index 0000000..74ba85b
> --- /dev/null
> +++ b/fs/regfs/Kconfig
> @@ -0,0 +1,7 @@
> +config REGFS_FS
> +	tristate "registers filesystem support"
> +	depends on ARM64
> +	help
> +	  regfs support the read and write register of device resource by
> +	  dentry filesystem, it is more easy to support bsp debug. it also
> +	  support to printk the register val when panic
> diff --git a/fs/regfs/Makefile b/fs/regfs/Makefile
> new file mode 100644
> index 0000000..26d5eef
> --- /dev/null
> +++ b/fs/regfs/Makefile
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +#Makefile for the linux ramfs routines.
> +#
> +
> +obj-y += regfs.o
> +
> +regfs-objs += inode.o file.o supper.o
> diff --git a/fs/regfs/file.c b/fs/regfs/file.c
> new file mode 100644
> index 0000000..6cd9f3d
> --- /dev/null
> +++ b/fs/regfs/file.c
> @@ -0,0 +1,107 @@
> +#include <linux/fs.h>
> +#include <linux/mm.h>
> +#include <linux/mpage.h>
> +#include <linux/writeback.h>
> +#include <linux/buffer_head.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/memcontrol.h>
> +#include <linux/iomap.h>
> +#include <linux/pagemap.h>
> +#include <linux/uio.h>
> +#include <linux/buffer_head.h>
> +#include <linux/dax.h>
> +#include <linux/writeback.h>
> +#include <linux/swap.h>
> +#include <linux/bio.h>
> +#include <linux/sched/signal.h>
> +#include <linux/migrate.h>
> +
> +#include "regfs_inode.h"
> +#include "internal.h"
> +
> +ssize_t regfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
> +{
> +	struct file *file = iocb->ki_filp;
> +	struct inode *inode = file->f_mapping->host;
> +	ssize_t ret;
> +
> +	inode_lock(inode);
> +	ret = generic_write_checks(iocb, from);
> +	if (ret > 0)
> +		ret = __generic_file_write_iter(iocb, from);
> +	inode_unlock(inode);
> +
> +	if (ret > 0)
> +		ret = generic_write_sync(iocb, ret);
> +	return ret;
> +}
> +
> +static ssize_t regfs_file_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
> +{
> +	struct address_space *mapping = file->f_mapping;
> +	struct regfs_inode_info  *info = REGFS_I(mapping->host);
> +	char str[64];
> +	unsigned long val;
> +
> +	val = readl_relaxed(info->base + info->offset);
> +
> +	loc_debug("name:%s base:%p val:%lx\n"
> +			, file->f_path.dentry->d_iname
> +			, info->base + info->offset
> +			, val);
> +
> +	snprintf(str, 64, "%lx", val);
> +
> +	return simple_read_from_buffer(buf, len, ppos, str, strlen(str));
> +}
> +
> +static ssize_t regfs_file_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos)
> +{
> +	struct address_space *mapping = file->f_mapping;
> +	struct regfs_inode_info  *info = REGFS_I(mapping->host);
> +	char str[67];
> +	unsigned long val = 0;
> +	loff_t pos = *ppos;
> +	size_t res;
> +
> +	if (pos < 0)
> +		return -EINVAL;
> +	if (pos >= len || len > 66)
> +		return 0;
> +
> +	res = copy_from_user(str, buf, len);
> +	if (res)
> +		return -EFAULT;
> +	str[len] = 0;
> +
> +	if (kstrtoul(str, 16, &val) < 0)
> +		return -EINVAL;
> +
> +	writel_relaxed(val, info->base + info->offset);
> +
> +	loc_debug("name:%s base:%p val:%lx\n"
> +			, file->f_path.dentry->d_iname
> +			, info->base + info->offset
> +			, val);
> +
> +	return len;
> +}
> +
> +const struct file_operations regfs_file_operations = {
> +	.read = regfs_file_read,
> +	.write = regfs_file_write,
> +};
> +
> +const struct inode_operations regfs_file_inode_operations = {
> +	.setattr	= simple_setattr,
> +	.getattr	= simple_getattr,
> +};
> +
> +const struct address_space_operations regfs_aops = {
> +	.readpage   = simple_readpage,
> +	.write_begin    = simple_write_begin,
> +	.write_end  = simple_write_end,
> +	.set_page_dirty = __set_page_dirty_buffers,
> +};
> +
> diff --git a/fs/regfs/inode.c b/fs/regfs/inode.c
> new file mode 100644
> index 0000000..1643fcd
> --- /dev/null
> +++ b/fs/regfs/inode.c
> @@ -0,0 +1,354 @@
> +/*
> + * Resizable simple ram filesystem for Linux.
> + *
> + * Copyright (C) 2000 Linus Torvalds.
> + *               2000 Transmeta Corp.
> + *
> + * Usage limits added by David Gibson, Linuxcare Australia.
> + * This file is released under the GPL.
> + */
> +
> +/*
> + * NOTE! This filesystem is probably most useful
> + * not as a real filesystem, but as an example of
> + * how virtual filesystems can be written.
> + *
> + * It doesn't get much simpler than this. Consider
> + * that this file implements the full semantics of
> + * a POSIX-compliant read-write filesystem.
> + *
> + * Note in particular how the filesystem does not
> + * need to implement any data structures of its own
> + * to keep track of the virtual data: using the VFS
> + * caches is sufficient.
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/pagemap.h>
> +#include <linux/highmem.h>
> +#include <linux/time.h>
> +#include <linux/init.h>
> +#include <linux/string.h>
> +#include <linux/backing-dev.h>
> +#include <linux/sched.h>
> +#include <linux/parser.h>
> +#include <linux/magic.h>
> +#include <linux/slab.h>
> +#include <linux/kernel.h>
> +#include <linux/of.h>
> +#include <linux/of_fdt.h>
> +#include <linux/libfdt.h>
> +#include <asm/uaccess.h>
> +#include <linux/module.h>
> +#include "regfs_inode.h"
> +#include "internal.h"
> +
> +static LIST_HEAD(regfs_head);
> +
> +static const struct inode_operations regfs_dir_inode_operations;
> +int regfs_debug;
> +module_param(regfs_debug, int, S_IRUGO);
> +MODULE_PARM_DESC(regfs_debug, "enable regfs debug mode");
> +
> +struct inode *regfs_get_inode(struct super_block *sb, const struct inode *dir, umode_t mode, dev_t dev)
> +{
> +	struct inode *inode = new_inode(sb);
> +
> +	if (inode) {
> +		inode->i_ino = get_next_ino();
> +		inode_init_owner(inode, dir, mode);
> +		inode->i_mapping->a_ops = &regfs_aops;
> +		//inode->i_mapping->backing_dev_info = &regfs_backing_dev_info;
> +		mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
> +		mapping_set_unevictable(inode->i_mapping);
> +		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
> +		switch (mode & S_IFMT) {
> +		default:
> +			init_special_inode(inode, mode, dev);
> +			break;
> +		case S_IFREG:
> +			inode->i_op = &regfs_file_inode_operations;
> +			inode->i_fop = &regfs_file_operations;
> +			break;
> +		case S_IFDIR:
> +			inode->i_op = &regfs_dir_inode_operations;
> +			inode->i_fop = &simple_dir_operations;
> +
> +			/* directory inodes start off with i_nlink == 2 (for "." entry) */
> +			inc_nlink(inode);
> +			break;
> +		case S_IFLNK:
> +			inode->i_op = &page_symlink_inode_operations;
> +			break;
> +		}
> +	}
> +
> +	return inode;
> +}
> +
> +static const struct inode_operations regfs_dir_inode_operations = {
> +	.lookup		= simple_lookup,
> +};
> +
> +static struct dentry *new_dentry_create(struct super_block *sb, struct dentry *parent,
> +		 const char *name, bool is_dir, struct res_data *res)
> +{
> +	struct dentry *dentry;
> +	struct inode *inode;
> +	struct regfs_inode_info *ei;
> +	struct regfs_fs_info *fsi = sb->s_fs_info;
> +
> +	dentry = d_alloc_name(parent, name);
> +	if (!dentry)
> +		return NULL;
> +
> +	inode = new_inode(sb);
> +	if (!inode)
> +		goto out;
> +
> +	ei = REGFS_I(inode);
> +	inode->i_ino = get_next_ino();;
> +	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
> +	inode->i_uid =  GLOBAL_ROOT_UID;
> +	inode->i_gid =  GLOBAL_ROOT_GID;
> +	if (is_dir) {
> +		inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR;
> +		inode->i_op = &regfs_dir_inode_operations;
> +		inode->i_fop = &simple_dir_operations;
> +		list_add(&ei->list, &fsi->list);
> +	} else {
> +		inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
> +		inode->i_op = &regfs_file_inode_operations;
> +		inode->i_fop = &regfs_file_operations;
> +		inc_nlink(inode);
> +	}
> +	ei->base = (void *)res->base;
> +	ei->offset = res->offset;
> +	ei->type = res->type;
> +
> +	d_add(dentry, inode);
> +
> +	loc_debug("new dentry io base:%llx offset:%llx ei:%llx\n", (u64)ei->base, (u64)ei->offset, (u64)ei);
> +	return dentry;
> +out:
> +	dput(dentry);
> +	return NULL;
> +}
> +
> +static void node_transfer_dentry(struct super_block *sb)
> +{
> +	struct regfs_fs_info *fsi = sb->s_fs_info;
> +	void *blob = fsi->dtb_buf;
> +	const char *pathp;
> +	int node_offset, depth = -1;
> +	struct dentry *parent = NULL;
> +	u64 parent_base;
> +
> +	for (node_offset = fdt_next_node(blob, -1, &depth);
> +		node_offset >= 0 && depth >= 0;
> +		node_offset = fdt_next_node(blob, node_offset, &depth)) {
> +
> +		const struct fdt_property *prop;
> +		struct res_data res;
> +
> +		pathp = fdt_get_name(blob, node_offset, NULL);
> +		prop = (void *)fdt_getprop(blob, node_offset, "reg", NULL);
> +
> +		if (prop) {
> +			unsigned long phys;
> +
> +			phys = fdt32_to_cpu(((const __be32 *)prop)[1]);
> +			res.type = RES_TYPE_RANGE;
> +			res.offset = fdt32_to_cpu(((const __be32 *)prop)[3]);
> +			res.base = (u64)ioremap(phys, res.offset);
> +
> +			if (!res.base) {
> +				parent = NULL;
> +				parent_base = 0;
> +				continue;
> +			}
> +
> +			loc_debug("%s reg:%lx size:%lx map:%llx\n\n", pathp
> +				 , (unsigned long) fdt32_to_cpu(((const __be32 *)prop)[1])
> +				 , (unsigned long) fdt32_to_cpu(((const __be32 *)prop)[3])
> +				 , (u64)res.base);
> +
> +			parent = new_dentry_create(sb, sb->s_root, (const char *)pathp, true, &res);
> +			parent_base = res.base;
> +
> +		} else {
> +			// parent dentry is create failed, igonre all child dentry
> +			if (!parent)
> +				continue;
> +
> +			prop = (void *)fdt_getprop(blob, node_offset, "offset", NULL);
> +			if (prop) {
> +
> +				res.offset = fdt32_to_cpu(*(const __be32 *)prop);
> +				res.base = parent_base;
> +				res.type = RES_TYPE_ITEM;
> +
> +				new_dentry_create(sb, parent, (const char *) pathp, false, &res);
> +				loc_debug("%s offset:%lx\n", pathp, (unsigned long)fdt32_to_cpu(*(const __be32 *)prop));
> +			}
> +		}
> +	}
> +}
> +
> +static int parse_options(char *options, struct super_block *sb)
> +{
> +	char *p;
> +	int ret = -EINVAL;
> +	struct regfs_fs_info *fsi;
> +	size_t msize = INT_MAX;
> +
> +	fsi = sb->s_fs_info;
> +
> +	if (!options)
> +		return -EINVAL;
> +
> +	while ((p = strsep(&options, ",")) != NULL) {
> +		char *name, *name_val;
> +
> +		name = strsep(&p, "=");
> +		if (name == NULL)
> +			goto failed;
> +
> +		name_val = strsep(&p, "=");
> +		if (name_val == NULL)
> +			goto failed;
> +
> +		//get resource address
> +		if (!strcmp(name, "dtb")) {
> +			ret = kernel_read_file_from_path(name_val, &fsi->dtb_buf, &fsi->dtb_len, msize, READING_UNKNOWN);
> +			if (ret) {
> +				pr_err("load %s failed\n", name_val);
> +				goto failed;
> +			}
> +		} else
> +			goto failed;
> +	};
> +
> +	return 0;
> +
> +failed:
> +	return ret;
> +}
> +
> +int regfs_fill_super(struct super_block *sb, void *data, int silent)
> +{
> +	struct regfs_fs_info *fsi;
> +	struct inode *inode;
> +	int err;
> +
> +	fsi = kzalloc(sizeof(struct regfs_fs_info), GFP_KERNEL);
> +	if (!fsi)
> +		return -ENOMEM;
> +
> +	sb->s_fs_info = fsi;
> +	fsi->sb = sb;
> +
> +	err = parse_options((char *)data, sb);
> +	if (err)
> +		goto out;
> +
> +	sb->s_maxbytes		= MAX_LFS_FILESIZE;
> +	sb->s_blocksize		= PAGE_SIZE;
> +	sb->s_blocksize_bits	= PAGE_SHIFT;
> +	sb->s_magic		= RAMFS_MAGIC;
> +	sb->s_op		= &regfs_ops;
> +	sb->s_time_gran		= 1;
> +
> +	inode = regfs_get_inode(sb, NULL, S_IFDIR, 0);
> +	sb->s_root = d_make_root(inode);
> +	if (!sb->s_root)
> +		goto out;
> +
> +	INIT_LIST_HEAD(&fsi->list);
> +	INIT_LIST_HEAD(&fsi->regfs_head);
> +	list_add(&fsi->regfs_head, &regfs_head);
> +
> +	return 0;
> +
> +out:
> +	if (fsi)
> +		kfree(fsi);
> +
> +	return err;
> +}
> +
> +struct dentry *regfs_mount(struct file_system_type *fs_type,
> +	int flags, const char *dev_name, void *data)
> +{
> +	struct dentry *root_dentry;
> +	struct super_block *sb;
> +
> +	root_dentry = mount_nodev(fs_type, flags, data, regfs_fill_super);
> +
> +	sb = root_dentry->d_sb;
> +
> +	if (sb->s_root) {
> +		node_transfer_dentry(sb);
> +	} else
> +		return NULL;
> +
> +	return root_dentry;
> +}
> +
> +static void regfs_kill_sb(struct super_block *sb)
> +{
> +	struct regfs_fs_info *fsi = sb->s_fs_info;
> +	struct regfs_inode_info *info_res;
> +
> +	list_for_each_entry(info_res,  &fsi->list, list)
> +		iounmap(info_res->base);
> +
> +	if (fsi) {
> +		if (fsi->dtb_buf)
> +			vfree(fsi->dtb_buf);
> +		list_del(&fsi->regfs_head);
> +		kfree(sb->s_fs_info);
> +	}
> +	kill_litter_super(sb);
> +}
> +
> +static struct file_system_type regfs_fs_type = {
> +	.name		= "regfs",
> +	.mount		= regfs_mount,
> +	.kill_sb	= regfs_kill_sb,
> +	.fs_flags	= FS_USERNS_MOUNT,
> +};
> +
> +static void init_once(void *foo)
> +{
> +	struct regfs_inode_info *ei = (struct regfs_inode_info *) foo;
> +
> +	inode_init_once(&ei->vfs_inode);
> +}
> +
> +static int __init init_regfs_fs(void)
> +{
> +
> +	regfs_inode_cachep = kmem_cache_create_usercopy("regfs_inode_cache",
> +				sizeof(struct regfs_inode_info), 0,
> +				(SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD | SLAB_ACCOUNT),
> +				0, 0, init_once);
> +
> +	if (!regfs_inode_cachep)
> +		return -ENOMEM;
> +
> +	return  register_filesystem(&regfs_fs_type);
> +}
> +
> +static void __exit exit_regfs_fs(void)
> +{
> +	unregister_filesystem(&regfs_fs_type);
> +	rcu_barrier();
> +	kmem_cache_destroy(regfs_inode_cachep);
> +}
> +
> +module_init(init_regfs_fs);
> +module_exit(exit_regfs_fs);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Zou Cao<zoucaox@linux.alibaba.com>");
> diff --git a/fs/regfs/internal.h b/fs/regfs/internal.h
> new file mode 100644
> index 0000000..61577bb
> --- /dev/null
> +++ b/fs/regfs/internal.h
> @@ -0,0 +1,32 @@
> +#ifndef __INTERNAL_H__
> +#define __INTERNAL_H__
> +
> +#define loc_debug(fmt, ...)   \
> +	do {                       \
> +		if (regfs_debug)   \
> +			printk(fmt, ##__VA_ARGS__); \
> +	} while (0)
> +
> +struct regfs_fs_info {
> +	struct super_block *sb;
> +	void   *dtb_buf;
> +	u64  	dtb_len;
> +	u64  	iomem;
> +	u64  size;
> +	// supper ifs list
> +	struct list_head regfs_head;
> +	// io map list of inode
> +	struct list_head list;
> +};
> +
> +#define RAMFS_DEFAULT_MODE	0755
> +
> +extern int regfs_debug;
> +extern const struct address_space_operations regfs_aops;
> +extern const struct inode_operations regfs_file_inode_operations;
> +extern const struct file_operations regfs_file_operations;
> +extern const struct super_operations regfs_ops;
> +extern struct kmem_cache *regfs_inode_cachep;
> +int regfs_supper_init(void);
> +
> +#endif
> diff --git a/fs/regfs/regfs_inode.h b/fs/regfs/regfs_inode.h
> new file mode 100644
> index 0000000..0883e05
> --- /dev/null
> +++ b/fs/regfs/regfs_inode.h
> @@ -0,0 +1,32 @@
> +#ifndef __REGFS_INODE_H__
> +#define __REGFS_INODE_H__
> +
> +enum res_type {
> +	RES_TYPE_NONE = 0,
> +	RES_TYPE_RANGE,
> +	RES_TYPE_ITEM,
> +};
> +
> +struct regfs_inode_info {
> +	unsigned long flag;
> +	struct inode vfs_inode;
> +	void __iomem *base;
> +	u64  offset;
> +	u64  val;  //for panic save
> +	struct list_head list;  //inode list
> +	enum res_type type;
> +};
> +
> +struct res_data {
> +	enum res_type type;
> +	u64  base;
> +	u64  offset;
> +};
> +
> +static inline struct regfs_inode_info *REGFS_I(struct inode *inode)
> +{
> +	return container_of(inode, struct regfs_inode_info, vfs_inode);
> +}
> +
> +#endif
> +
> diff --git a/fs/regfs/supper.c b/fs/regfs/supper.c
> new file mode 100644
> index 0000000..35733b6
> --- /dev/null
> +++ b/fs/regfs/supper.c
> @@ -0,0 +1,71 @@
> +#include <linux/fs.h>
> +#include <linux/pagemap.h>
> +#include <linux/highmem.h>
> +#include <linux/time.h>
> +#include <linux/init.h>
> +#include <linux/string.h>
> +#include <linux/backing-dev.h>
> +#include <linux/sched.h>
> +#include <linux/parser.h>
> +#include <linux/magic.h>
> +#include <linux/slab.h>
> +#include <linux/kernel.h>
> +#include <linux/uaccess.h>
> +#include <linux/module.h>
> +#include <linux/iversion.h>
> +#include "regfs_inode.h"
> +#include "internal.h"
> +
> +struct kmem_cache *regfs_inode_cachep;
> +
> +/*
> + * Display the mount options in /proc/mounts.
> + */
> +
> +static struct inode *regfs_alloc_inode(struct super_block *sb)
> +{
> +	struct regfs_inode_info *ei;
> +
> +	ei = kmem_cache_alloc(regfs_inode_cachep, GFP_NOFS);
> +	if (!ei)
> +		return NULL;
> +
> +	inode_set_iversion(&ei->vfs_inode, 1);
> +	ei->type = RES_TYPE_NONE;
> +
> +	return &ei->vfs_inode;
> +}
> +
> +static void regfs_i_callback(struct rcu_head *head)
> +{
> +	struct inode *inode = container_of(head, struct inode, i_rcu);
> +
> +	kmem_cache_free(regfs_inode_cachep, REGFS_I(inode));
> +}
> +
> +static void regfs_destroy_inode(struct inode *inode)
> +{
> +	call_rcu(&inode->i_rcu, regfs_i_callback);
> +}
> +
> +static int regfs_write_inode(struct inode *inode, struct writeback_control *wbc)
> +{
> +	return 0;
> +}
> +
> +static int regfs_drop_inode(struct inode *inode)
> +{
> +	return generic_drop_inode(inode);
> +}
> +
> +const struct super_operations regfs_ops = {
> +	.alloc_inode    = regfs_alloc_inode,
> +	.destroy_inode  = regfs_destroy_inode,
> +	.write_inode    = regfs_write_inode,
> +	.drop_inode = regfs_drop_inode,
> +};
> +
> +int regfs_supper_init(void)
> +{
> +	return 0;
> +}

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] fs:regfs: add register easy filesystem
  2020-10-23  2:57 ` [PATCH 1/2] fs:regfs: add register easy filesystem zc
@ 2020-10-27  9:16   ` zc
       [not found]   ` <fe9f0382-da87-77ab-75ab-5a4bec0a9a21@linux.alibaba.com>
  1 sibling, 0 replies; 8+ messages in thread
From: zc @ 2020-10-27  9:16 UTC (permalink / raw)
  To: viro; +Cc: linux-kernel, linux-fsdevel

pending ?

在 2020/10/23 上午10:57, zc 写道:
> Hi viro:
>
>   Through regfs is very sample and easy,  but i think it is a Interest 
> ,  could give  some suggestions?
>
>
> Regards,
>
> zc
>
> 在 2020/10/20 下午2:30, Zou Cao 写道:
>> register filesystem is mapping the register into file dentry, it
>> will use the io readio to get the register val. DBT file is use
>> to decript the register tree, you can use it as follow:
>>
>>     mount -t regfs -o dtb=test.dtb none /mnt
>>
>>     test.dts:
>>     / {
>>
>>     compatible = "hisilicon,hi6220-hikey", "hisilicon,hi6220";
>>     #address-cells = <0x2>;
>>     #size-cells = <0x2>;
>>     model = "HiKey Development Board";
>>
>>     gic-v3-dist{
>>         reg = <0x0 0x8000000 0x0 0x10000>;
>>         GIC_CTRL {
>>             offset = <0x0>;
>>         };
>>         GICD_TYPER {
>>             offset = <0x4>;
>>         };
>>        };
>>     };
>>
>> it will create all regiter dentry file in /mnt
>>
>> Signed-off-by: Zou Cao <zoucao@linux.alibaba.com>
>> ---
>>   fs/Kconfig             |   1 +
>>   fs/Makefile            |   1 +
>>   fs/regfs/Kconfig       |   7 +
>>   fs/regfs/Makefile      |   8 ++
>>   fs/regfs/file.c        | 107 +++++++++++++++
>>   fs/regfs/inode.c       | 354 
>> +++++++++++++++++++++++++++++++++++++++++++++++++
>>   fs/regfs/internal.h    |  32 +++++
>>   fs/regfs/regfs_inode.h |  32 +++++
>>   fs/regfs/supper.c      |  71 ++++++++++
>>   9 files changed, 613 insertions(+)
>>   create mode 100644 fs/regfs/Kconfig
>>   create mode 100644 fs/regfs/Makefile
>>   create mode 100644 fs/regfs/file.c
>>   create mode 100644 fs/regfs/inode.c
>>   create mode 100644 fs/regfs/internal.h
>>   create mode 100644 fs/regfs/regfs_inode.h
>>   create mode 100644 fs/regfs/supper.c
>>
>> diff --git a/fs/Kconfig b/fs/Kconfig
>> index a88aa3a..d95acaf 100644
>> --- a/fs/Kconfig
>> +++ b/fs/Kconfig
>> @@ -324,6 +324,7 @@ endif # NETWORK_FILESYSTEMS
>>   source "fs/nls/Kconfig"
>>   source "fs/dlm/Kconfig"
>>   source "fs/unicode/Kconfig"
>> +source "fs/regfs/Kconfig"
>>     config IO_WQ
>>       bool
>> diff --git a/fs/Makefile b/fs/Makefile
>> index 2ce5112..24f3878 100644
>> --- a/fs/Makefile
>> +++ b/fs/Makefile
>> @@ -136,3 +136,4 @@ obj-$(CONFIG_EFIVAR_FS)        += efivarfs/
>>   obj-$(CONFIG_EROFS_FS)        += erofs/
>>   obj-$(CONFIG_VBOXSF_FS)        += vboxsf/
>>   obj-$(CONFIG_ZONEFS_FS)        += zonefs/
>> +obj-$(CONFIG_REGFS_FS)        += zonefs/
>> diff --git a/fs/regfs/Kconfig b/fs/regfs/Kconfig
>> new file mode 100644
>> index 0000000..74ba85b
>> --- /dev/null
>> +++ b/fs/regfs/Kconfig
>> @@ -0,0 +1,7 @@
>> +config REGFS_FS
>> +    tristate "registers filesystem support"
>> +    depends on ARM64
>> +    help
>> +      regfs support the read and write register of device resource by
>> +      dentry filesystem, it is more easy to support bsp debug. it also
>> +      support to printk the register val when panic
>> diff --git a/fs/regfs/Makefile b/fs/regfs/Makefile
>> new file mode 100644
>> index 0000000..26d5eef
>> --- /dev/null
>> +++ b/fs/regfs/Makefile
>> @@ -0,0 +1,8 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +#
>> +#Makefile for the linux ramfs routines.
>> +#
>> +
>> +obj-y += regfs.o
>> +
>> +regfs-objs += inode.o file.o supper.o
>> diff --git a/fs/regfs/file.c b/fs/regfs/file.c
>> new file mode 100644
>> index 0000000..6cd9f3d
>> --- /dev/null
>> +++ b/fs/regfs/file.c
>> @@ -0,0 +1,107 @@
>> +#include <linux/fs.h>
>> +#include <linux/mm.h>
>> +#include <linux/mpage.h>
>> +#include <linux/writeback.h>
>> +#include <linux/buffer_head.h>
>> +#include <linux/kernel.h>
>> +#include <linux/mm.h>
>> +#include <linux/memcontrol.h>
>> +#include <linux/iomap.h>
>> +#include <linux/pagemap.h>
>> +#include <linux/uio.h>
>> +#include <linux/buffer_head.h>
>> +#include <linux/dax.h>
>> +#include <linux/writeback.h>
>> +#include <linux/swap.h>
>> +#include <linux/bio.h>
>> +#include <linux/sched/signal.h>
>> +#include <linux/migrate.h>
>> +
>> +#include "regfs_inode.h"
>> +#include "internal.h"
>> +
>> +ssize_t regfs_file_write_iter(struct kiocb *iocb, struct iov_iter 
>> *from)
>> +{
>> +    struct file *file = iocb->ki_filp;
>> +    struct inode *inode = file->f_mapping->host;
>> +    ssize_t ret;
>> +
>> +    inode_lock(inode);
>> +    ret = generic_write_checks(iocb, from);
>> +    if (ret > 0)
>> +        ret = __generic_file_write_iter(iocb, from);
>> +    inode_unlock(inode);
>> +
>> +    if (ret > 0)
>> +        ret = generic_write_sync(iocb, ret);
>> +    return ret;
>> +}
>> +
>> +static ssize_t regfs_file_read(struct file *file, char __user *buf, 
>> size_t len, loff_t *ppos)
>> +{
>> +    struct address_space *mapping = file->f_mapping;
>> +    struct regfs_inode_info  *info = REGFS_I(mapping->host);
>> +    char str[64];
>> +    unsigned long val;
>> +
>> +    val = readl_relaxed(info->base + info->offset);
>> +
>> +    loc_debug("name:%s base:%p val:%lx\n"
>> +            , file->f_path.dentry->d_iname
>> +            , info->base + info->offset
>> +            , val);
>> +
>> +    snprintf(str, 64, "%lx", val);
>> +
>> +    return simple_read_from_buffer(buf, len, ppos, str, strlen(str));
>> +}
>> +
>> +static ssize_t regfs_file_write(struct file *file, const char __user 
>> *buf, size_t len, loff_t *ppos)
>> +{
>> +    struct address_space *mapping = file->f_mapping;
>> +    struct regfs_inode_info  *info = REGFS_I(mapping->host);
>> +    char str[67];
>> +    unsigned long val = 0;
>> +    loff_t pos = *ppos;
>> +    size_t res;
>> +
>> +    if (pos < 0)
>> +        return -EINVAL;
>> +    if (pos >= len || len > 66)
>> +        return 0;
>> +
>> +    res = copy_from_user(str, buf, len);
>> +    if (res)
>> +        return -EFAULT;
>> +    str[len] = 0;
>> +
>> +    if (kstrtoul(str, 16, &val) < 0)
>> +        return -EINVAL;
>> +
>> +    writel_relaxed(val, info->base + info->offset);
>> +
>> +    loc_debug("name:%s base:%p val:%lx\n"
>> +            , file->f_path.dentry->d_iname
>> +            , info->base + info->offset
>> +            , val);
>> +
>> +    return len;
>> +}
>> +
>> +const struct file_operations regfs_file_operations = {
>> +    .read = regfs_file_read,
>> +    .write = regfs_file_write,
>> +};
>> +
>> +const struct inode_operations regfs_file_inode_operations = {
>> +    .setattr    = simple_setattr,
>> +    .getattr    = simple_getattr,
>> +};
>> +
>> +const struct address_space_operations regfs_aops = {
>> +    .readpage   = simple_readpage,
>> +    .write_begin    = simple_write_begin,
>> +    .write_end  = simple_write_end,
>> +    .set_page_dirty = __set_page_dirty_buffers,
>> +};
>> +
>> diff --git a/fs/regfs/inode.c b/fs/regfs/inode.c
>> new file mode 100644
>> index 0000000..1643fcd
>> --- /dev/null
>> +++ b/fs/regfs/inode.c
>> @@ -0,0 +1,354 @@
>> +/*
>> + * Resizable simple ram filesystem for Linux.
>> + *
>> + * Copyright (C) 2000 Linus Torvalds.
>> + *               2000 Transmeta Corp.
>> + *
>> + * Usage limits added by David Gibson, Linuxcare Australia.
>> + * This file is released under the GPL.
>> + */
>> +
>> +/*
>> + * NOTE! This filesystem is probably most useful
>> + * not as a real filesystem, but as an example of
>> + * how virtual filesystems can be written.
>> + *
>> + * It doesn't get much simpler than this. Consider
>> + * that this file implements the full semantics of
>> + * a POSIX-compliant read-write filesystem.
>> + *
>> + * Note in particular how the filesystem does not
>> + * need to implement any data structures of its own
>> + * to keep track of the virtual data: using the VFS
>> + * caches is sufficient.
>> + */
>> +
>> +#include <linux/fs.h>
>> +#include <linux/pagemap.h>
>> +#include <linux/highmem.h>
>> +#include <linux/time.h>
>> +#include <linux/init.h>
>> +#include <linux/string.h>
>> +#include <linux/backing-dev.h>
>> +#include <linux/sched.h>
>> +#include <linux/parser.h>
>> +#include <linux/magic.h>
>> +#include <linux/slab.h>
>> +#include <linux/kernel.h>
>> +#include <linux/of.h>
>> +#include <linux/of_fdt.h>
>> +#include <linux/libfdt.h>
>> +#include <asm/uaccess.h>
>> +#include <linux/module.h>
>> +#include "regfs_inode.h"
>> +#include "internal.h"
>> +
>> +static LIST_HEAD(regfs_head);
>> +
>> +static const struct inode_operations regfs_dir_inode_operations;
>> +int regfs_debug;
>> +module_param(regfs_debug, int, S_IRUGO);
>> +MODULE_PARM_DESC(regfs_debug, "enable regfs debug mode");
>> +
>> +struct inode *regfs_get_inode(struct super_block *sb, const struct 
>> inode *dir, umode_t mode, dev_t dev)
>> +{
>> +    struct inode *inode = new_inode(sb);
>> +
>> +    if (inode) {
>> +        inode->i_ino = get_next_ino();
>> +        inode_init_owner(inode, dir, mode);
>> +        inode->i_mapping->a_ops = &regfs_aops;
>> +        //inode->i_mapping->backing_dev_info = &regfs_backing_dev_info;
>> +        mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
>> +        mapping_set_unevictable(inode->i_mapping);
>> +        inode->i_atime = inode->i_mtime = inode->i_ctime = 
>> current_time(inode);
>> +        switch (mode & S_IFMT) {
>> +        default:
>> +            init_special_inode(inode, mode, dev);
>> +            break;
>> +        case S_IFREG:
>> +            inode->i_op = &regfs_file_inode_operations;
>> +            inode->i_fop = &regfs_file_operations;
>> +            break;
>> +        case S_IFDIR:
>> +            inode->i_op = &regfs_dir_inode_operations;
>> +            inode->i_fop = &simple_dir_operations;
>> +
>> +            /* directory inodes start off with i_nlink == 2 (for "." 
>> entry) */
>> +            inc_nlink(inode);
>> +            break;
>> +        case S_IFLNK:
>> +            inode->i_op = &page_symlink_inode_operations;
>> +            break;
>> +        }
>> +    }
>> +
>> +    return inode;
>> +}
>> +
>> +static const struct inode_operations regfs_dir_inode_operations = {
>> +    .lookup        = simple_lookup,
>> +};
>> +
>> +static struct dentry *new_dentry_create(struct super_block *sb, 
>> struct dentry *parent,
>> +         const char *name, bool is_dir, struct res_data *res)
>> +{
>> +    struct dentry *dentry;
>> +    struct inode *inode;
>> +    struct regfs_inode_info *ei;
>> +    struct regfs_fs_info *fsi = sb->s_fs_info;
>> +
>> +    dentry = d_alloc_name(parent, name);
>> +    if (!dentry)
>> +        return NULL;
>> +
>> +    inode = new_inode(sb);
>> +    if (!inode)
>> +        goto out;
>> +
>> +    ei = REGFS_I(inode);
>> +    inode->i_ino = get_next_ino();;
>> +    inode->i_mtime = inode->i_atime = inode->i_ctime = 
>> current_time(inode);
>> +    inode->i_uid =  GLOBAL_ROOT_UID;
>> +    inode->i_gid =  GLOBAL_ROOT_GID;
>> +    if (is_dir) {
>> +        inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR;
>> +        inode->i_op = &regfs_dir_inode_operations;
>> +        inode->i_fop = &simple_dir_operations;
>> +        list_add(&ei->list, &fsi->list);
>> +    } else {
>> +        inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
>> +        inode->i_op = &regfs_file_inode_operations;
>> +        inode->i_fop = &regfs_file_operations;
>> +        inc_nlink(inode);
>> +    }
>> +    ei->base = (void *)res->base;
>> +    ei->offset = res->offset;
>> +    ei->type = res->type;
>> +
>> +    d_add(dentry, inode);
>> +
>> +    loc_debug("new dentry io base:%llx offset:%llx ei:%llx\n", 
>> (u64)ei->base, (u64)ei->offset, (u64)ei);
>> +    return dentry;
>> +out:
>> +    dput(dentry);
>> +    return NULL;
>> +}
>> +
>> +static void node_transfer_dentry(struct super_block *sb)
>> +{
>> +    struct regfs_fs_info *fsi = sb->s_fs_info;
>> +    void *blob = fsi->dtb_buf;
>> +    const char *pathp;
>> +    int node_offset, depth = -1;
>> +    struct dentry *parent = NULL;
>> +    u64 parent_base;
>> +
>> +    for (node_offset = fdt_next_node(blob, -1, &depth);
>> +        node_offset >= 0 && depth >= 0;
>> +        node_offset = fdt_next_node(blob, node_offset, &depth)) {
>> +
>> +        const struct fdt_property *prop;
>> +        struct res_data res;
>> +
>> +        pathp = fdt_get_name(blob, node_offset, NULL);
>> +        prop = (void *)fdt_getprop(blob, node_offset, "reg", NULL);
>> +
>> +        if (prop) {
>> +            unsigned long phys;
>> +
>> +            phys = fdt32_to_cpu(((const __be32 *)prop)[1]);
>> +            res.type = RES_TYPE_RANGE;
>> +            res.offset = fdt32_to_cpu(((const __be32 *)prop)[3]);
>> +            res.base = (u64)ioremap(phys, res.offset);
>> +
>> +            if (!res.base) {
>> +                parent = NULL;
>> +                parent_base = 0;
>> +                continue;
>> +            }
>> +
>> +            loc_debug("%s reg:%lx size:%lx map:%llx\n\n", pathp
>> +                 , (unsigned long) fdt32_to_cpu(((const __be32 
>> *)prop)[1])
>> +                 , (unsigned long) fdt32_to_cpu(((const __be32 
>> *)prop)[3])
>> +                 , (u64)res.base);
>> +
>> +            parent = new_dentry_create(sb, sb->s_root, (const char 
>> *)pathp, true, &res);
>> +            parent_base = res.base;
>> +
>> +        } else {
>> +            // parent dentry is create failed, igonre all child dentry
>> +            if (!parent)
>> +                continue;
>> +
>> +            prop = (void *)fdt_getprop(blob, node_offset, "offset", 
>> NULL);
>> +            if (prop) {
>> +
>> +                res.offset = fdt32_to_cpu(*(const __be32 *)prop);
>> +                res.base = parent_base;
>> +                res.type = RES_TYPE_ITEM;
>> +
>> +                new_dentry_create(sb, parent, (const char *) pathp, 
>> false, &res);
>> +                loc_debug("%s offset:%lx\n", pathp, (unsigned 
>> long)fdt32_to_cpu(*(const __be32 *)prop));
>> +            }
>> +        }
>> +    }
>> +}
>> +
>> +static int parse_options(char *options, struct super_block *sb)
>> +{
>> +    char *p;
>> +    int ret = -EINVAL;
>> +    struct regfs_fs_info *fsi;
>> +    size_t msize = INT_MAX;
>> +
>> +    fsi = sb->s_fs_info;
>> +
>> +    if (!options)
>> +        return -EINVAL;
>> +
>> +    while ((p = strsep(&options, ",")) != NULL) {
>> +        char *name, *name_val;
>> +
>> +        name = strsep(&p, "=");
>> +        if (name == NULL)
>> +            goto failed;
>> +
>> +        name_val = strsep(&p, "=");
>> +        if (name_val == NULL)
>> +            goto failed;
>> +
>> +        //get resource address
>> +        if (!strcmp(name, "dtb")) {
>> +            ret = kernel_read_file_from_path(name_val, 
>> &fsi->dtb_buf, &fsi->dtb_len, msize, READING_UNKNOWN);
>> +            if (ret) {
>> +                pr_err("load %s failed\n", name_val);
>> +                goto failed;
>> +            }
>> +        } else
>> +            goto failed;
>> +    };
>> +
>> +    return 0;
>> +
>> +failed:
>> +    return ret;
>> +}
>> +
>> +int regfs_fill_super(struct super_block *sb, void *data, int silent)
>> +{
>> +    struct regfs_fs_info *fsi;
>> +    struct inode *inode;
>> +    int err;
>> +
>> +    fsi = kzalloc(sizeof(struct regfs_fs_info), GFP_KERNEL);
>> +    if (!fsi)
>> +        return -ENOMEM;
>> +
>> +    sb->s_fs_info = fsi;
>> +    fsi->sb = sb;
>> +
>> +    err = parse_options((char *)data, sb);
>> +    if (err)
>> +        goto out;
>> +
>> +    sb->s_maxbytes        = MAX_LFS_FILESIZE;
>> +    sb->s_blocksize        = PAGE_SIZE;
>> +    sb->s_blocksize_bits    = PAGE_SHIFT;
>> +    sb->s_magic        = RAMFS_MAGIC;
>> +    sb->s_op        = &regfs_ops;
>> +    sb->s_time_gran        = 1;
>> +
>> +    inode = regfs_get_inode(sb, NULL, S_IFDIR, 0);
>> +    sb->s_root = d_make_root(inode);
>> +    if (!sb->s_root)
>> +        goto out;
>> +
>> +    INIT_LIST_HEAD(&fsi->list);
>> +    INIT_LIST_HEAD(&fsi->regfs_head);
>> +    list_add(&fsi->regfs_head, &regfs_head);
>> +
>> +    return 0;
>> +
>> +out:
>> +    if (fsi)
>> +        kfree(fsi);
>> +
>> +    return err;
>> +}
>> +
>> +struct dentry *regfs_mount(struct file_system_type *fs_type,
>> +    int flags, const char *dev_name, void *data)
>> +{
>> +    struct dentry *root_dentry;
>> +    struct super_block *sb;
>> +
>> +    root_dentry = mount_nodev(fs_type, flags, data, regfs_fill_super);
>> +
>> +    sb = root_dentry->d_sb;
>> +
>> +    if (sb->s_root) {
>> +        node_transfer_dentry(sb);
>> +    } else
>> +        return NULL;
>> +
>> +    return root_dentry;
>> +}
>> +
>> +static void regfs_kill_sb(struct super_block *sb)
>> +{
>> +    struct regfs_fs_info *fsi = sb->s_fs_info;
>> +    struct regfs_inode_info *info_res;
>> +
>> +    list_for_each_entry(info_res,  &fsi->list, list)
>> +        iounmap(info_res->base);
>> +
>> +    if (fsi) {
>> +        if (fsi->dtb_buf)
>> +            vfree(fsi->dtb_buf);
>> +        list_del(&fsi->regfs_head);
>> +        kfree(sb->s_fs_info);
>> +    }
>> +    kill_litter_super(sb);
>> +}
>> +
>> +static struct file_system_type regfs_fs_type = {
>> +    .name        = "regfs",
>> +    .mount        = regfs_mount,
>> +    .kill_sb    = regfs_kill_sb,
>> +    .fs_flags    = FS_USERNS_MOUNT,
>> +};
>> +
>> +static void init_once(void *foo)
>> +{
>> +    struct regfs_inode_info *ei = (struct regfs_inode_info *) foo;
>> +
>> +    inode_init_once(&ei->vfs_inode);
>> +}
>> +
>> +static int __init init_regfs_fs(void)
>> +{
>> +
>> +    regfs_inode_cachep = 
>> kmem_cache_create_usercopy("regfs_inode_cache",
>> +                sizeof(struct regfs_inode_info), 0,
>> +                (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD | 
>> SLAB_ACCOUNT),
>> +                0, 0, init_once);
>> +
>> +    if (!regfs_inode_cachep)
>> +        return -ENOMEM;
>> +
>> +    return  register_filesystem(&regfs_fs_type);
>> +}
>> +
>> +static void __exit exit_regfs_fs(void)
>> +{
>> +    unregister_filesystem(&regfs_fs_type);
>> +    rcu_barrier();
>> +    kmem_cache_destroy(regfs_inode_cachep);
>> +}
>> +
>> +module_init(init_regfs_fs);
>> +module_exit(exit_regfs_fs);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("Zou Cao<zoucaox@linux.alibaba.com>");
>> diff --git a/fs/regfs/internal.h b/fs/regfs/internal.h
>> new file mode 100644
>> index 0000000..61577bb
>> --- /dev/null
>> +++ b/fs/regfs/internal.h
>> @@ -0,0 +1,32 @@
>> +#ifndef __INTERNAL_H__
>> +#define __INTERNAL_H__
>> +
>> +#define loc_debug(fmt, ...)   \
>> +    do {                       \
>> +        if (regfs_debug)   \
>> +            printk(fmt, ##__VA_ARGS__); \
>> +    } while (0)
>> +
>> +struct regfs_fs_info {
>> +    struct super_block *sb;
>> +    void   *dtb_buf;
>> +    u64      dtb_len;
>> +    u64      iomem;
>> +    u64  size;
>> +    // supper ifs list
>> +    struct list_head regfs_head;
>> +    // io map list of inode
>> +    struct list_head list;
>> +};
>> +
>> +#define RAMFS_DEFAULT_MODE    0755
>> +
>> +extern int regfs_debug;
>> +extern const struct address_space_operations regfs_aops;
>> +extern const struct inode_operations regfs_file_inode_operations;
>> +extern const struct file_operations regfs_file_operations;
>> +extern const struct super_operations regfs_ops;
>> +extern struct kmem_cache *regfs_inode_cachep;
>> +int regfs_supper_init(void);
>> +
>> +#endif
>> diff --git a/fs/regfs/regfs_inode.h b/fs/regfs/regfs_inode.h
>> new file mode 100644
>> index 0000000..0883e05
>> --- /dev/null
>> +++ b/fs/regfs/regfs_inode.h
>> @@ -0,0 +1,32 @@
>> +#ifndef __REGFS_INODE_H__
>> +#define __REGFS_INODE_H__
>> +
>> +enum res_type {
>> +    RES_TYPE_NONE = 0,
>> +    RES_TYPE_RANGE,
>> +    RES_TYPE_ITEM,
>> +};
>> +
>> +struct regfs_inode_info {
>> +    unsigned long flag;
>> +    struct inode vfs_inode;
>> +    void __iomem *base;
>> +    u64  offset;
>> +    u64  val;  //for panic save
>> +    struct list_head list;  //inode list
>> +    enum res_type type;
>> +};
>> +
>> +struct res_data {
>> +    enum res_type type;
>> +    u64  base;
>> +    u64  offset;
>> +};
>> +
>> +static inline struct regfs_inode_info *REGFS_I(struct inode *inode)
>> +{
>> +    return container_of(inode, struct regfs_inode_info, vfs_inode);
>> +}
>> +
>> +#endif
>> +
>> diff --git a/fs/regfs/supper.c b/fs/regfs/supper.c
>> new file mode 100644
>> index 0000000..35733b6
>> --- /dev/null
>> +++ b/fs/regfs/supper.c
>> @@ -0,0 +1,71 @@
>> +#include <linux/fs.h>
>> +#include <linux/pagemap.h>
>> +#include <linux/highmem.h>
>> +#include <linux/time.h>
>> +#include <linux/init.h>
>> +#include <linux/string.h>
>> +#include <linux/backing-dev.h>
>> +#include <linux/sched.h>
>> +#include <linux/parser.h>
>> +#include <linux/magic.h>
>> +#include <linux/slab.h>
>> +#include <linux/kernel.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/module.h>
>> +#include <linux/iversion.h>
>> +#include "regfs_inode.h"
>> +#include "internal.h"
>> +
>> +struct kmem_cache *regfs_inode_cachep;
>> +
>> +/*
>> + * Display the mount options in /proc/mounts.
>> + */
>> +
>> +static struct inode *regfs_alloc_inode(struct super_block *sb)
>> +{
>> +    struct regfs_inode_info *ei;
>> +
>> +    ei = kmem_cache_alloc(regfs_inode_cachep, GFP_NOFS);
>> +    if (!ei)
>> +        return NULL;
>> +
>> +    inode_set_iversion(&ei->vfs_inode, 1);
>> +    ei->type = RES_TYPE_NONE;
>> +
>> +    return &ei->vfs_inode;
>> +}
>> +
>> +static void regfs_i_callback(struct rcu_head *head)
>> +{
>> +    struct inode *inode = container_of(head, struct inode, i_rcu);
>> +
>> +    kmem_cache_free(regfs_inode_cachep, REGFS_I(inode));
>> +}
>> +
>> +static void regfs_destroy_inode(struct inode *inode)
>> +{
>> +    call_rcu(&inode->i_rcu, regfs_i_callback);
>> +}
>> +
>> +static int regfs_write_inode(struct inode *inode, struct 
>> writeback_control *wbc)
>> +{
>> +    return 0;
>> +}
>> +
>> +static int regfs_drop_inode(struct inode *inode)
>> +{
>> +    return generic_drop_inode(inode);
>> +}
>> +
>> +const struct super_operations regfs_ops = {
>> +    .alloc_inode    = regfs_alloc_inode,
>> +    .destroy_inode  = regfs_destroy_inode,
>> +    .write_inode    = regfs_write_inode,
>> +    .drop_inode = regfs_drop_inode,
>> +};
>> +
>> +int regfs_supper_init(void)
>> +{
>> +    return 0;
>> +}

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] fs:regfs: add register easy filesystem
  2020-10-20  6:30 [PATCH 1/2] fs:regfs: add register easy filesystem Zou Cao
  2020-10-20  6:30 ` [PATCH 2/2] fs:regfs: add panic notifier callback for saving regs Zou Cao
  2020-10-23  2:57 ` [PATCH 1/2] fs:regfs: add register easy filesystem zc
@ 2020-10-29  2:42 ` Al Viro
  2020-10-29  8:14   ` zc
  2 siblings, 1 reply; 8+ messages in thread
From: Al Viro @ 2020-10-29  2:42 UTC (permalink / raw)
  To: Zou Cao; +Cc: linux-kernel, linux-fsdevel

On Tue, Oct 20, 2020 at 02:30:07PM +0800, Zou Cao wrote:
> +ssize_t regfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
> +{
> +	struct file *file = iocb->ki_filp;
> +	struct inode *inode = file->f_mapping->host;
> +	ssize_t ret;
> +
> +	inode_lock(inode);
> +	ret = generic_write_checks(iocb, from);
> +	if (ret > 0)
> +		ret = __generic_file_write_iter(iocb, from);
> +	inode_unlock(inode);
> +
> +	if (ret > 0)
> +		ret = generic_write_sync(iocb, ret);
> +	return ret;
> +}

Huh?  How is that different from generic_file_write_iter()?  And who's
using it, anyway?

> +	struct regfs_inode_info  *info = REGFS_I(mapping->host);
> +	char str[67];
> +	unsigned long val = 0;
> +	loff_t pos = *ppos;
> +	size_t res;
> +
> +	if (pos < 0)
> +		return -EINVAL;
> +	if (pos >= len || len > 66)
> +		return 0;

This is completely bogus.  "If current position is greater than the
length of string we are asking to write, quietly return 0"?

> +	res = copy_from_user(str, buf, len);
> +	if (res)
> +		return -EFAULT;
> +	str[len] = 0;
> +
> +	if (kstrtoul(str, 16, &val) < 0)
> +		return -EINVAL;

Where does 67 come from?  If you are expecting a hexadecimal representation
of a unsigned long on arm64, you should have at most 16 digits.  67 looks
rather odd...

> +	writel_relaxed(val, info->base + info->offset);

... and you are promptly discarding the upper 32 bits, since writel_relaxed()
takes u32:
	((void)__raw_writel((__force u32)cpu_to_le32(v),(c)))
is going to truncate to 32bit, no matter what.  Quietly truncate, at that...

> +const struct address_space_operations regfs_aops = {
> +	.readpage   = simple_readpage,
> +	.write_begin    = simple_write_begin,
> +	.write_end  = simple_write_end,
> +	.set_page_dirty = __set_page_dirty_buffers,
> +};

Again, huh?  What would use the page cache there, anyway?

> +static LIST_HEAD(regfs_head);

Protected by...?

> +static const struct inode_operations regfs_dir_inode_operations;
> +int regfs_debug;
> +module_param(regfs_debug, int, S_IRUGO);
> +MODULE_PARM_DESC(regfs_debug, "enable regfs debug mode");
> +
> +struct inode *regfs_get_inode(struct super_block *sb, const struct inode *dir, umode_t mode, dev_t dev)
> +{
> +	struct inode *inode = new_inode(sb);
> +
> +	if (inode) {
> +		inode->i_ino = get_next_ino();
> +		inode_init_owner(inode, dir, mode);
> +		inode->i_mapping->a_ops = &regfs_aops;
> +		//inode->i_mapping->backing_dev_info = &regfs_backing_dev_info;
> +		mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
> +		mapping_set_unevictable(inode->i_mapping);
> +		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
> +		switch (mode & S_IFMT) {
> +		default:
> +			init_special_inode(inode, mode, dev);
> +			break;
> +		case S_IFREG:
> +			inode->i_op = &regfs_file_inode_operations;
> +			inode->i_fop = &regfs_file_operations;
> +			break;
> +		case S_IFDIR:
> +			inode->i_op = &regfs_dir_inode_operations;
> +			inode->i_fop = &simple_dir_operations;
> +
> +			/* directory inodes start off with i_nlink == 2 (for "." entry) */
> +			inc_nlink(inode);
> +			break;
> +		case S_IFLNK:
> +			inode->i_op = &page_symlink_inode_operations;
> +			break;
> +		}
> +	}
> +
> +	return inode;
> +}

Seriously?  Where would symlinks, device nodes, FIFOs and sockets come from?
And you are open-coding the regular file case in the new_dentry_create() anyway,
so the only thing this is actually used for is the root directory.

> +static const struct inode_operations regfs_dir_inode_operations = {
> +	.lookup		= simple_lookup,
> +};

... and simple_dir_inode_operations is wrong, because...?

> +static struct dentry *new_dentry_create(struct super_block *sb, struct dentry *parent,
> +		 const char *name, bool is_dir, struct res_data *res)
> +{
> +	struct dentry *dentry;
> +	struct inode *inode;
> +	struct regfs_inode_info *ei;
> +	struct regfs_fs_info *fsi = sb->s_fs_info;
> +
> +	dentry = d_alloc_name(parent, name);
> +	if (!dentry)
> +		return NULL;
> +
> +	inode = new_inode(sb);
> +	if (!inode)
> +		goto out;
> +
> +	ei = REGFS_I(inode);
> +	inode->i_ino = get_next_ino();;
> +	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
> +	inode->i_uid =  GLOBAL_ROOT_UID;
> +	inode->i_gid =  GLOBAL_ROOT_GID;
> +	if (is_dir) {
> +		inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR;
> +		inode->i_op = &regfs_dir_inode_operations;
> +		inode->i_fop = &simple_dir_operations;
> +		list_add(&ei->list, &fsi->list);

where's the matching removal from the list?

> +	} else {
> +		inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
> +		inode->i_op = &regfs_file_inode_operations;
> +		inode->i_fop = &regfs_file_operations;
> +		inc_nlink(inode);
> +	}
> +	ei->base = (void *)res->base;
> +	ei->offset = res->offset;
> +	ei->type = res->type;
> +
> +	d_add(dentry, inode);
> +
> +	loc_debug("new dentry io base:%llx offset:%llx ei:%llx\n", (u64)ei->base, (u64)ei->offset, (u64)ei);
> +	return dentry;
> +out:
> +	dput(dentry);
> +	return NULL;
> +}
> +
> +static void node_transfer_dentry(struct super_block *sb)
> +{
> +	struct regfs_fs_info *fsi = sb->s_fs_info;
> +	void *blob = fsi->dtb_buf;
> +	const char *pathp;
> +	int node_offset, depth = -1;
> +	struct dentry *parent = NULL;
> +	u64 parent_base;
> +
> +	for (node_offset = fdt_next_node(blob, -1, &depth);
> +		node_offset >= 0 && depth >= 0;
> +		node_offset = fdt_next_node(blob, node_offset, &depth)) {
> +
> +		const struct fdt_property *prop;
> +		struct res_data res;
> +
> +		pathp = fdt_get_name(blob, node_offset, NULL);
> +		prop = (void *)fdt_getprop(blob, node_offset, "reg", NULL);
> +
> +		if (prop) {
> +			unsigned long phys;
> +
> +			phys = fdt32_to_cpu(((const __be32 *)prop)[1]);
> +			res.type = RES_TYPE_RANGE;
> +			res.offset = fdt32_to_cpu(((const __be32 *)prop)[3]);
> +			res.base = (u64)ioremap(phys, res.offset);
> +
> +			if (!res.base) {
> +				parent = NULL;
> +				parent_base = 0;
> +				continue;
> +			}
> +
> +			loc_debug("%s reg:%lx size:%lx map:%llx\n\n", pathp
> +				 , (unsigned long) fdt32_to_cpu(((const __be32 *)prop)[1])
> +				 , (unsigned long) fdt32_to_cpu(((const __be32 *)prop)[3])
> +				 , (u64)res.base);
> +
> +			parent = new_dentry_create(sb, sb->s_root, (const char *)pathp, true, &res);
> +			parent_base = res.base;
> +
> +		} else {
> +			// parent dentry is create failed, igonre all child dentry
> +			if (!parent)
> +				continue;
> +
> +			prop = (void *)fdt_getprop(blob, node_offset, "offset", NULL);
> +			if (prop) {
> +
> +				res.offset = fdt32_to_cpu(*(const __be32 *)prop);
> +				res.base = parent_base;
> +				res.type = RES_TYPE_ITEM;
> +
> +				new_dentry_create(sb, parent, (const char *) pathp, false, &res);
> +				loc_debug("%s offset:%lx\n", pathp, (unsigned long)fdt32_to_cpu(*(const __be32 *)prop));
> +			}
> +		}
> +	}
> +}
> +
> +static int parse_options(char *options, struct super_block *sb)
> +{
> +	char *p;
> +	int ret = -EINVAL;
> +	struct regfs_fs_info *fsi;
> +	size_t msize = INT_MAX;
> +
> +	fsi = sb->s_fs_info;
> +
> +	if (!options)
> +		return -EINVAL;
> +
> +	while ((p = strsep(&options, ",")) != NULL) {
> +		char *name, *name_val;
> +
> +		name = strsep(&p, "=");
> +		if (name == NULL)
> +			goto failed;
> +
> +		name_val = strsep(&p, "=");
> +		if (name_val == NULL)
> +			goto failed;
> +
> +		//get resource address
> +		if (!strcmp(name, "dtb")) {
> +			ret = kernel_read_file_from_path(name_val, &fsi->dtb_buf, &fsi->dtb_len, msize, READING_UNKNOWN);

Why bother doing that in the kernel?

> +struct dentry *regfs_mount(struct file_system_type *fs_type,
> +	int flags, const char *dev_name, void *data)
> +{
> +	struct dentry *root_dentry;
> +	struct super_block *sb;
> +
> +	root_dentry = mount_nodev(fs_type, flags, data, regfs_fill_super);
> +
> +	sb = root_dentry->d_sb;
> +
> +	if (sb->s_root) {
> +		node_transfer_dentry(sb);

Er... Why not do that in regfs_fill_super()?

Al, not going any further for now...

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] fs:regfs: add register easy filesystem
       [not found]   ` <fe9f0382-da87-77ab-75ab-5a4bec0a9a21@linux.alibaba.com>
@ 2020-10-29  2:44     ` Al Viro
  2020-10-29  7:55       ` zc
  0 siblings, 1 reply; 8+ messages in thread
From: Al Viro @ 2020-10-29  2:44 UTC (permalink / raw)
  To: zc; +Cc: linux-kernel, linux-fsdevel

On Wed, Oct 28, 2020 at 02:27:20PM +0800, zc wrote:
> Hi viro:
> 
>    have time for reviewing this?

Start with removing unused boilerplate.  When quite a chunk
of the codebase is simply never used, filtering _that_ out
is on the author, not reviewers.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] fs:regfs: add register easy filesystem
  2020-10-29  2:44     ` Al Viro
@ 2020-10-29  7:55       ` zc
  0 siblings, 0 replies; 8+ messages in thread
From: zc @ 2020-10-29  7:55 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-kernel, linux-fsdevel


在 2020/10/29 上午10:44, Al Viro 写道:
> On Wed, Oct 28, 2020 at 02:27:20PM +0800, zc wrote:
>> Hi viro:
>>
>>     have time for reviewing this?
> Start with removing unused boilerplate.  When quite a chunk
> of the codebase is simply never used, filtering _that_ out
> is on the author, not reviewers.

sorry, i will remove these unused boilerplat.

Regsards,

zoucao


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] fs:regfs: add register easy filesystem
  2020-10-29  2:42 ` Al Viro
@ 2020-10-29  8:14   ` zc
  0 siblings, 0 replies; 8+ messages in thread
From: zc @ 2020-10-29  8:14 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-kernel, linux-fsdevel


在 2020/10/29 上午10:42, Al Viro 写道:
> On Tue, Oct 20, 2020 at 02:30:07PM +0800, Zou Cao wrote:
>> +ssize_t regfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
>> +{
>> +	struct file *file = iocb->ki_filp;
>> +	struct inode *inode = file->f_mapping->host;
>> +	ssize_t ret;
>> +
>> +	inode_lock(inode);
>> +	ret = generic_write_checks(iocb, from);
>> +	if (ret > 0)
>> +		ret = __generic_file_write_iter(iocb, from);
>> +	inode_unlock(inode);
>> +
>> +	if (ret > 0)
>> +		ret = generic_write_sync(iocb, ret);
>> +	return ret;
>> +}
> Huh?  How is that different from generic_file_write_iter()?  And who's
> using it, anyway?
my bug,  remove it.
>> +	struct regfs_inode_info  *info = REGFS_I(mapping->host);
>> +	char str[67];
>> +	unsigned long val = 0;
>> +	loff_t pos = *ppos;
>> +	size_t res;
>> +
>> +	if (pos < 0)
>> +		return -EINVAL;
>> +	if (pos >= len || len > 66)
>> +		return 0;
> This is completely bogus.  "If current position is greater than the
> length of string we are asking to write, quietly return 0"?
Yes, fixed it.
>> +	res = copy_from_user(str, buf, len);
>> +	if (res)
>> +		return -EFAULT;
>> +	str[len] = 0;
>> +
>> +	if (kstrtoul(str, 16, &val) < 0)
>> +		return -EINVAL;
> Where does 67 come from?  If you are expecting a hexadecimal representation
> of a unsigned long on arm64, you should have at most 16 digits.  67 looks
> rather odd...
>
Yes, it is only 16.  thank you so much.
>> +	writel_relaxed(val, info->base + info->offset);
> ... and you are promptly discarding the upper 32 bits, since writel_relaxed()
> takes u32:
> 	((void)__raw_writel((__force u32)cpu_to_le32(v),(c)))
> is going to truncate to 32bit, no matter what.  Quietly truncate, at that...
>
>> +const struct address_space_operations regfs_aops = {
>> +	.readpage   = simple_readpage,
>> +	.write_begin    = simple_write_begin,
>> +	.write_end  = simple_write_end,
>> +	.set_page_dirty = __set_page_dirty_buffers,
>> +};
> Again, huh?  What would use the page cache there, anyway?

actually it not need,  it will be remove

>> +static LIST_HEAD(regfs_head);
> Protected by...?
It is  better to add a protect for multpite mount, i will do more test 
for it.
>> +static const struct inode_operations regfs_dir_inode_operations;
>> +int regfs_debug;
>> +module_param(regfs_debug, int, S_IRUGO);
>> +MODULE_PARM_DESC(regfs_debug, "enable regfs debug mode");
>> +
>> +struct inode *regfs_get_inode(struct super_block *sb, const struct inode *dir, umode_t mode, dev_t dev)
>> +{
>> +	struct inode *inode = new_inode(sb);
>> +
>> +	if (inode) {
>> +		inode->i_ino = get_next_ino();
>> +		inode_init_owner(inode, dir, mode);
>> +		inode->i_mapping->a_ops = &regfs_aops;
>> +		//inode->i_mapping->backing_dev_info = &regfs_backing_dev_info;
>> +		mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
>> +		mapping_set_unevictable(inode->i_mapping);
>> +		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
>> +		switch (mode & S_IFMT) {
>> +		default:
>> +			init_special_inode(inode, mode, dev);
>> +			break;
>> +		case S_IFREG:
>> +			inode->i_op = &regfs_file_inode_operations;
>> +			inode->i_fop = &regfs_file_operations;
>> +			break;
>> +		case S_IFDIR:
>> +			inode->i_op = &regfs_dir_inode_operations;
>> +			inode->i_fop = &simple_dir_operations;
>> +
>> +			/* directory inodes start off with i_nlink == 2 (for "." entry) */
>> +			inc_nlink(inode);
>> +			break;
>> +		case S_IFLNK:
>> +			inode->i_op = &page_symlink_inode_operations;
>> +			break;
>> +		}
>> +	}
>> +
>> +	return inode;
>> +}
> Seriously?  Where would symlinks, device nodes, FIFOs and sockets come from?
> And you are open-coding the regular file case in the new_dentry_create() anyway,
> so the only thing this is actually used for is the root directory.
yes,  i have limited the rm/create/link in regfs in inode_operations, so 
i need to remove all these and only leave the

inode->i_op = &regfs_file_inode_operations; ?

>> +static const struct inode_operations regfs_dir_inode_operations = {
>> +	.lookup		= simple_lookup,
>> +};
> ... and simple_dir_inode_operations is wrong, because...?
>
Here i limit the inode create/rm/link and so on, only support the read 
and write, it isn't useful for new inode,  because these inode have not 
register addr to bind its dentry,

i think these create/rm/simlink are invalidm,  only use simple_lookup.

>> +static struct dentry *new_dentry_create(struct super_block *sb, struct dentry *parent,
>> +		 const char *name, bool is_dir, struct res_data *res)
>> +{
>> +	struct dentry *dentry;
>> +	struct inode *inode;
>> +	struct regfs_inode_info *ei;
>> +	struct regfs_fs_info *fsi = sb->s_fs_info;
>> +
>> +	dentry = d_alloc_name(parent, name);
>> +	if (!dentry)
>> +		return NULL;
>> +
>> +	inode = new_inode(sb);
>> +	if (!inode)
>> +		goto out;
>> +
>> +	ei = REGFS_I(inode);
>> +	inode->i_ino = get_next_ino();;
>> +	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
>> +	inode->i_uid =  GLOBAL_ROOT_UID;
>> +	inode->i_gid =  GLOBAL_ROOT_GID;
>> +	if (is_dir) {
>> +		inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR;
>> +		inode->i_op = &regfs_dir_inode_operations;
>> +		inode->i_fop = &simple_dir_operations;
>> +		list_add(&ei->list, &fsi->list);
> where's the matching removal from the list?

Current regfs doesn't support  inode  create/del in regfs,  sorry i am 
lazy to free it,  all will be free when

umounting,  if inode can't del, it will never need to remove from list,  
but it is better to add the remove list.

>> +	} else {
>> +		inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
>> +		inode->i_op = &regfs_file_inode_operations;
>> +		inode->i_fop = &regfs_file_operations;
>> +		inc_nlink(inode);
>> +	}
>> +	ei->base = (void *)res->base;
>> +	ei->offset = res->offset;
>> +	ei->type = res->type;
>> +
>> +	d_add(dentry, inode);
>> +
>> +	loc_debug("new dentry io base:%llx offset:%llx ei:%llx\n", (u64)ei->base, (u64)ei->offset, (u64)ei);
>> +	return dentry;
>> +out:
>> +	dput(dentry);
>> +	return NULL;
>> +}
>> +
>> +static void node_transfer_dentry(struct super_block *sb)
>> +{
>> +	struct regfs_fs_info *fsi = sb->s_fs_info;
>> +	void *blob = fsi->dtb_buf;
>> +	const char *pathp;
>> +	int node_offset, depth = -1;
>> +	struct dentry *parent = NULL;
>> +	u64 parent_base;
>> +
>> +	for (node_offset = fdt_next_node(blob, -1, &depth);
>> +		node_offset >= 0 && depth >= 0;
>> +		node_offset = fdt_next_node(blob, node_offset, &depth)) {
>> +
>> +		const struct fdt_property *prop;
>> +		struct res_data res;
>> +
>> +		pathp = fdt_get_name(blob, node_offset, NULL);
>> +		prop = (void *)fdt_getprop(blob, node_offset, "reg", NULL);
>> +
>> +		if (prop) {
>> +			unsigned long phys;
>> +
>> +			phys = fdt32_to_cpu(((const __be32 *)prop)[1]);
>> +			res.type = RES_TYPE_RANGE;
>> +			res.offset = fdt32_to_cpu(((const __be32 *)prop)[3]);
>> +			res.base = (u64)ioremap(phys, res.offset);
>> +
>> +			if (!res.base) {
>> +				parent = NULL;
>> +				parent_base = 0;
>> +				continue;
>> +			}
>> +
>> +			loc_debug("%s reg:%lx size:%lx map:%llx\n\n", pathp
>> +				 , (unsigned long) fdt32_to_cpu(((const __be32 *)prop)[1])
>> +				 , (unsigned long) fdt32_to_cpu(((const __be32 *)prop)[3])
>> +				 , (u64)res.base);
>> +
>> +			parent = new_dentry_create(sb, sb->s_root, (const char *)pathp, true, &res);
>> +			parent_base = res.base;
>> +
>> +		} else {
>> +			// parent dentry is create failed, igonre all child dentry
>> +			if (!parent)
>> +				continue;
>> +
>> +			prop = (void *)fdt_getprop(blob, node_offset, "offset", NULL);
>> +			if (prop) {
>> +
>> +				res.offset = fdt32_to_cpu(*(const __be32 *)prop);
>> +				res.base = parent_base;
>> +				res.type = RES_TYPE_ITEM;
>> +
>> +				new_dentry_create(sb, parent, (const char *) pathp, false, &res);
>> +				loc_debug("%s offset:%lx\n", pathp, (unsigned long)fdt32_to_cpu(*(const __be32 *)prop));
>> +			}
>> +		}
>> +	}
>> +}
>> +
>> +static int parse_options(char *options, struct super_block *sb)
>> +{
>> +	char *p;
>> +	int ret = -EINVAL;
>> +	struct regfs_fs_info *fsi;
>> +	size_t msize = INT_MAX;
>> +
>> +	fsi = sb->s_fs_info;
>> +
>> +	if (!options)
>> +		return -EINVAL;
>> +
>> +	while ((p = strsep(&options, ",")) != NULL) {
>> +		char *name, *name_val;
>> +
>> +		name = strsep(&p, "=");
>> +		if (name == NULL)
>> +			goto failed;
>> +
>> +		name_val = strsep(&p, "=");
>> +		if (name_val == NULL)
>> +			goto failed;
>> +
>> +		//get resource address
>> +		if (!strcmp(name, "dtb")) {
>> +			ret = kernel_read_file_from_path(name_val, &fsi->dtb_buf, &fsi->dtb_len, msize, READING_UNKNOWN);
> Why bother doing that in the kernel?

   Do you mean why using  kernel_read_file_from_path and bind these dtb 
into dentry?  i want to support different devices registers to mount

different path by using itself dtb file. i introduce mount param: 
dtb=test1.dtb,  load these dtb to module and explain them, later these 
registers

will bind into different  dentrys:

        mount -t regfs -o dtb=test.dtb none /mnt

     also you can mount others device dts to different dirtecotry.

        mount -t regfs -o dtb=test1.dtb none /mnt1

        mount -t regfs -o dtb=test2.dtb none /mnt2

I meet a hateful problem is devmem, for debugging some divices registers 
in user level,  I can't remember so many registers and to find their 
addr by read device spec,

if it can be list and more easy to write/read and don't let me to 
reconfig these map of dentry in next time,  i think dts file is suitably.

>> +struct dentry *regfs_mount(struct file_system_type *fs_type,
>> +	int flags, const char *dev_name, void *data)
>> +{
>> +	struct dentry *root_dentry;
>> +	struct super_block *sb;
>> +
>> +	root_dentry = mount_nodev(fs_type, flags, data, regfs_fill_super);
>> +
>> +	sb = root_dentry->d_sb;
>> +
>> +	if (sb->s_root) {
>> +		node_transfer_dentry(sb);
> Er... Why not do that in regfs_fill_super()?
Yes, it is betters to move it into regfs_fill_super.
> Al, not going any further for now...

the master problem is i need to support inode create, del and so on?  
event i provide these opereations, these new inodes haven't bind the dts 
register addr,

it just a page cache inode,  like ramfs inode, these inodes are invalid 
for regfs.


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, back to index

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-20  6:30 [PATCH 1/2] fs:regfs: add register easy filesystem Zou Cao
2020-10-20  6:30 ` [PATCH 2/2] fs:regfs: add panic notifier callback for saving regs Zou Cao
2020-10-23  2:57 ` [PATCH 1/2] fs:regfs: add register easy filesystem zc
2020-10-27  9:16   ` zc
     [not found]   ` <fe9f0382-da87-77ab-75ab-5a4bec0a9a21@linux.alibaba.com>
2020-10-29  2:44     ` Al Viro
2020-10-29  7:55       ` zc
2020-10-29  2:42 ` Al Viro
2020-10-29  8:14   ` zc

Linux-Fsdevel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-fsdevel/0 linux-fsdevel/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-fsdevel linux-fsdevel/ https://lore.kernel.org/linux-fsdevel \
		linux-fsdevel@vger.kernel.org
	public-inbox-index linux-fsdevel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-fsdevel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git