2012-11-16 02:31:34

by Myklebust, Trond

[permalink] [raw]
Subject: [RFC PATCH 1/4] NFS: Add an ioctl to allow applications limited control over caching

Add an ioctl that allows an application to force the NFS client to
revalidate the attributes and/or data for a regular file, or
directory.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/Makefile | 2 +-
fs/nfs/dir.c | 4 +++
fs/nfs/file.c | 4 +++
fs/nfs/internal.h | 3 ++
fs/nfs/ioctl.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/nfs/ioctl.h | 35 ++++++++++++++++++++++
fs/nfs/nfs4file.c | 4 +++
7 files changed, 137 insertions(+), 1 deletion(-)
create mode 100644 fs/nfs/ioctl.c
create mode 100644 fs/nfs/ioctl.h

diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index b7db608..581db7a 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_NFS_FS) += nfs.o
nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
direct.o pagelist.o read.o symlink.o unlink.o \
write.o namespace.o mount_clnt.o \
- dns_resolve.o cache_lib.o
+ dns_resolve.o cache_lib.o ioctl.o
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
nfs-$(CONFIG_SYSCTL) += sysctl.o
nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index ce8cb92..10ad886 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -58,6 +58,10 @@ const struct file_operations nfs_dir_operations = {
.open = nfs_opendir,
.release = nfs_closedir,
.fsync = nfs_fsync_dir,
+ .unlocked_ioctl = nfs_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = nfs_ioctl,
+#endif
};

const struct address_space_operations nfs_dir_aops = {
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 582bb88..deefa62 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -925,5 +925,9 @@ const struct file_operations nfs_file_operations = {
.splice_write = nfs_file_splice_write,
.check_flags = nfs_check_flags,
.setlease = nfs_setlease,
+ .unlocked_ioctl = nfs_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = nfs_ioctl,
+#endif
};
EXPORT_SYMBOL_GPL(nfs_file_operations);
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index e1827b0..4b425bd 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -481,6 +481,9 @@ extern int nfs41_walk_client_list(struct nfs_client *clp,
struct nfs_client **result,
struct rpc_cred *cred);

+/* ioctl.c */
+extern long nfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+
/*
* Determine the device name as a string
*/
diff --git a/fs/nfs/ioctl.c b/fs/nfs/ioctl.c
new file mode 100644
index 0000000..9da5f7d
--- /dev/null
+++ b/fs/nfs/ioctl.c
@@ -0,0 +1,86 @@
+/*
+ * linux/fs/nfs/ioctl.c
+ *
+ * Copyright (C) 2012 Trond Myklebust <[email protected]>
+ *
+ * nfs ioctl implementation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/nfs_fs.h>
+#include "internal.h"
+#include "ioctl.h"
+
+static long nfs_ioctl_cache_revalidate(struct file *filp, bool metadata, bool data)
+{
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ struct nfs_inode *nfsi = NFS_I(inode);
+ umode_t mode = inode->i_mode;
+ unsigned long invalid = 0;
+ long ret = -EINVAL;
+
+ if (metadata)
+ invalid |= NFS_INO_INVALID_ATTR
+ | NFS_INO_INVALID_ACCESS
+ | NFS_INO_INVALID_ACL;
+ if (data)
+ invalid |= NFS_INO_INVALID_DATA;
+
+ switch (mode & S_IFMT) {
+ default:
+ goto out;
+ case S_IFDIR:
+ spin_lock(&inode->i_lock);
+ if (data)
+ nfsi->cache_change_attribute++;
+ nfsi->cache_validity |= invalid;
+ spin_unlock(&inode->i_lock);
+ break;
+ case S_IFREG:
+ vfs_fsync(filp, 1);
+ if (data)
+ invalid |= NFS_INO_REVAL_PAGECACHE;
+ case S_IFLNK:
+ spin_lock(&inode->i_lock);
+ nfsi->cache_validity |= invalid;
+ spin_unlock(&inode->i_lock);
+ }
+ ret = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ if (ret == 0)
+ ret = nfs_revalidate_mapping(inode, filp->f_mapping);
+
+out:
+ return ret;
+}
+
+static long nfs_ioctl_cachectl(struct file *filp, struct nfs_cachectl __user *argp)
+{
+ u64 cmd;
+
+ if (!(filp->f_mode & (FMODE_READ|FMODE_WRITE)))
+ return -EBADF;
+ if (get_user(cmd, &argp->cmd))
+ return -EFAULT;
+ switch (cmd) {
+ case NFS_CACHECTL_REVALIDATE_ALL:
+ return nfs_ioctl_cache_revalidate(filp, true, true);
+ case NFS_CACHECTL_REVALIDATE_METADATA:
+ return nfs_ioctl_cache_revalidate(filp, true, false);
+ case NFS_CACHECTL_REVALIDATE_DATA:
+ return nfs_ioctl_cache_revalidate(filp, false, true);
+ }
+ return -EINVAL;
+}
+
+long nfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case NFS_IOC_CACHECTL:
+ return nfs_ioctl_cachectl(filp, argp);
+ }
+ return -ENOTTY;
+}
diff --git a/fs/nfs/ioctl.h b/fs/nfs/ioctl.h
new file mode 100644
index 0000000..cf79f0d
--- /dev/null
+++ b/fs/nfs/ioctl.h
@@ -0,0 +1,35 @@
+/*
+ * linux/fs/nfs/ioctl.h
+ *
+ * Copyright (C) 2012 Trond Myklebust <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ *
+ * nfs ioctl definitions
+ *
+ */
+
+#include <uapi/linux/ioctl.h>
+
+/* Cache revalidation modes */
+#define NFS_CACHECTL_REVALIDATE_ALL 1
+#define NFS_CACHECTL_REVALIDATE_METADATA 2
+#define NFS_CACHECTL_REVALIDATE_DATA 3
+
+struct nfs_cachectl {
+ u64 cmd;
+};
+
+#define NFS_IOC_CACHECTL _IOW('N', 1, struct nfs_cachectl)
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index e769930..b221690 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -133,4 +133,8 @@ const struct file_operations nfs4_file_operations = {
.splice_write = nfs_file_splice_write,
.check_flags = nfs_check_flags,
.setlease = nfs_setlease,
+ .unlocked_ioctl = nfs_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = nfs_ioctl,
+#endif
};
--
1.7.11.7



2012-11-19 17:42:40

by Myklebust, Trond

[permalink] [raw]
Subject: RE: [RFC PATCH 1/4] NFS: Add an ioctl to allow applications limited control over caching


> -----Original Message-----
> From: Peter Staubach [mailto:[email protected]]
> Sent: Monday, November 19, 2012 11:53 AM
> To: Myklebust, Trond; [email protected]
> Subject: RE: [RFC PATCH 1/4] NFS: Add an ioctl to allow applications limited
> control over caching
>
> Hi.
>
> What application is having a problem which can be addressed via this support
> and what is the problem?

I've mainly heard complaints from the people who are trying to make distributed 'make' work well, but there have also been a few complaints from the MPIO crowd over the years.

O_DIRECT will generally satisfy the needs of the folks who want to do uncached I/O on regular files, however the caching of directories remains as problematic as ever. We don't have write delegations (although that wouldn't help the distributed make folks), and we have no locking primitives in NFS. The 'lookupcache=' and 'noac' client-side mount options do help mitigate some of the problems, but at a cost: revalidation is enforced on all lookups and attribute-related operations, and there is no option to target only the files/directories that are changing.

Adding a complete set of cache control primitives is the missing piece that will allow people to invent their own locking schemes in the form of user-space DLMs, and have them work safely on both directories and files. It should allow them to replace the many hacks that I've seen being used (and that often break when we fix caching bugs) with a documented API that we can actually support and standardise; at least across Linux-based NFS clients.

Cheers
Trond

>
> Thanx...
>
> ps
>
>
> -----Original Message-----
> From: [email protected] [mailto:linux-nfs-
> [email protected]] On Behalf Of Trond Myklebust
> Sent: Thursday, November 15, 2012 9:31 PM
> To: [email protected]
> Subject: [RFC PATCH 1/4] NFS: Add an ioctl to allow applications limited
> control over caching
>
> Add an ioctl that allows an application to force the NFS client to revalidate the
> attributes and/or data for a regular file, or directory.
>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfs/Makefile | 2 +-
> fs/nfs/dir.c | 4 +++
> fs/nfs/file.c | 4 +++
> fs/nfs/internal.h | 3 ++
> fs/nfs/ioctl.c | 86
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> fs/nfs/ioctl.h | 35 ++++++++++++++++++++++
> fs/nfs/nfs4file.c | 4 +++
> 7 files changed, 137 insertions(+), 1 deletion(-) create mode 100644
> fs/nfs/ioctl.c create mode 100644 fs/nfs/ioctl.h
>
> diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index b7db608..581db7a 100644
> --- a/fs/nfs/Makefile
> +++ b/fs/nfs/Makefile
> @@ -7,7 +7,7 @@ obj-$(CONFIG_NFS_FS) += nfs.o
> nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
> direct.o pagelist.o read.o symlink.o unlink.o \
> write.o namespace.o mount_clnt.o \
> - dns_resolve.o cache_lib.o
> + dns_resolve.o cache_lib.o ioctl.o
> nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
> nfs-$(CONFIG_SYSCTL) += sysctl.o
> nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o diff --git
> a/fs/nfs/dir.c b/fs/nfs/dir.c index ce8cb92..10ad886 100644
> --- a/fs/nfs/dir.c
> +++ b/fs/nfs/dir.c
> @@ -58,6 +58,10 @@ const struct file_operations nfs_dir_operations = {
> .open = nfs_opendir,
> .release = nfs_closedir,
> .fsync = nfs_fsync_dir,
> + .unlocked_ioctl = nfs_ioctl,
> +#ifdef CONFIG_COMPAT
> + .compat_ioctl = nfs_ioctl,
> +#endif
> };
>
> const struct address_space_operations nfs_dir_aops = { diff --git
> a/fs/nfs/file.c b/fs/nfs/file.c index 582bb88..deefa62 100644
> --- a/fs/nfs/file.c
> +++ b/fs/nfs/file.c
> @@ -925,5 +925,9 @@ const struct file_operations nfs_file_operations = {
> .splice_write = nfs_file_splice_write,
> .check_flags = nfs_check_flags,
> .setlease = nfs_setlease,
> + .unlocked_ioctl = nfs_ioctl,
> +#ifdef CONFIG_COMPAT
> + .compat_ioctl = nfs_ioctl,
> +#endif
> };
> EXPORT_SYMBOL_GPL(nfs_file_operations);
> diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index e1827b0..4b425bd
> 100644
> --- a/fs/nfs/internal.h
> +++ b/fs/nfs/internal.h
> @@ -481,6 +481,9 @@ extern int nfs41_walk_client_list(struct nfs_client
> *clp,
> struct nfs_client **result,
> struct rpc_cred *cred);
>
> +/* ioctl.c */
> +extern long nfs_ioctl(struct file *filp, unsigned int cmd, unsigned
> +long arg);
> +
> /*
> * Determine the device name as a string
> */
> diff --git a/fs/nfs/ioctl.c b/fs/nfs/ioctl.c new file mode 100644 index
> 0000000..9da5f7d
> --- /dev/null
> +++ b/fs/nfs/ioctl.c
> @@ -0,0 +1,86 @@
> +/*
> + * linux/fs/nfs/ioctl.c
> + *
> + * Copyright (C) 2012 Trond Myklebust <[email protected]>
> + *
> + * nfs ioctl implementation
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/fs.h>
> +#include <linux/nfs_fs.h>
> +#include "internal.h"
> +#include "ioctl.h"
> +
> +static long nfs_ioctl_cache_revalidate(struct file *filp, bool
> +metadata, bool data) {
> + struct inode *inode = filp->f_path.dentry->d_inode;
> + struct nfs_inode *nfsi = NFS_I(inode);
> + umode_t mode = inode->i_mode;
> + unsigned long invalid = 0;
> + long ret = -EINVAL;
> +
> + if (metadata)
> + invalid |= NFS_INO_INVALID_ATTR
> + | NFS_INO_INVALID_ACCESS
> + | NFS_INO_INVALID_ACL;
> + if (data)
> + invalid |= NFS_INO_INVALID_DATA;
> +
> + switch (mode & S_IFMT) {
> + default:
> + goto out;
> + case S_IFDIR:
> + spin_lock(&inode->i_lock);
> + if (data)
> + nfsi->cache_change_attribute++;
> + nfsi->cache_validity |= invalid;
> + spin_unlock(&inode->i_lock);
> + break;
> + case S_IFREG:
> + vfs_fsync(filp, 1);
> + if (data)
> + invalid |= NFS_INO_REVAL_PAGECACHE;
> + case S_IFLNK:
> + spin_lock(&inode->i_lock);
> + nfsi->cache_validity |= invalid;
> + spin_unlock(&inode->i_lock);
> + }
> + ret = nfs_revalidate_inode(NFS_SERVER(inode), inode);
> + if (ret == 0)
> + ret = nfs_revalidate_mapping(inode, filp->f_mapping);
> +
> +out:
> + return ret;
> +}
> +
> +static long nfs_ioctl_cachectl(struct file *filp, struct nfs_cachectl
> +__user *argp) {
> + u64 cmd;
> +
> + if (!(filp->f_mode & (FMODE_READ|FMODE_WRITE)))
> + return -EBADF;
> + if (get_user(cmd, &argp->cmd))
> + return -EFAULT;
> + switch (cmd) {
> + case NFS_CACHECTL_REVALIDATE_ALL:
> + return nfs_ioctl_cache_revalidate(filp, true, true);
> + case NFS_CACHECTL_REVALIDATE_METADATA:
> + return nfs_ioctl_cache_revalidate(filp, true, false);
> + case NFS_CACHECTL_REVALIDATE_DATA:
> + return nfs_ioctl_cache_revalidate(filp, false, true);
> + }
> + return -EINVAL;
> +}
> +
> +long nfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> +{
> + void __user *argp = (void __user *)arg;
> +
> + switch (cmd) {
> + case NFS_IOC_CACHECTL:
> + return nfs_ioctl_cachectl(filp, argp);
> + }
> + return -ENOTTY;
> +}
> diff --git a/fs/nfs/ioctl.h b/fs/nfs/ioctl.h new file mode 100644 index
> 0000000..cf79f0d
> --- /dev/null
> +++ b/fs/nfs/ioctl.h
> @@ -0,0 +1,35 @@
> +/*
> + * linux/fs/nfs/ioctl.h
> + *
> + * Copyright (C) 2012 Trond Myklebust <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public
> + * License v2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public
> + * License along with this program; if not, write to the
> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
> + * Boston, MA 021110-1307, USA.
> + *
> + * nfs ioctl definitions
> + *
> + */
> +
> +#include <uapi/linux/ioctl.h>
> +
> +/* Cache revalidation modes */
> +#define NFS_CACHECTL_REVALIDATE_ALL 1
> +#define NFS_CACHECTL_REVALIDATE_METADATA 2
> +#define NFS_CACHECTL_REVALIDATE_DATA 3
> +
> +struct nfs_cachectl {
> + u64 cmd;
> +};
> +
> +#define NFS_IOC_CACHECTL _IOW('N', 1, struct nfs_cachectl)
> diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index e769930..b221690 100644
> --- a/fs/nfs/nfs4file.c
> +++ b/fs/nfs/nfs4file.c
> @@ -133,4 +133,8 @@ const struct file_operations nfs4_file_operations = {
> .splice_write = nfs_file_splice_write,
> .check_flags = nfs_check_flags,
> .setlease = nfs_setlease,
> + .unlocked_ioctl = nfs_ioctl,
> +#ifdef CONFIG_COMPAT
> + .compat_ioctl = nfs_ioctl,
> +#endif
> };
> --
> 1.7.11.7
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the
> body of a message to [email protected] More majordomo info at
> http://vger.kernel.org/majordomo-info.html

2012-11-16 02:31:36

by Myklebust, Trond

[permalink] [raw]
Subject: [RFC PATCH 4/4] NFS: Allow a process to set an aggressive caching policy on a file

This patch allows one process to take on the role as a file 'cache policy
controller'. It turns off the some of the more conservative caching
policies that the kernel uses, making it behave as if a delegation is
enabled.
The intention is that the cache policy controller will take over the
role of revalidating the cache using the NFS_CACHECTL ioctl.

The role is tied to an open file descriptor, which means that the
process can return control to the kernel by closing that
file descriptor (or by using the NFS_CACHECTL_CLEAR_AGGRESSIVE
ioctl).

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/inode.c | 8 ++++++++
fs/nfs/ioctl.c | 36 ++++++++++++++++++++++++++++++++++++
fs/nfs/ioctl.h | 2 ++
include/linux/nfs_fs.h | 2 ++
4 files changed, 48 insertions(+)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 117183b..e1de62e 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -672,11 +672,14 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
{
struct inode *inode = ctx->dentry->d_inode;
struct super_block *sb = ctx->dentry->d_sb;
+ struct nfs_inode *nfsi = NFS_I(inode);

if (!list_empty(&ctx->list)) {
if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock))
return;
list_del(&ctx->list);
+ if (test_bit(NFS_CONTEXT_CACHE_AGGRESSIVE, &ctx->flags))
+ nfsi->cache_validity &= ~NFS_INO_CACHE_AGGRESSIVE;
spin_unlock(&inode->i_lock);
} else if (!atomic_dec_and_test(&ctx->lock_context.count))
return;
@@ -885,6 +888,8 @@ static bool nfs_mapping_need_revalidate_inode(struct inode *inode)
{
if (nfs_have_delegated_attributes(inode))
return false;
+ if (NFS_I(inode)->cache_validity & NFS_INO_CACHE_AGGRESSIVE)
+ return false;
return (NFS_I(inode)->cache_validity & NFS_INO_REVAL_PAGECACHE)
|| nfs_attribute_timeout(inode)
|| NFS_STALE(inode);
@@ -1484,6 +1489,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
|| S_ISLNK(inode->i_mode)))
invalid &= ~NFS_INO_INVALID_DATA;
+ /* Honour the aggressive caching flag */
+ if (nfsi->cache_validity & NFS_INO_CACHE_AGGRESSIVE)
+ invalid &= ~(NFS_INO_INVALID_DATA|NFS_INO_REVAL_PAGECACHE);
if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ) ||
(save_cache_validity & NFS_INO_REVAL_FORCED))
nfsi->cache_validity |= invalid;
diff --git a/fs/nfs/ioctl.c b/fs/nfs/ioctl.c
index 72b8592..8c09d86 100644
--- a/fs/nfs/ioctl.c
+++ b/fs/nfs/ioctl.c
@@ -103,6 +103,38 @@ static long nfs_ioctl_return_delegation(struct file *filp)
return NFS_PROTO(inode)->return_delegation(inode);
}

+static long nfs_ioctl_cache_aggressive(struct file *filp, bool set)
+{
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ struct nfs_inode *nfsi = NFS_I(inode);
+ struct nfs_open_context *ctx;
+ int ret = 0;
+
+ /* Require write privileges to change the 'aggressive caching' mode */
+ if (!(filp->f_mode & FMODE_WRITE))
+ return -EBADF;
+ if (!S_ISREG(inode->i_mode))
+ return -EINVAL;
+ ctx = nfs_file_open_context(filp);
+ nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ spin_lock(&inode->i_lock);
+ if (set) {
+ if (!(nfsi->cache_validity & NFS_INO_CACHE_AGGRESSIVE)) {
+ set_bit(NFS_CONTEXT_CACHE_AGGRESSIVE, &ctx->flags);
+ nfsi->cache_validity |= NFS_INO_CACHE_AGGRESSIVE;
+ } else
+ ret = -EBUSY;
+ } else {
+ if (test_bit(NFS_CONTEXT_CACHE_AGGRESSIVE, &ctx->flags)) {
+ nfsi->cache_validity &= ~NFS_INO_CACHE_AGGRESSIVE;
+ clear_bit(NFS_CONTEXT_CACHE_AGGRESSIVE, &ctx->flags);
+ } else
+ ret = -EINVAL;
+ }
+ spin_unlock(&inode->i_lock);
+ return ret;
+}
+
static long nfs_ioctl_cachectl(struct file *filp, struct nfs_cachectl __user *argp)
{
u64 cmd;
@@ -122,6 +154,10 @@ static long nfs_ioctl_cachectl(struct file *filp, struct nfs_cachectl __user *ar
return nfs_ioctl_revalidate_range(filp, argp);
case NFS_CACHECTL_RETURN_DELEGATION:
return nfs_ioctl_return_delegation(filp);
+ case NFS_CACHECTL_SET_AGGRESSIVE:
+ return nfs_ioctl_cache_aggressive(filp, true);
+ case NFS_CACHECTL_CLEAR_AGGRESSIVE:
+ return nfs_ioctl_cache_aggressive(filp, false);
}
return -EINVAL;
}
diff --git a/fs/nfs/ioctl.h b/fs/nfs/ioctl.h
index 6cc5baf..f05b80b 100644
--- a/fs/nfs/ioctl.h
+++ b/fs/nfs/ioctl.h
@@ -29,6 +29,8 @@
#define NFS_CACHECTL_REVALIDATE_DATA 3
#define NFS_CACHECTL_REVALIDATE_RANGE 4
#define NFS_CACHECTL_RETURN_DELEGATION 5
+#define NFS_CACHECTL_SET_AGGRESSIVE 6
+#define NFS_CACHECTL_CLEAR_AGGRESSIVE 7

struct nfs_cachectl {
u64 cmd;
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 1cc2568..b481ea6 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -77,6 +77,7 @@ struct nfs_open_context {
unsigned long flags;
#define NFS_CONTEXT_ERROR_WRITE (0)
#define NFS_CONTEXT_RESEND_WRITES (1)
+#define NFS_CONTEXT_CACHE_AGGRESSIVE (2)
int error;

struct list_head list;
@@ -199,6 +200,7 @@ struct nfs_inode {
#define NFS_INO_INVALID_ACL 0x0010 /* cached acls are invalid */
#define NFS_INO_REVAL_PAGECACHE 0x0020 /* must revalidate pagecache */
#define NFS_INO_REVAL_FORCED 0x0040 /* force revalidation ignoring a delegation */
+#define NFS_INO_CACHE_AGGRESSIVE 0x8000 /* force aggressive caching */

/*
* Bit offsets in flags field
--
1.7.11.7


2012-11-16 02:31:35

by Myklebust, Trond

[permalink] [raw]
Subject: [RFC PATCH 3/4] NFS: Allow users to return the delegation

Not all processes are optimised by delegations. If the file is being
accessed by several clients simultaneously (or even sequentially)
then performance can suffer if the server has to keep doing
delegation callbacks.
This ioctl allows the process to pre-emptively return the delegation
and hence turn off aggressive caching of the file. It is expected
that most applications that want to use this feature will do so
just before the last close of the file.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/ioctl.c | 9 +++++++++
fs/nfs/ioctl.h | 1 +
2 files changed, 10 insertions(+)

diff --git a/fs/nfs/ioctl.c b/fs/nfs/ioctl.c
index e19fa9e..72b8592 100644
--- a/fs/nfs/ioctl.c
+++ b/fs/nfs/ioctl.c
@@ -96,6 +96,13 @@ static long nfs_ioctl_revalidate_range(struct file *filp,
return invalidate_inode_pages2_range(filp->f_mapping, pgstart, pgend);
}

+static long nfs_ioctl_return_delegation(struct file *filp)
+{
+ struct inode *inode = filp->f_path.dentry->d_inode;
+
+ return NFS_PROTO(inode)->return_delegation(inode);
+}
+
static long nfs_ioctl_cachectl(struct file *filp, struct nfs_cachectl __user *argp)
{
u64 cmd;
@@ -113,6 +120,8 @@ static long nfs_ioctl_cachectl(struct file *filp, struct nfs_cachectl __user *ar
return nfs_ioctl_cache_revalidate(filp, false, true);
case NFS_CACHECTL_REVALIDATE_RANGE:
return nfs_ioctl_revalidate_range(filp, argp);
+ case NFS_CACHECTL_RETURN_DELEGATION:
+ return nfs_ioctl_return_delegation(filp);
}
return -EINVAL;
}
diff --git a/fs/nfs/ioctl.h b/fs/nfs/ioctl.h
index 6cf8337..6cc5baf 100644
--- a/fs/nfs/ioctl.h
+++ b/fs/nfs/ioctl.h
@@ -28,6 +28,7 @@
#define NFS_CACHECTL_REVALIDATE_METADATA 2
#define NFS_CACHECTL_REVALIDATE_DATA 3
#define NFS_CACHECTL_REVALIDATE_RANGE 4
+#define NFS_CACHECTL_RETURN_DELEGATION 5

struct nfs_cachectl {
u64 cmd;
--
1.7.11.7


2012-11-16 02:31:35

by Myklebust, Trond

[permalink] [raw]
Subject: [RFC PATCH 2/4] NFS: Add an ioctl to flush and invalidate a limited range of data

Allow finer grained control over the file data cache, by allowing the
user to select a particular range of data to invalidate.
This is not the same as punching a hole. Instead, dirty data will get
flushed to disk, and any clean cached data gets thrown out of the
page cache for that range.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/ioctl.c | 43 +++++++++++++++++++++++++++++++++++++++++++
fs/nfs/ioctl.h | 4 ++++
2 files changed, 47 insertions(+)

diff --git a/fs/nfs/ioctl.c b/fs/nfs/ioctl.c
index 9da5f7d..e19fa9e 100644
--- a/fs/nfs/ioctl.c
+++ b/fs/nfs/ioctl.c
@@ -55,6 +55,47 @@ out:
return ret;
}

+static bool nfs_cachectl_get_range(struct nfs_cachectl __user *argp,
+ loff_t *start, loff_t *len)
+{
+ u64 tmp;
+
+ if (get_user(tmp, &argp->start))
+ return false;
+ *start = tmp;
+ if (get_user(tmp, &argp->len))
+ return false;
+ *len = tmp;
+ return true;
+}
+
+static long nfs_ioctl_revalidate_range(struct file *filp,
+ struct nfs_cachectl __user *argp)
+{
+ loff_t start, len, end;
+ pgoff_t pgstart, pgend;
+
+ if (!nfs_cachectl_get_range(argp, &start, &len))
+ return -EFAULT;
+ end = -1;
+ if (len < 0) {
+ end = start - 1;
+ start += len;
+ } else if (len > 0)
+ end = start + len - 1;
+ if (start < 0)
+ return -EINVAL;
+
+ if (!S_ISREG(filp->f_path.dentry->d_inode->i_mode))
+ return -EINVAL;
+
+ vfs_fsync_range(filp, start, end, 1);
+
+ pgstart = start >> PAGE_CACHE_SHIFT;
+ pgend = end >> PAGE_CACHE_SHIFT;
+ return invalidate_inode_pages2_range(filp->f_mapping, pgstart, pgend);
+}
+
static long nfs_ioctl_cachectl(struct file *filp, struct nfs_cachectl __user *argp)
{
u64 cmd;
@@ -70,6 +111,8 @@ static long nfs_ioctl_cachectl(struct file *filp, struct nfs_cachectl __user *ar
return nfs_ioctl_cache_revalidate(filp, true, false);
case NFS_CACHECTL_REVALIDATE_DATA:
return nfs_ioctl_cache_revalidate(filp, false, true);
+ case NFS_CACHECTL_REVALIDATE_RANGE:
+ return nfs_ioctl_revalidate_range(filp, argp);
}
return -EINVAL;
}
diff --git a/fs/nfs/ioctl.h b/fs/nfs/ioctl.h
index cf79f0d..6cf8337 100644
--- a/fs/nfs/ioctl.h
+++ b/fs/nfs/ioctl.h
@@ -27,9 +27,13 @@
#define NFS_CACHECTL_REVALIDATE_ALL 1
#define NFS_CACHECTL_REVALIDATE_METADATA 2
#define NFS_CACHECTL_REVALIDATE_DATA 3
+#define NFS_CACHECTL_REVALIDATE_RANGE 4

struct nfs_cachectl {
u64 cmd;
+
+ u64 start;
+ u64 len;
};

#define NFS_IOC_CACHECTL _IOW('N', 1, struct nfs_cachectl)
--
1.7.11.7


2012-11-19 17:03:05

by Peter Staubach

[permalink] [raw]
Subject: RE: [RFC PATCH 1/4] NFS: Add an ioctl to allow applications limited control over caching

Hi.

What application is having a problem which can be addressed via this support and what is the problem?

Thanx...

ps


-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Trond Myklebust
Sent: Thursday, November 15, 2012 9:31 PM
To: [email protected]
Subject: [RFC PATCH 1/4] NFS: Add an ioctl to allow applications limited control over caching

Add an ioctl that allows an application to force the NFS client to revalidate the attributes and/or data for a regular file, or directory.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/Makefile | 2 +-
fs/nfs/dir.c | 4 +++
fs/nfs/file.c | 4 +++
fs/nfs/internal.h | 3 ++
fs/nfs/ioctl.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/nfs/ioctl.h | 35 ++++++++++++++++++++++
fs/nfs/nfs4file.c | 4 +++
7 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 fs/nfs/ioctl.c create mode 100644 fs/nfs/ioctl.h

diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index b7db608..581db7a 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_NFS_FS) += nfs.o
nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
direct.o pagelist.o read.o symlink.o unlink.o \
write.o namespace.o mount_clnt.o \
- dns_resolve.o cache_lib.o
+ dns_resolve.o cache_lib.o ioctl.o
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
nfs-$(CONFIG_SYSCTL) += sysctl.o
nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index ce8cb92..10ad886 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -58,6 +58,10 @@ const struct file_operations nfs_dir_operations = {
.open = nfs_opendir,
.release = nfs_closedir,
.fsync = nfs_fsync_dir,
+ .unlocked_ioctl = nfs_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = nfs_ioctl,
+#endif
};

const struct address_space_operations nfs_dir_aops = { diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 582bb88..deefa62 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -925,5 +925,9 @@ const struct file_operations nfs_file_operations = {
.splice_write = nfs_file_splice_write,
.check_flags = nfs_check_flags,
.setlease = nfs_setlease,
+ .unlocked_ioctl = nfs_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = nfs_ioctl,
+#endif
};
EXPORT_SYMBOL_GPL(nfs_file_operations);
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index e1827b0..4b425bd 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -481,6 +481,9 @@ extern int nfs41_walk_client_list(struct nfs_client *clp,
struct nfs_client **result,
struct rpc_cred *cred);

+/* ioctl.c */
+extern long nfs_ioctl(struct file *filp, unsigned int cmd, unsigned
+long arg);
+
/*
* Determine the device name as a string
*/
diff --git a/fs/nfs/ioctl.c b/fs/nfs/ioctl.c new file mode 100644 index 0000000..9da5f7d
--- /dev/null
+++ b/fs/nfs/ioctl.c
@@ -0,0 +1,86 @@
+/*
+ * linux/fs/nfs/ioctl.c
+ *
+ * Copyright (C) 2012 Trond Myklebust <[email protected]>
+ *
+ * nfs ioctl implementation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/nfs_fs.h>
+#include "internal.h"
+#include "ioctl.h"
+
+static long nfs_ioctl_cache_revalidate(struct file *filp, bool
+metadata, bool data) {
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ struct nfs_inode *nfsi = NFS_I(inode);
+ umode_t mode = inode->i_mode;
+ unsigned long invalid = 0;
+ long ret = -EINVAL;
+
+ if (metadata)
+ invalid |= NFS_INO_INVALID_ATTR
+ | NFS_INO_INVALID_ACCESS
+ | NFS_INO_INVALID_ACL;
+ if (data)
+ invalid |= NFS_INO_INVALID_DATA;
+
+ switch (mode & S_IFMT) {
+ default:
+ goto out;
+ case S_IFDIR:
+ spin_lock(&inode->i_lock);
+ if (data)
+ nfsi->cache_change_attribute++;
+ nfsi->cache_validity |= invalid;
+ spin_unlock(&inode->i_lock);
+ break;
+ case S_IFREG:
+ vfs_fsync(filp, 1);
+ if (data)
+ invalid |= NFS_INO_REVAL_PAGECACHE;
+ case S_IFLNK:
+ spin_lock(&inode->i_lock);
+ nfsi->cache_validity |= invalid;
+ spin_unlock(&inode->i_lock);
+ }
+ ret = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ if (ret == 0)
+ ret = nfs_revalidate_mapping(inode, filp->f_mapping);
+
+out:
+ return ret;
+}
+
+static long nfs_ioctl_cachectl(struct file *filp, struct nfs_cachectl
+__user *argp) {
+ u64 cmd;
+
+ if (!(filp->f_mode & (FMODE_READ|FMODE_WRITE)))
+ return -EBADF;
+ if (get_user(cmd, &argp->cmd))
+ return -EFAULT;
+ switch (cmd) {
+ case NFS_CACHECTL_REVALIDATE_ALL:
+ return nfs_ioctl_cache_revalidate(filp, true, true);
+ case NFS_CACHECTL_REVALIDATE_METADATA:
+ return nfs_ioctl_cache_revalidate(filp, true, false);
+ case NFS_CACHECTL_REVALIDATE_DATA:
+ return nfs_ioctl_cache_revalidate(filp, false, true);
+ }
+ return -EINVAL;
+}
+
+long nfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case NFS_IOC_CACHECTL:
+ return nfs_ioctl_cachectl(filp, argp);
+ }
+ return -ENOTTY;
+}
diff --git a/fs/nfs/ioctl.h b/fs/nfs/ioctl.h new file mode 100644 index 0000000..cf79f0d
--- /dev/null
+++ b/fs/nfs/ioctl.h
@@ -0,0 +1,35 @@
+/*
+ * linux/fs/nfs/ioctl.h
+ *
+ * Copyright (C) 2012 Trond Myklebust <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ *
+ * nfs ioctl definitions
+ *
+ */
+
+#include <uapi/linux/ioctl.h>
+
+/* Cache revalidation modes */
+#define NFS_CACHECTL_REVALIDATE_ALL 1
+#define NFS_CACHECTL_REVALIDATE_METADATA 2
+#define NFS_CACHECTL_REVALIDATE_DATA 3
+
+struct nfs_cachectl {
+ u64 cmd;
+};
+
+#define NFS_IOC_CACHECTL _IOW('N', 1, struct nfs_cachectl)
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index e769930..b221690 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -133,4 +133,8 @@ const struct file_operations nfs4_file_operations = {
.splice_write = nfs_file_splice_write,
.check_flags = nfs_check_flags,
.setlease = nfs_setlease,
+ .unlocked_ioctl = nfs_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = nfs_ioctl,
+#endif
};
--
1.7.11.7