NFS: Fix O_DIRECT accounting of number of bytes read/written
commit 031d73ed768a40684f3ca21992265ffdb6a270bf upstream. When a series of O_DIRECT reads or writes are truncated, either due to eof or due to an error, then we should return the number of contiguous bytes that were received/sent starting at the offset specified by the application. Currently, we are failing to correctly check contiguity, and so we're failing the generic/465 in xfstests when the race between the read and write RPCs causes the file to get extended while the 2 reads are outstanding. If the first read RPC call wins the race and returns with eof set, we should treat the second read RPC as being truncated. Reported-by: Su Yanjun <suyj.fnst@cn.fujitsu.com> Fixes: 1ccbad9f9f9bd ("nfs: fix DIO good bytes calculation") Cc: stable@vger.kernel.org # 4.1+ Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
f94575d0b4
commit
c3388b1c33
@ -122,32 +122,49 @@ static inline int put_dreq(struct nfs_direct_req *dreq)
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_direct_good_bytes(struct nfs_direct_req *dreq, struct nfs_pgio_header *hdr)
|
||||
nfs_direct_handle_truncated(struct nfs_direct_req *dreq,
|
||||
const struct nfs_pgio_header *hdr,
|
||||
ssize_t dreq_len)
|
||||
{
|
||||
int i;
|
||||
ssize_t count;
|
||||
struct nfs_direct_mirror *mirror = &dreq->mirrors[hdr->pgio_mirror_idx];
|
||||
|
||||
WARN_ON_ONCE(dreq->count >= dreq->max_count);
|
||||
if (!(test_bit(NFS_IOHDR_ERROR, &hdr->flags) ||
|
||||
test_bit(NFS_IOHDR_EOF, &hdr->flags)))
|
||||
return;
|
||||
if (dreq->max_count >= dreq_len) {
|
||||
dreq->max_count = dreq_len;
|
||||
if (dreq->count > dreq_len)
|
||||
dreq->count = dreq_len;
|
||||
|
||||
if (dreq->mirror_count == 1) {
|
||||
dreq->mirrors[hdr->pgio_mirror_idx].count += hdr->good_bytes;
|
||||
dreq->count += hdr->good_bytes;
|
||||
} else {
|
||||
/* mirrored writes */
|
||||
count = dreq->mirrors[hdr->pgio_mirror_idx].count;
|
||||
if (count + dreq->io_start < hdr->io_start + hdr->good_bytes) {
|
||||
count = hdr->io_start + hdr->good_bytes - dreq->io_start;
|
||||
dreq->mirrors[hdr->pgio_mirror_idx].count = count;
|
||||
}
|
||||
/* update the dreq->count by finding the minimum agreed count from all
|
||||
* mirrors */
|
||||
count = dreq->mirrors[0].count;
|
||||
|
||||
for (i = 1; i < dreq->mirror_count; i++)
|
||||
count = min(count, dreq->mirrors[i].count);
|
||||
|
||||
dreq->count = count;
|
||||
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags))
|
||||
dreq->error = hdr->error;
|
||||
else /* Clear outstanding error if this is EOF */
|
||||
dreq->error = 0;
|
||||
}
|
||||
if (mirror->count > dreq_len)
|
||||
mirror->count = dreq_len;
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_direct_count_bytes(struct nfs_direct_req *dreq,
|
||||
const struct nfs_pgio_header *hdr)
|
||||
{
|
||||
struct nfs_direct_mirror *mirror = &dreq->mirrors[hdr->pgio_mirror_idx];
|
||||
loff_t hdr_end = hdr->io_start + hdr->good_bytes;
|
||||
ssize_t dreq_len = 0;
|
||||
|
||||
if (hdr_end > dreq->io_start)
|
||||
dreq_len = hdr_end - dreq->io_start;
|
||||
|
||||
nfs_direct_handle_truncated(dreq, hdr, dreq_len);
|
||||
|
||||
if (dreq_len > dreq->max_count)
|
||||
dreq_len = dreq->max_count;
|
||||
|
||||
if (mirror->count < dreq_len)
|
||||
mirror->count = dreq_len;
|
||||
if (dreq->count < dreq_len)
|
||||
dreq->count = dreq_len;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -401,20 +418,12 @@ static void nfs_direct_read_completion(struct nfs_pgio_header *hdr)
|
||||
struct nfs_direct_req *dreq = hdr->dreq;
|
||||
|
||||
spin_lock(&dreq->lock);
|
||||
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags))
|
||||
dreq->error = hdr->error;
|
||||
|
||||
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;
|
||||
|
||||
nfs_direct_count_bytes(dreq, hdr);
|
||||
spin_unlock(&dreq->lock);
|
||||
|
||||
while (!list_empty(&hdr->pages)) {
|
||||
@ -651,6 +660,9 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
|
||||
nfs_direct_write_scan_commit_list(dreq->inode, &reqs, &cinfo);
|
||||
|
||||
dreq->count = 0;
|
||||
dreq->max_count = 0;
|
||||
list_for_each_entry(req, &reqs, wb_list)
|
||||
dreq->max_count += req->wb_bytes;
|
||||
dreq->verf.committed = NFS_INVALID_STABLE_HOW;
|
||||
nfs_clear_pnfs_ds_commit_verifiers(&dreq->ds_cinfo);
|
||||
for (i = 0; i < dreq->mirror_count; i++)
|
||||
@ -783,17 +795,13 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
|
||||
nfs_init_cinfo_from_dreq(&cinfo, dreq);
|
||||
|
||||
spin_lock(&dreq->lock);
|
||||
|
||||
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags))
|
||||
dreq->error = hdr->error;
|
||||
|
||||
if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) {
|
||||
spin_unlock(&dreq->lock);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
nfs_direct_count_bytes(dreq, hdr);
|
||||
if (hdr->good_bytes != 0) {
|
||||
nfs_direct_good_bytes(dreq, hdr);
|
||||
if (nfs_write_need_commit(hdr)) {
|
||||
if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES)
|
||||
request_commit = true;
|
||||
|
Loading…
x
Reference in New Issue
Block a user