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_XPRTFAILED)
  59 
  60 static struct kmem_cache *nlm_rpch_cache = NULL;
  61 
  62 static int nlm_rpch_ctor(void *, void *, int);
  63 static void nlm_rpch_dtor(void *, void *);
  64 static void destroy_rpch(nlm_rpc_t *);
  65 static nlm_rpc_t *get_nlm_rpc_fromcache(struct nlm_host *, int);
  66 static void update_host_rpcbinding(struct nlm_host *, int);
  67 static int refresh_nlm_rpc(struct nlm_host *, nlm_rpc_t *);
  68 static void nlm_host_rele_rpc_locked(struct nlm_host *, nlm_rpc_t *);
  69 
  70 static nlm_rpc_t *
  71 get_nlm_rpc_fromcache(struct nlm_host *hostp, int vers)
  72 {
  73         nlm_rpc_t *rpcp;
  74         bool_t found = FALSE;
  75 
  76         ASSERT(MUTEX_HELD(&hostp->nh_lock));
  77         if (TAILQ_EMPTY(&hostp->nh_rpchc))
  78                 return (NULL);
  79 
  80         TAILQ_FOREACH(rpcp, &hostp->nh_rpchc, nr_link) {
  81                 if (rpcp->nr_vers == vers) {
  82                         found = TRUE;
  83                         break;
  84                 }
  85         }
  86 
  87         if (!found)
  88                 return (NULL);
  89 
  90         TAILQ_REMOVE(&hostp->nh_rpchc, rpcp, nr_link);
  91         return (rpcp);
  92 }
  93 
  94 /*
  95  * Update host's RPC binding (host->nh_addr).
  96  * The function is executed by only one thread at time.
  97  */
  98 static void
  99 update_host_rpcbinding(struct nlm_host *hostp, int vers)
 100 {
 101         enum clnt_stat stat;
 102 
 103         ASSERT(MUTEX_HELD(&hostp->nh_lock));
 104 
 105         /*
 106          * Mark RPC binding state as "update in progress" in order
 107          * to say other threads that they need to wait until binding
 108          * is fully updated.
 109          */
 110         hostp->nh_rpcb_state = NRPCB_UPDATE_INPROGRESS;
 111         hostp->nh_rpcb_ustat = RPC_SUCCESS;
 112         mutex_exit(&hostp->nh_lock);
 113 
 114         stat = rpcbind_getaddr(&hostp->nh_knc, NLM_PROG, vers, &hostp->nh_addr);
 115         mutex_enter(&hostp->nh_lock);
 116 
 117         hostp->nh_rpcb_state = ((stat == RPC_SUCCESS) ?
 118             NRPCB_UPDATED : NRPCB_NEED_UPDATE);
 119 
 120         hostp->nh_rpcb_ustat = stat;
 121         cv_broadcast(&hostp->nh_rpcb_cv);
 122 }
 123 
 124 /*
 125  * Refresh RPC handle taken from host handles cache.
 126  * This function is called when an RPC handle is either
 127  * uninitialized or was initialized using a binding that's
 128  * no longer current.
 129  */
 130 static int
 131 refresh_nlm_rpc(struct nlm_host *hostp, nlm_rpc_t *rpcp)
 132 {
 133         int ret;
 134 
 135         if (rpcp->nr_handle == NULL) {
 136                 bool_t clset = TRUE;
 137 
 138                 ret = clnt_tli_kcreate(&hostp->nh_knc, &hostp->nh_addr,
 139                     NLM_PROG, rpcp->nr_vers, 0, NLM_RPC_RETRIES,
 140                     CRED(), &rpcp->nr_handle);
 141 
 142                 /*
 143                  * Set the client's CLSET_NODELAYONERR option to true. The
 144                  * RPC clnt_call interface creates an artificial delay for
 145                  * certain call errors in order to prevent RPC consumers
 146                  * from getting into tight retry loops. Since this function is
 147                  * called by the NLM service routines we would like to avoid
 148                  * this artificial delay when possible. We do not retry if the
 149                  * NULL request fails so it is safe for us to turn this option
 150                  * on.
 151                  */
 152                 if (clnt_control(rpcp->nr_handle, CLSET_NODELAYONERR,
 153                     (char *)&clset)) {
 154                         NLM_ERR("Unable to set CLSET_NODELAYONERR\n");
 155                 }
 156         } else {
 157                 ret = clnt_tli_kinit(rpcp->nr_handle, &hostp->nh_knc,
 158                     &hostp->nh_addr, 0, NLM_RPC_RETRIES, CRED());
 159                 if (ret == 0) {
 160                         enum clnt_stat stat;
 161 
 162                         /*
 163                          * Check whether host's RPC binding is still
 164                          * fresh, i.e. if remote program is still sits
 165                          * on the same port we assume. Call NULL proc
 166                          * to do it.
 167                          *
 168                          * Note: Even though we set no delay on error on the
 169                          * client handle the call to nlm_null_rpc can still
 170                          * delay for 10 seconds before returning an error. For
 171                          * example the no delay on error option is not honored
 172                          * for RPC_XPRTFAILED errors (see clnt_cots_kcallit).
 173                          */
 174                         stat = nlm_null_rpc(rpcp->nr_handle, rpcp->nr_vers);
 175                         if (NLM_STALE_CLNT(stat)) {
 176                                 ret = ESTALE;
 177                         }
 178                 }
 179         }
 180 
 181         return (ret);
 182 }
 183 
 184 /*
 185  * Get RPC handle that can be used to talk to the NLM
 186  * of given version running on given host.
 187  * Saves obtained RPC handle to rpcpp argument.
 188  *
 189  * If error occures, return nonzero error code.
 190  */
 191 int
 192 nlm_host_get_rpc(struct nlm_host *hostp, int vers, nlm_rpc_t **rpcpp)
 193 {
 194         nlm_rpc_t *rpcp = NULL;
 195         int rc;
 196 
 197         mutex_enter(&hostp->nh_lock);
 198 
 199         /*
 200          * If this handle is either uninitialized, or was
 201          * initialized using binding that's now stale
 202          * do the init or re-init.
 203          * See comments to enum nlm_rpcb_state for more
 204          * details.
 205          */
 206 again:
 207         while (hostp->nh_rpcb_state != NRPCB_UPDATED) {
 208                 if (hostp->nh_rpcb_state == NRPCB_UPDATE_INPROGRESS) {
 209                         rc = cv_wait_sig(&hostp->nh_rpcb_cv, &hostp->nh_lock);
 210                         if (rc == 0) {
 211                                 mutex_exit(&hostp->nh_lock);
 212                                 return (EINTR);
 213                         }
 214                 }
 215 
 216                 /*
 217                  * Check if RPC binding was marked for update.
 218                  * If so, start RPC binding update operation.
 219                  * NOTE: the operation can be executed by only
 220                  * one thread at time.
 221                  */
 222                 if (hostp->nh_rpcb_state == NRPCB_NEED_UPDATE)
 223                         update_host_rpcbinding(hostp, vers);
 224 
 225                 /*
 226                  * Check if RPC error occured during RPC binding
 227                  * update operation. If so, report a correspoding
 228                  * error.
 229                  */
 230                 if (hostp->nh_rpcb_ustat != RPC_SUCCESS) {
 231                         mutex_exit(&hostp->nh_lock);
 232                         return (ENOENT);
 233                 }
 234         }
 235 
 236         rpcp = get_nlm_rpc_fromcache(hostp, vers);
 237         mutex_exit(&hostp->nh_lock);
 238         if (rpcp == NULL) {
 239                 /*
 240                  * There weren't any RPC handles in a host
 241                  * cache. No luck, just create a new one.
 242                  */
 243                 rpcp = kmem_cache_alloc(nlm_rpch_cache, KM_SLEEP);
 244                 rpcp->nr_vers = vers;
 245         }
 246 
 247         /*
 248          * Refresh RPC binding
 249          */
 250         rc = refresh_nlm_rpc(hostp, rpcp);
 251         if (rc != 0) {
 252                 if (rc == ESTALE) {
 253                         /*
 254                          * Host's RPC binding is stale, we have
 255                          * to update it. Put the RPC handle back
 256                          * to the cache and mark the host as
 257                          * "need update".
 258                          */
 259                         mutex_enter(&hostp->nh_lock);
 260                         hostp->nh_rpcb_state = NRPCB_NEED_UPDATE;
 261                         nlm_host_rele_rpc_locked(hostp, rpcp);
 262                         goto again;
 263                 }
 264 
 265                 destroy_rpch(rpcp);
 266                 return (rc);
 267         }
 268 
 269         DTRACE_PROBE2(end, struct nlm_host *, hostp,
 270             nlm_rpc_t *, rpcp);
 271 
 272         *rpcpp = rpcp;
 273         return (0);
 274 }
 275 
 276 void
 277 nlm_host_rele_rpc(struct nlm_host *hostp, nlm_rpc_t *rpcp)
 278 {
 279         mutex_enter(&hostp->nh_lock);
 280         nlm_host_rele_rpc_locked(hostp, rpcp);
 281         mutex_exit(&hostp->nh_lock);
 282 }
 283 
 284 static void
 285 nlm_host_rele_rpc_locked(struct nlm_host *hostp, nlm_rpc_t *rpcp)
 286 {
 287         ASSERT(mutex_owned(&hostp->nh_lock));
 288         TAILQ_INSERT_HEAD(&hostp->nh_rpchc, rpcp, nr_link);
 289 }
 290 
 291 /*
 292  * The function invalidates host's RPC binding by marking it
 293  * as not fresh. In this case another time thread tries to
 294  * get RPC handle from host's handles cache, host's RPC binding
 295  * will be updated.
 296  *
 297  * The function should be executed when RPC call invoked via
 298  * handle taken from RPC cache returns RPC_PROCUNAVAIL.
 299  */
 300 void
 301 nlm_host_invalidate_binding(struct nlm_host *hostp)
 302 {
 303         mutex_enter(&hostp->nh_lock);
 304         hostp->nh_rpcb_state = NRPCB_NEED_UPDATE;
 305         mutex_exit(&hostp->nh_lock);
 306 }
 307 
 308 void
 309 nlm_rpc_init(void)
 310 {
 311         nlm_rpch_cache = kmem_cache_create("nlm_rpch_cache",
 312             sizeof (nlm_rpc_t), 0, nlm_rpch_ctor, nlm_rpch_dtor,
 313             NULL, NULL, NULL, 0);
 314 }
 315 
 316 void
 317 nlm_rpc_cache_destroy(struct nlm_host *hostp)
 318 {
 319         nlm_rpc_t *rpcp;
 320 
 321         /*
 322          * There's no need to lock host's mutex here,
 323          * nlm_rpc_cache_destroy() should be called from
 324          * only one place: nlm_host_destroy, when all
 325          * resources host owns are already cleaned up.
 326          * So there shouldn't be any raises.
 327          */
 328         while ((rpcp = TAILQ_FIRST(&hostp->nh_rpchc)) != NULL) {
 329                 TAILQ_REMOVE(&hostp->nh_rpchc, rpcp, nr_link);
 330                 destroy_rpch(rpcp);
 331         }
 332 }
 333 
 334 /* ARGSUSED */
 335 static int
 336 nlm_rpch_ctor(void *datap, void *cdrarg, int kmflags)
 337 {
 338         nlm_rpc_t *rpcp = (nlm_rpc_t *)datap;
 339 
 340         bzero(rpcp, sizeof (*rpcp));
 341         return (0);
 342 }
 343 
 344 /* ARGSUSED */
 345 static void
 346 nlm_rpch_dtor(void *datap, void *cdrarg)
 347 {
 348         nlm_rpc_t *rpcp = (nlm_rpc_t *)datap;
 349         ASSERT(rpcp->nr_handle == NULL);
 350 }
 351 
 352 static void
 353 destroy_rpch(nlm_rpc_t *rpcp)
 354 {
 355         if (rpcp->nr_handle != NULL) {
 356                 AUTH_DESTROY(rpcp->nr_handle->cl_auth);
 357                 CLNT_DESTROY(rpcp->nr_handle);
 358                 rpcp->nr_handle = NULL;
 359         }
 360 
 361         kmem_cache_free(nlm_rpch_cache, rpcp);
 362 }