// SPDX-License-Identifier: GPL-2.0-or-later /* * dlmfs.c * * Code which implements the kernel side of a minimal userspace * interface to our DLM. This file handles the virtual file system * used for communication with userspace. Credit should go to ramfs, * which was a template for the fs side of this module. * * Copyright (C) 2003, 2004 Oracle. All rights reserved.
*/
/* Simple VFS hooks based on: */ /* * Resizable simple ram filesystem for Linux. * * Copyright (C) 2000 Linus Torvalds. * 2000 Transmeta Corp.
*/
/* * These are the ABI capabilities of dlmfs. * * Over time, dlmfs has added some features that were not part of the * initial ABI. Unfortunately, some of these features are not detectable * via standard usage. For example, Linux's default poll always returns * EPOLLIN, so there is no way for a caller of poll(2) to know when dlmfs * added poll support. Instead, we provide this list of new capabilities. * * Capabilities is a read-only attribute. We do it as a module parameter * so we can discover it whether dlmfs is built in, loaded, or even not * loaded. * * The ABI features are local to this machine's dlmfs mount. This is * distinct from the locking protocol, which is concerned with inter-node * interaction. * * Capabilities: * - bast : EPOLLIN against the file descriptor of a held lock * signifies a bast fired on the lock.
*/ #define DLMFS_CAPABILITIES "bast stackglue" staticint param_set_dlmfs_capabilities(constchar *val, conststruct kernel_param *kp)
{
printk(KERN_ERR "%s: readonly parameter\n", kp->name); return -EINVAL;
} staticint param_get_dlmfs_capabilities(char *buffer, conststruct kernel_param *kp)
{ return sysfs_emit(buffer, DLMFS_CAPABILITIES);
}
module_param_call(capabilities, param_set_dlmfs_capabilities,
param_get_dlmfs_capabilities, NULL, 0444);
MODULE_PARM_DESC(capabilities, DLMFS_CAPABILITIES);
/* * decodes a set of open flags into a valid lock level and a set of flags. * returns < 0 if we have invalid flags * flags which mean something to us: * O_RDONLY -> PRMODE level * O_WRONLY -> EXMODE level * * O_NONBLOCK -> NOQUEUE
*/ staticint dlmfs_decode_open_flags(int open_flags, int *level, int *flags)
{ if (open_flags & (O_WRONLY|O_RDWR))
*level = DLM_LOCK_EX; else
*level = DLM_LOCK_PR;
*flags = 0; if (open_flags & O_NONBLOCK)
*flags |= DLM_LKF_NOQUEUE;
mlog(0, "open called on inode %lu, flags 0x%x\n", inode->i_ino,
file->f_flags);
status = dlmfs_decode_open_flags(file->f_flags, &level, &flags); if (status < 0) goto bail;
/* We don't want to honor O_APPEND at read/write time as it
* doesn't make sense for LVB writes. */
file->f_flags &= ~O_APPEND;
fp = kmalloc(sizeof(*fp), GFP_NOFS); if (!fp) {
status = -ENOMEM; goto bail;
}
fp->fp_lock_level = level;
ip = DLMFS_I(inode);
status = user_dlm_cluster_lock(&ip->ip_lockres, level, flags); if (status < 0) { /* this is a strange error to return here but I want * to be able userspace to be able to distinguish a * valid lock request from one that simply couldn't be
* granted. */ if (flags & DLM_LKF_NOQUEUE && status == -EAGAIN)
status = -ETXTBSY;
kfree(fp); goto bail;
}
mlog(0, "close called on inode %lu\n", inode->i_ino);
if (fp) {
level = fp->fp_lock_level; if (level != DLM_LOCK_IV)
user_dlm_cluster_unlock(&ip->ip_lockres, level);
kfree(fp);
file->private_data = NULL;
}
return 0;
}
/* * We do ->setattr() just to override size changes. Our size is the size * of the LVB and nothing else.
*/ staticint dlmfs_file_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr)
{ int error; struct inode *inode = d_inode(dentry);
staticvoid dlmfs_evict_inode(struct inode *inode)
{ int status; struct dlmfs_inode_private *ip; struct user_lock_res *lockres; int teardown;
clear_inode(inode);
mlog(0, "inode %lu\n", inode->i_ino);
ip = DLMFS_I(inode);
lockres = &ip->ip_lockres;
if (S_ISREG(inode->i_mode)) {
spin_lock(&lockres->l_lock);
teardown = !!(lockres->l_flags & USER_LOCK_IN_TEARDOWN);
spin_unlock(&lockres->l_lock); if (!teardown) {
status = user_dlm_destroy_lock(lockres); if (status < 0)
mlog_errno(status);
}
iput(ip->ip_parent); goto clear_fields;
}
mlog(0, "we're a directory, ip->ip_conn = 0x%p\n", ip->ip_conn); /* we must be a directory. If required, lets unregister the
* dlm context now. */ if (ip->ip_conn)
user_dlm_unregister(ip->ip_conn);
clear_fields:
ip->ip_parent = NULL;
ip->ip_conn = NULL;
}
ip = DLMFS_I(inode);
ip->ip_conn = DLMFS_I(parent)->ip_conn;
switch (mode & S_IFMT) { default: /* for now we don't support anything other than
* directories and regular files. */
BUG(); break; case S_IFREG:
inode->i_op = &dlmfs_file_inode_operations;
inode->i_fop = &dlmfs_file_operations;
i_size_write(inode, DLM_LVB_LEN);
user_dlm_lock_res_init(&ip->ip_lockres, dentry);
/* released at clear_inode time, this insures that we * get to drop the dlm reference on each lock *before* * we call the unregister code for releasing parent
* directories. */
ip->ip_parent = igrab(parent);
BUG_ON(!ip->ip_parent); break; case S_IFDIR:
inode->i_op = &dlmfs_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
/* directory inodes start off with i_nlink ==
* 2 (for "." entry) */
inc_nlink(inode); break;
} return inode;
}
/* verify that we have a proper domain */ if (domain->len >= GROUP_NAME_MAX) {
status = -EINVAL;
mlog(ML_ERROR, "invalid domain name for directory.\n"); goto bail;
}
inode = dlmfs_get_inode(dir, dentry, mode | S_IFDIR); if (!inode) {
status = -ENOMEM;
mlog_errno(status); goto bail;
}
ip = DLMFS_I(inode);
conn = user_dlm_register(domain); if (IS_ERR(conn)) {
status = PTR_ERR(conn);
mlog(ML_ERROR, "Error %d could not register domain \"%.*s\"\n",
status, domain->len, domain->name); goto bail;
}
ip->ip_conn = conn;
inc_nlink(dir);
d_instantiate(dentry, inode);
dget(dentry); /* Extra count - pin the dentry in core */
status = 0;
bail: if (status < 0)
iput(inode); return ERR_PTR(status);
}
/* if there are no current holders, or none that are waiting
* to acquire a lock, this basically destroys our lockres. */
status = user_dlm_destroy_lock(&DLMFS_I(inode)->ip_lockres); if (status < 0) {
mlog(ML_ERROR, "unlink %pd, error %d from destroy\n",
dentry, status); goto bail;
}
status = simple_unlink(dir, dentry);
bail: return status;
}
/* this way we can restrict mkdir to only the toplevel of the fs. */ staticconststruct inode_operations dlmfs_root_inode_operations = {
.lookup = simple_lookup,
.mkdir = dlmfs_mkdir,
.rmdir = simple_rmdir,
};
user_dlm_worker = alloc_workqueue("user_dlm", WQ_MEM_RECLAIM, 0); if (!user_dlm_worker) {
status = -ENOMEM; goto bail;
}
cleanup_worker = 1;
user_dlm_set_locking_protocol();
status = register_filesystem(&dlmfs_fs_type);
bail: if (status) { if (cleanup_inode)
kmem_cache_destroy(dlmfs_inode_cache); if (cleanup_worker)
destroy_workqueue(user_dlm_worker);
} else
printk("OCFS2 User DLM kernel interface loaded\n"); return status;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.