Commit 90d51d56 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-3.16-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client fixes from Trond Myklebust:
 "Apologies for the relative lateness of this pull request, however the
  commits fix some issues with the NFS read/write code updates in
  3.16-rc1 that can cause serious Oopsing when using small r/wsize.  The
  delay was mainly due to extra testing to make sure that the fixes
  behave correctly.

  Highlights include;
   - Stable fix for an NFSv3 posix ACL regression
   - Multiple fixes for regressions to the NFS generic read/write code:
     - Fix page splitting bugs that come into play when a small
       rsize/wsize read/write needs to be sent again (due to error
       conditions or page redirty)
     - Fix nfs_wb_page_cancel, which is called by the "invalidatepage"
       method
   - Fix 2 compile warnings about unused variables
   - Fix a performance issue affecting unstable writes"

* tag 'nfs-for-3.16-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS: Don't reset pg_moreio in __nfs_pageio_add_request
  NFS: Remove 2 unused variables
  nfs: handle multiple reqs in nfs_wb_page_cancel
  nfs: handle multiple reqs in nfs_page_async_flush
  nfs: change find_request to find_head_request
  nfs: nfs_page should take a ref on the head req
  nfs: mark nfs_page reqs with flag for extra ref
  nfs: only show Posix ACLs in listxattr if actually present
parents d0571909 f563b89b
...@@ -756,7 +756,6 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) ...@@ -756,7 +756,6 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
spin_unlock(&dreq->lock); spin_unlock(&dreq->lock);
while (!list_empty(&hdr->pages)) { while (!list_empty(&hdr->pages)) {
bool do_destroy = true;
req = nfs_list_entry(hdr->pages.next); req = nfs_list_entry(hdr->pages.next);
nfs_list_remove_request(req); nfs_list_remove_request(req);
...@@ -765,7 +764,6 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) ...@@ -765,7 +764,6 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
case NFS_IOHDR_NEED_COMMIT: case NFS_IOHDR_NEED_COMMIT:
kref_get(&req->wb_kref); kref_get(&req->wb_kref);
nfs_mark_request_commit(req, hdr->lseg, &cinfo); nfs_mark_request_commit(req, hdr->lseg, &cinfo);
do_destroy = false;
} }
nfs_unlock_and_release_request(req); nfs_unlock_and_release_request(req);
} }
......
...@@ -244,6 +244,7 @@ void nfs_pgio_data_release(struct nfs_pgio_data *); ...@@ -244,6 +244,7 @@ void nfs_pgio_data_release(struct nfs_pgio_data *);
int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *); int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *);
int nfs_initiate_pgio(struct rpc_clnt *, struct nfs_pgio_data *, int nfs_initiate_pgio(struct rpc_clnt *, struct nfs_pgio_data *,
const struct rpc_call_ops *, int, int); const struct rpc_call_ops *, int, int);
void nfs_free_request(struct nfs_page *req);
static inline void nfs_iocounter_init(struct nfs_io_counter *c) static inline void nfs_iocounter_init(struct nfs_io_counter *c)
{ {
......
...@@ -247,3 +247,46 @@ const struct xattr_handler *nfs3_xattr_handlers[] = { ...@@ -247,3 +247,46 @@ const struct xattr_handler *nfs3_xattr_handlers[] = {
&posix_acl_default_xattr_handler, &posix_acl_default_xattr_handler,
NULL, NULL,
}; };
static int
nfs3_list_one_acl(struct inode *inode, int type, const char *name, void *data,
size_t size, ssize_t *result)
{
struct posix_acl *acl;
char *p = data + *result;
acl = get_acl(inode, type);
if (!acl)
return 0;
posix_acl_release(acl);
*result += strlen(name);
*result += 1;
if (!size)
return 0;
if (*result > size)
return -ERANGE;
strcpy(p, name);
return 0;
}
ssize_t
nfs3_listxattr(struct dentry *dentry, char *data, size_t size)
{
struct inode *inode = dentry->d_inode;
ssize_t result = 0;
int error;
error = nfs3_list_one_acl(inode, ACL_TYPE_ACCESS,
POSIX_ACL_XATTR_ACCESS, data, size, &result);
if (error)
return error;
error = nfs3_list_one_acl(inode, ACL_TYPE_DEFAULT,
POSIX_ACL_XATTR_DEFAULT, data, size, &result);
if (error)
return error;
return result;
}
...@@ -885,7 +885,7 @@ static const struct inode_operations nfs3_dir_inode_operations = { ...@@ -885,7 +885,7 @@ static const struct inode_operations nfs3_dir_inode_operations = {
.getattr = nfs_getattr, .getattr = nfs_getattr,
.setattr = nfs_setattr, .setattr = nfs_setattr,
#ifdef CONFIG_NFS_V3_ACL #ifdef CONFIG_NFS_V3_ACL
.listxattr = generic_listxattr, .listxattr = nfs3_listxattr,
.getxattr = generic_getxattr, .getxattr = generic_getxattr,
.setxattr = generic_setxattr, .setxattr = generic_setxattr,
.removexattr = generic_removexattr, .removexattr = generic_removexattr,
...@@ -899,7 +899,7 @@ static const struct inode_operations nfs3_file_inode_operations = { ...@@ -899,7 +899,7 @@ static const struct inode_operations nfs3_file_inode_operations = {
.getattr = nfs_getattr, .getattr = nfs_getattr,
.setattr = nfs_setattr, .setattr = nfs_setattr,
#ifdef CONFIG_NFS_V3_ACL #ifdef CONFIG_NFS_V3_ACL
.listxattr = generic_listxattr, .listxattr = nfs3_listxattr,
.getxattr = generic_getxattr, .getxattr = generic_getxattr,
.setxattr = generic_setxattr, .setxattr = generic_setxattr,
.removexattr = generic_removexattr, .removexattr = generic_removexattr,
......
...@@ -29,8 +29,6 @@ ...@@ -29,8 +29,6 @@
static struct kmem_cache *nfs_page_cachep; static struct kmem_cache *nfs_page_cachep;
static const struct rpc_call_ops nfs_pgio_common_ops; static const struct rpc_call_ops nfs_pgio_common_ops;
static void nfs_free_request(struct nfs_page *);
static bool nfs_pgarray_set(struct nfs_page_array *p, unsigned int pagecount) static bool nfs_pgarray_set(struct nfs_page_array *p, unsigned int pagecount)
{ {
p->npages = pagecount; p->npages = pagecount;
...@@ -239,20 +237,28 @@ nfs_page_group_init(struct nfs_page *req, struct nfs_page *prev) ...@@ -239,20 +237,28 @@ nfs_page_group_init(struct nfs_page *req, struct nfs_page *prev)
WARN_ON_ONCE(prev == req); WARN_ON_ONCE(prev == req);
if (!prev) { if (!prev) {
/* a head request */
req->wb_head = req; req->wb_head = req;
req->wb_this_page = req; req->wb_this_page = req;
} else { } else {
/* a subrequest */
WARN_ON_ONCE(prev->wb_this_page != prev->wb_head); WARN_ON_ONCE(prev->wb_this_page != prev->wb_head);
WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &prev->wb_head->wb_flags)); WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &prev->wb_head->wb_flags));
req->wb_head = prev->wb_head; req->wb_head = prev->wb_head;
req->wb_this_page = prev->wb_this_page; req->wb_this_page = prev->wb_this_page;
prev->wb_this_page = req; prev->wb_this_page = req;
/* All subrequests take a ref on the head request until
* nfs_page_group_destroy is called */
kref_get(&req->wb_head->wb_kref);
/* grab extra ref if head request has extra ref from /* grab extra ref if head request has extra ref from
* the write/commit path to handle handoff between write * the write/commit path to handle handoff between write
* and commit lists */ * and commit lists */
if (test_bit(PG_INODE_REF, &prev->wb_head->wb_flags)) if (test_bit(PG_INODE_REF, &prev->wb_head->wb_flags)) {
set_bit(PG_INODE_REF, &req->wb_flags);
kref_get(&req->wb_kref); kref_get(&req->wb_kref);
}
} }
} }
...@@ -269,6 +275,10 @@ nfs_page_group_destroy(struct kref *kref) ...@@ -269,6 +275,10 @@ nfs_page_group_destroy(struct kref *kref)
struct nfs_page *req = container_of(kref, struct nfs_page, wb_kref); struct nfs_page *req = container_of(kref, struct nfs_page, wb_kref);
struct nfs_page *tmp, *next; struct nfs_page *tmp, *next;
/* subrequests must release the ref on the head request */
if (req->wb_head != req)
nfs_release_request(req->wb_head);
if (!nfs_page_group_sync_on_bit(req, PG_TEARDOWN)) if (!nfs_page_group_sync_on_bit(req, PG_TEARDOWN))
return; return;
...@@ -394,7 +404,7 @@ static void nfs_clear_request(struct nfs_page *req) ...@@ -394,7 +404,7 @@ static void nfs_clear_request(struct nfs_page *req)
* *
* Note: Should never be called with the spinlock held! * Note: Should never be called with the spinlock held!
*/ */
static void nfs_free_request(struct nfs_page *req) void nfs_free_request(struct nfs_page *req)
{ {
WARN_ON_ONCE(req->wb_this_page != req); WARN_ON_ONCE(req->wb_this_page != req);
...@@ -925,7 +935,6 @@ static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, ...@@ -925,7 +935,6 @@ static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
nfs_pageio_doio(desc); nfs_pageio_doio(desc);
if (desc->pg_error < 0) if (desc->pg_error < 0)
return 0; return 0;
desc->pg_moreio = 0;
if (desc->pg_recoalesce) if (desc->pg_recoalesce)
return 0; return 0;
/* retry add_request for this subreq */ /* retry add_request for this subreq */
...@@ -972,6 +981,7 @@ static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc) ...@@ -972,6 +981,7 @@ static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc)
desc->pg_count = 0; desc->pg_count = 0;
desc->pg_base = 0; desc->pg_base = 0;
desc->pg_recoalesce = 0; desc->pg_recoalesce = 0;
desc->pg_moreio = 0;
while (!list_empty(&head)) { while (!list_empty(&head)) {
struct nfs_page *req; struct nfs_page *req;
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment