diff options
| -rw-r--r-- | fs/nfsd/blocklayout.c | 47 |
1 files changed, 34 insertions, 13 deletions
diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c index 6d29ea5e8623..101cccbee4a3 100644 --- a/fs/nfsd/blocklayout.c +++ b/fs/nfsd/blocklayout.c @@ -89,9 +89,9 @@ nfsd4_block_proc_layoutget(struct svc_rqst *rqstp, struct inode *inode, { struct nfsd4_layout_seg *seg = &args->lg_seg; struct pnfs_block_layout *bl; - struct pnfs_block_extent *bex; - u64 length; - u32 nr_extents_max = 1, block_size = i_blocksize(inode); + struct pnfs_block_extent *first_bex, *last_bex; + u64 offset = seg->offset, length = seg->length; + u32 i, nr_extents_max, block_size = i_blocksize(inode); __be32 nfserr; if (locks_in_grace(SVC_NET(rqstp))) @@ -119,6 +119,13 @@ nfsd4_block_proc_layoutget(struct svc_rqst *rqstp, struct inode *inode, goto out_error; /* + * Limit the maximum layout size to avoid allocating + * a large buffer on the server for each layout request. + */ + nr_extents_max = (min(args->lg_maxcount, PAGE_SIZE) - + PNFS_BLOCK_LAYOUT4_SIZE) / PNFS_BLOCK_EXTENT_SIZE; + + /* * Some clients barf on non-zero block numbers for NONE or INVALID * layouts, so make sure to zero the whole structure. */ @@ -129,23 +136,37 @@ nfsd4_block_proc_layoutget(struct svc_rqst *rqstp, struct inode *inode, bl->nr_extents = nr_extents_max; args->lg_content = bl; - bex = &bl->extents[0]; - nfserr = nfsd4_block_map_extent(inode, fhp, seg->offset, seg->length, - seg->iomode, args->lg_minlength, bex); - if (nfserr != nfs_ok) - goto out_error; + for (i = 0; i < bl->nr_extents; i++) { + struct pnfs_block_extent *bex = bl->extents + i; + u64 bex_length; + + nfserr = nfsd4_block_map_extent(inode, fhp, offset, length, + seg->iomode, args->lg_minlength, bex); + if (nfserr != nfs_ok) + goto out_error; + + bex_length = bex->len - (offset - bex->foff); + if (bex_length >= length) { + bl->nr_extents = i + 1; + break; + } + + offset = bex->foff + bex->len; + length -= bex_length; + } + + first_bex = bl->extents; + last_bex = bl->extents + bl->nr_extents - 1; nfserr = nfserr_layoutunavailable; - length = bex->foff + bex->len - seg->offset; + length = last_bex->foff + last_bex->len - seg->offset; if (length < args->lg_minlength) { dprintk("pnfsd: extent smaller than minlength\n"); goto out_error; } - seg->offset = bex->foff; - seg->length = bex->len; - - dprintk("GET: 0x%llx:0x%llx %d\n", bex->foff, bex->len, bex->es); + seg->offset = first_bex->foff; + seg->length = last_bex->foff - first_bex->foff + last_bex->len; return nfs_ok; out_error: |
