/* * Copyright (c) 2001 The Regents of the University of Michigan. * All rights reserved. * * Kendrick Smith * Andy Adamson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "nfsd.h" #include "state.h" #include "netns.h" #include "xdr4cb.h" #define NFSDDBG_FACILITY NFSDDBG_PROC static void nfsd4_mark_cb_fault(struct nfs4_client *, int reason); #define NFSPROC4_CB_NULL 0 #define NFSPROC4_CB_COMPOUND 1 /* Index of predefined Linux callback client operations */ struct nfs4_cb_compound_hdr { /* args */ u32 ident; /* minorversion 0 only */ u32 nops; __be32 *nops_p; u32 minorversion; /* res */ int status; }; /* * Handle decode buffer overflows out-of-line. */ static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) { dprintk("NFS: %s prematurely hit the end of our receive buffer. " "Remaining buffer length is %tu words.\n", func, xdr->end - xdr->p); } static __be32 *xdr_encode_empty_array(__be32 *p) { *p++ = xdr_zero; return p; } /* * Encode/decode NFSv4 CB basic data types * * Basic NFSv4 callback data types are defined in section 15 of RFC * 3530: "Network File System (NFS) version 4 Protocol" and section * 20 of RFC 5661: "Network File System (NFS) Version 4 Minor Version * 1 Protocol" */ /* * nfs_cb_opnum4 * * enum nfs_cb_opnum4 { * OP_CB_GETATTR = 3, * ... * }; */ enum nfs_cb_opnum4 { OP_CB_GETATTR = 3, OP_CB_RECALL = 4, 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 }; static void encode_nfs_cb_opnum4(struct xdr_stream *xdr, enum nfs_cb_opnum4 op) { __be32 *p; p = xdr_reserve_space(xdr, 4); *p = cpu_to_be32(op); } /* * nfs_fh4 * * typedef opaque nfs_fh4; */ static void encode_nfs_fh4(struct xdr_stream *xdr, const struct knfsd_fh *fh) { u32 length = fh->fh_size; __be32 *p; BUG_ON(length > NFS4_FHSIZE); p = xdr_reserve_space(xdr, 4 + length); xdr_encode_opaque(p, &fh->fh_base, length); } /* * stateid4 * * struct stateid4 { * uint32_t seqid; * opaque other[12]; * }; */ static void encode_stateid4(struct xdr_stream *xdr, const stateid_t *sid) { __be32 *p; p = xdr_reserve_space(xdr, NFS4_STATEID_SIZE); *p++ = cpu_to_be32(sid->si_generation); xdr_encode_opaque_fixed(p, &sid->si_opaque, NFS4_STATEID_OTHER_SIZE); } /* * sessionid4 * * typedef opaque sessionid4[NFS4_SESSIONID_SIZE]; */ static void encode_sessionid4(struct xdr_stream *xdr, const struct nfsd4_session *session) { __be32 *p; p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN); xdr_encode_opaque_fixed(p, session->se_sessionid.data, NFS4_MAX_SESSIONID_LEN); } /* * nfsstat4 */ static const struct { int stat; int errno; } nfs_cb_errtbl[] = { { NFS4_OK, 0 }, { NFS4ERR_PERM, -EPERM }, { NFS4ERR_NOENT, -ENOENT }, { NFS4ERR_IO, -EIO }, { NFS4ERR_NXIO, -ENXIO }, { NFS4ERR_ACCESS, -EACCES }, { NFS4ERR_EXIST, -EEXIST }, { NFS4ERR_XDEV, -EXDEV }, { NFS4ERR_NOTDIR, -ENOTDIR }, { NFS4ERR_ISDIR, -EISDIR }, { NFS4ERR_INVAL, -EINVAL }, { NFS4ERR_FBIG, -EFBIG }, { NFS4ERR_NOSPC, -ENOSPC }, { NFS4ERR_ROFS, -EROFS }, { NFS4ERR_MLINK, -EMLINK }, { NFS4ERR_NAMETOOLONG, -ENAMETOOLONG }, { NFS4ERR_NOTEMPTY, -ENOTEMPTY }, { NFS4ERR_DQUOT, -EDQUOT }, { NFS4ERR_ST
##############################################################################
# Copyright (c) 2017 Mirantis Inc., Enea AB and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
---
cluster:
  domain: mcp-pike-ovs-dpdk-noha.local
  states:
    - dpdk
    - openstack_noha
    - neutron_gateway
    - neutron_compute
    - networks
virtual:
  nodes:
    - cfg01
    - ctl01
    - cmp01
    - cmp02
    - gtw01
  ctl01:
    vcpus: 4
    ram: 14336
  gtw01:
    ram: 2048
  cmp01:
    vcpus: 4
    ram: 8192
  cmp02:
    vcpus: 4
    ram: 8192
struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses) { int maxtime = max_cb_time(clp->net); struct rpc_timeout timeparms = { .to_initval = maxtime, .to_retries = 0, .to_maxval = maxtime, }; struct rpc_create_args args = { .net = clp->net, .address = (struct sockaddr *) &conn->cb_addr, .addrsize = conn->cb_addrlen, .saddress = (struct sockaddr *) &conn->cb_saddr, .timeout = &timeparms, .program = &cb_program, .version = 0, .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), }; struct rpc_clnt *client; struct rpc_cred *cred; if (clp->cl_minorversion == 0) { if (!clp->cl_cred.cr_principal && (clp->cl_cred.cr_flavor >= RPC_AUTH_GSS_KRB5)) return -EINVAL; args.client_name = clp->cl_cred.cr_principal; args.prognumber = conn->cb_prog; args.protocol = XPRT_TRANSPORT_TCP; args.authflavor = clp->cl_cred.cr_flavor; clp->cl_cb_ident = conn->cb_ident; } else { if (!conn->cb_xprt) return -EINVAL; clp->cl_cb_conn.cb_xprt = conn->cb_xprt; clp->cl_cb_session = ses; args.bc_xprt = conn->cb_xprt; args.prognumber = clp->cl_cb_session->se_cb_prog; args.protocol = conn->cb_xprt->xpt_class->xcl_ident | XPRT_TRANSPORT_BC; args.authflavor = ses->se_cb_sec.flavor; } /* Create RPC client */ client = create_backchannel_client(&args); if (IS_ERR(client)) { dprintk("NFSD: couldn't create callback client: %ld\n", PTR_ERR(client)); return PTR_ERR(client); } cred = get_backchannel_cred(clp, client, ses); if (IS_ERR(cred)) { rpc_shutdown_client(client); return PTR_ERR(cred); } clp->cl_cb_client = client; clp->cl_cb_cred = cred; return 0; } static void warn_no_callback_path(struct nfs4_client *clp, int reason) { dprintk("NFSD: warning: no callback path to client %.*s: error %d\n", (int)clp->cl_name.len, clp->cl_name.data, reason); } static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason) { clp->cl_cb_state = NFSD4_CB_DOWN; warn_no_callback_path(clp, reason); } static void nfsd4_mark_cb_fault(struct nfs4_client *clp, int reason) { clp->cl_cb_state = NFSD4_CB_FAULT; warn_no_callback_path(clp, reason); } static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata) { struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null); if (task->tk_status) nfsd4_mark_cb_down(clp, task->tk_status); else clp->cl_cb_state = NFSD4_CB_UP; } static const struct rpc_call_ops nfsd4_cb_probe_ops = { /* XXX: release method to ensure we set the cb channel down if * necessary on early failure? */ .rpc_call_done = nfsd4_cb_probe_done, }; static struct workqueue_struct *callback_wq; /* * Poke the callback thread to process any updates to the callback * parameters, and send a null probe. */ void nfsd4_probe_callback(struct nfs4_client *clp) { clp->cl_cb_state = NFSD4_CB_UNKNOWN; set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags); nfsd4_run_cb(&clp->cl_cb_null); } void nfsd4_probe_callback_sync(struct nfs4_client *clp) { nfsd4_probe_callback(clp); flush_workqueue(callback_wq); } void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) { clp->cl_cb_state = NFSD4_CB_UNKNOWN; spin_lock(&clp->cl_lock); memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn)); spin_unlock(&clp->cl_lock); } /* * There's currently a single callback channel slot. * If the slot is available, then mark it busy. Otherwise, set the * thread for sleeping on the callback RPC wait queue. */ static bool nfsd41_cb_get_slot(struct nfs4_client *clp, struct rpc_task *task) { if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) { rpc_sleep_on(&clp->cl_cb_waitq, task, NULL); /* Race breaker */ if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) { dprintk("%s slot is busy\n", __func__); return false; } rpc_wake_up_queued_task(&clp->cl_cb_waitq, task); } return true; } /* * TODO: cb_sequence should support referring call lists, cachethis, multiple * slots, and mark callback channel down on communication errors. */ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) { struct nfsd4_callback *cb = calldata; struct nfs4_client *clp = cb->cb_clp; u32 minorversion = clp->cl_minorversion; cb->cb_minorversion = minorversion; /* * cb_seq_status is only set in decode_cb_sequence4res, * and so will remain 1 if an rpc level failure occurs. */ cb->cb_seq_status = 1; cb->cb_status = 0; if (minorversion) { if (!nfsd41_cb_get_slot(clp, task)) return; } rpc_call_start(task); } static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback *cb) { struct nfs4_client *clp = cb->cb_clp; struct nfsd4_session *session = clp->cl_cb_session; bool ret = true; if (!clp->cl_minorversion) { /* * If the backchannel connection was shut down while this * task was queued, we need to resubmit it after setting up * a new backchannel connection. * * Note that if we lost our callback connection permanently * the submission code will error out, so we don't need to * handle that case here. */ if (task->tk_flags & RPC_TASK_KILLED) goto need_restart; return true; } switch (cb->cb_seq_status) { case 0: /* * No need for lock, access serialized in nfsd4_cb_prepare * * RFC5661 20.9.3 * If CB_SEQUENCE returns an error, then the state of the slot * (sequence ID, cached reply) MUST NOT change. */ ++session->se_cb_seq_nr; break; case -ESERVERFAULT: ++session->se_cb_seq_nr; case 1: case -NFS4ERR_BADSESSION: nfsd4_mark_cb_fault(cb->cb_clp, cb->cb_seq_status); ret = false; break; case -NFS4ERR_DELAY: if (!rpc_restart_call(task)) goto out; rpc_delay(task, 2 * HZ); return false; case -NFS4ERR_BADSLOT: goto retry_nowait; case -NFS4ERR_SEQ_MISORDERED: if (session->se_cb_seq_nr != 1) { session->se_cb_seq_nr = 1; goto retry_nowait; } break; default: dprintk("%s: unprocessed error %d\n", __func__, cb->cb_seq_status); } clear_bit(0, &clp->cl_cb_slot_busy); rpc_wake_up_next(&clp->cl_cb_waitq); dprintk("%s: freed slot, new seqid=%d\n", __func__, clp->cl_cb_session->se_cb_seq_nr); if (task->tk_flags & RPC_TASK_KILLED) goto need_restart; out: return ret; retry_nowait: if (rpc_restart_call_prepare(task)) ret = false; goto out; need_restart: task->tk_status = 0; cb->cb_need_restart = true; return false; } static void nfsd4_cb_done(struct rpc_task *task, void *calldata) { struct nfsd4_callback *cb = calldata; struct nfs4_client *clp = cb->cb_clp; dprintk("%s: minorversion=%d\n", __func__, clp->cl_minorversion); if (!nfsd4_cb_sequence_done(task, cb)) return; if (cb->cb_status) { WARN_ON_ONCE(task->tk_status); task->tk_status = cb->cb_status; } switch (cb->cb_ops->done(cb, task)) { case 0: task->tk_status = 0; rpc_restart_call_prepare(task); return; case 1: break; case -1: /* Network partition? */ nfsd4_mark_cb_down(clp, task->tk_status); break; default: BUG(); } } static void nfsd4_cb_release(void *calldata) { struct nfsd4_callback *cb = calldata; if (cb->cb_need_restart) nfsd4_run_cb(cb); else cb->cb_ops->release(cb); } static const struct rpc_call_ops nfsd4_cb_ops = { .rpc_call_prepare = nfsd4_cb_prepare, .rpc_call_done = nfsd4_cb_done, .rpc_release = nfsd4_cb_release, }; int nfsd4_create_callback_queue(void) { callback_wq = create_singlethread_workqueue("nfsd4_callbacks"); if (!callback_wq) return -ENOMEM; return 0; } void nfsd4_destroy_callback_queue(void) { destroy_workqueue(callback_wq); } /* must be called under the state lock */ void nfsd4_shutdown_callback(struct nfs4_client *clp) { set_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags); /* * Note this won't actually result in a null callback; * instead, nfsd4_run_cb_null() will detect the killed * client, destroy the rpc client, and stop: */ nfsd4_run_cb(&clp->cl_cb_null); flush_workqueue(callback_wq); } /* requires cl_lock: */ static struct nfsd4_conn * __nfsd4_find_backchannel(struct nfs4_client *clp) { struct nfsd4_session *s; struct nfsd4_conn *c; list_for_each_entry(s, &clp->cl_sessions, se_perclnt) { list_for_each_entry(c, &s->se_conns, cn_persession) { if (c->cn_flags & NFS4_CDFC4_BACK) return c; } } return NULL; } static void nfsd4_process_cb_update(struct nfsd4_callback *cb) { struct nfs4_cb_conn conn; struct nfs4_client *clp = cb->cb_clp; struct nfsd4_session *ses = NULL; struct nfsd4_conn *c; int err; /* * This is either an update, or the client dying; in either case, * kill the old client: */ if (clp->cl_cb_client) { rpc_shutdown_client(clp->cl_cb_client); clp->cl_cb_client = NULL; put_rpccred(clp->cl_cb_cred); clp->cl_cb_cred = NULL; } if (clp->cl_cb_conn.cb_xprt) { svc_xprt_put(clp->cl_cb_conn.cb_xprt); clp->cl_cb_conn.cb_xprt = NULL; } if (test_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags)) return; spin_lock(&clp->cl_lock); /* * Only serialized callback code is allowed to clear these * flags; main nfsd code can only set them: */ BUG_ON(!(clp->cl_flags & NFSD4_CLIENT_CB_FLAG_MASK)); clear_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags); memcpy(&conn, &cb->cb_clp->cl_cb_conn, sizeof(struct nfs4_cb_conn)); c = __nfsd4_find_backchannel(clp); if (c) { svc_xprt_get(c->cn_xprt); conn.cb_xprt = c->cn_xprt; ses = c->cn_session; } spin_unlock(&clp->cl_lock); err = setup_callback_client(clp, &conn, ses); if (err) { nfsd4_mark_cb_down(clp, err); return; } } static void nfsd4_run_cb_work(struct work_struct *work) { struct nfsd4_callback *cb = container_of(work, struct nfsd4_callback, cb_work); struct nfs4_client *clp = cb->cb_clp; struct rpc_clnt *clnt; if (cb->cb_need_restart) { cb->cb_need_restart = false; } else { if (cb->cb_ops && cb->cb_ops->prepare) cb->cb_ops->prepare(cb); } if (clp->cl_flags & NFSD4_CLIENT_CB_FLAG_MASK) nfsd4_process_cb_update(cb); clnt = clp->cl_cb_client; if (!clnt) { /* Callback channel broken, or client killed; give up: */ if (cb->cb_ops && cb->cb_ops->release) cb->cb_ops->release(cb); return; } /* * Don't send probe messages for 4.1 or later. */ if (!cb->cb_ops && clp->cl_minorversion) { clp->cl_cb_state = NFSD4_CB_UP; return; } cb->cb_msg.rpc_cred = clp->cl_cb_cred; rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN, cb->cb_ops ? &nfsd4_cb_ops : &nfsd4_cb_probe_ops, cb); } void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp, struct nfsd4_callback_ops *ops, enum nfsd4_cb_op op) { cb->cb_clp = clp; cb->cb_msg.rpc_proc = &nfs4_cb_procedures[op]; cb->cb_msg.rpc_argp = cb; cb->cb_msg.rpc_resp = cb; cb->cb_ops = ops; INIT_WORK(&cb->cb_work, nfsd4_run_cb_work); cb->cb_seq_status = 1; cb->cb_status = 0; cb->cb_need_restart = false; } void nfsd4_run_cb(struct nfsd4_callback *cb) { queue_work(callback_wq, &cb->cb_work); }