diff --git a/Makefile b/Makefile index b435b56594f0..ce1eec0b5010 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 57 +SUBLEVEL = 58 EXTRAVERSION = NAME = Curry Ramen diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 5a976fa343df..3bb530d4bb5c 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -93,10 +93,12 @@ 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) && !dreq->error) - dreq->error = hdr->error; + if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) + dreq->error = hdr->error; + else /* Clear outstanding error if this is EOF */ + dreq->error = 0; + } } static void @@ -118,18 +120,6 @@ 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 @@ -500,9 +490,7 @@ 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 nfs_commit_info *cinfo, - struct inode *inode) +static void nfs_direct_join_group(struct list_head *list, struct inode *inode) { struct nfs_page *req, *subreq; @@ -524,7 +512,7 @@ static void nfs_direct_join_group(struct list_head *list, nfs_release_request(subreq); } } while ((subreq = subreq->wb_this_page) != req); - nfs_join_page_group(req, cinfo, inode); + nfs_join_page_group(req, inode); } } @@ -542,15 +530,20 @@ 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; + struct nfs_page *req, *tmp; 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, &cinfo, dreq->inode); + nfs_direct_join_group(&reqs, 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); @@ -558,40 +551,27 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) &nfs_direct_write_completion_ops); desc.pg_dreq = dreq; - while (!list_empty(&reqs)) { - req = nfs_list_entry(reqs.next); + list_for_each_entry_safe(req, tmp, &reqs, wb_list) { /* Bump the transmission count */ req->wb_nio++; if (!nfs_pageio_add_request(&desc, req)) { - 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) - desc.pg_error = -EIO; + nfs_list_move_request(req, &failed); + spin_lock(&cinfo.inode->i_lock); + dreq->flags = 0; + if (desc.pg_error < 0) dreq->error = desc.pg_error; - } else - dreq->flags = NFS_ODIRECT_RESCHED_WRITES; - spin_unlock(&dreq->lock); - break; + else + dreq->error = -EIO; + spin_unlock(&cinfo.inode->i_lock); } nfs_release_request(req); } nfs_pageio_complete(&desc); - while (!list_empty(&reqs)) { - req = nfs_list_entry(reqs.next); + while (!list_empty(&failed)) { + req = nfs_list_entry(failed.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)) @@ -611,6 +591,8 @@ 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; @@ -621,12 +603,7 @@ 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) { - 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)) { + if (status >= 0 && !nfs_write_match_verf(verf, req)) { dreq->flags = NFS_ODIRECT_RESCHED_WRITES; /* * Despite the reboot, the write was successful, @@ -634,7 +611,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 + } else /* Error or match */ nfs_release_request(req); nfs_unlock_and_release_request(req); } @@ -687,7 +664,6 @@ 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); } @@ -737,8 +713,7 @@ 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) && - !test_bit(NFS_IOHDR_ERROR, &hdr->flags)) { + if (test_bit(NFS_IOHDR_UNSTABLE_WRITES, &hdr->flags)) { if (!dreq->flags) dreq->flags = NFS_ODIRECT_DO_COMMIT; flags = dreq->flags; @@ -782,23 +757,18 @@ 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; - 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); + /* 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; } + spin_unlock(&dreq->lock); } static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = { @@ -826,11 +796,9 @@ 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); @@ -871,39 +839,19 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, 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); req->wb_index = pos >> PAGE_SHIFT; req->wb_offset = pos & ~PAGE_MASK; - if (nfs_pageio_add_request(&desc, req)) - continue; - - /* Exit on hard errors */ - if (desc.pg_error < 0 && desc.pg_error != -EAGAIN) { + if (!nfs_pageio_add_request(&desc, req)) { 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; + pgbase = 0; + bytes -= req_len; + requested_bytes += req_len; + pos += req_len; + dreq->bytes_left -= req_len; } nfs_direct_release_pages(pagevec, npages); kvfree(pagevec); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 0a8aed0ac994..f41d24b54fd1 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -58,8 +58,7 @@ 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_commit_info *cinfo, - 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); static struct nfs_page * @@ -503,8 +502,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 nfs_commit_info *cinfo, - struct inode *inode) +void +nfs_join_page_group(struct nfs_page *head, struct inode *inode) { struct nfs_page *subreq; struct nfs_page *destroy_list = NULL; @@ -534,7 +533,7 @@ void nfs_join_page_group(struct nfs_page *head, struct nfs_commit_info *cinfo, * Commit list removal accounting is done after locks are dropped */ subreq = head; do { - nfs_clear_request_commit(cinfo, subreq); + nfs_clear_request_commit(subreq); subreq = subreq->wb_this_page; } while (subreq != head); @@ -568,10 +567,8 @@ nfs_lock_and_join_requests(struct page *page) { struct inode *inode = page_file_mapping(page)->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 @@ -588,7 +585,7 @@ nfs_lock_and_join_requests(struct page *page) return ERR_PTR(ret); } - nfs_join_page_group(head, &cinfo, inode); + nfs_join_page_group(head, inode); return head; } @@ -959,16 +956,18 @@ nfs_clear_page_commit(struct page *page) } /* Called holding the request lock on @req */ -static void nfs_clear_request_commit(struct nfs_commit_info *cinfo, - struct nfs_page *req) +static void +nfs_clear_request_commit(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_clear_page_commit(req->wb_page); diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index e39a8cf8b179..ba7e2e4b0926 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -145,9 +145,7 @@ extern void nfs_unlock_request(struct nfs_page *req); extern void nfs_unlock_and_release_request(struct nfs_page *); extern struct nfs_page *nfs_page_group_lock_head(struct nfs_page *req); extern int nfs_page_group_lock_subrequests(struct nfs_page *head); -extern void nfs_join_page_group(struct nfs_page *head, - struct nfs_commit_info *cinfo, - struct inode *inode); +extern void nfs_join_page_group(struct nfs_page *head, struct inode *inode); extern int nfs_page_group_lock(struct nfs_page *); extern void nfs_page_group_unlock(struct nfs_page *); extern bool nfs_page_group_sync_on_bit(struct nfs_page *, unsigned int); diff --git a/lib/test_meminit.c b/lib/test_meminit.c index 0ae35223d773..85d8dd8e01dc 100644 --- a/lib/test_meminit.c +++ b/lib/test_meminit.c @@ -93,7 +93,7 @@ static int __init test_pages(int *total_failures) int failures = 0, num_tests = 0; int i; - for (i = 0; i <= MAX_ORDER; i++) + for (i = 0; i < MAX_ORDER; i++) num_tests += do_alloc_pages_order(i, &failures); REPORT_FAILURES_IN_FN();