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 }