summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/klm
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/klm')
-rw-r--r--usr/src/uts/common/klm/nlm_impl.h8
-rw-r--r--usr/src/uts/common/klm/nlm_rpc_handle.c21
-rw-r--r--usr/src/uts/common/klm/nlm_rpc_svc.c80
-rw-r--r--usr/src/uts/common/klm/nlm_service.c120
4 files changed, 162 insertions, 67 deletions
diff --git a/usr/src/uts/common/klm/nlm_impl.h b/usr/src/uts/common/klm/nlm_impl.h
index 68604309a2..9caae1a8c7 100644
--- a/usr/src/uts/common/klm/nlm_impl.h
+++ b/usr/src/uts/common/klm/nlm_impl.h
@@ -28,7 +28,7 @@
*/
/*
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright 2016 Joyent, Inc.
*/
@@ -112,7 +112,7 @@ struct _kthread;
* We pass three callback functions to nlm_do_lock:
* nlm_reply_cb: send a normal RPC reply
* nlm_res_cb: do a _res (message style) RPC (call)
- * nlm_testargs_cb: do a "granted" RPC call (after blocking)
+ * nlm_granted_cb: do a "granted" RPC call (after blocking)
* Only one of the 1st or 2nd is used.
* The 3rd is used only for blocking
*
@@ -123,7 +123,7 @@ struct _kthread;
*/
typedef bool_t (*nlm_reply_cb)(SVCXPRT *, nlm4_res *);
typedef enum clnt_stat (*nlm_res_cb)(nlm4_res *, void *, CLIENT *);
-typedef enum clnt_stat (*nlm_testargs_cb)(nlm4_testargs *, void *, CLIENT *);
+typedef enum clnt_stat (*nlm_granted_cb)(nlm4_testargs *, nlm4_res *, CLIENT *);
typedef enum clnt_stat (*nlm_testres_cb)(nlm4_testres *, void *, CLIENT *);
/*
@@ -624,7 +624,7 @@ void nlm_do_notify2(nlm_sm_status *, void *, struct svc_req *);
void nlm_do_test(nlm4_testargs *, nlm4_testres *,
struct svc_req *, nlm_testres_cb);
void nlm_do_lock(nlm4_lockargs *, nlm4_res *, struct svc_req *,
- nlm_reply_cb, nlm_res_cb, nlm_testargs_cb);
+ nlm_reply_cb, nlm_res_cb, nlm_granted_cb);
void nlm_do_cancel(nlm4_cancargs *, nlm4_res *,
struct svc_req *, nlm_res_cb);
void nlm_do_unlock(nlm4_unlockargs *, nlm4_res *,
diff --git a/usr/src/uts/common/klm/nlm_rpc_handle.c b/usr/src/uts/common/klm/nlm_rpc_handle.c
index 9ddf56856c..b022acc380 100644
--- a/usr/src/uts/common/klm/nlm_rpc_handle.c
+++ b/usr/src/uts/common/klm/nlm_rpc_handle.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
@@ -130,6 +130,7 @@ update_host_rpcbinding(struct nlm_host *hostp, int vers)
static int
refresh_nlm_rpc(struct nlm_host *hostp, nlm_rpc_t *rpcp)
{
+ uint32_t zero = 0;
int ret;
if (rpcp->nr_handle == NULL) {
@@ -175,6 +176,12 @@ refresh_nlm_rpc(struct nlm_host *hostp, nlm_rpc_t *rpcp)
if (NLM_STALE_CLNT(stat)) {
ret = ESTALE;
}
+ /*
+ * Need to reset the XID after the null call above,
+ * otherwise we'll reuse the XID from that call.
+ */
+ (void) CLNT_CONTROL(rpcp->nr_handle, CLSET_XID,
+ (char *)&zero);
}
}
@@ -209,7 +216,8 @@ again:
rc = cv_wait_sig(&hostp->nh_rpcb_cv, &hostp->nh_lock);
if (rc == 0) {
mutex_exit(&hostp->nh_lock);
- return (EINTR);
+ rc = EINTR;
+ goto errout;
}
}
@@ -229,7 +237,8 @@ again:
*/
if (hostp->nh_rpcb_ustat != RPC_SUCCESS) {
mutex_exit(&hostp->nh_lock);
- return (ENOENT);
+ rc = ENOENT;
+ goto errout;
}
}
@@ -263,7 +272,7 @@ again:
}
destroy_rpch(rpcp);
- return (rc);
+ goto errout;
}
DTRACE_PROBE2(end, struct nlm_host *, hostp,
@@ -271,6 +280,10 @@ again:
*rpcpp = rpcp;
return (0);
+
+errout:
+ NLM_ERR("Can't get RPC client handle for: %s", hostp->nh_name);
+ return (rc);
}
void
diff --git a/usr/src/uts/common/klm/nlm_rpc_svc.c b/usr/src/uts/common/klm/nlm_rpc_svc.c
index 2911b31877..1f04e3f036 100644
--- a/usr/src/uts/common/klm/nlm_rpc_svc.c
+++ b/usr/src/uts/common/klm/nlm_rpc_svc.c
@@ -26,7 +26,7 @@
*/
/*
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
@@ -63,7 +63,7 @@
* 32-bit lock ranges.
*/
static void
-nlm_convert_to_nlm_lock(struct nlm_lock *dst, struct nlm4_lock *src)
+nlm_convert_to_nlm_lock(struct nlm_lock *dst, const struct nlm4_lock *src)
{
dst->caller_name = src->caller_name;
dst->fh = src->fh;
@@ -76,12 +76,22 @@ nlm_convert_to_nlm_lock(struct nlm_lock *dst, struct nlm4_lock *src)
}
/*
+ * Up-convert for v1 granted response
+ */
+static void
+nlm_convert_to_nlm4_res(struct nlm4_res *dst, const struct nlm_res *src)
+{
+ dst->cookie = src->cookie;
+ dst->stat.stat = (nlm4_stats) src->stat.stat;
+}
+
+/*
* Up-convert for v1 svc functions with a 32-bit lock range arg.
* Note that lock range checks (like overflow) are done later,
* in nlm_init_flock().
*/
static void
-nlm_convert_to_nlm4_lock(struct nlm4_lock *dst, struct nlm_lock *src)
+nlm_convert_to_nlm4_lock(struct nlm4_lock *dst, const struct nlm_lock *src)
{
dst->caller_name = src->caller_name;
@@ -93,7 +103,7 @@ nlm_convert_to_nlm4_lock(struct nlm4_lock *dst, struct nlm_lock *src)
}
static void
-nlm_convert_to_nlm4_share(struct nlm4_share *dst, struct nlm_share *src)
+nlm_convert_to_nlm4_share(struct nlm4_share *dst, const struct nlm_share *src)
{
dst->caller_name = src->caller_name;
@@ -113,7 +123,7 @@ nlm_convert_to_nlm4_share(struct nlm4_share *dst, struct nlm_share *src)
* valid 32-bit lock range.
*/
static void
-nlm_convert_to_nlm_holder(struct nlm_holder *dst, struct nlm4_holder *src)
+nlm_convert_to_nlm_holder(struct nlm_holder *dst, const struct nlm4_holder *src)
{
dst->exclusive = src->exclusive;
dst->svid = src->svid;
@@ -133,7 +143,7 @@ nlm_convert_to_nlm_stats(enum nlm4_stats src)
}
static void
-nlm_convert_to_nlm_res(struct nlm_res *dst, struct nlm4_res *src)
+nlm_convert_to_nlm_res(struct nlm_res *dst, const struct nlm4_res *src)
{
dst->cookie = src->cookie;
dst->stat.stat = nlm_convert_to_nlm_stats(src->stat.stat);
@@ -175,7 +185,7 @@ nlm_test_1_svc(struct nlm_testargs *argp, nlm_testres *resp,
* Callback functions for nlm_lock_1_svc
*/
static bool_t nlm_lock_1_reply(SVCXPRT *, nlm4_res *);
-static enum clnt_stat nlm_granted_1_cb(nlm4_testargs *, void *, CLIENT *);
+static enum clnt_stat nlm_granted_1_cb(nlm4_testargs *, nlm4_res *, CLIENT *);
bool_t
nlm_lock_1_svc(nlm_lockargs *argp, nlm_res *resp,
@@ -215,7 +225,7 @@ nlm_lock_1_reply(SVCXPRT *transp, nlm4_res *resp)
}
static enum clnt_stat
-nlm_granted_1_cb(nlm4_testargs *argp, void *resp, CLIENT *clnt)
+nlm_granted_1_cb(nlm4_testargs *argp, nlm4_res *resp, CLIENT *clnt)
{
nlm_testargs args1;
nlm_res res1;
@@ -229,9 +239,7 @@ nlm_granted_1_cb(nlm4_testargs *argp, void *resp, CLIENT *clnt)
rv = nlm_granted_1(&args1, &res1, clnt);
- /* NB: We have a result our caller will not free. */
- xdr_free((xdrproc_t)xdr_nlm_res, (void *)&res1);
- (void) resp;
+ nlm_convert_to_nlm4_res(resp, &res1);
return (rv);
}
@@ -355,7 +363,8 @@ nlm_test_res_1_cb(nlm4_testres *res4, void *null, CLIENT *clnt)
* Callback functions for nlm_lock_msg_1_svc
*/
static enum clnt_stat nlm_lock_res_1_cb(nlm4_res *, void *, CLIENT *);
-static enum clnt_stat nlm_granted_msg_1_cb(nlm4_testargs *, void *, CLIENT *);
+static enum clnt_stat nlm_granted_msg_1_cb(nlm4_testargs *, nlm4_res *,
+ CLIENT *);
bool_t
nlm_lock_msg_1_svc(nlm_lockargs *argp, void *resp,
@@ -396,16 +405,22 @@ nlm_lock_res_1_cb(nlm4_res *resp, void *null, CLIENT *clnt)
}
static enum clnt_stat
-nlm_granted_msg_1_cb(nlm4_testargs *argp, void *null, CLIENT *clnt)
+nlm_granted_msg_1_cb(nlm4_testargs *argp, nlm4_res *resp, CLIENT *clnt)
{
nlm_testargs args1;
+ int rv;
args1.cookie = argp->cookie;
args1.exclusive = argp->exclusive;
nlm_convert_to_nlm_lock(&args1.alock, &argp->alock);
- return (nlm_granted_msg_1(&args1, null, clnt));
+ rv = nlm_granted_msg_1(&args1, NULL, clnt);
+
+ /* MSG call doesn't fill in *resp, so do it here. */
+ if (rv != RPC_SUCCESS)
+ resp->stat.stat = nlm4_failed;
+ return (rv);
}
@@ -693,7 +708,6 @@ nlm4_test_4_svc(nlm4_testargs *argp, nlm4_testres *resp, struct svc_req *sr)
* Callback functions for nlm4_lock_4_svc
*/
static bool_t nlm4_lock_4_reply(SVCXPRT *, nlm4_res *);
-static enum clnt_stat nlm4_granted_4_cb(nlm4_testargs *, void *, CLIENT *);
bool_t
nlm4_lock_4_svc(nlm4_lockargs *argp, nlm4_res *resp,
@@ -703,7 +717,7 @@ nlm4_lock_4_svc(nlm4_lockargs *argp, nlm4_res *resp,
/* NLM4_LOCK */
nlm_do_lock(argp, resp, sr,
nlm4_lock_4_reply, NULL,
- nlm4_granted_4_cb);
+ nlm4_granted_4);
/* above does its own reply */
return (FALSE);
@@ -715,22 +729,6 @@ nlm4_lock_4_reply(SVCXPRT *transp, nlm4_res *resp)
return (svc_sendreply(transp, xdr_nlm4_res, (char *)resp));
}
-static enum clnt_stat
-nlm4_granted_4_cb(nlm4_testargs *argp, void *resp, CLIENT *clnt)
-{
- nlm4_res res4;
- int rv;
-
- bzero(&res4, sizeof (res4));
- rv = nlm4_granted_4(argp, &res4, clnt);
-
- /* NB: We have a result our caller will not free. */
- xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res4);
- (void) resp;
-
- return (rv);
-}
-
bool_t
nlm4_cancel_4_svc(nlm4_cancargs *argp, nlm4_res *resp, struct svc_req *sr)
{
@@ -773,6 +771,8 @@ nlm4_test_msg_4_svc(nlm4_testargs *argp, void *resp, struct svc_req *sr)
* Callback functions for nlm4_lock_msg_4_svc
* (using the RPC client stubs directly)
*/
+static enum clnt_stat nlm4_granted_msg_4_cb(nlm4_testargs *, nlm4_res *,
+ CLIENT *);
bool_t
nlm4_lock_msg_4_svc(nlm4_lockargs *argp, void *resp,
@@ -784,7 +784,7 @@ nlm4_lock_msg_4_svc(nlm4_lockargs *argp, void *resp,
bzero(&res4, sizeof (res4));
nlm_do_lock(argp, &res4, sr,
NULL, nlm4_lock_res_4,
- nlm4_granted_msg_4);
+ nlm4_granted_msg_4_cb);
/* NB: We have a result our caller will not free. */
xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res4);
@@ -794,6 +794,20 @@ nlm4_lock_msg_4_svc(nlm4_lockargs *argp, void *resp,
return (FALSE);
}
+static enum clnt_stat
+nlm4_granted_msg_4_cb(nlm4_testargs *argp, nlm4_res *resp, CLIENT *clnt)
+{
+ int rv;
+
+ rv = nlm4_granted_msg_4(argp, NULL, clnt);
+
+ /* MSG call doesn't fill in *resp, so do it here. */
+ if (rv != RPC_SUCCESS)
+ resp->stat.stat = nlm4_failed;
+
+ return (rv);
+}
+
bool_t
nlm4_cancel_msg_4_svc(nlm4_cancargs *argp, void *resp, struct svc_req *sr)
{
diff --git a/usr/src/uts/common/klm/nlm_service.c b/usr/src/uts/common/klm/nlm_service.c
index dceabaf53f..f4f733443e 100644
--- a/usr/src/uts/common/klm/nlm_service.c
+++ b/usr/src/uts/common/klm/nlm_service.c
@@ -27,7 +27,7 @@
/*
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
* Copyright 2014 Joyent, Inc. All rights reserved.
*/
@@ -81,6 +81,7 @@ struct nlm_block_cb_data {
struct nlm_host *hostp;
struct nlm_vhold *nvp;
struct flock64 *flp;
+ bool_t registered;
};
/*
@@ -107,9 +108,9 @@ static void nlm_block(
nlm4_lockargs *lockargs,
struct nlm_host *host,
struct nlm_vhold *nvp,
- nlm_rpc_t *rpcp,
struct flock64 *fl,
- nlm_testargs_cb grant_cb);
+ nlm_granted_cb grant_cb,
+ rpcvers_t);
static vnode_t *nlm_fh_to_vp(struct netobj *);
static struct nlm_vhold *nlm_fh_to_vhold(struct nlm_host *, struct netobj *);
@@ -314,6 +315,11 @@ nlm_do_notify2(nlm_sm_status *argp, void *res, struct svc_req *sr)
* NLM_TEST, NLM_TEST_MSG,
* NLM4_TEST, NLM4_TEST_MSG,
* Client inquiry about locks, non-blocking.
+ *
+ * Arg cb is NULL for NLM_TEST, NLM4_TEST, and
+ * non-NULL for NLM_TEST_MSG, NLM4_TEST_MSG
+ * The MSG forms use the cb to send the reply,
+ * and don't return a reply for this call.
*/
void
nlm_do_test(nlm4_testargs *argp, nlm4_testres *resp,
@@ -455,10 +461,19 @@ out:
* We also have to keep a list of locks (pending + granted)
* both to handle retransmitted requests, and to keep the
* vnodes for those locks active.
+ *
+ * Callback arguments:
+ * reply_cb Used to send a normal RPC reply just as if
+ * we had filled in a response for our caller.
+ * Needed because we do work after the reply.
+ * res_cb Used for the MSG calls, where there's no
+ * regular RPC response.
+ * grant_cb Used to CALL the client informing them of a
+ * granted lock after a "blocked" reply.
*/
void
nlm_do_lock(nlm4_lockargs *argp, nlm4_res *resp, struct svc_req *sr,
- nlm_reply_cb reply_cb, nlm_res_cb res_cb, nlm_testargs_cb grant_cb)
+ nlm_reply_cb reply_cb, nlm_res_cb res_cb, nlm_granted_cb grant_cb)
{
struct nlm_globals *g;
struct flock64 fl;
@@ -492,20 +507,18 @@ nlm_do_lock(nlm4_lockargs *argp, nlm4_res *resp, struct svc_req *sr,
struct nlm_host *, host, nlm4_lockargs *, argp);
/*
- * If we may need to do _msg_ call needing an RPC
- * callback, get the RPC client handle now,
- * so we know if we can bind to the NLM service on
- * this client.
- *
- * Note: host object carries transport type.
- * One client using multiple transports gets
- * separate sysids for each of its transports.
+ * If this is a MSG call (NLM_LOCK_MSG, NLM4_LOCK_MSG)
+ * we'll have res_cb != NULL, and we know we'll need an
+ * RPC client handle _now_ so we can send the response.
+ * If we can't get an rpc handle (rpcp) then we have
+ * no way to respond, and the client will time out.
*/
- if (res_cb != NULL || (grant_cb != NULL && argp->block == TRUE)) {
+ if (res_cb != NULL) {
error = nlm_host_get_rpc(host, sr->rq_vers, &rpcp);
if (error != 0) {
+ ASSERT(rpcp == NULL);
status = nlm4_denied_nolocks;
- goto doreply;
+ goto out;
}
}
@@ -584,6 +597,8 @@ nlm_do_lock(nlm4_lockargs *argp, nlm4_res *resp, struct svc_req *sr,
/*
* OK, can detach this thread, so this call
* will block below (after we reply).
+ * The "blocked" reply tells the client to
+ * expect a "granted" call-back later.
*/
status = nlm4_blocked;
do_blocking = TRUE;
@@ -655,11 +670,12 @@ doreply:
* "detach" it from the RPC SVC pool, allowing it
* to block indefinitely if needed.
*/
- ASSERT(rpcp != NULL);
+ ASSERT(grant_cb != NULL);
(void) svc_detach_thread(sr->rq_xprt);
- nlm_block(argp, host, nvp, rpcp, &fl, grant_cb);
+ nlm_block(argp, host, nvp, &fl, grant_cb, sr->rq_vers);
}
+out:
DTRACE_PROBE3(lock__end, struct nlm_globals *, g,
struct nlm_host *, host, nlm4_res *, resp);
@@ -679,25 +695,26 @@ static void
nlm_block(nlm4_lockargs *lockargs,
struct nlm_host *host,
struct nlm_vhold *nvp,
- nlm_rpc_t *rpcp,
struct flock64 *flp,
- nlm_testargs_cb grant_cb)
+ nlm_granted_cb grant_cb,
+ rpcvers_t vers)
{
nlm4_testargs args;
+ nlm4_res res;
int error;
flk_callback_t flk_cb;
struct nlm_block_cb_data cb_data;
+ nlm_rpc_t *rpcp = NULL;
+ enum clnt_stat status;
/*
* Keep a list of blocked locks on nh_pending, and use it
* to cancel these threads in nlm_destroy_client_pending.
*
- * Check to see if this lock is already in the list
- * and if not, add an entry for it. Allocate first,
- * then if we don't insert, free the new one.
- * Caller already has vp held.
+ * Check to see if this lock is already in the list. If so,
+ * some earlier call is already blocked getting this lock,
+ * so there's nothing more this call needs to do.
*/
-
error = nlm_slreq_register(host, nvp, flp);
if (error != 0) {
/*
@@ -710,9 +727,22 @@ nlm_block(nlm4_lockargs *lockargs,
return;
}
+ /*
+ * Make sure we can get an RPC client handle we can use to
+ * deliver the "granted" callback if/when we get the lock.
+ * If we can't, there's no point blocking to get the lock
+ * for them because they'll never find out about it.
+ */
+ error = nlm_host_get_rpc(host, vers, &rpcp);
+ if (error != 0) {
+ (void) nlm_slreq_unregister(host, nvp, flp);
+ return;
+ }
+
cb_data.hostp = host;
cb_data.nvp = nvp;
cb_data.flp = flp;
+ cb_data.registered = TRUE;
flk_init_callback(&flk_cb, nlm_block_callback, &cb_data);
/* BSD: VOP_ADVLOCK(vp, NULL, F_SETLK, fl, F_REMOTE); */
@@ -720,23 +750,60 @@ nlm_block(nlm4_lockargs *lockargs,
F_REMOTELOCK | FREAD | FWRITE,
(u_offset_t)0, &flk_cb, CRED(), NULL);
+ /*
+ * If the nlm_block_callback didn't already do it...
+ */
+ if (cb_data.registered)
+ (void) nlm_slreq_unregister(host, nvp, flp);
+
if (error != 0) {
/*
* We failed getting the lock, but have no way to
* tell the client about that. Let 'em time out.
*/
- (void) nlm_slreq_unregister(host, nvp, flp);
return;
}
-
/*
+ * ... else we got the lock on behalf of this client.
+ *
+ * We MUST either tell the client about this lock
+ * (via the "granted" callback RPC) or unlock.
+ *
* Do the "granted" call-back to the client.
*/
+ bzero(&args, sizeof (args));
args.cookie = lockargs->cookie;
args.exclusive = lockargs->exclusive;
args.alock = lockargs->alock;
+ bzero(&res, sizeof (res));
+
+ /*
+ * Not using the NLM_INVOKE_CALLBACK() macro because
+ * we need to take actions on errors.
+ */
+ status = (*grant_cb)(&args, &res, (rpcp)->nr_handle);
+ if (status != RPC_SUCCESS) {
+ struct rpc_err err;
+
+ CLNT_GETERR((rpcp)->nr_handle, &err);
+ NLM_ERR("NLM: %s callback failed: "
+ "stat %d, err %d\n", "grant", status,
+ err.re_errno);
+ res.stat.stat = nlm4_failed;
+ }
+ if (res.stat.stat != nlm4_granted) {
+ /*
+ * Failed to deliver the granted callback, so
+ * the client doesn't know about this lock.
+ * Unlock the lock. The client will time out.
+ */
+ (void) nlm_vop_frlock(nvp->nv_vp, F_UNLCK, flp,
+ F_REMOTELOCK | FREAD | FWRITE,
+ (u_offset_t)0, NULL, CRED(), NULL);
+ }
+ xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res);
- NLM_INVOKE_CALLBACK("grant", rpcp, &args, grant_cb);
+ nlm_host_rele_rpc(host, rpcp);
}
/*
@@ -756,6 +823,7 @@ nlm_block_callback(flk_cb_when_t when, void *data)
if (when == FLK_AFTER_SLEEP) {
(void) nlm_slreq_unregister(cb_data->hostp,
cb_data->nvp, cb_data->flp);
+ cb_data->registered = FALSE;
}
return (0);