Skip to content

Commit 5d3869a

Browse files
Olga KornievskaiaTrond Myklebust
authored andcommitted
NFS: fix writeback in presence of errors
After running xfstest generic/751, in certain conditions, can have a writeback IO stuck while experiencing one of the two patterns. Pattern#1: writeback IO experiences ENOSPC on an offset smaller than the filesize. Example, write offset=0 len=4096 how=unstable OK write offset=8192 len=4096 how=unstable OK write offset=12288 len=4096 how=unstable ENOSPC write offset=4096 len=4096 how=unstable ENOSPC client sends a commit and receives a verifier which is different from the last successful write. It marks pages dirty and writeback retries. But it again send writes unstable and gets into the same pattern, running into the ENOSPC error and sending a commit because writes were sent at unstable. Pattern#2: an unstable write followed by a short write and ENOSPC. write offset=0 len=4096 how=unstable OK write offset=4096 len=4096 how=unstable returns OK but count=100 write offset=4197 len=3996 how=stable returns ENOSPC client send a commit and receives a verifier different from the last unstable write. The same behaviour is retried in a loop. Instead, this patch proposes to identify those conditions and mark requests to be done synchronously instead. Previous solution tried to mark it in the nfs_page, however that's not persistent thus instead mark it in the nfs_open_context. Furthermore, the same problem occurs during localio code path so recognize that IO needs to be done sync in that case as well. Signed-off-by: Olga Kornievskaia <okorniev@redhat.com> Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
1 parent 43ea703 commit 5d3869a

4 files changed

Lines changed: 27 additions & 1 deletion

File tree

fs/nfs/localio.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,8 @@ static void nfs_local_call_write(struct work_struct *work)
865865
file_start_write(filp);
866866
n_iters = atomic_read(&iocb->n_iters);
867867
for (int i = 0; i < n_iters ; i++) {
868+
size_t icount;
869+
868870
if (iocb->iter_is_dio_aligned[i]) {
869871
iocb->kiocb.ki_flags |= IOCB_DIRECT;
870872
/* Only use AIO completion if DIO-aligned segment is last */
@@ -881,8 +883,16 @@ static void nfs_local_call_write(struct work_struct *work)
881883
if (status == -EIOCBQUEUED)
882884
continue;
883885
/* Break on completion, errors, or short writes */
886+
icount = iov_iter_count(&iocb->iters[i]);
884887
if (nfs_local_pgio_done(iocb, status) || status < 0 ||
885-
(size_t)status < iov_iter_count(&iocb->iters[i])) {
888+
(size_t)status < icount) {
889+
if ((size_t)status < icount) {
890+
struct nfs_lock_context *ctx =
891+
iocb->hdr->req->wb_lock_context;
892+
893+
set_bit(NFS_CONTEXT_WRITE_SYNC,
894+
&ctx->open_context->flags);
895+
}
886896
nfs_local_write_iocb_done(iocb);
887897
break;
888898
}
@@ -901,6 +911,9 @@ static void nfs_local_do_write(struct nfs_local_kiocb *iocb,
901911
__func__, hdr->args.count, hdr->args.offset,
902912
(hdr->args.stable == NFS_UNSTABLE) ? "unstable" : "stable");
903913

914+
if (test_bit(NFS_CONTEXT_WRITE_SYNC,
915+
&hdr->req->wb_lock_context->open_context->flags))
916+
hdr->args.stable = NFS_FILE_SYNC;
904917
switch (hdr->args.stable) {
905918
default:
906919
break;

fs/nfs/pagelist.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,9 @@ static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
11861186

11871187
nfs_page_group_lock(req);
11881188

1189+
if (test_bit(NFS_CONTEXT_WRITE_SYNC,
1190+
&req->wb_lock_context->open_context->flags))
1191+
desc->pg_ioflags |= FLUSH_STABLE;
11891192
subreq = req;
11901193
subreq_size = subreq->wb_bytes;
11911194
for(;;) {

fs/nfs/write.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,9 +927,13 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
927927
goto remove_req;
928928
}
929929
if (nfs_write_need_commit(hdr)) {
930+
struct nfs_open_context *ctx =
931+
hdr->req->wb_lock_context->open_context;
932+
930933
/* Reset wb_nio, since the write was successful. */
931934
req->wb_nio = 0;
932935
memcpy(&req->wb_verf, &hdr->verf.verifier, sizeof(req->wb_verf));
936+
clear_bit(NFS_CONTEXT_WRITE_SYNC, &ctx->flags);
933937
nfs_mark_request_commit(req, hdr->lseg, &cinfo,
934938
hdr->ds_commit_idx);
935939
goto next;
@@ -1553,7 +1557,10 @@ static void nfs_writeback_result(struct rpc_task *task,
15531557

15541558
if (resp->count < argp->count && !list_empty(&hdr->pages)) {
15551559
static unsigned long complain;
1560+
struct nfs_open_context *ctx =
1561+
hdr->req->wb_lock_context->open_context;
15561562

1563+
set_bit(NFS_CONTEXT_WRITE_SYNC, &ctx->flags);
15571564
/* This a short write! */
15581565
nfs_inc_stats(hdr->inode, NFSIOS_SHORTWRITE);
15591566

@@ -1837,6 +1844,8 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
18371844
/* We have a mismatch. Write the page again */
18381845
dprintk(" mismatch\n");
18391846
nfs_mark_request_dirty(req);
1847+
set_bit(NFS_CONTEXT_WRITE_SYNC,
1848+
&req->wb_lock_context->open_context->flags);
18401849
atomic_long_inc(&NFS_I(data->inode)->redirtied_pages);
18411850
next:
18421851
nfs_unlock_and_release_request(req);

include/linux/nfs_fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ struct nfs_open_context {
109109
#define NFS_CONTEXT_BAD (2)
110110
#define NFS_CONTEXT_UNLOCK (3)
111111
#define NFS_CONTEXT_FILE_OPEN (4)
112+
#define NFS_CONTEXT_WRITE_SYNC (5)
112113

113114
struct nfs4_threshold *mdsthreshold;
114115
struct list_head list;

0 commit comments

Comments
 (0)