mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 22:21:42 +00:00
NFS client bugfixes for Linux 5.3
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. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEESQctxSBg8JpV8KqEZwvnipYKAPIFAl1lgz4ACgkQZwvnipYK APIsHhAApqaVaGzwfeR87zq+QaaVOzYzejyvFgs3wh/Lc5xPH+SlQ6NxLbs8ppdT srrOHV9E2MA4JgqoHaIBMTqWacQ0UfQQ/6qLEFCrps9/0QHs7fg0CAHS5emmgk2v rD6Mezr5Nx8h5/QJCBEZXfas5lxsICz1EYJ4Pk8QT6IoyeC+fvarGZKvzIQJ3KDN 8yrdv5kCVtN7noREf1KDIqIlYvFbIEoOoglNA40G49e1ffT9Oz6qzTcg19HFO50x eAIxc9u4KCUY/ASCvcv9biQ5200l7QSCqmR7/Xlj/+4aClKp6Ay058j0awxtHHDy NlZt6V3XGlm1/SVpvtU/XXWcyJmQwX7kOVIEYOFmt+lEqC7ZBzWEpAaJ8h4DMLLc PIxIWBSmXNxp6LPNI0dZFf7O6UZ3ZMRacav+HHu7mjWolEB22f4jQJs+RxNhnfLU fg180YWBMX4V/98S7iigxZkRd+qqQhddYtku+o+bp3h4m6mVrrYNm11J0o0GWQWf Lio9nlkLq9hkYpdBwkH4PtIv3b+O5f9yhfEYn15eF27Ru0Bob0+DiBkzlflcrJve W2VfNAj+jxP3Wg0QAI40BSqUB3b+zVtZW5FenAUEK7NxhhPi6jrIsVhhVgGFZIAd i1xwYUg6fDjielhGOxMTF66ilvduA9uBCFAnTD3iSBoZmF63vew= =YHhU -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
9e8312f5e1
@ -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))
|
||||
nfs_file_set_open_context(file, ctx);
|
||||
else
|
||||
err = -ESTALE;
|
||||
err = -EOPENSTALE;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
@ -401,15 +401,21 @@ static void nfs_direct_read_completion(struct nfs_pgio_header *hdr)
|
||||
unsigned long bytes = 0;
|
||||
struct nfs_direct_req *dreq = hdr->dreq;
|
||||
|
||||
if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
|
||||
goto out_put;
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
if (test_bit(NFS_IOHDR_EOF, &hdr->flags))
|
||||
dreq->error = 0;
|
||||
|
||||
spin_unlock(&dreq->lock);
|
||||
|
||||
while (!list_empty(&hdr->pages)) {
|
||||
@ -782,16 +788,19 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
|
||||
bool request_commit = false;
|
||||
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);
|
||||
|
||||
spin_lock(&dreq->lock);
|
||||
|
||||
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags))
|
||||
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);
|
||||
if (nfs_write_need_commit(hdr)) {
|
||||
if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES)
|
||||
|
@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/nfs_mount.h>
|
||||
#include <linux/nfs_page.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched/mm.h>
|
||||
@ -928,7 +929,9 @@ retry:
|
||||
pgm = &pgio->pg_mirrors[0];
|
||||
pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].rsize;
|
||||
|
||||
pgio->pg_maxretrans = io_maxretrans;
|
||||
if (NFS_SERVER(pgio->pg_inode)->flags &
|
||||
(NFS_MOUNT_SOFT|NFS_MOUNT_SOFTERR))
|
||||
pgio->pg_maxretrans = io_maxretrans;
|
||||
return;
|
||||
out_nolseg:
|
||||
if (pgio->pg_error < 0)
|
||||
@ -940,6 +943,7 @@ out_mds:
|
||||
pgio->pg_lseg);
|
||||
pnfs_put_lseg(pgio->pg_lseg);
|
||||
pgio->pg_lseg = NULL;
|
||||
pgio->pg_maxretrans = 0;
|
||||
nfs_pageio_reset_read_mds(pgio);
|
||||
}
|
||||
|
||||
@ -1000,7 +1004,9 @@ retry:
|
||||
pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].wsize;
|
||||
}
|
||||
|
||||
pgio->pg_maxretrans = io_maxretrans;
|
||||
if (NFS_SERVER(pgio->pg_inode)->flags &
|
||||
(NFS_MOUNT_SOFT|NFS_MOUNT_SOFTERR))
|
||||
pgio->pg_maxretrans = io_maxretrans;
|
||||
return;
|
||||
|
||||
out_mds:
|
||||
@ -1010,6 +1016,7 @@ out_mds:
|
||||
pgio->pg_lseg);
|
||||
pnfs_put_lseg(pgio->pg_lseg);
|
||||
pgio->pg_lseg = NULL;
|
||||
pgio->pg_maxretrans = 0;
|
||||
nfs_pageio_reset_write_mds(pgio);
|
||||
}
|
||||
|
||||
@ -1148,8 +1155,6 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task,
|
||||
break;
|
||||
case -NFS4ERR_RETRY_UNCACHED_REP:
|
||||
break;
|
||||
case -EAGAIN:
|
||||
return -NFS4ERR_RESET_TO_PNFS;
|
||||
/* Invalidate Layout errors */
|
||||
case -NFS4ERR_PNFS_NO_LAYOUT:
|
||||
case -ESTALE: /* mapped NFS4ERR_STALE */
|
||||
@ -1210,7 +1215,6 @@ static int ff_layout_async_handle_error_v3(struct rpc_task *task,
|
||||
case -EBADHANDLE:
|
||||
case -ELOOP:
|
||||
case -ENOSPC:
|
||||
case -EAGAIN:
|
||||
break;
|
||||
case -EJUKEBOX:
|
||||
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)
|
||||
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)
|
||||
{
|
||||
struct nfs_pgio_header *hdr = 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 = {
|
||||
.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_count_stats = ff_layout_read_count_stats,
|
||||
.rpc_release = ff_layout_read_release,
|
||||
@ -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 = {
|
||||
.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_count_stats = ff_layout_read_count_stats,
|
||||
.rpc_release = ff_layout_read_release,
|
||||
@ -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 = {
|
||||
.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_count_stats = ff_layout_write_count_stats,
|
||||
.rpc_release = ff_layout_write_release,
|
||||
@ -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 = {
|
||||
.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_count_stats = ff_layout_write_count_stats,
|
||||
.rpc_release = ff_layout_write_release,
|
||||
|
@ -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))
|
||||
return 0;
|
||||
|
||||
/* No fileid? Just exit */
|
||||
if (!(fattr->valid & NFS_ATTR_FATTR_FILEID))
|
||||
return 0;
|
||||
/* 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;
|
||||
}
|
||||
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
|
||||
return -ESTALE;
|
||||
|
||||
|
||||
if (!nfs_file_has_buffered_writers(nfsi)) {
|
||||
/* Verify a few of the more important attributes */
|
||||
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
|
||||
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
|
||||
* 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)
|
||||
nfs_display_fhandle_hash(NFS_FH(inode)),
|
||||
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"
|
||||
"fsid %s: expected fileid 0x%Lx, got 0x%Lx\n",
|
||||
NFS_SERVER(inode)->nfs_client->cl_hostname,
|
||||
|
@ -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)
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
switch (err) {
|
||||
case -EPERM:
|
||||
case -EACCES:
|
||||
case -EDQUOT:
|
||||
case -ENOSPC:
|
||||
case -EROFS:
|
||||
goto out_put_ctx;
|
||||
default:
|
||||
goto out_put_ctx;
|
||||
case -ENOENT:
|
||||
case -ESTALE:
|
||||
case -EISDIR:
|
||||
case -ENOTDIR:
|
||||
case -ELOOP:
|
||||
goto out_drop;
|
||||
}
|
||||
}
|
||||
|
@ -590,7 +590,7 @@ static void nfs_pgio_rpcsetup(struct nfs_pgio_header *hdr,
|
||||
}
|
||||
|
||||
hdr->res.fattr = &hdr->fattr;
|
||||
hdr->res.count = count;
|
||||
hdr->res.count = 0;
|
||||
hdr->res.eof = 0;
|
||||
hdr->res.verf = &hdr->verf;
|
||||
nfs_fattr_init(&hdr->fattr);
|
||||
@ -1251,20 +1251,23 @@ static void nfs_pageio_complete_mirror(struct nfs_pageio_descriptor *desc,
|
||||
int nfs_pageio_resend(struct nfs_pageio_descriptor *desc,
|
||||
struct nfs_pgio_header *hdr)
|
||||
{
|
||||
LIST_HEAD(failed);
|
||||
LIST_HEAD(pages);
|
||||
|
||||
desc->pg_io_completion = hdr->io_completion;
|
||||
desc->pg_dreq = hdr->dreq;
|
||||
while (!list_empty(&hdr->pages)) {
|
||||
struct nfs_page *req = nfs_list_entry(hdr->pages.next);
|
||||
list_splice_init(&hdr->pages, &pages);
|
||||
while (!list_empty(&pages)) {
|
||||
struct nfs_page *req = nfs_list_entry(pages.next);
|
||||
|
||||
if (!nfs_pageio_add_request(desc, req))
|
||||
nfs_list_move_request(req, &failed);
|
||||
break;
|
||||
}
|
||||
nfs_pageio_complete(desc);
|
||||
if (!list_empty(&failed)) {
|
||||
list_move(&failed, &hdr->pages);
|
||||
return desc->pg_error < 0 ? desc->pg_error : -EIO;
|
||||
if (!list_empty(&pages)) {
|
||||
int err = 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;
|
||||
}
|
||||
|
@ -627,11 +627,16 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
|
||||
/* Add this address as an alias */
|
||||
rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
|
||||
rpc_clnt_test_and_add_xprt, NULL);
|
||||
} else
|
||||
clp = get_v3_ds_connect(mds_srv,
|
||||
(struct sockaddr *)&da->da_addr,
|
||||
da->da_addrlen, IPPROTO_TCP,
|
||||
timeo, retrans);
|
||||
continue;
|
||||
}
|
||||
clp = get_v3_ds_connect(mds_srv,
|
||||
(struct sockaddr *)&da->da_addr,
|
||||
da->da_addrlen, IPPROTO_TCP,
|
||||
timeo, retrans);
|
||||
if (IS_ERR(clp))
|
||||
continue;
|
||||
clp->cl_rpcclient->cl_softerr = 0;
|
||||
clp->cl_rpcclient->cl_softrtry = 0;
|
||||
}
|
||||
|
||||
if (IS_ERR(clp)) {
|
||||
|
@ -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
|
||||
* 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;
|
||||
}
|
||||
return 0;
|
||||
@ -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)
|
||||
{
|
||||
if (task->tk_status >= 0)
|
||||
if (task->tk_status >= 0) {
|
||||
hdr->res.count = hdr->args.count;
|
||||
nfs_writeback_update_inode(hdr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -91,19 +91,25 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
|
||||
}
|
||||
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 page *page = req->wb_page;
|
||||
|
||||
dprintk("NFS: read done (%s/%llu %d@%lld)\n", inode->i_sb->s_id,
|
||||
(unsigned long long)NFS_FILEID(inode), req->wb_bytes,
|
||||
(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 (PageUptodate(req->wb_page))
|
||||
nfs_readpage_to_fscache(inode, req->wb_page, 0);
|
||||
struct address_space *mapping = page_file_mapping(page);
|
||||
|
||||
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);
|
||||
}
|
||||
@ -131,7 +137,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
|
||||
&nfs_async_read_completion_ops);
|
||||
if (!nfs_pageio_add_request(&pgio, new)) {
|
||||
nfs_list_remove_request(new);
|
||||
nfs_readpage_release(new);
|
||||
nfs_readpage_release(new, pgio.pg_error);
|
||||
}
|
||||
nfs_pageio_complete(&pgio);
|
||||
|
||||
@ -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)
|
||||
{
|
||||
unsigned long bytes = 0;
|
||||
int error;
|
||||
|
||||
if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
|
||||
goto out;
|
||||
@ -179,14 +186,19 @@ static void nfs_read_completion(struct nfs_pgio_header *hdr)
|
||||
zero_user_segment(page, start, end);
|
||||
}
|
||||
}
|
||||
error = 0;
|
||||
bytes += req->wb_bytes;
|
||||
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) {
|
||||
if (bytes <= hdr->good_bytes)
|
||||
nfs_page_group_set_uptodate(req);
|
||||
else {
|
||||
error = hdr->error;
|
||||
xchg(&nfs_req_openctx(req)->error, error);
|
||||
}
|
||||
} else
|
||||
nfs_page_group_set_uptodate(req);
|
||||
nfs_list_remove_request(req);
|
||||
nfs_readpage_release(req);
|
||||
nfs_readpage_release(req, error);
|
||||
}
|
||||
out:
|
||||
hdr->release(hdr);
|
||||
@ -213,7 +225,7 @@ nfs_async_read_error(struct list_head *head, int error)
|
||||
while (!list_empty(head)) {
|
||||
req = nfs_list_entry(head->next);
|
||||
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)
|
||||
goto out;
|
||||
}
|
||||
|
||||
xchg(&ctx->error, 0);
|
||||
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:
|
||||
put_nfs_open_context(ctx);
|
||||
return error;
|
||||
@ -372,8 +389,8 @@ readpage_async_filler(void *data, struct page *page)
|
||||
zero_user_segment(page, len, PAGE_SIZE);
|
||||
if (!nfs_pageio_add_request(desc->pgio, new)) {
|
||||
nfs_list_remove_request(new);
|
||||
nfs_readpage_release(new);
|
||||
error = desc->pgio->pg_error;
|
||||
nfs_readpage_release(new, error);
|
||||
goto out;
|
||||
}
|
||||
return 0;
|
||||
|
@ -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_commit_completion_ops nfs_commit_completion_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_init_cinfo_from_inode(struct nfs_commit_info *cinfo,
|
||||
struct inode *inode);
|
||||
@ -591,23 +592,13 @@ release_request:
|
||||
|
||||
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_inode_remove_request(req);
|
||||
nfs_end_page_writeback(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
|
||||
* May return an error if the user signalled nfs_wait_on_request().
|
||||
@ -615,7 +606,6 @@ nfs_error_is_fatal_on_server(int err)
|
||||
static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
|
||||
struct page *page)
|
||||
{
|
||||
struct address_space *mapping;
|
||||
struct nfs_page *req;
|
||||
int ret = 0;
|
||||
|
||||
@ -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));
|
||||
|
||||
/* If there is a fatal error that covers this write, just exit */
|
||||
ret = 0;
|
||||
mapping = page_file_mapping(page);
|
||||
if (test_bit(AS_ENOSPC, &mapping->flags) ||
|
||||
test_bit(AS_EIO, &mapping->flags))
|
||||
ret = pgio->pg_error;
|
||||
if (nfs_error_is_fatal_on_server(ret))
|
||||
goto out_launder;
|
||||
|
||||
ret = 0;
|
||||
if (!nfs_pageio_add_request(pgio, req)) {
|
||||
ret = pgio->pg_error;
|
||||
/*
|
||||
@ -647,6 +636,7 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
|
||||
} else
|
||||
ret = -EAGAIN;
|
||||
nfs_redirty_request(req);
|
||||
pgio->pg_error = 0;
|
||||
} else
|
||||
nfs_add_stats(page_file_mapping(page)->host,
|
||||
NFSIOS_WRITEPAGES, 1);
|
||||
@ -666,7 +656,7 @@ static int nfs_do_writepage(struct page *page, struct writeback_control *wbc,
|
||||
ret = nfs_page_async_flush(pgio, page);
|
||||
if (ret == -EAGAIN) {
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
ret = 0;
|
||||
ret = AOP_WRITEPAGE_ACTIVATE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -685,10 +675,11 @@ static int nfs_writepage_locked(struct page *page,
|
||||
nfs_pageio_init_write(&pgio, inode, 0,
|
||||
false, &nfs_async_write_completion_ops);
|
||||
err = nfs_do_writepage(page, wbc, &pgio);
|
||||
pgio.pg_error = 0;
|
||||
nfs_pageio_complete(&pgio);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (pgio.pg_error < 0)
|
||||
if (nfs_error_is_fatal(pgio.pg_error))
|
||||
return pgio.pg_error;
|
||||
return 0;
|
||||
}
|
||||
@ -698,7 +689,8 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc)
|
||||
int ret;
|
||||
|
||||
ret = nfs_writepage_locked(page, wbc);
|
||||
unlock_page(page);
|
||||
if (ret != AOP_WRITEPAGE_ACTIVATE)
|
||||
unlock_page(page);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -707,7 +699,8 @@ static int nfs_writepages_callback(struct page *page, struct writeback_control *
|
||||
int ret;
|
||||
|
||||
ret = nfs_do_writepage(page, wbc, data);
|
||||
unlock_page(page);
|
||||
if (ret != AOP_WRITEPAGE_ACTIVATE)
|
||||
unlock_page(page);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -733,13 +726,14 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
|
||||
&nfs_async_write_completion_ops);
|
||||
pgio.pg_io_completion = ioc;
|
||||
err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
|
||||
pgio.pg_error = 0;
|
||||
nfs_pageio_complete(&pgio);
|
||||
nfs_io_completion_put(ioc);
|
||||
|
||||
if (err < 0)
|
||||
goto out_err;
|
||||
err = pgio.pg_error;
|
||||
if (err < 0)
|
||||
if (nfs_error_is_fatal(err))
|
||||
goto out_err;
|
||||
return 0;
|
||||
out_err:
|
||||
|
@ -98,7 +98,6 @@ typedef void (*rpc_action)(struct rpc_task *);
|
||||
|
||||
struct rpc_call_ops {
|
||||
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_count_stats)(struct rpc_task *, void *);
|
||||
void (*rpc_release)(void *);
|
||||
|
@ -1970,6 +1970,7 @@ call_bind(struct rpc_task *task)
|
||||
static void
|
||||
call_bind_status(struct rpc_task *task)
|
||||
{
|
||||
struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
|
||||
int status = -EIO;
|
||||
|
||||
if (rpc_task_transmitted(task)) {
|
||||
@ -1977,14 +1978,15 @@ call_bind_status(struct rpc_task *task)
|
||||
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_action = call_connect;
|
||||
return;
|
||||
goto out_next;
|
||||
}
|
||||
|
||||
trace_rpc_bind_status(task);
|
||||
switch (task->tk_status) {
|
||||
case -ENOMEM:
|
||||
dprintk("RPC: %5u rpcbind out of memory\n", task->tk_pid);
|
||||
@ -2003,6 +2005,9 @@ call_bind_status(struct rpc_task *task)
|
||||
task->tk_rebind_retry--;
|
||||
rpc_delay(task, 3*HZ);
|
||||
goto retry_timeout;
|
||||
case -ENOBUFS:
|
||||
rpc_delay(task, HZ >> 2);
|
||||
goto retry_timeout;
|
||||
case -EAGAIN:
|
||||
goto retry_timeout;
|
||||
case -ETIMEDOUT:
|
||||
@ -2026,7 +2031,6 @@ call_bind_status(struct rpc_task *task)
|
||||
case -ENETDOWN:
|
||||
case -EHOSTUNREACH:
|
||||
case -ENETUNREACH:
|
||||
case -ENOBUFS:
|
||||
case -EPIPE:
|
||||
dprintk("RPC: %5u remote rpcbind unreachable: %d\n",
|
||||
task->tk_pid, task->tk_status);
|
||||
@ -2043,7 +2047,9 @@ call_bind_status(struct rpc_task *task)
|
||||
|
||||
rpc_call_rpcerror(task, status);
|
||||
return;
|
||||
|
||||
out_next:
|
||||
task->tk_action = call_connect;
|
||||
return;
|
||||
retry_timeout:
|
||||
task->tk_status = 0;
|
||||
task->tk_action = call_bind;
|
||||
@ -2090,6 +2096,7 @@ call_connect(struct rpc_task *task)
|
||||
static void
|
||||
call_connect_status(struct rpc_task *task)
|
||||
{
|
||||
struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
|
||||
struct rpc_clnt *clnt = task->tk_client;
|
||||
int status = task->tk_status;
|
||||
|
||||
@ -2099,8 +2106,17 @@ call_connect_status(struct rpc_task *task)
|
||||
}
|
||||
|
||||
dprint_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;
|
||||
switch (status) {
|
||||
case -ECONNREFUSED:
|
||||
@ -2117,8 +2133,6 @@ call_connect_status(struct rpc_task *task)
|
||||
case -ENETDOWN:
|
||||
case -ENETUNREACH:
|
||||
case -EHOSTUNREACH:
|
||||
case -EADDRINUSE:
|
||||
case -ENOBUFS:
|
||||
case -EPIPE:
|
||||
xprt_conditional_disconnect(task->tk_rqstp->rq_xprt,
|
||||
task->tk_rqstp->rq_connect_cookie);
|
||||
@ -2127,17 +2141,20 @@ call_connect_status(struct rpc_task *task)
|
||||
/* retry with existing socket, after a delay */
|
||||
rpc_delay(task, 3*HZ);
|
||||
/* fall through */
|
||||
case -EADDRINUSE:
|
||||
case -ENOTCONN:
|
||||
case -EAGAIN:
|
||||
case -ETIMEDOUT:
|
||||
goto out_retry;
|
||||
case 0:
|
||||
clnt->cl_stats->netreconn++;
|
||||
task->tk_action = call_transmit;
|
||||
return;
|
||||
case -ENOBUFS:
|
||||
rpc_delay(task, HZ >> 2);
|
||||
goto out_retry;
|
||||
}
|
||||
rpc_call_rpcerror(task, status);
|
||||
return;
|
||||
out_next:
|
||||
task->tk_action = call_transmit;
|
||||
return;
|
||||
out_retry:
|
||||
/* Check for timeouts before looping back to call_bind */
|
||||
task->tk_action = call_bind;
|
||||
@ -2365,7 +2382,7 @@ call_status(struct rpc_task *task)
|
||||
case -ECONNABORTED:
|
||||
case -ENOTCONN:
|
||||
rpc_force_rebind(clnt);
|
||||
/* fall through */
|
||||
break;
|
||||
case -EADDRINUSE:
|
||||
rpc_delay(task, 3*HZ);
|
||||
/* fall through */
|
||||
|
@ -1408,13 +1408,6 @@ xprt_request_transmit(struct rpc_rqst *req, struct rpc_task *snd_task)
|
||||
status = -EBADMSG;
|
||||
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)) {
|
||||
status = -ERESTARTSYS;
|
||||
goto out_dequeue;
|
||||
|
Loading…
Reference in New Issue
Block a user