]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
NFSD/blocklayout: Introduce layout content structure
authorSergey Bashirov <sergeybashirov@gmail.com>
Fri, 3 Oct 2025 09:11:05 +0000 (12:11 +0300)
committerChuck Lever <chuck.lever@oracle.com>
Sun, 16 Nov 2025 23:20:11 +0000 (18:20 -0500)
Add a layout content structure instead of a single extent. The ability
to store and encode an array of extents is then used to implement support
for multiple extents per LAYOUTGET.

Signed-off-by: Sergey Bashirov <sergeybashirov@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/blocklayout.c
fs/nfsd/blocklayoutxdr.c
fs/nfsd/blocklayoutxdr.h

index 35a95501db633fcc0a98fe1f34e1c2185f84a8be..6d29ea5e8623146b4b1ab489ea10298fb6b0b000 100644 (file)
@@ -88,9 +88,10 @@ nfsd4_block_proc_layoutget(struct svc_rqst *rqstp, struct inode *inode,
                const struct svc_fh *fhp, struct nfsd4_layoutget *args)
 {
        struct nfsd4_layout_seg *seg = &args->lg_seg;
+       struct pnfs_block_layout *bl;
        struct pnfs_block_extent *bex;
        u64 length;
-       u32 block_size = i_blocksize(inode);
+       u32 nr_extents_max = 1, block_size = i_blocksize(inode);
        __be32 nfserr;
 
        if (locks_in_grace(SVC_NET(rqstp)))
@@ -102,16 +103,33 @@ nfsd4_block_proc_layoutget(struct svc_rqst *rqstp, struct inode *inode,
                goto out_error;
        }
 
+       /*
+        * RFC 8881, section 3.3.17:
+        *   The layout4 data type defines a layout for a file.
+        *
+        * RFC 8881, section 18.43.3:
+        *   The loga_maxcount field specifies the maximum layout size
+        *   (in bytes) that the client can handle. If the size of the
+        *   layout structure exceeds the size specified by maxcount,
+        *   the metadata server will return the NFS4ERR_TOOSMALL error.
+        */
+       nfserr = nfserr_toosmall;
+       if (args->lg_maxcount < PNFS_BLOCK_LAYOUT4_SIZE +
+                               PNFS_BLOCK_EXTENT_SIZE)
+               goto out_error;
+
        /*
         * Some clients barf on non-zero block numbers for NONE or INVALID
         * layouts, so make sure to zero the whole structure.
         */
        nfserr = nfserrno(-ENOMEM);
-       bex = kzalloc(sizeof(*bex), GFP_KERNEL);
-       if (!bex)
+       bl = kzalloc(struct_size(bl, extents, nr_extents_max), GFP_KERNEL);
+       if (!bl)
                goto out_error;
-       args->lg_content = bex;
+       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)
index e50afe34073719413fbbd9de36de05440ab762ad..196ef42456049bc08b177d0774c6d46e501705e7 100644 (file)
 #define NFSDDBG_FACILITY       NFSDDBG_PNFS
 
 
+/**
+ * nfsd4_block_encode_layoutget - encode block/scsi layout extent array
+ * @xdr: stream for data encoding
+ * @lgp: layoutget content, actually an array of extents to encode
+ *
+ * Encode the opaque loc_body field in the layoutget response. Since the
+ * pnfs_block_layout4 and pnfs_scsi_layout4 structures on the wire are
+ * the same, this function is used by both layout drivers.
+ *
+ * Return values:
+ *   %nfs_ok: Success, all extents encoded into @xdr
+ *   %nfserr_toosmall: Not enough space in @xdr to encode all the data
+ */
 __be32
 nfsd4_block_encode_layoutget(struct xdr_stream *xdr,
                const struct nfsd4_layoutget *lgp)
 {
-       const struct pnfs_block_extent *b = lgp->lg_content;
-       int len = sizeof(__be32) + 5 * sizeof(__be64) + sizeof(__be32);
+       const struct pnfs_block_layout *bl = lgp->lg_content;
+       u32 i, len = sizeof(__be32) + bl->nr_extents * PNFS_BLOCK_EXTENT_SIZE;
        __be32 *p;
 
        p = xdr_reserve_space(xdr, sizeof(__be32) + len);
@@ -27,14 +40,19 @@ nfsd4_block_encode_layoutget(struct xdr_stream *xdr,
                return nfserr_toosmall;
 
        *p++ = cpu_to_be32(len);
-       *p++ = cpu_to_be32(1);          /* we always return a single extent */
+       *p++ = cpu_to_be32(bl->nr_extents);
 
-       p = svcxdr_encode_deviceid4(p, &b->vol_id);
-       p = xdr_encode_hyper(p, b->foff);
-       p = xdr_encode_hyper(p, b->len);
-       p = xdr_encode_hyper(p, b->soff);
-       *p++ = cpu_to_be32(b->es);
-       return 0;
+       for (i = 0; i < bl->nr_extents; i++) {
+               const struct pnfs_block_extent *bex = bl->extents + i;
+
+               p = svcxdr_encode_deviceid4(p, &bex->vol_id);
+               p = xdr_encode_hyper(p, bex->foff);
+               p = xdr_encode_hyper(p, bex->len);
+               p = xdr_encode_hyper(p, bex->soff);
+               *p++ = cpu_to_be32(bex->es);
+       }
+
+       return nfs_ok;
 }
 
 static int
index 7d25ef689671f7056e20cb2dd02bb794b128741a..2e0c6c7d2b42f077b01ffba27900634784d42b81 100644 (file)
@@ -8,6 +8,15 @@
 struct iomap;
 struct xdr_stream;
 
+/* On the wire size of the layout4 struct with zero number of extents */
+#define PNFS_BLOCK_LAYOUT4_SIZE \
+       (sizeof(__be32) * 2 +   /* offset4 */ \
+        sizeof(__be32) * 2 +   /* length4 */ \
+        sizeof(__be32) +       /* layoutiomode4 */ \
+        sizeof(__be32) +       /* layouttype4 */ \
+        sizeof(__be32) +       /* number of bytes */ \
+        sizeof(__be32))        /* number of extents */
+
 struct pnfs_block_extent {
        struct nfsd4_deviceid           vol_id;
        u64                             foff;
@@ -21,6 +30,11 @@ struct pnfs_block_range {
        u64                             len;
 };
 
+struct pnfs_block_layout {
+       u32                             nr_extents;
+       struct pnfs_block_extent        extents[] __counted_by(nr_extents);
+};
+
 /*
  * Random upper cap for the uuid length to avoid unbounded allocation.
  * Not actually limited by the protocol.