Loading fs/nfs/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -74,6 +74,15 @@ config NFS_V4 If unsure, say N. config NFS_V4_1 bool "NFS client support for NFSv4.1 (DEVELOPER ONLY)" depends on NFS_V4 && EXPERIMENTAL help This option enables support for minor version 1 of the NFSv4 protocol (draft-ietf-nfsv4-minorversion1) in the kernel's NFS client. Unless you're an NFS developer, say N. config ROOT_NFS bool "Root file system on NFS" depends on NFS_FS=y && IP_PNP Loading fs/nfs/callback.c +179 −35 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ #include <linux/freezer.h> #include <linux/kthread.h> #include <linux/sunrpc/svcauth_gss.h> #if defined(CONFIG_NFS_V4_1) #include <linux/sunrpc/bc_xprt.h> #endif #include <net/inet_sock.h> Loading @@ -28,11 +31,12 @@ struct nfs_callback_data { unsigned int users; struct svc_serv *serv; struct svc_rqst *rqst; struct task_struct *task; }; static struct nfs_callback_data nfs_callback_info; static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1]; static DEFINE_MUTEX(nfs_callback_mutex); static struct svc_program nfs4_callback_program; Loading @@ -56,10 +60,10 @@ module_param_call(callback_tcpport, param_set_port, param_get_int, &nfs_callback_set_tcpport, 0644); /* * This is the callback kernel thread. * This is the NFSv4 callback kernel thread. */ static int nfs_callback_svc(void *vrqstp) nfs4_callback_svc(void *vrqstp) { int err, preverr = 0; struct svc_rqst *rqstp = vrqstp; Loading Loading @@ -97,20 +101,12 @@ nfs_callback_svc(void *vrqstp) } /* * Bring up the callback thread if it is not already up. * Prepare to bring up the NFSv4 callback service */ int nfs_callback_up(void) struct svc_rqst * nfs4_callback_up(struct svc_serv *serv) { struct svc_serv *serv = NULL; int ret = 0; mutex_lock(&nfs_callback_mutex); if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) goto out; serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); ret = -ENOMEM; if (!serv) goto out_err; int ret; ret = svc_create_xprt(serv, "tcp", PF_INET, nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); Loading @@ -131,23 +127,168 @@ int nfs_callback_up(void) goto out_err; #endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]); if (IS_ERR(nfs_callback_info.rqst)) { ret = PTR_ERR(nfs_callback_info.rqst); nfs_callback_info.rqst = NULL; return svc_prepare_thread(serv, &serv->sv_pools[0]); out_err: if (ret == 0) ret = -ENOMEM; return ERR_PTR(ret); } #if defined(CONFIG_NFS_V4_1) /* * The callback service for NFSv4.1 callbacks */ static int nfs41_callback_svc(void *vrqstp) { struct svc_rqst *rqstp = vrqstp; struct svc_serv *serv = rqstp->rq_server; struct rpc_rqst *req; int error; DEFINE_WAIT(wq); set_freezable(); /* * FIXME: do we really need to run this under the BKL? If so, please * add a comment about what it's intended to protect. */ lock_kernel(); while (!kthread_should_stop()) { prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE); spin_lock_bh(&serv->sv_cb_lock); if (!list_empty(&serv->sv_cb_list)) { req = list_first_entry(&serv->sv_cb_list, struct rpc_rqst, rq_bc_list); list_del(&req->rq_bc_list); spin_unlock_bh(&serv->sv_cb_lock); dprintk("Invoking bc_svc_process()\n"); error = bc_svc_process(serv, req, rqstp); dprintk("bc_svc_process() returned w/ error code= %d\n", error); } else { spin_unlock_bh(&serv->sv_cb_lock); schedule(); } finish_wait(&serv->sv_cb_waitq, &wq); } unlock_kernel(); return 0; } /* * Bring up the NFSv4.1 callback service */ struct svc_rqst * nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) { struct svc_xprt *bc_xprt; struct svc_rqst *rqstp = ERR_PTR(-ENOMEM); dprintk("--> %s\n", __func__); /* Create a svc_sock for the service */ bc_xprt = svc_sock_create(serv, xprt->prot); if (!bc_xprt) goto out; /* * Save the svc_serv in the transport so that it can * be referenced when the session backchannel is initialized */ serv->bc_xprt = bc_xprt; xprt->bc_serv = serv; INIT_LIST_HEAD(&serv->sv_cb_list); spin_lock_init(&serv->sv_cb_lock); init_waitqueue_head(&serv->sv_cb_waitq); rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); if (IS_ERR(rqstp)) svc_sock_destroy(bc_xprt); out: dprintk("--> %s return %p\n", __func__, rqstp); return rqstp; } static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, struct svc_serv *serv, struct rpc_xprt *xprt, struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) { if (minorversion) { *rqstpp = nfs41_callback_up(serv, xprt); *callback_svc = nfs41_callback_svc; } return minorversion; } static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, struct nfs_callback_data *cb_info) { if (minorversion) xprt->bc_serv = cb_info->serv; } #else static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, struct svc_serv *serv, struct rpc_xprt *xprt, struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) { return 0; } static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, struct nfs_callback_data *cb_info) { } #endif /* CONFIG_NFS_V4_1 */ /* * Bring up the callback thread if it is not already up. */ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) { struct svc_serv *serv = NULL; struct svc_rqst *rqstp; int (*callback_svc)(void *vrqstp); struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; char svc_name[12]; int ret = 0; int minorversion_setup; mutex_lock(&nfs_callback_mutex); if (cb_info->users++ || cb_info->task != NULL) { nfs_callback_bc_serv(minorversion, xprt, cb_info); goto out; } serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); if (!serv) { ret = -ENOMEM; goto out_err; } minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion, serv, xprt, &rqstp, &callback_svc); if (!minorversion_setup) { /* v4.0 callback setup */ rqstp = nfs4_callback_up(serv); callback_svc = nfs4_callback_svc; } if (IS_ERR(rqstp)) { ret = PTR_ERR(rqstp); goto out_err; } svc_sock_update_bufs(serv); nfs_callback_info.task = kthread_run(nfs_callback_svc, nfs_callback_info.rqst, "nfsv4-svc"); if (IS_ERR(nfs_callback_info.task)) { ret = PTR_ERR(nfs_callback_info.task); svc_exit_thread(nfs_callback_info.rqst); nfs_callback_info.rqst = NULL; nfs_callback_info.task = NULL; sprintf(svc_name, "nfsv4.%u-svc", minorversion); cb_info->serv = serv; cb_info->rqst = rqstp; cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name); if (IS_ERR(cb_info->task)) { ret = PTR_ERR(cb_info->task); svc_exit_thread(cb_info->rqst); cb_info->rqst = NULL; cb_info->task = NULL; goto out_err; } out: Loading @@ -164,22 +305,25 @@ int nfs_callback_up(void) out_err: dprintk("NFS: Couldn't create callback socket or server thread; " "err = %d\n", ret); nfs_callback_info.users--; cb_info->users--; goto out; } /* * Kill the callback thread if it's no longer being used. */ void nfs_callback_down(void) void nfs_callback_down(int minorversion) { struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; mutex_lock(&nfs_callback_mutex); nfs_callback_info.users--; if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) { kthread_stop(nfs_callback_info.task); svc_exit_thread(nfs_callback_info.rqst); nfs_callback_info.rqst = NULL; nfs_callback_info.task = NULL; cb_info->users--; if (cb_info->users == 0 && cb_info->task != NULL) { kthread_stop(cb_info->task); svc_exit_thread(cb_info->rqst); cb_info->serv = NULL; cb_info->rqst = NULL; cb_info->task = NULL; } mutex_unlock(&nfs_callback_mutex); } Loading fs/nfs/callback.h +61 −7 Original line number Diff line number Diff line Loading @@ -20,13 +20,24 @@ enum nfs4_callback_procnum { enum nfs4_callback_opnum { OP_CB_GETATTR = 3, OP_CB_RECALL = 4, /* Callback operations new to NFSv4.1 */ OP_CB_LAYOUTRECALL = 5, OP_CB_NOTIFY = 6, OP_CB_PUSH_DELEG = 7, OP_CB_RECALL_ANY = 8, OP_CB_RECALLABLE_OBJ_AVAIL = 9, OP_CB_RECALL_SLOT = 10, OP_CB_SEQUENCE = 11, OP_CB_WANTS_CANCELLED = 12, OP_CB_NOTIFY_LOCK = 13, OP_CB_NOTIFY_DEVICEID = 14, OP_CB_ILLEGAL = 10044, }; struct cb_compound_hdr_arg { unsigned int taglen; const char *tag; unsigned int callback_ident; unsigned int minorversion; unsigned nops; }; Loading Loading @@ -59,16 +70,59 @@ struct cb_recallargs { uint32_t truncate; }; #if defined(CONFIG_NFS_V4_1) struct referring_call { uint32_t rc_sequenceid; uint32_t rc_slotid; }; struct referring_call_list { struct nfs4_sessionid rcl_sessionid; uint32_t rcl_nrefcalls; struct referring_call *rcl_refcalls; }; struct cb_sequenceargs { struct sockaddr *csa_addr; struct nfs4_sessionid csa_sessionid; uint32_t csa_sequenceid; uint32_t csa_slotid; uint32_t csa_highestslotid; uint32_t csa_cachethis; uint32_t csa_nrclists; struct referring_call_list *csa_rclists; }; struct cb_sequenceres { __be32 csr_status; struct nfs4_sessionid csr_sessionid; uint32_t csr_sequenceid; uint32_t csr_slotid; uint32_t csr_highestslotid; uint32_t csr_target_highestslotid; }; extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, struct cb_sequenceres *res); #endif /* CONFIG_NFS_V4_1 */ extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res); extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy); #ifdef CONFIG_NFS_V4 extern int nfs_callback_up(void); extern void nfs_callback_down(void); #else #define nfs_callback_up() (0) #define nfs_callback_down() do {} while(0) #endif extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt); extern void nfs_callback_down(int minorversion); #endif /* CONFIG_NFS_V4 */ /* * nfs41: Callbacks are expected to not cause substantial latency, * so we limit their concurrency to 1 by setting up the maximum number * of slots for the backchannel. */ #define NFS41_BC_MIN_CALLBACKS 1 #define NFS41_BC_MAX_CALLBACKS 1 extern unsigned int nfs_callback_set_tcpport; extern unsigned short nfs_callback_tcpport; Loading fs/nfs/callback_proc.c +127 −0 Original line number Diff line number Diff line Loading @@ -101,3 +101,130 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) dprintk("%s: exit with status = %d\n", __func__, ntohl(res)); return res; } #if defined(CONFIG_NFS_V4_1) /* * Validate the sequenceID sent by the server. * Return success if the sequenceID is one more than what we last saw on * this slot, accounting for wraparound. Increments the slot's sequence. * * We don't yet implement a duplicate request cache, so at this time * we will log replays, and process them as if we had not seen them before, * but we don't bump the sequence in the slot. Not too worried about it, * since we only currently implement idempotent callbacks anyway. * * We have a single slot backchannel at this time, so we don't bother * checking the used_slots bit array on the table. The lower layer guarantees * a single outstanding callback request at a time. */ static int validate_seqid(struct nfs4_slot_table *tbl, u32 slotid, u32 seqid) { struct nfs4_slot *slot; dprintk("%s enter. slotid %d seqid %d\n", __func__, slotid, seqid); if (slotid > NFS41_BC_MAX_CALLBACKS) return htonl(NFS4ERR_BADSLOT); slot = tbl->slots + slotid; dprintk("%s slot table seqid: %d\n", __func__, slot->seq_nr); /* Normal */ if (likely(seqid == slot->seq_nr + 1)) { slot->seq_nr++; return htonl(NFS4_OK); } /* Replay */ if (seqid == slot->seq_nr) { dprintk("%s seqid %d is a replay - no DRC available\n", __func__, seqid); return htonl(NFS4_OK); } /* Wraparound */ if (seqid == 1 && (slot->seq_nr + 1) == 0) { slot->seq_nr = 1; return htonl(NFS4_OK); } /* Misordered request */ return htonl(NFS4ERR_SEQ_MISORDERED); } /* * Returns a pointer to a held 'struct nfs_client' that matches the server's * address, major version number, and session ID. It is the caller's * responsibility to release the returned reference. * * Returns NULL if there are no connections with sessions, or if no session * matches the one of interest. */ static struct nfs_client *find_client_with_session( const struct sockaddr *addr, u32 nfsversion, struct nfs4_sessionid *sessionid) { struct nfs_client *clp; clp = nfs_find_client(addr, 4); if (clp == NULL) return NULL; do { struct nfs_client *prev = clp; if (clp->cl_session != NULL) { if (memcmp(clp->cl_session->sess_id.data, sessionid->data, NFS4_MAX_SESSIONID_LEN) == 0) { /* Returns a held reference to clp */ return clp; } } clp = nfs_find_client_next(prev); nfs_put_client(prev); } while (clp != NULL); return NULL; } /* FIXME: referring calls should be processed */ unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, struct cb_sequenceres *res) { struct nfs_client *clp; int i, status; for (i = 0; i < args->csa_nrclists; i++) kfree(args->csa_rclists[i].rcl_refcalls); kfree(args->csa_rclists); status = htonl(NFS4ERR_BADSESSION); clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid); if (clp == NULL) goto out; status = validate_seqid(&clp->cl_session->bc_slot_table, args->csa_slotid, args->csa_sequenceid); if (status) goto out_putclient; memcpy(&res->csr_sessionid, &args->csa_sessionid, sizeof(res->csr_sessionid)); res->csr_sequenceid = args->csa_sequenceid; res->csr_slotid = args->csa_slotid; res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; out_putclient: nfs_put_client(clp); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); res->csr_status = status; return res->csr_status; } #endif /* CONFIG_NFS_V4_1 */ fs/nfs/callback_xdr.c +258 −22 Original line number Diff line number Diff line Loading @@ -20,6 +20,11 @@ 2 + 2 + 3 + 3) #define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) #if defined(CONFIG_NFS_V4_1) #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ 4 + 1 + 3) #endif /* CONFIG_NFS_V4_1 */ #define NFSDBG_FACILITY NFSDBG_CALLBACK typedef __be32 (*callback_process_op_t)(void *, void *); Loading Loading @@ -132,7 +137,6 @@ static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid) static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound_hdr_arg *hdr) { __be32 *p; unsigned int minor_version; __be32 status; status = decode_string(xdr, &hdr->taglen, &hdr->tag); Loading @@ -147,15 +151,19 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound p = read_buf(xdr, 12); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); minor_version = ntohl(*p++); /* Check minor version is zero. */ if (minor_version != 0) { printk(KERN_WARNING "%s: NFSv4 server callback with illegal minor version %u!\n", __func__, minor_version); hdr->minorversion = ntohl(*p++); /* Check minor version is zero or one. */ if (hdr->minorversion <= 1) { p++; /* skip callback_ident */ } else { printk(KERN_WARNING "%s: NFSv4 server callback with " "illegal minor version %u!\n", __func__, hdr->minorversion); return htonl(NFS4ERR_MINOR_VERS_MISMATCH); } hdr->callback_ident = ntohl(*p++); hdr->nops = ntohl(*p); dprintk("%s: minorversion %d nops %d\n", __func__, hdr->minorversion, hdr->nops); return 0; } Loading Loading @@ -204,6 +212,122 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, return status; } #if defined(CONFIG_NFS_V4_1) static unsigned decode_sessionid(struct xdr_stream *xdr, struct nfs4_sessionid *sid) { uint32_t *p; int len = NFS4_MAX_SESSIONID_LEN; p = read_buf(xdr, len); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE);; memcpy(sid->data, p, len); return 0; } static unsigned decode_rc_list(struct xdr_stream *xdr, struct referring_call_list *rc_list) { uint32_t *p; int i; unsigned status; status = decode_sessionid(xdr, &rc_list->rcl_sessionid); if (status) goto out; status = htonl(NFS4ERR_RESOURCE); p = read_buf(xdr, sizeof(uint32_t)); if (unlikely(p == NULL)) goto out; rc_list->rcl_nrefcalls = ntohl(*p++); if (rc_list->rcl_nrefcalls) { p = read_buf(xdr, rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t)); if (unlikely(p == NULL)) goto out; rc_list->rcl_refcalls = kmalloc(rc_list->rcl_nrefcalls * sizeof(*rc_list->rcl_refcalls), GFP_KERNEL); if (unlikely(rc_list->rcl_refcalls == NULL)) goto out; for (i = 0; i < rc_list->rcl_nrefcalls; i++) { rc_list->rcl_refcalls[i].rc_sequenceid = ntohl(*p++); rc_list->rcl_refcalls[i].rc_slotid = ntohl(*p++); } } status = 0; out: return status; } static unsigned decode_cb_sequence_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_sequenceargs *args) { uint32_t *p; int i; unsigned status; status = decode_sessionid(xdr, &args->csa_sessionid); if (status) goto out; status = htonl(NFS4ERR_RESOURCE); p = read_buf(xdr, 5 * sizeof(uint32_t)); if (unlikely(p == NULL)) goto out; args->csa_addr = svc_addr(rqstp); args->csa_sequenceid = ntohl(*p++); args->csa_slotid = ntohl(*p++); args->csa_highestslotid = ntohl(*p++); args->csa_cachethis = ntohl(*p++); args->csa_nrclists = ntohl(*p++); args->csa_rclists = NULL; if (args->csa_nrclists) { args->csa_rclists = kmalloc(args->csa_nrclists * sizeof(*args->csa_rclists), GFP_KERNEL); if (unlikely(args->csa_rclists == NULL)) goto out; for (i = 0; i < args->csa_nrclists; i++) { status = decode_rc_list(xdr, &args->csa_rclists[i]); if (status) goto out_free; } } status = 0; dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u slotid %u " "highestslotid %u cachethis %d nrclists %u\n", __func__, ((u32 *)&args->csa_sessionid)[0], ((u32 *)&args->csa_sessionid)[1], ((u32 *)&args->csa_sessionid)[2], ((u32 *)&args->csa_sessionid)[3], args->csa_sequenceid, args->csa_slotid, args->csa_highestslotid, args->csa_cachethis, args->csa_nrclists); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); return status; out_free: for (i = 0; i < args->csa_nrclists; i++) kfree(args->csa_rclists[i].rcl_refcalls); kfree(args->csa_rclists); goto out; } #endif /* CONFIG_NFS_V4_1 */ static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str) { __be32 *p; Loading Loading @@ -353,31 +477,134 @@ static __be32 encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, return status; } static __be32 process_op(struct svc_rqst *rqstp, #if defined(CONFIG_NFS_V4_1) static unsigned encode_sessionid(struct xdr_stream *xdr, const struct nfs4_sessionid *sid) { uint32_t *p; int len = NFS4_MAX_SESSIONID_LEN; p = xdr_reserve_space(xdr, len); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); memcpy(p, sid, len); return 0; } static unsigned encode_cb_sequence_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, const struct cb_sequenceres *res) { uint32_t *p; unsigned status = res->csr_status; if (unlikely(status != 0)) goto out; encode_sessionid(xdr, &res->csr_sessionid); p = xdr_reserve_space(xdr, 4 * sizeof(uint32_t)); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); *p++ = htonl(res->csr_sequenceid); *p++ = htonl(res->csr_slotid); *p++ = htonl(res->csr_highestslotid); *p++ = htonl(res->csr_target_highestslotid); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); return status; } static __be32 preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) { if (op_nr == OP_CB_SEQUENCE) { if (nop != 0) return htonl(NFS4ERR_SEQUENCE_POS); } else { if (nop == 0) return htonl(NFS4ERR_OP_NOT_IN_SESSION); } switch (op_nr) { case OP_CB_GETATTR: case OP_CB_RECALL: case OP_CB_SEQUENCE: *op = &callback_ops[op_nr]; break; case OP_CB_LAYOUTRECALL: case OP_CB_NOTIFY_DEVICEID: case OP_CB_NOTIFY: case OP_CB_PUSH_DELEG: case OP_CB_RECALL_ANY: case OP_CB_RECALLABLE_OBJ_AVAIL: case OP_CB_RECALL_SLOT: case OP_CB_WANTS_CANCELLED: case OP_CB_NOTIFY_LOCK: return htonl(NFS4ERR_NOTSUPP); default: return htonl(NFS4ERR_OP_ILLEGAL); } return htonl(NFS_OK); } #else /* CONFIG_NFS_V4_1 */ static __be32 preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) { return htonl(NFS4ERR_MINOR_VERS_MISMATCH); } #endif /* CONFIG_NFS_V4_1 */ static __be32 preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op) { switch (op_nr) { case OP_CB_GETATTR: case OP_CB_RECALL: *op = &callback_ops[op_nr]; break; default: return htonl(NFS4ERR_OP_ILLEGAL); } return htonl(NFS_OK); } static __be32 process_op(uint32_t minorversion, int nop, struct svc_rqst *rqstp, struct xdr_stream *xdr_in, void *argp, struct xdr_stream *xdr_out, void *resp) { struct callback_op *op = &callback_ops[0]; unsigned int op_nr = OP_CB_ILLEGAL; __be32 status = 0; __be32 status; long maxlen; __be32 res; dprintk("%s: start\n", __func__); status = decode_op_hdr(xdr_in, &op_nr); if (likely(status == 0)) { switch (op_nr) { case OP_CB_GETATTR: case OP_CB_RECALL: op = &callback_ops[op_nr]; break; default: op_nr = OP_CB_ILLEGAL; op = &callback_ops[0]; if (unlikely(status)) { status = htonl(NFS4ERR_OP_ILLEGAL); goto out; } } dprintk("%s: minorversion=%d nop=%d op_nr=%u\n", __func__, minorversion, nop, op_nr); status = minorversion ? preprocess_nfs41_op(nop, op_nr, &op) : preprocess_nfs4_op(op_nr, &op); if (status == htonl(NFS4ERR_OP_ILLEGAL)) op_nr = OP_CB_ILLEGAL; out: maxlen = xdr_out->end - xdr_out->p; if (maxlen > 0 && maxlen < PAGE_SIZE) { if (likely(status == 0 && op->decode_args != NULL)) Loading Loading @@ -425,7 +652,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r return rpc_system_err; while (status == 0 && nops != hdr_arg.nops) { status = process_op(rqstp, &xdr_in, argp, &xdr_out, resp); status = process_op(hdr_arg.minorversion, nops, rqstp, &xdr_in, argp, &xdr_out, resp); nops++; } Loading @@ -452,7 +680,15 @@ static struct callback_op callback_ops[] = { .process_op = (callback_process_op_t)nfs4_callback_recall, .decode_args = (callback_decode_arg_t)decode_recall_args, .res_maxsize = CB_OP_RECALL_RES_MAXSZ, } }, #if defined(CONFIG_NFS_V4_1) [OP_CB_SEQUENCE] = { .process_op = (callback_process_op_t)nfs4_callback_sequence, .decode_args = (callback_decode_arg_t)decode_cb_sequence_args, .encode_res = (callback_encode_res_t)encode_cb_sequence_res, .res_maxsize = CB_OP_SEQUENCE_RES_MAXSZ, }, #endif /* CONFIG_NFS_V4_1 */ }; /* Loading Loading
fs/nfs/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -74,6 +74,15 @@ config NFS_V4 If unsure, say N. config NFS_V4_1 bool "NFS client support for NFSv4.1 (DEVELOPER ONLY)" depends on NFS_V4 && EXPERIMENTAL help This option enables support for minor version 1 of the NFSv4 protocol (draft-ietf-nfsv4-minorversion1) in the kernel's NFS client. Unless you're an NFS developer, say N. config ROOT_NFS bool "Root file system on NFS" depends on NFS_FS=y && IP_PNP Loading
fs/nfs/callback.c +179 −35 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ #include <linux/freezer.h> #include <linux/kthread.h> #include <linux/sunrpc/svcauth_gss.h> #if defined(CONFIG_NFS_V4_1) #include <linux/sunrpc/bc_xprt.h> #endif #include <net/inet_sock.h> Loading @@ -28,11 +31,12 @@ struct nfs_callback_data { unsigned int users; struct svc_serv *serv; struct svc_rqst *rqst; struct task_struct *task; }; static struct nfs_callback_data nfs_callback_info; static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1]; static DEFINE_MUTEX(nfs_callback_mutex); static struct svc_program nfs4_callback_program; Loading @@ -56,10 +60,10 @@ module_param_call(callback_tcpport, param_set_port, param_get_int, &nfs_callback_set_tcpport, 0644); /* * This is the callback kernel thread. * This is the NFSv4 callback kernel thread. */ static int nfs_callback_svc(void *vrqstp) nfs4_callback_svc(void *vrqstp) { int err, preverr = 0; struct svc_rqst *rqstp = vrqstp; Loading Loading @@ -97,20 +101,12 @@ nfs_callback_svc(void *vrqstp) } /* * Bring up the callback thread if it is not already up. * Prepare to bring up the NFSv4 callback service */ int nfs_callback_up(void) struct svc_rqst * nfs4_callback_up(struct svc_serv *serv) { struct svc_serv *serv = NULL; int ret = 0; mutex_lock(&nfs_callback_mutex); if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) goto out; serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); ret = -ENOMEM; if (!serv) goto out_err; int ret; ret = svc_create_xprt(serv, "tcp", PF_INET, nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); Loading @@ -131,23 +127,168 @@ int nfs_callback_up(void) goto out_err; #endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]); if (IS_ERR(nfs_callback_info.rqst)) { ret = PTR_ERR(nfs_callback_info.rqst); nfs_callback_info.rqst = NULL; return svc_prepare_thread(serv, &serv->sv_pools[0]); out_err: if (ret == 0) ret = -ENOMEM; return ERR_PTR(ret); } #if defined(CONFIG_NFS_V4_1) /* * The callback service for NFSv4.1 callbacks */ static int nfs41_callback_svc(void *vrqstp) { struct svc_rqst *rqstp = vrqstp; struct svc_serv *serv = rqstp->rq_server; struct rpc_rqst *req; int error; DEFINE_WAIT(wq); set_freezable(); /* * FIXME: do we really need to run this under the BKL? If so, please * add a comment about what it's intended to protect. */ lock_kernel(); while (!kthread_should_stop()) { prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE); spin_lock_bh(&serv->sv_cb_lock); if (!list_empty(&serv->sv_cb_list)) { req = list_first_entry(&serv->sv_cb_list, struct rpc_rqst, rq_bc_list); list_del(&req->rq_bc_list); spin_unlock_bh(&serv->sv_cb_lock); dprintk("Invoking bc_svc_process()\n"); error = bc_svc_process(serv, req, rqstp); dprintk("bc_svc_process() returned w/ error code= %d\n", error); } else { spin_unlock_bh(&serv->sv_cb_lock); schedule(); } finish_wait(&serv->sv_cb_waitq, &wq); } unlock_kernel(); return 0; } /* * Bring up the NFSv4.1 callback service */ struct svc_rqst * nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) { struct svc_xprt *bc_xprt; struct svc_rqst *rqstp = ERR_PTR(-ENOMEM); dprintk("--> %s\n", __func__); /* Create a svc_sock for the service */ bc_xprt = svc_sock_create(serv, xprt->prot); if (!bc_xprt) goto out; /* * Save the svc_serv in the transport so that it can * be referenced when the session backchannel is initialized */ serv->bc_xprt = bc_xprt; xprt->bc_serv = serv; INIT_LIST_HEAD(&serv->sv_cb_list); spin_lock_init(&serv->sv_cb_lock); init_waitqueue_head(&serv->sv_cb_waitq); rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); if (IS_ERR(rqstp)) svc_sock_destroy(bc_xprt); out: dprintk("--> %s return %p\n", __func__, rqstp); return rqstp; } static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, struct svc_serv *serv, struct rpc_xprt *xprt, struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) { if (minorversion) { *rqstpp = nfs41_callback_up(serv, xprt); *callback_svc = nfs41_callback_svc; } return minorversion; } static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, struct nfs_callback_data *cb_info) { if (minorversion) xprt->bc_serv = cb_info->serv; } #else static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, struct svc_serv *serv, struct rpc_xprt *xprt, struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) { return 0; } static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, struct nfs_callback_data *cb_info) { } #endif /* CONFIG_NFS_V4_1 */ /* * Bring up the callback thread if it is not already up. */ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) { struct svc_serv *serv = NULL; struct svc_rqst *rqstp; int (*callback_svc)(void *vrqstp); struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; char svc_name[12]; int ret = 0; int minorversion_setup; mutex_lock(&nfs_callback_mutex); if (cb_info->users++ || cb_info->task != NULL) { nfs_callback_bc_serv(minorversion, xprt, cb_info); goto out; } serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); if (!serv) { ret = -ENOMEM; goto out_err; } minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion, serv, xprt, &rqstp, &callback_svc); if (!minorversion_setup) { /* v4.0 callback setup */ rqstp = nfs4_callback_up(serv); callback_svc = nfs4_callback_svc; } if (IS_ERR(rqstp)) { ret = PTR_ERR(rqstp); goto out_err; } svc_sock_update_bufs(serv); nfs_callback_info.task = kthread_run(nfs_callback_svc, nfs_callback_info.rqst, "nfsv4-svc"); if (IS_ERR(nfs_callback_info.task)) { ret = PTR_ERR(nfs_callback_info.task); svc_exit_thread(nfs_callback_info.rqst); nfs_callback_info.rqst = NULL; nfs_callback_info.task = NULL; sprintf(svc_name, "nfsv4.%u-svc", minorversion); cb_info->serv = serv; cb_info->rqst = rqstp; cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name); if (IS_ERR(cb_info->task)) { ret = PTR_ERR(cb_info->task); svc_exit_thread(cb_info->rqst); cb_info->rqst = NULL; cb_info->task = NULL; goto out_err; } out: Loading @@ -164,22 +305,25 @@ int nfs_callback_up(void) out_err: dprintk("NFS: Couldn't create callback socket or server thread; " "err = %d\n", ret); nfs_callback_info.users--; cb_info->users--; goto out; } /* * Kill the callback thread if it's no longer being used. */ void nfs_callback_down(void) void nfs_callback_down(int minorversion) { struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; mutex_lock(&nfs_callback_mutex); nfs_callback_info.users--; if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) { kthread_stop(nfs_callback_info.task); svc_exit_thread(nfs_callback_info.rqst); nfs_callback_info.rqst = NULL; nfs_callback_info.task = NULL; cb_info->users--; if (cb_info->users == 0 && cb_info->task != NULL) { kthread_stop(cb_info->task); svc_exit_thread(cb_info->rqst); cb_info->serv = NULL; cb_info->rqst = NULL; cb_info->task = NULL; } mutex_unlock(&nfs_callback_mutex); } Loading
fs/nfs/callback.h +61 −7 Original line number Diff line number Diff line Loading @@ -20,13 +20,24 @@ enum nfs4_callback_procnum { enum nfs4_callback_opnum { OP_CB_GETATTR = 3, OP_CB_RECALL = 4, /* Callback operations new to NFSv4.1 */ OP_CB_LAYOUTRECALL = 5, OP_CB_NOTIFY = 6, OP_CB_PUSH_DELEG = 7, OP_CB_RECALL_ANY = 8, OP_CB_RECALLABLE_OBJ_AVAIL = 9, OP_CB_RECALL_SLOT = 10, OP_CB_SEQUENCE = 11, OP_CB_WANTS_CANCELLED = 12, OP_CB_NOTIFY_LOCK = 13, OP_CB_NOTIFY_DEVICEID = 14, OP_CB_ILLEGAL = 10044, }; struct cb_compound_hdr_arg { unsigned int taglen; const char *tag; unsigned int callback_ident; unsigned int minorversion; unsigned nops; }; Loading Loading @@ -59,16 +70,59 @@ struct cb_recallargs { uint32_t truncate; }; #if defined(CONFIG_NFS_V4_1) struct referring_call { uint32_t rc_sequenceid; uint32_t rc_slotid; }; struct referring_call_list { struct nfs4_sessionid rcl_sessionid; uint32_t rcl_nrefcalls; struct referring_call *rcl_refcalls; }; struct cb_sequenceargs { struct sockaddr *csa_addr; struct nfs4_sessionid csa_sessionid; uint32_t csa_sequenceid; uint32_t csa_slotid; uint32_t csa_highestslotid; uint32_t csa_cachethis; uint32_t csa_nrclists; struct referring_call_list *csa_rclists; }; struct cb_sequenceres { __be32 csr_status; struct nfs4_sessionid csr_sessionid; uint32_t csr_sequenceid; uint32_t csr_slotid; uint32_t csr_highestslotid; uint32_t csr_target_highestslotid; }; extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, struct cb_sequenceres *res); #endif /* CONFIG_NFS_V4_1 */ extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res); extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy); #ifdef CONFIG_NFS_V4 extern int nfs_callback_up(void); extern void nfs_callback_down(void); #else #define nfs_callback_up() (0) #define nfs_callback_down() do {} while(0) #endif extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt); extern void nfs_callback_down(int minorversion); #endif /* CONFIG_NFS_V4 */ /* * nfs41: Callbacks are expected to not cause substantial latency, * so we limit their concurrency to 1 by setting up the maximum number * of slots for the backchannel. */ #define NFS41_BC_MIN_CALLBACKS 1 #define NFS41_BC_MAX_CALLBACKS 1 extern unsigned int nfs_callback_set_tcpport; extern unsigned short nfs_callback_tcpport; Loading
fs/nfs/callback_proc.c +127 −0 Original line number Diff line number Diff line Loading @@ -101,3 +101,130 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) dprintk("%s: exit with status = %d\n", __func__, ntohl(res)); return res; } #if defined(CONFIG_NFS_V4_1) /* * Validate the sequenceID sent by the server. * Return success if the sequenceID is one more than what we last saw on * this slot, accounting for wraparound. Increments the slot's sequence. * * We don't yet implement a duplicate request cache, so at this time * we will log replays, and process them as if we had not seen them before, * but we don't bump the sequence in the slot. Not too worried about it, * since we only currently implement idempotent callbacks anyway. * * We have a single slot backchannel at this time, so we don't bother * checking the used_slots bit array on the table. The lower layer guarantees * a single outstanding callback request at a time. */ static int validate_seqid(struct nfs4_slot_table *tbl, u32 slotid, u32 seqid) { struct nfs4_slot *slot; dprintk("%s enter. slotid %d seqid %d\n", __func__, slotid, seqid); if (slotid > NFS41_BC_MAX_CALLBACKS) return htonl(NFS4ERR_BADSLOT); slot = tbl->slots + slotid; dprintk("%s slot table seqid: %d\n", __func__, slot->seq_nr); /* Normal */ if (likely(seqid == slot->seq_nr + 1)) { slot->seq_nr++; return htonl(NFS4_OK); } /* Replay */ if (seqid == slot->seq_nr) { dprintk("%s seqid %d is a replay - no DRC available\n", __func__, seqid); return htonl(NFS4_OK); } /* Wraparound */ if (seqid == 1 && (slot->seq_nr + 1) == 0) { slot->seq_nr = 1; return htonl(NFS4_OK); } /* Misordered request */ return htonl(NFS4ERR_SEQ_MISORDERED); } /* * Returns a pointer to a held 'struct nfs_client' that matches the server's * address, major version number, and session ID. It is the caller's * responsibility to release the returned reference. * * Returns NULL if there are no connections with sessions, or if no session * matches the one of interest. */ static struct nfs_client *find_client_with_session( const struct sockaddr *addr, u32 nfsversion, struct nfs4_sessionid *sessionid) { struct nfs_client *clp; clp = nfs_find_client(addr, 4); if (clp == NULL) return NULL; do { struct nfs_client *prev = clp; if (clp->cl_session != NULL) { if (memcmp(clp->cl_session->sess_id.data, sessionid->data, NFS4_MAX_SESSIONID_LEN) == 0) { /* Returns a held reference to clp */ return clp; } } clp = nfs_find_client_next(prev); nfs_put_client(prev); } while (clp != NULL); return NULL; } /* FIXME: referring calls should be processed */ unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, struct cb_sequenceres *res) { struct nfs_client *clp; int i, status; for (i = 0; i < args->csa_nrclists; i++) kfree(args->csa_rclists[i].rcl_refcalls); kfree(args->csa_rclists); status = htonl(NFS4ERR_BADSESSION); clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid); if (clp == NULL) goto out; status = validate_seqid(&clp->cl_session->bc_slot_table, args->csa_slotid, args->csa_sequenceid); if (status) goto out_putclient; memcpy(&res->csr_sessionid, &args->csa_sessionid, sizeof(res->csr_sessionid)); res->csr_sequenceid = args->csa_sequenceid; res->csr_slotid = args->csa_slotid; res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; out_putclient: nfs_put_client(clp); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); res->csr_status = status; return res->csr_status; } #endif /* CONFIG_NFS_V4_1 */
fs/nfs/callback_xdr.c +258 −22 Original line number Diff line number Diff line Loading @@ -20,6 +20,11 @@ 2 + 2 + 3 + 3) #define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) #if defined(CONFIG_NFS_V4_1) #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ 4 + 1 + 3) #endif /* CONFIG_NFS_V4_1 */ #define NFSDBG_FACILITY NFSDBG_CALLBACK typedef __be32 (*callback_process_op_t)(void *, void *); Loading Loading @@ -132,7 +137,6 @@ static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid) static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound_hdr_arg *hdr) { __be32 *p; unsigned int minor_version; __be32 status; status = decode_string(xdr, &hdr->taglen, &hdr->tag); Loading @@ -147,15 +151,19 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound p = read_buf(xdr, 12); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); minor_version = ntohl(*p++); /* Check minor version is zero. */ if (minor_version != 0) { printk(KERN_WARNING "%s: NFSv4 server callback with illegal minor version %u!\n", __func__, minor_version); hdr->minorversion = ntohl(*p++); /* Check minor version is zero or one. */ if (hdr->minorversion <= 1) { p++; /* skip callback_ident */ } else { printk(KERN_WARNING "%s: NFSv4 server callback with " "illegal minor version %u!\n", __func__, hdr->minorversion); return htonl(NFS4ERR_MINOR_VERS_MISMATCH); } hdr->callback_ident = ntohl(*p++); hdr->nops = ntohl(*p); dprintk("%s: minorversion %d nops %d\n", __func__, hdr->minorversion, hdr->nops); return 0; } Loading Loading @@ -204,6 +212,122 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, return status; } #if defined(CONFIG_NFS_V4_1) static unsigned decode_sessionid(struct xdr_stream *xdr, struct nfs4_sessionid *sid) { uint32_t *p; int len = NFS4_MAX_SESSIONID_LEN; p = read_buf(xdr, len); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE);; memcpy(sid->data, p, len); return 0; } static unsigned decode_rc_list(struct xdr_stream *xdr, struct referring_call_list *rc_list) { uint32_t *p; int i; unsigned status; status = decode_sessionid(xdr, &rc_list->rcl_sessionid); if (status) goto out; status = htonl(NFS4ERR_RESOURCE); p = read_buf(xdr, sizeof(uint32_t)); if (unlikely(p == NULL)) goto out; rc_list->rcl_nrefcalls = ntohl(*p++); if (rc_list->rcl_nrefcalls) { p = read_buf(xdr, rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t)); if (unlikely(p == NULL)) goto out; rc_list->rcl_refcalls = kmalloc(rc_list->rcl_nrefcalls * sizeof(*rc_list->rcl_refcalls), GFP_KERNEL); if (unlikely(rc_list->rcl_refcalls == NULL)) goto out; for (i = 0; i < rc_list->rcl_nrefcalls; i++) { rc_list->rcl_refcalls[i].rc_sequenceid = ntohl(*p++); rc_list->rcl_refcalls[i].rc_slotid = ntohl(*p++); } } status = 0; out: return status; } static unsigned decode_cb_sequence_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_sequenceargs *args) { uint32_t *p; int i; unsigned status; status = decode_sessionid(xdr, &args->csa_sessionid); if (status) goto out; status = htonl(NFS4ERR_RESOURCE); p = read_buf(xdr, 5 * sizeof(uint32_t)); if (unlikely(p == NULL)) goto out; args->csa_addr = svc_addr(rqstp); args->csa_sequenceid = ntohl(*p++); args->csa_slotid = ntohl(*p++); args->csa_highestslotid = ntohl(*p++); args->csa_cachethis = ntohl(*p++); args->csa_nrclists = ntohl(*p++); args->csa_rclists = NULL; if (args->csa_nrclists) { args->csa_rclists = kmalloc(args->csa_nrclists * sizeof(*args->csa_rclists), GFP_KERNEL); if (unlikely(args->csa_rclists == NULL)) goto out; for (i = 0; i < args->csa_nrclists; i++) { status = decode_rc_list(xdr, &args->csa_rclists[i]); if (status) goto out_free; } } status = 0; dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u slotid %u " "highestslotid %u cachethis %d nrclists %u\n", __func__, ((u32 *)&args->csa_sessionid)[0], ((u32 *)&args->csa_sessionid)[1], ((u32 *)&args->csa_sessionid)[2], ((u32 *)&args->csa_sessionid)[3], args->csa_sequenceid, args->csa_slotid, args->csa_highestslotid, args->csa_cachethis, args->csa_nrclists); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); return status; out_free: for (i = 0; i < args->csa_nrclists; i++) kfree(args->csa_rclists[i].rcl_refcalls); kfree(args->csa_rclists); goto out; } #endif /* CONFIG_NFS_V4_1 */ static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str) { __be32 *p; Loading Loading @@ -353,31 +477,134 @@ static __be32 encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, return status; } static __be32 process_op(struct svc_rqst *rqstp, #if defined(CONFIG_NFS_V4_1) static unsigned encode_sessionid(struct xdr_stream *xdr, const struct nfs4_sessionid *sid) { uint32_t *p; int len = NFS4_MAX_SESSIONID_LEN; p = xdr_reserve_space(xdr, len); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); memcpy(p, sid, len); return 0; } static unsigned encode_cb_sequence_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, const struct cb_sequenceres *res) { uint32_t *p; unsigned status = res->csr_status; if (unlikely(status != 0)) goto out; encode_sessionid(xdr, &res->csr_sessionid); p = xdr_reserve_space(xdr, 4 * sizeof(uint32_t)); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); *p++ = htonl(res->csr_sequenceid); *p++ = htonl(res->csr_slotid); *p++ = htonl(res->csr_highestslotid); *p++ = htonl(res->csr_target_highestslotid); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); return status; } static __be32 preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) { if (op_nr == OP_CB_SEQUENCE) { if (nop != 0) return htonl(NFS4ERR_SEQUENCE_POS); } else { if (nop == 0) return htonl(NFS4ERR_OP_NOT_IN_SESSION); } switch (op_nr) { case OP_CB_GETATTR: case OP_CB_RECALL: case OP_CB_SEQUENCE: *op = &callback_ops[op_nr]; break; case OP_CB_LAYOUTRECALL: case OP_CB_NOTIFY_DEVICEID: case OP_CB_NOTIFY: case OP_CB_PUSH_DELEG: case OP_CB_RECALL_ANY: case OP_CB_RECALLABLE_OBJ_AVAIL: case OP_CB_RECALL_SLOT: case OP_CB_WANTS_CANCELLED: case OP_CB_NOTIFY_LOCK: return htonl(NFS4ERR_NOTSUPP); default: return htonl(NFS4ERR_OP_ILLEGAL); } return htonl(NFS_OK); } #else /* CONFIG_NFS_V4_1 */ static __be32 preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) { return htonl(NFS4ERR_MINOR_VERS_MISMATCH); } #endif /* CONFIG_NFS_V4_1 */ static __be32 preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op) { switch (op_nr) { case OP_CB_GETATTR: case OP_CB_RECALL: *op = &callback_ops[op_nr]; break; default: return htonl(NFS4ERR_OP_ILLEGAL); } return htonl(NFS_OK); } static __be32 process_op(uint32_t minorversion, int nop, struct svc_rqst *rqstp, struct xdr_stream *xdr_in, void *argp, struct xdr_stream *xdr_out, void *resp) { struct callback_op *op = &callback_ops[0]; unsigned int op_nr = OP_CB_ILLEGAL; __be32 status = 0; __be32 status; long maxlen; __be32 res; dprintk("%s: start\n", __func__); status = decode_op_hdr(xdr_in, &op_nr); if (likely(status == 0)) { switch (op_nr) { case OP_CB_GETATTR: case OP_CB_RECALL: op = &callback_ops[op_nr]; break; default: op_nr = OP_CB_ILLEGAL; op = &callback_ops[0]; if (unlikely(status)) { status = htonl(NFS4ERR_OP_ILLEGAL); goto out; } } dprintk("%s: minorversion=%d nop=%d op_nr=%u\n", __func__, minorversion, nop, op_nr); status = minorversion ? preprocess_nfs41_op(nop, op_nr, &op) : preprocess_nfs4_op(op_nr, &op); if (status == htonl(NFS4ERR_OP_ILLEGAL)) op_nr = OP_CB_ILLEGAL; out: maxlen = xdr_out->end - xdr_out->p; if (maxlen > 0 && maxlen < PAGE_SIZE) { if (likely(status == 0 && op->decode_args != NULL)) Loading Loading @@ -425,7 +652,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r return rpc_system_err; while (status == 0 && nops != hdr_arg.nops) { status = process_op(rqstp, &xdr_in, argp, &xdr_out, resp); status = process_op(hdr_arg.minorversion, nops, rqstp, &xdr_in, argp, &xdr_out, resp); nops++; } Loading @@ -452,7 +680,15 @@ static struct callback_op callback_ops[] = { .process_op = (callback_process_op_t)nfs4_callback_recall, .decode_args = (callback_decode_arg_t)decode_recall_args, .res_maxsize = CB_OP_RECALL_RES_MAXSZ, } }, #if defined(CONFIG_NFS_V4_1) [OP_CB_SEQUENCE] = { .process_op = (callback_process_op_t)nfs4_callback_sequence, .decode_args = (callback_decode_arg_t)decode_cb_sequence_args, .encode_res = (callback_encode_res_t)encode_cb_sequence_res, .res_maxsize = CB_OP_SEQUENCE_RES_MAXSZ, }, #endif /* CONFIG_NFS_V4_1 */ }; /* Loading