2019-06-30 13:55:20

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 01/16] sunrpc: add a new cache_detail operation for when a cache is flushed

From: Jeff Layton <[email protected]>

When the exports table is changed, exportfs will usually write a new
time to the "flush" file in the nfsd.export cache procfile. This tells
the kernel to flush any entries that are older than that value.

This gives us a mechanism to tell whether an unexport might have
occurred. Add a new ->flush cache_detail operation that is called after
flushing the cache whenever someone writes to a "flush" file.

Signed-off-by: Jeff Layton <[email protected]>
Signed-off-by: Trond Myklebust <[email protected]>
Signed-off-by: Trond Myklebust <[email protected]>
---
include/linux/sunrpc/cache.h | 1 +
net/sunrpc/cache.c | 3 +++
2 files changed, 4 insertions(+)

diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index c7f38e897174..dfa3ab97564a 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -87,6 +87,7 @@ struct cache_detail {
int has_died);

struct cache_head * (*alloc)(void);
+ void (*flush)(void);
int (*match)(struct cache_head *orig, struct cache_head *new);
void (*init)(struct cache_head *orig, struct cache_head *new);
void (*update)(struct cache_head *orig, struct cache_head *new);
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 66fbb9d2fba7..195b46a4a512 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -1521,6 +1521,9 @@ static ssize_t write_flush(struct file *file, const char __user *buf,
cd->nextcheck = now;
cache_flush();

+ if (cd->flush)
+ cd->flush();
+
*ppos += count;
return count;
}
--
2.21.0


2019-06-30 13:55:20

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 02/16] locks: create a new notifier chain for lease attempts

From: Jeff Layton <[email protected]>

With the new file caching infrastructure in nfsd, we can end up holding
files open for an indefinite period of time, even when they are still
idle. This may prevent the kernel from handing out leases on the file,
which is something we don't want to block.

Fix this by running a SRCU notifier call chain whenever on any
lease attempt. nfsd can then purge the cache for that inode before
returning.

Since SRCU is only conditionally compiled in, we must only define the
new chain if it's enabled, and users of the chain must ensure that
SRCU is enabled.

Signed-off-by: Jeff Layton <[email protected]>
Signed-off-by: Trond Myklebust <[email protected]>
Signed-off-by: Trond Myklebust <[email protected]>
---
fs/locks.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/fs.h | 5 ++++
2 files changed, 67 insertions(+)

diff --git a/fs/locks.c b/fs/locks.c
index ec1e4a5df629..33ae1a7f3031 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -212,6 +212,7 @@ struct file_lock_list_struct {
static DEFINE_PER_CPU(struct file_lock_list_struct, file_lock_list);
DEFINE_STATIC_PERCPU_RWSEM(file_rwsem);

+
/*
* The blocked_hash is used to find POSIX lock loops for deadlock detection.
* It is protected by blocked_lock_lock.
@@ -1973,6 +1974,64 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp,
}
EXPORT_SYMBOL(generic_setlease);

+#if IS_ENABLED(CONFIG_SRCU)
+/*
+ * Kernel subsystems can register to be notified on any attempt to set
+ * a new lease with the lease_notifier_chain. This is used by (e.g.) nfsd
+ * to close files that it may have cached when there is an attempt to set a
+ * conflicting lease.
+ */
+static struct srcu_notifier_head lease_notifier_chain;
+
+static inline void
+lease_notifier_chain_init(void)
+{
+ srcu_init_notifier_head(&lease_notifier_chain);
+}
+
+static inline void
+setlease_notifier(long arg, struct file_lock *lease)
+{
+ if (arg != F_UNLCK)
+ srcu_notifier_call_chain(&lease_notifier_chain, arg, lease);
+}
+
+int lease_register_notifier(struct notifier_block *nb)
+{
+ return srcu_notifier_chain_register(&lease_notifier_chain, nb);
+}
+EXPORT_SYMBOL_GPL(lease_register_notifier);
+
+void lease_unregister_notifier(struct notifier_block *nb)
+{
+ srcu_notifier_chain_unregister(&lease_notifier_chain, nb);
+}
+EXPORT_SYMBOL_GPL(lease_unregister_notifier);
+
+#else /* !IS_ENABLED(CONFIG_SRCU) */
+static inline void
+lease_notifier_chain_init(void)
+{
+}
+
+static inline void
+setlease_notifier(long arg, struct file_lock *lease)
+{
+}
+
+int lease_register_notifier(struct notifier_block *nb)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lease_register_notifier);
+
+void lease_unregister_notifier(struct notifier_block *nb)
+{
+}
+EXPORT_SYMBOL_GPL(lease_unregister_notifier);
+
+#endif /* IS_ENABLED(CONFIG_SRCU) */
+
/**
* vfs_setlease - sets a lease on an open file
* @filp: file pointer
@@ -1993,6 +2052,8 @@ EXPORT_SYMBOL(generic_setlease);
int
vfs_setlease(struct file *filp, long arg, struct file_lock **lease, void **priv)
{
+ if (lease)
+ setlease_notifier(arg, *lease);
if (filp->f_op->setlease)
return filp->f_op->setlease(filp, arg, lease, priv);
else
@@ -2906,6 +2967,7 @@ static int __init filelock_init(void)
INIT_HLIST_HEAD(&fll->hlist);
}

+ lease_notifier_chain_init();
return 0;
}
core_initcall(filelock_init);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..066dfc3963b5 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1157,6 +1157,11 @@ extern void lease_get_mtime(struct inode *, struct timespec64 *time);
extern int generic_setlease(struct file *, long, struct file_lock **, void **priv);
extern int vfs_setlease(struct file *, long, struct file_lock **, void **);
extern int lease_modify(struct file_lock *, int, struct list_head *);
+
+struct notifier_block;
+extern int lease_register_notifier(struct notifier_block *);
+extern void lease_unregister_notifier(struct notifier_block *);
+
struct files_struct;
extern void show_fd_locks(struct seq_file *f,
struct file *filp, struct files_struct *files);
--
2.21.0

2019-06-30 15:28:19

by Matthew Wilcox

[permalink] [raw]
Subject: Re: [PATCH 02/16] locks: create a new notifier chain for lease attempts

On Sun, Jun 30, 2019 at 09:52:26AM -0400, Trond Myklebust wrote:
> +++ b/fs/locks.c
> @@ -212,6 +212,7 @@ struct file_lock_list_struct {
> static DEFINE_PER_CPU(struct file_lock_list_struct, file_lock_list);
> DEFINE_STATIC_PERCPU_RWSEM(file_rwsem);
>
> +
> /*
> * The blocked_hash is used to find POSIX lock loops for deadlock detection.
> * It is protected by blocked_lock_lock.

*cough*

2019-06-30 15:50:54

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 02/16] locks: create a new notifier chain for lease attempts

On Sun, 2019-06-30 at 08:27 -0700, Matthew Wilcox wrote:
> On Sun, Jun 30, 2019 at 09:52:26AM -0400, Trond Myklebust wrote:
> > +++ b/fs/locks.c
> > @@ -212,6 +212,7 @@ struct file_lock_list_struct {
> > static DEFINE_PER_CPU(struct file_lock_list_struct,
> > file_lock_list);
> > DEFINE_STATIC_PERCPU_RWSEM(file_rwsem);
> >
> > +
> > /*
> > * The blocked_hash is used to find POSIX lock loops for deadlock
> > detection.
> > * It is protected by blocked_lock_lock.
>
> *cough*
>

Oops. Yes, that hunk is probably non-critical.

--
Trond Myklebust
Linux NFS client maintainer, Hammerspace
[email protected]