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 }