Commit 2cf0f715 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull NFS client fixes from Anna Schumaker:
 "Various O_DIRECT related fixes from Trond:
   - Error handling
   - Locking issues
   - Use the correct commit info for joining page groups
   - Fixes for rescheduling IO

  Sunrpc bad verifier fixes:
   - Report EINVAL errors from connect()
   - Revalidate creds that the server has rejected
   - Revert "SUNRPC: Fail faster on bad verifier"

  Misc:
   - Fix pNFS session trunking when MDS=DS
   - Fix zero-value filehandles for post-open getattr operations
   - Fix compiler warning about tautological comparisons
   - Revert 'SUNRPC: clean up integer overflow check' before Trond's fix"

* tag 'nfs-for-6.6-2' of git://git.linux-nfs.org/projects/anna/linux-nfs:
  SUNRPC: Silence compiler complaints about tautological comparisons
  Revert "SUNRPC: clean up integer overflow check"
  NFSv4.1: fix zero value filehandle in post open getattr
  NFSv4.1: fix pnfs MDS=DS session trunking
  Revert "SUNRPC: Fail faster on bad verifier"
  SUNRPC: Mark the cred for revalidation if the server rejects it
  NFS/pNFS: Report EINVAL errors from connect() to the server
  NFS: More fixes for nfs_direct_write_reschedule_io()
  NFS: Use the correct commit info in nfs_join_page_group()
  NFS: More O_DIRECT accounting fixes for error paths
  NFS: Fix O_DIRECT locking issues
  NFS: Fix error handling for O_DIRECT write scheduling
parents df1c357f 993b5662
Loading
Loading
Loading
Loading
+93 −41
Original line number Diff line number Diff line
@@ -93,12 +93,10 @@ nfs_direct_handle_truncated(struct nfs_direct_req *dreq,
		dreq->max_count = dreq_len;
		if (dreq->count > dreq_len)
			dreq->count = dreq_len;
	}

		if (test_bit(NFS_IOHDR_ERROR, &hdr->flags))
	if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) && !dreq->error)
		dreq->error = hdr->error;
		else /* Clear outstanding error if this is EOF */
			dreq->error = 0;
	}
}

static void
@@ -120,6 +118,18 @@ nfs_direct_count_bytes(struct nfs_direct_req *dreq,
		dreq->count = dreq_len;
}

static void nfs_direct_truncate_request(struct nfs_direct_req *dreq,
					struct nfs_page *req)
{
	loff_t offs = req_offset(req);
	size_t req_start = (size_t)(offs - dreq->io_start);

	if (req_start < dreq->max_count)
		dreq->max_count = req_start;
	if (req_start < dreq->count)
		dreq->count = req_start;
}

/**
 * nfs_swap_rw - NFS address space operation for swap I/O
 * @iocb: target I/O control block
@@ -488,7 +498,9 @@ static void nfs_direct_add_page_head(struct list_head *list,
	kref_get(&head->wb_kref);
}

static void nfs_direct_join_group(struct list_head *list, struct inode *inode)
static void nfs_direct_join_group(struct list_head *list,
				  struct nfs_commit_info *cinfo,
				  struct inode *inode)
{
	struct nfs_page *req, *subreq;

@@ -510,7 +522,7 @@ static void nfs_direct_join_group(struct list_head *list, struct inode *inode)
				nfs_release_request(subreq);
			}
		} while ((subreq = subreq->wb_this_page) != req);
		nfs_join_page_group(req, inode);
		nfs_join_page_group(req, cinfo, inode);
	}
}

@@ -528,20 +540,15 @@ nfs_direct_write_scan_commit_list(struct inode *inode,
static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
{
	struct nfs_pageio_descriptor desc;
	struct nfs_page *req, *tmp;
	struct nfs_page *req;
	LIST_HEAD(reqs);
	struct nfs_commit_info cinfo;
	LIST_HEAD(failed);

	nfs_init_cinfo_from_dreq(&cinfo, dreq);
	nfs_direct_write_scan_commit_list(dreq->inode, &reqs, &cinfo);

	nfs_direct_join_group(&reqs, dreq->inode);
	nfs_direct_join_group(&reqs, &cinfo, dreq->inode);

	dreq->count = 0;
	dreq->max_count = 0;
	list_for_each_entry(req, &reqs, wb_list)
		dreq->max_count += req->wb_bytes;
	nfs_clear_pnfs_ds_commit_verifiers(&dreq->ds_cinfo);
	get_dreq(dreq);

@@ -549,27 +556,40 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
			      &nfs_direct_write_completion_ops);
	desc.pg_dreq = dreq;

	list_for_each_entry_safe(req, tmp, &reqs, wb_list) {
	while (!list_empty(&reqs)) {
		req = nfs_list_entry(reqs.next);
		/* Bump the transmission count */
		req->wb_nio++;
		if (!nfs_pageio_add_request(&desc, req)) {
			nfs_list_move_request(req, &failed);
			spin_lock(&cinfo.inode->i_lock);
			spin_lock(&dreq->lock);
			if (dreq->error < 0) {
				desc.pg_error = dreq->error;
			} else if (desc.pg_error != -EAGAIN) {
				dreq->flags = 0;
			if (desc.pg_error < 0)
				if (!desc.pg_error)
					desc.pg_error = -EIO;
				dreq->error = desc.pg_error;
			else
				dreq->error = -EIO;
			spin_unlock(&cinfo.inode->i_lock);
			} else
				dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
			spin_unlock(&dreq->lock);
			break;
		}
		nfs_release_request(req);
	}
	nfs_pageio_complete(&desc);

	while (!list_empty(&failed)) {
		req = nfs_list_entry(failed.next);
	while (!list_empty(&reqs)) {
		req = nfs_list_entry(reqs.next);
		nfs_list_remove_request(req);
		nfs_unlock_and_release_request(req);
		if (desc.pg_error == -EAGAIN) {
			nfs_mark_request_commit(req, NULL, &cinfo, 0);
		} else {
			spin_lock(&dreq->lock);
			nfs_direct_truncate_request(dreq, req);
			spin_unlock(&dreq->lock);
			nfs_release_request(req);
		}
	}

	if (put_dreq(dreq))
@@ -589,8 +609,6 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data)
	if (status < 0) {
		/* Errors in commit are fatal */
		dreq->error = status;
		dreq->max_count = 0;
		dreq->count = 0;
		dreq->flags = NFS_ODIRECT_DONE;
	} else {
		status = dreq->error;
@@ -601,7 +619,12 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data)
	while (!list_empty(&data->pages)) {
		req = nfs_list_entry(data->pages.next);
		nfs_list_remove_request(req);
		if (status >= 0 && !nfs_write_match_verf(verf, req)) {
		if (status < 0) {
			spin_lock(&dreq->lock);
			nfs_direct_truncate_request(dreq, req);
			spin_unlock(&dreq->lock);
			nfs_release_request(req);
		} else if (!nfs_write_match_verf(verf, req)) {
			dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
			/*
			 * Despite the reboot, the write was successful,
@@ -609,7 +632,7 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data)
			 */
			req->wb_nio = 0;
			nfs_mark_request_commit(req, NULL, &cinfo, 0);
		} else /* Error or match */
		} else
			nfs_release_request(req);
		nfs_unlock_and_release_request(req);
	}
@@ -662,6 +685,7 @@ static void nfs_direct_write_clear_reqs(struct nfs_direct_req *dreq)
	while (!list_empty(&reqs)) {
		req = nfs_list_entry(reqs.next);
		nfs_list_remove_request(req);
		nfs_direct_truncate_request(dreq, req);
		nfs_release_request(req);
		nfs_unlock_and_release_request(req);
	}
@@ -711,7 +735,8 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
	}

	nfs_direct_count_bytes(dreq, hdr);
	if (test_bit(NFS_IOHDR_UNSTABLE_WRITES, &hdr->flags)) {
	if (test_bit(NFS_IOHDR_UNSTABLE_WRITES, &hdr->flags) &&
	    !test_bit(NFS_IOHDR_ERROR, &hdr->flags)) {
		if (!dreq->flags)
			dreq->flags = NFS_ODIRECT_DO_COMMIT;
		flags = dreq->flags;
@@ -755,18 +780,23 @@ static void nfs_write_sync_pgio_error(struct list_head *head, int error)
static void nfs_direct_write_reschedule_io(struct nfs_pgio_header *hdr)
{
	struct nfs_direct_req *dreq = hdr->dreq;
	struct nfs_page *req;
	struct nfs_commit_info cinfo;

	trace_nfs_direct_write_reschedule_io(dreq);

	nfs_init_cinfo_from_dreq(&cinfo, dreq);
	spin_lock(&dreq->lock);
	if (dreq->error == 0) {
	if (dreq->error == 0)
		dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
		/* fake unstable write to let common nfs resend pages */
		hdr->verf.committed = NFS_UNSTABLE;
		hdr->good_bytes = hdr->args.offset + hdr->args.count -
			hdr->io_start;
	}
	set_bit(NFS_IOHDR_REDO, &hdr->flags);
	spin_unlock(&dreq->lock);
	while (!list_empty(&hdr->pages)) {
		req = nfs_list_entry(hdr->pages.next);
		nfs_list_remove_request(req);
		nfs_unlock_request(req);
		nfs_mark_request_commit(req, NULL, &cinfo, 0);
	}
}

static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = {
@@ -794,9 +824,11 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
{
	struct nfs_pageio_descriptor desc;
	struct inode *inode = dreq->inode;
	struct nfs_commit_info cinfo;
	ssize_t result = 0;
	size_t requested_bytes = 0;
	size_t wsize = max_t(size_t, NFS_SERVER(inode)->wsize, PAGE_SIZE);
	bool defer = false;

	trace_nfs_direct_write_schedule_iovec(dreq);

@@ -837,17 +869,37 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
				break;
			}

			nfs_lock_request(req);
			if (!nfs_pageio_add_request(&desc, req)) {
				result = desc.pg_error;
				nfs_unlock_and_release_request(req);
				break;
			}
			pgbase = 0;
			bytes -= req_len;
			requested_bytes += req_len;
			pos += req_len;
			dreq->bytes_left -= req_len;

			if (defer) {
				nfs_mark_request_commit(req, NULL, &cinfo, 0);
				continue;
			}

			nfs_lock_request(req);
			if (nfs_pageio_add_request(&desc, req))
				continue;

			/* Exit on hard errors */
			if (desc.pg_error < 0 && desc.pg_error != -EAGAIN) {
				result = desc.pg_error;
				nfs_unlock_and_release_request(req);
				break;
			}

			/* If the error is soft, defer remaining requests */
			nfs_init_cinfo_from_dreq(&cinfo, dreq);
			spin_lock(&dreq->lock);
			dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
			spin_unlock(&dreq->lock);
			nfs_unlock_request(req);
			nfs_mark_request_commit(req, NULL, &cinfo, 0);
			desc.pg_error = 0;
			defer = true;
		}
		nfs_direct_release_pages(pagevec, npages);
		kvfree(pagevec);
+1 −0
Original line number Diff line number Diff line
@@ -1235,6 +1235,7 @@ static void ff_layout_io_track_ds_error(struct pnfs_layout_segment *lseg,
		case -EPFNOSUPPORT:
		case -EPROTONOSUPPORT:
		case -EOPNOTSUPP:
		case -EINVAL:
		case -ECONNREFUSED:
		case -ECONNRESET:
		case -EHOSTDOWN:
+5 −1
Original line number Diff line number Diff line
@@ -417,6 +417,8 @@ static void nfs4_add_trunk(struct nfs_client *clp, struct nfs_client *old)
		.net = old->cl_net,
		.servername = old->cl_hostname,
	};
	int max_connect = test_bit(NFS_CS_PNFS, &clp->cl_flags) ?
		clp->cl_max_connect : old->cl_max_connect;

	if (clp->cl_proto != old->cl_proto)
		return;
@@ -430,7 +432,7 @@ static void nfs4_add_trunk(struct nfs_client *clp, struct nfs_client *old)
	xprt_args.addrlen = clp_salen;

	rpc_clnt_add_xprt(old->cl_rpcclient, &xprt_args,
			  rpc_clnt_test_and_add_xprt, NULL);
			  rpc_clnt_test_and_add_xprt, &max_connect);
}

/**
@@ -1010,6 +1012,8 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
		__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);

	__set_bit(NFS_CS_DS, &cl_init.init_flags);
	__set_bit(NFS_CS_PNFS, &cl_init.init_flags);
	cl_init.max_connect = NFS_MAX_TRANSPORTS;
	/*
	 * Set an authflavor equual to the MDS value. Use the MDS nfs_client
	 * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS
+5 −1
Original line number Diff line number Diff line
@@ -2703,8 +2703,12 @@ static int _nfs4_proc_open(struct nfs4_opendata *data,
			return status;
	}
	if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) {
		struct nfs_fh *fh = &o_res->fh;

		nfs4_sequence_free_slot(&o_res->seq_res);
		nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr, NULL);
		if (o_arg->claim == NFS4_OPEN_CLAIM_FH)
			fh = NFS_FH(d_inode(data->dentry));
		nfs4_proc_getattr(server, fh, o_res->f_attr, NULL);
	}
	return 0;
}
+12 −11
Original line number Diff line number Diff line
@@ -59,7 +59,8 @@ 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_clear_request_commit(struct nfs_commit_info *cinfo,
				     struct nfs_page *req);
static void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo,
				      struct inode *inode);
static struct nfs_page *
@@ -502,8 +503,8 @@ nfs_destroy_unlinked_subrequests(struct nfs_page *destroy_list,
 * the (former) group.  All subrequests are removed from any write or commit
 * lists, unlinked from the group and destroyed.
 */
void
nfs_join_page_group(struct nfs_page *head, struct inode *inode)
void nfs_join_page_group(struct nfs_page *head, struct nfs_commit_info *cinfo,
			 struct inode *inode)
{
	struct nfs_page *subreq;
	struct nfs_page *destroy_list = NULL;
@@ -533,7 +534,7 @@ nfs_join_page_group(struct nfs_page *head, struct inode *inode)
	 * Commit list removal accounting is done after locks are dropped */
	subreq = head;
	do {
		nfs_clear_request_commit(subreq);
		nfs_clear_request_commit(cinfo, subreq);
		subreq = subreq->wb_this_page;
	} while (subreq != head);

@@ -566,8 +567,10 @@ static struct nfs_page *nfs_lock_and_join_requests(struct folio *folio)
{
	struct inode *inode = folio_file_mapping(folio)->host;
	struct nfs_page *head;
	struct nfs_commit_info cinfo;
	int ret;

	nfs_init_cinfo_from_inode(&cinfo, inode);
	/*
	 * A reference is taken only on the head request which acts as a
	 * reference to the whole page group - the group will not be destroyed
@@ -584,7 +587,7 @@ static struct nfs_page *nfs_lock_and_join_requests(struct folio *folio)
		return ERR_PTR(ret);
	}

	nfs_join_page_group(head, inode);
	nfs_join_page_group(head, &cinfo, inode);

	return head;
}
@@ -955,18 +958,16 @@ static void nfs_folio_clear_commit(struct folio *folio)
}

/* Called holding the request lock on @req */
static void
nfs_clear_request_commit(struct nfs_page *req)
static void nfs_clear_request_commit(struct nfs_commit_info *cinfo,
				     struct nfs_page *req)
{
	if (test_bit(PG_CLEAN, &req->wb_flags)) {
		struct nfs_open_context *ctx = nfs_req_openctx(req);
		struct inode *inode = d_inode(ctx->dentry);
		struct nfs_commit_info cinfo;

		nfs_init_cinfo_from_inode(&cinfo, inode);
		mutex_lock(&NFS_I(inode)->commit_mutex);
		if (!pnfs_clear_request_commit(req, &cinfo)) {
			nfs_request_remove_commit_list(req, &cinfo);
		if (!pnfs_clear_request_commit(req, cinfo)) {
			nfs_request_remove_commit_list(req, cinfo);
		}
		mutex_unlock(&NFS_I(inode)->commit_mutex);
		nfs_folio_clear_commit(nfs_page_to_folio(req));
Loading