If a NFS client requests a directory delegation with a notification bitmask covering directory change events, the server shouldn't recall the delegation. Instead the client will be notified of the change after the fact. Add a support for ignoring lease breaks on directory changes. Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx> --- fs/locks.c | 56 ++++++++++++++++++++++++++++++++++++++++-------- include/linux/filelock.h | 29 ++++++++++++++----------- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 6e46176d1e00962904f03c151500e593f410e4c6..95270a1fab4a1792a6fcad738cc9d937d99ad2af 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1526,15 +1526,52 @@ any_leases_conflict(struct inode *inode, struct file_lease *breaker) return false; } +static bool +ignore_dir_deleg_break(struct file_lease *fl, unsigned int flags) +{ + if ((flags & LEASE_BREAK_DIR_CREATE) && (fl->c.flc_flags & FL_IGN_DIR_CREATE)) + return true; + if ((flags & LEASE_BREAK_DIR_DELETE) && (fl->c.flc_flags & FL_IGN_DIR_DELETE)) + return true; + if ((flags & LEASE_BREAK_DIR_RENAME) && (fl->c.flc_flags & FL_IGN_DIR_RENAME)) + return true; + + return false; +} + +static bool +visible_leases_remaining(struct inode *inode, unsigned int flags) +{ + struct file_lock_context *ctx = locks_inode_context(inode); + struct file_lease *fl; + + lockdep_assert_held(&ctx->flc_lock); + + if (list_empty(&ctx->flc_lease)) + return false; + + if (!S_ISDIR(inode->i_mode)) + return true; + + list_for_each_entry(fl, &ctx->flc_lease, c.flc_list) { + if (!ignore_dir_deleg_break(fl, flags)) + return true; + } + return false; +} + /** - * __break_lease - revoke all outstanding leases on file - * @inode: the inode of the file to return - * @flags: LEASE_BREAK_* flags + * __break_lease - revoke all outstanding leases on file + * @inode: the inode of the file to return + * @flags: LEASE_BREAK_* flags * - * break_lease (inlined for speed) has checked there already is at least - * some kind of lock (maybe a lease) on this file. Leases are broken on - * a call to open() or truncate(). This function can block waiting for the - * lease break unless you specify LEASE_BREAK_NONBLOCK. + * break_lease (inlined for speed) has checked there already is at least + * some kind of lock (maybe a lease) on this file. Leases and Delegations + * are broken on a call to open() or truncate(). Delegations are also + * broken on any event that would change the ctime. Directory delegations + * are broken whenever the directory changes (unless the delegation is set + * up to ignore the event). This function can block waiting for the lease + * break unless you specify LEASE_BREAK_NONBLOCK. */ int __break_lease(struct inode *inode, unsigned int flags) { @@ -1545,7 +1582,6 @@ int __break_lease(struct inode *inode, unsigned int flags) bool want_write = !(flags & LEASE_BREAK_OPEN_RDONLY); int error = 0; - new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK); if (IS_ERR(new_fl)) return PTR_ERR(new_fl); @@ -1584,6 +1620,8 @@ int __break_lease(struct inode *inode, unsigned int flags) list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, c.flc_list) { if (!leases_conflict(&fl->c, &new_fl->c)) continue; + if (S_ISDIR(inode->i_mode) && ignore_dir_deleg_break(fl, flags)) + continue; if (want_write) { if (fl->c.flc_flags & FL_UNLOCK_PENDING) continue; @@ -1599,7 +1637,7 @@ int __break_lease(struct inode *inode, unsigned int flags) locks_delete_lock_ctx(&fl->c, &dispose); } - if (list_empty(&ctx->flc_lease)) + if (!visible_leases_remaining(inode, flags)) goto out; if (flags & LEASE_BREAK_NONBLOCK) { diff --git a/include/linux/filelock.h b/include/linux/filelock.h index f2b2d1e1d1ab08671895c3bfe398e5bba02353d8..32b30c14f5fd52727b1a18957e9dbc930c922941 100644 --- a/include/linux/filelock.h +++ b/include/linux/filelock.h @@ -4,19 +4,22 @@ #include <linux/fs.h> -#define FL_POSIX 1 -#define FL_FLOCK 2 -#define FL_DELEG 4 /* NFSv4 delegation */ -#define FL_ACCESS 8 /* not trying to lock, just looking */ -#define FL_EXISTS 16 /* when unlocking, test for existence */ -#define FL_LEASE 32 /* lease held on this file */ -#define FL_CLOSE 64 /* unlock on close */ -#define FL_SLEEP 128 /* A blocking lock */ -#define FL_DOWNGRADE_PENDING 256 /* Lease is being downgraded */ -#define FL_UNLOCK_PENDING 512 /* Lease is being broken */ -#define FL_OFDLCK 1024 /* lock is "owned" by struct file */ -#define FL_LAYOUT 2048 /* outstanding pNFS layout */ -#define FL_RECLAIM 4096 /* reclaiming from a reboot server */ +#define FL_POSIX BIT(0) /* POSIX lock */ +#define FL_FLOCK BIT(1) /* BSD lock */ +#define FL_LEASE BIT(2) /* file lease */ +#define FL_DELEG BIT(3) /* NFSv4 delegation */ +#define FL_LAYOUT BIT(4) /* outstanding pNFS layout */ +#define FL_ACCESS BIT(5) /* not trying to lock, just looking */ +#define FL_EXISTS BIT(6) /* when unlocking, test for existence */ +#define FL_CLOSE BIT(7) /* unlock on close */ +#define FL_SLEEP BIT(8) /* A blocking lock */ +#define FL_DOWNGRADE_PENDING BIT(9) /* Lease is being downgraded */ +#define FL_UNLOCK_PENDING BIT(10) /* Lease is being broken */ +#define FL_OFDLCK BIT(11) /* POSIX lock "owned" by struct file */ +#define FL_RECLAIM BIT(12) /* reclaiming from a reboot server */ +#define FL_IGN_DIR_CREATE BIT(13) /* ignore DIR_CREATE events */ +#define FL_IGN_DIR_DELETE BIT(14) /* ignore DIR_DELETE events */ +#define FL_IGN_DIR_RENAME BIT(15) /* ignore DIR_RENAME events */ #define FL_CLOSE_POSIX (FL_POSIX | FL_CLOSE) -- 2.49.0