Commit 9e8312f5 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-5.3-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client bugfixes from Trond Myklebust:
 "Highlights include:

  Stable fixes:

   - Fix a page lock leak in nfs_pageio_resend()

   - Ensure O_DIRECT reports an error if the bytes read/written is 0

   - Don't handle errors if the bind/connect succeeded

   - Revert "NFSv4/flexfiles: Abort I/O early if the layout segment was
     invalidat ed"

  Bugfixes:

   - Don't refresh attributes with mounted-on-file information

   - Fix return values for nfs4_file_open() and nfs_finish_open()

   - Fix pnfs layoutstats reporting of I/O errors

   - Don't use soft RPC calls for pNFS/flexfiles I/O, and don't abort
     for soft I/O errors when the user specifies a hard mount.

   - Various fixes to the error handling in sunrpc

   - Don't report writepage()/writepages() errors twice"

* tag 'nfs-for-5.3-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS: remove set but not used variable 'mapping'
  NFSv2: Fix write regression
  NFSv2: Fix eof handling
  NFS: Fix writepage(s) error handling to not report errors twice
  NFS: Fix spurious EIO read errors
  pNFS/flexfiles: Don't time out requests on hard mounts
  SUNRPC: Handle connection breakages correctly in call_status()
  Revert "NFSv4/flexfiles: Abort I/O early if the layout segment was invalidated"
  SUNRPC: Handle EADDRINUSE and ENOBUFS correctly
  pNFS/flexfiles: Turn off soft RPC calls
  SUNRPC: Don't handle errors if the bind/connect succeeded
  NFS: On fatal writeback errors, we need to call nfs_inode_remove_request()
  NFS: Fix initialisation of I/O result struct in nfs_pgio_rpcsetup
  NFS: Ensure O_DIRECT reports an error if the bytes read/written is 0
  NFSv4/pnfs: Fix a page lock leak in nfs_pageio_resend()
  NFSv4: Fix return value in nfs_finish_open()
  NFSv4: Fix return values for nfs4_file_open()
  NFS: Don't refresh attributes with mounted-on-file information
parents 6525771f 99300a85
...@@ -1487,7 +1487,7 @@ static int nfs_finish_open(struct nfs_open_context *ctx, ...@@ -1487,7 +1487,7 @@ static int nfs_finish_open(struct nfs_open_context *ctx,
if (S_ISREG(file->f_path.dentry->d_inode->i_mode)) if (S_ISREG(file->f_path.dentry->d_inode->i_mode))
nfs_file_set_open_context(file, ctx); nfs_file_set_open_context(file, ctx);
else else
err = -ESTALE; err = -EOPENSTALE;
out: out:
return err; return err;
} }
......
...@@ -401,15 +401,21 @@ static void nfs_direct_read_completion(struct nfs_pgio_header *hdr) ...@@ -401,15 +401,21 @@ static void nfs_direct_read_completion(struct nfs_pgio_header *hdr)
unsigned long bytes = 0; unsigned long bytes = 0;
struct nfs_direct_req *dreq = hdr->dreq; struct nfs_direct_req *dreq = hdr->dreq;
if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
goto out_put;
spin_lock(&dreq->lock); spin_lock(&dreq->lock);
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) && (hdr->good_bytes == 0)) if (test_bit(NFS_IOHDR_ERROR, &hdr->flags))
dreq->error = hdr->error; dreq->error = hdr->error;
else
if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) {
spin_unlock(&dreq->lock);
goto out_put;
}
if (hdr->good_bytes != 0)
nfs_direct_good_bytes(dreq, hdr); nfs_direct_good_bytes(dreq, hdr);
if (test_bit(NFS_IOHDR_EOF, &hdr->flags))
dreq->error = 0;
spin_unlock(&dreq->lock); spin_unlock(&dreq->lock);
while (!list_empty(&hdr->pages)) { while (!list_empty(&hdr->pages)) {
...@@ -782,16 +788,19 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) ...@@ -782,16 +788,19 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
bool request_commit = false; bool request_commit = false;
struct nfs_page *req = nfs_list_entry(hdr->pages.next); struct nfs_page *req = nfs_list_entry(hdr->pages.next);
if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
goto out_put;
nfs_init_cinfo_from_dreq(&cinfo, dreq); nfs_init_cinfo_from_dreq(&cinfo, dreq);
spin_lock(&dreq->lock); spin_lock(&dreq->lock);
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) if (test_bit(NFS_IOHDR_ERROR, &hdr->flags))
dreq->error = hdr->error; dreq->error = hdr->error;
if (dreq->error == 0) {
if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) {
spin_unlock(&dreq->lock);
goto out_put;
}
if (hdr->good_bytes != 0) {
nfs_direct_good_bytes(dreq, hdr); nfs_direct_good_bytes(dreq, hdr);
if (nfs_write_need_commit(hdr)) { if (nfs_write_need_commit(hdr)) {
if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES) if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES)
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
*/ */
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/nfs_page.h> #include <linux/nfs_page.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched/mm.h> #include <linux/sched/mm.h>
...@@ -928,6 +929,8 @@ ff_layout_pg_init_read(struct nfs_pageio_descriptor *pgio, ...@@ -928,6 +929,8 @@ ff_layout_pg_init_read(struct nfs_pageio_descriptor *pgio,
pgm = &pgio->pg_mirrors[0]; pgm = &pgio->pg_mirrors[0];
pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].rsize; pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].rsize;
if (NFS_SERVER(pgio->pg_inode)->flags &
(NFS_MOUNT_SOFT|NFS_MOUNT_SOFTERR))
pgio->pg_maxretrans = io_maxretrans; pgio->pg_maxretrans = io_maxretrans;
return; return;
out_nolseg: out_nolseg:
...@@ -940,6 +943,7 @@ ff_layout_pg_init_read(struct nfs_pageio_descriptor *pgio, ...@@ -940,6 +943,7 @@ ff_layout_pg_init_read(struct nfs_pageio_descriptor *pgio,
pgio->pg_lseg); pgio->pg_lseg);
pnfs_put_lseg(pgio->pg_lseg); pnfs_put_lseg(pgio->pg_lseg);
pgio->pg_lseg = NULL; pgio->pg_lseg = NULL;
pgio->pg_maxretrans = 0;
nfs_pageio_reset_read_mds(pgio); nfs_pageio_reset_read_mds(pgio);
} }
...@@ -1000,6 +1004,8 @@ ff_layout_pg_init_write(struct nfs_pageio_descriptor *pgio, ...@@ -1000,6 +1004,8 @@ ff_layout_pg_init_write(struct nfs_pageio_descriptor *pgio,
pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].wsize; pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].wsize;
} }
if (NFS_SERVER(pgio->pg_inode)->flags &
(NFS_MOUNT_SOFT|NFS_MOUNT_SOFTERR))
pgio->pg_maxretrans = io_maxretrans; pgio->pg_maxretrans = io_maxretrans;
return; return;
...@@ -1010,6 +1016,7 @@ ff_layout_pg_init_write(struct nfs_pageio_descriptor *pgio, ...@@ -1010,6 +1016,7 @@ ff_layout_pg_init_write(struct nfs_pageio_descriptor *pgio,
pgio->pg_lseg); pgio->pg_lseg);
pnfs_put_lseg(pgio->pg_lseg); pnfs_put_lseg(pgio->pg_lseg);
pgio->pg_lseg = NULL; pgio->pg_lseg = NULL;
pgio->pg_maxretrans = 0;
nfs_pageio_reset_write_mds(pgio); nfs_pageio_reset_write_mds(pgio);
} }
...@@ -1148,8 +1155,6 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task, ...@@ -1148,8 +1155,6 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task,
break; break;
case -NFS4ERR_RETRY_UNCACHED_REP: case -NFS4ERR_RETRY_UNCACHED_REP:
break; break;
case -EAGAIN:
return -NFS4ERR_RESET_TO_PNFS;
/* Invalidate Layout errors */ /* Invalidate Layout errors */
case -NFS4ERR_PNFS_NO_LAYOUT: case -NFS4ERR_PNFS_NO_LAYOUT:
case -ESTALE: /* mapped NFS4ERR_STALE */ case -ESTALE: /* mapped NFS4ERR_STALE */
...@@ -1210,7 +1215,6 @@ static int ff_layout_async_handle_error_v3(struct rpc_task *task, ...@@ -1210,7 +1215,6 @@ static int ff_layout_async_handle_error_v3(struct rpc_task *task,
case -EBADHANDLE: case -EBADHANDLE:
case -ELOOP: case -ELOOP:
case -ENOSPC: case -ENOSPC:
case -EAGAIN:
break; break;
case -EJUKEBOX: case -EJUKEBOX:
nfs_inc_stats(lseg->pls_layout->plh_inode, NFSIOS_DELAY); nfs_inc_stats(lseg->pls_layout->plh_inode, NFSIOS_DELAY);
...@@ -1445,16 +1449,6 @@ static void ff_layout_read_prepare_v4(struct rpc_task *task, void *data) ...@@ -1445,16 +1449,6 @@ static void ff_layout_read_prepare_v4(struct rpc_task *task, void *data)
ff_layout_read_prepare_common(task, hdr); ff_layout_read_prepare_common(task, hdr);
} }
static void
ff_layout_io_prepare_transmit(struct rpc_task *task,
void *data)
{
struct nfs_pgio_header *hdr = data;
if (!pnfs_is_valid_lseg(hdr->lseg))
rpc_exit(task, -EAGAIN);
}
static void ff_layout_read_call_done(struct rpc_task *task, void *data) static void ff_layout_read_call_done(struct rpc_task *task, void *data)
{ {
struct nfs_pgio_header *hdr = data; struct nfs_pgio_header *hdr = data;
...@@ -1740,7 +1734,6 @@ static void ff_layout_commit_release(void *data) ...@@ -1740,7 +1734,6 @@ static void ff_layout_commit_release(void *data)
static const struct rpc_call_ops ff_layout_read_call_ops_v3 = { static const struct rpc_call_ops ff_layout_read_call_ops_v3 = {
.rpc_call_prepare = ff_layout_read_prepare_v3, .rpc_call_prepare = ff_layout_read_prepare_v3,
.rpc_call_prepare_transmit = ff_layout_io_prepare_transmit,
.rpc_call_done = ff_layout_read_call_done, .rpc_call_done = ff_layout_read_call_done,
.rpc_count_stats = ff_layout_read_count_stats, .rpc_count_stats = ff_layout_read_count_stats,
.rpc_release = ff_layout_read_release, .rpc_release = ff_layout_read_release,
...@@ -1748,7 +1741,6 @@ static const struct rpc_call_ops ff_layout_read_call_ops_v3 = { ...@@ -1748,7 +1741,6 @@ static const struct rpc_call_ops ff_layout_read_call_ops_v3 = {
static const struct rpc_call_ops ff_layout_read_call_ops_v4 = { static const struct rpc_call_ops ff_layout_read_call_ops_v4 = {
.rpc_call_prepare = ff_layout_read_prepare_v4, .rpc_call_prepare = ff_layout_read_prepare_v4,
.rpc_call_prepare_transmit = ff_layout_io_prepare_transmit,
.rpc_call_done = ff_layout_read_call_done, .rpc_call_done = ff_layout_read_call_done,
.rpc_count_stats = ff_layout_read_count_stats, .rpc_count_stats = ff_layout_read_count_stats,
.rpc_release = ff_layout_read_release, .rpc_release = ff_layout_read_release,
...@@ -1756,7 +1748,6 @@ static const struct rpc_call_ops ff_layout_read_call_ops_v4 = { ...@@ -1756,7 +1748,6 @@ static const struct rpc_call_ops ff_layout_read_call_ops_v4 = {
static const struct rpc_call_ops ff_layout_write_call_ops_v3 = { static const struct rpc_call_ops ff_layout_write_call_ops_v3 = {
.rpc_call_prepare = ff_layout_write_prepare_v3, .rpc_call_prepare = ff_layout_write_prepare_v3,
.rpc_call_prepare_transmit = ff_layout_io_prepare_transmit,
.rpc_call_done = ff_layout_write_call_done, .rpc_call_done = ff_layout_write_call_done,
.rpc_count_stats = ff_layout_write_count_stats, .rpc_count_stats = ff_layout_write_count_stats,
.rpc_release = ff_layout_write_release, .rpc_release = ff_layout_write_release,
...@@ -1764,7 +1755,6 @@ static const struct rpc_call_ops ff_layout_write_call_ops_v3 = { ...@@ -1764,7 +1755,6 @@ static const struct rpc_call_ops ff_layout_write_call_ops_v3 = {
static const struct rpc_call_ops ff_layout_write_call_ops_v4 = { static const struct rpc_call_ops ff_layout_write_call_ops_v4 = {
.rpc_call_prepare = ff_layout_write_prepare_v4, .rpc_call_prepare = ff_layout_write_prepare_v4,
.rpc_call_prepare_transmit = ff_layout_io_prepare_transmit,
.rpc_call_done = ff_layout_write_call_done, .rpc_call_done = ff_layout_write_call_done,
.rpc_count_stats = ff_layout_write_count_stats, .rpc_count_stats = ff_layout_write_count_stats,
.rpc_release = ff_layout_write_release, .rpc_release = ff_layout_write_release,
......
...@@ -1403,12 +1403,21 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat ...@@ -1403,12 +1403,21 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ)) if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
return 0; return 0;
/* No fileid? Just exit */
if (!(fattr->valid & NFS_ATTR_FATTR_FILEID))
return 0;
/* Has the inode gone and changed behind our back? */ /* Has the inode gone and changed behind our back? */
if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid) if (nfsi->fileid != fattr->fileid) {
/* Is this perhaps the mounted-on fileid? */
if ((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) &&
nfsi->fileid == fattr->mounted_on_fileid)
return 0;
return -ESTALE; return -ESTALE;
}
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
return -ESTALE; return -ESTALE;
if (!nfs_file_has_buffered_writers(nfsi)) { if (!nfs_file_has_buffered_writers(nfsi)) {
/* Verify a few of the more important attributes */ /* Verify a few of the more important attributes */
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 && !inode_eq_iversion_raw(inode, fattr->change_attr)) if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 && !inode_eq_iversion_raw(inode, fattr->change_attr))
...@@ -1768,18 +1777,6 @@ int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fa ...@@ -1768,18 +1777,6 @@ int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fa
EXPORT_SYMBOL_GPL(nfs_post_op_update_inode_force_wcc); EXPORT_SYMBOL_GPL(nfs_post_op_update_inode_force_wcc);
static inline bool nfs_fileid_valid(struct nfs_inode *nfsi,
struct nfs_fattr *fattr)
{
bool ret1 = true, ret2 = true;
if (fattr->valid & NFS_ATTR_FATTR_FILEID)
ret1 = (nfsi->fileid == fattr->fileid);
if (fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID)
ret2 = (nfsi->fileid == fattr->mounted_on_fileid);
return ret1 || ret2;
}
/* /*
* Many nfs protocol calls return the new file attributes after * Many nfs protocol calls return the new file attributes after
* an operation. Here we update the inode to reflect the state * an operation. Here we update the inode to reflect the state
...@@ -1810,7 +1807,15 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1810,7 +1807,15 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
nfs_display_fhandle_hash(NFS_FH(inode)), nfs_display_fhandle_hash(NFS_FH(inode)),
atomic_read(&inode->i_count), fattr->valid); atomic_read(&inode->i_count), fattr->valid);
if (!nfs_fileid_valid(nfsi, fattr)) { /* No fileid? Just exit */
if (!(fattr->valid & NFS_ATTR_FATTR_FILEID))
return 0;
/* Has the inode gone and changed behind our back? */
if (nfsi->fileid != fattr->fileid) {
/* Is this perhaps the mounted-on fileid? */
if ((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) &&
nfsi->fileid == fattr->mounted_on_fileid)
return 0;
printk(KERN_ERR "NFS: server %s error: fileid changed\n" printk(KERN_ERR "NFS: server %s error: fileid changed\n"
"fsid %s: expected fileid 0x%Lx, got 0x%Lx\n", "fsid %s: expected fileid 0x%Lx, got 0x%Lx\n",
NFS_SERVER(inode)->nfs_client->cl_hostname, NFS_SERVER(inode)->nfs_client->cl_hostname,
......
...@@ -775,3 +775,13 @@ static inline bool nfs_error_is_fatal(int err) ...@@ -775,3 +775,13 @@ static inline bool nfs_error_is_fatal(int err)
} }
} }
static inline bool nfs_error_is_fatal_on_server(int err)
{
switch (err) {
case 0:
case -ERESTARTSYS:
case -EINTR:
return false;
}
return nfs_error_is_fatal(err);
}
...@@ -73,13 +73,13 @@ nfs4_file_open(struct inode *inode, struct file *filp) ...@@ -73,13 +73,13 @@ nfs4_file_open(struct inode *inode, struct file *filp)
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
err = PTR_ERR(inode); err = PTR_ERR(inode);
switch (err) { switch (err) {
case -EPERM:
case -EACCES:
case -EDQUOT:
case -ENOSPC:
case -EROFS:
goto out_put_ctx;
default: default:
goto out_put_ctx;
case -ENOENT:
case -ESTALE:
case -EISDIR:
case -ENOTDIR:
case -ELOOP:
goto out_drop; goto out_drop;
} }
} }
......
...@@ -590,7 +590,7 @@ static void nfs_pgio_rpcsetup(struct nfs_pgio_header *hdr, ...@@ -590,7 +590,7 @@ static void nfs_pgio_rpcsetup(struct nfs_pgio_header *hdr,
} }
hdr->res.fattr = &hdr->fattr; hdr->res.fattr = &hdr->fattr;
hdr->res.count = count; hdr->res.count = 0;
hdr->res.eof = 0; hdr->res.eof = 0;
hdr->res.verf = &hdr->verf; hdr->res.verf = &hdr->verf;
nfs_fattr_init(&hdr->fattr); nfs_fattr_init(&hdr->fattr);
...@@ -1251,20 +1251,23 @@ static void nfs_pageio_complete_mirror(struct nfs_pageio_descriptor *desc, ...@@ -1251,20 +1251,23 @@ static void nfs_pageio_complete_mirror(struct nfs_pageio_descriptor *desc,
int nfs_pageio_resend(struct nfs_pageio_descriptor *desc, int nfs_pageio_resend(struct nfs_pageio_descriptor *desc,
struct nfs_pgio_header *hdr) struct nfs_pgio_header *hdr)
{ {
LIST_HEAD(failed); LIST_HEAD(pages);
desc->pg_io_completion = hdr->io_completion; desc->pg_io_completion = hdr->io_completion;
desc->pg_dreq = hdr->dreq; desc->pg_dreq = hdr->dreq;
while (!list_empty(&hdr->pages)) { list_splice_init(&hdr->pages, &pages);
struct nfs_page *req = nfs_list_entry(hdr->pages.next); while (!list_empty(&pages)) {
struct nfs_page *req = nfs_list_entry(pages.next);
if (!nfs_pageio_add_request(desc, req)) if (!nfs_pageio_add_request(desc, req))
nfs_list_move_request(req, &failed); break;
} }
nfs_pageio_complete(desc); nfs_pageio_complete(desc);
if (!list_empty(&failed)) { if (!list_empty(&pages)) {
list_move(&failed, &hdr->pages); int err = desc->pg_error < 0 ? desc->pg_error : -EIO;
return desc->pg_error < 0 ? desc->pg_error : -EIO; hdr->completion_ops->error_cleanup(&pages, err);
nfs_set_pgio_error(hdr, err, hdr->io_start);
return err;
} }
return 0; return 0;
} }
......
...@@ -627,11 +627,16 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv, ...@@ -627,11 +627,16 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
/* Add this address as an alias */ /* Add this address as an alias */
rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args, rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
rpc_clnt_test_and_add_xprt, NULL); rpc_clnt_test_and_add_xprt, NULL);
} else continue;
}
clp = get_v3_ds_connect(mds_srv, clp = get_v3_ds_connect(mds_srv,
(struct sockaddr *)&da->da_addr, (struct sockaddr *)&da->da_addr,
da->da_addrlen, IPPROTO_TCP, da->da_addrlen, IPPROTO_TCP,
timeo, retrans); timeo, retrans);
if (IS_ERR(clp))
continue;
clp->cl_rpcclient->cl_softerr = 0;
clp->cl_rpcclient->cl_softrtry = 0;
} }
if (IS_ERR(clp)) { if (IS_ERR(clp)) {
......
...@@ -594,7 +594,8 @@ static int nfs_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr) ...@@ -594,7 +594,8 @@ static int nfs_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
/* Emulate the eof flag, which isn't normally needed in NFSv2 /* Emulate the eof flag, which isn't normally needed in NFSv2
* as it is guaranteed to always return the file attributes * as it is guaranteed to always return the file attributes
*/ */
if (hdr->args.offset + hdr->res.count >= hdr->res.fattr->size) if ((hdr->res.count == 0 && hdr->args.count > 0) ||
hdr->args.offset + hdr->res.count >= hdr->res.fattr->size)
hdr->res.eof = 1; hdr->res.eof = 1;
} }
return 0; return 0;
...@@ -615,8 +616,10 @@ static int nfs_proc_pgio_rpc_prepare(struct rpc_task *task, ...@@ -615,8 +616,10 @@ static int nfs_proc_pgio_rpc_prepare(struct rpc_task *task,
static int nfs_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr) static int nfs_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
{ {
if (task->tk_status >= 0) if (task->tk_status >= 0) {
hdr->res.count = hdr->args.count;
nfs_writeback_update_inode(hdr); nfs_writeback_update_inode(hdr);
}
return 0; return 0;
} }
......
...@@ -91,19 +91,25 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio) ...@@ -91,19 +91,25 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
} }
EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds); EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
static void nfs_readpage_release(struct nfs_page *req) static void nfs_readpage_release(struct nfs_page *req, int error)
{ {
struct inode *inode = d_inode(nfs_req_openctx(req)->dentry); struct inode *inode = d_inode(nfs_req_openctx(req)->dentry);
struct page *page = req->wb_page;
dprintk("NFS: read done (%s/%llu %d@%lld)\n", inode->i_sb->s_id, dprintk("NFS: read done (%s/%llu %d@%lld)\n", inode->i_sb->s_id,
(unsigned long long)NFS_FILEID(inode), req->wb_bytes, (unsigned long long)NFS_FILEID(inode), req->wb_bytes,
(long long)req_offset(req)); (long long)req_offset(req));
if (nfs_error_is_fatal_on_server(error) && error != -ETIMEDOUT)
SetPageError(page);
if (nfs_page_group_sync_on_bit(req, PG_UNLOCKPAGE)) { if (nfs_page_group_sync_on_bit(req, PG_UNLOCKPAGE)) {
if (PageUptodate(req->wb_page)) struct address_space *mapping = page_file_mapping(page);
nfs_readpage_to_fscache(inode, req->wb_page, 0);
unlock_page(req->wb_page); if (PageUptodate(page))
nfs_readpage_to_fscache(inode, page, 0);
else if (!PageError(page) && !PagePrivate(page))
generic_error_remove_page(mapping, page);
unlock_page(page);
} }
nfs_release_request(req); nfs_release_request(req);
} }
...@@ -131,7 +137,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, ...@@ -131,7 +137,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
&nfs_async_read_completion_ops); &nfs_async_read_completion_ops);
if (!nfs_pageio_add_request(&pgio, new)) { if (!nfs_pageio_add_request(&pgio, new)) {
nfs_list_remove_request(new); nfs_list_remove_request(new);
nfs_readpage_release(new); nfs_readpage_release(new, pgio.pg_error);
} }
nfs_pageio_complete(&pgio); nfs_pageio_complete(&pgio);
...@@ -153,6 +159,7 @@ static void nfs_page_group_set_uptodate(struct nfs_page *req) ...@@ -153,6 +159,7 @@ static void nfs_page_group_set_uptodate(struct nfs_page *req)
static void nfs_read_completion(struct nfs_pgio_header *hdr) static void nfs_read_completion(struct nfs_pgio_header *hdr)
{ {
unsigned long bytes = 0; unsigned long bytes = 0;
int error;
if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
goto out; goto out;
...@@ -179,14 +186,19 @@ static void nfs_read_completion(struct nfs_pgio_header *hdr) ...@@ -179,14 +186,19 @@ static void nfs_read_completion(struct nfs_pgio_header *hdr)
zero_user_segment(page, start, end); zero_user_segment(page, start, end);
} }
} }
error = 0;
bytes += req->wb_bytes; bytes += req->wb_bytes;
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) { if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) {
if (bytes <= hdr->good_bytes) if (bytes <= hdr->good_bytes)
nfs_page_group_set_uptodate(req); nfs_page_group_set_uptodate(req);
else {
error = hdr->error;
xchg(&nfs_req_openctx(req)->error, error);
}
} else } else
nfs_page_group_set_uptodate(req); nfs_page_group_set_uptodate(req);
nfs_list_remove_request(req); nfs_list_remove_request(req);
nfs_readpage_release(req); nfs_readpage_release(req, error);
} }
out: out:
hdr->release(hdr); hdr->release(hdr);
...@@ -213,7 +225,7 @@ nfs_async_read_error(struct list_head *head, int error) ...@@ -213,7 +225,7 @@ nfs_async_read_error(struct list_head *head, int error)
while (!list_empty(head)) { while (!list_empty(head)) {
req = nfs_list_entry(head->next); req = nfs_list_entry(head->next);
nfs_list_remove_request(req); nfs_list_remove_request(req);
nfs_readpage_release(req); nfs_readpage_release(req, error);
} }
} }
...@@ -337,8 +349,13 @@ int nfs_readpage(struct file *file, struct page *page) ...@@ -337,8 +349,13 @@ int nfs_readpage(struct file *file, struct page *page)
goto out; goto out;
} }
xchg(&ctx->error, 0);
error = nfs_readpage_async(ctx, inode, page); error = nfs_readpage_async(ctx, inode, page);
if (!error) {
error = wait_on_page_locked_killable(page);
if (!PageUptodate(page) && !error)
error = xchg(&ctx->error, 0);
}
out: out:
put_nfs_open_context(ctx); put_nfs_open_context(ctx);
return error; return error;
...@@ -372,8 +389,8 @@ readpage_async_filler(void *data, struct page *page) ...@@ -372,8 +389,8 @@ readpage_async_filler(void *data, struct page *page)
zero_user_segment(page, len, PAGE_SIZE); zero_user_segment(page, len, PAGE_SIZE);
if (!nfs_pageio_add_request(desc->pgio, new)) { if (!nfs_pageio_add_request(desc->pgio, new)) {
nfs_list_remove_request(new); nfs_list_remove_request(new);
nfs_readpage_release(new);
error = desc->pgio->pg_error; error = desc->pgio->pg_error;
nfs_readpage_release(new, error);
goto out; goto out;
} }
return 0; return 0;
......
...@@ -57,6 +57,7 @@ static const struct rpc_call_ops nfs_commit_ops; ...@@ -57,6 +57,7 @@ static const struct rpc_call_ops nfs_commit_ops;
static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops; static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops;
static const struct nfs_commit_completion_ops nfs_commit_completion_ops; static const struct nfs_commit_completion_ops nfs_commit_completion_ops;
static const struct nfs_rw_ops nfs_rw_write_ops; static const struct nfs_rw_ops nfs_rw_write_ops;
static void nfs_inode_remove_request(struct nfs_page *req);
static void nfs_clear_request_commit(struct nfs_page *req); static void nfs_clear_request_commit(struct nfs_page *req);
static void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo, static void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo,
struct inode *inode); struct inode *inode);
...@@ -591,23 +592,13 @@ nfs_lock_and_join_requests(struct page *page) ...@@ -591,23 +592,13 @@ nfs_lock_and_join_requests(struct page *page)
static void nfs_write_error(struct nfs_page *req, int error) static void nfs_write_error(struct nfs_page *req, int error)
{ {
nfs_set_pageerror(page_file_mapping(req->wb_page));
nfs_mapping_set_error(req->wb_page, error); nfs_mapping_set_error(req->wb_page, error);
nfs_inode_remove_request(req);
nfs_end_page_writeback(req); nfs_end_page_writeback(req);
nfs_release_request(req); nfs_release_request(req);
} }
static bool
nfs_error_is_fatal_on_server(int err)
{
switch (err) {
case 0:
case -ERESTARTSYS:
case -EINTR:
return false;
}
return nfs_error_is_fatal(err);
}
/* /*
* Find an associated nfs write request, and prepare to flush it out * Find an associated nfs write request, and prepare to flush it out
* May return an error if the user signalled nfs_wait_on_request(). * May return an error if the user signalled nfs_wait_on_request().
...@@ -615,7 +606,6 @@ nfs_error_is_fatal_on_server(int err) ...@@ -615,7 +606,6 @@ nfs_error_is_fatal_on_server(int err)
static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
struct page *page) struct page *page)
{ {
struct address_space *mapping;
struct nfs_page *req; struct nfs_page *req;
int ret = 0; int ret = 0;
...@@ -630,12 +620,11 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, ...@@ -630,12 +620,11 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags)); WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags));
/* If there is a fatal error that covers this write, just exit */ /* If there is a fatal error that covers this write, just exit */
ret = 0; ret = pgio->pg_error;
mapping = page_file_mapping(page); if (nfs_error_is_fatal_on_server(ret))
if (test_bit(AS_ENOSPC, &mapping->flags) ||
test_bit(AS_EIO, &mapping->flags))
goto out_launder; goto out_launder;
ret = 0;
if (!nfs_pageio_add_request(pgio, req)) { if (!nfs_pageio_add_request(pgio, req)) {
ret = pgio->pg_error; ret = pgio->pg_error;
/* /*
...@@ -647,6 +636,7 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, ...@@ -647,6 +636,7 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
} else } else
ret = -EAGAIN; ret = -EAGAIN;
nfs_redirty_request(req); nfs_redirty_request(req);
pgio->pg_error = 0;
} else } else
nfs_add_stats(page_file_mapping(page)->host, nfs_add_stats(page_file_mapping(page)->host,
NFSIOS_WRITEPAGES, 1); NFSIOS_WRITEPAGES, 1);
...@@ -666,7 +656,7 @@ static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, ...@@ -666,7 +656,7 @@ static int nfs_do_writepage(struct page *page, struct writeback_control *wbc,
ret = nfs_page_async_flush(pgio, page); ret = nfs_page_async_flush(pgio, page);
if (ret == -EAGAIN) { if (ret == -EAGAIN) {
redirty_page_for_writepage(wbc, page); redirty_page_for_writepage(wbc, page);
ret = 0; ret = AOP_WRITEPAGE_ACTIVATE;
} }
return ret; return ret;
} }
...@@ -685,10 +675,11 @@ static int nfs_writepage_locked(struct page *page, ...@@ -685,10 +675,11 @@ static int nfs_writepage_locked(struct page *page,
nfs_pageio_init_write(&pgio, inode, 0, nfs_pageio_init_write(&pgio, inode, 0,
false, &nfs_async_write_completion_ops); false, &nfs_async_write_completion_ops);
err = nfs_do_writepage(page, wbc, &pgio); err = nfs_do_writepage(page, wbc, &pgio);
pgio.pg_error = 0;
nfs_pageio_complete(&pgio); nfs_pageio_complete(&pgio);
if (err < 0) if (err < 0)
return err; return err;
if (pgio.pg_error < 0) if (nfs_error_is_fatal(pgio.pg_error))
return pgio.pg_error; return pgio.pg_error;
return 0; return 0;
} }
...@@ -698,6 +689,7 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -698,6 +689,7 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc)
int ret; int ret;
ret = nfs_writepage_locked(page, wbc); ret = nfs_writepage_locked(page, wbc);
if (ret != AOP_WRITEPAGE_ACTIVATE)
unlock_page(page); unlock_page(page);
return ret; return ret;
} }
...@@ -707,6 +699,7 @@ static int nfs_writepages_callback(struct page *page, struct writeback_control * ...@@ -707,6 +699,7 @@ static int nfs_writepages_callback(struct page *page, struct writeback_control *
int ret; int ret;
ret = nfs_do_writepage(page, wbc, data); ret = nfs_do_writepage(page, wbc, data);
if (ret != AOP_WRITEPAGE_ACTIVATE)
unlock_page(page); unlock_page(page);
return ret; return ret;
} }
...@@ -733,13 +726,14 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) ...@@ -733,13 +726,14 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
&nfs_async_write_completion_ops); &nfs_async_write_completion_ops);
pgio.pg_io_completion = ioc; pgio.pg_io_completion = ioc;
err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio); err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
pgio.pg_error = 0;
nfs_pageio_complete(&pgio); nfs_pageio_complete(&pgio);
nfs_io_completion_put(ioc); nfs_io_completion_put(ioc);
if (err < 0) if (err < 0)
goto out_err; goto out_err;
err = pgio.pg_error; err = pgio.pg_error;
if (err < 0) if (nfs_error_is_fatal(err))
goto out_err; goto out_err;
return 0; return 0;
out_err: out_err:
......
...@@ -98,7 +98,6 @@ typedef void (*rpc_action)(struct rpc_task *); ...@@ -98,7 +98,6 @@ typedef void (*rpc_action)(struct rpc_task *);
struct rpc_call_ops { struct rpc_call_ops {
void (*rpc_call_prepare)(struct rpc_task *, void *); void (*rpc_call_prepare)(struct rpc_task *, void *);
void (*rpc_call_prepare_transmit)(struct rpc_task *, void *);
void (*rpc_call_done)(struct rpc_task *, void *); void (*rpc_call_done)(struct rpc_task *, void *);
void (*rpc_count_stats)(struct rpc_task *, void *); void (*rpc_count_stats)(struct rpc_task *, void *);
void (*rpc_release)(void *); void (*rpc_release)(void *);
......
...@@ -1970,6 +1970,7 @@ call_bind(struct rpc_task *task) ...@@ -1970,6 +1970,7 @@ call_bind(struct rpc_task *task)
static void static void
call_bind_status(struct rpc_task *task) call_bind_status(struct rpc_task *task)
{ {
struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
int status = -EIO; int status = -EIO;
if (rpc_task_transmitted(task)) { if (rpc_task_transmitted(task)) {
...@@ -1977,14 +1978,15 @@ call_bind_status(struct rpc_task *task) ...@@ -1977,14 +1978,15 @@ call_bind_status(struct rpc_task *task)
return; return;
} }
if (task->tk_status >= 0) {
dprint_status(task); dprint_status(task);
trace_rpc_bind_status(task);
if (task->tk_status >= 0)
goto out_next;
if (xprt_bound(xprt)) {
task->tk_status = 0; task->tk_status = 0;
task->tk_action = call_connect; goto out_next;
return;
} }
trace_rpc_bind_status(task);
switch (task->tk_status) { switch (task->tk_status) {
case -ENOMEM: case -ENOMEM:
dprintk("RPC: %5u rpcbind out of memory\n", task->tk_pid); dprintk("RPC: %5u rpcbind out of memory\n", task->tk_pid);
...@@ -2003,6 +2005,9 @@ call_bind_status(struct rpc_task *task) ...@@ -2003,6 +2005,9 @@ call_bind_status(struct rpc_task *task)
task->tk_rebind_retry--; task->tk_rebind_retry--;
rpc_delay(task, 3*HZ); rpc_delay(task, 3*HZ);
goto retry_timeout; goto retry_timeout;
case -ENOBUFS:
rpc_delay(task, HZ >> 2);
goto retry_timeout;
case -EAGAIN: case -EAGAIN:
goto retry_timeout; goto retry_timeout;
case -ETIMEDOUT: case -ETIMEDOUT:
...@@ -2026,7 +2031,6 @@ call_bind_status(struct rpc_task *task) ...@@ -2026,7 +2031,6 @@ call_bind_status(struct rpc_task *task)
case -ENETDOWN: case -ENETDOWN:
case -EHOSTUNREACH: case -EHOSTUNREACH:
case -ENETUNREACH: case -ENETUNREACH:
case -ENOBUFS:
case -EPIPE: case -EPIPE:
dprintk("RPC: %5u remote rpcbind unreachable: %d\n", dprintk("RPC: %5u remote rpcbind unreachable: %d\n",
task->tk_pid, task->tk_status); task->tk_pid, task->tk_status);
...@@ -2043,7 +2047,9 @@ call_bind_status(struct rpc_task *task) ...@@ -2043,7 +2047,9 @@ call_bind_status(struct rpc_task *task)
rpc_call_rpcerror(task, status); rpc_call_rpcerror(task, status);
return; return;
out_next:
task->tk_action = call_connect;
return;
retry_timeout: retry_timeout:
task->tk_status = 0; task->tk_status = 0;
task->tk_action = call_bind; task->tk_action = call_bind;
...@@ -2090,6 +2096,7 @@ call_connect(struct rpc_task *task) ...@@ -2090,6 +2096,7 @@ call_connect(struct rpc_task *task)
static void static void
call_connect_status(struct rpc_task *task) call_connect_status(struct rpc_task *task)
{ {
struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
struct rpc_clnt *clnt = task->tk_client; struct rpc_clnt *clnt = task->tk_client;
int status = task->tk_status; int status = task->tk_status;
...@@ -2099,8 +2106,17 @@ call_connect_status(struct rpc_task *task) ...@@ -2099,8 +2106,17 @@ call_connect_status(struct rpc_task *task)
} }
dprint_status(task); dprint_status(task);
trace_rpc_connect_status(task); trace_rpc_connect_status(task);
if (task->tk_status == 0) {
clnt->cl_stats->netreconn++;
goto out_next;
}
if (xprt_connected(xprt)) {
task->tk_status = 0;
goto out_next;
}
task->tk_status = 0; task->tk_status = 0;
switch (status) { switch (status) {
case -ECONNREFUSED: case -ECONNREFUSED:
...@@ -2117,8 +2133,6 @@ call_connect_status(struct rpc_task *task) ...@@ -2117,8 +2133,6 @@ call_connect_status(struct rpc_task *task)
case -ENETDOWN: case -ENETDOWN:
case -ENETUNREACH: case -ENETUNREACH:
case -EHOSTUNREACH: case -EHOSTUNREACH:
case -EADDRINUSE:
case -ENOBUFS:
case -EPIPE: case -EPIPE:
xprt_conditional_disconnect(task->tk_rqstp->rq_xprt, xprt_conditional_disconnect(task->tk_rqstp->rq_xprt,
task->tk_rqstp->rq_connect_cookie); task->tk_rqstp->rq_connect_cookie);
...@@ -2127,17 +2141,20 @@ call_connect_status(struct rpc_task *task) ...@@ -2127,17 +2141,20 @@ call_connect_status(struct rpc_task *task)
/* retry with existing socket, after a delay */ /* retry with existing socket, after a delay */
rpc_delay(task, 3*HZ); rpc_delay(task, 3*HZ);
/* fall through */ /* fall through */
case -EADDRINUSE:
case -ENOTCONN: case -ENOTCONN:
case -EAGAIN: case -EAGAIN:
case -ETIMEDOUT: case -ETIMEDOUT:
goto out_retry; goto out_retry;
case 0: case -ENOBUFS:
clnt->cl_stats->netreconn++; rpc_delay(task, HZ >> 2);
task->tk_action = call_transmit; goto out_retry;
return;
} }
rpc_call_rpcerror(task, status); rpc_call_rpcerror(task, status);
return; return;
out_next:
task->tk_action = call_transmit;
return;
out_retry: out_retry:
/* Check for timeouts before looping back to call_bind */ /* Check for timeouts before looping back to call_bind */
task->tk_action = call_bind; task->tk_action = call_bind;
...@@ -2365,7 +2382,7 @@ call_status(struct rpc_task *task) ...@@ -2365,7 +2382,7 @@ call_status(struct rpc_task *task)
case -ECONNABORTED: case -ECONNABORTED:
case -ENOTCONN: case -ENOTCONN:
rpc_force_rebind(clnt); rpc_force_rebind(clnt);
/* fall through */ break;
case -EADDRINUSE: case -EADDRINUSE:
rpc_delay(task, 3*HZ); rpc_delay(task, 3*HZ);
/* fall through */ /* fall through */
......
...@@ -1408,13 +1408,6 @@ xprt_request_transmit(struct rpc_rqst *req, struct rpc_task *snd_task) ...@@ -1408,13 +1408,6 @@ xprt_request_transmit(struct rpc_rqst *req, struct rpc_task *snd_task)
status = -EBADMSG; status = -EBADMSG;
goto out_dequeue; goto out_dequeue;
} }
if (task->tk_ops->rpc_call_prepare_transmit) {
task->tk_ops->rpc_call_prepare_transmit(task,
task->tk_calldata);
status = task->tk_status;
if (status < 0)
goto out_dequeue;
}
if (RPC_SIGNALLED(task)) { if (RPC_SIGNALLED(task)) {
status = -ERESTARTSYS; status = -ERESTARTSYS;
goto out_dequeue; goto out_dequeue;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment