Skip to content

Commit a8058f8

Browse files
committed
Merge tag 'vfs-6.19-rc1.directory.locking' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull directory locking updates from Christian Brauner: "This contains the work to add centralized APIs for directory locking operations. This series is part of a larger effort to change directory operation locking to allow multiple concurrent operations in a directory. The ultimate goal is to lock the target dentry(s) rather than the whole parent directory. To help with changing the locking protocol, this series centralizes locking and lookup in new helper functions. The helpers establish a pattern where it is the dentry that is being locked and unlocked (currently the lock is held on dentry->d_parent->d_inode, but that can change in the future). This also changes vfs_mkdir() to unlock the parent on failure, as well as dput()ing the dentry. This allows end_creating() to only require the target dentry (which may be IS_ERR() after vfs_mkdir()), not the parent" * tag 'vfs-6.19-rc1.directory.locking' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: nfsd: fix end_creating() conversion 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()
2 parents db74a7d + eeec741 commit a8058f8

31 files changed

Lines changed: 1302 additions & 838 deletions

File tree

Documentation/filesystems/porting.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,3 +1309,16 @@ a different length, use
13091309
vfs_parse_fs_qstr(fc, key, &QSTR_LEN(value, len))
13101310

13111311
instead.
1312+
1313+
---
1314+
1315+
**mandatory**
1316+
1317+
vfs_mkdir() now returns a dentry - the one returned by ->mkdir(). If
1318+
that dentry is different from the dentry passed in, including if it is
1319+
an IS_ERR() dentry pointer, the original dentry is dput().
1320+
1321+
When vfs_mkdir() returns an error, and so both dputs() the original
1322+
dentry and doesn't provide a replacement, it also unlocks the parent.
1323+
Consequently the return value from vfs_mkdir() can be passed to
1324+
end_creating() and the parent will be unlocked precisely when necessary.

fs/btrfs/ioctl.c

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -904,14 +904,9 @@ static noinline int btrfs_mksubvol(struct dentry *parent,
904904
struct fscrypt_str name_str = FSTR_INIT((char *)qname->name, qname->len);
905905
int ret;
906906

907-
ret = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
908-
if (ret == -EINTR)
909-
return ret;
910-
911-
dentry = lookup_one(idmap, qname, parent);
912-
ret = PTR_ERR(dentry);
907+
dentry = start_creating_killable(idmap, parent, qname);
913908
if (IS_ERR(dentry))
914-
goto out_unlock;
909+
return PTR_ERR(dentry);
915910

916911
ret = btrfs_may_create(idmap, dir, dentry);
917912
if (ret)
@@ -940,9 +935,7 @@ static noinline int btrfs_mksubvol(struct dentry *parent,
940935
out_up_read:
941936
up_read(&fs_info->subvol_sem);
942937
out_dput:
943-
dput(dentry);
944-
out_unlock:
945-
btrfs_inode_unlock(BTRFS_I(dir), 0);
938+
end_creating(dentry);
946939
return ret;
947940
}
948941

@@ -2417,18 +2410,10 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
24172410
goto free_subvol_name;
24182411
}
24192412

2420-
ret = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
2421-
if (ret == -EINTR)
2422-
goto free_subvol_name;
2423-
dentry = lookup_one(idmap, &QSTR(subvol_name), parent);
2413+
dentry = start_removing_killable(idmap, parent, &QSTR(subvol_name));
24242414
if (IS_ERR(dentry)) {
24252415
ret = PTR_ERR(dentry);
2426-
goto out_unlock_dir;
2427-
}
2428-
2429-
if (d_really_is_negative(dentry)) {
2430-
ret = -ENOENT;
2431-
goto out_dput;
2416+
goto out_end_removing;
24322417
}
24332418

24342419
inode = d_inode(dentry);
@@ -2449,7 +2434,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
24492434
*/
24502435
ret = -EPERM;
24512436
if (!btrfs_test_opt(fs_info, USER_SUBVOL_RM_ALLOWED))
2452-
goto out_dput;
2437+
goto out_end_removing;
24532438

24542439
/*
24552440
* Do not allow deletion if the parent dir is the same
@@ -2460,21 +2445,21 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
24602445
*/
24612446
ret = -EINVAL;
24622447
if (root == dest)
2463-
goto out_dput;
2448+
goto out_end_removing;
24642449

24652450
ret = inode_permission(idmap, inode, MAY_WRITE | MAY_EXEC);
24662451
if (ret)
2467-
goto out_dput;
2452+
goto out_end_removing;
24682453
}
24692454

24702455
/* check if subvolume may be deleted by a user */
24712456
ret = btrfs_may_delete(idmap, dir, dentry, 1);
24722457
if (ret)
2473-
goto out_dput;
2458+
goto out_end_removing;
24742459

24752460
if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) {
24762461
ret = -EINVAL;
2477-
goto out_dput;
2462+
goto out_end_removing;
24782463
}
24792464

24802465
btrfs_inode_lock(BTRFS_I(inode), 0);
@@ -2483,10 +2468,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
24832468
if (!ret)
24842469
d_delete_notify(dir, dentry);
24852470

2486-
out_dput:
2487-
dput(dentry);
2488-
out_unlock_dir:
2489-
btrfs_inode_unlock(BTRFS_I(dir), 0);
2471+
out_end_removing:
2472+
end_removing(dentry);
24902473
free_subvol_name:
24912474
kfree(subvol_name_ptr);
24922475
free_parent:

fs/cachefiles/interface.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/mount.h>
1010
#include <linux/xattr.h>
1111
#include <linux/file.h>
12+
#include <linux/namei.h>
1213
#include <linux/falloc.h>
1314
#include <trace/events/fscache.h>
1415
#include "internal.h"
@@ -428,11 +429,13 @@ static bool cachefiles_invalidate_cookie(struct fscache_cookie *cookie)
428429
if (!old_tmpfile) {
429430
struct cachefiles_volume *volume = object->volume;
430431
struct dentry *fan = volume->fanout[(u8)cookie->key_hash];
432+
struct dentry *obj;
431433

432-
inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
433-
cachefiles_bury_object(volume->cache, object, fan,
434-
old_file->f_path.dentry,
435-
FSCACHE_OBJECT_INVALIDATED);
434+
obj = start_removing_dentry(fan, old_file->f_path.dentry);
435+
if (!IS_ERR(obj))
436+
cachefiles_bury_object(volume->cache, object,
437+
fan, obj,
438+
FSCACHE_OBJECT_INVALIDATED);
436439
}
437440
fput(old_file);
438441
}

fs/cachefiles/namei.c

Lines changed: 46 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,11 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
9393
_enter(",,%s", dirname);
9494

9595
/* search the current directory for the element name */
96-
inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
9796

9897
retry:
9998
ret = cachefiles_inject_read_error();
10099
if (ret == 0)
101-
subdir = lookup_one(&nop_mnt_idmap, &QSTR(dirname), dir);
100+
subdir = start_creating(&nop_mnt_idmap, dir, &QSTR(dirname));
102101
else
103102
subdir = ERR_PTR(ret);
104103
trace_cachefiles_lookup(NULL, dir, subdir);
@@ -129,10 +128,12 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
129128
if (ret < 0)
130129
goto mkdir_error;
131130
ret = cachefiles_inject_write_error();
132-
if (ret == 0)
131+
if (ret == 0) {
133132
subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700, NULL);
134-
else
133+
} else {
134+
end_creating(subdir);
135135
subdir = ERR_PTR(ret);
136+
}
136137
if (IS_ERR(subdir)) {
137138
trace_cachefiles_vfs_error(NULL, d_inode(dir), ret,
138139
cachefiles_trace_mkdir_error);
@@ -141,7 +142,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
141142
trace_cachefiles_mkdir(dir, subdir);
142143

143144
if (unlikely(d_unhashed(subdir) || d_is_negative(subdir))) {
144-
dput(subdir);
145+
end_creating(subdir);
145146
goto retry;
146147
}
147148
ASSERT(d_backing_inode(subdir));
@@ -154,7 +155,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
154155

155156
/* Tell rmdir() it's not allowed to delete the subdir */
156157
inode_lock(d_inode(subdir));
157-
inode_unlock(d_inode(dir));
158+
end_creating_keep(subdir);
158159

159160
if (!__cachefiles_mark_inode_in_use(NULL, d_inode(subdir))) {
160161
pr_notice("cachefiles: Inode already in use: %pd (B=%lx)\n",
@@ -196,14 +197,11 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
196197
return ERR_PTR(-EBUSY);
197198

198199
mkdir_error:
199-
inode_unlock(d_inode(dir));
200-
if (!IS_ERR(subdir))
201-
dput(subdir);
200+
end_creating(subdir);
202201
pr_err("mkdir %s failed with error %d\n", dirname, ret);
203202
return ERR_PTR(ret);
204203

205204
lookup_error:
206-
inode_unlock(d_inode(dir));
207205
ret = PTR_ERR(subdir);
208206
pr_err("Lookup %s failed with error %d\n", dirname, ret);
209207
return ERR_PTR(ret);
@@ -263,6 +261,8 @@ static int cachefiles_unlink(struct cachefiles_cache *cache,
263261
* - File backed objects are unlinked
264262
* - Directory backed objects are stuffed into the graveyard for userspace to
265263
* delete
264+
* On entry dir must be locked. It will be unlocked on exit.
265+
* On entry there must be at least 2 refs on rep, one will be dropped on exit.
266266
*/
267267
int cachefiles_bury_object(struct cachefiles_cache *cache,
268268
struct cachefiles_object *object,
@@ -278,27 +278,23 @@ int cachefiles_bury_object(struct cachefiles_cache *cache,
278278
_enter(",'%pd','%pd'", dir, rep);
279279

280280
if (rep->d_parent != dir) {
281-
inode_unlock(d_inode(dir));
281+
end_removing(rep);
282282
_leave(" = -ESTALE");
283283
return -ESTALE;
284284
}
285285

286286
/* non-directories can just be unlinked */
287287
if (!d_is_dir(rep)) {
288-
dget(rep); /* Stop the dentry being negated if it's only pinned
289-
* by a file struct.
290-
*/
291288
ret = cachefiles_unlink(cache, object, dir, rep, why);
292-
dput(rep);
289+
end_removing(rep);
293290

294-
inode_unlock(d_inode(dir));
295291
_leave(" = %d", ret);
296292
return ret;
297293
}
298294

299295
/* directories have to be moved to the graveyard */
300296
_debug("move stale object to graveyard");
301-
inode_unlock(d_inode(dir));
297+
end_removing(rep);
302298

303299
try_again:
304300
/* first step is to make up a grave dentry in the graveyard */
@@ -425,13 +421,12 @@ int cachefiles_delete_object(struct cachefiles_object *object,
425421

426422
_enter(",OBJ%x{%pD}", object->debug_id, object->file);
427423

428-
/* Stop the dentry being negated if it's only pinned by a file struct. */
429-
dget(dentry);
430-
431-
inode_lock_nested(d_backing_inode(fan), I_MUTEX_PARENT);
432-
ret = cachefiles_unlink(volume->cache, object, fan, dentry, why);
433-
inode_unlock(d_backing_inode(fan));
434-
dput(dentry);
424+
dentry = start_removing_dentry(fan, dentry);
425+
if (IS_ERR(dentry))
426+
ret = PTR_ERR(dentry);
427+
else
428+
ret = cachefiles_unlink(volume->cache, object, fan, dentry, why);
429+
end_removing(dentry);
435430
return ret;
436431
}
437432

@@ -644,9 +639,13 @@ bool cachefiles_look_up_object(struct cachefiles_object *object)
644639

645640
if (!d_is_reg(dentry)) {
646641
pr_err("%pd is not a file\n", dentry);
647-
inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
648-
ret = cachefiles_bury_object(volume->cache, object, fan, dentry,
649-
FSCACHE_OBJECT_IS_WEIRD);
642+
struct dentry *de = start_removing_dentry(fan, dentry);
643+
if (IS_ERR(de))
644+
ret = PTR_ERR(de);
645+
else
646+
ret = cachefiles_bury_object(volume->cache, object,
647+
fan, de,
648+
FSCACHE_OBJECT_IS_WEIRD);
650649
dput(dentry);
651650
if (ret < 0)
652651
return false;
@@ -679,36 +678,41 @@ bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache,
679678

680679
_enter(",%pD", object->file);
681680

682-
inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
683681
ret = cachefiles_inject_read_error();
684682
if (ret == 0)
685-
dentry = lookup_one(&nop_mnt_idmap, &QSTR(object->d_name), fan);
683+
dentry = start_creating(&nop_mnt_idmap, fan, &QSTR(object->d_name));
686684
else
687685
dentry = ERR_PTR(ret);
688686
if (IS_ERR(dentry)) {
689687
trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry),
690688
cachefiles_trace_lookup_error);
691689
_debug("lookup fail %ld", PTR_ERR(dentry));
692-
goto out_unlock;
690+
goto out;
693691
}
694692

695-
if (!d_is_negative(dentry)) {
693+
/*
694+
* This loop will only execute more than once if some other thread
695+
* races to create the object we are trying to create.
696+
*/
697+
while (!d_is_negative(dentry)) {
696698
ret = cachefiles_unlink(volume->cache, object, fan, dentry,
697699
FSCACHE_OBJECT_IS_STALE);
698700
if (ret < 0)
699-
goto out_dput;
701+
goto out_end;
702+
703+
end_creating(dentry);
700704

701-
dput(dentry);
702705
ret = cachefiles_inject_read_error();
703706
if (ret == 0)
704-
dentry = lookup_one(&nop_mnt_idmap, &QSTR(object->d_name), fan);
707+
dentry = start_creating(&nop_mnt_idmap, fan,
708+
&QSTR(object->d_name));
705709
else
706710
dentry = ERR_PTR(ret);
707711
if (IS_ERR(dentry)) {
708712
trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry),
709713
cachefiles_trace_lookup_error);
710714
_debug("lookup fail %ld", PTR_ERR(dentry));
711-
goto out_unlock;
715+
goto out;
712716
}
713717
}
714718

@@ -729,10 +733,9 @@ bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache,
729733
success = true;
730734
}
731735

732-
out_dput:
733-
dput(dentry);
734-
out_unlock:
735-
inode_unlock(d_inode(fan));
736+
out_end:
737+
end_creating(dentry);
738+
out:
736739
_leave(" = %u", success);
737740
return success;
738741
}
@@ -748,26 +751,20 @@ static struct dentry *cachefiles_lookup_for_cull(struct cachefiles_cache *cache,
748751
struct dentry *victim;
749752
int ret = -ENOENT;
750753

751-
inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
754+
victim = start_removing(&nop_mnt_idmap, dir, &QSTR(filename));
752755

753-
victim = lookup_one(&nop_mnt_idmap, &QSTR(filename), dir);
754756
if (IS_ERR(victim))
755757
goto lookup_error;
756-
if (d_is_negative(victim))
757-
goto lookup_put;
758758
if (d_inode(victim)->i_flags & S_KERNEL_FILE)
759759
goto lookup_busy;
760760
return victim;
761761

762762
lookup_busy:
763763
ret = -EBUSY;
764-
lookup_put:
765-
inode_unlock(d_inode(dir));
766-
dput(victim);
764+
end_removing(victim);
767765
return ERR_PTR(ret);
768766

769767
lookup_error:
770-
inode_unlock(d_inode(dir));
771768
ret = PTR_ERR(victim);
772769
if (ret == -ENOENT)
773770
return ERR_PTR(-ESTALE); /* Probably got retired by the netfs */
@@ -815,18 +812,17 @@ int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir,
815812

816813
ret = cachefiles_bury_object(cache, NULL, dir, victim,
817814
FSCACHE_OBJECT_WAS_CULLED);
815+
dput(victim);
818816
if (ret < 0)
819817
goto error;
820818

821819
fscache_count_culled();
822-
dput(victim);
823820
_leave(" = 0");
824821
return 0;
825822

826823
error_unlock:
827-
inode_unlock(d_inode(dir));
824+
end_removing(victim);
828825
error:
829-
dput(victim);
830826
if (ret == -ENOENT)
831827
return -ESTALE; /* Probably got retired by the netfs */
832828

0 commit comments

Comments
 (0)