|
@@ -51,6 +51,7 @@
|
|
|
#include <linux/sunrpc/bc_xprt.h>
|
|
|
#include <linux/xattr.h>
|
|
|
#include <linux/utsname.h>
|
|
|
+#include <linux/mm.h>
|
|
|
|
|
|
#include "nfs4_fs.h"
|
|
|
#include "delegation.h"
|
|
@@ -3252,6 +3253,35 @@ static void buf_to_pages(const void *buf, size_t buflen,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int buf_to_pages_noslab(const void *buf, size_t buflen,
|
|
|
+ struct page **pages, unsigned int *pgbase)
|
|
|
+{
|
|
|
+ struct page *newpage, **spages;
|
|
|
+ int rc = 0;
|
|
|
+ size_t len;
|
|
|
+ spages = pages;
|
|
|
+
|
|
|
+ do {
|
|
|
+ len = min(PAGE_CACHE_SIZE, buflen);
|
|
|
+ newpage = alloc_page(GFP_KERNEL);
|
|
|
+
|
|
|
+ if (newpage == NULL)
|
|
|
+ goto unwind;
|
|
|
+ memcpy(page_address(newpage), buf, len);
|
|
|
+ buf += len;
|
|
|
+ buflen -= len;
|
|
|
+ *pages++ = newpage;
|
|
|
+ rc++;
|
|
|
+ } while (buflen != 0);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+
|
|
|
+unwind:
|
|
|
+ for(; rc > 0; rc--)
|
|
|
+ __free_page(spages[rc-1]);
|
|
|
+ return -ENOMEM;
|
|
|
+}
|
|
|
+
|
|
|
struct nfs4_cached_acl {
|
|
|
int cached;
|
|
|
size_t len;
|
|
@@ -3420,13 +3450,23 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
|
|
|
.rpc_argp = &arg,
|
|
|
.rpc_resp = &res,
|
|
|
};
|
|
|
- int ret;
|
|
|
+ int ret, i;
|
|
|
|
|
|
if (!nfs4_server_supports_acls(server))
|
|
|
return -EOPNOTSUPP;
|
|
|
+ i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
|
|
|
+ if (i < 0)
|
|
|
+ return i;
|
|
|
nfs_inode_return_delegation(inode);
|
|
|
- buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
|
|
|
ret = nfs4_call_sync(server, &msg, &arg, &res, 1);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Free each page after tx, so the only ref left is
|
|
|
+ * held by the network stack
|
|
|
+ */
|
|
|
+ for (; i > 0; i--)
|
|
|
+ put_page(pages[i-1]);
|
|
|
+
|
|
|
/*
|
|
|
* Acl update can result in inode attribute update.
|
|
|
* so mark the attribute cache invalid.
|