diff options
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/client.c | 18 | ||||
-rw-r--r-- | fs/nfs/dir.c | 37 | ||||
-rw-r--r-- | fs/nfs/direct.c | 8 | ||||
-rw-r--r-- | fs/nfs/getroot.c | 11 | ||||
-rw-r--r-- | fs/nfs/inode.c | 50 | ||||
-rw-r--r-- | fs/nfs/internal.h | 4 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 24 | ||||
-rw-r--r-- | fs/nfs/nfs4_fs.h | 2 | ||||
-rw-r--r-- | fs/nfs/nfs4namespace.c | 16 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 60 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 2 | ||||
-rw-r--r-- | fs/nfs/proc.c | 30 | ||||
-rw-r--r-- | fs/nfs/read.c | 109 | ||||
-rw-r--r-- | fs/nfs/super.c | 2 | ||||
-rw-r--r-- | fs/nfs/write.c | 91 |
15 files changed, 130 insertions, 334 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c index ae9f36e..2190e6c 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -394,7 +394,8 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, static int nfs_create_rpc_client(struct nfs_client *clp, int proto, unsigned int timeo, unsigned int retrans, - rpc_authflavor_t flavor) + rpc_authflavor_t flavor, + int flags) { struct rpc_timeout timeparms; struct rpc_clnt *clnt = NULL; @@ -407,6 +408,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp, int proto, .program = &nfs_program, .version = clp->rpc_ops->version, .authflavor = flavor, + .flags = flags, }; if (!IS_ERR(clp->cl_rpcclient)) @@ -548,7 +550,7 @@ static int nfs_init_client(struct nfs_client *clp, const struct nfs_mount_data * * - RFC 2623, sec 2.3.2 */ error = nfs_create_rpc_client(clp, proto, data->timeo, data->retrans, - RPC_AUTH_UNIX); + RPC_AUTH_UNIX, 0); if (error < 0) goto error; nfs_mark_client_ready(clp, NFS_CS_READY); @@ -868,7 +870,8 @@ static int nfs4_init_client(struct nfs_client *clp, /* Check NFS protocol revision and initialize RPC op vector */ clp->rpc_ops = &nfs_v4_clientops; - error = nfs_create_rpc_client(clp, proto, timeo, retrans, authflavour); + error = nfs_create_rpc_client(clp, proto, timeo, retrans, authflavour, + RPC_CLNT_CREATE_DISCRTRY); if (error < 0) goto error; memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); @@ -1030,7 +1033,7 @@ error: * Create an NFS4 referral server record */ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, - struct nfs_fh *fh) + struct nfs_fh *mntfh) { struct nfs_client *parent_client; struct nfs_server *server, *parent_server; @@ -1069,8 +1072,13 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, BUG_ON(!server->nfs_client->rpc_ops); BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); + /* Probe the root fh to retrieve its FSID and filehandle */ + error = nfs4_path_walk(server, mntfh, data->mnt_path); + if (error < 0) + goto error; + /* probe the filesystem info for this server filesystem */ - error = nfs_probe_fsinfo(server, fh, &fattr); + error = nfs_probe_fsinfo(server, mntfh, &fattr); if (error < 0) goto error; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index f03a770..92d8ec8 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -637,7 +637,7 @@ int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync) * In the case it has, we assume that the dentries are untrustworthy * and may need to be looked up again. */ -static inline int nfs_check_verifier(struct inode *dir, struct dentry *dentry) +static int nfs_check_verifier(struct inode *dir, struct dentry *dentry) { if (IS_ROOT(dentry)) return 1; @@ -652,6 +652,12 @@ static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf) dentry->d_fsdata = (void *)verf; } +static void nfs_refresh_verifier(struct dentry * dentry, unsigned long verf) +{ + if (time_after(verf, (unsigned long)dentry->d_fsdata)) + nfs_set_verifier(dentry, verf); +} + /* * Whenever an NFS operation succeeds, we know that the dentry * is valid, so we update the revalidation timestamp. @@ -785,7 +791,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) goto out_bad; nfs_renew_times(dentry); - nfs_set_verifier(dentry, verifier); + nfs_refresh_verifier(dentry, verifier); out_valid: unlock_kernel(); dput(parent); @@ -1085,7 +1091,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) verifier = nfs_save_change_attribute(dir); ret = nfs4_open_revalidate(dir, dentry, openflags, nd); if (!ret) - nfs_set_verifier(dentry, verifier); + nfs_refresh_verifier(dentry, verifier); unlock_kernel(); out: dput(parent); @@ -1123,8 +1129,21 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc) } name.hash = full_name_hash(name.name, name.len); dentry = d_lookup(parent, &name); - if (dentry != NULL) - return dentry; + if (dentry != NULL) { + /* Is this a positive dentry that matches the readdir info? */ + if (dentry->d_inode != NULL && + (NFS_FILEID(dentry->d_inode) == entry->ino || + d_mountpoint(dentry))) { + if (!desc->plus || entry->fh->size == 0) + return dentry; + if (nfs_compare_fh(NFS_FH(dentry->d_inode), + entry->fh) == 0) + goto out_renew; + } + /* No, so d_drop to allow one to be created */ + d_drop(dentry); + dput(dentry); + } if (!desc->plus || !(entry->fattr->valid & NFS_ATTR_FATTR)) return NULL; /* Note: caller is already holding the dir->i_mutex! */ @@ -1149,6 +1168,10 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc) nfs_renew_times(dentry); nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); return dentry; +out_renew: + nfs_renew_times(dentry); + nfs_refresh_verifier(dentry, nfs_save_change_attribute(dir)); + return dentry; } /* @@ -1443,6 +1466,8 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) if (atomic_read(&dentry->d_count) > 1) { spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); + /* Start asynchronous writeout of the inode */ + write_inode_now(dentry->d_inode, 0); error = nfs_sillyrename(dir, dentry); unlock_kernel(); return error; @@ -1684,7 +1709,7 @@ out: if (!error) { d_move(old_dentry, new_dentry); nfs_renew_times(new_dentry); - nfs_set_verifier(new_dentry, nfs_save_change_attribute(new_dir)); + nfs_refresh_verifier(new_dentry, nfs_save_change_attribute(new_dir)); } /* new dentry created? */ diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index bd21d7f..b1c98ea 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -309,7 +309,8 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo rpc_execute(&data->task); - dfprintk(VFS, "NFS: %5u initiated direct read call (req %s/%Ld, %zu bytes @ offset %Lu)\n", + dprintk("NFS: %5u initiated direct read call " + "(req %s/%Ld, %zu bytes @ offset %Lu)\n", data->task.tk_pid, inode->i_sb->s_id, (long long)NFS_FILEID(inode), @@ -639,7 +640,8 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l rpc_execute(&data->task); - dfprintk(VFS, "NFS: %5u initiated direct write call (req %s/%Ld, %zu bytes @ offset %Lu)\n", + dprintk("NFS: %5u initiated direct write call " + "(req %s/%Ld, %zu bytes @ offset %Lu)\n", data->task.tk_pid, inode->i_sb->s_id, (long long)NFS_FILEID(inode), @@ -797,7 +799,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, const char __user *buf = iov[0].iov_base; size_t count = iov[0].iov_len; - dfprintk(VFS, "nfs: direct write(%s/%s, %lu@%Ld)\n", + dprintk("nfs: direct write(%s/%s, %lu@%Ld)\n", file->f_path.dentry->d_parent->d_name.name, file->f_path.dentry->d_name.name, (unsigned long) count, (long long) pos); diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c index 8391bd7..6ef268f 100644 --- a/fs/nfs/getroot.c +++ b/fs/nfs/getroot.c @@ -135,17 +135,15 @@ int nfs4_path_walk(struct nfs_server *server, struct nfs_fh lastfh; struct qstr name; int ret; - //int referral_count = 0; dprintk("--> nfs4_path_walk(,,%s)\n", path); fsinfo.fattr = &fattr; nfs_fattr_init(&fattr); - if (*path++ != '/') { - dprintk("nfs4_get_root: Path does not begin with a slash\n"); - return -EINVAL; - } + /* Eat leading slashes */ + while (*path == '/') + path++; /* Start by getting the root filehandle from the server */ ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); @@ -160,6 +158,7 @@ int nfs4_path_walk(struct nfs_server *server, return -ENOTDIR; } + /* FIXME: It is quite valid for the server to return a referral here */ if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { printk(KERN_ERR "nfs4_get_root:" " getroot obtained referral\n"); @@ -187,6 +186,7 @@ eat_dot_dir: goto eat_dot_dir; } + /* FIXME: Why shouldn't the user be able to use ".." in the path? */ if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2]) ) { printk(KERN_ERR "nfs4_get_root:" @@ -212,6 +212,7 @@ eat_dot_dir: return -ENOTDIR; } + /* FIXME: Referrals are quite valid here too */ if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { printk(KERN_ERR "nfs4_get_root:" " lookupfh obtained referral\n"); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index d834982..af53c02 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -65,13 +65,18 @@ nfs_fattr_to_ino_t(struct nfs_fattr *fattr) int nfs_write_inode(struct inode *inode, int sync) { - int flags = sync ? FLUSH_SYNC : 0; int ret; - ret = nfs_commit_inode(inode, flags); - if (ret < 0) - return ret; - return 0; + if (sync) { + ret = filemap_fdatawait(inode->i_mapping); + if (ret == 0) + ret = nfs_commit_inode(inode, FLUSH_SYNC); + } else + ret = nfs_commit_inode(inode, 0); + if (ret >= 0) + return 0; + __mark_inode_dirty(inode, I_DIRTY_DATASYNC); + return ret; } void nfs_clear_inode(struct inode *inode) @@ -235,6 +240,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) if (inode->i_state & I_NEW) { struct nfs_inode *nfsi = NFS_I(inode); + unsigned long now = jiffies; /* We set i_ino for the few things that still rely on it, * such as stat(2) */ @@ -271,7 +277,8 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) init_special_inode(inode, inode->i_mode, fattr->rdev); nfsi->read_cache_jiffies = fattr->time_start; - nfsi->last_updated = jiffies; + nfsi->last_updated = now; + nfsi->cache_change_attribute = now; inode->i_atime = fattr->atime; inode->i_mtime = fattr->mtime; inode->i_ctime = fattr->ctime; @@ -290,7 +297,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) inode->i_blocks = fattr->du.nfs2.blocks; } nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); - nfsi->attrtimeo_timestamp = jiffies; + nfsi->attrtimeo_timestamp = now; memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); nfsi->access_cache = RB_ROOT; @@ -783,20 +790,21 @@ void nfs_end_data_update(struct inode *inode) static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr) { struct nfs_inode *nfsi = NFS_I(inode); + unsigned long now = 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; + nfsi->cache_change_attribute = now; } if (timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) { memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); - nfsi->cache_change_attribute = jiffies; + nfsi->cache_change_attribute = now; } if (inode->i_size == fattr->pre_size && nfsi->npages == 0) { inode->i_size = fattr->size; - nfsi->cache_change_attribute = jiffies; + nfsi->cache_change_attribute = now; } } } @@ -934,6 +942,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) struct nfs_inode *nfsi = NFS_I(inode); loff_t cur_isize, new_isize; unsigned int invalid = 0; + unsigned long now = jiffies; int data_stable; dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n", @@ -959,7 +968,11 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) * Update the read time so we don't revalidate too often. */ nfsi->read_cache_jiffies = fattr->time_start; - nfsi->last_updated = jiffies; + nfsi->last_updated = now; + + /* Fix a wraparound issue with nfsi->cache_change_attribute */ + if (time_before(now, nfsi->cache_change_attribute)) + nfsi->cache_change_attribute = now - 600*HZ; /* Are we racing with known updates of the metadata on the server? */ data_stable = nfs_verify_change_attribute(inode, fattr->time_start); @@ -985,7 +998,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) inode->i_size = new_isize; invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; } - nfsi->cache_change_attribute = jiffies; + nfsi->cache_change_attribute = now; dprintk("NFS: isize change on server for file %s/%ld\n", inode->i_sb->s_id, inode->i_ino); } @@ -996,14 +1009,14 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) 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; + nfsi->cache_change_attribute = now; } /* 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; + nfsi->cache_change_attribute = now; } memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime)); @@ -1032,18 +1045,18 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) 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; + nfsi->cache_change_attribute = now; } /* 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)) { + nfsi->attrtimeo_timestamp = now; + } else if (time_after(now, nfsi->attrtimeo_timestamp+nfsi->attrtimeo)) { if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode)) nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode); - nfsi->attrtimeo_timestamp = jiffies; + nfsi->attrtimeo_timestamp = now; } /* Don't invalidate the data if we were to blame */ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) @@ -1122,7 +1135,6 @@ struct inode *nfs_alloc_inode(struct super_block *sb) return NULL; 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); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index a28f6ce..6610f2b 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -107,10 +107,6 @@ extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus); /* nfs4proc.c */ #ifdef CONFIG_NFS_V4 extern struct rpc_procinfo nfs4_procedures[]; - -extern int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry, - struct nfs4_fs_locations *fs_locations, - struct page *page); #endif /* dir.c */ diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index acd8fe9..7d0371e 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -253,29 +253,6 @@ static int nfs3_proc_readlink(struct inode *inode, struct page *page, return status; } -static int nfs3_proc_read(struct nfs_read_data *rdata) -{ - int flags = rdata->flags; - struct inode * inode = rdata->inode; - struct nfs_fattr * fattr = rdata->res.fattr; - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_READ], - .rpc_argp = &rdata->args, - .rpc_resp = &rdata->res, - .rpc_cred = rdata->cred, - }; - int status; - - dprintk("NFS call read %d @ %Ld\n", rdata->args.count, - (long long) rdata->args.offset); - nfs_fattr_init(fattr); - status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); - if (status >= 0) - nfs_refresh_inode(inode, fattr); - dprintk("NFS reply read: %d\n", status); - return status; -} - /* * Create a regular file. * For now, we don't implement O_EXCL. @@ -855,7 +832,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .lookup = nfs3_proc_lookup, .access = nfs3_proc_access, .readlink = nfs3_proc_readlink, - .read = nfs3_proc_read, .create = nfs3_proc_create, .remove = nfs3_proc_remove, .unlink_setup = nfs3_proc_unlink_setup, diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index e234176..cf3a17e 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -169,7 +169,7 @@ extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state); extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); -extern int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry, +extern int nfs4_proc_fs_locations(struct inode *dir, struct qstr *name, struct nfs4_fs_locations *fs_locations, struct page *page); extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index b872779..dd5fef2 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c @@ -16,6 +16,7 @@ #include <linux/vfs.h> #include <linux/inet.h> #include "internal.h" +#include "nfs4_fs.h" #define NFSDBG_FACILITY NFSDBG_VFS @@ -130,7 +131,6 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, .authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor, }; char *page = NULL, *page2 = NULL; - char *devname; int loc, s, error; if (locations == NULL || locations->nlocations <= 0) @@ -154,12 +154,6 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, goto out; } - devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); - if (IS_ERR(devname)) { - mnt = (struct vfsmount *)devname; - goto out; - } - loc = 0; while (loc < locations->nlocations && IS_ERR(mnt)) { const struct nfs4_fs_location *location = &locations->locations[loc]; @@ -194,7 +188,11 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, addr.sin_port = htons(NFS_PORT); mountdata.addr = &addr; - mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, devname, &mountdata); + snprintf(page, PAGE_SIZE, "%s:%s", + mountdata.hostname, + mountdata.mnt_path); + + mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, page, &mountdata); if (!IS_ERR(mnt)) { break; } @@ -242,7 +240,7 @@ struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentr dprintk("%s: getting locations for %s/%s\n", __FUNCTION__, parent->d_name.name, dentry->d_name.name); - err = nfs4_proc_fs_locations(parent->d_inode, dentry, fs_locations, page); + err = nfs4_proc_fs_locations(parent->d_inode, &dentry->d_name, fs_locations, page); dput(parent); if (err != 0 || fs_locations->nlocations <= 0 || diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 1daee65..f52cf5c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1140,7 +1140,6 @@ static void nfs4_close_done(struct rpc_task *task, void *data) break; case -NFS4ERR_STALE_STATEID: case -NFS4ERR_EXPIRED: - nfs4_schedule_state_recovery(server->nfs_client); break; default: if (nfs4_async_handle_error(task, server) == -EAGAIN) { @@ -1424,7 +1423,6 @@ static int nfs4_get_referral(struct inode *dir, struct qstr *name, struct nfs_fa int status = -ENOMEM; struct page *page = NULL; struct nfs4_fs_locations *locations = NULL; - struct dentry dentry = {}; page = alloc_page(GFP_KERNEL); if (page == NULL) @@ -1433,9 +1431,7 @@ static int nfs4_get_referral(struct inode *dir, struct qstr *name, struct nfs_fa if (locations == NULL) goto out; - dentry.d_name.name = name->name; - dentry.d_name.len = name->len; - status = nfs4_proc_fs_locations(dir, &dentry, locations, page); + status = nfs4_proc_fs_locations(dir, name, locations, page); if (status != 0) goto out; /* Make sure server returned a different fsid for the referral */ @@ -1737,44 +1733,6 @@ static int nfs4_proc_readlink(struct inode *inode, struct page *page, return err; } -static int _nfs4_proc_read(struct nfs_read_data *rdata) -{ - int flags = rdata->flags; - struct inode *inode = rdata->inode; - struct nfs_fattr *fattr = rdata->res.fattr; - struct nfs_server *server = NFS_SERVER(inode); - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ], - .rpc_argp = &rdata->args, - .rpc_resp = &rdata->res, - .rpc_cred = rdata->cred, - }; - unsigned long timestamp = jiffies; - int status; - - dprintk("NFS call read %d @ %Ld\n", rdata->args.count, - (long long) rdata->args.offset); - - nfs_fattr_init(fattr); - status = rpc_call_sync(server->client, &msg, flags); - if (!status) - renew_lease(server, timestamp); - dprintk("NFS reply read: %d\n", status); - return status; -} - -static int nfs4_proc_read(struct nfs_read_data *rdata) -{ - struct nfs4_exception exception = { }; - int err; - do { - err = nfs4_handle_exception(NFS_SERVER(rdata->inode), - _nfs4_proc_read(rdata), - &exception); - } while (exception.retry); - return err; -} - /* * Got race? * We will need to arrange for the VFS layer to provide an atomic open. @@ -2753,11 +2711,15 @@ static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp) might_sleep(); + rwsem_acquire(&clp->cl_sem.dep_map, 0, 0, _RET_IP_); + rpc_clnt_sigmask(clnt, &oldset); res = wait_on_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER, nfs4_wait_bit_interruptible, TASK_INTERRUPTIBLE); rpc_clnt_sigunmask(clnt, &oldset); + + rwsem_release(&clp->cl_sem.dep_map, 1, _RET_IP_); return res; } @@ -2996,7 +2958,6 @@ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4 switch (err) { case -NFS4ERR_STALE_STATEID: case -NFS4ERR_EXPIRED: - nfs4_schedule_state_recovery(server->nfs_client); case 0: return 0; } @@ -3150,12 +3111,10 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) break; case -NFS4ERR_STALE_STATEID: case -NFS4ERR_EXPIRED: - nfs4_schedule_state_recovery(calldata->server->nfs_client); break; default: - if (nfs4_async_handle_error(task, calldata->server) == -EAGAIN) { + if (nfs4_async_handle_error(task, calldata->server) == -EAGAIN) rpc_restart_call(task); - } } } @@ -3585,7 +3544,7 @@ ssize_t nfs4_listxattr(struct dentry *dentry, char *buf, size_t buflen) return len; } -int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry, +int nfs4_proc_fs_locations(struct inode *dir, struct qstr *name, struct nfs4_fs_locations *fs_locations, struct page *page) { struct nfs_server *server = NFS_SERVER(dir); @@ -3595,7 +3554,7 @@ int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry, }; struct nfs4_fs_locations_arg args = { .dir_fh = NFS_FH(dir), - .name = &dentry->d_name, + .name = name, .page = page, .bitmask = bitmask, }; @@ -3607,7 +3566,7 @@ int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry, int status; dprintk("%s: start\n", __FUNCTION__); - fs_locations->fattr.valid = 0; + nfs_fattr_init(&fs_locations->fattr); fs_locations->server = server; fs_locations->nlocations = 0; status = rpc_call_sync(server->client, &msg, 0); @@ -3646,7 +3605,6 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .lookup = nfs4_proc_lookup, .access = nfs4_proc_access, .readlink = nfs4_proc_readlink, - .read = nfs4_proc_read, .create = nfs4_proc_create, .remove = nfs4_proc_remove, .unlink_setup = nfs4_proc_unlink_setup, diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 0cf3fa3..f02d522 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -387,8 +387,10 @@ static int nfs4_stat_to_errno(int); decode_putfh_maxsz + \ op_decode_hdr_maxsz + 12) #define NFS4_enc_server_caps_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_server_caps_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_delegreturn_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 560536a..1dcf56d 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -186,35 +186,6 @@ static int nfs_proc_readlink(struct inode *inode, struct page *page, return status; } -static int nfs_proc_read(struct nfs_read_data *rdata) -{ - int flags = rdata->flags; - struct inode * inode = rdata->inode; - struct nfs_fattr * fattr = rdata->res.fattr; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_READ], - .rpc_argp = &rdata->args, - .rpc_resp = &rdata->res, - .rpc_cred = rdata->cred, - }; - int status; - - dprintk("NFS call read %d @ %Ld\n", rdata->args.count, - (long long) rdata->args.offset); - nfs_fattr_init(fattr); - status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); - if (status >= 0) { - nfs_refresh_inode(inode, fattr); - /* Emulate the eof flag, which isn't normally needed in NFSv2 - * as it is guaranteed to always return the file attributes - */ - if (rdata->args.offset + rdata->args.count >= fattr->size) - rdata->res.eof = 1; - } - dprintk("NFS reply read: %d\n", status); - return status; -} - static int nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, int flags, struct nameidata *nd) @@ -666,7 +637,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .lookup = nfs_proc_lookup, .access = NULL, /* access */ .readlink = nfs_proc_readlink, - .read = nfs_proc_read, .create = nfs_proc_create, .remove = nfs_proc_remove, .unlink_setup = nfs_proc_unlink_setup, diff --git a/fs/nfs/read.c b/fs/nfs/read.c index a9c2652..6ab4d5a 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -5,14 +5,6 @@ * * Partial copy of Linus' read cache modifications to fs/nfs/file.c * modified for async RPC by okir@monad.swb.de - * - * We do an ugly hack here in order to return proper error codes to the - * user program when a read request failed: since generic_file_read - * only checks the return value of inode->i_op->readpage() which is always 0 - * for async RPC, we set the error bit of the page to 1 when an error occurs, - * and make nfs_readpage transmit requests synchronously when encountering this. - * This is only a small problem, though, since we now retry all operations - * within the RPC code when root squashing is suspected. */ #include <linux/time.h> @@ -122,93 +114,6 @@ static void nfs_readpage_truncate_uninitialised_page(struct nfs_read_data *data) } } -/* - * Read a page synchronously. - */ -static int nfs_readpage_sync(struct nfs_open_context *ctx, struct inode *inode, - struct page *page) -{ - unsigned int rsize = NFS_SERVER(inode)->rsize; - unsigned int count = PAGE_CACHE_SIZE; - int result = -ENOMEM; - struct nfs_read_data *rdata; - - rdata = nfs_readdata_alloc(count); - if (!rdata) - goto out_unlock; - - memset(rdata, 0, sizeof(*rdata)); - rdata->flags = (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); - rdata->cred = ctx->cred; - rdata->inode = inode; - INIT_LIST_HEAD(&rdata->pages); - rdata->args.fh = NFS_FH(inode); - rdata->args.context = ctx; - rdata->args.pages = &page; - rdata->args.pgbase = 0UL; - rdata->args.count = rsize; - rdata->res.fattr = &rdata->fattr; - - dprintk("NFS: nfs_readpage_sync(%p)\n", page); - - /* - * This works now because the socket layer never tries to DMA - * into this buffer directly. - */ - do { - if (count < rsize) - rdata->args.count = count; - rdata->res.count = rdata->args.count; - rdata->args.offset = page_offset(page) + rdata->args.pgbase; - - dprintk("NFS: nfs_proc_read(%s, (%s/%Ld), %Lu, %u)\n", - NFS_SERVER(inode)->nfs_client->cl_hostname, - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), - (unsigned long long)rdata->args.pgbase, - rdata->args.count); - - lock_kernel(); - result = NFS_PROTO(inode)->read(rdata); - unlock_kernel(); - - /* - * Even if we had a partial success we can't mark the page - * cache valid. - */ - if (result < 0) { - if (result == -EISDIR) - result = -EINVAL; - goto io_error; - } - count -= result; - rdata->args.pgbase += result; - nfs_add_stats(inode, NFSIOS_SERVERREADBYTES, result); - - /* Note: result == 0 should only happen if we're caching - * a write that extends the file and punches a hole. - */ - if (rdata->res.eof != 0 || result == 0) - break; - } while (count); - spin_lock(&inode->i_lock); - NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME; - spin_unlock(&inode->i_lock); - - if (rdata->res.eof || rdata->res.count == rdata->args.count) { - SetPageUptodate(page); - if (rdata->res.eof && count != 0) - memclear_highpage_flush(page, rdata->args.pgbase, count); - } - result = 0; - -io_error: - nfs_readdata_free(rdata); -out_unlock: - unlock_page(page); - return result; -} - static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, struct page *page) { @@ -278,7 +183,7 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, data->task.tk_cookie = (unsigned long)inode; - dprintk("NFS: %4d initiated read call (req %s/%Ld, %u bytes @ offset %Lu)\n", + dprintk("NFS: %5u initiated read call (req %s/%Ld, %u bytes @ offset %Lu)\n", data->task.tk_pid, inode->i_sb->s_id, (long long)NFS_FILEID(inode), @@ -452,7 +357,7 @@ int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data) { int status; - dprintk("%s: %4d, (status %d)\n", __FUNCTION__, task->tk_pid, + dprintk("NFS: %s: %5u, (status %d)\n", __FUNCTION__, task->tk_pid, task->tk_status); status = NFS_PROTO(data->inode)->read_done(task, data); @@ -621,15 +526,9 @@ int nfs_readpage(struct file *file, struct page *page) } else ctx = get_nfs_open_context((struct nfs_open_context *) file->private_data); - if (!IS_SYNC(inode)) { - error = nfs_readpage_async(ctx, inode, page); - goto out; - } - error = nfs_readpage_sync(ctx, inode, page); - if (error < 0 && IS_SWAPFILE(inode)) - printk("Aiee.. nfs swap-in of page failed!\n"); -out: + error = nfs_readpage_async(ctx, inode, page); + put_nfs_open_context(ctx); return error; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index baa2886..bb516a2 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1045,7 +1045,7 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, nfs4_fill_super(s); } - mntroot = nfs4_get_root(s, data->fh); + mntroot = nfs4_get_root(s, &mntfh); if (IS_ERR(mntroot)) { error = PTR_ERR(mntroot); goto error_splat_super; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 345492e..febdade 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1,47 +1,7 @@ /* * linux/fs/nfs/write.c * - * Writing file data over NFS. - * - * We do it like this: When a (user) process wishes to write data to an - * NFS file, a write request is allocated that contains the RPC task data - * plus some info on the page to be written, and added to the inode's - * write chain. If the process writes past the end of the page, an async - * RPC call to write the page is scheduled immediately; otherwise, the call - * is delayed for a few seconds. - * - * Just like readahead, no async I/O is performed if wsize < PAGE_SIZE. - * - * Write requests are kept on the inode's writeback list. Each entry in - * that list references the page (portion) to be written. When the - * cache timeout has expired, the RPC task is woken up, and tries to - * lock the page. As soon as it manages to do so, the request is moved - * from the writeback list to the writelock list. - * - * Note: we must make sure never to confuse the inode passed in the - * write_page request with the one in page->inode. As far as I understand - * it, these are different when doing a swap-out. - * - * To understand everything that goes on here and in the NFS read code, - * one should be aware that a page is locked in exactly one of the following - * cases: - * - * - A write request is in progress. - * - A user process is in generic_file_write/nfs_update_page - * - A user process is in generic_file_read - * - * Also note that because of the way pages are invalidated in - * nfs_revalidate_inode, the following assertions hold: - * - * - If a page is dirty, there will be no read requests (a page will - * not be re-read unless invalidated by nfs_revalidate_inode). - * - If the page is not uptodate, there will be no pending write - * requests, and no process will be in nfs_update_page. - * - * FIXME: Interaction with the vmscan routines is not optimal yet. - * Either vmscan must be made nfs-savvy, or we need a different page - * reclaim concept that supports something like FS-independent - * buffer_heads with a b_ops-> field. + * Write file data over NFS. * * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de> */ @@ -79,7 +39,6 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context*, unsigned int, unsigned int); static void nfs_mark_request_dirty(struct nfs_page *req); static int nfs_wait_on_write_congestion(struct address_space *, int); -static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int); static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); static const struct rpc_call_ops nfs_write_partial_ops; static const struct rpc_call_ops nfs_write_full_ops; @@ -194,6 +153,13 @@ static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int c i_size_write(inode, end); } +/* A writeback failed: mark the page as bad, and invalidate the page cache */ +static void nfs_set_pageerror(struct page *page) +{ + SetPageError(page); + nfs_zap_mapping(page->mapping->host, page->mapping); +} + /* We can set the PG_uptodate flag if we see that a write request * covers the full page. */ @@ -323,7 +289,7 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc err = 0; out: if (!wbc->for_writepages) - nfs_flush_mapping(page->mapping, wbc, wb_priority(wbc)); + nfs_flush_mapping(page->mapping, wbc, FLUSH_STABLE|wb_priority(wbc)); return err; } @@ -360,14 +326,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) if (err < 0) goto out; nfs_add_stats(inode, NFSIOS_WRITEPAGES, err); - if (!wbc->nonblocking && wbc->sync_mode == WB_SYNC_ALL) { - err = nfs_wait_on_requests(inode, 0, 0); - if (err < 0) - goto out; - } - err = nfs_commit_inode(inode, wb_priority(wbc)); - if (err > 0) - err = 0; + err = 0; out: clear_bit(BDI_write_congested, &bdi->state); wake_up_all(&nfs_write_congestion); @@ -516,17 +475,6 @@ static int nfs_wait_on_requests_locked(struct inode *inode, unsigned long idx_st return res; } -static int nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int npages) -{ - struct nfs_inode *nfsi = NFS_I(inode); - int ret; - - spin_lock(&nfsi->req_lock); - ret = nfs_wait_on_requests_locked(inode, idx_start, npages); - spin_unlock(&nfsi->req_lock); - return ret; -} - static void nfs_cancel_dirty_list(struct list_head *head) { struct nfs_page *req; @@ -773,7 +721,7 @@ int nfs_updatepage(struct file *file, struct page *page, dprintk("NFS: nfs_updatepage returns %d (isize %Ld)\n", status, (long long)i_size_read(inode)); if (status < 0) - ClearPageUptodate(page); + nfs_set_pageerror(page); return status; } @@ -852,7 +800,8 @@ static void nfs_write_rpcsetup(struct nfs_page *req, data->task.tk_priority = flush_task_priority(how); data->task.tk_cookie = (unsigned long)inode; - dprintk("NFS: %4d initiated write call (req %s/%Ld, %u bytes @ offset %Lu)\n", + dprintk("NFS: %5u initiated write call " + "(req %s/%Ld, %u bytes @ offset %Lu)\n", data->task.tk_pid, inode->i_sb->s_id, (long long)NFS_FILEID(inode), @@ -1034,8 +983,7 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) return; if (task->tk_status < 0) { - ClearPageUptodate(page); - SetPageError(page); + nfs_set_pageerror(page); req->wb_context->error = task->tk_status; dprintk(", error = %d\n", task->tk_status); } else { @@ -1092,8 +1040,7 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata) (long long)req_offset(req)); if (task->tk_status < 0) { - ClearPageUptodate(page); - SetPageError(page); + nfs_set_pageerror(page); req->wb_context->error = task->tk_status; end_page_writeback(page); nfs_inode_remove_request(req); @@ -1134,7 +1081,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) struct nfs_writeres *resp = &data->res; int status; - dprintk("NFS: %4d nfs_writeback_done (status %d)\n", + dprintk("NFS: %5u nfs_writeback_done (status %d)\n", task->tk_pid, task->tk_status); /* @@ -1250,7 +1197,7 @@ static void nfs_commit_rpcsetup(struct list_head *head, data->task.tk_priority = flush_task_priority(how); data->task.tk_cookie = (unsigned long)inode; - dprintk("NFS: %4d initiated commit call\n", data->task.tk_pid); + dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid); } /* @@ -1291,7 +1238,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata) struct nfs_write_data *data = calldata; struct nfs_page *req; - dprintk("NFS: %4d nfs_commit_done (status %d)\n", + dprintk("NFS: %5u nfs_commit_done (status %d)\n", task->tk_pid, task->tk_status); /* Call the NFS version-specific code */ @@ -1516,6 +1463,8 @@ int nfs_wb_page_priority(struct inode *inode, struct page *page, int how) if (ret < 0) goto out; } + if (!PagePrivate(page)) + return 0; ret = nfs_sync_mapping_wait(page->mapping, &wbc, how); if (ret >= 0) return 0; |