mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	NFS: Fix O_DIRECT accounting of number of bytes read/written
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: 1ccbad9f9f ("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>
			
			
This commit is contained in:
		
							parent
							
								
									33ea5aaa87
								
							
						
					
					
						commit
						031d73ed76
					
				
					 1 changed files with 44 additions and 36 deletions
				
			
		| 
						 | 
				
			
			@ -123,32 +123,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -402,20 +419,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)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -652,6 +661,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++)
 | 
			
		||||
| 
						 | 
				
			
			@ -791,17 +803,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…
	
		Reference in a new issue