Skip to content

Commit db74a7d

Browse files
committed
Merge tag 'vfs-6.19-rc1.directory.delegations' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull directory delegations update from Christian Brauner: "This contains the work for recall-only directory delegations for knfsd. Add support for simple, recallable-only directory delegations. This was decided at the fall NFS Bakeathon where the NFS client and server maintainers discussed how to merge directory delegation support. The approach starts with recallable-only delegations for several reasons: 1. RFC8881 has gaps that are being addressed in RFC8881bis. In particular, it requires directory position information for CB_NOTIFY callbacks, which is difficult to implement properly under Linux. The spec is being extended to allow that information to be omitted. 2. Client-side support for CB_NOTIFY still lags. The client side involves heuristics about when to request a delegation. 3. Early indication shows simple, recallable-only delegations can help performance. Anna Schumaker mentioned seeing a multi-minute speedup in xfstests runs with them enabled. With these changes, userspace can also request a read lease on a directory that will be recalled on conflicting accesses. This may be useful for applications like Samba. Users can disable leases altogether via the fs.leases-enable sysctl if needed. VFS changes: - Dedicated Type for Delegations Introduce struct delegated_inode to track inodes that may have delegations that need to be broken. This replaces the previous approach of passing raw inode pointers through the delegation breaking code paths, providing better type safety and clearer semantics for the delegation machinery. - Break parent directory delegations in open(..., O_CREAT) codepath - Allow mkdir to wait for delegation break on parent - Allow rmdir to wait for delegation break on parent - Add try_break_deleg calls for parents to vfs_link(), vfs_rename(), and vfs_unlink() - Make vfs_create(), vfs_mknod(), and vfs_symlink() break delegations on parent directory - Clean up argument list for vfs_create() - Expose delegation support to userland Filelock changes: - Make lease_alloc() take a flags argument - Rework the __break_lease API to use flags - Add struct delegated_inode - Push the S_ISREG check down to ->setlease handlers - Lift the ban on directory leases in generic_setlease NFSD changes: - Allow filecache to hold S_IFDIR files - Allow DELEGRETURN on directories - Wire up GET_DIR_DELEGATION handling Fixes: - Fix kernel-doc warnings in __fcntl_getlease - Add needed headers for new struct delegation definition" * tag 'vfs-6.19-rc1.directory.delegations' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: vfs: add needed headers for new struct delegation definition filelock: __fcntl_getlease: fix kernel-doc warnings vfs: expose delegation support to userland nfsd: wire up GET_DIR_DELEGATION handling nfsd: allow DELEGRETURN on directories nfsd: allow filecache to hold S_IFDIR files filelock: lift the ban on directory leases in generic_setlease vfs: make vfs_symlink break delegations on parent dir vfs: make vfs_mknod break delegations on parent directory vfs: make vfs_create break delegations on parent directory vfs: clean up argument list for vfs_create() vfs: break parent dir delegations in open(..., O_CREAT) codepath vfs: allow rmdir to wait for delegation break on parent vfs: allow mkdir to wait for delegation break on parent vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink} filelock: push the S_ISREG check down to ->setlease handlers filelock: add struct delegated_inode filelock: rework the __break_lease API to use flags filelock: make lease_alloc() take a flags argument
2 parents 4664fb4 + 4be9e04 commit db74a7d

32 files changed

Lines changed: 550 additions & 177 deletions

File tree

drivers/base/devtmpfs.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ static int dev_mkdir(const char *name, umode_t mode)
180180
if (IS_ERR(dentry))
181181
return PTR_ERR(dentry);
182182

183-
dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode);
183+
dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode, NULL);
184184
if (!IS_ERR(dentry))
185185
/* mark as kernel-created inode */
186186
d_inode(dentry)->i_private = &thread;
@@ -231,7 +231,7 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
231231
return PTR_ERR(dentry);
232232

233233
err = vfs_mknod(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode,
234-
dev->devt);
234+
dev->devt, NULL);
235235
if (!err) {
236236
struct iattr newattrs;
237237

@@ -261,7 +261,7 @@ static int dev_rmdir(const char *name)
261261
return PTR_ERR(dentry);
262262
if (d_inode(dentry)->i_private == &thread)
263263
err = vfs_rmdir(&nop_mnt_idmap, d_inode(parent.dentry),
264-
dentry);
264+
dentry, NULL);
265265
else
266266
err = -EPERM;
267267

fs/attr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ EXPORT_SYMBOL(may_setattr);
415415
* performed on the raw inode simply pass @nop_mnt_idmap.
416416
*/
417417
int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
418-
struct iattr *attr, struct inode **delegated_inode)
418+
struct iattr *attr, struct delegated_inode *delegated_inode)
419419
{
420420
struct inode *inode = dentry->d_inode;
421421
umode_t mode = inode->i_mode;

fs/cachefiles/namei.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
130130
goto mkdir_error;
131131
ret = cachefiles_inject_write_error();
132132
if (ret == 0)
133-
subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700);
133+
subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700, NULL);
134134
else
135135
subdir = ERR_PTR(ret);
136136
if (IS_ERR(subdir)) {

fs/ecryptfs/inode.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,7 @@ ecryptfs_do_create(struct inode *directory_inode,
188188

189189
rc = lock_parent(ecryptfs_dentry, &lower_dentry, &lower_dir);
190190
if (!rc)
191-
rc = vfs_create(&nop_mnt_idmap, lower_dir,
192-
lower_dentry, mode, true);
191+
rc = vfs_create(&nop_mnt_idmap, lower_dentry, mode, NULL);
193192
if (rc) {
194193
printk(KERN_ERR "%s: Failure to create dentry in lower fs; "
195194
"rc = [%d]\n", __func__, rc);
@@ -480,7 +479,7 @@ static int ecryptfs_symlink(struct mnt_idmap *idmap,
480479
if (rc)
481480
goto out_lock;
482481
rc = vfs_symlink(&nop_mnt_idmap, lower_dir, lower_dentry,
483-
encoded_symname);
482+
encoded_symname, NULL);
484483
kfree(encoded_symname);
485484
if (rc || d_really_is_negative(lower_dentry))
486485
goto out_lock;
@@ -508,7 +507,7 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
508507
goto out;
509508

510509
lower_dentry = vfs_mkdir(&nop_mnt_idmap, lower_dir,
511-
lower_dentry, mode);
510+
lower_dentry, mode, NULL);
512511
rc = PTR_ERR(lower_dentry);
513512
if (IS_ERR(lower_dentry))
514513
goto out;
@@ -540,7 +539,7 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
540539
if (d_unhashed(lower_dentry))
541540
rc = -EINVAL;
542541
else
543-
rc = vfs_rmdir(&nop_mnt_idmap, lower_dir, lower_dentry);
542+
rc = vfs_rmdir(&nop_mnt_idmap, lower_dir, lower_dentry, NULL);
544543
}
545544
if (!rc) {
546545
clear_nlink(d_inode(dentry));
@@ -565,7 +564,7 @@ ecryptfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
565564
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
566565
if (!rc)
567566
rc = vfs_mknod(&nop_mnt_idmap, lower_dir,
568-
lower_dentry, mode, dev);
567+
lower_dentry, mode, dev, NULL);
569568
if (rc || d_really_is_negative(lower_dentry))
570569
goto out;
571570
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb);

fs/fcntl.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
445445
struct file *filp)
446446
{
447447
void __user *argp = (void __user *)arg;
448+
struct delegation deleg;
448449
int argi = (int)arg;
449450
struct flock flock;
450451
long err = -EINVAL;
@@ -550,6 +551,18 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
550551
case F_SET_RW_HINT:
551552
err = fcntl_set_rw_hint(filp, arg);
552553
break;
554+
case F_GETDELEG:
555+
if (copy_from_user(&deleg, argp, sizeof(deleg)))
556+
return -EFAULT;
557+
err = fcntl_getdeleg(filp, &deleg);
558+
if (!err && copy_to_user(argp, &deleg, sizeof(deleg)))
559+
return -EFAULT;
560+
break;
561+
case F_SETDELEG:
562+
if (copy_from_user(&deleg, argp, sizeof(deleg)))
563+
return -EFAULT;
564+
err = fcntl_setdeleg(fd, filp, &deleg);
565+
break;
553566
default:
554567
break;
555568
}

fs/fuse/dir.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,6 +2230,7 @@ static const struct file_operations fuse_dir_operations = {
22302230
.fsync = fuse_dir_fsync,
22312231
.unlocked_ioctl = fuse_dir_ioctl,
22322232
.compat_ioctl = fuse_dir_compat_ioctl,
2233+
.setlease = simple_nosetlease,
22332234
};
22342235

22352236
static const struct inode_operations fuse_common_inode_operations = {

fs/init.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ int __init init_mknod(const char *filename, umode_t mode, unsigned int dev)
157157
error = security_path_mknod(&path, dentry, mode, dev);
158158
if (!error)
159159
error = vfs_mknod(mnt_idmap(path.mnt), path.dentry->d_inode,
160-
dentry, mode, new_decode_dev(dev));
160+
dentry, mode, new_decode_dev(dev), NULL);
161161
end_creating_path(&path, dentry);
162162
return error;
163163
}
@@ -209,7 +209,7 @@ int __init init_symlink(const char *oldname, const char *newname)
209209
error = security_path_symlink(&path, dentry, oldname);
210210
if (!error)
211211
error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode,
212-
dentry, oldname);
212+
dentry, oldname, NULL);
213213
end_creating_path(&path, dentry);
214214
return error;
215215
}
@@ -233,7 +233,7 @@ int __init init_mkdir(const char *pathname, umode_t mode)
233233
error = security_path_mkdir(&path, dentry, mode);
234234
if (!error) {
235235
dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
236-
dentry, mode);
236+
dentry, mode, NULL);
237237
if (IS_ERR(dentry))
238238
error = PTR_ERR(dentry);
239239
}

fs/locks.c

Lines changed: 77 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ static const struct lease_manager_operations lease_manager_ops = {
585585
/*
586586
* Initialize a lease, use the default lock manager operations
587587
*/
588-
static int lease_init(struct file *filp, int type, struct file_lease *fl)
588+
static int lease_init(struct file *filp, unsigned int flags, int type, struct file_lease *fl)
589589
{
590590
if (assign_type(&fl->c, type) != 0)
591591
return -EINVAL;
@@ -594,21 +594,21 @@ static int lease_init(struct file *filp, int type, struct file_lease *fl)
594594
fl->c.flc_pid = current->tgid;
595595

596596
fl->c.flc_file = filp;
597-
fl->c.flc_flags = FL_LEASE;
597+
fl->c.flc_flags = flags;
598598
fl->fl_lmops = &lease_manager_ops;
599599
return 0;
600600
}
601601

602602
/* Allocate a file_lock initialised to this type of lease */
603-
static struct file_lease *lease_alloc(struct file *filp, int type)
603+
static struct file_lease *lease_alloc(struct file *filp, unsigned int flags, int type)
604604
{
605605
struct file_lease *fl = locks_alloc_lease();
606606
int error = -ENOMEM;
607607

608608
if (fl == NULL)
609609
return ERR_PTR(error);
610610

611-
error = lease_init(filp, type, fl);
611+
error = lease_init(filp, flags, type, fl);
612612
if (error) {
613613
locks_free_lease(fl);
614614
return ERR_PTR(error);
@@ -1529,29 +1529,35 @@ any_leases_conflict(struct inode *inode, struct file_lease *breaker)
15291529
/**
15301530
* __break_lease - revoke all outstanding leases on file
15311531
* @inode: the inode of the file to return
1532-
* @mode: O_RDONLY: break only write leases; O_WRONLY or O_RDWR:
1533-
* break all leases
1534-
* @type: FL_LEASE: break leases and delegations; FL_DELEG: break
1535-
* only delegations
1532+
* @flags: LEASE_BREAK_* flags
15361533
*
15371534
* break_lease (inlined for speed) has checked there already is at least
15381535
* some kind of lock (maybe a lease) on this file. Leases are broken on
1539-
* a call to open() or truncate(). This function can sleep unless you
1540-
* specified %O_NONBLOCK to your open().
1536+
* a call to open() or truncate(). This function can block waiting for the
1537+
* lease break unless you specify LEASE_BREAK_NONBLOCK.
15411538
*/
1542-
int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
1539+
int __break_lease(struct inode *inode, unsigned int flags)
15431540
{
1544-
int error = 0;
1545-
struct file_lock_context *ctx;
15461541
struct file_lease *new_fl, *fl, *tmp;
1542+
struct file_lock_context *ctx;
15471543
unsigned long break_time;
1548-
int want_write = (mode & O_ACCMODE) != O_RDONLY;
1544+
unsigned int type;
15491545
LIST_HEAD(dispose);
1546+
bool want_write = !(flags & LEASE_BREAK_OPEN_RDONLY);
1547+
int error = 0;
15501548

1551-
new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK);
1549+
if (flags & LEASE_BREAK_LEASE)
1550+
type = FL_LEASE;
1551+
else if (flags & LEASE_BREAK_DELEG)
1552+
type = FL_DELEG;
1553+
else if (flags & LEASE_BREAK_LAYOUT)
1554+
type = FL_LAYOUT;
1555+
else
1556+
return -EINVAL;
1557+
1558+
new_fl = lease_alloc(NULL, type, want_write ? F_WRLCK : F_RDLCK);
15521559
if (IS_ERR(new_fl))
15531560
return PTR_ERR(new_fl);
1554-
new_fl->c.flc_flags = type;
15551561

15561562
/* typically we will check that ctx is non-NULL before calling */
15571563
ctx = locks_inode_context(inode);
@@ -1596,7 +1602,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
15961602
if (list_empty(&ctx->flc_lease))
15971603
goto out;
15981604

1599-
if (mode & O_NONBLOCK) {
1605+
if (flags & LEASE_BREAK_NONBLOCK) {
16001606
trace_break_lease_noblock(inode, new_fl);
16011607
error = -EWOULDBLOCK;
16021608
goto out;
@@ -1675,8 +1681,9 @@ void lease_get_mtime(struct inode *inode, struct timespec64 *time)
16751681
EXPORT_SYMBOL(lease_get_mtime);
16761682

16771683
/**
1678-
* fcntl_getlease - Enquire what lease is currently active
1684+
* __fcntl_getlease - Enquire what lease is currently active
16791685
* @filp: the file
1686+
* @flavor: type of lease flags to check
16801687
*
16811688
* The value returned by this function will be one of
16821689
* (if no lease break is pending):
@@ -1697,7 +1704,7 @@ EXPORT_SYMBOL(lease_get_mtime);
16971704
* XXX: sfr & willy disagree over whether F_INPROGRESS
16981705
* should be returned to userspace.
16991706
*/
1700-
int fcntl_getlease(struct file *filp)
1707+
static int __fcntl_getlease(struct file *filp, unsigned int flavor)
17011708
{
17021709
struct file_lease *fl;
17031710
struct inode *inode = file_inode(filp);
@@ -1713,7 +1720,8 @@ int fcntl_getlease(struct file *filp)
17131720
list_for_each_entry(fl, &ctx->flc_lease, c.flc_list) {
17141721
if (fl->c.flc_file != filp)
17151722
continue;
1716-
type = target_leasetype(fl);
1723+
if (fl->c.flc_flags & flavor)
1724+
type = target_leasetype(fl);
17171725
break;
17181726
}
17191727
spin_unlock(&ctx->flc_lock);
@@ -1724,6 +1732,19 @@ int fcntl_getlease(struct file *filp)
17241732
return type;
17251733
}
17261734

1735+
int fcntl_getlease(struct file *filp)
1736+
{
1737+
return __fcntl_getlease(filp, FL_LEASE);
1738+
}
1739+
1740+
int fcntl_getdeleg(struct file *filp, struct delegation *deleg)
1741+
{
1742+
if (deleg->d_flags != 0 || deleg->__pad != 0)
1743+
return -EINVAL;
1744+
deleg->d_type = __fcntl_getlease(filp, FL_DELEG);
1745+
return 0;
1746+
}
1747+
17271748
/**
17281749
* check_conflicting_open - see if the given file points to an inode that has
17291750
* an existing open that would conflict with the
@@ -1929,11 +1950,19 @@ static int generic_delete_lease(struct file *filp, void *owner)
19291950
int generic_setlease(struct file *filp, int arg, struct file_lease **flp,
19301951
void **priv)
19311952
{
1953+
struct inode *inode = file_inode(filp);
1954+
1955+
if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
1956+
return -EINVAL;
1957+
19321958
switch (arg) {
19331959
case F_UNLCK:
19341960
return generic_delete_lease(filp, *priv);
1935-
case F_RDLCK:
19361961
case F_WRLCK:
1962+
if (S_ISDIR(inode->i_mode))
1963+
return -EINVAL;
1964+
fallthrough;
1965+
case F_RDLCK:
19371966
if (!(*flp)->fl_lmops->lm_break) {
19381967
WARN_ON_ONCE(1);
19391968
return -ENOLCK;
@@ -2018,22 +2047,20 @@ vfs_setlease(struct file *filp, int arg, struct file_lease **lease, void **priv)
20182047

20192048
if ((!vfsuid_eq_kuid(vfsuid, current_fsuid())) && !capable(CAP_LEASE))
20202049
return -EACCES;
2021-
if (!S_ISREG(inode->i_mode))
2022-
return -EINVAL;
20232050
error = security_file_lock(filp, arg);
20242051
if (error)
20252052
return error;
20262053
return kernel_setlease(filp, arg, lease, priv);
20272054
}
20282055
EXPORT_SYMBOL_GPL(vfs_setlease);
20292056

2030-
static int do_fcntl_add_lease(unsigned int fd, struct file *filp, int arg)
2057+
static int do_fcntl_add_lease(unsigned int fd, struct file *filp, unsigned int flavor, int arg)
20312058
{
20322059
struct file_lease *fl;
20332060
struct fasync_struct *new;
20342061
int error;
20352062

2036-
fl = lease_alloc(filp, arg);
2063+
fl = lease_alloc(filp, flavor, arg);
20372064
if (IS_ERR(fl))
20382065
return PTR_ERR(fl);
20392066

@@ -2064,9 +2091,33 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, int arg)
20642091
*/
20652092
int fcntl_setlease(unsigned int fd, struct file *filp, int arg)
20662093
{
2094+
if (S_ISDIR(file_inode(filp)->i_mode))
2095+
return -EINVAL;
2096+
20672097
if (arg == F_UNLCK)
20682098
return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp);
2069-
return do_fcntl_add_lease(fd, filp, arg);
2099+
return do_fcntl_add_lease(fd, filp, FL_LEASE, arg);
2100+
}
2101+
2102+
/**
2103+
* fcntl_setdeleg - sets a delegation on an open file
2104+
* @fd: open file descriptor
2105+
* @filp: file pointer
2106+
* @deleg: delegation request from userland
2107+
*
2108+
* Call this fcntl to establish a delegation on the file.
2109+
* Note that you also need to call %F_SETSIG to
2110+
* receive a signal when the lease is broken.
2111+
*/
2112+
int fcntl_setdeleg(unsigned int fd, struct file *filp, struct delegation *deleg)
2113+
{
2114+
/* For now, no flags are supported */
2115+
if (deleg->d_flags != 0 || deleg->__pad != 0)
2116+
return -EINVAL;
2117+
2118+
if (deleg->d_type == F_UNLCK)
2119+
return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp);
2120+
return do_fcntl_add_lease(fd, filp, FL_DELEG, deleg->d_type);
20702121
}
20712122

20722123
/**

0 commit comments

Comments
 (0)