diff options
Diffstat (limited to 'fs/nfs/inode.c')
| -rw-r--r-- | fs/nfs/inode.c | 202 | 
1 files changed, 131 insertions, 71 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index d4eadeea128..fc0f12ba89c 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -358,6 +358,35 @@ out_no_root:  	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.   */ @@ -367,22 +396,12 @@ nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data)  	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; - -	if (!timeparms.to_initval) -		timeparms.to_initval = (tcp ? 600 : 11) * HZ / 10; -	if (!timeparms.to_retries) -		timeparms.to_retries = 5; +	nfs_init_timeout_values(&timeparms, proto, data->timeo, data->retrans);  	/* 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)) {  		dprintk("%s: cannot create RPC transport. Error = %ld\n",  				__FUNCTION__, PTR_ERR(xprt)); @@ -576,7 +595,6 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)  		{ 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" }, @@ -585,6 +603,8 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)  	};  	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, ",rsize=%d", nfss->rsize); @@ -603,6 +623,18 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)  		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_puts(m, ",addr=");  	seq_escape(m, nfss->hostname, " \t\n\\");  	return 0; @@ -753,7 +785,8 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)  		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; @@ -821,6 +854,11 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)  			filemap_fdatawait(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)  		nfs_refresh_inode(inode, &fattr); @@ -1019,15 +1057,11 @@ int nfs_open(struct inode *inode, struct file *filp)  	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;  } @@ -1083,14 +1117,15 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)  		goto out;  	} +	spin_lock(&inode->i_lock);  	status = nfs_update_inode(inode, &fattr, verifier);  	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;  	} -	spin_lock(&inode->i_lock);  	cache_validity = nfsi->cache_validity;  	nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE; @@ -1098,7 +1133,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)  	 * We may need to keep the attributes marked as invalid if  	 * we raced with nfs_end_attr_update().  	 */ -	if (verifier == nfsi->cache_change_attribute) +	if (time_after_eq(verifier, nfsi->cache_change_attribute))  		nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);  	spin_unlock(&inode->i_lock); @@ -1165,7 +1200,7 @@ void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)  		if (S_ISDIR(inode->i_mode)) {  			memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));  			/* This ensures we revalidate child dentries */ -			nfsi->cache_change_attribute++; +			nfsi->cache_change_attribute = jiffies;  		}  		spin_unlock(&inode->i_lock); @@ -1197,20 +1232,19 @@ void nfs_end_data_update(struct inode *inode)  	struct nfs_inode *nfsi = NFS_I(inode);  	if (!nfs_have_delegation(inode, FMODE_READ)) { -		/* Mark the attribute cache for revalidation */ -		spin_lock(&inode->i_lock); -		nfsi->cache_validity |= NFS_INO_INVALID_ATTR; -		/* Directories and symlinks: invalidate page cache too */ -		if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) +		/* 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); +			spin_unlock(&inode->i_lock); +		}  	} -	nfsi->cache_change_attribute ++; +	nfsi->cache_change_attribute = jiffies;  	atomic_dec(&nfsi->data_updates);  }  /** - * 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   * @@ -1218,13 +1252,12 @@ void nfs_end_data_update(struct inode *inode)   * 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; -	spin_lock(&inode->i_lock);  	/* Are we in the process of updating data on the server? */  	data_unstable = nfs_caches_unstable(inode); @@ -1241,14 +1274,12 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)  	}  	if ((fattr->valid & NFS_ATTR_FATTR) == 0) { -		spin_unlock(&inode->i_lock);  		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)) { -		spin_unlock(&inode->i_lock);  		return -EIO;  	} @@ -1288,11 +1319,67 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)  	if (!timespec_equal(&inode->i_atime, &fattr->atime))  		nfsi->cache_validity |= NFS_INO_INVALID_ATIME; -	nfsi->read_cache_jiffies = fattr->timestamp; -	spin_unlock(&inode->i_lock); +	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 (nfs_verify_change_attribute(inode, fattr->time_start)) +		nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME); +	if (time_after(fattr->time_start, nfsi->last_updated)) +		status = nfs_update_inode(inode, fattr, fattr->time_start); +	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, fattr->time_start); +	if (time_after_eq(fattr->time_start, nfsi->cache_change_attribute)) +		nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE); +	nfsi->cache_change_attribute = jiffies; +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 @@ -1328,20 +1415,17 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign  		goto out_err;  	} -	spin_lock(&inode->i_lock); -  	/*  	 * Make sure the inode's type hasn't changed.  	 */ -	if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) { -		spin_unlock(&inode->i_lock); +	if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))  		goto out_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) || @@ -1354,7 +1438,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign  		/* Do we perhaps have any outstanding writes? */  		if (nfsi->npages == 0) {  			/* No, but did we race with nfs_end_data_update()? */ -			if (verifier  ==  nfsi->cache_change_attribute) { +			if (time_after_eq(verifier,  nfsi->cache_change_attribute)) {  				inode->i_size = new_isize;  				invalid |= NFS_INO_INVALID_DATA;  			} @@ -1430,7 +1514,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign  	if (!nfs_have_delegation(inode, FMODE_READ))  		nfsi->cache_validity |= invalid; -	spin_unlock(&inode->i_lock);  	return 0;   out_changed:  	/* @@ -1639,8 +1722,7 @@ static void nfs4_clear_inode(struct inode *inode)  	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 */ @@ -1669,7 +1751,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,  	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; @@ -1687,30 +1769,8 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,  	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; -	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; -	} +	nfs_init_timeout_values(&timeparms, data->proto, data->timeo, data->retrans);  	clp = nfs4_get_client(&server->addr.sin_addr);  	if (!clp) { @@ -1735,7 +1795,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,  	down_write(&clp->cl_sem);  	if (IS_ERR(clp->cl_rpcclient)) { -		xprt = xprt_create_proto(proto, &server->addr, &timeparms); +		xprt = xprt_create_proto(data->proto, &server->addr, &timeparms);  		if (IS_ERR(xprt)) {  			up_write(&clp->cl_sem);  			err = PTR_ERR(xprt);  | 
