Browse Source

Merge branch 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6

* 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6:
  NFS: Fix panic after nfs_umount()
  nfs: remove extraneous and problematic calls to nfs_clear_request
  nfs: kernel should return EPROTONOSUPPORT when not support NFSv4
  NFS: Fix fcntl F_GETLK not reporting some conflicts
  nfs: Discard ACL cache on mode update
  NFS: Readdir cleanups
  NFS: nfs_readdir_search_for_cookie() don't mark as eof if cookie not found
  NFS: Fix a memory leak in nfs_readdir
  Call the filesystem back whenever a page is removed from the page cache
  NFS: Ensure we use the correct cookie in nfs_readdir_xdr_filler
Linus Torvalds 15 years ago
parent
commit
38971ce2fa

+ 6 - 1
Documentation/filesystems/Locking

@@ -173,12 +173,13 @@ prototypes:
 	sector_t (*bmap)(struct address_space *, sector_t);
 	sector_t (*bmap)(struct address_space *, sector_t);
 	int (*invalidatepage) (struct page *, unsigned long);
 	int (*invalidatepage) (struct page *, unsigned long);
 	int (*releasepage) (struct page *, int);
 	int (*releasepage) (struct page *, int);
+	void (*freepage)(struct page *);
 	int (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
 	int (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
 			loff_t offset, unsigned long nr_segs);
 			loff_t offset, unsigned long nr_segs);
 	int (*launder_page) (struct page *);
 	int (*launder_page) (struct page *);
 
 
 locking rules:
 locking rules:
-	All except set_page_dirty may block
+	All except set_page_dirty and freepage may block
 
 
 			BKL	PageLocked(page)	i_mutex
 			BKL	PageLocked(page)	i_mutex
 writepage:		no	yes, unlocks (see below)
 writepage:		no	yes, unlocks (see below)
@@ -193,6 +194,7 @@ perform_write:		no	n/a			yes
 bmap:			no
 bmap:			no
 invalidatepage:		no	yes
 invalidatepage:		no	yes
 releasepage:		no	yes
 releasepage:		no	yes
+freepage:		no	yes
 direct_IO:		no
 direct_IO:		no
 launder_page:		no	yes
 launder_page:		no	yes
 
 
@@ -288,6 +290,9 @@ buffers from the page in preparation for freeing it.  It returns zero to
 indicate that the buffers are (or may be) freeable.  If ->releasepage is zero,
 indicate that the buffers are (or may be) freeable.  If ->releasepage is zero,
 the kernel assumes that the fs has no private interest in the buffers.
 the kernel assumes that the fs has no private interest in the buffers.
 
 
+	->freepage() is called when the kernel is done dropping the page
+from the page cache.
+
 	->launder_page() may be called prior to releasing a page if
 	->launder_page() may be called prior to releasing a page if
 it is still found to be dirty. It returns zero if the page was successfully
 it is still found to be dirty. It returns zero if the page was successfully
 cleaned, or an error value if not. Note that in order to prevent the page
 cleaned, or an error value if not. Note that in order to prevent the page

+ 7 - 0
Documentation/filesystems/vfs.txt

@@ -534,6 +534,7 @@ struct address_space_operations {
 	sector_t (*bmap)(struct address_space *, sector_t);
 	sector_t (*bmap)(struct address_space *, sector_t);
 	int (*invalidatepage) (struct page *, unsigned long);
 	int (*invalidatepage) (struct page *, unsigned long);
 	int (*releasepage) (struct page *, int);
 	int (*releasepage) (struct page *, int);
+	void (*freepage)(struct page *);
 	ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
 	ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
 			loff_t offset, unsigned long nr_segs);
 			loff_t offset, unsigned long nr_segs);
 	struct page* (*get_xip_page)(struct address_space *, sector_t,
 	struct page* (*get_xip_page)(struct address_space *, sector_t,
@@ -678,6 +679,12 @@ struct address_space_operations {
         need to ensure this.  Possibly it can clear the PageUptodate
         need to ensure this.  Possibly it can clear the PageUptodate
         bit if it cannot free private data yet.
         bit if it cannot free private data yet.
 
 
+  freepage: freepage is called once the page is no longer visible in
+        the page cache in order to allow the cleanup of any private
+	data. Since it may be called by the memory reclaimer, it
+	should not assume that the original address_space mapping still
+	exists, and it should not block.
+
   direct_IO: called by the generic read/write routines to perform
   direct_IO: called by the generic read/write routines to perform
         direct_IO - that is IO requests which bypass the page cache
         direct_IO - that is IO requests which bypass the page cache
         and transfer data directly between the storage and the
         and transfer data directly between the storage and the

+ 35 - 41
fs/nfs/dir.c

@@ -57,7 +57,7 @@ static int nfs_rename(struct inode *, struct dentry *,
 		      struct inode *, struct dentry *);
 		      struct inode *, struct dentry *);
 static int nfs_fsync_dir(struct file *, int);
 static int nfs_fsync_dir(struct file *, int);
 static loff_t nfs_llseek_dir(struct file *, loff_t, int);
 static loff_t nfs_llseek_dir(struct file *, loff_t, int);
-static int nfs_readdir_clear_array(struct page*, gfp_t);
+static void nfs_readdir_clear_array(struct page*);
 
 
 const struct file_operations nfs_dir_operations = {
 const struct file_operations nfs_dir_operations = {
 	.llseek		= nfs_llseek_dir,
 	.llseek		= nfs_llseek_dir,
@@ -83,8 +83,8 @@ const struct inode_operations nfs_dir_inode_operations = {
 	.setattr	= nfs_setattr,
 	.setattr	= nfs_setattr,
 };
 };
 
 
-const struct address_space_operations nfs_dir_addr_space_ops = {
-	.releasepage = nfs_readdir_clear_array,
+const struct address_space_operations nfs_dir_aops = {
+	.freepage = nfs_readdir_clear_array,
 };
 };
 
 
 #ifdef CONFIG_NFS_V3
 #ifdef CONFIG_NFS_V3
@@ -178,6 +178,7 @@ typedef struct {
 	struct page	*page;
 	struct page	*page;
 	unsigned long	page_index;
 	unsigned long	page_index;
 	u64		*dir_cookie;
 	u64		*dir_cookie;
+	u64		last_cookie;
 	loff_t		current_index;
 	loff_t		current_index;
 	decode_dirent_t	decode;
 	decode_dirent_t	decode;
 
 
@@ -213,17 +214,15 @@ void nfs_readdir_release_array(struct page *page)
  * we are freeing strings created by nfs_add_to_readdir_array()
  * we are freeing strings created by nfs_add_to_readdir_array()
  */
  */
 static
 static
-int nfs_readdir_clear_array(struct page *page, gfp_t mask)
+void nfs_readdir_clear_array(struct page *page)
 {
 {
-	struct nfs_cache_array *array = nfs_readdir_get_array(page);
+	struct nfs_cache_array *array;
 	int i;
 	int i;
 
 
-	if (IS_ERR(array))
-		return PTR_ERR(array);
+	array = kmap_atomic(page, KM_USER0);
 	for (i = 0; i < array->size; i++)
 	for (i = 0; i < array->size; i++)
 		kfree(array->array[i].string.name);
 		kfree(array->array[i].string.name);
-	nfs_readdir_release_array(page);
-	return 0;
+	kunmap_atomic(array, KM_USER0);
 }
 }
 
 
 /*
 /*
@@ -272,7 +271,7 @@ int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page)
 		goto out;
 		goto out;
 	array->last_cookie = entry->cookie;
 	array->last_cookie = entry->cookie;
 	array->size++;
 	array->size++;
-	if (entry->eof == 1)
+	if (entry->eof != 0)
 		array->eof_index = array->size;
 		array->eof_index = array->size;
 out:
 out:
 	nfs_readdir_release_array(page);
 	nfs_readdir_release_array(page);
@@ -312,15 +311,14 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des
 	for (i = 0; i < array->size; i++) {
 	for (i = 0; i < array->size; i++) {
 		if (array->array[i].cookie == *desc->dir_cookie) {
 		if (array->array[i].cookie == *desc->dir_cookie) {
 			desc->cache_entry_index = i;
 			desc->cache_entry_index = i;
-			status = 0;
-			goto out;
+			return 0;
 		}
 		}
 	}
 	}
-	if (i == array->eof_index) {
-		desc->eof = 1;
+	if (array->eof_index >= 0) {
 		status = -EBADCOOKIE;
 		status = -EBADCOOKIE;
+		if (*desc->dir_cookie == array->last_cookie)
+			desc->eof = 1;
 	}
 	}
-out:
 	return status;
 	return status;
 }
 }
 
 
@@ -328,10 +326,7 @@ static
 int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc)
 int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc)
 {
 {
 	struct nfs_cache_array *array;
 	struct nfs_cache_array *array;
-	int status = -EBADCOOKIE;
-
-	if (desc->dir_cookie == NULL)
-		goto out;
+	int status;
 
 
 	array = nfs_readdir_get_array(desc->page);
 	array = nfs_readdir_get_array(desc->page);
 	if (IS_ERR(array)) {
 	if (IS_ERR(array)) {
@@ -344,6 +339,10 @@ int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc)
 	else
 	else
 		status = nfs_readdir_search_for_cookie(array, desc);
 		status = nfs_readdir_search_for_cookie(array, desc);
 
 
+	if (status == -EAGAIN) {
+		desc->last_cookie = array->last_cookie;
+		desc->page_index++;
+	}
 	nfs_readdir_release_array(desc->page);
 	nfs_readdir_release_array(desc->page);
 out:
 out:
 	return status;
 	return status;
@@ -490,7 +489,7 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
 
 
 		count++;
 		count++;
 
 
-		if (desc->plus == 1)
+		if (desc->plus != 0)
 			nfs_prime_dcache(desc->file->f_path.dentry, entry);
 			nfs_prime_dcache(desc->file->f_path.dentry, entry);
 
 
 		status = nfs_readdir_add_to_array(entry, page);
 		status = nfs_readdir_add_to_array(entry, page);
@@ -498,7 +497,7 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
 			break;
 			break;
 	} while (!entry->eof);
 	} while (!entry->eof);
 
 
-	if (count == 0 || (status == -EBADCOOKIE && entry->eof == 1)) {
+	if (count == 0 || (status == -EBADCOOKIE && entry->eof != 0)) {
 		array = nfs_readdir_get_array(page);
 		array = nfs_readdir_get_array(page);
 		if (!IS_ERR(array)) {
 		if (!IS_ERR(array)) {
 			array->eof_index = array->size;
 			array->eof_index = array->size;
@@ -563,7 +562,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
 	unsigned int array_size = ARRAY_SIZE(pages);
 	unsigned int array_size = ARRAY_SIZE(pages);
 
 
 	entry.prev_cookie = 0;
 	entry.prev_cookie = 0;
-	entry.cookie = *desc->dir_cookie;
+	entry.cookie = desc->last_cookie;
 	entry.eof = 0;
 	entry.eof = 0;
 	entry.fh = nfs_alloc_fhandle();
 	entry.fh = nfs_alloc_fhandle();
 	entry.fattr = nfs_alloc_fattr();
 	entry.fattr = nfs_alloc_fattr();
@@ -636,6 +635,8 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page)
 static
 static
 void cache_page_release(nfs_readdir_descriptor_t *desc)
 void cache_page_release(nfs_readdir_descriptor_t *desc)
 {
 {
+	if (!desc->page->mapping)
+		nfs_readdir_clear_array(desc->page);
 	page_cache_release(desc->page);
 	page_cache_release(desc->page);
 	desc->page = NULL;
 	desc->page = NULL;
 }
 }
@@ -660,9 +661,8 @@ int find_cache_page(nfs_readdir_descriptor_t *desc)
 		return PTR_ERR(desc->page);
 		return PTR_ERR(desc->page);
 
 
 	res = nfs_readdir_search_array(desc);
 	res = nfs_readdir_search_array(desc);
-	if (res == 0)
-		return 0;
-	cache_page_release(desc);
+	if (res != 0)
+		cache_page_release(desc);
 	return res;
 	return res;
 }
 }
 
 
@@ -672,22 +672,16 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
 {
 {
 	int res;
 	int res;
 
 
-	if (desc->page_index == 0)
+	if (desc->page_index == 0) {
 		desc->current_index = 0;
 		desc->current_index = 0;
-	while (1) {
-		res = find_cache_page(desc);
-		if (res != -EAGAIN)
-			break;
-		desc->page_index++;
+		desc->last_cookie = 0;
 	}
 	}
+	do {
+		res = find_cache_page(desc);
+	} while (res == -EAGAIN);
 	return res;
 	return res;
 }
 }
 
 
-static inline unsigned int dt_type(struct inode *inode)
-{
-	return (inode->i_mode >> 12) & 15;
-}
-
 /*
 /*
  * Once we've found the start of the dirent within a page: fill 'er up...
  * Once we've found the start of the dirent within a page: fill 'er up...
  */
  */
@@ -717,13 +711,12 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
 			break;
 			break;
 		}
 		}
 		file->f_pos++;
 		file->f_pos++;
-		desc->cache_entry_index = i;
 		if (i < (array->size-1))
 		if (i < (array->size-1))
 			*desc->dir_cookie = array->array[i+1].cookie;
 			*desc->dir_cookie = array->array[i+1].cookie;
 		else
 		else
 			*desc->dir_cookie = array->last_cookie;
 			*desc->dir_cookie = array->last_cookie;
 	}
 	}
-	if (i == array->eof_index)
+	if (array->eof_index >= 0)
 		desc->eof = 1;
 		desc->eof = 1;
 
 
 	nfs_readdir_release_array(desc->page);
 	nfs_readdir_release_array(desc->page);
@@ -764,6 +757,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
 	}
 	}
 
 
 	desc->page_index = 0;
 	desc->page_index = 0;
+	desc->last_cookie = *desc->dir_cookie;
 	desc->page = page;
 	desc->page = page;
 
 
 	status = nfs_readdir_xdr_to_array(desc, page, inode);
 	status = nfs_readdir_xdr_to_array(desc, page, inode);
@@ -791,7 +785,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 	struct inode	*inode = dentry->d_inode;
 	struct inode	*inode = dentry->d_inode;
 	nfs_readdir_descriptor_t my_desc,
 	nfs_readdir_descriptor_t my_desc,
 			*desc = &my_desc;
 			*desc = &my_desc;
-	int res = -ENOMEM;
+	int res;
 
 
 	dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n",
 	dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n",
 			dentry->d_parent->d_name.name, dentry->d_name.name,
 			dentry->d_parent->d_name.name, dentry->d_name.name,
@@ -816,7 +810,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 	if (res < 0)
 	if (res < 0)
 		goto out;
 		goto out;
 
 
-	while (desc->eof != 1) {
+	do {
 		res = readdir_search_pagecache(desc);
 		res = readdir_search_pagecache(desc);
 
 
 		if (res == -EBADCOOKIE) {
 		if (res == -EBADCOOKIE) {
@@ -844,7 +838,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 		res = nfs_do_filldir(desc, dirent, filldir);
 		res = nfs_do_filldir(desc, dirent, filldir);
 		if (res < 0)
 		if (res < 0)
 			break;
 			break;
-	}
+	} while (!desc->eof);
 out:
 out:
 	nfs_unblock_sillyrename(dentry);
 	nfs_unblock_sillyrename(dentry);
 	if (res > 0)
 	if (res > 0)

+ 2 - 0
fs/nfs/file.c

@@ -693,6 +693,7 @@ do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
 {
 {
 	struct inode *inode = filp->f_mapping->host;
 	struct inode *inode = filp->f_mapping->host;
 	int status = 0;
 	int status = 0;
+	unsigned int saved_type = fl->fl_type;
 
 
 	/* Try local locking first */
 	/* Try local locking first */
 	posix_test_lock(filp, fl);
 	posix_test_lock(filp, fl);
@@ -700,6 +701,7 @@ do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
 		/* found a conflict */
 		/* found a conflict */
 		goto out;
 		goto out;
 	}
 	}
+	fl->fl_type = saved_type;
 
 
 	if (nfs_have_delegation(inode, FMODE_READ))
 	if (nfs_have_delegation(inode, FMODE_READ))
 		goto out_noconflict;
 		goto out_noconflict;

+ 1 - 0
fs/nfs/inode.c

@@ -289,6 +289,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 		} else if (S_ISDIR(inode->i_mode)) {
 		} else if (S_ISDIR(inode->i_mode)) {
 			inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
 			inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
 			inode->i_fop = &nfs_dir_operations;
 			inode->i_fop = &nfs_dir_operations;
+			inode->i_data.a_ops = &nfs_dir_aops;
 			if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS))
 			if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS))
 				set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
 				set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
 			/* Deal with crossing mountpoints */
 			/* Deal with crossing mountpoints */

+ 2 - 2
fs/nfs/mount_clnt.c

@@ -505,13 +505,13 @@ static struct rpc_procinfo mnt3_procedures[] = {
 
 
 static struct rpc_version mnt_version1 = {
 static struct rpc_version mnt_version1 = {
 	.number		= 1,
 	.number		= 1,
-	.nrprocs	= 2,
+	.nrprocs	= ARRAY_SIZE(mnt_procedures),
 	.procs		= mnt_procedures,
 	.procs		= mnt_procedures,
 };
 };
 
 
 static struct rpc_version mnt_version3 = {
 static struct rpc_version mnt_version3 = {
 	.number		= 3,
 	.number		= 3,
-	.nrprocs	= 2,
+	.nrprocs	= ARRAY_SIZE(mnt3_procedures),
 	.procs		= mnt3_procedures,
 	.procs		= mnt3_procedures,
 };
 };
 
 

+ 9 - 0
fs/nfs/nfs4proc.c

@@ -3361,6 +3361,8 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
 	ret = nfs_revalidate_inode(server, inode);
 	ret = nfs_revalidate_inode(server, inode);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
+	if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
+		nfs_zap_acl_cache(inode);
 	ret = nfs4_read_cached_acl(inode, buf, buflen);
 	ret = nfs4_read_cached_acl(inode, buf, buflen);
 	if (ret != -ENOENT)
 	if (ret != -ENOENT)
 		return ret;
 		return ret;
@@ -3389,6 +3391,13 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
 	nfs_inode_return_delegation(inode);
 	nfs_inode_return_delegation(inode);
 	buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
 	buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
 	ret = nfs4_call_sync(server, &msg, &arg, &res, 1);
 	ret = nfs4_call_sync(server, &msg, &arg, &res, 1);
+	/*
+	 * Acl update can result in inode attribute update.
+	 * so mark the attribute cache invalid.
+	 */
+	spin_lock(&inode->i_lock);
+	NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR;
+	spin_unlock(&inode->i_lock);
 	nfs_access_zap_cache(inode);
 	nfs_access_zap_cache(inode);
 	nfs_zap_acl_cache(inode);
 	nfs_zap_acl_cache(inode);
 	return ret;
 	return ret;

+ 2 - 2
fs/nfs/pagelist.c

@@ -115,7 +115,7 @@ int nfs_set_page_tag_locked(struct nfs_page *req)
 {
 {
 	if (!nfs_lock_request_dontget(req))
 	if (!nfs_lock_request_dontget(req))
 		return 0;
 		return 0;
-	if (req->wb_page != NULL)
+	if (test_bit(PG_MAPPED, &req->wb_flags))
 		radix_tree_tag_set(&NFS_I(req->wb_context->path.dentry->d_inode)->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED);
 		radix_tree_tag_set(&NFS_I(req->wb_context->path.dentry->d_inode)->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED);
 	return 1;
 	return 1;
 }
 }
@@ -125,7 +125,7 @@ int nfs_set_page_tag_locked(struct nfs_page *req)
  */
  */
 void nfs_clear_page_tag_locked(struct nfs_page *req)
 void nfs_clear_page_tag_locked(struct nfs_page *req)
 {
 {
-	if (req->wb_page != NULL) {
+	if (test_bit(PG_MAPPED, &req->wb_flags)) {
 		struct inode *inode = req->wb_context->path.dentry->d_inode;
 		struct inode *inode = req->wb_context->path.dentry->d_inode;
 		struct nfs_inode *nfsi = NFS_I(inode);
 		struct nfs_inode *nfsi = NFS_I(inode);
 
 

+ 0 - 1
fs/nfs/read.c

@@ -152,7 +152,6 @@ static void nfs_readpage_release(struct nfs_page *req)
 			(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
 			(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
 			req->wb_bytes,
 			req->wb_bytes,
 			(long long)req_offset(req));
 			(long long)req_offset(req));
-	nfs_clear_request(req);
 	nfs_release_request(req);
 	nfs_release_request(req);
 }
 }
 
 

+ 0 - 4
fs/nfs/super.c

@@ -1069,12 +1069,10 @@ static int nfs_parse_mount_options(char *raw,
 			mnt->flags |= NFS_MOUNT_VER3;
 			mnt->flags |= NFS_MOUNT_VER3;
 			mnt->version = 3;
 			mnt->version = 3;
 			break;
 			break;
-#ifdef CONFIG_NFS_V4
 		case Opt_v4:
 		case Opt_v4:
 			mnt->flags &= ~NFS_MOUNT_VER3;
 			mnt->flags &= ~NFS_MOUNT_VER3;
 			mnt->version = 4;
 			mnt->version = 4;
 			break;
 			break;
-#endif
 		case Opt_udp:
 		case Opt_udp:
 			mnt->flags &= ~NFS_MOUNT_TCP;
 			mnt->flags &= ~NFS_MOUNT_TCP;
 			mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
 			mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
@@ -1286,12 +1284,10 @@ static int nfs_parse_mount_options(char *raw,
 				mnt->flags |= NFS_MOUNT_VER3;
 				mnt->flags |= NFS_MOUNT_VER3;
 				mnt->version = 3;
 				mnt->version = 3;
 				break;
 				break;
-#ifdef CONFIG_NFS_V4
 			case NFS4_VERSION:
 			case NFS4_VERSION:
 				mnt->flags &= ~NFS_MOUNT_VER3;
 				mnt->flags &= ~NFS_MOUNT_VER3;
 				mnt->version = 4;
 				mnt->version = 4;
 				break;
 				break;
-#endif
 			default:
 			default:
 				goto out_invalid_value;
 				goto out_invalid_value;
 			}
 			}

+ 2 - 1
fs/nfs/write.c

@@ -390,6 +390,7 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
 		if (nfs_have_delegation(inode, FMODE_WRITE))
 		if (nfs_have_delegation(inode, FMODE_WRITE))
 			nfsi->change_attr++;
 			nfsi->change_attr++;
 	}
 	}
+	set_bit(PG_MAPPED, &req->wb_flags);
 	SetPagePrivate(req->wb_page);
 	SetPagePrivate(req->wb_page);
 	set_page_private(req->wb_page, (unsigned long)req);
 	set_page_private(req->wb_page, (unsigned long)req);
 	nfsi->npages++;
 	nfsi->npages++;
@@ -415,6 +416,7 @@ static void nfs_inode_remove_request(struct nfs_page *req)
 	spin_lock(&inode->i_lock);
 	spin_lock(&inode->i_lock);
 	set_page_private(req->wb_page, 0);
 	set_page_private(req->wb_page, 0);
 	ClearPagePrivate(req->wb_page);
 	ClearPagePrivate(req->wb_page);
+	clear_bit(PG_MAPPED, &req->wb_flags);
 	radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
 	radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
 	nfsi->npages--;
 	nfsi->npages--;
 	if (!nfsi->npages) {
 	if (!nfsi->npages) {
@@ -422,7 +424,6 @@ static void nfs_inode_remove_request(struct nfs_page *req)
 		iput(inode);
 		iput(inode);
 	} else
 	} else
 		spin_unlock(&inode->i_lock);
 		spin_unlock(&inode->i_lock);
-	nfs_clear_request(req);
 	nfs_release_request(req);
 	nfs_release_request(req);
 }
 }
 
 

+ 1 - 0
include/linux/fs.h

@@ -602,6 +602,7 @@ struct address_space_operations {
 	sector_t (*bmap)(struct address_space *, sector_t);
 	sector_t (*bmap)(struct address_space *, sector_t);
 	void (*invalidatepage) (struct page *, unsigned long);
 	void (*invalidatepage) (struct page *, unsigned long);
 	int (*releasepage) (struct page *, gfp_t);
 	int (*releasepage) (struct page *, gfp_t);
+	void (*freepage)(struct page *);
 	ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
 	ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
 			loff_t offset, unsigned long nr_segs);
 			loff_t offset, unsigned long nr_segs);
 	int (*get_xip_mem)(struct address_space *, pgoff_t, int,
 	int (*get_xip_mem)(struct address_space *, pgoff_t, int,

+ 1 - 0
include/linux/nfs_fs.h

@@ -401,6 +401,7 @@ extern const struct inode_operations nfs3_file_inode_operations;
 #endif /* CONFIG_NFS_V3 */
 #endif /* CONFIG_NFS_V3 */
 extern const struct file_operations nfs_file_operations;
 extern const struct file_operations nfs_file_operations;
 extern const struct address_space_operations nfs_file_aops;
 extern const struct address_space_operations nfs_file_aops;
+extern const struct address_space_operations nfs_dir_aops;
 
 
 static inline struct nfs_open_context *nfs_file_open_context(struct file *filp)
 static inline struct nfs_open_context *nfs_file_open_context(struct file *filp)
 {
 {

+ 1 - 0
include/linux/nfs_page.h

@@ -29,6 +29,7 @@
  */
  */
 enum {
 enum {
 	PG_BUSY = 0,
 	PG_BUSY = 0,
+	PG_MAPPED,
 	PG_CLEAN,
 	PG_CLEAN,
 	PG_NEED_COMMIT,
 	PG_NEED_COMMIT,
 	PG_NEED_RESCHED,
 	PG_NEED_RESCHED,

+ 5 - 0
mm/filemap.c

@@ -143,13 +143,18 @@ void __remove_from_page_cache(struct page *page)
 void remove_from_page_cache(struct page *page)
 void remove_from_page_cache(struct page *page)
 {
 {
 	struct address_space *mapping = page->mapping;
 	struct address_space *mapping = page->mapping;
+	void (*freepage)(struct page *);
 
 
 	BUG_ON(!PageLocked(page));
 	BUG_ON(!PageLocked(page));
 
 
+	freepage = mapping->a_ops->freepage;
 	spin_lock_irq(&mapping->tree_lock);
 	spin_lock_irq(&mapping->tree_lock);
 	__remove_from_page_cache(page);
 	__remove_from_page_cache(page);
 	spin_unlock_irq(&mapping->tree_lock);
 	spin_unlock_irq(&mapping->tree_lock);
 	mem_cgroup_uncharge_cache_page(page);
 	mem_cgroup_uncharge_cache_page(page);
+
+	if (freepage)
+		freepage(page);
 }
 }
 EXPORT_SYMBOL(remove_from_page_cache);
 EXPORT_SYMBOL(remove_from_page_cache);
 
 

+ 4 - 0
mm/truncate.c

@@ -390,6 +390,10 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page)
 	__remove_from_page_cache(page);
 	__remove_from_page_cache(page);
 	spin_unlock_irq(&mapping->tree_lock);
 	spin_unlock_irq(&mapping->tree_lock);
 	mem_cgroup_uncharge_cache_page(page);
 	mem_cgroup_uncharge_cache_page(page);
+
+	if (mapping->a_ops->freepage)
+		mapping->a_ops->freepage(page);
+
 	page_cache_release(page);	/* pagecache ref */
 	page_cache_release(page);	/* pagecache ref */
 	return 1;
 	return 1;
 failed:
 failed:

+ 7 - 0
mm/vmscan.c

@@ -494,9 +494,16 @@ static int __remove_mapping(struct address_space *mapping, struct page *page)
 		spin_unlock_irq(&mapping->tree_lock);
 		spin_unlock_irq(&mapping->tree_lock);
 		swapcache_free(swap, page);
 		swapcache_free(swap, page);
 	} else {
 	} else {
+		void (*freepage)(struct page *);
+
+		freepage = mapping->a_ops->freepage;
+
 		__remove_from_page_cache(page);
 		__remove_from_page_cache(page);
 		spin_unlock_irq(&mapping->tree_lock);
 		spin_unlock_irq(&mapping->tree_lock);
 		mem_cgroup_uncharge_cache_page(page);
 		mem_cgroup_uncharge_cache_page(page);
+
+		if (freepage != NULL)
+			freepage(page);
 	}
 	}
 
 
 	return 1;
 	return 1;