diff options
Diffstat (limited to 'fs/nfs/callback_xdr.c')
-rw-r--r-- | fs/nfs/callback_xdr.c | 238 |
1 files changed, 221 insertions, 17 deletions
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 05af212f0edf..c6c86a77e043 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c | |||
@@ -10,8 +10,10 @@ | |||
10 | #include <linux/nfs4.h> | 10 | #include <linux/nfs4.h> |
11 | #include <linux/nfs_fs.h> | 11 | #include <linux/nfs_fs.h> |
12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
13 | #include <linux/sunrpc/bc_xprt.h> | ||
13 | #include "nfs4_fs.h" | 14 | #include "nfs4_fs.h" |
14 | #include "callback.h" | 15 | #include "callback.h" |
16 | #include "internal.h" | ||
15 | 17 | ||
16 | #define CB_OP_TAGLEN_MAXSZ (512) | 18 | #define CB_OP_TAGLEN_MAXSZ (512) |
17 | #define CB_OP_HDR_RES_MAXSZ (2 + CB_OP_TAGLEN_MAXSZ) | 19 | #define CB_OP_HDR_RES_MAXSZ (2 + CB_OP_TAGLEN_MAXSZ) |
@@ -22,6 +24,8 @@ | |||
22 | #define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) | 24 | #define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) |
23 | 25 | ||
24 | #if defined(CONFIG_NFS_V4_1) | 26 | #if defined(CONFIG_NFS_V4_1) |
27 | #define CB_OP_LAYOUTRECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) | ||
28 | #define CB_OP_DEVICENOTIFY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) | ||
25 | #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ | 29 | #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ |
26 | 4 + 1 + 3) | 30 | 4 + 1 + 3) |
27 | #define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) | 31 | #define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) |
@@ -33,7 +37,8 @@ | |||
33 | /* Internal error code */ | 37 | /* Internal error code */ |
34 | #define NFS4ERR_RESOURCE_HDR 11050 | 38 | #define NFS4ERR_RESOURCE_HDR 11050 |
35 | 39 | ||
36 | typedef __be32 (*callback_process_op_t)(void *, void *); | 40 | typedef __be32 (*callback_process_op_t)(void *, void *, |
41 | struct cb_process_state *); | ||
37 | typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *); | 42 | typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *); |
38 | typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *); | 43 | typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *); |
39 | 44 | ||
@@ -160,7 +165,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound | |||
160 | hdr->minorversion = ntohl(*p++); | 165 | hdr->minorversion = ntohl(*p++); |
161 | /* Check minor version is zero or one. */ | 166 | /* Check minor version is zero or one. */ |
162 | if (hdr->minorversion <= 1) { | 167 | if (hdr->minorversion <= 1) { |
163 | p++; /* skip callback_ident */ | 168 | hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */ |
164 | } else { | 169 | } else { |
165 | printk(KERN_WARNING "%s: NFSv4 server callback with " | 170 | printk(KERN_WARNING "%s: NFSv4 server callback with " |
166 | "illegal minor version %u!\n", | 171 | "illegal minor version %u!\n", |
@@ -220,6 +225,153 @@ out: | |||
220 | 225 | ||
221 | #if defined(CONFIG_NFS_V4_1) | 226 | #if defined(CONFIG_NFS_V4_1) |
222 | 227 | ||
228 | static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp, | ||
229 | struct xdr_stream *xdr, | ||
230 | struct cb_layoutrecallargs *args) | ||
231 | { | ||
232 | __be32 *p; | ||
233 | __be32 status = 0; | ||
234 | uint32_t iomode; | ||
235 | |||
236 | args->cbl_addr = svc_addr(rqstp); | ||
237 | p = read_buf(xdr, 4 * sizeof(uint32_t)); | ||
238 | if (unlikely(p == NULL)) { | ||
239 | status = htonl(NFS4ERR_BADXDR); | ||
240 | goto out; | ||
241 | } | ||
242 | |||
243 | args->cbl_layout_type = ntohl(*p++); | ||
244 | /* Depite the spec's xdr, iomode really belongs in the FILE switch, | ||
245 | * as it is unusable and ignored with the other types. | ||
246 | */ | ||
247 | iomode = ntohl(*p++); | ||
248 | args->cbl_layoutchanged = ntohl(*p++); | ||
249 | args->cbl_recall_type = ntohl(*p++); | ||
250 | |||
251 | if (args->cbl_recall_type == RETURN_FILE) { | ||
252 | args->cbl_range.iomode = iomode; | ||
253 | status = decode_fh(xdr, &args->cbl_fh); | ||
254 | if (unlikely(status != 0)) | ||
255 | goto out; | ||
256 | |||
257 | p = read_buf(xdr, 2 * sizeof(uint64_t)); | ||
258 | if (unlikely(p == NULL)) { | ||
259 | status = htonl(NFS4ERR_BADXDR); | ||
260 | goto out; | ||
261 | } | ||
262 | p = xdr_decode_hyper(p, &args->cbl_range.offset); | ||
263 | p = xdr_decode_hyper(p, &args->cbl_range.length); | ||
264 | status = decode_stateid(xdr, &args->cbl_stateid); | ||
265 | if (unlikely(status != 0)) | ||
266 | goto out; | ||
267 | } else if (args->cbl_recall_type == RETURN_FSID) { | ||
268 | p = read_buf(xdr, 2 * sizeof(uint64_t)); | ||
269 | if (unlikely(p == NULL)) { | ||
270 | status = htonl(NFS4ERR_BADXDR); | ||
271 | goto out; | ||
272 | } | ||
273 | p = xdr_decode_hyper(p, &args->cbl_fsid.major); | ||
274 | p = xdr_decode_hyper(p, &args->cbl_fsid.minor); | ||
275 | } else if (args->cbl_recall_type != RETURN_ALL) { | ||
276 | status = htonl(NFS4ERR_BADXDR); | ||
277 | goto out; | ||
278 | } | ||
279 | dprintk("%s: ltype 0x%x iomode %d changed %d recall_type %d\n", | ||
280 | __func__, | ||
281 | args->cbl_layout_type, iomode, | ||
282 | args->cbl_layoutchanged, args->cbl_recall_type); | ||
283 | out: | ||
284 | dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); | ||
285 | return status; | ||
286 | } | ||
287 | |||
288 | static | ||
289 | __be32 decode_devicenotify_args(struct svc_rqst *rqstp, | ||
290 | struct xdr_stream *xdr, | ||
291 | struct cb_devicenotifyargs *args) | ||
292 | { | ||
293 | __be32 *p; | ||
294 | __be32 status = 0; | ||
295 | u32 tmp; | ||
296 | int n, i; | ||
297 | args->ndevs = 0; | ||
298 | |||
299 | /* Num of device notifications */ | ||
300 | p = read_buf(xdr, sizeof(uint32_t)); | ||
301 | if (unlikely(p == NULL)) { | ||
302 | status = htonl(NFS4ERR_BADXDR); | ||
303 | goto out; | ||
304 | } | ||
305 | n = ntohl(*p++); | ||
306 | if (n <= 0) | ||
307 | goto out; | ||
308 | |||
309 | args->devs = kmalloc(n * sizeof(*args->devs), GFP_KERNEL); | ||
310 | if (!args->devs) { | ||
311 | status = htonl(NFS4ERR_DELAY); | ||
312 | goto out; | ||
313 | } | ||
314 | |||
315 | /* Decode each dev notification */ | ||
316 | for (i = 0; i < n; i++) { | ||
317 | struct cb_devicenotifyitem *dev = &args->devs[i]; | ||
318 | |||
319 | p = read_buf(xdr, (4 * sizeof(uint32_t)) + NFS4_DEVICEID4_SIZE); | ||
320 | if (unlikely(p == NULL)) { | ||
321 | status = htonl(NFS4ERR_BADXDR); | ||
322 | goto err; | ||
323 | } | ||
324 | |||
325 | tmp = ntohl(*p++); /* bitmap size */ | ||
326 | if (tmp != 1) { | ||
327 | status = htonl(NFS4ERR_INVAL); | ||
328 | goto err; | ||
329 | } | ||
330 | dev->cbd_notify_type = ntohl(*p++); | ||
331 | if (dev->cbd_notify_type != NOTIFY_DEVICEID4_CHANGE && | ||
332 | dev->cbd_notify_type != NOTIFY_DEVICEID4_DELETE) { | ||
333 | status = htonl(NFS4ERR_INVAL); | ||
334 | goto err; | ||
335 | } | ||
336 | |||
337 | tmp = ntohl(*p++); /* opaque size */ | ||
338 | if (((dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE) && | ||
339 | (tmp != NFS4_DEVICEID4_SIZE + 8)) || | ||
340 | ((dev->cbd_notify_type == NOTIFY_DEVICEID4_DELETE) && | ||
341 | (tmp != NFS4_DEVICEID4_SIZE + 4))) { | ||
342 | status = htonl(NFS4ERR_INVAL); | ||
343 | goto err; | ||
344 | } | ||
345 | dev->cbd_layout_type = ntohl(*p++); | ||
346 | memcpy(dev->cbd_dev_id.data, p, NFS4_DEVICEID4_SIZE); | ||
347 | p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); | ||
348 | |||
349 | if (dev->cbd_layout_type == NOTIFY_DEVICEID4_CHANGE) { | ||
350 | p = read_buf(xdr, sizeof(uint32_t)); | ||
351 | if (unlikely(p == NULL)) { | ||
352 | status = htonl(NFS4ERR_BADXDR); | ||
353 | goto err; | ||
354 | } | ||
355 | dev->cbd_immediate = ntohl(*p++); | ||
356 | } else { | ||
357 | dev->cbd_immediate = 0; | ||
358 | } | ||
359 | |||
360 | args->ndevs++; | ||
361 | |||
362 | dprintk("%s: type %d layout 0x%x immediate %d\n", | ||
363 | __func__, dev->cbd_notify_type, dev->cbd_layout_type, | ||
364 | dev->cbd_immediate); | ||
365 | } | ||
366 | out: | ||
367 | dprintk("%s: status %d ndevs %d\n", | ||
368 | __func__, ntohl(status), args->ndevs); | ||
369 | return status; | ||
370 | err: | ||
371 | kfree(args->devs); | ||
372 | goto out; | ||
373 | } | ||
374 | |||
223 | static __be32 decode_sessionid(struct xdr_stream *xdr, | 375 | static __be32 decode_sessionid(struct xdr_stream *xdr, |
224 | struct nfs4_sessionid *sid) | 376 | struct nfs4_sessionid *sid) |
225 | { | 377 | { |
@@ -574,11 +726,11 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) | |||
574 | case OP_CB_SEQUENCE: | 726 | case OP_CB_SEQUENCE: |
575 | case OP_CB_RECALL_ANY: | 727 | case OP_CB_RECALL_ANY: |
576 | case OP_CB_RECALL_SLOT: | 728 | case OP_CB_RECALL_SLOT: |
729 | case OP_CB_LAYOUTRECALL: | ||
730 | case OP_CB_NOTIFY_DEVICEID: | ||
577 | *op = &callback_ops[op_nr]; | 731 | *op = &callback_ops[op_nr]; |
578 | break; | 732 | break; |
579 | 733 | ||
580 | case OP_CB_LAYOUTRECALL: | ||
581 | case OP_CB_NOTIFY_DEVICEID: | ||
582 | case OP_CB_NOTIFY: | 734 | case OP_CB_NOTIFY: |
583 | case OP_CB_PUSH_DELEG: | 735 | case OP_CB_PUSH_DELEG: |
584 | case OP_CB_RECALLABLE_OBJ_AVAIL: | 736 | case OP_CB_RECALLABLE_OBJ_AVAIL: |
@@ -593,6 +745,37 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) | |||
593 | return htonl(NFS_OK); | 745 | return htonl(NFS_OK); |
594 | } | 746 | } |
595 | 747 | ||
748 | static void nfs4_callback_free_slot(struct nfs4_session *session) | ||
749 | { | ||
750 | struct nfs4_slot_table *tbl = &session->bc_slot_table; | ||
751 | |||
752 | spin_lock(&tbl->slot_tbl_lock); | ||
753 | /* | ||
754 | * Let the state manager know callback processing done. | ||
755 | * A single slot, so highest used slotid is either 0 or -1 | ||
756 | */ | ||
757 | tbl->highest_used_slotid--; | ||
758 | nfs4_check_drain_bc_complete(session); | ||
759 | spin_unlock(&tbl->slot_tbl_lock); | ||
760 | } | ||
761 | |||
762 | static void nfs4_cb_free_slot(struct nfs_client *clp) | ||
763 | { | ||
764 | if (clp && clp->cl_session) | ||
765 | nfs4_callback_free_slot(clp->cl_session); | ||
766 | } | ||
767 | |||
768 | /* A single slot, so highest used slotid is either 0 or -1 */ | ||
769 | void nfs4_cb_take_slot(struct nfs_client *clp) | ||
770 | { | ||
771 | struct nfs4_slot_table *tbl = &clp->cl_session->bc_slot_table; | ||
772 | |||
773 | spin_lock(&tbl->slot_tbl_lock); | ||
774 | tbl->highest_used_slotid++; | ||
775 | BUG_ON(tbl->highest_used_slotid != 0); | ||
776 | spin_unlock(&tbl->slot_tbl_lock); | ||
777 | } | ||
778 | |||
596 | #else /* CONFIG_NFS_V4_1 */ | 779 | #else /* CONFIG_NFS_V4_1 */ |
597 | 780 | ||
598 | static __be32 | 781 | static __be32 |
@@ -601,6 +784,9 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) | |||
601 | return htonl(NFS4ERR_MINOR_VERS_MISMATCH); | 784 | return htonl(NFS4ERR_MINOR_VERS_MISMATCH); |
602 | } | 785 | } |
603 | 786 | ||
787 | static void nfs4_cb_free_slot(struct nfs_client *clp) | ||
788 | { | ||
789 | } | ||
604 | #endif /* CONFIG_NFS_V4_1 */ | 790 | #endif /* CONFIG_NFS_V4_1 */ |
605 | 791 | ||
606 | static __be32 | 792 | static __be32 |
@@ -621,7 +807,8 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op) | |||
621 | static __be32 process_op(uint32_t minorversion, int nop, | 807 | static __be32 process_op(uint32_t minorversion, int nop, |
622 | struct svc_rqst *rqstp, | 808 | struct svc_rqst *rqstp, |
623 | struct xdr_stream *xdr_in, void *argp, | 809 | struct xdr_stream *xdr_in, void *argp, |
624 | struct xdr_stream *xdr_out, void *resp, int* drc_status) | 810 | struct xdr_stream *xdr_out, void *resp, |
811 | struct cb_process_state *cps) | ||
625 | { | 812 | { |
626 | struct callback_op *op = &callback_ops[0]; | 813 | struct callback_op *op = &callback_ops[0]; |
627 | unsigned int op_nr; | 814 | unsigned int op_nr; |
@@ -644,8 +831,8 @@ static __be32 process_op(uint32_t minorversion, int nop, | |||
644 | if (status) | 831 | if (status) |
645 | goto encode_hdr; | 832 | goto encode_hdr; |
646 | 833 | ||
647 | if (*drc_status) { | 834 | if (cps->drc_status) { |
648 | status = *drc_status; | 835 | status = cps->drc_status; |
649 | goto encode_hdr; | 836 | goto encode_hdr; |
650 | } | 837 | } |
651 | 838 | ||
@@ -653,16 +840,10 @@ static __be32 process_op(uint32_t minorversion, int nop, | |||
653 | if (maxlen > 0 && maxlen < PAGE_SIZE) { | 840 | if (maxlen > 0 && maxlen < PAGE_SIZE) { |
654 | status = op->decode_args(rqstp, xdr_in, argp); | 841 | status = op->decode_args(rqstp, xdr_in, argp); |
655 | if (likely(status == 0)) | 842 | if (likely(status == 0)) |
656 | status = op->process_op(argp, resp); | 843 | status = op->process_op(argp, resp, cps); |
657 | } else | 844 | } else |
658 | status = htonl(NFS4ERR_RESOURCE); | 845 | status = htonl(NFS4ERR_RESOURCE); |
659 | 846 | ||
660 | /* Only set by OP_CB_SEQUENCE processing */ | ||
661 | if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) { | ||
662 | *drc_status = status; | ||
663 | status = 0; | ||
664 | } | ||
665 | |||
666 | encode_hdr: | 847 | encode_hdr: |
667 | res = encode_op_hdr(xdr_out, op_nr, status); | 848 | res = encode_op_hdr(xdr_out, op_nr, status); |
668 | if (unlikely(res)) | 849 | if (unlikely(res)) |
@@ -681,8 +862,11 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r | |||
681 | struct cb_compound_hdr_arg hdr_arg = { 0 }; | 862 | struct cb_compound_hdr_arg hdr_arg = { 0 }; |
682 | struct cb_compound_hdr_res hdr_res = { NULL }; | 863 | struct cb_compound_hdr_res hdr_res = { NULL }; |
683 | struct xdr_stream xdr_in, xdr_out; | 864 | struct xdr_stream xdr_in, xdr_out; |
684 | __be32 *p; | 865 | __be32 *p, status; |
685 | __be32 status, drc_status = 0; | 866 | struct cb_process_state cps = { |
867 | .drc_status = 0, | ||
868 | .clp = NULL, | ||
869 | }; | ||
686 | unsigned int nops = 0; | 870 | unsigned int nops = 0; |
687 | 871 | ||
688 | dprintk("%s: start\n", __func__); | 872 | dprintk("%s: start\n", __func__); |
@@ -696,6 +880,12 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r | |||
696 | if (status == __constant_htonl(NFS4ERR_RESOURCE)) | 880 | if (status == __constant_htonl(NFS4ERR_RESOURCE)) |
697 | return rpc_garbage_args; | 881 | return rpc_garbage_args; |
698 | 882 | ||
883 | if (hdr_arg.minorversion == 0) { | ||
884 | cps.clp = nfs4_find_client_ident(hdr_arg.cb_ident); | ||
885 | if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp)) | ||
886 | return rpc_drop_reply; | ||
887 | } | ||
888 | |||
699 | hdr_res.taglen = hdr_arg.taglen; | 889 | hdr_res.taglen = hdr_arg.taglen; |
700 | hdr_res.tag = hdr_arg.tag; | 890 | hdr_res.tag = hdr_arg.tag; |
701 | if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0) | 891 | if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0) |
@@ -703,7 +893,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r | |||
703 | 893 | ||
704 | while (status == 0 && nops != hdr_arg.nops) { | 894 | while (status == 0 && nops != hdr_arg.nops) { |
705 | status = process_op(hdr_arg.minorversion, nops, rqstp, | 895 | status = process_op(hdr_arg.minorversion, nops, rqstp, |
706 | &xdr_in, argp, &xdr_out, resp, &drc_status); | 896 | &xdr_in, argp, &xdr_out, resp, &cps); |
707 | nops++; | 897 | nops++; |
708 | } | 898 | } |
709 | 899 | ||
@@ -716,6 +906,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r | |||
716 | 906 | ||
717 | *hdr_res.status = status; | 907 | *hdr_res.status = status; |
718 | *hdr_res.nops = htonl(nops); | 908 | *hdr_res.nops = htonl(nops); |
909 | nfs4_cb_free_slot(cps.clp); | ||
910 | nfs_put_client(cps.clp); | ||
719 | dprintk("%s: done, status = %u\n", __func__, ntohl(status)); | 911 | dprintk("%s: done, status = %u\n", __func__, ntohl(status)); |
720 | return rpc_success; | 912 | return rpc_success; |
721 | } | 913 | } |
@@ -739,6 +931,18 @@ static struct callback_op callback_ops[] = { | |||
739 | .res_maxsize = CB_OP_RECALL_RES_MAXSZ, | 931 | .res_maxsize = CB_OP_RECALL_RES_MAXSZ, |
740 | }, | 932 | }, |
741 | #if defined(CONFIG_NFS_V4_1) | 933 | #if defined(CONFIG_NFS_V4_1) |
934 | [OP_CB_LAYOUTRECALL] = { | ||
935 | .process_op = (callback_process_op_t)nfs4_callback_layoutrecall, | ||
936 | .decode_args = | ||
937 | (callback_decode_arg_t)decode_layoutrecall_args, | ||
938 | .res_maxsize = CB_OP_LAYOUTRECALL_RES_MAXSZ, | ||
939 | }, | ||
940 | [OP_CB_NOTIFY_DEVICEID] = { | ||
941 | .process_op = (callback_process_op_t)nfs4_callback_devicenotify, | ||
942 | .decode_args = | ||
943 | (callback_decode_arg_t)decode_devicenotify_args, | ||
944 | .res_maxsize = CB_OP_DEVICENOTIFY_RES_MAXSZ, | ||
945 | }, | ||
742 | [OP_CB_SEQUENCE] = { | 946 | [OP_CB_SEQUENCE] = { |
743 | .process_op = (callback_process_op_t)nfs4_callback_sequence, | 947 | .process_op = (callback_process_op_t)nfs4_callback_sequence, |
744 | .decode_args = (callback_decode_arg_t)decode_cb_sequence_args, | 948 | .decode_args = (callback_decode_arg_t)decode_cb_sequence_args, |