#include <linux/unistd.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/metrics.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/nfs4_mount.h>
#include <asm/system.h>
#include <asm/uaccess.h>
+#include "nfs4_fs.h"
+#include "callback.h"
#include "delegation.h"
+#include "iostat.h"
#define NFSDBG_FACILITY NFSDBG_VFS
#define NFS_PARANOIA 1
#define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1)
static void nfs_invalidate_inode(struct inode *);
-static int nfs_update_inode(struct inode *, struct nfs_fattr *, unsigned long);
+static int nfs_update_inode(struct inode *, struct nfs_fattr *);
static struct inode *nfs_alloc_inode(struct super_block *sb);
static void nfs_destroy_inode(struct inode *);
static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct super_block *, struct kstatfs *);
static int nfs_show_options(struct seq_file *, struct vfsmount *);
+static int nfs_show_stats(struct seq_file *, struct vfsmount *);
+static void nfs_zap_acl_cache(struct inode *);
static struct rpc_program nfs_program;
.clear_inode = nfs_clear_inode,
.umount_begin = nfs_umount_begin,
.show_options = nfs_show_options,
+ .show_stats = nfs_show_stats,
};
/*
static struct rpc_program nfs_program = {
.name = "nfs",
.number = NFS_PROGRAM,
- .nrvers = sizeof(nfs_version) / sizeof(nfs_version[0]),
+ .nrvers = ARRAY_SIZE(nfs_version),
.version = nfs_version,
.stats = &nfs_rpcstat,
.pipe_dir_name = "/nfs",
};
+#ifdef CONFIG_NFS_V3_ACL
+static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program };
+static struct rpc_version * nfsacl_version[] = {
+ [3] = &nfsacl_version3,
+};
+
+struct rpc_program nfsacl_program = {
+ .name = "nfsacl",
+ .number = NFS_ACL_PROGRAM,
+ .nrvers = ARRAY_SIZE(nfsacl_version),
+ .version = nfsacl_version,
+ .stats = &nfsacl_rpcstat,
+};
+#endif /* CONFIG_NFS_V3_ACL */
+
static inline unsigned long
nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
{
static int
nfs_write_inode(struct inode *inode, int sync)
{
- int flags = sync ? FLUSH_WAIT : 0;
+ int flags = sync ? FLUSH_SYNC : 0;
int ret;
- ret = nfs_commit_inode(inode, 0, 0, flags);
+ ret = nfs_commit_inode(inode, flags);
if (ret < 0)
return ret;
return 0;
{
dprintk("NFS: delete_inode(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
+ truncate_inode_pages(&inode->i_data, 0);
+
nfs_wb_all(inode);
/*
* The following should never happen...
clear_inode(inode);
}
-/*
- * For the moment, the only task for the NFS clear_inode method is to
- * release the mmap credential
- */
static void
nfs_clear_inode(struct inode *inode)
{
nfs_wb_all(inode);
BUG_ON (!list_empty(&nfsi->open_files));
+ nfs_zap_acl_cache(inode);
cred = nfsi->cache_access.cred;
if (cred)
put_rpccred(cred);
void
nfs_umount_begin(struct super_block *sb)
{
- struct nfs_server *server = NFS_SB(sb);
- struct rpc_clnt *rpc;
+ struct rpc_clnt *rpc = NFS_SB(sb)->client;
/* -EIO all pending I/O */
- if ((rpc = server->client) != NULL)
+ if (!IS_ERR(rpc))
+ rpc_killall_tasks(rpc);
+ rpc = NFS_SB(sb)->client_acl;
+ if (!IS_ERR(rpc))
rpc_killall_tasks(rpc);
}
static inline unsigned long
nfs_block_size(unsigned long bsize, unsigned char *nrbitsp)
{
- if (bsize < 1024)
- bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
- else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
- bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
+ if (bsize < NFS_MIN_FILE_IO_SIZE)
+ bsize = NFS_DEF_FILE_IO_SIZE;
+ else if (bsize >= NFS_MAX_FILE_IO_SIZE)
+ bsize = NFS_MAX_FILE_IO_SIZE;
return nfs_block_bits(bsize, nrbitsp);
}
nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *fsinfo)
{
struct nfs_server *server = NFS_SB(sb);
- struct inode *rooti;
int error;
error = server->rpc_ops->getroot(server, rootfh, fsinfo);
return ERR_PTR(error);
}
- rooti = nfs_fhget(sb, rootfh, fsinfo->fattr);
- if (!rooti)
- return ERR_PTR(-ENOMEM);
- return rooti;
+ return nfs_fhget(sb, rootfh, fsinfo->fattr);
}
/*
sb->s_magic = NFS_SUPER_MAGIC;
+ server->io_stats = nfs_alloc_iostats();
+ if (server->io_stats == NULL)
+ return -ENOMEM;
+
root_inode = nfs_get_root(sb, &server->fh, &fsinfo);
/* Did getting the root inode fail? */
if (IS_ERR(root_inode)) {
}
sb->s_root->d_op = server->rpc_ops->dentry_ops;
+ /* mount time stamp, in seconds */
+ server->mount_time = jiffies;
+
/* Get some general file system info */
if (server->namelen == 0 &&
server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0)
max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL);
if (server->rsize > max_rpc_payload)
server->rsize = max_rpc_payload;
- if (server->wsize > max_rpc_payload)
- server->wsize = max_rpc_payload;
-
+ if (server->rsize > NFS_MAX_FILE_IO_SIZE)
+ server->rsize = NFS_MAX_FILE_IO_SIZE;
server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
- if (server->rpages > NFS_READ_MAXIOV) {
- server->rpages = NFS_READ_MAXIOV;
- server->rsize = server->rpages << PAGE_CACHE_SHIFT;
- }
+ if (server->wsize > max_rpc_payload)
+ server->wsize = max_rpc_payload;
+ if (server->wsize > NFS_MAX_FILE_IO_SIZE)
+ server->wsize = NFS_MAX_FILE_IO_SIZE;
server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
- if (server->wpages > NFS_WRITE_MAXIOV) {
- server->wpages = NFS_WRITE_MAXIOV;
- server->wsize = server->wpages << PAGE_CACHE_SHIFT;
- }
if (sb->s_blocksize == 0)
sb->s_blocksize = nfs_block_bits(server->wsize,
return no_root_error;
}
+static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, unsigned int timeo, unsigned int retrans)
+{
+ to->to_initval = timeo * HZ / 10;
+ to->to_retries = retrans;
+ if (!to->to_retries)
+ to->to_retries = 2;
+
+ switch (proto) {
+ case IPPROTO_TCP:
+ if (!to->to_initval)
+ to->to_initval = 60 * HZ;
+ if (to->to_initval > NFS_MAX_TCP_TIMEOUT)
+ to->to_initval = NFS_MAX_TCP_TIMEOUT;
+ to->to_increment = to->to_initval;
+ to->to_maxval = to->to_initval + (to->to_increment * to->to_retries);
+ to->to_exponential = 0;
+ break;
+ case IPPROTO_UDP:
+ default:
+ if (!to->to_initval)
+ to->to_initval = 11 * HZ / 10;
+ if (to->to_initval > NFS_MAX_UDP_TIMEOUT)
+ to->to_initval = NFS_MAX_UDP_TIMEOUT;
+ to->to_maxval = NFS_MAX_UDP_TIMEOUT;
+ to->to_exponential = 1;
+ break;
+ }
+}
+
/*
* Create an RPC client handle.
*/
struct rpc_timeout timeparms;
struct rpc_xprt *xprt = NULL;
struct rpc_clnt *clnt = NULL;
- int tcp = (data->flags & NFS_MOUNT_TCP);
+ int proto = (data->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
- /* Initialize timeout values */
- timeparms.to_initval = data->timeo * HZ / 10;
- timeparms.to_retries = data->retrans;
- timeparms.to_maxval = tcp ? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
- timeparms.to_exponential = 1;
+ nfs_init_timeout_values(&timeparms, proto, data->timeo, data->retrans);
- if (!timeparms.to_initval)
- timeparms.to_initval = (tcp ? 600 : 11) * HZ / 10;
- if (!timeparms.to_retries)
- timeparms.to_retries = 5;
+ server->retrans_timeo = timeparms.to_initval;
+ server->retrans_count = timeparms.to_retries;
/* create transport and client */
- xprt = xprt_create_proto(tcp ? IPPROTO_TCP : IPPROTO_UDP,
- &server->addr, &timeparms);
+ xprt = xprt_create_proto(proto, &server->addr, &timeparms);
if (IS_ERR(xprt)) {
- printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
+ dprintk("%s: cannot create RPC transport. Error = %ld\n",
+ __FUNCTION__, PTR_ERR(xprt));
return (struct rpc_clnt *)xprt;
}
clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
server->rpc_ops->version, data->pseudoflavor);
if (IS_ERR(clnt)) {
- printk(KERN_WARNING "NFS: cannot create RPC client.\n");
+ dprintk("%s: cannot create RPC client. Error = %ld\n",
+ __FUNCTION__, PTR_ERR(xprt));
goto out_fail;
}
clnt->cl_intr = 1;
clnt->cl_softrtry = 1;
clnt->cl_tagxid = 1;
- clnt->cl_chatty = 1;
return clnt;
out_fail:
- xprt_destroy(xprt);
return clnt;
}
/* Check NFS protocol revision and initialize RPC op vector
* and file handle pool. */
- if (server->flags & NFS_MOUNT_VER3) {
#ifdef CONFIG_NFS_V3
+ if (server->flags & NFS_MOUNT_VER3) {
server->rpc_ops = &nfs_v3_clientops;
server->caps |= NFS_CAP_READDIRPLUS;
- if (data->version < 4) {
- printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n");
- return -EIO;
- }
-#else
- printk(KERN_NOTICE "NFS: NFSv3 not supported.\n");
- return -EIO;
-#endif
} else {
server->rpc_ops = &nfs_v2_clientops;
}
+#else
+ server->rpc_ops = &nfs_v2_clientops;
+#endif
/* Fill in pseudoflavor for mount version < 5 */
if (!(data->flags & NFS_MOUNT_SECFLAVOUR))
return PTR_ERR(server->client);
/* RFC 2623, sec 2.3.2 */
if (authflavor != RPC_AUTH_UNIX) {
+ struct rpc_auth *auth;
+
server->client_sys = rpc_clone_client(server->client);
if (IS_ERR(server->client_sys))
return PTR_ERR(server->client_sys);
- if (!rpcauth_create(RPC_AUTH_UNIX, server->client_sys))
- return -ENOMEM;
+ auth = rpcauth_create(RPC_AUTH_UNIX, server->client_sys);
+ if (IS_ERR(auth))
+ return PTR_ERR(auth);
} else {
atomic_inc(&server->client->cl_count);
server->client_sys = server->client;
}
-
if (server->flags & NFS_MOUNT_VER3) {
+#ifdef CONFIG_NFS_V3_ACL
+ if (!(server->flags & NFS_MOUNT_NOACL)) {
+ server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3);
+ /* No errors! Assume that Sun nfsacls are supported */
+ if (!IS_ERR(server->client_acl))
+ server->caps |= NFS_CAP_ACLS;
+ }
+#else
+ server->flags &= ~NFS_MOUNT_NOACL;
+#endif /* CONFIG_NFS_V3_ACL */
+ /*
+ * The VFS shouldn't apply the umask to mode bits. We will
+ * do so ourselves when necessary.
+ */
+ sb->s_flags |= MS_POSIXACL;
if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
server->namelen = NFS3_MAXNAMLEN;
sb->s_time_gran = 1;
buf->f_namelen = server->namelen;
out:
unlock_kernel();
-
return 0;
out_err:
- printk(KERN_WARNING "nfs_statfs: statfs error = %d\n", -error);
+ dprintk("%s: statfs error = %d\n", __FUNCTION__, -error);
buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1;
goto out;
}
-static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
+static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, int showdefaults)
{
static struct proc_nfs_info {
int flag;
} nfs_info[] = {
{ NFS_MOUNT_SOFT, ",soft", ",hard" },
{ NFS_MOUNT_INTR, ",intr", "" },
- { NFS_MOUNT_POSIX, ",posix", "" },
- { NFS_MOUNT_TCP, ",tcp", ",udp" },
{ NFS_MOUNT_NOCTO, ",nocto", "" },
{ NFS_MOUNT_NOAC, ",noac", "" },
- { NFS_MOUNT_NONLM, ",nolock", ",lock" },
+ { NFS_MOUNT_NONLM, ",nolock", "" },
+ { NFS_MOUNT_NOACL, ",noacl", "" },
{ NFS_MOUNT_TAGXID, ",tagxid", "" },
{ 0, NULL, NULL }
};
struct proc_nfs_info *nfs_infop;
- struct nfs_server *nfss = NFS_SB(mnt->mnt_sb);
+ char buf[12];
+ char *proto;
- seq_printf(m, ",v%d", nfss->rpc_ops->version);
+ seq_printf(m, ",vers=%d", nfss->rpc_ops->version);
seq_printf(m, ",rsize=%d", nfss->rsize);
seq_printf(m, ",wsize=%d", nfss->wsize);
- if (nfss->acregmin != 3*HZ)
+ if (nfss->acregmin != 3*HZ || showdefaults)
seq_printf(m, ",acregmin=%d", nfss->acregmin/HZ);
- if (nfss->acregmax != 60*HZ)
+ if (nfss->acregmax != 60*HZ || showdefaults)
seq_printf(m, ",acregmax=%d", nfss->acregmax/HZ);
- if (nfss->acdirmin != 30*HZ)
+ if (nfss->acdirmin != 30*HZ || showdefaults)
seq_printf(m, ",acdirmin=%d", nfss->acdirmin/HZ);
- if (nfss->acdirmax != 60*HZ)
+ if (nfss->acdirmax != 60*HZ || showdefaults)
seq_printf(m, ",acdirmax=%d", nfss->acdirmax/HZ);
for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) {
if (nfss->flags & nfs_infop->flag)
else
seq_puts(m, nfs_infop->nostr);
}
+ switch (nfss->client->cl_xprt->prot) {
+ case IPPROTO_TCP:
+ proto = "tcp";
+ break;
+ case IPPROTO_UDP:
+ proto = "udp";
+ break;
+ default:
+ snprintf(buf, sizeof(buf), "%u", nfss->client->cl_xprt->prot);
+ proto = buf;
+ }
+ seq_printf(m, ",proto=%s", proto);
+ seq_printf(m, ",timeo=%lu", 10U * nfss->retrans_timeo / HZ);
+ seq_printf(m, ",retrans=%u", nfss->retrans_count);
+}
+
+static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+ struct nfs_server *nfss = NFS_SB(mnt->mnt_sb);
+
+ nfs_show_mount_options(m, nfss, 0);
+
seq_puts(m, ",addr=");
seq_escape(m, nfss->hostname, " \t\n\\");
+
+ return 0;
+}
+
+static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
+{
+ int i, cpu;
+ struct nfs_server *nfss = NFS_SB(mnt->mnt_sb);
+ struct rpc_auth *auth = nfss->client->cl_auth;
+ struct nfs_iostats totals = { };
+
+ seq_printf(m, "statvers=%s", NFS_IOSTAT_VERS);
+
+ /*
+ * Display all mount option settings
+ */
+ seq_printf(m, "\n\topts:\t");
+ seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? "ro" : "rw");
+ seq_puts(m, mnt->mnt_sb->s_flags & MS_SYNCHRONOUS ? ",sync" : "");
+ seq_puts(m, mnt->mnt_sb->s_flags & MS_NOATIME ? ",noatime" : "");
+ seq_puts(m, mnt->mnt_sb->s_flags & MS_NODIRATIME ? ",nodiratime" : "");
+ nfs_show_mount_options(m, nfss, 1);
+
+ seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ);
+
+ seq_printf(m, "\n\tcaps:\t");
+ seq_printf(m, "caps=0x%x", nfss->caps);
+ seq_printf(m, ",wtmult=%d", nfss->wtmult);
+ seq_printf(m, ",dtsize=%d", nfss->dtsize);
+ seq_printf(m, ",bsize=%d", nfss->bsize);
+ seq_printf(m, ",namelen=%d", nfss->namelen);
+
+#ifdef CONFIG_NFS_V4
+ if (nfss->rpc_ops->version == 4) {
+ seq_printf(m, "\n\tnfsv4:\t");
+ seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
+ seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]);
+ seq_printf(m, ",acl=0x%x", nfss->acl_bitmask);
+ }
+#endif
+
+ /*
+ * Display security flavor in effect for this mount
+ */
+ seq_printf(m, "\n\tsec:\tflavor=%d", auth->au_ops->au_flavor);
+ if (auth->au_flavor)
+ seq_printf(m, ",pseudoflavor=%d", auth->au_flavor);
+
+ /*
+ * Display superblock I/O counters
+ */
+ for_each_possible_cpu(cpu) {
+ struct nfs_iostats *stats;
+
+ preempt_disable();
+ stats = per_cpu_ptr(nfss->io_stats, cpu);
+
+ for (i = 0; i < __NFSIOS_COUNTSMAX; i++)
+ totals.events[i] += stats->events[i];
+ for (i = 0; i < __NFSIOS_BYTESMAX; i++)
+ totals.bytes[i] += stats->bytes[i];
+
+ preempt_enable();
+ }
+
+ seq_printf(m, "\n\tevents:\t");
+ for (i = 0; i < __NFSIOS_COUNTSMAX; i++)
+ seq_printf(m, "%lu ", totals.events[i]);
+ seq_printf(m, "\n\tbytes:\t");
+ for (i = 0; i < __NFSIOS_BYTESMAX; i++)
+ seq_printf(m, "%Lu ", totals.bytes[i]);
+ seq_printf(m, "\n");
+
+ rpc_print_iostats(m, nfss->client);
+
return 0;
}
+/**
+ * nfs_sync_mapping - helper to flush all mmapped dirty data to disk
+ */
+int nfs_sync_mapping(struct address_space *mapping)
+{
+ int ret;
+
+ if (mapping->nrpages == 0)
+ return 0;
+ unmap_mapping_range(mapping, 0, 0, 0);
+ ret = filemap_write_and_wait(mapping);
+ if (ret != 0)
+ goto out;
+ ret = nfs_wb_all(mapping->host);
+out:
+ return ret;
+}
+
/*
* Invalidate the local caches
*/
-void
-nfs_zap_caches(struct inode *inode)
+static void nfs_zap_caches_locked(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
int mode = inode->i_mode;
+ nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
+
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
- nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS;
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
else
- nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
+}
+
+void nfs_zap_caches(struct inode *inode)
+{
+ spin_lock(&inode->i_lock);
+ nfs_zap_caches_locked(inode);
+ spin_unlock(&inode->i_lock);
+}
+
+static void nfs_zap_acl_cache(struct inode *inode)
+{
+ void (*clear_acl_cache)(struct inode *);
+
+ clear_acl_cache = NFS_PROTO(inode)->clear_acl_cache;
+ if (clear_acl_cache != NULL)
+ clear_acl_cache(inode);
+ spin_lock(&inode->i_lock);
+ NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ACL;
+ spin_unlock(&inode->i_lock);
}
/*
- * Invalidate, but do not unhash, the inode
+ * Invalidate, but do not unhash, the inode.
+ * NB: must be called with inode->i_lock held!
*/
-static void
-nfs_invalidate_inode(struct inode *inode)
+static void nfs_invalidate_inode(struct inode *inode)
{
- umode_t save_mode = inode->i_mode;
-
- make_bad_inode(inode);
- inode->i_mode = save_mode;
- nfs_zap_caches(inode);
+ set_bit(NFS_INO_STALE, &NFS_FLAGS(inode));
+ nfs_zap_caches_locked(inode);
}
struct nfs_find_desc {
.fh = fh,
.fattr = fattr
};
- struct inode *inode = NULL;
+ struct inode *inode = ERR_PTR(-ENOENT);
unsigned long hash;
if ((fattr->valid & NFS_ATTR_FATTR) == 0)
hash = nfs_fattr_to_ino_t(fattr);
- if (!(inode = iget5_locked(sb, hash, nfs_find_actor, nfs_init_locked, &desc)))
+ inode = iget5_locked(sb, hash, nfs_find_actor, nfs_init_locked, &desc);
+ if (inode == NULL) {
+ inode = ERR_PTR(-ENOMEM);
goto out_no_inode;
+ }
if (inode->i_state & I_NEW) {
struct nfs_inode *nfsi = NFS_I(inode);
/* Why so? Because we want revalidate for devices/FIFOs, and
* that's precisely what we have in nfs_file_inode_operations.
*/
- inode->i_op = &nfs_file_inode_operations;
+ inode->i_op = NFS_SB(sb)->rpc_ops->file_inode_ops;
if (S_ISREG(inode->i_mode)) {
inode->i_fop = &nfs_file_operations;
inode->i_data.a_ops = &nfs_file_aops;
inode->i_fop = &nfs_dir_operations;
if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)
&& fattr->size <= NFS_LIMIT_READDIRPLUS)
- NFS_FLAGS(inode) |= NFS_INO_ADVISE_RDPLUS;
+ set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode));
} else if (S_ISLNK(inode->i_mode))
inode->i_op = &nfs_symlink_inode_operations;
else
init_special_inode(inode, inode->i_mode, fattr->rdev);
- nfsi->read_cache_jiffies = fattr->timestamp;
+ nfsi->read_cache_jiffies = fattr->time_start;
+ nfsi->last_updated = jiffies;
inode->i_atime = fattr->atime;
inode->i_mtime = fattr->mtime;
inode->i_ctime = fattr->ctime;
return inode;
out_no_inode:
- printk("nfs_fhget: iget failed\n");
+ dprintk("nfs_fhget: iget failed with error %ld\n", PTR_ERR(inode));
goto out;
}
struct nfs_fattr fattr;
int error;
+ nfs_inc_stats(inode, NFSIOS_VFSSETATTR);
+
if (attr->ia_valid & ATTR_SIZE) {
if (!S_ISREG(inode->i_mode) || attr->ia_size == i_size_read(inode))
attr->ia_valid &= ~ATTR_SIZE;
lock_kernel();
nfs_begin_data_update(inode);
- /* Write all dirty data if we're changing file permissions or size */
- if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE)) != 0) {
- if (filemap_fdatawrite(inode->i_mapping) == 0)
- filemap_fdatawait(inode->i_mapping);
- nfs_wb_all(inode);
- }
+ /* Write all dirty data */
+ filemap_write_and_wait(inode->i_mapping);
+ nfs_wb_all(inode);
+ /*
+ * Return any delegations if we're going to change ACLs
+ */
+ if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
+ nfs_inode_return_delegation(inode);
error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
- if (error == 0) {
+ if (error == 0)
nfs_refresh_inode(inode, &fattr);
+ nfs_end_data_update(inode);
+ unlock_kernel();
+ return error;
+}
+
+/**
+ * nfs_setattr_update_inode - Update inode metadata after a setattr call.
+ * @inode: pointer to struct inode
+ * @attr: pointer to struct iattr
+ *
+ * Note: we do this in the *proc.c in order to ensure that
+ * it works for things like exclusive creates too.
+ */
+void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
+{
+ if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
if ((attr->ia_valid & ATTR_MODE) != 0) {
- int mode;
- mode = inode->i_mode & ~S_IALLUGO;
- mode |= attr->ia_mode & S_IALLUGO;
+ int mode = attr->ia_mode & S_IALLUGO;
+ mode |= inode->i_mode & ~S_IALLUGO;
inode->i_mode = mode;
}
if ((attr->ia_valid & ATTR_UID) != 0)
inode->i_uid = attr->ia_uid;
if ((attr->ia_valid & ATTR_GID) != 0)
inode->i_gid = attr->ia_gid;
- if ((attr->ia_valid & ATTR_XID) != 0)
+ if ((attr->ia_valid & ATTR_XID) && IS_TAGXID(inode))
inode->i_xid = attr->ia_xid;
- if ((attr->ia_valid & ATTR_SIZE) != 0) {
- inode->i_size = attr->ia_size;
- vmtruncate(inode, attr->ia_size);
- }
+ spin_lock(&inode->i_lock);
+ NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+ spin_unlock(&inode->i_lock);
+ }
+ if ((attr->ia_valid & ATTR_SIZE) != 0) {
+ nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
+ inode->i_size = attr->ia_size;
+ vmtruncate(inode, attr->ia_size);
}
- if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
- NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS;
- nfs_end_data_update(inode);
- unlock_kernel();
- return error;
+}
+
+static int nfs_wait_schedule(void *word)
+{
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ schedule();
+ return 0;
}
/*
* Wait for the inode to get unlocked.
- * (Used for NFS_INO_LOCKED and NFS_INO_REVALIDATING).
*/
-static int
-nfs_wait_on_inode(struct inode *inode, int flag)
+static int nfs_wait_on_inode(struct inode *inode)
{
struct rpc_clnt *clnt = NFS_CLIENT(inode);
struct nfs_inode *nfsi = NFS_I(inode);
-
+ sigset_t oldmask;
int error;
- if (!(NFS_FLAGS(inode) & flag))
- return 0;
- atomic_inc(&inode->i_count);
- error = nfs_wait_event(clnt, nfsi->nfs_i_wait,
- !(NFS_FLAGS(inode) & flag));
- iput(inode);
+
+ rpc_clnt_sigmask(clnt, &oldmask);
+ error = wait_on_bit_lock(&nfsi->flags, NFS_INO_REVALIDATING,
+ nfs_wait_schedule, TASK_INTERRUPTIBLE);
+ rpc_clnt_sigunmask(clnt, &oldmask);
+
return error;
}
+static void nfs_wake_up_inode(struct inode *inode)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+
+ clear_bit(NFS_INO_REVALIDATING, &nfsi->flags);
+ smp_mb__after_clear_bit();
+ wake_up_bit(&nfsi->flags, NFS_INO_REVALIDATING);
+}
+
int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{
struct inode *inode = dentry->d_inode;
- struct nfs_inode *nfsi = NFS_I(inode);
- int need_atime = nfsi->flags & NFS_INO_INVALID_ATIME;
+ int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
int err;
- if (__IS_FLG(inode, MS_NOATIME))
- need_atime = 0;
- else if (__IS_FLG(inode, MS_NODIRATIME) && S_ISDIR(inode->i_mode))
+ /* Flush out writes to the server in order to update c/mtime */
+ nfs_sync_inode_wait(inode, 0, 0, FLUSH_NOCOMMIT);
+
+ /*
+ * We may force a getattr if the user cares about atime.
+ *
+ * Note that we only have to check the vfsmount flags here:
+ * - NFS always sets S_NOATIME by so checking it would give a
+ * bogus result
+ * - NFS never sets MS_NOATIME or MS_NODIRATIME so there is
+ * no point in checking those.
+ */
+ if ((mnt->mnt_flags & MNT_NOATIME) ||
+ ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)))
need_atime = 0;
- /* We may force a getattr if the user cares about atime */
+
if (need_atime)
err = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
else
return err;
}
-struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rpc_cred *cred)
+static struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, struct dentry *dentry, struct rpc_cred *cred)
{
struct nfs_open_context *ctx;
if (ctx != NULL) {
atomic_set(&ctx->count, 1);
ctx->dentry = dget(dentry);
+ ctx->vfsmnt = mntget(mnt);
ctx->cred = get_rpccred(cred);
ctx->state = NULL;
ctx->lockowner = current->files;
ctx->error = 0;
- init_waitqueue_head(&ctx->waitq);
+ ctx->dir_cookie = 0;
}
return ctx;
}
if (ctx->cred != NULL)
put_rpccred(ctx->cred);
dput(ctx->dentry);
+ mntput(ctx->vfsmnt);
kfree(ctx);
}
}
* Ensure that mmap has a recent RPC credential for use when writing out
* shared pages
*/
-void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
+static void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
{
struct inode *inode = filp->f_dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode);
spin_unlock(&inode->i_lock);
}
-struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode)
+/*
+ * Given an inode, search for an open context with the desired characteristics
+ */
+struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *pos, *ctx = NULL;
spin_lock(&inode->i_lock);
list_for_each_entry(pos, &nfsi->open_files, list) {
+ if (cred != NULL && pos->cred != cred)
+ continue;
if ((pos->mode & mode) == mode) {
ctx = get_nfs_open_context(pos);
break;
return ctx;
}
-void nfs_file_clear_open_context(struct file *filp)
+static void nfs_file_clear_open_context(struct file *filp)
{
struct inode *inode = filp->f_dentry->d_inode;
struct nfs_open_context *ctx = (struct nfs_open_context *)filp->private_data;
cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
if (IS_ERR(cred))
return PTR_ERR(cred);
- ctx = alloc_nfs_open_context(filp->f_dentry, cred);
+ ctx = alloc_nfs_open_context(filp->f_vfsmnt, filp->f_dentry, cred);
put_rpccred(cred);
if (ctx == NULL)
return -ENOMEM;
ctx->mode = filp->f_mode;
nfs_file_set_open_context(filp, ctx);
put_nfs_open_context(ctx);
- if ((filp->f_mode & FMODE_WRITE) != 0)
- nfs_begin_data_update(inode);
return 0;
}
int nfs_release(struct inode *inode, struct file *filp)
{
- if ((filp->f_mode & FMODE_WRITE) != 0)
- nfs_end_data_update(inode);
nfs_file_clear_open_context(filp);
return 0;
}
int status = -ESTALE;
struct nfs_fattr fattr;
struct nfs_inode *nfsi = NFS_I(inode);
- unsigned long verifier;
- unsigned int flags;
dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n",
inode->i_sb->s_id, (long long)NFS_FILEID(inode));
if (NFS_STALE(inode))
goto out_nowait;
- while (NFS_REVALIDATING(inode)) {
- status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
- if (status < 0)
- goto out_nowait;
- if (NFS_ATTRTIMEO(inode) == 0)
- continue;
- if (NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ATIME))
- continue;
- status = NFS_STALE(inode) ? -ESTALE : 0;
- goto out_nowait;
+ status = nfs_wait_on_inode(inode);
+ if (status < 0)
+ goto out;
+ if (NFS_STALE(inode)) {
+ status = -ESTALE;
+ /* Do we trust the cached ESTALE? */
+ if (NFS_ATTRTIMEO(inode) != 0) {
+ if (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ATIME)) {
+ /* no */
+ } else
+ goto out;
+ }
}
- NFS_FLAGS(inode) |= NFS_INO_REVALIDATING;
- /* Protect against RPC races by saving the change attribute */
- verifier = nfs_save_change_attribute(inode);
status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), &fattr);
if (status != 0) {
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
if (status == -ESTALE) {
nfs_zap_caches(inode);
if (!S_ISDIR(inode->i_mode))
- NFS_FLAGS(inode) |= NFS_INO_STALE;
+ set_bit(NFS_INO_STALE, &NFS_FLAGS(inode));
}
goto out;
}
- status = nfs_update_inode(inode, &fattr, verifier);
+ spin_lock(&inode->i_lock);
+ status = nfs_update_inode(inode, &fattr);
if (status) {
+ spin_unlock(&inode->i_lock);
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode), status);
goto out;
}
- flags = nfsi->flags;
- /*
- * We may need to keep the attributes marked as invalid if
- * we raced with nfs_end_attr_update().
- */
- if (verifier == nfsi->cache_change_attribute)
- nfsi->flags &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);
- /* Do the page cache invalidation */
- if (flags & NFS_INO_INVALID_DATA) {
- if (S_ISREG(inode->i_mode)) {
- if (filemap_fdatawrite(inode->i_mapping) == 0)
- filemap_fdatawait(inode->i_mapping);
- nfs_wb_all(inode);
- }
- nfsi->flags &= ~NFS_INO_INVALID_DATA;
- invalidate_inode_pages2(inode->i_mapping);
- memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
- dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
- inode->i_sb->s_id,
- (long long)NFS_FILEID(inode));
- /* This ensures we revalidate dentries */
- nfsi->cache_change_attribute++;
- }
+ spin_unlock(&inode->i_lock);
+
+ nfs_revalidate_mapping(inode, inode->i_mapping);
+
+ if (nfsi->cache_validity & NFS_INO_INVALID_ACL)
+ nfs_zap_acl_cache(inode);
+
dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode));
-out:
- NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
- wake_up(&nfsi->nfs_i_wait);
+ out:
+ nfs_wake_up_inode(inode);
+
out_nowait:
unlock_kernel();
return status;
*/
int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{
- if (!(NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))
+ nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
+ if (!(NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))
&& !nfs_attribute_timeout(inode))
return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode);
}
+/**
+ * nfs_revalidate_mapping - Revalidate the pagecache
+ * @inode - pointer to host inode
+ * @mapping - pointer to mapping
+ */
+void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+
+ if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
+ nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
+ if (S_ISREG(inode->i_mode))
+ nfs_sync_mapping(mapping);
+ invalidate_inode_pages2(mapping);
+
+ spin_lock(&inode->i_lock);
+ nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
+ if (S_ISDIR(inode->i_mode)) {
+ memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
+ /* This ensures we revalidate child dentries */
+ nfsi->cache_change_attribute = jiffies;
+ }
+ spin_unlock(&inode->i_lock);
+
+ dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
+ inode->i_sb->s_id,
+ (long long)NFS_FILEID(inode));
+ }
+}
+
/**
* nfs_begin_data_update
* @inode - pointer to inode
struct nfs_inode *nfsi = NFS_I(inode);
if (!nfs_have_delegation(inode, FMODE_READ)) {
- /* Mark the attribute cache for revalidation */
- nfsi->flags |= NFS_INO_INVALID_ATTR;
- /* Directories and symlinks: invalidate page cache too */
- if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
- nfsi->flags |= NFS_INO_INVALID_DATA;
+ /* Directories and symlinks: invalidate page cache */
+ if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
+ spin_lock(&inode->i_lock);
+ nfsi->cache_validity |= NFS_INO_INVALID_DATA;
+ spin_unlock(&inode->i_lock);
+ }
}
- nfsi->cache_change_attribute ++;
+ nfsi->cache_change_attribute = jiffies;
atomic_dec(&nfsi->data_updates);
}
-/**
- * nfs_end_data_update_defer
- * @inode - pointer to inode
- * Declare end of the operations that will update file data
- * This will defer marking the inode as needing revalidation
- * unless there are no other pending updates.
- */
-void nfs_end_data_update_defer(struct inode *inode)
+static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
{
struct nfs_inode *nfsi = NFS_I(inode);
- if (atomic_dec_and_test(&nfsi->data_updates)) {
- /* Mark the attribute cache for revalidation */
- nfsi->flags |= NFS_INO_INVALID_ATTR;
- /* Directories and symlinks: invalidate page cache too */
- if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
- nfsi->flags |= NFS_INO_INVALID_DATA;
- nfsi->cache_change_attribute ++;
+ if ((fattr->valid & NFS_ATTR_PRE_CHANGE) != 0
+ && nfsi->change_attr == fattr->pre_change_attr) {
+ nfsi->change_attr = fattr->change_attr;
+ nfsi->cache_change_attribute = jiffies;
+ }
+
+ /* If we have atomic WCC data, we may update some attributes */
+ if ((fattr->valid & NFS_ATTR_WCC) != 0) {
+ if (timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) {
+ memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
+ nfsi->cache_change_attribute = jiffies;
+ }
+ if (timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) {
+ memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
+ nfsi->cache_change_attribute = jiffies;
+ }
+ if (inode->i_size == fattr->pre_size && nfsi->npages == 0) {
+ inode->i_size = fattr->size;
+ nfsi->cache_change_attribute = jiffies;
+ }
}
}
/**
- * nfs_refresh_inode - verify consistency of the inode attribute cache
+ * nfs_check_inode_attributes - verify consistency of the inode attribute cache
* @inode - pointer to inode
* @fattr - updated attributes
*
* so that fattr carries weak cache consistency data, then it may
* also update the ctime/mtime/change_attribute.
*/
-int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fattr)
{
struct nfs_inode *nfsi = NFS_I(inode);
loff_t cur_size, new_isize;
int data_unstable;
uid_t uid;
gid_t gid;
- xid_t xid = 0;
-
- /* Do we hold a delegation? */
- if (nfs_have_delegation(inode, FMODE_READ))
- return 0;
-
- /* Are we in the process of updating data on the server? */
- data_unstable = nfs_caches_unstable(inode);
+ xid_t xid;
- if (fattr->valid & NFS_ATTR_FATTR_V4) {
- if ((fattr->valid & NFS_ATTR_PRE_CHANGE) != 0
- && nfsi->change_attr == fattr->pre_change_attr)
- nfsi->change_attr = fattr->change_attr;
- if (!data_unstable && nfsi->change_attr != fattr->change_attr)
- nfsi->flags |= NFS_INO_INVALID_ATTR;
- }
if ((fattr->valid & NFS_ATTR_FATTR) == 0)
return 0;
/* Has the inode gone and changed behind our back? */
if (nfsi->fileid != fattr->fileid
- || (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
+ || (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
return -EIO;
+ }
- cur_size = i_size_read(inode);
- new_isize = nfs_size_to_loff_t(fattr->size);
+ /* Are we in the process of updating data on the server? */
+ data_unstable = nfs_caches_unstable(inode);
- /* If we have atomic WCC data, we may update some attributes */
- if ((fattr->valid & NFS_ATTR_WCC) != 0) {
- if (timespec_equal(&inode->i_ctime, &fattr->pre_ctime))
- memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
- if (timespec_equal(&inode->i_mtime, &fattr->pre_mtime))
- memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
+ /* Do atomic weak cache consistency updates */
+ nfs_wcc_update_inode(inode, fattr);
+
+ if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0) {
+ if (nfsi->change_attr == fattr->change_attr)
+ goto out;
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
+ if (!data_unstable)
+ nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE;
}
/* Verify a few of the more important attributes */
- if (!data_unstable) {
- if (!timespec_equal(&inode->i_mtime, &fattr->mtime)
- || cur_size != new_isize)
- nfsi->flags |= NFS_INO_INVALID_ATTR;
- } else if (S_ISREG(inode->i_mode) && new_isize > cur_size)
- nfsi->flags |= NFS_INO_INVALID_ATTR;
+ if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) {
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
+ if (!data_unstable)
+ nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE;
+ }
+
+ cur_size = i_size_read(inode);
+ new_isize = nfs_size_to_loff_t(fattr->size);
+ if (cur_size != new_isize && nfsi->npages == 0)
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
uid = INOXID_UID(XID_TAG(inode), fattr->uid, fattr->gid);
gid = INOXID_GID(XID_TAG(inode), fattr->uid, fattr->gid);
|| inode->i_uid != uid
|| inode->i_gid != gid
|| inode->i_xid != xid)
- nfsi->flags |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS;
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL;
/* Has the link count changed? */
if (inode->i_nlink != fattr->nlink)
- nfsi->flags |= NFS_INO_INVALID_ATTR;
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
+out:
if (!timespec_equal(&inode->i_atime, &fattr->atime))
- nfsi->flags |= NFS_INO_INVALID_ATIME;
+ nfsi->cache_validity |= NFS_INO_INVALID_ATIME;
- nfsi->read_cache_jiffies = fattr->timestamp;
+ nfsi->read_cache_jiffies = fattr->time_start;
return 0;
}
+/**
+ * nfs_refresh_inode - try to update the inode attribute cache
+ * @inode - pointer to inode
+ * @fattr - updated attributes
+ *
+ * Check that an RPC call that returned attributes has not overlapped with
+ * other recent updates of the inode metadata, then decide whether it is
+ * safe to do a full update of the inode attributes, or whether just to
+ * call nfs_check_inode_attributes.
+ */
+int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+ int status;
+
+ if ((fattr->valid & NFS_ATTR_FATTR) == 0)
+ return 0;
+ spin_lock(&inode->i_lock);
+ nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE;
+ if (time_after(fattr->time_start, nfsi->last_updated))
+ status = nfs_update_inode(inode, fattr);
+ else
+ status = nfs_check_inode_attributes(inode, fattr);
+
+ spin_unlock(&inode->i_lock);
+ return status;
+}
+
+/**
+ * nfs_post_op_update_inode - try to update the inode attribute cache
+ * @inode - pointer to inode
+ * @fattr - updated attributes
+ *
+ * After an operation that has changed the inode metadata, mark the
+ * attribute cache as being invalid, then try to update it.
+ */
+int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+ int status = 0;
+
+ spin_lock(&inode->i_lock);
+ if (unlikely((fattr->valid & NFS_ATTR_FATTR) == 0)) {
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS;
+ goto out;
+ }
+ status = nfs_update_inode(inode, fattr);
+out:
+ spin_unlock(&inode->i_lock);
+ return status;
+}
+
/*
* Many nfs protocol calls return the new file attributes after
* an operation. Here we update the inode to reflect the state
*
* A very similar scenario holds for the dir cache.
*/
-static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsigned long verifier)
+static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
{
struct nfs_inode *nfsi = NFS_I(inode);
- __u64 new_size;
- loff_t new_isize;
+ loff_t cur_isize, new_isize;
unsigned int invalid = 0;
- loff_t cur_isize;
- int data_unstable;
- uid_t uid;
- gid_t gid;
- xid_t xid = 0;
+ int data_stable;
+ uid_t uid;
+ gid_t gid;
+ xid_t xid;
dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n",
__FUNCTION__, inode->i_sb->s_id, inode->i_ino,
if ((fattr->valid & NFS_ATTR_FATTR) == 0)
return 0;
- if (nfsi->fileid != fattr->fileid) {
- printk(KERN_ERR "%s: inode number mismatch\n"
- "expected (%s/0x%Lx), got (%s/0x%Lx)\n",
- __FUNCTION__,
- inode->i_sb->s_id, (long long)nfsi->fileid,
- inode->i_sb->s_id, (long long)fattr->fileid);
- goto out_err;
- }
+ if (nfsi->fileid != fattr->fileid)
+ goto out_fileid;
/*
* Make sure the inode's type hasn't changed.
/*
* Update the read time so we don't revalidate too often.
*/
- nfsi->read_cache_jiffies = fattr->timestamp;
+ nfsi->read_cache_jiffies = fattr->time_start;
+ nfsi->last_updated = jiffies;
/* Are we racing with known updates of the metadata on the server? */
- data_unstable = ! nfs_verify_change_attribute(inode, verifier);
+ data_stable = nfs_verify_change_attribute(inode, fattr->time_start);
+ if (data_stable)
+ nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);
+
+ /* Do atomic weak cache consistency updates */
+ nfs_wcc_update_inode(inode, fattr);
- /* Check if the file size agrees */
- new_size = fattr->size;
+ /* Check if our cached file size is stale */
new_isize = nfs_size_to_loff_t(fattr->size);
cur_isize = i_size_read(inode);
- if (cur_isize != new_size) {
-#ifdef NFS_DEBUG_VERBOSE
- printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
-#endif
- /*
- * If we have pending writebacks, things can get
- * messy.
- */
- if (S_ISREG(inode->i_mode) && data_unstable) {
- if (new_isize > cur_isize) {
+ if (new_isize != cur_isize) {
+ /* Do we perhaps have any outstanding writes? */
+ if (nfsi->npages == 0) {
+ /* No, but did we race with nfs_end_data_update()? */
+ if (data_stable) {
inode->i_size = new_isize;
- invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
+ invalid |= NFS_INO_INVALID_DATA;
}
- } else {
+ invalid |= NFS_INO_INVALID_ATTR;
+ } else if (new_isize > cur_isize) {
inode->i_size = new_isize;
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
}
+ nfsi->cache_change_attribute = jiffies;
+ dprintk("NFS: isize change on server for file %s/%ld\n",
+ inode->i_sb->s_id, inode->i_ino);
}
- /*
- * Note: we don't check inode->i_mtime since pipes etc.
- * can change this value in VFS without requiring a
- * cache revalidation.
- */
+ /* Check if the mtime agrees */
if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) {
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
-#ifdef NFS_DEBUG_VERBOSE
- printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
-#endif
- if (!data_unstable)
- invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
+ dprintk("NFS: mtime change on server for file %s/%ld\n",
+ inode->i_sb->s_id, inode->i_ino);
+ invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
+ nfsi->cache_change_attribute = jiffies;
}
- if ((fattr->valid & NFS_ATTR_FATTR_V4)
- && nfsi->change_attr != fattr->change_attr) {
-#ifdef NFS_DEBUG_VERBOSE
- printk(KERN_DEBUG "NFS: change_attr change on %s/%ld\n",
- inode->i_sb->s_id, inode->i_ino);
-#endif
- nfsi->change_attr = fattr->change_attr;
- if (!data_unstable)
- invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS;
+ /* If ctime has changed we should definitely clear access+acl caches */
+ if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) {
+ invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+ memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
+ nfsi->cache_change_attribute = jiffies;
}
-
- memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
uid = INOXID_UID(XID_TAG(inode), fattr->uid, fattr->gid);
inode->i_uid != uid ||
inode->i_gid != gid ||
inode->i_xid != xid)
- invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
+ invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
inode->i_mode = fattr->mode;
inode->i_nlink = fattr->nlink;
inode->i_blksize = fattr->du.nfs2.blocksize;
}
+ if ((fattr->valid & NFS_ATTR_FATTR_V4)) {
+ if (nfsi->change_attr != fattr->change_attr) {
+ dprintk("NFS: change_attr change on server for file %s/%ld\n",
+ inode->i_sb->s_id, inode->i_ino);
+ nfsi->change_attr = fattr->change_attr;
+ invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+ nfsi->cache_change_attribute = jiffies;
+ } else
+ invalid &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA);
+ }
+
/* Update attrtimeo value if we're out of the unstable period */
if (invalid & NFS_INO_INVALID_ATTR) {
+ nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies;
} else if (time_after(jiffies, nfsi->attrtimeo_timestamp+nfsi->attrtimeo)) {
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
|| S_ISLNK(inode->i_mode)))
invalid &= ~NFS_INO_INVALID_DATA;
+ if (data_stable)
+ invalid &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE);
if (!nfs_have_delegation(inode, FMODE_READ))
- nfsi->flags |= invalid;
+ nfsi->cache_validity |= invalid;
return 0;
out_changed:
printk(KERN_DEBUG "%s: inode %ld mode changed, %07o to %07o\n",
__FUNCTION__, inode->i_ino, inode->i_mode, fattr->mode);
#endif
+ out_err:
/*
* No need to worry about unhashing the dentry, as the
* lookup validation will know that the inode is bad.
* (But we fall through to invalidate the caches.)
*/
nfs_invalidate_inode(inode);
- out_err:
- NFS_FLAGS(inode) |= NFS_INO_STALE;
return -ESTALE;
+
+ out_fileid:
+ printk(KERN_ERR "NFS: server %s error: fileid changed\n"
+ "fsid %s: expected fileid 0x%Lx, got 0x%Lx\n",
+ NFS_SERVER(inode)->hostname, inode->i_sb->s_id,
+ (long long)nfsi->fileid, (long long)fattr->fileid);
+ goto out_err;
}
/*
int flags, const char *dev_name, void *raw_data)
{
int error;
- struct nfs_server *server;
+ struct nfs_server *server = NULL;
struct super_block *s;
struct nfs_fh *root;
struct nfs_mount_data *data = raw_data;
- if (!data) {
- printk("nfs_read_super: missing data argument\n");
- return ERR_PTR(-EINVAL);
+ s = ERR_PTR(-EINVAL);
+ if (data == NULL) {
+ dprintk("%s: missing data argument\n", __FUNCTION__);
+ goto out_err;
}
-
- server = kmalloc(sizeof(struct nfs_server), GFP_KERNEL);
- if (!server)
- return ERR_PTR(-ENOMEM);
- memset(server, 0, sizeof(struct nfs_server));
- /* Zero out the NFS state stuff */
- init_nfsv4_state(server);
-
- if (data->version != NFS_MOUNT_VERSION) {
- printk("nfs warning: mount version %s than kernel\n",
- data->version < NFS_MOUNT_VERSION ? "older" : "newer");
- if (data->version < 2)
+ if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) {
+ dprintk("%s: bad mount version\n", __FUNCTION__);
+ goto out_err;
+ }
+ switch (data->version) {
+ case 1:
data->namlen = 0;
- if (data->version < 3)
+ case 2:
data->bsize = 0;
- if (data->version < 4) {
- data->flags &= ~NFS_MOUNT_VER3;
+ case 3:
+ if (data->flags & NFS_MOUNT_VER3) {
+ dprintk("%s: mount structure version %d does not support NFSv3\n",
+ __FUNCTION__,
+ data->version);
+ goto out_err;
+ }
data->root.size = NFS2_FHSIZE;
memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
- }
- if (data->version < 5)
- data->flags &= ~NFS_MOUNT_SECFLAVOUR;
+ case 4:
+ if (data->flags & NFS_MOUNT_SECFLAVOUR) {
+ dprintk("%s: mount structure version %d does not support strong security\n",
+ __FUNCTION__,
+ data->version);
+ goto out_err;
+ }
+ case 5:
+ memset(data->context, 0, sizeof(data->context));
+ }
+#ifndef CONFIG_NFS_V3
+ /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */
+ s = ERR_PTR(-EPROTONOSUPPORT);
+ if (data->flags & NFS_MOUNT_VER3) {
+ dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__);
+ goto out_err;
}
+#endif /* CONFIG_NFS_V3 */
+
+ s = ERR_PTR(-ENOMEM);
+ server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
+ if (!server)
+ goto out_err;
+ /* Zero out the NFS state stuff */
+ init_nfsv4_state(server);
+ server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
root = &server->fh;
if (data->flags & NFS_MOUNT_VER3)
root->size = data->root.size;
else
root->size = NFS2_FHSIZE;
+ s = ERR_PTR(-EINVAL);
if (root->size > sizeof(root->data)) {
- printk("nfs_get_sb: invalid root filehandle\n");
- kfree(server);
- return ERR_PTR(-EINVAL);
+ dprintk("%s: invalid root filehandle\n", __FUNCTION__);
+ goto out_err;
}
memcpy(root->data, data->root.data, root->size);
/* We now require that the mount process passes the remote address */
memcpy(&server->addr, &data->addr, sizeof(server->addr));
if (server->addr.sin_addr.s_addr == INADDR_ANY) {
- printk("NFS: mount program didn't pass remote address!\n");
- kfree(server);
- return ERR_PTR(-EINVAL);
+ dprintk("%s: mount program didn't pass remote address!\n",
+ __FUNCTION__);
+ goto out_err;
}
- s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
-
- if (IS_ERR(s) || s->s_root) {
- kfree(server);
- return s;
+ /* Fire up rpciod if not yet running */
+ s = ERR_PTR(rpciod_up());
+ if (IS_ERR(s)) {
+ dprintk("%s: couldn't start rpciod! Error = %ld\n",
+ __FUNCTION__, PTR_ERR(s));
+ goto out_err;
}
- s->s_flags = flags;
+ s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
+ if (IS_ERR(s) || s->s_root)
+ goto out_rpciod_down;
- /* Fire up rpciod if not yet running */
- if (rpciod_up() != 0) {
- printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
- kfree(server);
- return ERR_PTR(-EIO);
- }
+ s->s_flags = flags;
- error = nfs_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
+ error = nfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
if (error) {
up_write(&s->s_umount);
deactivate_super(s);
}
s->s_flags |= MS_ACTIVE;
return s;
+out_rpciod_down:
+ rpciod_down();
+out_err:
+ kfree(server);
+ return s;
}
static void nfs_kill_super(struct super_block *s)
kill_anon_super(s);
- if (server->client != NULL && !IS_ERR(server->client))
+ if (!IS_ERR(server->client))
rpc_shutdown_client(server->client);
- if (server->client_sys != NULL && !IS_ERR(server->client_sys))
+ if (!IS_ERR(server->client_sys))
rpc_shutdown_client(server->client_sys);
+ if (!IS_ERR(server->client_acl))
+ rpc_shutdown_client(server->client_acl);
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_down(); /* release rpc.lockd */
rpciod_down(); /* release rpciod */
- if (server->hostname != NULL)
- kfree(server->hostname);
+ nfs_free_iostats(server->io_stats);
+ kfree(server->hostname);
kfree(server);
}
.clear_inode = nfs4_clear_inode,
.umount_begin = nfs_umount_begin,
.show_options = nfs_show_options,
+ .show_stats = nfs_show_stats,
};
/*
struct nfs_inode *nfsi = NFS_I(inode);
/* If we are holding a delegation, return it! */
- if (nfsi->delegation != NULL)
- nfs_inode_return_delegation(inode);
+ nfs_inode_return_delegation(inode);
/* First call standard NFS clear_inode() code */
nfs_clear_inode(inode);
/* Now clear out any remaining state */
struct rpc_clnt *clnt = NULL;
struct rpc_timeout timeparms;
rpc_authflavor_t authflavour;
- int proto, err = -EIO;
+ int err = -EIO;
sb->s_blocksize_bits = 0;
sb->s_blocksize = 0;
server->acdirmax = data->acdirmax*HZ;
server->rpc_ops = &nfs_v4_clientops;
- /* Initialize timeout values */
- timeparms.to_initval = data->timeo * HZ / 10;
- timeparms.to_retries = data->retrans;
- timeparms.to_exponential = 1;
- if (!timeparms.to_retries)
- timeparms.to_retries = 5;
+ nfs_init_timeout_values(&timeparms, data->proto, data->timeo, data->retrans);
- proto = data->proto;
- /* Which IP protocol do we use? */
- switch (proto) {
- case IPPROTO_TCP:
- timeparms.to_maxval = RPC_MAX_TCP_TIMEOUT;
- if (!timeparms.to_initval)
- timeparms.to_initval = 600 * HZ / 10;
- break;
- case IPPROTO_UDP:
- timeparms.to_maxval = RPC_MAX_UDP_TIMEOUT;
- if (!timeparms.to_initval)
- timeparms.to_initval = 11 * HZ / 10;
- break;
- default:
- return -EINVAL;
- }
+ server->retrans_timeo = timeparms.to_initval;
+ server->retrans_count = timeparms.to_retries;
clp = nfs4_get_client(&server->addr.sin_addr);
if (!clp) {
- printk(KERN_WARNING "NFS: failed to create NFS4 client.\n");
+ dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__);
return -EIO;
}
/* Now create transport and client */
authflavour = RPC_AUTH_UNIX;
if (data->auth_flavourlen != 0) {
- if (data->auth_flavourlen > 1)
- printk(KERN_INFO "NFS: cannot yet deal with multiple auth flavours.\n");
+ if (data->auth_flavourlen != 1) {
+ dprintk("%s: Invalid number of RPC auth flavours %d.\n",
+ __FUNCTION__, data->auth_flavourlen);
+ err = -EINVAL;
+ goto out_fail;
+ }
if (copy_from_user(&authflavour, data->auth_flavours, sizeof(authflavour))) {
err = -EFAULT;
goto out_fail;
}
down_write(&clp->cl_sem);
- if (clp->cl_rpcclient == NULL) {
- xprt = xprt_create_proto(proto, &server->addr, &timeparms);
+ if (IS_ERR(clp->cl_rpcclient)) {
+ xprt = xprt_create_proto(data->proto, &server->addr, &timeparms);
if (IS_ERR(xprt)) {
up_write(&clp->cl_sem);
- printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
err = PTR_ERR(xprt);
+ dprintk("%s: cannot create RPC transport. Error = %d\n",
+ __FUNCTION__, err);
goto out_fail;
}
clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
server->rpc_ops->version, authflavour);
if (IS_ERR(clnt)) {
up_write(&clp->cl_sem);
- printk(KERN_WARNING "NFS: cannot create RPC client.\n");
- xprt_destroy(xprt);
err = PTR_ERR(clnt);
+ dprintk("%s: cannot create RPC client. Error = %d\n",
+ __FUNCTION__, err);
goto out_fail;
}
clnt->cl_intr = 1;
clnt->cl_softrtry = 1;
- clnt->cl_chatty = 1;
clp->cl_rpcclient = clnt;
- clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0);
- if (IS_ERR(clp->cl_cred)) {
- up_write(&clp->cl_sem);
- err = PTR_ERR(clp->cl_cred);
- clp->cl_cred = NULL;
- goto out_fail;
- }
memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr));
nfs_idmap_new(clp);
}
- if (list_empty(&clp->cl_superblocks)) {
- err = nfs4_init_client(clp);
- if (err != 0) {
- up_write(&clp->cl_sem);
- goto out_fail;
- }
- }
list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
clnt = rpc_clone_client(clp->cl_rpcclient);
if (!IS_ERR(clnt))
clp = NULL;
if (IS_ERR(clnt)) {
- printk(KERN_WARNING "NFS: cannot create RPC client.\n");
- return PTR_ERR(clnt);
+ err = PTR_ERR(clnt);
+ dprintk("%s: cannot create RPC client. Error = %d\n",
+ __FUNCTION__, err);
+ return err;
}
server->client = clnt;
if (server->nfs4_state->cl_idmap == NULL) {
- printk(KERN_WARNING "NFS: failed to create idmapper.\n");
+ dprintk("%s: failed to create idmapper.\n", __FUNCTION__);
return -ENOMEM;
}
if (clnt->cl_auth->au_flavor != authflavour) {
- if (rpcauth_create(authflavour, clnt) == NULL) {
- printk(KERN_WARNING "NFS: couldn't create credcache!\n");
- return -ENOMEM;
+ struct rpc_auth *auth;
+
+ auth = rpcauth_create(authflavour, clnt);
+ if (IS_ERR(auth)) {
+ dprintk("%s: couldn't create credcache!\n", __FUNCTION__);
+ return PTR_ERR(auth);
}
}
return ERR_PTR(-ENOMEM);
}
if (copy_from_user(dst, src->data, maxlen)) {
- if (p != NULL)
- kfree(p);
+ kfree(p);
return ERR_PTR(-EFAULT);
}
dst[maxlen] = '\0';
struct nfs4_mount_data *data = raw_data;
void *p;
- if (!data) {
- printk("nfs_read_super: missing data argument\n");
+ if (data == NULL) {
+ dprintk("%s: missing data argument\n", __FUNCTION__);
+ return ERR_PTR(-EINVAL);
+ }
+ if (data->version <= 0 || data->version > NFS4_MOUNT_VERSION) {
+ dprintk("%s: bad mount version\n", __FUNCTION__);
return ERR_PTR(-EINVAL);
}
- server = kmalloc(sizeof(struct nfs_server), GFP_KERNEL);
+ server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
if (!server)
return ERR_PTR(-ENOMEM);
- memset(server, 0, sizeof(struct nfs_server));
/* Zero out the NFS state stuff */
init_nfsv4_state(server);
-
- if (data->version != NFS4_MOUNT_VERSION) {
- printk("nfs warning: mount version %s than kernel\n",
- data->version < NFS4_MOUNT_VERSION ? "older" : "newer");
- }
+ server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
p = nfs_copy_user_string(NULL, &data->hostname, 256);
if (IS_ERR(p))
}
if (server->addr.sin_family != AF_INET ||
server->addr.sin_addr.s_addr == INADDR_ANY) {
- printk("NFS: mount program didn't pass remote IP address!\n");
+ dprintk("%s: mount program didn't pass remote IP address!\n",
+ __FUNCTION__);
s = ERR_PTR(-EINVAL);
goto out_free;
}
+ /* Fire up rpciod if not yet running */
+ s = ERR_PTR(rpciod_up());
+ if (IS_ERR(s)) {
+ dprintk("%s: couldn't start rpciod! Error = %ld\n",
+ __FUNCTION__, PTR_ERR(s));
+ goto out_free;
+ }
+
s = sget(fs_type, nfs4_compare_super, nfs_set_super, server);
if (IS_ERR(s) || s->s_root)
s->s_flags = flags;
- /* Fire up rpciod if not yet running */
- if (rpciod_up() != 0) {
- printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
- s = ERR_PTR(-EIO);
- goto out_free;
- }
-
- error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
+ error = nfs4_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
if (error) {
up_write(&s->s_umount);
deactivate_super(s);
out_err:
s = (struct super_block *)p;
out_free:
- if (server->mnt_path)
- kfree(server->mnt_path);
- if (server->hostname)
- kfree(server->hostname);
+ kfree(server->mnt_path);
+ kfree(server->hostname);
kfree(server);
return s;
}
if (server->client != NULL && !IS_ERR(server->client))
rpc_shutdown_client(server->client);
- rpciod_down(); /* release rpciod */
destroy_nfsv4_state(server);
- if (server->hostname != NULL)
- kfree(server->hostname);
+ rpciod_down();
+
+ nfs_free_iostats(server->io_stats);
+ kfree(server->hostname);
kfree(server);
}
.fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
+static const int nfs_set_port_min = 0;
+static const int nfs_set_port_max = 65535;
+static int param_set_port(const char *val, struct kernel_param *kp)
+{
+ char *endp;
+ int num = simple_strtol(val, &endp, 0);
+ if (endp == val || *endp || num < nfs_set_port_min || num > nfs_set_port_max)
+ return -EINVAL;
+ *((int *)kp->arg) = num;
+ return 0;
+}
+
+module_param_call(callback_tcpport, param_set_port, param_get_int,
+ &nfs_callback_set_tcpport, 0644);
+
+static int param_set_idmap_timeout(const char *val, struct kernel_param *kp)
+{
+ char *endp;
+ int num = simple_strtol(val, &endp, 0);
+ int jif = num * HZ;
+ if (endp == val || *endp || num < 0 || jif < num)
+ return -EINVAL;
+ *((int *)kp->arg) = jif;
+ return 0;
+}
+
+module_param_call(idmap_cache_timeout, param_set_idmap_timeout, param_get_int,
+ &nfs_idmap_cache_timeout, 0644);
+
#define nfs4_init_once(nfsi) \
do { \
INIT_LIST_HEAD(&(nfsi)->open_states); \
nfsi->delegation_state = 0; \
init_rwsem(&nfsi->rwsem); \
} while(0)
-#define register_nfs4fs() register_filesystem(&nfs4_fs_type)
-#define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type)
+
+static inline int register_nfs4fs(void)
+{
+ int ret;
+
+ ret = nfs_register_sysctl();
+ if (ret != 0)
+ return ret;
+ ret = register_filesystem(&nfs4_fs_type);
+ if (ret != 0)
+ nfs_unregister_sysctl();
+ return ret;
+}
+
+static inline void unregister_nfs4fs(void)
+{
+ unregister_filesystem(&nfs4_fs_type);
+ nfs_unregister_sysctl();
+}
#else
#define nfs4_init_once(nfsi) \
do { } while (0)
nfsi = (struct nfs_inode *)kmem_cache_alloc(nfs_inode_cachep, SLAB_KERNEL);
if (!nfsi)
return NULL;
- nfsi->flags = 0;
+ nfsi->flags = 0UL;
+ nfsi->cache_validity = 0UL;
+ nfsi->cache_change_attribute = jiffies;
+#ifdef CONFIG_NFS_V3_ACL
+ nfsi->acl_access = ERR_PTR(-EAGAIN);
+ nfsi->acl_default = ERR_PTR(-EAGAIN);
+#endif
+#ifdef CONFIG_NFS_V4
+ nfsi->nfs4_acl = NULL;
+#endif /* CONFIG_NFS_V4 */
return &nfsi->vfs_inode;
}
nfsi->ndirty = 0;
nfsi->ncommit = 0;
nfsi->npages = 0;
- init_waitqueue_head(&nfsi->nfs_i_wait);
nfs4_init_once(nfsi);
}
}
{
nfs_inode_cachep = kmem_cache_create("nfs_inode_cache",
sizeof(struct nfs_inode),
- 0, SLAB_RECLAIM_ACCOUNT,
+ 0, (SLAB_RECLAIM_ACCOUNT|
+ SLAB_MEM_SPREAD),
init_once, NULL);
if (nfs_inode_cachep == NULL)
return -ENOMEM;
#ifdef CONFIG_PROC_FS
rpc_proc_unregister("nfs");
#endif
- nfs_destroy_writepagecache();
#ifdef CONFIG_NFS_DIRECTIO
-out0:
nfs_destroy_directcache();
+out0:
#endif
+ nfs_destroy_writepagecache();
out1:
nfs_destroy_readpagecache();
out2: