Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6650239a authored by Trond Myklebust's avatar Trond Myklebust
Browse files

NFS: Don't use vm_map_ram() in readdir



vm_map_ram() is not available on NOMMU platforms, and causes trouble
on incoherrent architectures such as ARM when we access the page data
through both the direct and the virtual mapping.

The alternative is to use the direct mapping to access page data
for the case when we are not crossing a page boundary, but to copy
the data into a linear scratch buffer when we are accessing data
that spans page boundaries.

Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Tested-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
Cc: stable@kernel.org  [2.6.37]
parent 3c0eee3f
Loading
Loading
Loading
Loading
+21 −23
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/sched.h>
#include <linux/vmalloc.h>
#include <linux/kmemleak.h>

#include "delegation.h"
@@ -459,25 +458,26 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
/* Perform conversion from xdr to cache array */
static
int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry,
				void *xdr_page, struct page *page, unsigned int buflen)
				struct page **xdr_pages, struct page *page, unsigned int buflen)
{
	struct xdr_stream stream;
	struct xdr_buf buf;
	__be32 *ptr = xdr_page;
	struct xdr_buf buf = {
		.pages = xdr_pages,
		.page_len = buflen,
		.buflen = buflen,
		.len = buflen,
	};
	struct page *scratch;
	struct nfs_cache_array *array;
	unsigned int count = 0;
	int status;

	buf.head->iov_base = xdr_page;
	buf.head->iov_len = buflen;
	buf.tail->iov_len = 0;
	buf.page_base = 0;
	buf.page_len = 0;
	buf.buflen = buf.head->iov_len;
	buf.len = buf.head->iov_len;

	xdr_init_decode(&stream, &buf, ptr);
	scratch = alloc_page(GFP_KERNEL);
	if (scratch == NULL)
		return -ENOMEM;

	xdr_init_decode(&stream, &buf, NULL);
	xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);

	do {
		status = xdr_decode(desc, entry, &stream);
@@ -506,6 +506,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
		} else
			status = PTR_ERR(array);
	}

	put_page(scratch);
	return status;
}

@@ -521,7 +523,6 @@ static
void nfs_readdir_free_large_page(void *ptr, struct page **pages,
		unsigned int npages)
{
	vm_unmap_ram(ptr, npages);
	nfs_readdir_free_pagearray(pages, npages);
}

@@ -530,9 +531,8 @@ void nfs_readdir_free_large_page(void *ptr, struct page **pages,
 * to nfs_readdir_free_large_page
 */
static
void *nfs_readdir_large_page(struct page **pages, unsigned int npages)
int nfs_readdir_large_page(struct page **pages, unsigned int npages)
{
	void *ptr;
	unsigned int i;

	for (i = 0; i < npages; i++) {
@@ -541,13 +541,11 @@ void *nfs_readdir_large_page(struct page **pages, unsigned int npages)
			goto out_freepages;
		pages[i] = page;
	}
	return 0;

	ptr = vm_map_ram(pages, npages, 0, PAGE_KERNEL);
	if (!IS_ERR_OR_NULL(ptr))
		return ptr;
out_freepages:
	nfs_readdir_free_pagearray(pages, i);
	return NULL;
	return -ENOMEM;
}

static
@@ -577,8 +575,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
	memset(array, 0, sizeof(struct nfs_cache_array));
	array->eof_index = -1;

	pages_ptr = nfs_readdir_large_page(pages, array_size);
	if (!pages_ptr)
	status = nfs_readdir_large_page(pages, array_size);
	if (status < 0)
		goto out_release_array;
	do {
		unsigned int pglen;
@@ -587,7 +585,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
		if (status < 0)
			break;
		pglen = status;
		status = nfs_readdir_page_filler(desc, &entry, pages_ptr, page, pglen);
		status = nfs_readdir_page_filler(desc, &entry, pages, page, pglen);
		if (status < 0) {
			if (status == -ENOSPC)
				status = 0;
+0 −6
Original line number Diff line number Diff line
@@ -487,12 +487,6 @@ nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_se

	entry->d_type = DT_UNKNOWN;

	p = xdr_inline_peek(xdr, 8);
	if (p != NULL)
		entry->eof = !p[0] && p[1];
	else
		entry->eof = 0;

	return p;

out_overflow:
+0 −6
Original line number Diff line number Diff line
@@ -647,12 +647,6 @@ nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_s
			memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
	}

	p = xdr_inline_peek(xdr, 8);
	if (p != NULL)
		entry->eof = !p[0] && p[1];
	else
		entry->eof = 0;

	return p;

out_overflow:
+0 −6
Original line number Diff line number Diff line
@@ -6215,12 +6215,6 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
	if (verify_attr_len(xdr, p, len) < 0)
		goto out_overflow;

	p = xdr_inline_peek(xdr, 8);
	if (p != NULL)
		entry->eof = !p[0] && p[1];
	else
		entry->eof = 0;

	return p;

out_overflow:
+3 −1
Original line number Diff line number Diff line
@@ -201,6 +201,8 @@ struct xdr_stream {

	__be32 *end;		/* end of available buffer space */
	struct kvec *iov;	/* pointer to the current kvec */
	struct kvec scratch;	/* Scratch buffer */
	struct page **page_ptr;	/* pointer to the current page */
};

extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
@@ -208,7 +210,7 @@ extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
		unsigned int base, unsigned int len);
extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
extern __be32 *xdr_inline_peek(struct xdr_stream *xdr, size_t nbytes);
extern void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen);
extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes);
extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);
Loading