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