18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hypervisor filesystem for Linux on s390. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2006, 2008 68c2ecf20Sopenharmony_ci * Author(s): Michael Holzheu <holzheu@de.ibm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "hypfs" 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/fs.h> 158c2ecf20Sopenharmony_ci#include <linux/fs_context.h> 168c2ecf20Sopenharmony_ci#include <linux/fs_parser.h> 178c2ecf20Sopenharmony_ci#include <linux/namei.h> 188c2ecf20Sopenharmony_ci#include <linux/vfs.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 218c2ecf20Sopenharmony_ci#include <linux/time.h> 228c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci#include <linux/kobject.h> 258c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 268c2ecf20Sopenharmony_ci#include <linux/uio.h> 278c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 288c2ecf20Sopenharmony_ci#include "hypfs.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define HYPFS_MAGIC 0x687970 /* ASCII 'hyp' */ 318c2ecf20Sopenharmony_ci#define TMP_SIZE 64 /* size of temporary buffers */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct dentry *hypfs_create_update_file(struct dentry *dir); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct hypfs_sb_info { 368c2ecf20Sopenharmony_ci kuid_t uid; /* uid used for files and dirs */ 378c2ecf20Sopenharmony_ci kgid_t gid; /* gid used for files and dirs */ 388c2ecf20Sopenharmony_ci struct dentry *update_file; /* file to trigger update */ 398c2ecf20Sopenharmony_ci time64_t last_update; /* last update, CLOCK_MONOTONIC time */ 408c2ecf20Sopenharmony_ci struct mutex lock; /* lock to protect update process */ 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const struct file_operations hypfs_file_ops; 448c2ecf20Sopenharmony_cistatic struct file_system_type hypfs_type; 458c2ecf20Sopenharmony_cistatic const struct super_operations hypfs_s_ops; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* start of list of all dentries, which have to be deleted on update */ 488c2ecf20Sopenharmony_cistatic struct dentry *hypfs_last_dentry; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void hypfs_update_update(struct super_block *sb) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct hypfs_sb_info *sb_info = sb->s_fs_info; 538c2ecf20Sopenharmony_ci struct inode *inode = d_inode(sb_info->update_file); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci sb_info->last_update = ktime_get_seconds(); 568c2ecf20Sopenharmony_ci inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* directory tree removal functions */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void hypfs_add_dentry(struct dentry *dentry) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci dentry->d_fsdata = hypfs_last_dentry; 648c2ecf20Sopenharmony_ci hypfs_last_dentry = dentry; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void hypfs_remove(struct dentry *dentry) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct dentry *parent; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci parent = dentry->d_parent; 728c2ecf20Sopenharmony_ci inode_lock(d_inode(parent)); 738c2ecf20Sopenharmony_ci if (simple_positive(dentry)) { 748c2ecf20Sopenharmony_ci if (d_is_dir(dentry)) 758c2ecf20Sopenharmony_ci simple_rmdir(d_inode(parent), dentry); 768c2ecf20Sopenharmony_ci else 778c2ecf20Sopenharmony_ci simple_unlink(d_inode(parent), dentry); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci d_drop(dentry); 808c2ecf20Sopenharmony_ci dput(dentry); 818c2ecf20Sopenharmony_ci inode_unlock(d_inode(parent)); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void hypfs_delete_tree(struct dentry *root) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci while (hypfs_last_dentry) { 878c2ecf20Sopenharmony_ci struct dentry *next_dentry; 888c2ecf20Sopenharmony_ci next_dentry = hypfs_last_dentry->d_fsdata; 898c2ecf20Sopenharmony_ci hypfs_remove(hypfs_last_dentry); 908c2ecf20Sopenharmony_ci hypfs_last_dentry = next_dentry; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct inode *hypfs_make_inode(struct super_block *sb, umode_t mode) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct inode *ret = new_inode(sb); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (ret) { 998c2ecf20Sopenharmony_ci struct hypfs_sb_info *hypfs_info = sb->s_fs_info; 1008c2ecf20Sopenharmony_ci ret->i_ino = get_next_ino(); 1018c2ecf20Sopenharmony_ci ret->i_mode = mode; 1028c2ecf20Sopenharmony_ci ret->i_uid = hypfs_info->uid; 1038c2ecf20Sopenharmony_ci ret->i_gid = hypfs_info->gid; 1048c2ecf20Sopenharmony_ci ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret); 1058c2ecf20Sopenharmony_ci if (S_ISDIR(mode)) 1068c2ecf20Sopenharmony_ci set_nlink(ret, 2); 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci return ret; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void hypfs_evict_inode(struct inode *inode) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci clear_inode(inode); 1148c2ecf20Sopenharmony_ci kfree(inode->i_private); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int hypfs_open(struct inode *inode, struct file *filp) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci char *data = file_inode(filp)->i_private; 1208c2ecf20Sopenharmony_ci struct hypfs_sb_info *fs_info; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (filp->f_mode & FMODE_WRITE) { 1238c2ecf20Sopenharmony_ci if (!(inode->i_mode & S_IWUGO)) 1248c2ecf20Sopenharmony_ci return -EACCES; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci if (filp->f_mode & FMODE_READ) { 1278c2ecf20Sopenharmony_ci if (!(inode->i_mode & S_IRUGO)) 1288c2ecf20Sopenharmony_ci return -EACCES; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci fs_info = inode->i_sb->s_fs_info; 1328c2ecf20Sopenharmony_ci if(data) { 1338c2ecf20Sopenharmony_ci mutex_lock(&fs_info->lock); 1348c2ecf20Sopenharmony_ci filp->private_data = kstrdup(data, GFP_KERNEL); 1358c2ecf20Sopenharmony_ci if (!filp->private_data) { 1368c2ecf20Sopenharmony_ci mutex_unlock(&fs_info->lock); 1378c2ecf20Sopenharmony_ci return -ENOMEM; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci mutex_unlock(&fs_info->lock); 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci return nonseekable_open(inode, filp); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic ssize_t hypfs_read_iter(struct kiocb *iocb, struct iov_iter *to) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct file *file = iocb->ki_filp; 1478c2ecf20Sopenharmony_ci char *data = file->private_data; 1488c2ecf20Sopenharmony_ci size_t available = strlen(data); 1498c2ecf20Sopenharmony_ci loff_t pos = iocb->ki_pos; 1508c2ecf20Sopenharmony_ci size_t count; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (pos < 0) 1538c2ecf20Sopenharmony_ci return -EINVAL; 1548c2ecf20Sopenharmony_ci if (pos >= available || !iov_iter_count(to)) 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci count = copy_to_iter(data + pos, available - pos, to); 1578c2ecf20Sopenharmony_ci if (!count) 1588c2ecf20Sopenharmony_ci return -EFAULT; 1598c2ecf20Sopenharmony_ci iocb->ki_pos = pos + count; 1608c2ecf20Sopenharmony_ci file_accessed(file); 1618c2ecf20Sopenharmony_ci return count; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic ssize_t hypfs_write_iter(struct kiocb *iocb, struct iov_iter *from) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci int rc; 1678c2ecf20Sopenharmony_ci struct super_block *sb = file_inode(iocb->ki_filp)->i_sb; 1688c2ecf20Sopenharmony_ci struct hypfs_sb_info *fs_info = sb->s_fs_info; 1698c2ecf20Sopenharmony_ci size_t count = iov_iter_count(from); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * Currently we only allow one update per second for two reasons: 1738c2ecf20Sopenharmony_ci * 1. diag 204 is VERY expensive 1748c2ecf20Sopenharmony_ci * 2. If several processes do updates in parallel and then read the 1758c2ecf20Sopenharmony_ci * hypfs data, the likelihood of collisions is reduced, if we restrict 1768c2ecf20Sopenharmony_ci * the minimum update interval. A collision occurs, if during the 1778c2ecf20Sopenharmony_ci * data gathering of one process another process triggers an update 1788c2ecf20Sopenharmony_ci * If the first process wants to ensure consistent data, it has 1798c2ecf20Sopenharmony_ci * to restart data collection in this case. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci mutex_lock(&fs_info->lock); 1828c2ecf20Sopenharmony_ci if (fs_info->last_update == ktime_get_seconds()) { 1838c2ecf20Sopenharmony_ci rc = -EBUSY; 1848c2ecf20Sopenharmony_ci goto out; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci hypfs_delete_tree(sb->s_root); 1878c2ecf20Sopenharmony_ci if (MACHINE_IS_VM) 1888c2ecf20Sopenharmony_ci rc = hypfs_vm_create_files(sb->s_root); 1898c2ecf20Sopenharmony_ci else 1908c2ecf20Sopenharmony_ci rc = hypfs_diag_create_files(sb->s_root); 1918c2ecf20Sopenharmony_ci if (rc) { 1928c2ecf20Sopenharmony_ci pr_err("Updating the hypfs tree failed\n"); 1938c2ecf20Sopenharmony_ci hypfs_delete_tree(sb->s_root); 1948c2ecf20Sopenharmony_ci goto out; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci hypfs_update_update(sb); 1978c2ecf20Sopenharmony_ci rc = count; 1988c2ecf20Sopenharmony_ci iov_iter_advance(from, count); 1998c2ecf20Sopenharmony_ciout: 2008c2ecf20Sopenharmony_ci mutex_unlock(&fs_info->lock); 2018c2ecf20Sopenharmony_ci return rc; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int hypfs_release(struct inode *inode, struct file *filp) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci kfree(filp->private_data); 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cienum { Opt_uid, Opt_gid, }; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic const struct fs_parameter_spec hypfs_fs_parameters[] = { 2138c2ecf20Sopenharmony_ci fsparam_u32("gid", Opt_gid), 2148c2ecf20Sopenharmony_ci fsparam_u32("uid", Opt_uid), 2158c2ecf20Sopenharmony_ci {} 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int hypfs_parse_param(struct fs_context *fc, struct fs_parameter *param) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct hypfs_sb_info *hypfs_info = fc->s_fs_info; 2218c2ecf20Sopenharmony_ci struct fs_parse_result result; 2228c2ecf20Sopenharmony_ci kuid_t uid; 2238c2ecf20Sopenharmony_ci kgid_t gid; 2248c2ecf20Sopenharmony_ci int opt; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci opt = fs_parse(fc, hypfs_fs_parameters, param, &result); 2278c2ecf20Sopenharmony_ci if (opt < 0) 2288c2ecf20Sopenharmony_ci return opt; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci switch (opt) { 2318c2ecf20Sopenharmony_ci case Opt_uid: 2328c2ecf20Sopenharmony_ci uid = make_kuid(current_user_ns(), result.uint_32); 2338c2ecf20Sopenharmony_ci if (!uid_valid(uid)) 2348c2ecf20Sopenharmony_ci return invalf(fc, "Unknown uid"); 2358c2ecf20Sopenharmony_ci hypfs_info->uid = uid; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci case Opt_gid: 2388c2ecf20Sopenharmony_ci gid = make_kgid(current_user_ns(), result.uint_32); 2398c2ecf20Sopenharmony_ci if (!gid_valid(gid)) 2408c2ecf20Sopenharmony_ci return invalf(fc, "Unknown gid"); 2418c2ecf20Sopenharmony_ci hypfs_info->gid = gid; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int hypfs_show_options(struct seq_file *s, struct dentry *root) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct hypfs_sb_info *hypfs_info = root->d_sb->s_fs_info; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci seq_printf(s, ",uid=%u", from_kuid_munged(&init_user_ns, hypfs_info->uid)); 2528c2ecf20Sopenharmony_ci seq_printf(s, ",gid=%u", from_kgid_munged(&init_user_ns, hypfs_info->gid)); 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int hypfs_fill_super(struct super_block *sb, struct fs_context *fc) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct hypfs_sb_info *sbi = sb->s_fs_info; 2598c2ecf20Sopenharmony_ci struct inode *root_inode; 2608c2ecf20Sopenharmony_ci struct dentry *root_dentry, *update_file; 2618c2ecf20Sopenharmony_ci int rc; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci sb->s_blocksize = PAGE_SIZE; 2648c2ecf20Sopenharmony_ci sb->s_blocksize_bits = PAGE_SHIFT; 2658c2ecf20Sopenharmony_ci sb->s_magic = HYPFS_MAGIC; 2668c2ecf20Sopenharmony_ci sb->s_op = &hypfs_s_ops; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci root_inode = hypfs_make_inode(sb, S_IFDIR | 0755); 2698c2ecf20Sopenharmony_ci if (!root_inode) 2708c2ecf20Sopenharmony_ci return -ENOMEM; 2718c2ecf20Sopenharmony_ci root_inode->i_op = &simple_dir_inode_operations; 2728c2ecf20Sopenharmony_ci root_inode->i_fop = &simple_dir_operations; 2738c2ecf20Sopenharmony_ci sb->s_root = root_dentry = d_make_root(root_inode); 2748c2ecf20Sopenharmony_ci if (!root_dentry) 2758c2ecf20Sopenharmony_ci return -ENOMEM; 2768c2ecf20Sopenharmony_ci if (MACHINE_IS_VM) 2778c2ecf20Sopenharmony_ci rc = hypfs_vm_create_files(root_dentry); 2788c2ecf20Sopenharmony_ci else 2798c2ecf20Sopenharmony_ci rc = hypfs_diag_create_files(root_dentry); 2808c2ecf20Sopenharmony_ci if (rc) 2818c2ecf20Sopenharmony_ci return rc; 2828c2ecf20Sopenharmony_ci update_file = hypfs_create_update_file(root_dentry); 2838c2ecf20Sopenharmony_ci if (IS_ERR(update_file)) 2848c2ecf20Sopenharmony_ci return PTR_ERR(update_file); 2858c2ecf20Sopenharmony_ci sbi->update_file = update_file; 2868c2ecf20Sopenharmony_ci hypfs_update_update(sb); 2878c2ecf20Sopenharmony_ci pr_info("Hypervisor filesystem mounted\n"); 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int hypfs_get_tree(struct fs_context *fc) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci return get_tree_single(fc, hypfs_fill_super); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void hypfs_free_fc(struct fs_context *fc) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci kfree(fc->s_fs_info); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic const struct fs_context_operations hypfs_context_ops = { 3028c2ecf20Sopenharmony_ci .free = hypfs_free_fc, 3038c2ecf20Sopenharmony_ci .parse_param = hypfs_parse_param, 3048c2ecf20Sopenharmony_ci .get_tree = hypfs_get_tree, 3058c2ecf20Sopenharmony_ci}; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int hypfs_init_fs_context(struct fs_context *fc) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct hypfs_sb_info *sbi; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci sbi = kzalloc(sizeof(struct hypfs_sb_info), GFP_KERNEL); 3128c2ecf20Sopenharmony_ci if (!sbi) 3138c2ecf20Sopenharmony_ci return -ENOMEM; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci mutex_init(&sbi->lock); 3168c2ecf20Sopenharmony_ci sbi->uid = current_uid(); 3178c2ecf20Sopenharmony_ci sbi->gid = current_gid(); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci fc->s_fs_info = sbi; 3208c2ecf20Sopenharmony_ci fc->ops = &hypfs_context_ops; 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void hypfs_kill_super(struct super_block *sb) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct hypfs_sb_info *sb_info = sb->s_fs_info; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (sb->s_root) 3298c2ecf20Sopenharmony_ci hypfs_delete_tree(sb->s_root); 3308c2ecf20Sopenharmony_ci if (sb_info && sb_info->update_file) 3318c2ecf20Sopenharmony_ci hypfs_remove(sb_info->update_file); 3328c2ecf20Sopenharmony_ci kfree(sb->s_fs_info); 3338c2ecf20Sopenharmony_ci sb->s_fs_info = NULL; 3348c2ecf20Sopenharmony_ci kill_litter_super(sb); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic struct dentry *hypfs_create_file(struct dentry *parent, const char *name, 3388c2ecf20Sopenharmony_ci char *data, umode_t mode) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct dentry *dentry; 3418c2ecf20Sopenharmony_ci struct inode *inode; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci inode_lock(d_inode(parent)); 3448c2ecf20Sopenharmony_ci dentry = lookup_one_len(name, parent, strlen(name)); 3458c2ecf20Sopenharmony_ci if (IS_ERR(dentry)) { 3468c2ecf20Sopenharmony_ci dentry = ERR_PTR(-ENOMEM); 3478c2ecf20Sopenharmony_ci goto fail; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci inode = hypfs_make_inode(parent->d_sb, mode); 3508c2ecf20Sopenharmony_ci if (!inode) { 3518c2ecf20Sopenharmony_ci dput(dentry); 3528c2ecf20Sopenharmony_ci dentry = ERR_PTR(-ENOMEM); 3538c2ecf20Sopenharmony_ci goto fail; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci if (S_ISREG(mode)) { 3568c2ecf20Sopenharmony_ci inode->i_fop = &hypfs_file_ops; 3578c2ecf20Sopenharmony_ci if (data) 3588c2ecf20Sopenharmony_ci inode->i_size = strlen(data); 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci inode->i_size = 0; 3618c2ecf20Sopenharmony_ci } else if (S_ISDIR(mode)) { 3628c2ecf20Sopenharmony_ci inode->i_op = &simple_dir_inode_operations; 3638c2ecf20Sopenharmony_ci inode->i_fop = &simple_dir_operations; 3648c2ecf20Sopenharmony_ci inc_nlink(d_inode(parent)); 3658c2ecf20Sopenharmony_ci } else 3668c2ecf20Sopenharmony_ci BUG(); 3678c2ecf20Sopenharmony_ci inode->i_private = data; 3688c2ecf20Sopenharmony_ci d_instantiate(dentry, inode); 3698c2ecf20Sopenharmony_ci dget(dentry); 3708c2ecf20Sopenharmony_cifail: 3718c2ecf20Sopenharmony_ci inode_unlock(d_inode(parent)); 3728c2ecf20Sopenharmony_ci return dentry; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistruct dentry *hypfs_mkdir(struct dentry *parent, const char *name) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct dentry *dentry; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci dentry = hypfs_create_file(parent, name, NULL, S_IFDIR | DIR_MODE); 3808c2ecf20Sopenharmony_ci if (IS_ERR(dentry)) 3818c2ecf20Sopenharmony_ci return dentry; 3828c2ecf20Sopenharmony_ci hypfs_add_dentry(dentry); 3838c2ecf20Sopenharmony_ci return dentry; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic struct dentry *hypfs_create_update_file(struct dentry *dir) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct dentry *dentry; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci dentry = hypfs_create_file(dir, "update", NULL, 3918c2ecf20Sopenharmony_ci S_IFREG | UPDATE_FILE_MODE); 3928c2ecf20Sopenharmony_ci /* 3938c2ecf20Sopenharmony_ci * We do not put the update file on the 'delete' list with 3948c2ecf20Sopenharmony_ci * hypfs_add_dentry(), since it should not be removed when the tree 3958c2ecf20Sopenharmony_ci * is updated. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci return dentry; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistruct dentry *hypfs_create_u64(struct dentry *dir, 4018c2ecf20Sopenharmony_ci const char *name, __u64 value) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci char *buffer; 4048c2ecf20Sopenharmony_ci char tmp[TMP_SIZE]; 4058c2ecf20Sopenharmony_ci struct dentry *dentry; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci snprintf(tmp, TMP_SIZE, "%llu\n", (unsigned long long int)value); 4088c2ecf20Sopenharmony_ci buffer = kstrdup(tmp, GFP_KERNEL); 4098c2ecf20Sopenharmony_ci if (!buffer) 4108c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4118c2ecf20Sopenharmony_ci dentry = 4128c2ecf20Sopenharmony_ci hypfs_create_file(dir, name, buffer, S_IFREG | REG_FILE_MODE); 4138c2ecf20Sopenharmony_ci if (IS_ERR(dentry)) { 4148c2ecf20Sopenharmony_ci kfree(buffer); 4158c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci hypfs_add_dentry(dentry); 4188c2ecf20Sopenharmony_ci return dentry; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistruct dentry *hypfs_create_str(struct dentry *dir, 4228c2ecf20Sopenharmony_ci const char *name, char *string) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci char *buffer; 4258c2ecf20Sopenharmony_ci struct dentry *dentry; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci buffer = kmalloc(strlen(string) + 2, GFP_KERNEL); 4288c2ecf20Sopenharmony_ci if (!buffer) 4298c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4308c2ecf20Sopenharmony_ci sprintf(buffer, "%s\n", string); 4318c2ecf20Sopenharmony_ci dentry = 4328c2ecf20Sopenharmony_ci hypfs_create_file(dir, name, buffer, S_IFREG | REG_FILE_MODE); 4338c2ecf20Sopenharmony_ci if (IS_ERR(dentry)) { 4348c2ecf20Sopenharmony_ci kfree(buffer); 4358c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci hypfs_add_dentry(dentry); 4388c2ecf20Sopenharmony_ci return dentry; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic const struct file_operations hypfs_file_ops = { 4428c2ecf20Sopenharmony_ci .open = hypfs_open, 4438c2ecf20Sopenharmony_ci .release = hypfs_release, 4448c2ecf20Sopenharmony_ci .read_iter = hypfs_read_iter, 4458c2ecf20Sopenharmony_ci .write_iter = hypfs_write_iter, 4468c2ecf20Sopenharmony_ci .llseek = no_llseek, 4478c2ecf20Sopenharmony_ci}; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic struct file_system_type hypfs_type = { 4508c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4518c2ecf20Sopenharmony_ci .name = "s390_hypfs", 4528c2ecf20Sopenharmony_ci .init_fs_context = hypfs_init_fs_context, 4538c2ecf20Sopenharmony_ci .parameters = hypfs_fs_parameters, 4548c2ecf20Sopenharmony_ci .kill_sb = hypfs_kill_super 4558c2ecf20Sopenharmony_ci}; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic const struct super_operations hypfs_s_ops = { 4588c2ecf20Sopenharmony_ci .statfs = simple_statfs, 4598c2ecf20Sopenharmony_ci .evict_inode = hypfs_evict_inode, 4608c2ecf20Sopenharmony_ci .show_options = hypfs_show_options, 4618c2ecf20Sopenharmony_ci}; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int __init hypfs_init(void) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci int rc; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci hypfs_dbfs_init(); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (hypfs_diag_init()) { 4708c2ecf20Sopenharmony_ci rc = -ENODATA; 4718c2ecf20Sopenharmony_ci goto fail_dbfs_exit; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci if (hypfs_vm_init()) { 4748c2ecf20Sopenharmony_ci rc = -ENODATA; 4758c2ecf20Sopenharmony_ci goto fail_hypfs_diag_exit; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci hypfs_sprp_init(); 4788c2ecf20Sopenharmony_ci if (hypfs_diag0c_init()) { 4798c2ecf20Sopenharmony_ci rc = -ENODATA; 4808c2ecf20Sopenharmony_ci goto fail_hypfs_sprp_exit; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci rc = sysfs_create_mount_point(hypervisor_kobj, "s390"); 4838c2ecf20Sopenharmony_ci if (rc) 4848c2ecf20Sopenharmony_ci goto fail_hypfs_diag0c_exit; 4858c2ecf20Sopenharmony_ci rc = register_filesystem(&hypfs_type); 4868c2ecf20Sopenharmony_ci if (rc) 4878c2ecf20Sopenharmony_ci goto fail_filesystem; 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cifail_filesystem: 4918c2ecf20Sopenharmony_ci sysfs_remove_mount_point(hypervisor_kobj, "s390"); 4928c2ecf20Sopenharmony_cifail_hypfs_diag0c_exit: 4938c2ecf20Sopenharmony_ci hypfs_diag0c_exit(); 4948c2ecf20Sopenharmony_cifail_hypfs_sprp_exit: 4958c2ecf20Sopenharmony_ci hypfs_sprp_exit(); 4968c2ecf20Sopenharmony_ci hypfs_vm_exit(); 4978c2ecf20Sopenharmony_cifail_hypfs_diag_exit: 4988c2ecf20Sopenharmony_ci hypfs_diag_exit(); 4998c2ecf20Sopenharmony_ci pr_err("Initialization of hypfs failed with rc=%i\n", rc); 5008c2ecf20Sopenharmony_cifail_dbfs_exit: 5018c2ecf20Sopenharmony_ci hypfs_dbfs_exit(); 5028c2ecf20Sopenharmony_ci return rc; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_cidevice_initcall(hypfs_init) 505