summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2025-11-14 13:16:04 +0100
committerChristian Brauner <brauner@kernel.org>2025-11-14 13:16:04 +0100
commit523ac768800c95d34522e8e33775bf911b883fe2 (patch)
treef95c11fca54fe2b9184e92ebbd1abaaf2275801c /include/linux
parent3a8660878839faadb4f1a6dd72c3179c1df56787 (diff)
parentcf296b294c3bd8f7db229060efe677dfd49e46b6 (diff)
Merge patch series "Create and use APIs to centralise locking for directory ops."
NeilBrown <neilb@ownmail.net> says: This series is the next part of my effort to change directory-op locking to allow multiple concurrent ops in a directory. Ultimately we will (in my plan) lock the target dentry(s) rather than the whole parent directory. To help with changing the locking protocol, this series centralises locking and lookup in some helpers. The various helpers are introduced and then used in the same patch - roughly one patch per helper though with various exceptions. I haven't introduced these helpers into the various filesystems that Al's tree-in-dcache series is changing. That series introduces and uses similar helpers tuned to the specific needs of that set of filesystems. Ultimately all the helpers will use the same backends which can then be adjusted when it is time to change the locking protocol. One change that deserves highlighting is in patch 13 where vfs_mkdir() is changed to unlock the parent on failure, as well as the current behaviour of dput()ing the dentry on failure. Once this change is in place, the final step of both create and an remove sequences only requires the target dentry, not the parent. So e.g. end_creating() is only given the dentry (which may be IS_ERR() after vfs_mkdir()). This helps establish the pattern that it is the dentry that is being locked and unlocked (the lock is currently held on dentry->d_parent->d_inode, but that can change). * patches from https://patch.msgid.link/20251113002050.676694-1-neilb@ownmail.net: VFS: introduce end_creating_keep() VFS: change vfs_mkdir() to unlock on failure. ecryptfs: use new start_creating/start_removing APIs Add start_renaming_two_dentries() VFS/ovl/smb: introduce start_renaming_dentry() VFS/nfsd/ovl: introduce start_renaming() and end_renaming() VFS: add start_creating_killable() and start_removing_killable() VFS: introduce start_removing_dentry() smb/server: use end_removing_noperm for for target of smb2_create_link() VFS: introduce start_creating_noperm() and start_removing_noperm() VFS/nfsd/cachefiles/ovl: introduce start_removing() and end_removing() VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating() VFS: tidy up do_unlinkat() VFS: introduce start_dirop() and end_dirop() debugfs: rename end_creating() to debugfs_end_creating() Link: https://patch.msgid.link/20251113002050.676694-1-neilb@ownmail.net Signed-off-by: Christian Brauner <brauner@kernel.org>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/fs.h2
-rw-r--r--include/linux/namei.h82
2 files changed, 84 insertions, 0 deletions
diff --git a/include/linux/fs.h b/include/linux/fs.h
index c895146c1444..f4543612ef1e 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3609,6 +3609,8 @@ extern void iterate_supers_type(struct file_system_type *,
void filesystems_freeze(void);
void filesystems_thaw(void);
+void end_dirop(struct dentry *de);
+
extern int dcache_dir_open(struct inode *, struct file *);
extern int dcache_dir_close(struct inode *, struct file *);
extern loff_t dcache_dir_lseek(struct file *, loff_t, int);
diff --git a/include/linux/namei.h b/include/linux/namei.h
index fed86221c69c..3d82c6a19197 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -88,6 +88,81 @@ struct dentry *lookup_one_positive_killable(struct mnt_idmap *idmap,
struct qstr *name,
struct dentry *base);
+struct dentry *start_creating(struct mnt_idmap *idmap, struct dentry *parent,
+ struct qstr *name);
+struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *parent,
+ struct qstr *name);
+struct dentry *start_creating_killable(struct mnt_idmap *idmap,
+ struct dentry *parent,
+ struct qstr *name);
+struct dentry *start_removing_killable(struct mnt_idmap *idmap,
+ struct dentry *parent,
+ struct qstr *name);
+struct dentry *start_creating_noperm(struct dentry *parent, struct qstr *name);
+struct dentry *start_removing_noperm(struct dentry *parent, struct qstr *name);
+struct dentry *start_creating_dentry(struct dentry *parent,
+ struct dentry *child);
+struct dentry *start_removing_dentry(struct dentry *parent,
+ struct dentry *child);
+
+/* end_creating - finish action started with start_creating
+ * @child: dentry returned by start_creating() or vfs_mkdir()
+ *
+ * Unlock and release the child. This can be called after
+ * start_creating() whether that function succeeded or not,
+ * but it is not needed on failure.
+ *
+ * If vfs_mkdir() was called then the value returned from that function
+ * should be given for @child rather than the original dentry, as vfs_mkdir()
+ * may have provided a new dentry.
+ *
+ *
+ * If vfs_mkdir() was not called, then @child will be a valid dentry and
+ * @parent will be ignored.
+ */
+static inline void end_creating(struct dentry *child)
+{
+ end_dirop(child);
+}
+
+/* end_creating_keep - finish action started with start_creating() and return result
+ * @child: dentry returned by start_creating() or vfs_mkdir()
+ *
+ * Unlock and return the child. This can be called after
+ * start_creating() whether that function succeeded or not,
+ * but it is not needed on failure.
+ *
+ * If vfs_mkdir() was called then the value returned from that function
+ * should be given for @child rather than the original dentry, as vfs_mkdir()
+ * may have provided a new dentry.
+ *
+ * Returns: @child, which may be a dentry or an error.
+ *
+ */
+static inline struct dentry *end_creating_keep(struct dentry *child)
+{
+ if (!IS_ERR(child))
+ dget(child);
+ end_dirop(child);
+ return child;
+}
+
+/**
+ * end_removing - finish action started with start_removing
+ * @child: dentry returned by start_removing()
+ * @parent: dentry given to start_removing()
+ *
+ * Unlock and release the child.
+ *
+ * This is identical to end_dirop(). It can be passed the result of
+ * start_removing() whether that was successful or not, but it not needed
+ * if start_removing() failed.
+ */
+static inline void end_removing(struct dentry *child)
+{
+ end_dirop(child);
+}
+
extern int follow_down_one(struct path *);
extern int follow_down(struct path *path, unsigned int flags);
extern int follow_up(struct path *);
@@ -95,6 +170,13 @@ extern int follow_up(struct path *);
extern struct dentry *lock_rename(struct dentry *, struct dentry *);
extern struct dentry *lock_rename_child(struct dentry *, struct dentry *);
extern void unlock_rename(struct dentry *, struct dentry *);
+int start_renaming(struct renamedata *rd, int lookup_flags,
+ struct qstr *old_last, struct qstr *new_last);
+int start_renaming_dentry(struct renamedata *rd, int lookup_flags,
+ struct dentry *old_dentry, struct qstr *new_last);
+int start_renaming_two_dentries(struct renamedata *rd,
+ struct dentry *old_dentry, struct dentry *new_dentry);
+void end_renaming(struct renamedata *rd);
/**
* mode_strip_umask - handle vfs umask stripping