diff options
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r-- | fs/nfs/nfs4proc.c | 127 |
1 files changed, 87 insertions, 40 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a99a8d948721..1e50326d00dd 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -3215,11 +3215,11 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | |||
3215 | dentry->d_parent->d_name.name, | 3215 | dentry->d_parent->d_name.name, |
3216 | dentry->d_name.name, | 3216 | dentry->d_name.name, |
3217 | (unsigned long long)cookie); | 3217 | (unsigned long long)cookie); |
3218 | nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args); | 3218 | nfs4_setup_readdir(cookie, NFS_I(dir)->cookieverf, dentry, &args); |
3219 | res.pgbase = args.pgbase; | 3219 | res.pgbase = args.pgbase; |
3220 | status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0); | 3220 | status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0); |
3221 | if (status >= 0) { | 3221 | if (status >= 0) { |
3222 | memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE); | 3222 | memcpy(NFS_I(dir)->cookieverf, res.verifier.data, NFS4_VERIFIER_SIZE); |
3223 | status += args.pgbase; | 3223 | status += args.pgbase; |
3224 | } | 3224 | } |
3225 | 3225 | ||
@@ -3653,11 +3653,11 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server) | |||
3653 | && (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL); | 3653 | && (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL); |
3654 | } | 3654 | } |
3655 | 3655 | ||
3656 | /* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_CACHE_SIZE, and that | 3656 | /* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that |
3657 | * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_CACHE_SIZE) bytes on | 3657 | * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_SIZE) bytes on |
3658 | * the stack. | 3658 | * the stack. |
3659 | */ | 3659 | */ |
3660 | #define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT) | 3660 | #define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE) |
3661 | 3661 | ||
3662 | static int buf_to_pages_noslab(const void *buf, size_t buflen, | 3662 | static int buf_to_pages_noslab(const void *buf, size_t buflen, |
3663 | struct page **pages, unsigned int *pgbase) | 3663 | struct page **pages, unsigned int *pgbase) |
@@ -3668,7 +3668,7 @@ static int buf_to_pages_noslab(const void *buf, size_t buflen, | |||
3668 | spages = pages; | 3668 | spages = pages; |
3669 | 3669 | ||
3670 | do { | 3670 | do { |
3671 | len = min_t(size_t, PAGE_CACHE_SIZE, buflen); | 3671 | len = min_t(size_t, PAGE_SIZE, buflen); |
3672 | newpage = alloc_page(GFP_KERNEL); | 3672 | newpage = alloc_page(GFP_KERNEL); |
3673 | 3673 | ||
3674 | if (newpage == NULL) | 3674 | if (newpage == NULL) |
@@ -3737,9 +3737,10 @@ out: | |||
3737 | static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len) | 3737 | static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len) |
3738 | { | 3738 | { |
3739 | struct nfs4_cached_acl *acl; | 3739 | struct nfs4_cached_acl *acl; |
3740 | size_t buflen = sizeof(*acl) + acl_len; | ||
3740 | 3741 | ||
3741 | if (pages && acl_len <= PAGE_SIZE) { | 3742 | if (buflen <= PAGE_SIZE) { |
3742 | acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL); | 3743 | acl = kmalloc(buflen, GFP_KERNEL); |
3743 | if (acl == NULL) | 3744 | if (acl == NULL) |
3744 | goto out; | 3745 | goto out; |
3745 | acl->cached = 1; | 3746 | acl->cached = 1; |
@@ -3781,17 +3782,15 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu | |||
3781 | .rpc_argp = &args, | 3782 | .rpc_argp = &args, |
3782 | .rpc_resp = &res, | 3783 | .rpc_resp = &res, |
3783 | }; | 3784 | }; |
3784 | int ret = -ENOMEM, npages, i; | 3785 | unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE); |
3785 | size_t acl_len = 0; | 3786 | int ret = -ENOMEM, i; |
3786 | 3787 | ||
3787 | npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
3788 | /* As long as we're doing a round trip to the server anyway, | 3788 | /* As long as we're doing a round trip to the server anyway, |
3789 | * let's be prepared for a page of acl data. */ | 3789 | * let's be prepared for a page of acl data. */ |
3790 | if (npages == 0) | 3790 | if (npages == 0) |
3791 | npages = 1; | 3791 | npages = 1; |
3792 | 3792 | if (npages > ARRAY_SIZE(pages)) | |
3793 | /* Add an extra page to handle the bitmap returned */ | 3793 | return -ERANGE; |
3794 | npages++; | ||
3795 | 3794 | ||
3796 | for (i = 0; i < npages; i++) { | 3795 | for (i = 0; i < npages; i++) { |
3797 | pages[i] = alloc_page(GFP_KERNEL); | 3796 | pages[i] = alloc_page(GFP_KERNEL); |
@@ -3807,11 +3806,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu | |||
3807 | args.acl_len = npages * PAGE_SIZE; | 3806 | args.acl_len = npages * PAGE_SIZE; |
3808 | args.acl_pgbase = 0; | 3807 | args.acl_pgbase = 0; |
3809 | 3808 | ||
3810 | /* Let decode_getfacl know not to fail if the ACL data is larger than | ||
3811 | * the page we send as a guess */ | ||
3812 | if (buf == NULL) | ||
3813 | res.acl_flags |= NFS4_ACL_LEN_REQUEST; | ||
3814 | |||
3815 | dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n", | 3809 | dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n", |
3816 | __func__, buf, buflen, npages, args.acl_len); | 3810 | __func__, buf, buflen, npages, args.acl_len); |
3817 | ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), | 3811 | ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), |
@@ -3819,20 +3813,19 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu | |||
3819 | if (ret) | 3813 | if (ret) |
3820 | goto out_free; | 3814 | goto out_free; |
3821 | 3815 | ||
3822 | acl_len = res.acl_len - res.acl_data_offset; | 3816 | /* Handle the case where the passed-in buffer is too short */ |
3823 | if (acl_len > args.acl_len) | 3817 | if (res.acl_flags & NFS4_ACL_TRUNC) { |
3824 | nfs4_write_cached_acl(inode, NULL, 0, acl_len); | 3818 | /* Did the user only issue a request for the acl length? */ |
3825 | else | 3819 | if (buf == NULL) |
3826 | nfs4_write_cached_acl(inode, pages, res.acl_data_offset, | 3820 | goto out_ok; |
3827 | acl_len); | ||
3828 | if (buf) { | ||
3829 | ret = -ERANGE; | 3821 | ret = -ERANGE; |
3830 | if (acl_len > buflen) | 3822 | goto out_free; |
3831 | goto out_free; | ||
3832 | _copy_from_pages(buf, pages, res.acl_data_offset, | ||
3833 | acl_len); | ||
3834 | } | 3823 | } |
3835 | ret = acl_len; | 3824 | nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len); |
3825 | if (buf) | ||
3826 | _copy_from_pages(buf, pages, res.acl_data_offset, res.acl_len); | ||
3827 | out_ok: | ||
3828 | ret = res.acl_len; | ||
3836 | out_free: | 3829 | out_free: |
3837 | for (i = 0; i < npages; i++) | 3830 | for (i = 0; i < npages; i++) |
3838 | if (pages[i]) | 3831 | if (pages[i]) |
@@ -3890,10 +3883,13 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl | |||
3890 | .rpc_argp = &arg, | 3883 | .rpc_argp = &arg, |
3891 | .rpc_resp = &res, | 3884 | .rpc_resp = &res, |
3892 | }; | 3885 | }; |
3886 | unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE); | ||
3893 | int ret, i; | 3887 | int ret, i; |
3894 | 3888 | ||
3895 | if (!nfs4_server_supports_acls(server)) | 3889 | if (!nfs4_server_supports_acls(server)) |
3896 | return -EOPNOTSUPP; | 3890 | return -EOPNOTSUPP; |
3891 | if (npages > ARRAY_SIZE(pages)) | ||
3892 | return -ERANGE; | ||
3897 | i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase); | 3893 | i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase); |
3898 | if (i < 0) | 3894 | if (i < 0) |
3899 | return i; | 3895 | return i; |
@@ -6223,11 +6219,58 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata) | |||
6223 | dprintk("<-- %s\n", __func__); | 6219 | dprintk("<-- %s\n", __func__); |
6224 | } | 6220 | } |
6225 | 6221 | ||
6222 | static size_t max_response_pages(struct nfs_server *server) | ||
6223 | { | ||
6224 | u32 max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; | ||
6225 | return nfs_page_array_len(0, max_resp_sz); | ||
6226 | } | ||
6227 | |||
6228 | static void nfs4_free_pages(struct page **pages, size_t size) | ||
6229 | { | ||
6230 | int i; | ||
6231 | |||
6232 | if (!pages) | ||
6233 | return; | ||
6234 | |||
6235 | for (i = 0; i < size; i++) { | ||
6236 | if (!pages[i]) | ||
6237 | break; | ||
6238 | __free_page(pages[i]); | ||
6239 | } | ||
6240 | kfree(pages); | ||
6241 | } | ||
6242 | |||
6243 | static struct page **nfs4_alloc_pages(size_t size, gfp_t gfp_flags) | ||
6244 | { | ||
6245 | struct page **pages; | ||
6246 | int i; | ||
6247 | |||
6248 | pages = kcalloc(size, sizeof(struct page *), gfp_flags); | ||
6249 | if (!pages) { | ||
6250 | dprintk("%s: can't alloc array of %zu pages\n", __func__, size); | ||
6251 | return NULL; | ||
6252 | } | ||
6253 | |||
6254 | for (i = 0; i < size; i++) { | ||
6255 | pages[i] = alloc_page(gfp_flags); | ||
6256 | if (!pages[i]) { | ||
6257 | dprintk("%s: failed to allocate page\n", __func__); | ||
6258 | nfs4_free_pages(pages, size); | ||
6259 | return NULL; | ||
6260 | } | ||
6261 | } | ||
6262 | |||
6263 | return pages; | ||
6264 | } | ||
6265 | |||
6226 | static void nfs4_layoutget_release(void *calldata) | 6266 | static void nfs4_layoutget_release(void *calldata) |
6227 | { | 6267 | { |
6228 | struct nfs4_layoutget *lgp = calldata; | 6268 | struct nfs4_layoutget *lgp = calldata; |
6269 | struct nfs_server *server = NFS_SERVER(lgp->args.inode); | ||
6270 | size_t max_pages = max_response_pages(server); | ||
6229 | 6271 | ||
6230 | dprintk("--> %s\n", __func__); | 6272 | dprintk("--> %s\n", __func__); |
6273 | nfs4_free_pages(lgp->args.layout.pages, max_pages); | ||
6231 | put_nfs_open_context(lgp->args.ctx); | 6274 | put_nfs_open_context(lgp->args.ctx); |
6232 | kfree(calldata); | 6275 | kfree(calldata); |
6233 | dprintk("<-- %s\n", __func__); | 6276 | dprintk("<-- %s\n", __func__); |
@@ -6239,9 +6282,10 @@ static const struct rpc_call_ops nfs4_layoutget_call_ops = { | |||
6239 | .rpc_release = nfs4_layoutget_release, | 6282 | .rpc_release = nfs4_layoutget_release, |
6240 | }; | 6283 | }; |
6241 | 6284 | ||
6242 | int nfs4_proc_layoutget(struct nfs4_layoutget *lgp) | 6285 | void nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags) |
6243 | { | 6286 | { |
6244 | struct nfs_server *server = NFS_SERVER(lgp->args.inode); | 6287 | struct nfs_server *server = NFS_SERVER(lgp->args.inode); |
6288 | size_t max_pages = max_response_pages(server); | ||
6245 | struct rpc_task *task; | 6289 | struct rpc_task *task; |
6246 | struct rpc_message msg = { | 6290 | struct rpc_message msg = { |
6247 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET], | 6291 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET], |
@@ -6259,12 +6303,19 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp) | |||
6259 | 6303 | ||
6260 | dprintk("--> %s\n", __func__); | 6304 | dprintk("--> %s\n", __func__); |
6261 | 6305 | ||
6306 | lgp->args.layout.pages = nfs4_alloc_pages(max_pages, gfp_flags); | ||
6307 | if (!lgp->args.layout.pages) { | ||
6308 | nfs4_layoutget_release(lgp); | ||
6309 | return; | ||
6310 | } | ||
6311 | lgp->args.layout.pglen = max_pages * PAGE_SIZE; | ||
6312 | |||
6262 | lgp->res.layoutp = &lgp->args.layout; | 6313 | lgp->res.layoutp = &lgp->args.layout; |
6263 | lgp->res.seq_res.sr_slot = NULL; | 6314 | lgp->res.seq_res.sr_slot = NULL; |
6264 | nfs41_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0); | 6315 | nfs41_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0); |
6265 | task = rpc_run_task(&task_setup_data); | 6316 | task = rpc_run_task(&task_setup_data); |
6266 | if (IS_ERR(task)) | 6317 | if (IS_ERR(task)) |
6267 | return PTR_ERR(task); | 6318 | return; |
6268 | status = nfs4_wait_for_completion_rpc_task(task); | 6319 | status = nfs4_wait_for_completion_rpc_task(task); |
6269 | if (status == 0) | 6320 | if (status == 0) |
6270 | status = task->tk_status; | 6321 | status = task->tk_status; |
@@ -6272,7 +6323,7 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp) | |||
6272 | status = pnfs_layout_process(lgp); | 6323 | status = pnfs_layout_process(lgp); |
6273 | rpc_put_task(task); | 6324 | rpc_put_task(task); |
6274 | dprintk("<-- %s status=%d\n", __func__, status); | 6325 | dprintk("<-- %s status=%d\n", __func__, status); |
6275 | return status; | 6326 | return; |
6276 | } | 6327 | } |
6277 | 6328 | ||
6278 | static void | 6329 | static void |
@@ -6304,12 +6355,8 @@ static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata) | |||
6304 | return; | 6355 | return; |
6305 | } | 6356 | } |
6306 | spin_lock(&lo->plh_inode->i_lock); | 6357 | spin_lock(&lo->plh_inode->i_lock); |
6307 | if (task->tk_status == 0) { | 6358 | if (task->tk_status == 0 && lrp->res.lrs_present) |
6308 | if (lrp->res.lrs_present) { | 6359 | pnfs_set_layout_stateid(lo, &lrp->res.stateid, true); |
6309 | pnfs_set_layout_stateid(lo, &lrp->res.stateid, true); | ||
6310 | } else | ||
6311 | BUG_ON(!list_empty(&lo->plh_segs)); | ||
6312 | } | ||
6313 | lo->plh_block_lgets--; | 6360 | lo->plh_block_lgets--; |
6314 | spin_unlock(&lo->plh_inode->i_lock); | 6361 | spin_unlock(&lo->plh_inode->i_lock); |
6315 | dprintk("<-- %s\n", __func__); | 6362 | dprintk("<-- %s\n", __func__); |