162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * dlmfs.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Code which implements the kernel side of a minimal userspace 662306a36Sopenharmony_ci * interface to our DLM. This file handles the virtual file system 762306a36Sopenharmony_ci * used for communication with userspace. Credit should go to ramfs, 862306a36Sopenharmony_ci * which was a template for the fs side of this module. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 2003, 2004 Oracle. All rights reserved. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* Simple VFS hooks based on: */ 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Resizable simple ram filesystem for Linux. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Copyright (C) 2000 Linus Torvalds. 1862306a36Sopenharmony_ci * 2000 Transmeta Corp. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/fs.h> 2362306a36Sopenharmony_ci#include <linux/pagemap.h> 2462306a36Sopenharmony_ci#include <linux/types.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <linux/highmem.h> 2762306a36Sopenharmony_ci#include <linux/init.h> 2862306a36Sopenharmony_ci#include <linux/string.h> 2962306a36Sopenharmony_ci#include <linux/backing-dev.h> 3062306a36Sopenharmony_ci#include <linux/poll.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <linux/uaccess.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "../stackglue.h" 3562306a36Sopenharmony_ci#include "userdlm.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define MLOG_MASK_PREFIX ML_DLMFS 3862306a36Sopenharmony_ci#include "../cluster/masklog.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic const struct super_operations dlmfs_ops; 4262306a36Sopenharmony_cistatic const struct file_operations dlmfs_file_operations; 4362306a36Sopenharmony_cistatic const struct inode_operations dlmfs_dir_inode_operations; 4462306a36Sopenharmony_cistatic const struct inode_operations dlmfs_root_inode_operations; 4562306a36Sopenharmony_cistatic const struct inode_operations dlmfs_file_inode_operations; 4662306a36Sopenharmony_cistatic struct kmem_cache *dlmfs_inode_cache; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct workqueue_struct *user_dlm_worker; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * These are the ABI capabilities of dlmfs. 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * Over time, dlmfs has added some features that were not part of the 5662306a36Sopenharmony_ci * initial ABI. Unfortunately, some of these features are not detectable 5762306a36Sopenharmony_ci * via standard usage. For example, Linux's default poll always returns 5862306a36Sopenharmony_ci * EPOLLIN, so there is no way for a caller of poll(2) to know when dlmfs 5962306a36Sopenharmony_ci * added poll support. Instead, we provide this list of new capabilities. 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * Capabilities is a read-only attribute. We do it as a module parameter 6262306a36Sopenharmony_ci * so we can discover it whether dlmfs is built in, loaded, or even not 6362306a36Sopenharmony_ci * loaded. 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * The ABI features are local to this machine's dlmfs mount. This is 6662306a36Sopenharmony_ci * distinct from the locking protocol, which is concerned with inter-node 6762306a36Sopenharmony_ci * interaction. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * Capabilities: 7062306a36Sopenharmony_ci * - bast : EPOLLIN against the file descriptor of a held lock 7162306a36Sopenharmony_ci * signifies a bast fired on the lock. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci#define DLMFS_CAPABILITIES "bast stackglue" 7462306a36Sopenharmony_cistatic int param_set_dlmfs_capabilities(const char *val, 7562306a36Sopenharmony_ci const struct kernel_param *kp) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci printk(KERN_ERR "%s: readonly parameter\n", kp->name); 7862306a36Sopenharmony_ci return -EINVAL; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_cistatic int param_get_dlmfs_capabilities(char *buffer, 8162306a36Sopenharmony_ci const struct kernel_param *kp) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci return strlcpy(buffer, DLMFS_CAPABILITIES, 8462306a36Sopenharmony_ci strlen(DLMFS_CAPABILITIES) + 1); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_cimodule_param_call(capabilities, param_set_dlmfs_capabilities, 8762306a36Sopenharmony_ci param_get_dlmfs_capabilities, NULL, 0444); 8862306a36Sopenharmony_ciMODULE_PARM_DESC(capabilities, DLMFS_CAPABILITIES); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * decodes a set of open flags into a valid lock level and a set of flags. 9362306a36Sopenharmony_ci * returns < 0 if we have invalid flags 9462306a36Sopenharmony_ci * flags which mean something to us: 9562306a36Sopenharmony_ci * O_RDONLY -> PRMODE level 9662306a36Sopenharmony_ci * O_WRONLY -> EXMODE level 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * O_NONBLOCK -> NOQUEUE 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_cistatic int dlmfs_decode_open_flags(int open_flags, 10162306a36Sopenharmony_ci int *level, 10262306a36Sopenharmony_ci int *flags) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci if (open_flags & (O_WRONLY|O_RDWR)) 10562306a36Sopenharmony_ci *level = DLM_LOCK_EX; 10662306a36Sopenharmony_ci else 10762306a36Sopenharmony_ci *level = DLM_LOCK_PR; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci *flags = 0; 11062306a36Sopenharmony_ci if (open_flags & O_NONBLOCK) 11162306a36Sopenharmony_ci *flags |= DLM_LKF_NOQUEUE; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int dlmfs_file_open(struct inode *inode, 11762306a36Sopenharmony_ci struct file *file) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int status, level, flags; 12062306a36Sopenharmony_ci struct dlmfs_filp_private *fp = NULL; 12162306a36Sopenharmony_ci struct dlmfs_inode_private *ip; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 12462306a36Sopenharmony_ci BUG(); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci mlog(0, "open called on inode %lu, flags 0x%x\n", inode->i_ino, 12762306a36Sopenharmony_ci file->f_flags); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci status = dlmfs_decode_open_flags(file->f_flags, &level, &flags); 13062306a36Sopenharmony_ci if (status < 0) 13162306a36Sopenharmony_ci goto bail; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* We don't want to honor O_APPEND at read/write time as it 13462306a36Sopenharmony_ci * doesn't make sense for LVB writes. */ 13562306a36Sopenharmony_ci file->f_flags &= ~O_APPEND; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci fp = kmalloc(sizeof(*fp), GFP_NOFS); 13862306a36Sopenharmony_ci if (!fp) { 13962306a36Sopenharmony_ci status = -ENOMEM; 14062306a36Sopenharmony_ci goto bail; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci fp->fp_lock_level = level; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ip = DLMFS_I(inode); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci status = user_dlm_cluster_lock(&ip->ip_lockres, level, flags); 14762306a36Sopenharmony_ci if (status < 0) { 14862306a36Sopenharmony_ci /* this is a strange error to return here but I want 14962306a36Sopenharmony_ci * to be able userspace to be able to distinguish a 15062306a36Sopenharmony_ci * valid lock request from one that simply couldn't be 15162306a36Sopenharmony_ci * granted. */ 15262306a36Sopenharmony_ci if (flags & DLM_LKF_NOQUEUE && status == -EAGAIN) 15362306a36Sopenharmony_ci status = -ETXTBSY; 15462306a36Sopenharmony_ci kfree(fp); 15562306a36Sopenharmony_ci goto bail; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci file->private_data = fp; 15962306a36Sopenharmony_cibail: 16062306a36Sopenharmony_ci return status; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int dlmfs_file_release(struct inode *inode, 16462306a36Sopenharmony_ci struct file *file) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci int level; 16762306a36Sopenharmony_ci struct dlmfs_inode_private *ip = DLMFS_I(inode); 16862306a36Sopenharmony_ci struct dlmfs_filp_private *fp = file->private_data; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 17162306a36Sopenharmony_ci BUG(); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci mlog(0, "close called on inode %lu\n", inode->i_ino); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (fp) { 17662306a36Sopenharmony_ci level = fp->fp_lock_level; 17762306a36Sopenharmony_ci if (level != DLM_LOCK_IV) 17862306a36Sopenharmony_ci user_dlm_cluster_unlock(&ip->ip_lockres, level); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci kfree(fp); 18162306a36Sopenharmony_ci file->private_data = NULL; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* 18862306a36Sopenharmony_ci * We do ->setattr() just to override size changes. Our size is the size 18962306a36Sopenharmony_ci * of the LVB and nothing else. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_cistatic int dlmfs_file_setattr(struct mnt_idmap *idmap, 19262306a36Sopenharmony_ci struct dentry *dentry, struct iattr *attr) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci int error; 19562306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci attr->ia_valid &= ~ATTR_SIZE; 19862306a36Sopenharmony_ci error = setattr_prepare(&nop_mnt_idmap, dentry, attr); 19962306a36Sopenharmony_ci if (error) 20062306a36Sopenharmony_ci return error; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci setattr_copy(&nop_mnt_idmap, inode, attr); 20362306a36Sopenharmony_ci mark_inode_dirty(inode); 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic __poll_t dlmfs_file_poll(struct file *file, poll_table *wait) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci __poll_t event = 0; 21062306a36Sopenharmony_ci struct inode *inode = file_inode(file); 21162306a36Sopenharmony_ci struct dlmfs_inode_private *ip = DLMFS_I(inode); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci poll_wait(file, &ip->ip_lockres.l_event, wait); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci spin_lock(&ip->ip_lockres.l_lock); 21662306a36Sopenharmony_ci if (ip->ip_lockres.l_flags & USER_LOCK_BLOCKED) 21762306a36Sopenharmony_ci event = EPOLLIN | EPOLLRDNORM; 21862306a36Sopenharmony_ci spin_unlock(&ip->ip_lockres.l_lock); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return event; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic ssize_t dlmfs_file_read(struct file *file, 22462306a36Sopenharmony_ci char __user *buf, 22562306a36Sopenharmony_ci size_t count, 22662306a36Sopenharmony_ci loff_t *ppos) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci char lvb[DLM_LVB_LEN]; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (!user_dlm_read_lvb(file_inode(file), lvb)) 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, lvb, sizeof(lvb)); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic ssize_t dlmfs_file_write(struct file *filp, 23762306a36Sopenharmony_ci const char __user *buf, 23862306a36Sopenharmony_ci size_t count, 23962306a36Sopenharmony_ci loff_t *ppos) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci char lvb_buf[DLM_LVB_LEN]; 24262306a36Sopenharmony_ci int bytes_left; 24362306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci mlog(0, "inode %lu, count = %zu, *ppos = %llu\n", 24662306a36Sopenharmony_ci inode->i_ino, count, *ppos); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (*ppos >= DLM_LVB_LEN) 24962306a36Sopenharmony_ci return -ENOSPC; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* don't write past the lvb */ 25262306a36Sopenharmony_ci if (count > DLM_LVB_LEN - *ppos) 25362306a36Sopenharmony_ci count = DLM_LVB_LEN - *ppos; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (!count) 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci bytes_left = copy_from_user(lvb_buf, buf, count); 25962306a36Sopenharmony_ci count -= bytes_left; 26062306a36Sopenharmony_ci if (count) 26162306a36Sopenharmony_ci user_dlm_write_lvb(inode, lvb_buf, count); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci *ppos = *ppos + count; 26462306a36Sopenharmony_ci mlog(0, "wrote %zu bytes\n", count); 26562306a36Sopenharmony_ci return count; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic void dlmfs_init_once(void *foo) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct dlmfs_inode_private *ip = 27162306a36Sopenharmony_ci (struct dlmfs_inode_private *) foo; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ip->ip_conn = NULL; 27462306a36Sopenharmony_ci ip->ip_parent = NULL; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci inode_init_once(&ip->ip_vfs_inode); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic struct inode *dlmfs_alloc_inode(struct super_block *sb) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct dlmfs_inode_private *ip; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci ip = alloc_inode_sb(sb, dlmfs_inode_cache, GFP_NOFS); 28462306a36Sopenharmony_ci if (!ip) 28562306a36Sopenharmony_ci return NULL; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return &ip->ip_vfs_inode; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic void dlmfs_free_inode(struct inode *inode) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci kmem_cache_free(dlmfs_inode_cache, DLMFS_I(inode)); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void dlmfs_evict_inode(struct inode *inode) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci int status; 29862306a36Sopenharmony_ci struct dlmfs_inode_private *ip; 29962306a36Sopenharmony_ci struct user_lock_res *lockres; 30062306a36Sopenharmony_ci int teardown; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci clear_inode(inode); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci mlog(0, "inode %lu\n", inode->i_ino); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci ip = DLMFS_I(inode); 30762306a36Sopenharmony_ci lockres = &ip->ip_lockres; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) { 31062306a36Sopenharmony_ci spin_lock(&lockres->l_lock); 31162306a36Sopenharmony_ci teardown = !!(lockres->l_flags & USER_LOCK_IN_TEARDOWN); 31262306a36Sopenharmony_ci spin_unlock(&lockres->l_lock); 31362306a36Sopenharmony_ci if (!teardown) { 31462306a36Sopenharmony_ci status = user_dlm_destroy_lock(lockres); 31562306a36Sopenharmony_ci if (status < 0) 31662306a36Sopenharmony_ci mlog_errno(status); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci iput(ip->ip_parent); 31962306a36Sopenharmony_ci goto clear_fields; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci mlog(0, "we're a directory, ip->ip_conn = 0x%p\n", ip->ip_conn); 32362306a36Sopenharmony_ci /* we must be a directory. If required, lets unregister the 32462306a36Sopenharmony_ci * dlm context now. */ 32562306a36Sopenharmony_ci if (ip->ip_conn) 32662306a36Sopenharmony_ci user_dlm_unregister(ip->ip_conn); 32762306a36Sopenharmony_ciclear_fields: 32862306a36Sopenharmony_ci ip->ip_parent = NULL; 32962306a36Sopenharmony_ci ip->ip_conn = NULL; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic struct inode *dlmfs_get_root_inode(struct super_block *sb) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct inode *inode = new_inode(sb); 33562306a36Sopenharmony_ci umode_t mode = S_IFDIR | 0755; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (inode) { 33862306a36Sopenharmony_ci inode->i_ino = get_next_ino(); 33962306a36Sopenharmony_ci inode_init_owner(&nop_mnt_idmap, inode, NULL, mode); 34062306a36Sopenharmony_ci inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode); 34162306a36Sopenharmony_ci inc_nlink(inode); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci inode->i_fop = &simple_dir_operations; 34462306a36Sopenharmony_ci inode->i_op = &dlmfs_root_inode_operations; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return inode; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic struct inode *dlmfs_get_inode(struct inode *parent, 35162306a36Sopenharmony_ci struct dentry *dentry, 35262306a36Sopenharmony_ci umode_t mode) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct super_block *sb = parent->i_sb; 35562306a36Sopenharmony_ci struct inode * inode = new_inode(sb); 35662306a36Sopenharmony_ci struct dlmfs_inode_private *ip; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!inode) 35962306a36Sopenharmony_ci return NULL; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci inode->i_ino = get_next_ino(); 36262306a36Sopenharmony_ci inode_init_owner(&nop_mnt_idmap, inode, parent, mode); 36362306a36Sopenharmony_ci inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci ip = DLMFS_I(inode); 36662306a36Sopenharmony_ci ip->ip_conn = DLMFS_I(parent)->ip_conn; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci switch (mode & S_IFMT) { 36962306a36Sopenharmony_ci default: 37062306a36Sopenharmony_ci /* for now we don't support anything other than 37162306a36Sopenharmony_ci * directories and regular files. */ 37262306a36Sopenharmony_ci BUG(); 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci case S_IFREG: 37562306a36Sopenharmony_ci inode->i_op = &dlmfs_file_inode_operations; 37662306a36Sopenharmony_ci inode->i_fop = &dlmfs_file_operations; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci i_size_write(inode, DLM_LVB_LEN); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci user_dlm_lock_res_init(&ip->ip_lockres, dentry); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* released at clear_inode time, this insures that we 38362306a36Sopenharmony_ci * get to drop the dlm reference on each lock *before* 38462306a36Sopenharmony_ci * we call the unregister code for releasing parent 38562306a36Sopenharmony_ci * directories. */ 38662306a36Sopenharmony_ci ip->ip_parent = igrab(parent); 38762306a36Sopenharmony_ci BUG_ON(!ip->ip_parent); 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case S_IFDIR: 39062306a36Sopenharmony_ci inode->i_op = &dlmfs_dir_inode_operations; 39162306a36Sopenharmony_ci inode->i_fop = &simple_dir_operations; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* directory inodes start off with i_nlink == 39462306a36Sopenharmony_ci * 2 (for "." entry) */ 39562306a36Sopenharmony_ci inc_nlink(inode); 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci return inode; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/* 40262306a36Sopenharmony_ci * File creation. Allocate an inode, and we're done.. 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_ci/* SMP-safe */ 40562306a36Sopenharmony_cistatic int dlmfs_mkdir(struct mnt_idmap * idmap, 40662306a36Sopenharmony_ci struct inode * dir, 40762306a36Sopenharmony_ci struct dentry * dentry, 40862306a36Sopenharmony_ci umode_t mode) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci int status; 41162306a36Sopenharmony_ci struct inode *inode = NULL; 41262306a36Sopenharmony_ci const struct qstr *domain = &dentry->d_name; 41362306a36Sopenharmony_ci struct dlmfs_inode_private *ip; 41462306a36Sopenharmony_ci struct ocfs2_cluster_connection *conn; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci mlog(0, "mkdir %.*s\n", domain->len, domain->name); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* verify that we have a proper domain */ 41962306a36Sopenharmony_ci if (domain->len >= GROUP_NAME_MAX) { 42062306a36Sopenharmony_ci status = -EINVAL; 42162306a36Sopenharmony_ci mlog(ML_ERROR, "invalid domain name for directory.\n"); 42262306a36Sopenharmony_ci goto bail; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci inode = dlmfs_get_inode(dir, dentry, mode | S_IFDIR); 42662306a36Sopenharmony_ci if (!inode) { 42762306a36Sopenharmony_ci status = -ENOMEM; 42862306a36Sopenharmony_ci mlog_errno(status); 42962306a36Sopenharmony_ci goto bail; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci ip = DLMFS_I(inode); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci conn = user_dlm_register(domain); 43562306a36Sopenharmony_ci if (IS_ERR(conn)) { 43662306a36Sopenharmony_ci status = PTR_ERR(conn); 43762306a36Sopenharmony_ci mlog(ML_ERROR, "Error %d could not register domain \"%.*s\"\n", 43862306a36Sopenharmony_ci status, domain->len, domain->name); 43962306a36Sopenharmony_ci goto bail; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci ip->ip_conn = conn; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci inc_nlink(dir); 44462306a36Sopenharmony_ci d_instantiate(dentry, inode); 44562306a36Sopenharmony_ci dget(dentry); /* Extra count - pin the dentry in core */ 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci status = 0; 44862306a36Sopenharmony_cibail: 44962306a36Sopenharmony_ci if (status < 0) 45062306a36Sopenharmony_ci iput(inode); 45162306a36Sopenharmony_ci return status; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int dlmfs_create(struct mnt_idmap *idmap, 45562306a36Sopenharmony_ci struct inode *dir, 45662306a36Sopenharmony_ci struct dentry *dentry, 45762306a36Sopenharmony_ci umode_t mode, 45862306a36Sopenharmony_ci bool excl) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci int status = 0; 46162306a36Sopenharmony_ci struct inode *inode; 46262306a36Sopenharmony_ci const struct qstr *name = &dentry->d_name; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci mlog(0, "create %.*s\n", name->len, name->name); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* verify name is valid and doesn't contain any dlm reserved 46762306a36Sopenharmony_ci * characters */ 46862306a36Sopenharmony_ci if (name->len >= USER_DLM_LOCK_ID_MAX_LEN || 46962306a36Sopenharmony_ci name->name[0] == '$') { 47062306a36Sopenharmony_ci status = -EINVAL; 47162306a36Sopenharmony_ci mlog(ML_ERROR, "invalid lock name, %.*s\n", name->len, 47262306a36Sopenharmony_ci name->name); 47362306a36Sopenharmony_ci goto bail; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci inode = dlmfs_get_inode(dir, dentry, mode | S_IFREG); 47762306a36Sopenharmony_ci if (!inode) { 47862306a36Sopenharmony_ci status = -ENOMEM; 47962306a36Sopenharmony_ci mlog_errno(status); 48062306a36Sopenharmony_ci goto bail; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci d_instantiate(dentry, inode); 48462306a36Sopenharmony_ci dget(dentry); /* Extra count - pin the dentry in core */ 48562306a36Sopenharmony_cibail: 48662306a36Sopenharmony_ci return status; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int dlmfs_unlink(struct inode *dir, 49062306a36Sopenharmony_ci struct dentry *dentry) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci int status; 49362306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci mlog(0, "unlink inode %lu\n", inode->i_ino); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* if there are no current holders, or none that are waiting 49862306a36Sopenharmony_ci * to acquire a lock, this basically destroys our lockres. */ 49962306a36Sopenharmony_ci status = user_dlm_destroy_lock(&DLMFS_I(inode)->ip_lockres); 50062306a36Sopenharmony_ci if (status < 0) { 50162306a36Sopenharmony_ci mlog(ML_ERROR, "unlink %pd, error %d from destroy\n", 50262306a36Sopenharmony_ci dentry, status); 50362306a36Sopenharmony_ci goto bail; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci status = simple_unlink(dir, dentry); 50662306a36Sopenharmony_cibail: 50762306a36Sopenharmony_ci return status; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int dlmfs_fill_super(struct super_block * sb, 51162306a36Sopenharmony_ci void * data, 51262306a36Sopenharmony_ci int silent) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci sb->s_maxbytes = MAX_LFS_FILESIZE; 51562306a36Sopenharmony_ci sb->s_blocksize = PAGE_SIZE; 51662306a36Sopenharmony_ci sb->s_blocksize_bits = PAGE_SHIFT; 51762306a36Sopenharmony_ci sb->s_magic = DLMFS_MAGIC; 51862306a36Sopenharmony_ci sb->s_op = &dlmfs_ops; 51962306a36Sopenharmony_ci sb->s_root = d_make_root(dlmfs_get_root_inode(sb)); 52062306a36Sopenharmony_ci if (!sb->s_root) 52162306a36Sopenharmony_ci return -ENOMEM; 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic const struct file_operations dlmfs_file_operations = { 52662306a36Sopenharmony_ci .open = dlmfs_file_open, 52762306a36Sopenharmony_ci .release = dlmfs_file_release, 52862306a36Sopenharmony_ci .poll = dlmfs_file_poll, 52962306a36Sopenharmony_ci .read = dlmfs_file_read, 53062306a36Sopenharmony_ci .write = dlmfs_file_write, 53162306a36Sopenharmony_ci .llseek = default_llseek, 53262306a36Sopenharmony_ci}; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic const struct inode_operations dlmfs_dir_inode_operations = { 53562306a36Sopenharmony_ci .create = dlmfs_create, 53662306a36Sopenharmony_ci .lookup = simple_lookup, 53762306a36Sopenharmony_ci .unlink = dlmfs_unlink, 53862306a36Sopenharmony_ci}; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci/* this way we can restrict mkdir to only the toplevel of the fs. */ 54162306a36Sopenharmony_cistatic const struct inode_operations dlmfs_root_inode_operations = { 54262306a36Sopenharmony_ci .lookup = simple_lookup, 54362306a36Sopenharmony_ci .mkdir = dlmfs_mkdir, 54462306a36Sopenharmony_ci .rmdir = simple_rmdir, 54562306a36Sopenharmony_ci}; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic const struct super_operations dlmfs_ops = { 54862306a36Sopenharmony_ci .statfs = simple_statfs, 54962306a36Sopenharmony_ci .alloc_inode = dlmfs_alloc_inode, 55062306a36Sopenharmony_ci .free_inode = dlmfs_free_inode, 55162306a36Sopenharmony_ci .evict_inode = dlmfs_evict_inode, 55262306a36Sopenharmony_ci .drop_inode = generic_delete_inode, 55362306a36Sopenharmony_ci}; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic const struct inode_operations dlmfs_file_inode_operations = { 55662306a36Sopenharmony_ci .getattr = simple_getattr, 55762306a36Sopenharmony_ci .setattr = dlmfs_file_setattr, 55862306a36Sopenharmony_ci}; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic struct dentry *dlmfs_mount(struct file_system_type *fs_type, 56162306a36Sopenharmony_ci int flags, const char *dev_name, void *data) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci return mount_nodev(fs_type, flags, data, dlmfs_fill_super); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic struct file_system_type dlmfs_fs_type = { 56762306a36Sopenharmony_ci .owner = THIS_MODULE, 56862306a36Sopenharmony_ci .name = "ocfs2_dlmfs", 56962306a36Sopenharmony_ci .mount = dlmfs_mount, 57062306a36Sopenharmony_ci .kill_sb = kill_litter_super, 57162306a36Sopenharmony_ci}; 57262306a36Sopenharmony_ciMODULE_ALIAS_FS("ocfs2_dlmfs"); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int __init init_dlmfs_fs(void) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci int status; 57762306a36Sopenharmony_ci int cleanup_inode = 0, cleanup_worker = 0; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci dlmfs_inode_cache = kmem_cache_create("dlmfs_inode_cache", 58062306a36Sopenharmony_ci sizeof(struct dlmfs_inode_private), 58162306a36Sopenharmony_ci 0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| 58262306a36Sopenharmony_ci SLAB_MEM_SPREAD|SLAB_ACCOUNT), 58362306a36Sopenharmony_ci dlmfs_init_once); 58462306a36Sopenharmony_ci if (!dlmfs_inode_cache) { 58562306a36Sopenharmony_ci status = -ENOMEM; 58662306a36Sopenharmony_ci goto bail; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci cleanup_inode = 1; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci user_dlm_worker = alloc_workqueue("user_dlm", WQ_MEM_RECLAIM, 0); 59162306a36Sopenharmony_ci if (!user_dlm_worker) { 59262306a36Sopenharmony_ci status = -ENOMEM; 59362306a36Sopenharmony_ci goto bail; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci cleanup_worker = 1; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci user_dlm_set_locking_protocol(); 59862306a36Sopenharmony_ci status = register_filesystem(&dlmfs_fs_type); 59962306a36Sopenharmony_cibail: 60062306a36Sopenharmony_ci if (status) { 60162306a36Sopenharmony_ci if (cleanup_inode) 60262306a36Sopenharmony_ci kmem_cache_destroy(dlmfs_inode_cache); 60362306a36Sopenharmony_ci if (cleanup_worker) 60462306a36Sopenharmony_ci destroy_workqueue(user_dlm_worker); 60562306a36Sopenharmony_ci } else 60662306a36Sopenharmony_ci printk("OCFS2 User DLM kernel interface loaded\n"); 60762306a36Sopenharmony_ci return status; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic void __exit exit_dlmfs_fs(void) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci unregister_filesystem(&dlmfs_fs_type); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci destroy_workqueue(user_dlm_worker); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* 61762306a36Sopenharmony_ci * Make sure all delayed rcu free inodes are flushed before we 61862306a36Sopenharmony_ci * destroy cache. 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci rcu_barrier(); 62162306a36Sopenharmony_ci kmem_cache_destroy(dlmfs_inode_cache); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ciMODULE_AUTHOR("Oracle"); 62662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 62762306a36Sopenharmony_ciMODULE_DESCRIPTION("OCFS2 DLM-Filesystem"); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cimodule_init(init_dlmfs_fs) 63062306a36Sopenharmony_cimodule_exit(exit_dlmfs_fs) 631