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 <[email protected]>
---
 fs/locks.c                      | 56 ++++++++++++++++++++++++++++++++++-------
 include/linux/filelock.h        | 29 +++++++++++----------
 include/trace/events/filelock.h |  5 +++-
 3 files changed, 67 insertions(+), 23 deletions(-)

diff --git a/fs/locks.c b/fs/locks.c
index 
4cfa4fc7130137b2850cab871bc3b2b23bbd3db1..ff5f5a85680a2c8511f5828947e829c7ffc1bd59
 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 
68901da7671694f68d939085d08e0da21712540d..0f0e56490c7ba4242c1caa79c68ab1db459609d8
 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)
 
diff --git a/include/trace/events/filelock.h b/include/trace/events/filelock.h
index 
2dfeb158e848a528b7d9c0d5f8872c5060df1bf6..4988804908478912c6b8044dfb3b147fc50e4823
 100644
--- a/include/trace/events/filelock.h
+++ b/include/trace/events/filelock.h
@@ -28,7 +28,10 @@
                { FL_DOWNGRADE_PENDING, "FL_DOWNGRADE_PENDING" },       \
                { FL_UNLOCK_PENDING,    "FL_UNLOCK_PENDING" },          \
                { FL_OFDLCK,            "FL_OFDLCK" },                  \
-               { FL_RECLAIM,           "FL_RECLAIM"})
+               { FL_RECLAIM,           "FL_RECLAIM" },                 \
+               { FL_IGN_DIR_CREATE,    "FL_IGN_DIR_CREATE" },          \
+               { FL_IGN_DIR_DELETE,    "FL_IGN_DIR_DELETE" },          \
+               { FL_IGN_DIR_RENAME,    "FL_IGN_DIR_RENAME" })
 
 #define show_fl_type(val)                              \
        __print_symbolic(val,                           \

-- 
2.51.0


Reply via email to