summaryrefslogtreecommitdiffstats
path: root/fs/9p/vfs_dir.c
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /fs/9p/vfs_dir.c
downloadlinux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz
linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since it can be declared outside of the ftrace.c code, it calls ftrace_ops_init() on the ops in every global function. The issue is that if two tasks call it on the same ops at the same time it is possible to have the initialization of one corrupt the initialization of the other call. Create a ops_mutex to use to synchronize every initialization of the ftrace_ops. The mutex is taken within checking the ftrace_ops flag that states it was initializied but the flag is checked again after the mutex has been taken. Checking first outside the mutex allows it to shortcut having to take the mutex. But then the check needs to be done again after the mute is taken in case of races. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock") Reported-by: sashiko-bot@kernel.org Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/ Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'fs/9p/vfs_dir.c')
-rw-r--r--fs/9p/vfs_dir.c266
1 files changed, 266 insertions, 0 deletions
diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c
new file mode 100644
index 000000000..323f85352
--- /dev/null
+++ b/fs/9p/vfs_dir.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file contains vfs directory ops for the 9P2000 protocol.
+ *
+ * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
+ * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uio.h>
+#include <linux/fscache.h>
+#include <net/9p/9p.h>
+#include <net/9p/client.h>
+
+#include "v9fs.h"
+#include "v9fs_vfs.h"
+#include "fid.h"
+
+/**
+ * struct p9_rdir - readdir accounting
+ * @head: start offset of current dirread buffer
+ * @tail: end offset of current dirread buffer
+ * @offset: file position the data at @head corresponds to
+ * @buf: dirread buffer
+ *
+ * private structure for keeping track of readdir
+ * allocated on demand
+ */
+
+struct p9_rdir {
+ int head;
+ int tail;
+ loff_t offset;
+ uint8_t buf[];
+};
+
+/**
+ * dt_type - return file type
+ * @mistat: mistat structure
+ *
+ */
+
+static inline int dt_type(struct p9_wstat *mistat)
+{
+ unsigned long perm = mistat->mode;
+ int rettype = DT_REG;
+
+ if (perm & P9_DMDIR)
+ rettype = DT_DIR;
+ if (perm & P9_DMSYMLINK)
+ rettype = DT_LNK;
+
+ return rettype;
+}
+
+/**
+ * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
+ * @filp: opened file structure
+ * @buflen: Length in bytes of buffer to allocate
+ *
+ */
+
+static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
+{
+ struct p9_fid *fid = filp->private_data;
+
+ if (!fid->rdir)
+ fid->rdir = kvzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
+ return fid->rdir;
+}
+
+/**
+ * v9fs_dir_readdir - iterate through a directory
+ * @file: opened file structure
+ * @ctx: actor we feed the entries to
+ *
+ */
+
+static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
+{
+ bool over;
+ struct p9_wstat st;
+ int err = 0;
+ struct p9_fid *fid;
+ int buflen;
+ struct p9_rdir *rdir;
+ struct kvec kvec;
+
+ p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
+ fid = file->private_data;
+
+ buflen = fid->clnt->msize - P9_IOHDRSZ;
+
+ rdir = v9fs_alloc_rdir_buf(file, buflen);
+ if (!rdir)
+ return -ENOMEM;
+ kvec.iov_base = rdir->buf;
+ kvec.iov_len = buflen;
+
+ if (rdir->head < rdir->tail && rdir->offset != ctx->pos)
+ rdir->head = rdir->tail = 0;
+
+ while (1) {
+ if (rdir->tail == rdir->head) {
+ struct iov_iter to;
+ int n;
+
+ iov_iter_kvec(&to, ITER_DEST, &kvec, 1, buflen);
+ n = p9_client_read(file->private_data, ctx->pos, &to,
+ &err);
+ if (err)
+ return err;
+ if (n == 0)
+ return 0;
+
+ rdir->head = 0;
+ rdir->tail = n;
+ rdir->offset = ctx->pos;
+ }
+ while (rdir->head < rdir->tail) {
+ err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
+ rdir->tail - rdir->head, &st);
+ if (err <= 0) {
+ p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
+ return -EIO;
+ }
+
+ over = !dir_emit(ctx, st.name, strlen(st.name),
+ QID2INO(&st.qid), dt_type(&st));
+ p9stat_free(&st);
+ if (over)
+ return 0;
+
+ rdir->head += err;
+ ctx->pos += err;
+ rdir->offset = ctx->pos;
+ }
+ }
+}
+
+/**
+ * v9fs_dir_readdir_dotl - iterate through a directory
+ * @file: opened file structure
+ * @ctx: actor we feed the entries to
+ *
+ */
+static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
+{
+ int err = 0;
+ struct p9_fid *fid;
+ int buflen;
+ struct p9_rdir *rdir;
+ struct p9_dirent curdirent;
+
+ p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
+ fid = file->private_data;
+
+ buflen = fid->clnt->msize - P9_READDIRHDRSZ;
+
+ rdir = v9fs_alloc_rdir_buf(file, buflen);
+ if (!rdir)
+ return -ENOMEM;
+
+ if (rdir->head < rdir->tail && rdir->offset != ctx->pos)
+ rdir->head = rdir->tail = 0;
+
+ while (1) {
+ if (rdir->tail == rdir->head) {
+ err = p9_client_readdir(fid, rdir->buf, buflen,
+ ctx->pos);
+ if (err <= 0)
+ return err;
+
+ rdir->head = 0;
+ rdir->tail = err;
+ rdir->offset = ctx->pos;
+ }
+
+ while (rdir->head < rdir->tail) {
+
+ err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
+ rdir->tail - rdir->head,
+ &curdirent);
+ if (err < 0) {
+ p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
+ return -EIO;
+ }
+
+ if (!dir_emit(ctx, curdirent.d_name,
+ strlen(curdirent.d_name),
+ QID2INO(&curdirent.qid),
+ curdirent.d_type))
+ return 0;
+
+ ctx->pos = curdirent.d_off;
+ rdir->head += err;
+ rdir->offset = ctx->pos;
+ }
+ }
+}
+
+
+/**
+ * v9fs_dir_release - close a directory or a file
+ * @inode: inode of the directory or file
+ * @filp: file pointer to a directory or file
+ *
+ */
+
+int v9fs_dir_release(struct inode *inode, struct file *filp)
+{
+ struct v9fs_inode *v9inode = V9FS_I(inode);
+ struct p9_fid *fid;
+ __le32 version;
+ loff_t i_size;
+ int retval = 0, put_err;
+
+ fid = filp->private_data;
+ p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
+ inode, filp, fid ? fid->fid : -1);
+
+ if (fid) {
+ if ((S_ISREG(inode->i_mode)) && (filp->f_mode & FMODE_WRITE))
+ retval = filemap_fdatawrite(inode->i_mapping);
+
+ spin_lock(&inode->i_lock);
+ hlist_del(&fid->ilist);
+ spin_unlock(&inode->i_lock);
+ put_err = p9_fid_put(fid);
+ retval = retval < 0 ? retval : put_err;
+ }
+
+ if ((filp->f_mode & FMODE_WRITE)) {
+ version = cpu_to_le32(v9inode->qid.version);
+ i_size = i_size_read(inode);
+ fscache_unuse_cookie(v9fs_inode_cookie(v9inode),
+ &version, &i_size);
+ } else {
+ fscache_unuse_cookie(v9fs_inode_cookie(v9inode), NULL, NULL);
+ }
+ return retval;
+}
+
+const struct file_operations v9fs_dir_operations = {
+ .read = generic_read_dir,
+ .llseek = generic_file_llseek,
+ .iterate_shared = v9fs_dir_readdir,
+ .open = v9fs_file_open,
+ .release = v9fs_dir_release,
+};
+
+const struct file_operations v9fs_dir_operations_dotl = {
+ .read = generic_read_dir,
+ .llseek = generic_file_llseek,
+ .iterate_shared = v9fs_dir_readdir_dotl,
+ .open = v9fs_file_open,
+ .release = v9fs_dir_release,
+ .fsync = v9fs_file_fsync_dotl,
+};