1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  24  * Copyright (c) 2012 by Delphix. All rights reserved.
  25  */
  26 
  27 #include <sys/param.h>
  28 #include <sys/systm.h>
  29 #include <sys/socket.h>
  30 #include <sys/syslog.h>
  31 #include <sys/systm.h>
  32 #include <sys/unistd.h>
  33 #include <sys/queue.h>
  34 #include <sys/sdt.h>
  35 #include <netinet/in.h>
  36 
  37 #include <rpc/rpc.h>
  38 #include <rpc/xdr.h>
  39 #include <rpc/pmap_prot.h>
  40 #include <rpc/pmap_clnt.h>
  41 #include <rpc/rpcb_prot.h>
  42 
  43 #include <rpcsvc/nlm_prot.h>
  44 #include <rpcsvc/sm_inter.h>
  45 
  46 #include "nlm_impl.h"
  47 
  48 /*
  49  * The following errors codes from nlm_null_rpc indicate that the port we have
  50  * cached for the client's NLM service is stale and that we need to establish
  51  * a new RPC client.
  52  */
  53 #define NLM_STALE_CLNT(_status)                 \
  54         ((_status) == RPC_PROGUNAVAIL ||        \
  55         (_status) == RPC_PROGVERSMISMATCH ||    \
  56         (_status) == RPC_PROCUNAVAIL ||         \
  57         (_status) == RPC_CANTCONNECT ||         \
  58         (_status) == RPC_TIMEDOUT ||            \
  59         (_status) == RPC_XPRTFAILED)
  60 
  61 static struct kmem_cache *nlm_rpch_cache = NULL;
  62 
  63 static int nlm_rpch_ctor(void *, void *, int);
  64 static void nlm_rpch_dtor(void *, void *);
  65 static void destroy_rpch(nlm_rpc_t *);
  66 static nlm_rpc_t *get_nlm_rpc_fromcache(struct nlm_host *, int);
  67 static void update_host_rpcbinding(struct nlm_host *, int);
  68 static int refresh_nlm_rpc(struct nlm_host *, nlm_rpc_t *);
  69 static void nlm_host_rele_rpc_locked(struct nlm_host *, nlm_rpc_t *);
  70 
  71 static nlm_rpc_t *
  72 get_nlm_rpc_fromcache(struct nlm_host *hostp, int vers)
  73 {
  74         nlm_rpc_t *rpcp;
  75         bool_t found = FALSE;
  76 
  77         ASSERT(MUTEX_HELD(&hostp->nh_lock));
  78         if (TAILQ_EMPTY(&hostp->nh_rpchc))
  79                 return (NULL);
  80 
  81         TAILQ_FOREACH(rpcp, &hostp->nh_rpchc, nr_link) {
  82                 if (rpcp->nr_vers == vers) {
  83                         found = TRUE;
  84                         break;
  85                 }
  86         }
  87 
  88         if (!found)
  89                 return (NULL);
  90 
  91         TAILQ_REMOVE(&hostp->nh_rpchc, rpcp, nr_link);
  92         return (rpcp);
  93 }
  94 
  95 /*
  96  * Update host's RPC binding (host->nh_addr).
  97  * The function is executed by only one thread at time.
  98  */
  99 static void
 100 update_host_rpcbinding(struct nlm_host *hostp, int vers)
 101 {
 102         enum clnt_stat stat;
 103 
 104         ASSERT(MUTEX_HELD(&hostp->nh_lock));
 105 
 106         /*
 107          * Mark RPC binding state as "update in progress" in order
 108          * to say other threads that they need to wait until binding
 109          * is fully updated.
 110          */
 111         hostp->nh_rpcb_state = NRPCB_UPDATE_INPROGRESS;
 112         hostp->nh_rpcb_ustat = RPC_SUCCESS;
 113         mutex_exit(&hostp->nh_lock);
 114 
 115         stat = rpcbind_getaddr(&hostp->nh_knc, NLM_PROG, vers, &hostp->nh_addr);
 116         mutex_enter(&hostp->nh_lock);
 117 
 118         hostp->nh_rpcb_state = ((stat == RPC_SUCCESS) ?
 119             NRPCB_UPDATED : NRPCB_NEED_UPDATE);
 120 
 121         hostp->nh_rpcb_ustat = stat;
 122         cv_broadcast(&hostp->nh_rpcb_cv);
 123 }
 124 
 125 /*
 126  * Refresh RPC handle taken from host handles cache.
 127  * This function is called when an RPC handle is either
 128  * uninitialized or was initialized using a binding that's
 129  * no longer current.
 130  */
 131 static int
 132 refresh_nlm_rpc(struct nlm_host *hostp, nlm_rpc_t *rpcp)
 133 {
 134         int ret;
 135 
 136         if (rpcp->nr_handle == NULL) {
 137                 bool_t clset = TRUE;
 138 
 139                 ret = clnt_tli_kcreate(&hostp->nh_knc, &hostp->nh_addr,
 140                     NLM_PROG, rpcp->nr_vers, 0, NLM_RPC_RETRIES,
 141                     CRED(), &rpcp->nr_handle);
 142 
 143                 /*
 144                  * Set the client's CLSET_NODELAYONERR option to true. The
 145                  * RPC clnt_call interface creates an artificial delay for
 146                  * certain call errors in order to prevent RPC consumers
 147                  * from getting into tight retry loops. Since this function is
 148                  * called by the NLM service routines we would like to avoid
 149                  * this artificial delay when possible. We do not retry if the
 150                  * NULL request fails so it is safe for us to turn this option
 151                  * on.
 152                  */
 153                 if (clnt_control(rpcp->nr_handle, CLSET_NODELAYONERR,
 154                     (char *)&clset) == FALSE) {
 155                         NLM_ERR("Unable to set CLSET_NODELAYONERR\n");
 156                 }
 157         } else {
 158                 ret = clnt_tli_kinit(rpcp->nr_handle, &hostp->nh_knc,
 159                     &hostp->nh_addr, 0, NLM_RPC_RETRIES, CRED());
 160                 if (ret == 0) {
 161                         enum clnt_stat stat;
 162 
 163                         /*
 164                          * Check whether host's RPC binding is still
 165                          * fresh, i.e. if remote program is still sits
 166                          * on the same port we assume. Call NULL proc
 167                          * to do it.
 168                          *
 169                          * Note: Even though we set no delay on error on the
 170                          * client handle the call to nlm_null_rpc can still
 171                          * delay for 10 seconds before returning an error. For
 172                          * example the no delay on error option is not honored
 173                          * for RPC_XPRTFAILED errors (see clnt_cots_kcallit).
 174                          */
 175                         stat = nlm_null_rpc(rpcp->nr_handle, rpcp->nr_vers);
 176                         if (NLM_STALE_CLNT(stat)) {
 177                                 ret = ESTALE;
 178                         }
 179                 }
 180         }
 181 
 182         return (ret);
 183 }
 184 
 185 /*
 186  * Get RPC handle that can be used to talk to the NLM
 187  * of given version running on given host.
 188  * Saves obtained RPC handle to rpcpp argument.
 189  *
 190  * If error occures, return nonzero error code.
 191  */
 192 int
 193 nlm_host_get_rpc(struct nlm_host *hostp, int vers, nlm_rpc_t **rpcpp)
 194 {
 195         nlm_rpc_t *rpcp = NULL;
 196         int rc;
 197 
 198         mutex_enter(&hostp->nh_lock);
 199 
 200         /*
 201          * If this handle is either uninitialized, or was
 202          * initialized using binding that's now stale
 203          * do the init or re-init.
 204          * See comments to enum nlm_rpcb_state for more
 205          * details.
 206          */
 207 again:
 208         while (hostp->nh_rpcb_state != NRPCB_UPDATED) {
 209                 if (hostp->nh_rpcb_state == NRPCB_UPDATE_INPROGRESS) {
 210                         rc = cv_wait_sig(&hostp->nh_rpcb_cv, &hostp->nh_lock);
 211                         if (rc == 0) {
 212                                 mutex_exit(&hostp->nh_lock);
 213                                 return (EINTR);
 214                         }
 215                 }
 216 
 217                 /*
 218                  * Check if RPC binding was marked for update.
 219                  * If so, start RPC binding update operation.
 220                  * NOTE: the operation can be executed by only
 221                  * one thread at time.
 222                  */
 223                 if (hostp->nh_rpcb_state == NRPCB_NEED_UPDATE)
 224                         update_host_rpcbinding(hostp, vers);
 225 
 226                 /*
 227                  * Check if RPC error occured during RPC binding
 228                  * update operation. If so, report a correspoding
 229                  * error.
 230                  */
 231                 if (hostp->nh_rpcb_ustat != RPC_SUCCESS) {
 232                         mutex_exit(&hostp->nh_lock);
 233                         return (ENOENT);
 234                 }
 235         }
 236 
 237         rpcp = get_nlm_rpc_fromcache(hostp, vers);
 238         mutex_exit(&hostp->nh_lock);
 239         if (rpcp == NULL) {
 240                 /*
 241                  * There weren't any RPC handles in a host
 242                  * cache. No luck, just create a new one.
 243                  */
 244                 rpcp = kmem_cache_alloc(nlm_rpch_cache, KM_SLEEP);
 245                 rpcp->nr_vers = vers;
 246         }
 247 
 248         /*
 249          * Refresh RPC binding
 250          */
 251         rc = refresh_nlm_rpc(hostp, rpcp);
 252         if (rc != 0) {
 253                 if (rc == ESTALE) {
 254                         /*
 255                          * Host's RPC binding is stale, we have
 256                          * to update it. Put the RPC handle back
 257                          * to the cache and mark the host as
 258                          * "need update".
 259                          */
 260                         mutex_enter(&hostp->nh_lock);
 261                         hostp->nh_rpcb_state = NRPCB_NEED_UPDATE;
 262                         nlm_host_rele_rpc_locked(hostp, rpcp);
 263                         goto again;
 264                 }
 265 
 266                 destroy_rpch(rpcp);
 267                 return (rc);
 268         }
 269 
 270         DTRACE_PROBE2(end, struct nlm_host *, hostp,
 271             nlm_rpc_t *, rpcp);
 272 
 273         *rpcpp = rpcp;
 274         return (0);
 275 }
 276 
 277 void
 278 nlm_host_rele_rpc(struct nlm_host *hostp, nlm_rpc_t *rpcp)
 279 {
 280         mutex_enter(&hostp->nh_lock);
 281         nlm_host_rele_rpc_locked(hostp, rpcp);
 282         mutex_exit(&hostp->nh_lock);
 283 }
 284 
 285 static void
 286 nlm_host_rele_rpc_locked(struct nlm_host *hostp, nlm_rpc_t *rpcp)
 287 {
 288         ASSERT(mutex_owned(&hostp->nh_lock));
 289         TAILQ_INSERT_HEAD(&hostp->nh_rpchc, rpcp, nr_link);
 290 }
 291 
 292 /*
 293  * The function invalidates host's RPC binding by marking it
 294  * as not fresh. In this case another time thread tries to
 295  * get RPC handle from host's handles cache, host's RPC binding
 296  * will be updated.
 297  *
 298  * The function should be executed when RPC call invoked via
 299  * handle taken from RPC cache returns RPC_PROCUNAVAIL.
 300  */
 301 void
 302 nlm_host_invalidate_binding(struct nlm_host *hostp)
 303 {
 304         mutex_enter(&hostp->nh_lock);
 305         hostp->nh_rpcb_state = NRPCB_NEED_UPDATE;
 306         mutex_exit(&hostp->nh_lock);
 307 }
 308 
 309 void
 310 nlm_rpc_init(void)
 311 {
 312         nlm_rpch_cache = kmem_cache_create("nlm_rpch_cache",
 313             sizeof (nlm_rpc_t), 0, nlm_rpch_ctor, nlm_rpch_dtor,
 314             NULL, NULL, NULL, 0);
 315 }
 316 
 317 void
 318 nlm_rpc_cache_destroy(struct nlm_host *hostp)
 319 {
 320         nlm_rpc_t *rpcp;
 321 
 322         /*
 323          * There's no need to lock host's mutex here,
 324          * nlm_rpc_cache_destroy() should be called from
 325          * only one place: nlm_host_destroy, when all
 326          * resources host owns are already cleaned up.
 327          * So there shouldn't be any raises.
 328          */
 329         while ((rpcp = TAILQ_FIRST(&hostp->nh_rpchc)) != NULL) {
 330                 TAILQ_REMOVE(&hostp->nh_rpchc, rpcp, nr_link);
 331                 destroy_rpch(rpcp);
 332         }
 333 }
 334 
 335 /* ARGSUSED */
 336 static int
 337 nlm_rpch_ctor(void *datap, void *cdrarg, int kmflags)
 338 {
 339         nlm_rpc_t *rpcp = (nlm_rpc_t *)datap;
 340 
 341         bzero(rpcp, sizeof (*rpcp));
 342         return (0);
 343 }
 344 
 345 /* ARGSUSED */
 346 static void
 347 nlm_rpch_dtor(void *datap, void *cdrarg)
 348 {
 349         nlm_rpc_t *rpcp = (nlm_rpc_t *)datap;
 350         ASSERT(rpcp->nr_handle == NULL);
 351 }
 352 
 353 static void
 354 destroy_rpch(nlm_rpc_t *rpcp)
 355 {
 356         if (rpcp->nr_handle != NULL) {
 357                 AUTH_DESTROY(rpcp->nr_handle->cl_auth);
 358                 CLNT_DESTROY(rpcp->nr_handle);
 359                 rpcp->nr_handle = NULL;
 360         }
 361 
 362         kmem_cache_free(nlm_rpch_cache, rpcp);
 363 }