Skip to contentSkip to conversation

nfsd: supports read buffer from multiples pages

PATCH Kinglong Mee 4 participants 7 messages Jan 31, 2016 – Feb 29, 2016

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

ltp fsync02 will cause nfs sending LAYOUTCOMMIT with length larger than two pages. nfsd returns NFSERR_BAD_XDR right now.

This patch lets nfsd supports read buffer from multiples pages.

---
 fs/nfsd/nfs4xdr.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)
fs/nfsd/nfs4xdr.c +14 -4
index d6ef095..fcf399f 100644
@@ -143,11 +143,11 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
 	 * Maybe we need a new page, maybe we have just run out
 	 */
 	unsigned int avail = (char *)argp->end - (char *)argp->p;
+	unsigned int copied = 0;
 	__be32 *p;
+
 	if (avail + argp->pagelen < nbytes)
 		return NULL;
-	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
-		return NULL;
 	/* ok, we can do it with the current plus the next page */
 	if (nbytes <= sizeof(argp->tmp))
 		p = argp->tmp;
@@ -164,9 +164,19 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
 	 * guarantee p points to at least nbytes bytes.
 	 */
 	memcpy(p, argp->p, avail);
+	copied += avail;
+	nbytes -= avail;
+
+	while (nbytes > PAGE_SIZE) {
+		next_decode_page(argp);
+		memcpy(((char*)p) + copied, argp->p, PAGE_SIZE);
+		copied += PAGE_SIZE;
+		nbytes -= PAGE_SIZE;
+	}
+
 	next_decode_page(argp);
-	memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
-	argp->p += XDR_QUADLEN(nbytes - avail);
+	memcpy(((char*)p) + copied, argp->p, nbytes);
+	argp->p += XDR_QUADLEN(nbytes);
 	return p;
 }
 
Signature · 1 line
2.5.0
#1 lore raw
Quoted 2 lines · Kinglong Mee

ltp fsync02 will cause nfs sending LAYOUTCOMMIT with length larger than two pages. nfsd returns NFSERR_BAD_XDR right now.

This is with the xfs block layout?

Christoph, do we know anything about average or worst-case sizes for that layout update field?

Quoted 1 line

This patch lets nfsd supports read buffer from multiples pages.

Hm. We'll end up kmalloc()ing the passed-in field length:

		p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);

We still do still have that (avail + argp->pagelen) limit, so we're not going to pass arbitrarily large nbytes straight from the network to kmalloc. But we do try to avoid depending on higher-order allocations.

--b.

Quoted 48 lines
---
 fs/nfsd/nfs4xdr.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)
fs/nfsd/nfs4xdr.c +14 -4
index d6ef095..fcf399f 100644
@@ -143,11 +143,11 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
 	 * Maybe we need a new page, maybe we have just run out
 	 */
 	unsigned int avail = (char *)argp->end - (char *)argp->p;
+	unsigned int copied = 0;
 	__be32 *p;
+
 	if (avail + argp->pagelen < nbytes)
 		return NULL;
-	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
-		return NULL;
 	/* ok, we can do it with the current plus the next page */
 	if (nbytes <= sizeof(argp->tmp))
 		p = argp->tmp;
@@ -164,9 +164,19 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
 	 * guarantee p points to at least nbytes bytes.
 	 */
 	memcpy(p, argp->p, avail);
+	copied += avail;
+	nbytes -= avail;
+
+	while (nbytes > PAGE_SIZE) {
+		next_decode_page(argp);
+		memcpy(((char*)p) + copied, argp->p, PAGE_SIZE);
+		copied += PAGE_SIZE;
+		nbytes -= PAGE_SIZE;
+	}
+
 	next_decode_page(argp);
-	memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
-	argp->p += XDR_QUADLEN(nbytes - avail);
+	memcpy(((char*)p) + copied, argp->p, nbytes);
+	argp->p += XDR_QUADLEN(nbytes);
 	return p;
 }
 
Signature · 1 line
2.5.0
#2 lore raw
Quoted 11 lines · bfields
Quoted 2 lines · Kinglong Mee

ltp fsync02 will cause nfs sending LAYOUTCOMMIT with length larger than two pages. nfsd returns NFSERR_BAD_XDR right now.

This is with the xfs block layout?

Christoph, do we know anything about average or worst-case sizes for that layout update field?

Quoted 1 line

This patch lets nfsd supports read buffer from multiples pages.

Quoted 7 lines

Hm. We'll end up kmalloc()ing the passed-in field length:

		p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);

We still do still have that (avail + argp->pagelen) limit, so we're not going to pass arbitrarily large nbytes straight from the network to kmalloc. But we do try to avoid depending on higher-order allocations.

(Which it looks like we were allowing before, possibly by accident. But I doubt they were actually happening in practice, so that's not evidence that we don't need to worry about allocations greater than a page.)

--b.

#3 lore raw
In reply to J. Bruce Fields
Quoted 5 lines · J. Bruce Fields
Quoted 2 lines · Kinglong Mee

ltp fsync02 will cause nfs sending LAYOUTCOMMIT with length larger than two pages. nfsd returns NFSERR_BAD_XDR right now.

This is with the xfs block layout?

Yes, xfs block layout.
Tested by ltp's fsync02.

thanks,
Kinglong Mee

Quoted 65 lines

Christoph, do we know anything about average or worst-case sizes for that layout update field?

Quoted 1 line

This patch lets nfsd supports read buffer from multiples pages.

Hm. We'll end up kmalloc()ing the passed-in field length:

		p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);

We still do still have that (avail + argp->pagelen) limit, so we're not going to pass arbitrarily large nbytes straight from the network to kmalloc. But we do try to avoid depending on higher-order allocations.

--b.

Quoted 48 lines
---
 fs/nfsd/nfs4xdr.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)
fs/nfsd/nfs4xdr.c +14 -4
index d6ef095..fcf399f 100644
@@ -143,11 +143,11 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
 	 * Maybe we need a new page, maybe we have just run out
 	 */
 	unsigned int avail = (char *)argp->end - (char *)argp->p;
+	unsigned int copied = 0;
 	__be32 *p;
+
 	if (avail + argp->pagelen < nbytes)
 		return NULL;
-	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
-		return NULL;
 	/* ok, we can do it with the current plus the next page */
 	if (nbytes <= sizeof(argp->tmp))
 		p = argp->tmp;
@@ -164,9 +164,19 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
 	 * guarantee p points to at least nbytes bytes.
 	 */
 	memcpy(p, argp->p, avail);
+	copied += avail;
+	nbytes -= avail;
+
+	while (nbytes > PAGE_SIZE) {
+		next_decode_page(argp);
+		memcpy(((char*)p) + copied, argp->p, PAGE_SIZE);
+		copied += PAGE_SIZE;
+		nbytes -= PAGE_SIZE;
+	}
+
 	next_decode_page(argp);
-	memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
-	argp->p += XDR_QUADLEN(nbytes - avail);
+	memcpy(((char*)p) + copied, argp->p, nbytes);
+	argp->p += XDR_QUADLEN(nbytes);
 	return p;
 }
 
Signature · 1 line
2.5.0
#4 lore raw
In reply to J. Bruce Fields
Quoted 4 lines · J. Bruce Fields

This is with the xfs block layout?

Christoph, do we know anything about average or worst-case sizes for that layout update field?

The average is rather small and fits into a single page, the worst case is basically unlimited:

(file size / block size) * sizeof(pnfs_block_extent)

by the protocol, and about half that for a non-stupid client as it would merge consecutive blocks and only trigger something close to the worst case for a "block allocated, block hole, block allocated, ..." pattern.

#5 lore raw
Quoted 9 lines · Christoph Hellwig
Quoted 4 lines · J. Bruce Fields

This is with the xfs block layout?

Christoph, do we know anything about average or worst-case sizes for that layout update field?

The average is rather small and fits into a single page, the worst case is basically unlimited:

Quoted 6 lines

(file size / block size) * sizeof(pnfs_block_extent)

by the protocol, and about half that for a non-stupid client as it would merge consecutive blocks and only trigger something close to the worst case for a "block allocated, block hole, block allocated, ..." pattern.

OK. And what's the failure mode if the layoutcommit fails? I guess the client returns the layout and redoes IO through the MDS. For a rare failure maybe that's not so terrible.

So I guess the right thing to do is take Kinglong's patch for now.

After that, it wouldn't be that hard for nfsd4_block_proc_layoutcommit to decode from an array of pages. But does the iomaps array end up being just as big?

--b.

#6 lore raw
Quoted 5 lines · J. Bruce Fields

OK. And what's the failure mode if the layoutcommit fails? I guess the client returns the layout and redoes IO through the MDS. For a rare failure maybe that's not so terrible.

So I guess the right thing to do is take Kinglong's patch for now.

Yes, it would be great to take it.

Quoted 3 lines

After that, it wouldn't be that hard for nfsd4_block_proc_layoutcommit to decode from an array of pages. But does the iomaps array end up being just as big?

The block layout extents are rather bloated, so it will be significantly smaller.