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 } 363