diff options
Diffstat (limited to 'fs/nfs/nfs4proc.c')
| -rw-r--r-- | fs/nfs/nfs4proc.c | 218 |
1 files changed, 184 insertions, 34 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e87fe612ca18..32c8758c99fd 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
| @@ -55,6 +55,7 @@ | |||
| 55 | #include "internal.h" | 55 | #include "internal.h" |
| 56 | #include "iostat.h" | 56 | #include "iostat.h" |
| 57 | #include "callback.h" | 57 | #include "callback.h" |
| 58 | #include "pnfs.h" | ||
| 58 | 59 | ||
| 59 | #define NFSDBG_FACILITY NFSDBG_PROC | 60 | #define NFSDBG_FACILITY NFSDBG_PROC |
| 60 | 61 | ||
| @@ -130,6 +131,7 @@ const u32 nfs4_fsinfo_bitmap[2] = { FATTR4_WORD0_MAXFILESIZE | |||
| 130 | | FATTR4_WORD0_MAXWRITE | 131 | | FATTR4_WORD0_MAXWRITE |
| 131 | | FATTR4_WORD0_LEASE_TIME, | 132 | | FATTR4_WORD0_LEASE_TIME, |
| 132 | FATTR4_WORD1_TIME_DELTA | 133 | FATTR4_WORD1_TIME_DELTA |
| 134 | | FATTR4_WORD1_FS_LAYOUT_TYPES | ||
| 133 | }; | 135 | }; |
| 134 | 136 | ||
| 135 | const u32 nfs4_fs_locations_bitmap[2] = { | 137 | const u32 nfs4_fs_locations_bitmap[2] = { |
| @@ -4840,49 +4842,56 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args) | |||
| 4840 | args->bc_attrs.max_reqs); | 4842 | args->bc_attrs.max_reqs); |
| 4841 | } | 4843 | } |
| 4842 | 4844 | ||
| 4843 | static int _verify_channel_attr(char *chan, char *attr_name, u32 sent, u32 rcvd) | 4845 | static int nfs4_verify_fore_channel_attrs(struct nfs41_create_session_args *args, struct nfs4_session *session) |
| 4844 | { | 4846 | { |
| 4845 | if (rcvd <= sent) | 4847 | struct nfs4_channel_attrs *sent = &args->fc_attrs; |
| 4846 | return 0; | 4848 | struct nfs4_channel_attrs *rcvd = &session->fc_attrs; |
| 4847 | printk(KERN_WARNING "%s: Session INVALID: %s channel %s increased. " | 4849 | |
| 4848 | "sent=%u rcvd=%u\n", __func__, chan, attr_name, sent, rcvd); | 4850 | if (rcvd->headerpadsz > sent->headerpadsz) |
| 4849 | return -EINVAL; | 4851 | return -EINVAL; |
| 4852 | if (rcvd->max_resp_sz > sent->max_resp_sz) | ||
| 4853 | return -EINVAL; | ||
| 4854 | /* | ||
| 4855 | * Our requested max_ops is the minimum we need; we're not | ||
| 4856 | * prepared to break up compounds into smaller pieces than that. | ||
| 4857 | * So, no point even trying to continue if the server won't | ||
| 4858 | * cooperate: | ||
| 4859 | */ | ||
| 4860 | if (rcvd->max_ops < sent->max_ops) | ||
| 4861 | return -EINVAL; | ||
| 4862 | if (rcvd->max_reqs == 0) | ||
| 4863 | return -EINVAL; | ||
| 4864 | return 0; | ||
| 4850 | } | 4865 | } |
| 4851 | 4866 | ||
| 4852 | #define _verify_fore_channel_attr(_name_) \ | 4867 | static int nfs4_verify_back_channel_attrs(struct nfs41_create_session_args *args, struct nfs4_session *session) |
| 4853 | _verify_channel_attr("fore", #_name_, \ | 4868 | { |
| 4854 | args->fc_attrs._name_, \ | 4869 | struct nfs4_channel_attrs *sent = &args->bc_attrs; |
| 4855 | session->fc_attrs._name_) | 4870 | struct nfs4_channel_attrs *rcvd = &session->bc_attrs; |
| 4856 | 4871 | ||
| 4857 | #define _verify_back_channel_attr(_name_) \ | 4872 | if (rcvd->max_rqst_sz > sent->max_rqst_sz) |
| 4858 | _verify_channel_attr("back", #_name_, \ | 4873 | return -EINVAL; |
| 4859 | args->bc_attrs._name_, \ | 4874 | if (rcvd->max_resp_sz < sent->max_resp_sz) |
| 4860 | session->bc_attrs._name_) | 4875 | return -EINVAL; |
| 4876 | if (rcvd->max_resp_sz_cached > sent->max_resp_sz_cached) | ||
| 4877 | return -EINVAL; | ||
| 4878 | /* These would render the backchannel useless: */ | ||
| 4879 | if (rcvd->max_ops == 0) | ||
| 4880 | return -EINVAL; | ||
| 4881 | if (rcvd->max_reqs == 0) | ||
| 4882 | return -EINVAL; | ||
| 4883 | return 0; | ||
| 4884 | } | ||
| 4861 | 4885 | ||
| 4862 | /* | ||
| 4863 | * The server is not allowed to increase the fore channel header pad size, | ||
| 4864 | * maximum response size, or maximum number of operations. | ||
| 4865 | * | ||
| 4866 | * The back channel attributes are only negotiatied down: We send what the | ||
| 4867 | * (back channel) server insists upon. | ||
| 4868 | */ | ||
| 4869 | static int nfs4_verify_channel_attrs(struct nfs41_create_session_args *args, | 4886 | static int nfs4_verify_channel_attrs(struct nfs41_create_session_args *args, |
| 4870 | struct nfs4_session *session) | 4887 | struct nfs4_session *session) |
| 4871 | { | 4888 | { |
| 4872 | int ret = 0; | 4889 | int ret; |
| 4873 | |||
| 4874 | ret |= _verify_fore_channel_attr(headerpadsz); | ||
| 4875 | ret |= _verify_fore_channel_attr(max_resp_sz); | ||
| 4876 | ret |= _verify_fore_channel_attr(max_ops); | ||
| 4877 | |||
| 4878 | ret |= _verify_back_channel_attr(headerpadsz); | ||
| 4879 | ret |= _verify_back_channel_attr(max_rqst_sz); | ||
| 4880 | ret |= _verify_back_channel_attr(max_resp_sz); | ||
| 4881 | ret |= _verify_back_channel_attr(max_resp_sz_cached); | ||
| 4882 | ret |= _verify_back_channel_attr(max_ops); | ||
| 4883 | ret |= _verify_back_channel_attr(max_reqs); | ||
| 4884 | 4890 | ||
| 4885 | return ret; | 4891 | ret = nfs4_verify_fore_channel_attrs(args, session); |
| 4892 | if (ret) | ||
| 4893 | return ret; | ||
| 4894 | return nfs4_verify_back_channel_attrs(args, session); | ||
| 4886 | } | 4895 | } |
| 4887 | 4896 | ||
| 4888 | static int _nfs4_proc_create_session(struct nfs_client *clp) | 4897 | static int _nfs4_proc_create_session(struct nfs_client *clp) |
| @@ -5255,6 +5264,147 @@ out: | |||
| 5255 | dprintk("<-- %s status=%d\n", __func__, status); | 5264 | dprintk("<-- %s status=%d\n", __func__, status); |
| 5256 | return status; | 5265 | return status; |
| 5257 | } | 5266 | } |
| 5267 | |||
| 5268 | static void | ||
| 5269 | nfs4_layoutget_prepare(struct rpc_task *task, void *calldata) | ||
| 5270 | { | ||
| 5271 | struct nfs4_layoutget *lgp = calldata; | ||
| 5272 | struct inode *ino = lgp->args.inode; | ||
| 5273 | struct nfs_server *server = NFS_SERVER(ino); | ||
| 5274 | |||
| 5275 | dprintk("--> %s\n", __func__); | ||
| 5276 | if (nfs4_setup_sequence(server, &lgp->args.seq_args, | ||
| 5277 | &lgp->res.seq_res, 0, task)) | ||
| 5278 | return; | ||
| 5279 | rpc_call_start(task); | ||
| 5280 | } | ||
| 5281 | |||
| 5282 | static void nfs4_layoutget_done(struct rpc_task *task, void *calldata) | ||
| 5283 | { | ||
| 5284 | struct nfs4_layoutget *lgp = calldata; | ||
| 5285 | struct nfs_server *server = NFS_SERVER(lgp->args.inode); | ||
| 5286 | |||
| 5287 | dprintk("--> %s\n", __func__); | ||
| 5288 | |||
| 5289 | if (!nfs4_sequence_done(task, &lgp->res.seq_res)) | ||
| 5290 | return; | ||
| 5291 | |||
| 5292 | switch (task->tk_status) { | ||
| 5293 | case 0: | ||
| 5294 | break; | ||
| 5295 | case -NFS4ERR_LAYOUTTRYLATER: | ||
| 5296 | case -NFS4ERR_RECALLCONFLICT: | ||
| 5297 | task->tk_status = -NFS4ERR_DELAY; | ||
| 5298 | /* Fall through */ | ||
| 5299 | default: | ||
| 5300 | if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) { | ||
| 5301 | rpc_restart_call_prepare(task); | ||
| 5302 | return; | ||
| 5303 | } | ||
| 5304 | } | ||
| 5305 | lgp->status = task->tk_status; | ||
| 5306 | dprintk("<-- %s\n", __func__); | ||
| 5307 | } | ||
| 5308 | |||
| 5309 | static void nfs4_layoutget_release(void *calldata) | ||
| 5310 | { | ||
| 5311 | struct nfs4_layoutget *lgp = calldata; | ||
| 5312 | |||
| 5313 | dprintk("--> %s\n", __func__); | ||
| 5314 | put_layout_hdr(lgp->args.inode); | ||
| 5315 | if (lgp->res.layout.buf != NULL) | ||
| 5316 | free_page((unsigned long) lgp->res.layout.buf); | ||
| 5317 | put_nfs_open_context(lgp->args.ctx); | ||
| 5318 | kfree(calldata); | ||
| 5319 | dprintk("<-- %s\n", __func__); | ||
| 5320 | } | ||
| 5321 | |||
| 5322 | static const struct rpc_call_ops nfs4_layoutget_call_ops = { | ||
| 5323 | .rpc_call_prepare = nfs4_layoutget_prepare, | ||
| 5324 | .rpc_call_done = nfs4_layoutget_done, | ||
| 5325 | .rpc_release = nfs4_layoutget_release, | ||
| 5326 | }; | ||
| 5327 | |||
| 5328 | int nfs4_proc_layoutget(struct nfs4_layoutget *lgp) | ||
| 5329 | { | ||
| 5330 | struct nfs_server *server = NFS_SERVER(lgp->args.inode); | ||
| 5331 | struct rpc_task *task; | ||
| 5332 | struct rpc_message msg = { | ||
| 5333 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET], | ||
| 5334 | .rpc_argp = &lgp->args, | ||
| 5335 | .rpc_resp = &lgp->res, | ||
| 5336 | }; | ||
| 5337 | struct rpc_task_setup task_setup_data = { | ||
| 5338 | .rpc_client = server->client, | ||
| 5339 | .rpc_message = &msg, | ||
| 5340 | .callback_ops = &nfs4_layoutget_call_ops, | ||
| 5341 | .callback_data = lgp, | ||
| 5342 | .flags = RPC_TASK_ASYNC, | ||
| 5343 | }; | ||
| 5344 | int status = 0; | ||
| 5345 | |||
| 5346 | dprintk("--> %s\n", __func__); | ||
| 5347 | |||
| 5348 | lgp->res.layout.buf = (void *)__get_free_page(GFP_NOFS); | ||
| 5349 | if (lgp->res.layout.buf == NULL) { | ||
| 5350 | nfs4_layoutget_release(lgp); | ||
| 5351 | return -ENOMEM; | ||
| 5352 | } | ||
| 5353 | |||
| 5354 | lgp->res.seq_res.sr_slot = NULL; | ||
| 5355 | task = rpc_run_task(&task_setup_data); | ||
| 5356 | if (IS_ERR(task)) | ||
| 5357 | return PTR_ERR(task); | ||
| 5358 | status = nfs4_wait_for_completion_rpc_task(task); | ||
| 5359 | if (status != 0) | ||
| 5360 | goto out; | ||
| 5361 | status = lgp->status; | ||
| 5362 | if (status != 0) | ||
| 5363 | goto out; | ||
| 5364 | status = pnfs_layout_process(lgp); | ||
| 5365 | out: | ||
| 5366 | rpc_put_task(task); | ||
| 5367 | dprintk("<-- %s status=%d\n", __func__, status); | ||
| 5368 | return status; | ||
| 5369 | } | ||
| 5370 | |||
| 5371 | static int | ||
| 5372 | _nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev) | ||
| 5373 | { | ||
| 5374 | struct nfs4_getdeviceinfo_args args = { | ||
| 5375 | .pdev = pdev, | ||
| 5376 | }; | ||
| 5377 | struct nfs4_getdeviceinfo_res res = { | ||
| 5378 | .pdev = pdev, | ||
| 5379 | }; | ||
| 5380 | struct rpc_message msg = { | ||
| 5381 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICEINFO], | ||
| 5382 | .rpc_argp = &args, | ||
| 5383 | .rpc_resp = &res, | ||
| 5384 | }; | ||
| 5385 | int status; | ||
| 5386 | |||
| 5387 | dprintk("--> %s\n", __func__); | ||
| 5388 | status = nfs4_call_sync(server, &msg, &args, &res, 0); | ||
| 5389 | dprintk("<-- %s status=%d\n", __func__, status); | ||
| 5390 | |||
| 5391 | return status; | ||
| 5392 | } | ||
| 5393 | |||
| 5394 | int nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev) | ||
| 5395 | { | ||
| 5396 | struct nfs4_exception exception = { }; | ||
| 5397 | int err; | ||
| 5398 | |||
| 5399 | do { | ||
| 5400 | err = nfs4_handle_exception(server, | ||
| 5401 | _nfs4_proc_getdeviceinfo(server, pdev), | ||
| 5402 | &exception); | ||
| 5403 | } while (exception.retry); | ||
| 5404 | return err; | ||
| 5405 | } | ||
| 5406 | EXPORT_SYMBOL_GPL(nfs4_proc_getdeviceinfo); | ||
| 5407 | |||
| 5258 | #endif /* CONFIG_NFS_V4_1 */ | 5408 | #endif /* CONFIG_NFS_V4_1 */ |
| 5259 | 5409 | ||
| 5260 | struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { | 5410 | struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { |
