From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-11.4 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,NICE_REPLY_A, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,UNPARSEABLE_RELAY,USER_AGENT_SANE_1 autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 80B22C388F9 for ; Fri, 23 Oct 2020 02:57:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 23584207BB for ; Fri, 23 Oct 2020 02:57:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S374809AbgJWC5m (ORCPT ); Thu, 22 Oct 2020 22:57:42 -0400 Received: from out30-57.freemail.mail.aliyun.com ([115.124.30.57]:44447 "EHLO out30-57.freemail.mail.aliyun.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S374806AbgJWC5m (ORCPT ); Thu, 22 Oct 2020 22:57:42 -0400 X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R151e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=e01e04407;MF=zoucao@linux.alibaba.com;NM=1;PH=DS;RN=4;SR=0;TI=SMTPD_---0UCt828I_1603421853; Received: from ali-6c96cfe06eab.local(mailfrom:zoucao@linux.alibaba.com fp:SMTPD_---0UCt828I_1603421853) by smtp.aliyun-inc.com(127.0.0.1); Fri, 23 Oct 2020 10:57:34 +0800 Subject: Re: [PATCH 1/2] fs:regfs: add register easy filesystem From: zc To: viro@zeniv.linux.org.uk Cc: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, zoucao References: <1603175408-96164-1-git-send-email-zoucao@linux.alibaba.com> Message-ID: <00513aa5-1cc5-bc1c-1ca7-d5cd6e3f1ed6@linux.alibaba.com> Date: Fri, 23 Oct 2020 10:57:33 +0800 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Thunderbird/68.10.0 MIME-Version: 1.0 In-Reply-To: <1603175408-96164-1-git-send-email-zoucao@linux.alibaba.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org 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 > --- > 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#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 = ®fs_aops; > + //inode->i_mapping->backing_dev_info = ®fs_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 = ®fs_file_inode_operations; > + inode->i_fop = ®fs_file_operations; > + break; > + case S_IFDIR: > + inode->i_op = ®fs_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 = ®fs_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 = ®fs_file_inode_operations; > + inode->i_fop = ®fs_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 = ®fs_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, ®fs_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(®fs_fs_type); > +} > + > +static void __exit exit_regfs_fs(void) > +{ > + unregister_filesystem(®fs_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"); > 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#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; > +}