{
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)))
PNFS_BLOCK_EXTENT_SIZE)
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.
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: