Print this page
XXXXX convert NLM's single-count semaphore to a mutex
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/klm/nlm_impl.c
+++ new/usr/src/uts/common/klm/nlm_impl.c
1 1 /*
2 2 * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3 3 * Authors: Doug Rabson <dfr@rabson.org>
4 4 * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
5 5 *
6 6 * Redistribution and use in source and binary forms, with or without
7 7 * modification, are permitted provided that the following conditions
8 8 * are met:
9 9 * 1. Redistributions of source code must retain the above copyright
10 10 * notice, this list of conditions and the following disclaimer.
11 11 * 2. Redistributions in binary form must reproduce the above copyright
12 12 * notice, this list of conditions and the following disclaimer in the
13 13 * documentation and/or other materials provided with the distribution.
14 14 *
15 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 25 * SUCH DAMAGE.
26 26 */
27 27
28 28 /*
29 29 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
30 30 * Copyright (c) 2012 by Delphix. All rights reserved.
31 31 * Copyright 2017 Joyent, Inc. All rights reserved.
32 32 */
33 33
34 34 /*
35 35 * NFS LockManager, start/stop, support functions, etc.
36 36 * Most of the interesting code is here.
37 37 *
38 38 * Source code derived from FreeBSD nlm_prot_impl.c
39 39 */
40 40
41 41 #include <sys/param.h>
42 42 #include <sys/systm.h>
43 43 #include <sys/thread.h>
44 44 #include <sys/fcntl.h>
45 45 #include <sys/flock.h>
46 46 #include <sys/mount.h>
47 47 #include <sys/priv.h>
48 48 #include <sys/proc.h>
49 49 #include <sys/share.h>
50 50 #include <sys/socket.h>
51 51 #include <sys/syscall.h>
52 52 #include <sys/syslog.h>
53 53 #include <sys/systm.h>
54 54 #include <sys/class.h>
55 55 #include <sys/unistd.h>
56 56 #include <sys/vnode.h>
57 57 #include <sys/vfs.h>
58 58 #include <sys/queue.h>
59 59 #include <sys/bitmap.h>
60 60 #include <sys/sdt.h>
61 61 #include <sys/brand.h>
62 62 #include <netinet/in.h>
63 63
64 64 #include <rpc/rpc.h>
65 65 #include <rpc/xdr.h>
66 66 #include <rpc/pmap_prot.h>
67 67 #include <rpc/pmap_clnt.h>
68 68 #include <rpc/rpcb_prot.h>
69 69
70 70 #include <rpcsvc/nlm_prot.h>
71 71 #include <rpcsvc/sm_inter.h>
72 72 #include <rpcsvc/nsm_addr.h>
73 73
74 74 #include <nfs/nfs.h>
75 75 #include <nfs/nfs_clnt.h>
76 76 #include <nfs/export.h>
77 77 #include <nfs/rnode.h>
78 78 #include <nfs/lm.h>
79 79
80 80 #include "nlm_impl.h"
81 81
82 82 struct nlm_knc {
83 83 struct knetconfig n_knc;
84 84 const char *n_netid;
85 85 };
86 86
87 87 /*
88 88 * Number of attempts NLM tries to obtain RPC binding
89 89 * of local statd.
90 90 */
91 91 #define NLM_NSM_RPCBIND_RETRIES 10
92 92
93 93 /*
94 94 * Timeout (in seconds) NLM waits before making another
95 95 * attempt to obtain RPC binding of local statd.
96 96 */
97 97 #define NLM_NSM_RPCBIND_TIMEOUT 5
98 98
99 99 /*
100 100 * Total number of sysids in NLM sysid bitmap
101 101 */
102 102 #define NLM_BMAP_NITEMS (LM_SYSID_MAX + 1)
103 103
104 104 /*
105 105 * Number of ulong_t words in bitmap that is used
106 106 * for allocation of sysid numbers.
107 107 */
108 108 #define NLM_BMAP_WORDS (NLM_BMAP_NITEMS / BT_NBIPUL)
109 109
110 110 /*
111 111 * Given an integer x, the macro returns
112 112 * -1 if x is negative,
113 113 * 0 if x is zero
114 114 * 1 if x is positive
115 115 */
116 116 #define SIGN(x) (((x) > 0) - ((x) < 0))
117 117
118 118 #define ARRSIZE(arr) (sizeof (arr) / sizeof ((arr)[0]))
119 119 #define NLM_KNCS ARRSIZE(nlm_netconfigs)
120 120
121 121 krwlock_t lm_lck;
122 122
123 123 /*
124 124 * Zero timeout for asynchronous NLM RPC operations
125 125 */
126 126 static const struct timeval nlm_rpctv_zero = { 0, 0 };
127 127
128 128 /*
129 129 * List of all Zone globals nlm_globals instences
130 130 * linked together.
131 131 */
132 132 static struct nlm_globals_list nlm_zones_list; /* (g) */
133 133
134 134 /*
135 135 * NLM kmem caches
136 136 */
137 137 static struct kmem_cache *nlm_hosts_cache = NULL;
138 138 static struct kmem_cache *nlm_vhold_cache = NULL;
139 139
140 140 /*
141 141 * A bitmap for allocation of new sysids.
142 142 * Sysid is a unique number between LM_SYSID
143 143 * and LM_SYSID_MAX. Sysid represents unique remote
144 144 * host that does file locks on the given host.
145 145 */
146 146 static ulong_t nlm_sysid_bmap[NLM_BMAP_WORDS]; /* (g) */
147 147 static int nlm_sysid_nidx; /* (g) */
148 148
149 149 /*
150 150 * RPC service registration for all transports
151 151 */
152 152 static SVC_CALLOUT nlm_svcs[] = {
153 153 { NLM_PROG, 4, 4, nlm_prog_4 }, /* NLM4_VERS */
154 154 { NLM_PROG, 1, 3, nlm_prog_3 } /* NLM_VERS - NLM_VERSX */
155 155 };
156 156
157 157 static SVC_CALLOUT_TABLE nlm_sct = {
158 158 ARRSIZE(nlm_svcs),
159 159 FALSE,
160 160 nlm_svcs
161 161 };
162 162
163 163 /*
164 164 * Static table of all netid/knetconfig network
165 165 * lock manager can work with. nlm_netconfigs table
166 166 * is used when we need to get valid knetconfig by
167 167 * netid and vice versa.
168 168 *
169 169 * Knetconfigs are activated either by the call from
170 170 * user-space lockd daemon (server side) or by taking
171 171 * knetconfig from NFS mountinfo (client side)
172 172 */
173 173 static struct nlm_knc nlm_netconfigs[] = { /* (g) */
174 174 /* UDP */
175 175 {
176 176 { NC_TPI_CLTS, NC_INET, NC_UDP, NODEV },
177 177 "udp",
178 178 },
179 179 /* TCP */
180 180 {
181 181 { NC_TPI_COTS_ORD, NC_INET, NC_TCP, NODEV },
182 182 "tcp",
183 183 },
184 184 /* UDP over IPv6 */
185 185 {
186 186 { NC_TPI_CLTS, NC_INET6, NC_UDP, NODEV },
187 187 "udp6",
188 188 },
189 189 /* TCP over IPv6 */
190 190 {
191 191 { NC_TPI_COTS_ORD, NC_INET6, NC_TCP, NODEV },
192 192 "tcp6",
193 193 },
194 194 /* ticlts (loopback over UDP) */
195 195 {
196 196 { NC_TPI_CLTS, NC_LOOPBACK, NC_NOPROTO, NODEV },
197 197 "ticlts",
198 198 },
199 199 /* ticotsord (loopback over TCP) */
200 200 {
201 201 { NC_TPI_COTS_ORD, NC_LOOPBACK, NC_NOPROTO, NODEV },
202 202 "ticotsord",
203 203 },
204 204 };
205 205
206 206 /*
207 207 * NLM functions which can be called by a brand hook.
208 208 */
209 209 void nlm_netbuf_to_netobj(struct netbuf *, int *, netobj *);
210 210 void nlm_nsm_clnt_init(CLIENT *, struct nlm_nsm *);
211 211
212 212 /*
213 213 * NLM misc. function
214 214 */
215 215 static void nlm_copy_netbuf(struct netbuf *, struct netbuf *);
216 216 static int nlm_netbuf_addrs_cmp(struct netbuf *, struct netbuf *);
217 217 static void nlm_kmem_reclaim(void *);
218 218 static void nlm_pool_shutdown(void);
219 219 static void nlm_suspend_zone(struct nlm_globals *);
220 220 static void nlm_resume_zone(struct nlm_globals *);
221 221
222 222 /*
223 223 * NLM thread functions
224 224 */
225 225 static void nlm_gc(struct nlm_globals *);
226 226 static void nlm_reclaimer(struct nlm_host *);
227 227
228 228 /*
229 229 * NLM NSM functions
230 230 */
231 231 static int nlm_init_local_knc(struct knetconfig *);
232 232 static int nlm_nsm_init_local(struct nlm_nsm *);
233 233 static int nlm_nsm_init(struct nlm_nsm *, struct knetconfig *, struct netbuf *);
234 234 static void nlm_nsm_fini(struct nlm_nsm *);
235 235 static enum clnt_stat nlm_nsm_simu_crash(struct nlm_nsm *);
236 236 static enum clnt_stat nlm_nsm_stat(struct nlm_nsm *, int32_t *);
237 237 static enum clnt_stat nlm_nsm_mon(struct nlm_nsm *, char *, uint16_t);
238 238 static enum clnt_stat nlm_nsm_unmon(struct nlm_nsm *, char *);
239 239
240 240 /*
241 241 * NLM host functions
242 242 */
243 243 static int nlm_host_ctor(void *, void *, int);
244 244 static void nlm_host_dtor(void *, void *);
245 245 static void nlm_host_destroy(struct nlm_host *);
246 246 static struct nlm_host *nlm_host_create(char *, const char *,
247 247 struct knetconfig *, struct netbuf *);
248 248 static struct nlm_host *nlm_host_find_locked(struct nlm_globals *,
249 249 const char *, struct netbuf *, avl_index_t *);
250 250 static void nlm_host_unregister(struct nlm_globals *, struct nlm_host *);
251 251 static void nlm_host_gc_vholds(struct nlm_host *);
252 252 static bool_t nlm_host_has_srv_locks(struct nlm_host *);
253 253 static bool_t nlm_host_has_cli_locks(struct nlm_host *);
254 254 static bool_t nlm_host_has_locks(struct nlm_host *);
255 255
256 256 /*
257 257 * NLM vhold functions
258 258 */
259 259 static int nlm_vhold_ctor(void *, void *, int);
260 260 static void nlm_vhold_dtor(void *, void *);
261 261 static void nlm_vhold_destroy(struct nlm_host *,
262 262 struct nlm_vhold *);
263 263 static bool_t nlm_vhold_busy(struct nlm_host *, struct nlm_vhold *);
264 264 static void nlm_vhold_clean(struct nlm_vhold *, int);
265 265
266 266 /*
267 267 * NLM client/server sleeping locks/share reservation functions
268 268 */
269 269 struct nlm_slreq *nlm_slreq_find_locked(struct nlm_host *,
270 270 struct nlm_vhold *, struct flock64 *);
271 271 static struct nlm_shres *nlm_shres_create_item(struct shrlock *, vnode_t *);
272 272 static void nlm_shres_destroy_item(struct nlm_shres *);
273 273 static bool_t nlm_shres_equal(struct shrlock *, struct shrlock *);
274 274
275 275 /*
276 276 * NLM initialization functions.
277 277 */
278 278 void
279 279 nlm_init(void)
280 280 {
281 281 nlm_hosts_cache = kmem_cache_create("nlm_host_cache",
282 282 sizeof (struct nlm_host), 0, nlm_host_ctor, nlm_host_dtor,
283 283 nlm_kmem_reclaim, NULL, NULL, 0);
284 284
285 285 nlm_vhold_cache = kmem_cache_create("nlm_vhold_cache",
286 286 sizeof (struct nlm_vhold), 0, nlm_vhold_ctor, nlm_vhold_dtor,
287 287 NULL, NULL, NULL, 0);
288 288
289 289 nlm_rpc_init();
290 290 TAILQ_INIT(&nlm_zones_list);
291 291
292 292 /* initialize sysids bitmap */
293 293 bzero(nlm_sysid_bmap, sizeof (nlm_sysid_bmap));
294 294 nlm_sysid_nidx = 1;
295 295
296 296 /*
297 297 * Reserv the sysid #0, because it's associated
298 298 * with local locks only. Don't let to allocate
299 299 * it for remote locks.
300 300 */
301 301 BT_SET(nlm_sysid_bmap, 0);
302 302 }
303 303
304 304 void
305 305 nlm_globals_register(struct nlm_globals *g)
306 306 {
307 307 rw_enter(&lm_lck, RW_WRITER);
308 308 TAILQ_INSERT_TAIL(&nlm_zones_list, g, nlm_link);
309 309 rw_exit(&lm_lck);
310 310 }
311 311
312 312 void
313 313 nlm_globals_unregister(struct nlm_globals *g)
314 314 {
315 315 rw_enter(&lm_lck, RW_WRITER);
316 316 TAILQ_REMOVE(&nlm_zones_list, g, nlm_link);
317 317 rw_exit(&lm_lck);
318 318 }
319 319
320 320 /* ARGSUSED */
321 321 static void
322 322 nlm_kmem_reclaim(void *cdrarg)
323 323 {
324 324 struct nlm_globals *g;
325 325
326 326 rw_enter(&lm_lck, RW_READER);
327 327 TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
328 328 cv_broadcast(&g->nlm_gc_sched_cv);
329 329
330 330 rw_exit(&lm_lck);
331 331 }
332 332
333 333 /*
334 334 * NLM garbage collector thread (GC).
335 335 *
336 336 * NLM GC periodically checks whether there're any host objects
337 337 * that can be cleaned up. It also releases stale vnodes that
338 338 * live on the server side (under protection of vhold objects).
339 339 *
340 340 * NLM host objects are cleaned up from GC thread because
341 341 * operations helping us to determine whether given host has
342 342 * any locks can be quite expensive and it's not good to call
343 343 * them every time the very last reference to the host is dropped.
344 344 * Thus we use "lazy" approach for hosts cleanup.
345 345 *
346 346 * The work of GC is to release stale vnodes on the server side
347 347 * and destroy hosts that haven't any locks and any activity for
348 348 * some time (i.e. idle hosts).
349 349 */
350 350 static void
351 351 nlm_gc(struct nlm_globals *g)
352 352 {
353 353 struct nlm_host *hostp;
354 354 clock_t now, idle_period;
355 355
356 356 idle_period = SEC_TO_TICK(g->cn_idle_tmo);
357 357 mutex_enter(&g->lock);
358 358 for (;;) {
359 359 /*
360 360 * GC thread can be explicitly scheduled from
361 361 * memory reclamation function.
362 362 */
363 363 (void) cv_timedwait(&g->nlm_gc_sched_cv, &g->lock,
364 364 ddi_get_lbolt() + idle_period);
365 365
366 366 /*
367 367 * NLM is shutting down, time to die.
368 368 */
369 369 if (g->run_status == NLM_ST_STOPPING)
370 370 break;
371 371
372 372 now = ddi_get_lbolt();
373 373 DTRACE_PROBE2(gc__start, struct nlm_globals *, g,
374 374 clock_t, now);
375 375
376 376 /*
377 377 * Find all obviously unused vholds and destroy them.
378 378 */
379 379 for (hostp = avl_first(&g->nlm_hosts_tree); hostp != NULL;
380 380 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp)) {
381 381 struct nlm_vhold *nvp;
382 382
383 383 mutex_enter(&hostp->nh_lock);
384 384
385 385 nvp = TAILQ_FIRST(&hostp->nh_vholds_list);
386 386 while (nvp != NULL) {
387 387 struct nlm_vhold *new_nvp;
388 388
389 389 new_nvp = TAILQ_NEXT(nvp, nv_link);
390 390
391 391 /*
392 392 * If these conditions are met, the vhold is
393 393 * obviously unused and we will destroy it. In
394 394 * a case either v_filocks and/or v_shrlocks is
395 395 * non-NULL the vhold might still be unused by
396 396 * the host, but it is expensive to check that.
397 397 * We defer such check until the host is idle.
398 398 * The expensive check is done below without
399 399 * the global lock held.
400 400 */
401 401 if (nvp->nv_refcnt == 0 &&
402 402 nvp->nv_vp->v_filocks == NULL &&
403 403 nvp->nv_vp->v_shrlocks == NULL) {
404 404 nlm_vhold_destroy(hostp, nvp);
405 405 }
406 406
407 407 nvp = new_nvp;
408 408 }
409 409
410 410 mutex_exit(&hostp->nh_lock);
411 411 }
412 412
413 413 /*
414 414 * Handle all hosts that are unused at the moment
415 415 * until we meet one with idle timeout in future.
416 416 */
417 417 while ((hostp = TAILQ_FIRST(&g->nlm_idle_hosts)) != NULL) {
418 418 bool_t has_locks;
419 419
420 420 if (hostp->nh_idle_timeout > now)
421 421 break;
422 422
423 423 /*
424 424 * Drop global lock while doing expensive work
425 425 * on this host. We'll re-check any conditions
426 426 * that might change after retaking the global
427 427 * lock.
428 428 */
429 429 mutex_exit(&g->lock);
430 430 mutex_enter(&hostp->nh_lock);
431 431
432 432 /*
433 433 * nlm_globals lock was dropped earlier because
434 434 * garbage collecting of vholds and checking whether
435 435 * host has any locks/shares are expensive operations.
436 436 */
437 437 nlm_host_gc_vholds(hostp);
438 438 has_locks = nlm_host_has_locks(hostp);
439 439
440 440 mutex_exit(&hostp->nh_lock);
441 441 mutex_enter(&g->lock);
442 442
443 443 /*
444 444 * While we were doing expensive operations
445 445 * outside of nlm_globals critical section,
446 446 * somebody could take the host and remove it
447 447 * from the idle list. Whether its been
448 448 * reinserted or not, our information about
449 449 * the host is outdated, and we should take no
450 450 * further action.
451 451 */
452 452 if ((hostp->nh_flags & NLM_NH_INIDLE) == 0 ||
453 453 hostp->nh_idle_timeout > now)
454 454 continue;
455 455
456 456 /*
457 457 * If the host has locks we have to renew the
458 458 * host's timeout and put it at the end of LRU
459 459 * list.
460 460 */
461 461 if (has_locks) {
462 462 TAILQ_REMOVE(&g->nlm_idle_hosts,
463 463 hostp, nh_link);
464 464 hostp->nh_idle_timeout = now + idle_period;
465 465 TAILQ_INSERT_TAIL(&g->nlm_idle_hosts,
466 466 hostp, nh_link);
467 467 continue;
468 468 }
469 469
470 470 /*
471 471 * We're here if all the following conditions hold:
472 472 * 1) Host hasn't any locks or share reservations
473 473 * 2) Host is unused
474 474 * 3) Host wasn't touched by anyone at least for
475 475 * g->cn_idle_tmo seconds.
476 476 *
477 477 * So, now we can destroy it.
478 478 */
479 479 nlm_host_unregister(g, hostp);
480 480 mutex_exit(&g->lock);
481 481
482 482 nlm_host_unmonitor(g, hostp);
483 483 nlm_host_destroy(hostp);
484 484 mutex_enter(&g->lock);
485 485 if (g->run_status == NLM_ST_STOPPING)
486 486 break;
487 487
488 488 }
489 489
490 490 DTRACE_PROBE(gc__end);
491 491 }
492 492
493 493 DTRACE_PROBE1(gc__exit, struct nlm_globals *, g);
494 494
495 495 /* Let others know that GC has died */
496 496 g->nlm_gc_thread = NULL;
497 497 mutex_exit(&g->lock);
498 498
499 499 cv_broadcast(&g->nlm_gc_finish_cv);
500 500 zthread_exit();
501 501 }
502 502
503 503 /*
504 504 * Thread reclaim locks/shares acquired by the client side
505 505 * on the given server represented by hostp.
506 506 */
507 507 static void
508 508 nlm_reclaimer(struct nlm_host *hostp)
509 509 {
510 510 struct nlm_globals *g;
511 511
512 512 mutex_enter(&hostp->nh_lock);
513 513 hostp->nh_reclaimer = curthread;
514 514 mutex_exit(&hostp->nh_lock);
515 515
516 516 g = zone_getspecific(nlm_zone_key, curzone);
517 517 nlm_reclaim_client(g, hostp);
518 518
519 519 mutex_enter(&hostp->nh_lock);
520 520 hostp->nh_flags &= ~NLM_NH_RECLAIM;
521 521 hostp->nh_reclaimer = NULL;
522 522 cv_broadcast(&hostp->nh_recl_cv);
523 523 mutex_exit(&hostp->nh_lock);
524 524
525 525 /*
526 526 * Host was explicitly referenced before
527 527 * nlm_reclaim() was called, release it
528 528 * here.
529 529 */
530 530 nlm_host_release(g, hostp);
531 531 zthread_exit();
532 532 }
533 533
534 534 /*
535 535 * Copy a struct netobj. (see xdr.h)
536 536 */
537 537 void
538 538 nlm_copy_netobj(struct netobj *dst, struct netobj *src)
539 539 {
540 540 dst->n_len = src->n_len;
541 541 dst->n_bytes = kmem_alloc(src->n_len, KM_SLEEP);
542 542 bcopy(src->n_bytes, dst->n_bytes, src->n_len);
543 543 }
544 544
545 545 /*
546 546 * An NLM specificw replacement for clnt_call().
547 547 * nlm_clnt_call() is used by all RPC functions generated
548 548 * from nlm_prot.x specification. The function is aware
549 549 * about some pitfalls of NLM RPC procedures and has a logic
550 550 * that handles them properly.
551 551 */
552 552 enum clnt_stat
553 553 nlm_clnt_call(CLIENT *clnt, rpcproc_t procnum, xdrproc_t xdr_args,
554 554 caddr_t argsp, xdrproc_t xdr_result, caddr_t resultp, struct timeval wait)
555 555 {
556 556 k_sigset_t oldmask;
557 557 enum clnt_stat stat;
558 558 bool_t sig_blocked = FALSE;
559 559
560 560 /*
561 561 * If NLM RPC procnum is one of the NLM _RES procedures
562 562 * that are used to reply to asynchronous NLM RPC
563 563 * (MSG calls), explicitly set RPC timeout to zero.
564 564 * Client doesn't send a reply to RES procedures, so
565 565 * we don't need to wait anything.
566 566 *
567 567 * NOTE: we ignore NLM4_*_RES procnums because they are
568 568 * equal to NLM_*_RES numbers.
569 569 */
570 570 if (procnum >= NLM_TEST_RES && procnum <= NLM_GRANTED_RES)
571 571 wait = nlm_rpctv_zero;
572 572
573 573 /*
574 574 * We need to block signals in case of NLM_CANCEL RPC
575 575 * in order to prevent interruption of network RPC
576 576 * calls.
577 577 */
578 578 if (procnum == NLM_CANCEL) {
579 579 k_sigset_t newmask;
580 580
581 581 sigfillset(&newmask);
582 582 sigreplace(&newmask, &oldmask);
583 583 sig_blocked = TRUE;
584 584 }
585 585
586 586 stat = clnt_call(clnt, procnum, xdr_args,
587 587 argsp, xdr_result, resultp, wait);
588 588
589 589 /*
590 590 * Restore signal mask back if signals were blocked
591 591 */
592 592 if (sig_blocked)
593 593 sigreplace(&oldmask, (k_sigset_t *)NULL);
594 594
595 595 return (stat);
596 596 }
597 597
598 598 /*
599 599 * Suspend NLM client/server in the given zone.
600 600 *
601 601 * During suspend operation we mark those hosts
602 602 * that have any locks with NLM_NH_SUSPEND flags,
603 603 * so that they can be checked later, when resume
604 604 * operation occurs.
605 605 */
606 606 static void
607 607 nlm_suspend_zone(struct nlm_globals *g)
608 608 {
609 609 struct nlm_host *hostp;
610 610 struct nlm_host_list all_hosts;
611 611
612 612 /*
613 613 * Note that while we're doing suspend, GC thread is active
614 614 * and it can destroy some hosts while we're walking through
615 615 * the hosts tree. To prevent that and make suspend logic
616 616 * a bit more simple we put all hosts to local "all_hosts"
617 617 * list and increment reference counter of each host.
618 618 * This guaranties that no hosts will be released while
619 619 * we're doing suspend.
620 620 * NOTE: reference of each host must be dropped during
621 621 * resume operation.
622 622 */
623 623 TAILQ_INIT(&all_hosts);
624 624 mutex_enter(&g->lock);
625 625 for (hostp = avl_first(&g->nlm_hosts_tree); hostp != NULL;
626 626 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp)) {
627 627 /*
628 628 * If host is idle, remove it from idle list and
629 629 * clear idle flag. That is done to prevent GC
630 630 * from touching this host.
631 631 */
632 632 if (hostp->nh_flags & NLM_NH_INIDLE) {
633 633 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
634 634 hostp->nh_flags &= ~NLM_NH_INIDLE;
635 635 }
636 636
637 637 hostp->nh_refs++;
638 638 TAILQ_INSERT_TAIL(&all_hosts, hostp, nh_link);
639 639 }
640 640
641 641 /*
642 642 * Now we can walk through all hosts on the system
643 643 * with zone globals lock released. The fact the
644 644 * we have taken a reference to each host guaranties
645 645 * that no hosts can be destroyed during that process.
646 646 */
647 647 mutex_exit(&g->lock);
648 648 while ((hostp = TAILQ_FIRST(&all_hosts)) != NULL) {
649 649 mutex_enter(&hostp->nh_lock);
650 650 if (nlm_host_has_locks(hostp))
651 651 hostp->nh_flags |= NLM_NH_SUSPEND;
652 652
653 653 mutex_exit(&hostp->nh_lock);
654 654 TAILQ_REMOVE(&all_hosts, hostp, nh_link);
655 655 }
656 656 }
657 657
658 658 /*
659 659 * Resume NLM hosts for the given zone.
660 660 *
661 661 * nlm_resume_zone() is called after hosts were suspended
662 662 * (see nlm_suspend_zone) and its main purpose to check
663 663 * whether remote locks owned by hosts are still in consistent
664 664 * state. If they aren't, resume function tries to reclaim
665 665 * locks (for client side hosts) and clean locks (for
666 666 * server side hosts).
667 667 */
668 668 static void
669 669 nlm_resume_zone(struct nlm_globals *g)
670 670 {
671 671 struct nlm_host *hostp, *h_next;
672 672
673 673 mutex_enter(&g->lock);
674 674 hostp = avl_first(&g->nlm_hosts_tree);
675 675
676 676 /*
677 677 * In nlm_suspend_zone() the reference counter of each
678 678 * host was incremented, so we can safely iterate through
679 679 * all hosts without worrying that any host we touch will
680 680 * be removed at the moment.
681 681 */
682 682 while (hostp != NULL) {
683 683 struct nlm_nsm nsm;
684 684 enum clnt_stat stat;
685 685 int32_t sm_state;
686 686 int error;
687 687 bool_t resume_failed = FALSE;
688 688
689 689 h_next = AVL_NEXT(&g->nlm_hosts_tree, hostp);
690 690 mutex_exit(&g->lock);
691 691
692 692 DTRACE_PROBE1(resume__host, struct nlm_host *, hostp);
693 693
694 694 /*
695 695 * Suspend operation marked that the host doesn't
696 696 * have any locks. Skip it.
697 697 */
698 698 if (!(hostp->nh_flags & NLM_NH_SUSPEND))
699 699 goto cycle_end;
700 700
701 701 error = nlm_nsm_init(&nsm, &hostp->nh_knc, &hostp->nh_addr);
702 702 if (error != 0) {
703 703 NLM_ERR("Resume: Failed to contact to NSM of host %s "
704 704 "[error=%d]\n", hostp->nh_name, error);
705 705 resume_failed = TRUE;
706 706 goto cycle_end;
707 707 }
708 708
709 709 stat = nlm_nsm_stat(&nsm, &sm_state);
710 710 if (stat != RPC_SUCCESS) {
711 711 NLM_ERR("Resume: Failed to call SM_STAT operation for "
712 712 "host %s [stat=%d]\n", hostp->nh_name, stat);
713 713 resume_failed = TRUE;
714 714 nlm_nsm_fini(&nsm);
715 715 goto cycle_end;
716 716 }
717 717
718 718 if (sm_state != hostp->nh_state) {
719 719 /*
720 720 * Current SM state of the host isn't equal
721 721 * to the one host had when it was suspended.
722 722 * Probably it was rebooted. Try to reclaim
723 723 * locks if the host has any on its client side.
724 724 * Also try to clean up its server side locks
725 725 * (if the host has any).
726 726 */
727 727 nlm_host_notify_client(hostp, sm_state);
728 728 nlm_host_notify_server(hostp, sm_state);
729 729 }
730 730
731 731 nlm_nsm_fini(&nsm);
732 732
733 733 cycle_end:
734 734 if (resume_failed) {
735 735 /*
736 736 * Resume failed for the given host.
737 737 * Just clean up all resources it owns.
738 738 */
739 739 nlm_host_notify_server(hostp, 0);
740 740 nlm_client_cancel_all(g, hostp);
741 741 }
742 742
743 743 hostp->nh_flags &= ~NLM_NH_SUSPEND;
744 744 nlm_host_release(g, hostp);
745 745 hostp = h_next;
746 746 mutex_enter(&g->lock);
747 747 }
748 748
749 749 mutex_exit(&g->lock);
750 750 }
751 751
752 752 /*
753 753 * NLM functions responsible for operations on NSM handle.
754 754 */
755 755
756 756 /*
757 757 * Initialize knetconfig that is used for communication
758 758 * with local statd via loopback interface.
759 759 */
760 760 static int
761 761 nlm_init_local_knc(struct knetconfig *knc)
762 762 {
763 763 int error;
764 764 vnode_t *vp;
765 765
766 766 bzero(knc, sizeof (*knc));
767 767 error = lookupname("/dev/tcp", UIO_SYSSPACE,
768 768 FOLLOW, NULLVPP, &vp);
769 769 if (error != 0)
770 770 return (error);
771 771
772 772 knc->knc_semantics = NC_TPI_COTS;
773 773 knc->knc_protofmly = NC_INET;
774 774 knc->knc_proto = NC_TCP;
775 775 knc->knc_rdev = vp->v_rdev;
776 776 VN_RELE(vp);
777 777
778 778
779 779 return (0);
780 780 }
781 781
782 782 /*
783 783 * Initialize NSM handle that will be used to talk
784 784 * to local statd via loopback interface.
785 785 */
786 786 static int
787 787 nlm_nsm_init_local(struct nlm_nsm *nsm)
788 788 {
789 789 int error;
790 790 struct knetconfig knc;
791 791 struct sockaddr_in sin;
792 792 struct netbuf nb;
793 793
794 794 error = nlm_init_local_knc(&knc);
795 795 if (error != 0)
796 796 return (error);
797 797
798 798 bzero(&sin, sizeof (sin));
799 799 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
800 800 sin.sin_family = AF_INET;
801 801
802 802 nb.buf = (char *)&sin;
803 803 nb.len = nb.maxlen = sizeof (sin);
804 804
805 805 return (nlm_nsm_init(nsm, &knc, &nb));
806 806 }
807 807
808 808 /*
809 809 * Initialize NSM handle used for talking to statd
810 810 */
811 811 static int
812 812 nlm_nsm_init(struct nlm_nsm *nsm, struct knetconfig *knc, struct netbuf *nb)
813 813 {
814 814 enum clnt_stat stat;
815 815 int error, retries;
816 816
817 817 bzero(nsm, sizeof (*nsm));
818 818 nsm->ns_knc = *knc;
819 819 nlm_copy_netbuf(&nsm->ns_addr, nb);
820 820
821 821 /*
822 822 * Try several times to get the port of statd service,
823 823 * If rpcbind_getaddr returns RPC_PROGNOTREGISTERED,
824 824 * retry an attempt, but wait for NLM_NSM_RPCBIND_TIMEOUT
825 825 * seconds berofore.
826 826 */
827 827 for (retries = 0; retries < NLM_NSM_RPCBIND_RETRIES; retries++) {
828 828 stat = rpcbind_getaddr(&nsm->ns_knc, SM_PROG,
829 829 SM_VERS, &nsm->ns_addr);
830 830 if (stat != RPC_SUCCESS) {
831 831 if (stat == RPC_PROGNOTREGISTERED) {
832 832 delay(SEC_TO_TICK(NLM_NSM_RPCBIND_TIMEOUT));
833 833 continue;
834 834 }
835 835 }
836 836
837 837 break;
838 838 }
839 839
840 840 if (stat != RPC_SUCCESS) {
841 841 DTRACE_PROBE2(rpcbind__error, enum clnt_stat, stat,
842 842 int, retries);
843 843 error = ENOENT;
844 844 goto error;
845 845 }
846 846
847 847 /*
848 848 * Create an RPC handle that'll be used for communication with local
849 849 * statd using the status monitor protocol.
850 850 */
851 851 error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, SM_PROG, SM_VERS,
852 852 0, NLM_RPC_RETRIES, zone_kcred(), &nsm->ns_handle);
853 853 if (error != 0)
854 854 goto error;
855 855
|
↓ open down ↓ |
855 lines elided |
↑ open up ↑ |
856 856 /*
857 857 * Create an RPC handle that'll be used for communication with the
858 858 * local statd using the address registration protocol.
859 859 */
860 860 error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, NSM_ADDR_PROGRAM,
861 861 NSM_ADDR_V1, 0, NLM_RPC_RETRIES, zone_kcred(),
862 862 &nsm->ns_addr_handle);
863 863 if (error != 0)
864 864 goto error;
865 865
866 - sema_init(&nsm->ns_sem, 1, NULL, SEMA_DEFAULT, NULL);
866 + mutex_init(&nsm->ns_lock, NULL, MUTEX_DEFAULT, NULL);
867 867 return (0);
868 868
869 869 error:
870 870 kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen);
871 871 if (nsm->ns_handle) {
872 872 ASSERT(nsm->ns_handle->cl_auth != NULL);
873 873 auth_destroy(nsm->ns_handle->cl_auth);
874 874 CLNT_DESTROY(nsm->ns_handle);
875 875 }
876 876
877 877 return (error);
878 878 }
879 879
880 880 static void
881 881 nlm_nsm_fini(struct nlm_nsm *nsm)
|
↓ open down ↓ |
5 lines elided |
↑ open up ↑ |
882 882 {
883 883 kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen);
884 884 if (nsm->ns_addr_handle->cl_auth != NULL)
885 885 auth_destroy(nsm->ns_addr_handle->cl_auth);
886 886 CLNT_DESTROY(nsm->ns_addr_handle);
887 887 nsm->ns_addr_handle = NULL;
888 888 if (nsm->ns_handle->cl_auth != NULL)
889 889 auth_destroy(nsm->ns_handle->cl_auth);
890 890 CLNT_DESTROY(nsm->ns_handle);
891 891 nsm->ns_handle = NULL;
892 - sema_destroy(&nsm->ns_sem);
892 + mutex_destroy(&nsm->ns_lock);
893 893 }
894 894
895 895 static enum clnt_stat
896 896 nlm_nsm_simu_crash(struct nlm_nsm *nsm)
897 897 {
898 898 enum clnt_stat stat;
899 899
900 - sema_p(&nsm->ns_sem);
900 + mutex_enter(&nsm->ns_lock);
901 901 nlm_nsm_clnt_init(nsm->ns_handle, nsm);
902 902 stat = sm_simu_crash_1(NULL, NULL, nsm->ns_handle);
903 - sema_v(&nsm->ns_sem);
903 + mutex_exit(&nsm->ns_lock);
904 904
905 905 return (stat);
906 906 }
907 907
908 908 static enum clnt_stat
909 909 nlm_nsm_stat(struct nlm_nsm *nsm, int32_t *out_stat)
910 910 {
911 911 struct sm_name args;
912 912 struct sm_stat_res res;
913 913 enum clnt_stat stat;
914 914
915 915 args.mon_name = uts_nodename();
916 916 bzero(&res, sizeof (res));
917 917
918 - sema_p(&nsm->ns_sem);
918 + mutex_enter(&nsm->ns_lock);
919 919 nlm_nsm_clnt_init(nsm->ns_handle, nsm);
920 920 stat = sm_stat_1(&args, &res, nsm->ns_handle);
921 - sema_v(&nsm->ns_sem);
921 + mutex_exit(&nsm->ns_lock);
922 922
923 923 if (stat == RPC_SUCCESS)
924 924 *out_stat = res.state;
925 925
926 926 return (stat);
927 927 }
928 928
929 929 static enum clnt_stat
930 930 nlm_nsm_mon(struct nlm_nsm *nsm, char *hostname, uint16_t priv)
931 931 {
932 932 struct mon args;
933 933 struct sm_stat_res res;
934 934 enum clnt_stat stat;
935 935
|
↓ open down ↓ |
4 lines elided |
↑ open up ↑ |
936 936 bzero(&args, sizeof (args));
937 937 bzero(&res, sizeof (res));
938 938
939 939 args.mon_id.mon_name = hostname;
940 940 args.mon_id.my_id.my_name = uts_nodename();
941 941 args.mon_id.my_id.my_prog = NLM_PROG;
942 942 args.mon_id.my_id.my_vers = NLM_SM;
943 943 args.mon_id.my_id.my_proc = NLM_SM_NOTIFY1;
944 944 bcopy(&priv, args.priv, sizeof (priv));
945 945
946 - sema_p(&nsm->ns_sem);
946 + mutex_enter(&nsm->ns_lock);
947 947 nlm_nsm_clnt_init(nsm->ns_handle, nsm);
948 948 stat = sm_mon_1(&args, &res, nsm->ns_handle);
949 - sema_v(&nsm->ns_sem);
949 + mutex_exit(&nsm->ns_lock);
950 950
951 951 return (stat);
952 952 }
953 953
954 954 static enum clnt_stat
955 955 nlm_nsm_unmon(struct nlm_nsm *nsm, char *hostname)
956 956 {
957 957 struct mon_id args;
958 958 struct sm_stat res;
959 959 enum clnt_stat stat;
960 960
961 961 bzero(&args, sizeof (args));
962 962 bzero(&res, sizeof (res));
963 963
964 964 args.mon_name = hostname;
965 965 args.my_id.my_name = uts_nodename();
966 966 args.my_id.my_prog = NLM_PROG;
967 967 args.my_id.my_vers = NLM_SM;
968 968 args.my_id.my_proc = NLM_SM_NOTIFY1;
969 969
970 - sema_p(&nsm->ns_sem);
970 + mutex_enter(&nsm->ns_lock);
971 971 nlm_nsm_clnt_init(nsm->ns_handle, nsm);
972 972 stat = sm_unmon_1(&args, &res, nsm->ns_handle);
973 - sema_v(&nsm->ns_sem);
973 + mutex_exit(&nsm->ns_lock);
974 974
975 975 return (stat);
976 976 }
977 977
978 978 static enum clnt_stat
979 979 nlm_nsmaddr_reg(struct nlm_nsm *nsm, char *name, int family, netobj *address)
980 980 {
981 981 struct reg1args args = { 0 };
982 982 struct reg1res res = { 0 };
983 983 enum clnt_stat stat;
984 984
985 985 args.family = family;
986 986 args.name = name;
987 987 args.address = *address;
988 988
989 - sema_p(&nsm->ns_sem);
989 + mutex_enter(&nsm->ns_lock);
990 990 nlm_nsm_clnt_init(nsm->ns_addr_handle, nsm);
991 991 stat = nsmaddrproc1_reg_1(&args, &res, nsm->ns_addr_handle);
992 - sema_v(&nsm->ns_sem);
992 + mutex_exit(&nsm->ns_lock);
993 993
994 994 return (stat);
995 995 }
996 996
997 997 /*
998 998 * Get NLM vhold object corresponding to vnode "vp".
999 999 * If no such object was found, create a new one.
1000 1000 *
1001 1001 * The purpose of this function is to associate vhold
1002 1002 * object with given vnode, so that:
1003 1003 * 1) vnode is hold (VN_HOLD) while vhold object is alive.
1004 1004 * 2) host has a track of all vnodes it touched by lock
1005 1005 * or share operations. These vnodes are accessible
1006 1006 * via collection of vhold objects.
1007 1007 */
1008 1008 struct nlm_vhold *
1009 1009 nlm_vhold_get(struct nlm_host *hostp, vnode_t *vp)
1010 1010 {
1011 1011 struct nlm_vhold *nvp, *new_nvp = NULL;
1012 1012
1013 1013 mutex_enter(&hostp->nh_lock);
1014 1014 nvp = nlm_vhold_find_locked(hostp, vp);
1015 1015 if (nvp != NULL)
1016 1016 goto out;
1017 1017
1018 1018 /* nlm_vhold wasn't found, then create a new one */
1019 1019 mutex_exit(&hostp->nh_lock);
1020 1020 new_nvp = kmem_cache_alloc(nlm_vhold_cache, KM_SLEEP);
1021 1021
1022 1022 /*
1023 1023 * Check if another thread has already
1024 1024 * created the same nlm_vhold.
1025 1025 */
1026 1026 mutex_enter(&hostp->nh_lock);
1027 1027 nvp = nlm_vhold_find_locked(hostp, vp);
1028 1028 if (nvp == NULL) {
1029 1029 nvp = new_nvp;
1030 1030 new_nvp = NULL;
1031 1031
1032 1032 TAILQ_INIT(&nvp->nv_slreqs);
1033 1033 nvp->nv_vp = vp;
1034 1034 nvp->nv_refcnt = 1;
1035 1035 VN_HOLD(nvp->nv_vp);
1036 1036
1037 1037 VERIFY(mod_hash_insert(hostp->nh_vholds_by_vp,
1038 1038 (mod_hash_key_t)vp, (mod_hash_val_t)nvp) == 0);
1039 1039 TAILQ_INSERT_TAIL(&hostp->nh_vholds_list, nvp, nv_link);
1040 1040 }
1041 1041
1042 1042 out:
1043 1043 mutex_exit(&hostp->nh_lock);
1044 1044 if (new_nvp != NULL)
1045 1045 kmem_cache_free(nlm_vhold_cache, new_nvp);
1046 1046
1047 1047 return (nvp);
1048 1048 }
1049 1049
1050 1050 /*
1051 1051 * Drop a reference to vhold object nvp.
1052 1052 */
1053 1053 void
1054 1054 nlm_vhold_release(struct nlm_host *hostp, struct nlm_vhold *nvp)
1055 1055 {
1056 1056 if (nvp == NULL)
1057 1057 return;
1058 1058
1059 1059 mutex_enter(&hostp->nh_lock);
1060 1060 ASSERT(nvp->nv_refcnt > 0);
1061 1061 nvp->nv_refcnt--;
1062 1062
1063 1063 /*
1064 1064 * If these conditions are met, the vhold is obviously unused and we
1065 1065 * will destroy it. In a case either v_filocks and/or v_shrlocks is
1066 1066 * non-NULL the vhold might still be unused by the host, but it is
1067 1067 * expensive to check that. We defer such check until the host is
1068 1068 * idle. The expensive check is done in the NLM garbage collector.
1069 1069 */
1070 1070 if (nvp->nv_refcnt == 0 &&
1071 1071 nvp->nv_vp->v_filocks == NULL &&
1072 1072 nvp->nv_vp->v_shrlocks == NULL) {
1073 1073 nlm_vhold_destroy(hostp, nvp);
1074 1074 }
1075 1075
1076 1076 mutex_exit(&hostp->nh_lock);
1077 1077 }
1078 1078
1079 1079 /*
1080 1080 * Clean all locks and share reservations on the
1081 1081 * given vhold object that were acquired by the
1082 1082 * given sysid
1083 1083 */
1084 1084 static void
1085 1085 nlm_vhold_clean(struct nlm_vhold *nvp, int sysid)
1086 1086 {
1087 1087 cleanlocks(nvp->nv_vp, IGN_PID, sysid);
1088 1088 cleanshares_by_sysid(nvp->nv_vp, sysid);
1089 1089 }
1090 1090
1091 1091 static void
1092 1092 nlm_vhold_destroy(struct nlm_host *hostp, struct nlm_vhold *nvp)
1093 1093 {
1094 1094 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1095 1095
1096 1096 ASSERT(nvp->nv_refcnt == 0);
1097 1097 ASSERT(TAILQ_EMPTY(&nvp->nv_slreqs));
1098 1098
1099 1099 VERIFY(mod_hash_remove(hostp->nh_vholds_by_vp,
1100 1100 (mod_hash_key_t)nvp->nv_vp,
1101 1101 (mod_hash_val_t)&nvp) == 0);
1102 1102
1103 1103 TAILQ_REMOVE(&hostp->nh_vholds_list, nvp, nv_link);
1104 1104 VN_RELE(nvp->nv_vp);
1105 1105 nvp->nv_vp = NULL;
1106 1106
1107 1107 kmem_cache_free(nlm_vhold_cache, nvp);
1108 1108 }
1109 1109
1110 1110 /*
1111 1111 * Return TRUE if the given vhold is busy.
1112 1112 * Vhold object is considered to be "busy" when
1113 1113 * all the following conditions hold:
1114 1114 * 1) No one uses it at the moment;
1115 1115 * 2) It hasn't any locks;
1116 1116 * 3) It hasn't any share reservations;
1117 1117 */
1118 1118 static bool_t
1119 1119 nlm_vhold_busy(struct nlm_host *hostp, struct nlm_vhold *nvp)
1120 1120 {
1121 1121 vnode_t *vp;
1122 1122 int sysid;
1123 1123
1124 1124 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1125 1125
1126 1126 if (nvp->nv_refcnt > 0)
1127 1127 return (TRUE);
1128 1128
1129 1129 vp = nvp->nv_vp;
1130 1130 sysid = hostp->nh_sysid;
1131 1131 if (flk_has_remote_locks_for_sysid(vp, sysid) ||
1132 1132 shr_has_remote_shares(vp, sysid))
1133 1133 return (TRUE);
1134 1134
1135 1135 return (FALSE);
1136 1136 }
1137 1137
1138 1138 /* ARGSUSED */
1139 1139 static int
1140 1140 nlm_vhold_ctor(void *datap, void *cdrarg, int kmflags)
1141 1141 {
1142 1142 struct nlm_vhold *nvp = (struct nlm_vhold *)datap;
1143 1143
1144 1144 bzero(nvp, sizeof (*nvp));
1145 1145 return (0);
1146 1146 }
1147 1147
1148 1148 /* ARGSUSED */
1149 1149 static void
1150 1150 nlm_vhold_dtor(void *datap, void *cdrarg)
1151 1151 {
1152 1152 struct nlm_vhold *nvp = (struct nlm_vhold *)datap;
1153 1153
1154 1154 ASSERT(nvp->nv_refcnt == 0);
1155 1155 ASSERT(TAILQ_EMPTY(&nvp->nv_slreqs));
1156 1156 ASSERT(nvp->nv_vp == NULL);
1157 1157 }
1158 1158
1159 1159 struct nlm_vhold *
1160 1160 nlm_vhold_find_locked(struct nlm_host *hostp, const vnode_t *vp)
1161 1161 {
1162 1162 struct nlm_vhold *nvp = NULL;
1163 1163
1164 1164 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1165 1165 (void) mod_hash_find(hostp->nh_vholds_by_vp,
1166 1166 (mod_hash_key_t)vp,
1167 1167 (mod_hash_val_t)&nvp);
1168 1168
1169 1169 if (nvp != NULL)
1170 1170 nvp->nv_refcnt++;
1171 1171
1172 1172 return (nvp);
1173 1173 }
1174 1174
1175 1175 /*
1176 1176 * NLM host functions
1177 1177 */
1178 1178 static void
1179 1179 nlm_copy_netbuf(struct netbuf *dst, struct netbuf *src)
1180 1180 {
1181 1181 ASSERT(src->len <= src->maxlen);
1182 1182
1183 1183 dst->maxlen = src->maxlen;
1184 1184 dst->len = src->len;
1185 1185 dst->buf = kmem_zalloc(src->maxlen, KM_SLEEP);
1186 1186 bcopy(src->buf, dst->buf, src->len);
1187 1187 }
1188 1188
1189 1189 /* ARGSUSED */
1190 1190 static int
1191 1191 nlm_host_ctor(void *datap, void *cdrarg, int kmflags)
1192 1192 {
1193 1193 struct nlm_host *hostp = (struct nlm_host *)datap;
1194 1194
1195 1195 bzero(hostp, sizeof (*hostp));
1196 1196 return (0);
1197 1197 }
1198 1198
1199 1199 /* ARGSUSED */
1200 1200 static void
1201 1201 nlm_host_dtor(void *datap, void *cdrarg)
1202 1202 {
1203 1203 struct nlm_host *hostp = (struct nlm_host *)datap;
1204 1204 ASSERT(hostp->nh_refs == 0);
1205 1205 }
1206 1206
1207 1207 static void
1208 1208 nlm_host_unregister(struct nlm_globals *g, struct nlm_host *hostp)
1209 1209 {
1210 1210 ASSERT(hostp->nh_refs == 0);
1211 1211 ASSERT(hostp->nh_flags & NLM_NH_INIDLE);
1212 1212
1213 1213 avl_remove(&g->nlm_hosts_tree, hostp);
1214 1214 VERIFY(mod_hash_remove(g->nlm_hosts_hash,
1215 1215 (mod_hash_key_t)(uintptr_t)hostp->nh_sysid,
1216 1216 (mod_hash_val_t)&hostp) == 0);
1217 1217 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
1218 1218 hostp->nh_flags &= ~NLM_NH_INIDLE;
1219 1219 }
1220 1220
1221 1221 /*
1222 1222 * Free resources used by a host. This is called after the reference
1223 1223 * count has reached zero so it doesn't need to worry about locks.
1224 1224 */
1225 1225 static void
1226 1226 nlm_host_destroy(struct nlm_host *hostp)
1227 1227 {
1228 1228 ASSERT(hostp->nh_name != NULL);
1229 1229 ASSERT(hostp->nh_netid != NULL);
1230 1230 ASSERT(TAILQ_EMPTY(&hostp->nh_vholds_list));
1231 1231
1232 1232 strfree(hostp->nh_name);
1233 1233 strfree(hostp->nh_netid);
1234 1234 kmem_free(hostp->nh_addr.buf, hostp->nh_addr.maxlen);
1235 1235
1236 1236 if (hostp->nh_sysid != LM_NOSYSID)
1237 1237 nlm_sysid_free(hostp->nh_sysid);
1238 1238
1239 1239 nlm_rpc_cache_destroy(hostp);
1240 1240
1241 1241 ASSERT(TAILQ_EMPTY(&hostp->nh_vholds_list));
1242 1242 mod_hash_destroy_ptrhash(hostp->nh_vholds_by_vp);
1243 1243
1244 1244 mutex_destroy(&hostp->nh_lock);
1245 1245 cv_destroy(&hostp->nh_rpcb_cv);
1246 1246 cv_destroy(&hostp->nh_recl_cv);
1247 1247
1248 1248 kmem_cache_free(nlm_hosts_cache, hostp);
1249 1249 }
1250 1250
1251 1251 /*
1252 1252 * Cleanup SERVER-side state after a client restarts,
1253 1253 * or becomes unresponsive, or whatever.
1254 1254 *
1255 1255 * We unlock any active locks owned by the host.
1256 1256 * When rpc.lockd is shutting down,
1257 1257 * this function is called with newstate set to zero
1258 1258 * which allows us to cancel any pending async locks
1259 1259 * and clear the locking state.
1260 1260 *
1261 1261 * When "state" is 0, we don't update host's state,
1262 1262 * but cleanup all remote locks on the host.
1263 1263 * It's useful to call this function for resources
1264 1264 * cleanup.
1265 1265 */
1266 1266 void
1267 1267 nlm_host_notify_server(struct nlm_host *hostp, int32_t state)
1268 1268 {
1269 1269 struct nlm_vhold *nvp;
1270 1270 struct nlm_slreq *slr;
1271 1271 struct nlm_slreq_list slreqs2free;
1272 1272
1273 1273 TAILQ_INIT(&slreqs2free);
1274 1274 mutex_enter(&hostp->nh_lock);
1275 1275 if (state != 0)
1276 1276 hostp->nh_state = state;
1277 1277
1278 1278 TAILQ_FOREACH(nvp, &hostp->nh_vholds_list, nv_link) {
1279 1279
1280 1280 /* cleanup sleeping requests at first */
1281 1281 while ((slr = TAILQ_FIRST(&nvp->nv_slreqs)) != NULL) {
1282 1282 TAILQ_REMOVE(&nvp->nv_slreqs, slr, nsr_link);
1283 1283
1284 1284 /*
1285 1285 * Instead of freeing cancelled sleeping request
1286 1286 * here, we add it to the linked list created
1287 1287 * on the stack in order to do all frees outside
1288 1288 * the critical section.
1289 1289 */
1290 1290 TAILQ_INSERT_TAIL(&slreqs2free, slr, nsr_link);
1291 1291 }
1292 1292
1293 1293 nvp->nv_refcnt++;
1294 1294 mutex_exit(&hostp->nh_lock);
1295 1295
1296 1296 nlm_vhold_clean(nvp, hostp->nh_sysid);
1297 1297
1298 1298 mutex_enter(&hostp->nh_lock);
1299 1299 nvp->nv_refcnt--;
1300 1300 }
1301 1301
1302 1302 mutex_exit(&hostp->nh_lock);
1303 1303 while ((slr = TAILQ_FIRST(&slreqs2free)) != NULL) {
1304 1304 TAILQ_REMOVE(&slreqs2free, slr, nsr_link);
1305 1305 kmem_free(slr, sizeof (*slr));
1306 1306 }
1307 1307 }
1308 1308
1309 1309 /*
1310 1310 * Cleanup CLIENT-side state after a server restarts,
1311 1311 * or becomes unresponsive, or whatever.
1312 1312 *
1313 1313 * This is called by the local NFS statd when we receive a
1314 1314 * host state change notification. (also nlm_svc_stopping)
1315 1315 *
1316 1316 * Deal with a server restart. If we are stopping the
1317 1317 * NLM service, we'll have newstate == 0, and will just
1318 1318 * cancel all our client-side lock requests. Otherwise,
1319 1319 * start the "recovery" process to reclaim any locks
1320 1320 * we hold on this server.
1321 1321 */
1322 1322 void
1323 1323 nlm_host_notify_client(struct nlm_host *hostp, int32_t state)
1324 1324 {
1325 1325 mutex_enter(&hostp->nh_lock);
1326 1326 hostp->nh_state = state;
1327 1327 if (hostp->nh_flags & NLM_NH_RECLAIM) {
1328 1328 /*
1329 1329 * Either host's state is up to date or
1330 1330 * host is already in recovery.
1331 1331 */
1332 1332 mutex_exit(&hostp->nh_lock);
1333 1333 return;
1334 1334 }
1335 1335
1336 1336 hostp->nh_flags |= NLM_NH_RECLAIM;
1337 1337
1338 1338 /*
1339 1339 * Host will be released by the recovery thread,
1340 1340 * thus we need to increment refcount.
1341 1341 */
1342 1342 hostp->nh_refs++;
1343 1343 mutex_exit(&hostp->nh_lock);
1344 1344
1345 1345 (void) zthread_create(NULL, 0, nlm_reclaimer,
1346 1346 hostp, 0, minclsyspri);
1347 1347 }
1348 1348
1349 1349 /*
1350 1350 * The function is called when NLM client detects that
1351 1351 * server has entered in grace period and client needs
1352 1352 * to wait until reclamation process (if any) does
1353 1353 * its job.
1354 1354 */
1355 1355 int
1356 1356 nlm_host_wait_grace(struct nlm_host *hostp)
1357 1357 {
1358 1358 struct nlm_globals *g;
1359 1359 int error = 0;
1360 1360
1361 1361 g = zone_getspecific(nlm_zone_key, curzone);
1362 1362 mutex_enter(&hostp->nh_lock);
1363 1363
1364 1364 do {
1365 1365 int rc;
1366 1366
1367 1367 rc = cv_timedwait_sig(&hostp->nh_recl_cv,
1368 1368 &hostp->nh_lock, ddi_get_lbolt() +
1369 1369 SEC_TO_TICK(g->retrans_tmo));
1370 1370
1371 1371 if (rc == 0) {
1372 1372 error = EINTR;
1373 1373 break;
1374 1374 }
1375 1375 } while (hostp->nh_flags & NLM_NH_RECLAIM);
1376 1376
1377 1377 mutex_exit(&hostp->nh_lock);
1378 1378 return (error);
1379 1379 }
1380 1380
1381 1381 /*
1382 1382 * Create a new NLM host.
1383 1383 *
1384 1384 * NOTE: The in-kernel RPC (kRPC) subsystem uses TLI/XTI,
1385 1385 * which needs both a knetconfig and an address when creating
1386 1386 * endpoints. Thus host object stores both knetconfig and
1387 1387 * netid.
1388 1388 */
1389 1389 static struct nlm_host *
1390 1390 nlm_host_create(char *name, const char *netid,
1391 1391 struct knetconfig *knc, struct netbuf *naddr)
1392 1392 {
1393 1393 struct nlm_host *host;
1394 1394
1395 1395 host = kmem_cache_alloc(nlm_hosts_cache, KM_SLEEP);
1396 1396
1397 1397 mutex_init(&host->nh_lock, NULL, MUTEX_DEFAULT, NULL);
1398 1398 cv_init(&host->nh_rpcb_cv, NULL, CV_DEFAULT, NULL);
1399 1399 cv_init(&host->nh_recl_cv, NULL, CV_DEFAULT, NULL);
1400 1400
1401 1401 host->nh_sysid = LM_NOSYSID;
1402 1402 host->nh_refs = 1;
1403 1403 host->nh_name = strdup(name);
1404 1404 host->nh_netid = strdup(netid);
1405 1405 host->nh_knc = *knc;
1406 1406 nlm_copy_netbuf(&host->nh_addr, naddr);
1407 1407
1408 1408 host->nh_state = 0;
1409 1409 host->nh_rpcb_state = NRPCB_NEED_UPDATE;
1410 1410 host->nh_flags = 0;
1411 1411
1412 1412 host->nh_vholds_by_vp = mod_hash_create_ptrhash("nlm vholds hash",
1413 1413 32, mod_hash_null_valdtor, sizeof (vnode_t));
1414 1414
1415 1415 TAILQ_INIT(&host->nh_vholds_list);
1416 1416 TAILQ_INIT(&host->nh_rpchc);
1417 1417
1418 1418 return (host);
1419 1419 }
1420 1420
1421 1421 /*
1422 1422 * Cancel all client side sleeping locks owned by given host.
1423 1423 */
1424 1424 void
1425 1425 nlm_host_cancel_slocks(struct nlm_globals *g, struct nlm_host *hostp)
1426 1426 {
1427 1427 struct nlm_slock *nslp;
1428 1428
1429 1429 mutex_enter(&g->lock);
1430 1430 TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) {
1431 1431 if (nslp->nsl_host == hostp) {
1432 1432 nslp->nsl_state = NLM_SL_CANCELLED;
1433 1433 cv_broadcast(&nslp->nsl_cond);
1434 1434 }
1435 1435 }
1436 1436
1437 1437 mutex_exit(&g->lock);
1438 1438 }
1439 1439
1440 1440 /*
1441 1441 * Garbage collect stale vhold objects.
1442 1442 *
1443 1443 * In other words check whether vnodes that are
1444 1444 * held by vhold objects still have any locks
1445 1445 * or shares or still in use. If they aren't,
1446 1446 * just destroy them.
1447 1447 */
1448 1448 static void
1449 1449 nlm_host_gc_vholds(struct nlm_host *hostp)
1450 1450 {
1451 1451 struct nlm_vhold *nvp;
1452 1452
1453 1453 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1454 1454
1455 1455 nvp = TAILQ_FIRST(&hostp->nh_vholds_list);
1456 1456 while (nvp != NULL) {
1457 1457 struct nlm_vhold *nvp_tmp;
1458 1458
1459 1459 if (nlm_vhold_busy(hostp, nvp)) {
1460 1460 nvp = TAILQ_NEXT(nvp, nv_link);
1461 1461 continue;
1462 1462 }
1463 1463
1464 1464 nvp_tmp = TAILQ_NEXT(nvp, nv_link);
1465 1465 nlm_vhold_destroy(hostp, nvp);
1466 1466 nvp = nvp_tmp;
1467 1467 }
1468 1468 }
1469 1469
1470 1470 /*
1471 1471 * Check whether the given host has any
1472 1472 * server side locks or share reservations.
1473 1473 */
1474 1474 static bool_t
1475 1475 nlm_host_has_srv_locks(struct nlm_host *hostp)
1476 1476 {
1477 1477 /*
1478 1478 * It's cheap and simple: if server has
1479 1479 * any locks/shares there must be vhold
1480 1480 * object storing the affected vnode.
1481 1481 *
1482 1482 * NOTE: We don't need to check sleeping
1483 1483 * locks on the server side, because if
1484 1484 * server side sleeping lock is alive,
1485 1485 * there must be a vhold object corresponding
1486 1486 * to target vnode.
1487 1487 */
1488 1488 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1489 1489 if (!TAILQ_EMPTY(&hostp->nh_vholds_list))
1490 1490 return (TRUE);
1491 1491
1492 1492 return (FALSE);
1493 1493 }
1494 1494
1495 1495 /*
1496 1496 * Check whether the given host has any client side
1497 1497 * locks or share reservations.
1498 1498 */
1499 1499 static bool_t
1500 1500 nlm_host_has_cli_locks(struct nlm_host *hostp)
1501 1501 {
1502 1502 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1503 1503
1504 1504 /*
1505 1505 * XXX: It's not the way I'd like to do the check,
1506 1506 * because flk_sysid_has_locks() can be very
1507 1507 * expensive by design. Unfortunatelly it iterates
1508 1508 * through all locks on the system, doesn't matter
1509 1509 * were they made on remote system via NLM or
1510 1510 * on local system via reclock. To understand the
1511 1511 * problem, consider that there're dozens of thousands
1512 1512 * of locks that are made on some ZFS dataset. And there's
1513 1513 * another dataset shared by NFS where NLM client had locks
1514 1514 * some time ago, but doesn't have them now.
1515 1515 * In this case flk_sysid_has_locks() will iterate
1516 1516 * thrught dozens of thousands locks until it returns us
1517 1517 * FALSE.
1518 1518 * Oh, I hope that in shiny future somebody will make
1519 1519 * local lock manager (os/flock.c) better, so that
1520 1520 * it'd be more friedly to remote locks and
1521 1521 * flk_sysid_has_locks() wouldn't be so expensive.
1522 1522 */
1523 1523 if (flk_sysid_has_locks(hostp->nh_sysid |
1524 1524 LM_SYSID_CLIENT, FLK_QUERY_ACTIVE))
1525 1525 return (TRUE);
1526 1526
1527 1527 /*
1528 1528 * Check whether host has any share reservations
1529 1529 * registered on the client side.
1530 1530 */
1531 1531 if (hostp->nh_shrlist != NULL)
1532 1532 return (TRUE);
1533 1533
1534 1534 return (FALSE);
1535 1535 }
1536 1536
1537 1537 /*
1538 1538 * Determine whether the given host owns any
1539 1539 * locks or share reservations.
1540 1540 */
1541 1541 static bool_t
1542 1542 nlm_host_has_locks(struct nlm_host *hostp)
1543 1543 {
1544 1544 if (nlm_host_has_srv_locks(hostp))
1545 1545 return (TRUE);
1546 1546
1547 1547 return (nlm_host_has_cli_locks(hostp));
1548 1548 }
1549 1549
1550 1550 /*
1551 1551 * This function compares only addresses of two netbufs
1552 1552 * that belong to NC_TCP[6] or NC_UDP[6] protofamily.
1553 1553 * Port part of netbuf is ignored.
1554 1554 *
1555 1555 * Return values:
1556 1556 * -1: nb1's address is "smaller" than nb2's
1557 1557 * 0: addresses are equal
1558 1558 * 1: nb1's address is "greater" than nb2's
1559 1559 */
1560 1560 static int
1561 1561 nlm_netbuf_addrs_cmp(struct netbuf *nb1, struct netbuf *nb2)
1562 1562 {
1563 1563 union nlm_addr {
1564 1564 struct sockaddr sa;
1565 1565 struct sockaddr_in sin;
1566 1566 struct sockaddr_in6 sin6;
1567 1567 } *na1, *na2;
1568 1568 int res;
1569 1569
1570 1570 /* LINTED E_BAD_PTR_CAST_ALIGN */
1571 1571 na1 = (union nlm_addr *)nb1->buf;
1572 1572 /* LINTED E_BAD_PTR_CAST_ALIGN */
1573 1573 na2 = (union nlm_addr *)nb2->buf;
1574 1574
1575 1575 if (na1->sa.sa_family < na2->sa.sa_family)
1576 1576 return (-1);
1577 1577 if (na1->sa.sa_family > na2->sa.sa_family)
1578 1578 return (1);
1579 1579
1580 1580 switch (na1->sa.sa_family) {
1581 1581 case AF_INET:
1582 1582 res = memcmp(&na1->sin.sin_addr, &na2->sin.sin_addr,
1583 1583 sizeof (na1->sin.sin_addr));
1584 1584 break;
1585 1585 case AF_INET6:
1586 1586 res = memcmp(&na1->sin6.sin6_addr, &na2->sin6.sin6_addr,
1587 1587 sizeof (na1->sin6.sin6_addr));
1588 1588 break;
1589 1589 default:
1590 1590 VERIFY(0);
1591 1591 return (0);
1592 1592 }
1593 1593
1594 1594 return (SIGN(res));
1595 1595 }
1596 1596
1597 1597 /*
1598 1598 * Compare two nlm hosts.
1599 1599 * Return values:
1600 1600 * -1: host1 is "smaller" than host2
1601 1601 * 0: host1 is equal to host2
1602 1602 * 1: host1 is "greater" than host2
1603 1603 */
1604 1604 int
1605 1605 nlm_host_cmp(const void *p1, const void *p2)
1606 1606 {
1607 1607 struct nlm_host *h1 = (struct nlm_host *)p1;
1608 1608 struct nlm_host *h2 = (struct nlm_host *)p2;
1609 1609 int res;
1610 1610
1611 1611 res = strcmp(h1->nh_netid, h2->nh_netid);
1612 1612 if (res != 0)
1613 1613 return (SIGN(res));
1614 1614
1615 1615 res = nlm_netbuf_addrs_cmp(&h1->nh_addr, &h2->nh_addr);
1616 1616 return (res);
1617 1617 }
1618 1618
1619 1619 /*
1620 1620 * Find the host specified by... (see below)
1621 1621 * If found, increment the ref count.
1622 1622 */
1623 1623 static struct nlm_host *
1624 1624 nlm_host_find_locked(struct nlm_globals *g, const char *netid,
1625 1625 struct netbuf *naddr, avl_index_t *wherep)
1626 1626 {
1627 1627 struct nlm_host *hostp, key;
1628 1628 avl_index_t pos;
1629 1629
1630 1630 ASSERT(MUTEX_HELD(&g->lock));
1631 1631
1632 1632 key.nh_netid = (char *)netid;
1633 1633 key.nh_addr.buf = naddr->buf;
1634 1634 key.nh_addr.len = naddr->len;
1635 1635 key.nh_addr.maxlen = naddr->maxlen;
1636 1636
1637 1637 hostp = avl_find(&g->nlm_hosts_tree, &key, &pos);
1638 1638
1639 1639 if (hostp != NULL) {
1640 1640 /*
1641 1641 * Host is inuse now. Remove it from idle
1642 1642 * hosts list if needed.
1643 1643 */
1644 1644 if (hostp->nh_flags & NLM_NH_INIDLE) {
1645 1645 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
1646 1646 hostp->nh_flags &= ~NLM_NH_INIDLE;
1647 1647 }
1648 1648
1649 1649 hostp->nh_refs++;
1650 1650 }
1651 1651 if (wherep != NULL)
1652 1652 *wherep = pos;
1653 1653
1654 1654 return (hostp);
1655 1655 }
1656 1656
1657 1657 /*
1658 1658 * Find NLM host for the given name and address.
1659 1659 */
1660 1660 struct nlm_host *
1661 1661 nlm_host_find(struct nlm_globals *g, const char *netid,
1662 1662 struct netbuf *addr)
1663 1663 {
1664 1664 struct nlm_host *hostp = NULL;
1665 1665
1666 1666 mutex_enter(&g->lock);
1667 1667 if (g->run_status != NLM_ST_UP)
1668 1668 goto out;
1669 1669
1670 1670 hostp = nlm_host_find_locked(g, netid, addr, NULL);
1671 1671
1672 1672 out:
1673 1673 mutex_exit(&g->lock);
1674 1674 return (hostp);
1675 1675 }
1676 1676
1677 1677
1678 1678 /*
1679 1679 * Find or create an NLM host for the given name and address.
1680 1680 *
1681 1681 * The remote host is determined by all of: name, netid, address.
1682 1682 * Note that the netid is whatever nlm_svc_add_ep() gave to
1683 1683 * svc_tli_kcreate() for the service binding. If any of these
1684 1684 * are different, allocate a new host (new sysid).
1685 1685 */
1686 1686 struct nlm_host *
1687 1687 nlm_host_findcreate(struct nlm_globals *g, char *name,
1688 1688 const char *netid, struct netbuf *addr)
1689 1689 {
1690 1690 int err;
1691 1691 struct nlm_host *host, *newhost = NULL;
1692 1692 struct knetconfig knc;
1693 1693 avl_index_t where;
1694 1694
1695 1695 mutex_enter(&g->lock);
1696 1696 if (g->run_status != NLM_ST_UP) {
1697 1697 mutex_exit(&g->lock);
1698 1698 return (NULL);
1699 1699 }
1700 1700
1701 1701 host = nlm_host_find_locked(g, netid, addr, NULL);
1702 1702 mutex_exit(&g->lock);
1703 1703 if (host != NULL)
1704 1704 return (host);
1705 1705
1706 1706 err = nlm_knc_from_netid(netid, &knc);
1707 1707 if (err != 0)
1708 1708 return (NULL);
1709 1709 /*
1710 1710 * Do allocations (etc.) outside of mutex,
1711 1711 * and then check again before inserting.
1712 1712 */
1713 1713 newhost = nlm_host_create(name, netid, &knc, addr);
1714 1714 newhost->nh_sysid = nlm_sysid_alloc();
1715 1715 if (newhost->nh_sysid == LM_NOSYSID)
1716 1716 goto out;
1717 1717
1718 1718 mutex_enter(&g->lock);
1719 1719 host = nlm_host_find_locked(g, netid, addr, &where);
1720 1720 if (host == NULL) {
1721 1721 host = newhost;
1722 1722 newhost = NULL;
1723 1723
1724 1724 /*
1725 1725 * Insert host to the hosts AVL tree that is
1726 1726 * used to lookup by <netid, address> pair.
1727 1727 */
1728 1728 avl_insert(&g->nlm_hosts_tree, host, where);
1729 1729
1730 1730 /*
1731 1731 * Insert host to the hosts hash table that is
1732 1732 * used to lookup host by sysid.
1733 1733 */
1734 1734 VERIFY(mod_hash_insert(g->nlm_hosts_hash,
1735 1735 (mod_hash_key_t)(uintptr_t)host->nh_sysid,
1736 1736 (mod_hash_val_t)host) == 0);
1737 1737 }
1738 1738
1739 1739 mutex_exit(&g->lock);
1740 1740
1741 1741 out:
1742 1742 if (newhost != NULL) {
1743 1743 /*
1744 1744 * We do not need the preallocated nlm_host
1745 1745 * so decrement the reference counter
1746 1746 * and destroy it.
1747 1747 */
1748 1748 newhost->nh_refs--;
1749 1749 nlm_host_destroy(newhost);
1750 1750 }
1751 1751
1752 1752 return (host);
1753 1753 }
1754 1754
1755 1755 /*
1756 1756 * Find the NLM host that matches the value of 'sysid'.
1757 1757 * If found, return it with a new ref,
1758 1758 * else return NULL.
1759 1759 */
1760 1760 struct nlm_host *
1761 1761 nlm_host_find_by_sysid(struct nlm_globals *g, sysid_t sysid)
1762 1762 {
1763 1763 struct nlm_host *hostp = NULL;
1764 1764
1765 1765 mutex_enter(&g->lock);
1766 1766 if (g->run_status != NLM_ST_UP)
1767 1767 goto out;
1768 1768
1769 1769 (void) mod_hash_find(g->nlm_hosts_hash,
1770 1770 (mod_hash_key_t)(uintptr_t)sysid,
1771 1771 (mod_hash_val_t)&hostp);
1772 1772
1773 1773 if (hostp == NULL)
1774 1774 goto out;
1775 1775
1776 1776 /*
1777 1777 * Host is inuse now. Remove it
1778 1778 * from idle hosts list if needed.
1779 1779 */
1780 1780 if (hostp->nh_flags & NLM_NH_INIDLE) {
1781 1781 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
1782 1782 hostp->nh_flags &= ~NLM_NH_INIDLE;
1783 1783 }
1784 1784
1785 1785 hostp->nh_refs++;
1786 1786
1787 1787 out:
1788 1788 mutex_exit(&g->lock);
1789 1789 return (hostp);
1790 1790 }
1791 1791
1792 1792 /*
1793 1793 * Release the given host.
1794 1794 * I.e. drop a reference that was taken earlier by one of
1795 1795 * the following functions: nlm_host_findcreate(), nlm_host_find(),
1796 1796 * nlm_host_find_by_sysid().
1797 1797 *
1798 1798 * When the very last reference is dropped, host is moved to
1799 1799 * so-called "idle state". All hosts that are in idle state
1800 1800 * have an idle timeout. If timeout is expired, GC thread
1801 1801 * checks whether hosts have any locks and if they heven't
1802 1802 * any, it removes them.
1803 1803 * NOTE: only unused hosts can be in idle state.
1804 1804 */
1805 1805 static void
1806 1806 nlm_host_release_locked(struct nlm_globals *g, struct nlm_host *hostp)
1807 1807 {
1808 1808 if (hostp == NULL)
1809 1809 return;
1810 1810
1811 1811 ASSERT(MUTEX_HELD(&g->lock));
1812 1812 ASSERT(hostp->nh_refs > 0);
1813 1813
1814 1814 hostp->nh_refs--;
1815 1815 if (hostp->nh_refs != 0)
1816 1816 return;
1817 1817
1818 1818 /*
1819 1819 * The very last reference to the host was dropped,
1820 1820 * thus host is unused now. Set its idle timeout
1821 1821 * and move it to the idle hosts LRU list.
1822 1822 */
1823 1823 hostp->nh_idle_timeout = ddi_get_lbolt() +
1824 1824 SEC_TO_TICK(g->cn_idle_tmo);
1825 1825
1826 1826 ASSERT((hostp->nh_flags & NLM_NH_INIDLE) == 0);
1827 1827 TAILQ_INSERT_TAIL(&g->nlm_idle_hosts, hostp, nh_link);
1828 1828 hostp->nh_flags |= NLM_NH_INIDLE;
1829 1829 }
1830 1830
1831 1831 void
1832 1832 nlm_host_release(struct nlm_globals *g, struct nlm_host *hostp)
1833 1833 {
1834 1834 if (hostp == NULL)
1835 1835 return;
1836 1836
1837 1837 mutex_enter(&g->lock);
1838 1838 nlm_host_release_locked(g, hostp);
1839 1839 mutex_exit(&g->lock);
1840 1840 }
1841 1841
1842 1842 /*
1843 1843 * Unregister this NLM host (NFS client) with the local statd
1844 1844 * due to idleness (no locks held for a while).
1845 1845 */
1846 1846 void
1847 1847 nlm_host_unmonitor(struct nlm_globals *g, struct nlm_host *host)
1848 1848 {
1849 1849 enum clnt_stat stat;
1850 1850
1851 1851 VERIFY(host->nh_refs == 0);
1852 1852 if (!(host->nh_flags & NLM_NH_MONITORED))
1853 1853 return;
1854 1854
1855 1855 host->nh_flags &= ~NLM_NH_MONITORED;
1856 1856
1857 1857 if (ZONE_IS_BRANDED(curzone) && ZBROP(curzone)->b_rpc_statd != NULL) {
1858 1858 ZBROP(curzone)->b_rpc_statd(SM_UNMON, g, host);
1859 1859 return;
1860 1860 }
1861 1861
1862 1862 stat = nlm_nsm_unmon(&g->nlm_nsm, host->nh_name);
1863 1863 if (stat != RPC_SUCCESS) {
1864 1864 NLM_WARN("NLM: Failed to contact statd, stat=%d\n", stat);
1865 1865 return;
1866 1866 }
1867 1867 }
1868 1868
1869 1869 /*
1870 1870 * Ask the local NFS statd to begin monitoring this host.
1871 1871 * It will call us back when that host restarts, using the
1872 1872 * prog,vers,proc specified below, i.e. NLM_SM_NOTIFY1,
1873 1873 * which is handled in nlm_do_notify1().
1874 1874 */
1875 1875 void
1876 1876 nlm_host_monitor(struct nlm_globals *g, struct nlm_host *host, int state)
1877 1877 {
1878 1878 int family;
1879 1879 netobj obj;
1880 1880 enum clnt_stat stat;
1881 1881
1882 1882 if (state != 0 && host->nh_state == 0) {
1883 1883 /*
1884 1884 * This is the first time we have seen an NSM state
1885 1885 * Value for this host. We record it here to help
1886 1886 * detect host reboots.
1887 1887 */
1888 1888 host->nh_state = state;
1889 1889 }
1890 1890
1891 1891 mutex_enter(&host->nh_lock);
1892 1892 if (host->nh_flags & NLM_NH_MONITORED) {
1893 1893 mutex_exit(&host->nh_lock);
1894 1894 return;
1895 1895 }
1896 1896
1897 1897 host->nh_flags |= NLM_NH_MONITORED;
1898 1898 mutex_exit(&host->nh_lock);
1899 1899
1900 1900 if (ZONE_IS_BRANDED(curzone) && ZBROP(curzone)->b_rpc_statd != NULL) {
1901 1901 ZBROP(curzone)->b_rpc_statd(SM_MON, g, host);
1902 1902 return;
1903 1903 }
1904 1904
1905 1905 /*
1906 1906 * Before we begin monitoring the host register the network address
1907 1907 * associated with this hostname.
1908 1908 */
1909 1909 nlm_netbuf_to_netobj(&host->nh_addr, &family, &obj);
1910 1910 stat = nlm_nsmaddr_reg(&g->nlm_nsm, host->nh_name, family, &obj);
1911 1911 if (stat != RPC_SUCCESS) {
1912 1912 NLM_WARN("Failed to register address, stat=%d\n", stat);
1913 1913 mutex_enter(&g->lock);
1914 1914 host->nh_flags &= ~NLM_NH_MONITORED;
1915 1915 mutex_exit(&g->lock);
1916 1916
1917 1917 return;
1918 1918 }
1919 1919
1920 1920 /*
1921 1921 * Tell statd how to call us with status updates for
1922 1922 * this host. Updates arrive via nlm_do_notify1().
1923 1923 *
1924 1924 * We put our assigned system ID value in the priv field to
1925 1925 * make it simpler to find the host if we are notified of a
1926 1926 * host restart.
1927 1927 */
1928 1928 stat = nlm_nsm_mon(&g->nlm_nsm, host->nh_name, host->nh_sysid);
1929 1929 if (stat != RPC_SUCCESS) {
1930 1930 NLM_WARN("Failed to contact local NSM, stat=%d\n", stat);
1931 1931 mutex_enter(&g->lock);
1932 1932 host->nh_flags &= ~NLM_NH_MONITORED;
1933 1933 mutex_exit(&g->lock);
1934 1934
1935 1935 return;
1936 1936 }
1937 1937 }
1938 1938
1939 1939 int
1940 1940 nlm_host_get_state(struct nlm_host *hostp)
1941 1941 {
1942 1942
1943 1943 return (hostp->nh_state);
1944 1944 }
1945 1945
1946 1946 /*
1947 1947 * NLM client/server sleeping locks
1948 1948 */
1949 1949
1950 1950 /*
1951 1951 * Register client side sleeping lock.
1952 1952 *
1953 1953 * Our client code calls this to keep information
1954 1954 * about sleeping lock somewhere. When it receives
1955 1955 * grant callback from server or when it just
1956 1956 * needs to remove all sleeping locks from vnode,
1957 1957 * it uses this information for remove/apply lock
1958 1958 * properly.
1959 1959 */
1960 1960 struct nlm_slock *
1961 1961 nlm_slock_register(
1962 1962 struct nlm_globals *g,
1963 1963 struct nlm_host *host,
1964 1964 struct nlm4_lock *lock,
1965 1965 struct vnode *vp)
1966 1966 {
1967 1967 struct nlm_slock *nslp;
1968 1968
1969 1969 nslp = kmem_zalloc(sizeof (*nslp), KM_SLEEP);
1970 1970 cv_init(&nslp->nsl_cond, NULL, CV_DEFAULT, NULL);
1971 1971 nslp->nsl_lock = *lock;
1972 1972 nlm_copy_netobj(&nslp->nsl_fh, &nslp->nsl_lock.fh);
1973 1973 nslp->nsl_state = NLM_SL_BLOCKED;
1974 1974 nslp->nsl_host = host;
1975 1975 nslp->nsl_vp = vp;
1976 1976
1977 1977 mutex_enter(&g->lock);
1978 1978 TAILQ_INSERT_TAIL(&g->nlm_slocks, nslp, nsl_link);
1979 1979 mutex_exit(&g->lock);
1980 1980
1981 1981 return (nslp);
1982 1982 }
1983 1983
1984 1984 /*
1985 1985 * Remove this lock from the wait list and destroy it.
1986 1986 */
1987 1987 void
1988 1988 nlm_slock_unregister(struct nlm_globals *g, struct nlm_slock *nslp)
1989 1989 {
1990 1990 mutex_enter(&g->lock);
1991 1991 TAILQ_REMOVE(&g->nlm_slocks, nslp, nsl_link);
1992 1992 mutex_exit(&g->lock);
1993 1993
1994 1994 kmem_free(nslp->nsl_fh.n_bytes, nslp->nsl_fh.n_len);
1995 1995 cv_destroy(&nslp->nsl_cond);
1996 1996 kmem_free(nslp, sizeof (*nslp));
1997 1997 }
1998 1998
1999 1999 /*
2000 2000 * Wait for a granted callback or cancellation event
2001 2001 * for a sleeping lock.
2002 2002 *
2003 2003 * If a signal interrupted the wait or if the lock
2004 2004 * was cancelled, return EINTR - the caller must arrange to send
2005 2005 * a cancellation to the server.
2006 2006 *
2007 2007 * If timeout occurred, return ETIMEDOUT - the caller must
2008 2008 * resend the lock request to the server.
2009 2009 *
2010 2010 * On success return 0.
2011 2011 */
2012 2012 int
2013 2013 nlm_slock_wait(struct nlm_globals *g,
2014 2014 struct nlm_slock *nslp, uint_t timeo_secs)
2015 2015 {
2016 2016 clock_t timeo_ticks;
2017 2017 int cv_res, error;
2018 2018
2019 2019 /*
2020 2020 * If the granted message arrived before we got here,
2021 2021 * nslp->nsl_state will be NLM_SL_GRANTED - in that case don't sleep.
2022 2022 */
2023 2023 cv_res = 1;
2024 2024 timeo_ticks = ddi_get_lbolt() + SEC_TO_TICK(timeo_secs);
2025 2025
2026 2026 mutex_enter(&g->lock);
2027 2027 while (nslp->nsl_state == NLM_SL_BLOCKED && cv_res > 0) {
2028 2028 cv_res = cv_timedwait_sig(&nslp->nsl_cond,
2029 2029 &g->lock, timeo_ticks);
2030 2030 }
2031 2031
2032 2032 /*
2033 2033 * No matter why we wake up, if the lock was
2034 2034 * cancelled, let the function caller to know
2035 2035 * about it by returning EINTR.
2036 2036 */
2037 2037 if (nslp->nsl_state == NLM_SL_CANCELLED) {
2038 2038 error = EINTR;
2039 2039 goto out;
2040 2040 }
2041 2041
2042 2042 if (cv_res <= 0) {
2043 2043 /* We were woken up either by timeout or by interrupt */
2044 2044 error = (cv_res < 0) ? ETIMEDOUT : EINTR;
2045 2045
2046 2046 /*
2047 2047 * The granted message may arrive after the
2048 2048 * interrupt/timeout but before we manage to lock the
2049 2049 * mutex. Detect this by examining nslp.
2050 2050 */
2051 2051 if (nslp->nsl_state == NLM_SL_GRANTED)
2052 2052 error = 0;
2053 2053 } else { /* Awaken via cv_signal()/cv_broadcast() or didn't block */
2054 2054 error = 0;
2055 2055 VERIFY(nslp->nsl_state == NLM_SL_GRANTED);
2056 2056 }
2057 2057
2058 2058 out:
2059 2059 mutex_exit(&g->lock);
2060 2060 return (error);
2061 2061 }
2062 2062
2063 2063 /*
2064 2064 * Mark client side sleeping lock as granted
2065 2065 * and wake up a process blocked on the lock.
2066 2066 * Called from server side NLM_GRANT handler.
2067 2067 *
2068 2068 * If sleeping lock is found return 0, otherwise
2069 2069 * return ENOENT.
2070 2070 */
2071 2071 int
2072 2072 nlm_slock_grant(struct nlm_globals *g,
2073 2073 struct nlm_host *hostp, struct nlm4_lock *alock)
2074 2074 {
2075 2075 struct nlm_slock *nslp;
2076 2076 int error = ENOENT;
2077 2077
2078 2078 mutex_enter(&g->lock);
2079 2079 TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) {
2080 2080 if ((nslp->nsl_state != NLM_SL_BLOCKED) ||
2081 2081 (nslp->nsl_host != hostp))
2082 2082 continue;
2083 2083
2084 2084 if (alock->svid == nslp->nsl_lock.svid &&
2085 2085 alock->l_offset == nslp->nsl_lock.l_offset &&
2086 2086 alock->l_len == nslp->nsl_lock.l_len &&
2087 2087 alock->fh.n_len == nslp->nsl_lock.fh.n_len &&
2088 2088 bcmp(alock->fh.n_bytes, nslp->nsl_lock.fh.n_bytes,
2089 2089 nslp->nsl_lock.fh.n_len) == 0) {
2090 2090 nslp->nsl_state = NLM_SL_GRANTED;
2091 2091 cv_broadcast(&nslp->nsl_cond);
2092 2092 error = 0;
2093 2093 break;
2094 2094 }
2095 2095 }
2096 2096
2097 2097 mutex_exit(&g->lock);
2098 2098 return (error);
2099 2099 }
2100 2100
2101 2101 /*
2102 2102 * Register sleeping lock request corresponding to
2103 2103 * flp on the given vhold object.
2104 2104 * On success function returns 0, otherwise (if
2105 2105 * lock request with the same flp is already
2106 2106 * registered) function returns EEXIST.
2107 2107 */
2108 2108 int
2109 2109 nlm_slreq_register(struct nlm_host *hostp, struct nlm_vhold *nvp,
2110 2110 struct flock64 *flp)
2111 2111 {
2112 2112 struct nlm_slreq *slr, *new_slr = NULL;
2113 2113 int ret = EEXIST;
2114 2114
2115 2115 mutex_enter(&hostp->nh_lock);
2116 2116 slr = nlm_slreq_find_locked(hostp, nvp, flp);
2117 2117 if (slr != NULL)
2118 2118 goto out;
2119 2119
2120 2120 mutex_exit(&hostp->nh_lock);
2121 2121 new_slr = kmem_zalloc(sizeof (*slr), KM_SLEEP);
2122 2122 bcopy(flp, &new_slr->nsr_fl, sizeof (*flp));
2123 2123
2124 2124 mutex_enter(&hostp->nh_lock);
2125 2125 slr = nlm_slreq_find_locked(hostp, nvp, flp);
2126 2126 if (slr == NULL) {
2127 2127 slr = new_slr;
2128 2128 new_slr = NULL;
2129 2129 ret = 0;
2130 2130
2131 2131 TAILQ_INSERT_TAIL(&nvp->nv_slreqs, slr, nsr_link);
2132 2132 }
2133 2133
2134 2134 out:
2135 2135 mutex_exit(&hostp->nh_lock);
2136 2136 if (new_slr != NULL)
2137 2137 kmem_free(new_slr, sizeof (*new_slr));
2138 2138
2139 2139 return (ret);
2140 2140 }
2141 2141
2142 2142 /*
2143 2143 * Unregister sleeping lock request corresponding
2144 2144 * to flp from the given vhold object.
2145 2145 * On success function returns 0, otherwise (if
2146 2146 * lock request corresponding to flp isn't found
2147 2147 * on the given vhold) function returns ENOENT.
2148 2148 */
2149 2149 int
2150 2150 nlm_slreq_unregister(struct nlm_host *hostp, struct nlm_vhold *nvp,
2151 2151 struct flock64 *flp)
2152 2152 {
2153 2153 struct nlm_slreq *slr;
2154 2154
2155 2155 mutex_enter(&hostp->nh_lock);
2156 2156 slr = nlm_slreq_find_locked(hostp, nvp, flp);
2157 2157 if (slr == NULL) {
2158 2158 mutex_exit(&hostp->nh_lock);
2159 2159 return (ENOENT);
2160 2160 }
2161 2161
2162 2162 TAILQ_REMOVE(&nvp->nv_slreqs, slr, nsr_link);
2163 2163 mutex_exit(&hostp->nh_lock);
2164 2164
2165 2165 kmem_free(slr, sizeof (*slr));
2166 2166 return (0);
2167 2167 }
2168 2168
2169 2169 /*
2170 2170 * Find sleeping lock request on the given vhold object by flp.
2171 2171 */
2172 2172 struct nlm_slreq *
2173 2173 nlm_slreq_find_locked(struct nlm_host *hostp, struct nlm_vhold *nvp,
2174 2174 struct flock64 *flp)
2175 2175 {
2176 2176 struct nlm_slreq *slr = NULL;
2177 2177
2178 2178 ASSERT(MUTEX_HELD(&hostp->nh_lock));
2179 2179 TAILQ_FOREACH(slr, &nvp->nv_slreqs, nsr_link) {
2180 2180 if (slr->nsr_fl.l_start == flp->l_start &&
2181 2181 slr->nsr_fl.l_len == flp->l_len &&
2182 2182 slr->nsr_fl.l_pid == flp->l_pid &&
2183 2183 slr->nsr_fl.l_type == flp->l_type)
2184 2184 break;
2185 2185 }
2186 2186
2187 2187 return (slr);
2188 2188 }
2189 2189
2190 2190 /*
2191 2191 * NLM tracks active share reservations made on the client side.
2192 2192 * It needs to have a track of share reservations for two purposes
2193 2193 * 1) to determine if nlm_host is busy (if it has active locks and/or
2194 2194 * share reservations, it is)
2195 2195 * 2) to recover active share reservations when NLM server reports
2196 2196 * that it has rebooted.
2197 2197 *
2198 2198 * Unfortunately Illumos local share reservations manager (see os/share.c)
2199 2199 * doesn't have an ability to lookup all reservations on the system
2200 2200 * by sysid (like local lock manager) or get all reservations by sysid.
2201 2201 * It tracks reservations per vnode and is able to get/looup them
2202 2202 * on particular vnode. It's not what NLM needs. Thus it has that ugly
2203 2203 * share reservations tracking scheme.
2204 2204 */
2205 2205
2206 2206 void
2207 2207 nlm_shres_track(struct nlm_host *hostp, vnode_t *vp, struct shrlock *shrp)
2208 2208 {
2209 2209 struct nlm_shres *nsp, *nsp_new;
2210 2210
2211 2211 /*
2212 2212 * NFS code must fill the s_owner, so that
2213 2213 * s_own_len is never 0.
2214 2214 */
2215 2215 ASSERT(shrp->s_own_len > 0);
2216 2216 nsp_new = nlm_shres_create_item(shrp, vp);
2217 2217
2218 2218 mutex_enter(&hostp->nh_lock);
2219 2219 for (nsp = hostp->nh_shrlist; nsp != NULL; nsp = nsp->ns_next)
2220 2220 if (nsp->ns_vp == vp && nlm_shres_equal(shrp, nsp->ns_shr))
2221 2221 break;
2222 2222
2223 2223 if (nsp != NULL) {
2224 2224 /*
2225 2225 * Found a duplicate. Do nothing.
2226 2226 */
2227 2227
2228 2228 goto out;
2229 2229 }
2230 2230
2231 2231 nsp = nsp_new;
2232 2232 nsp_new = NULL;
2233 2233 nsp->ns_next = hostp->nh_shrlist;
2234 2234 hostp->nh_shrlist = nsp;
2235 2235
2236 2236 out:
2237 2237 mutex_exit(&hostp->nh_lock);
2238 2238 if (nsp_new != NULL)
2239 2239 nlm_shres_destroy_item(nsp_new);
2240 2240 }
2241 2241
2242 2242 void
2243 2243 nlm_shres_untrack(struct nlm_host *hostp, vnode_t *vp, struct shrlock *shrp)
2244 2244 {
2245 2245 struct nlm_shres *nsp, *nsp_prev = NULL;
2246 2246
2247 2247 mutex_enter(&hostp->nh_lock);
2248 2248 nsp = hostp->nh_shrlist;
2249 2249 while (nsp != NULL) {
2250 2250 if (nsp->ns_vp == vp && nlm_shres_equal(shrp, nsp->ns_shr)) {
2251 2251 struct nlm_shres *nsp_del;
2252 2252
2253 2253 nsp_del = nsp;
2254 2254 nsp = nsp->ns_next;
2255 2255 if (nsp_prev != NULL)
2256 2256 nsp_prev->ns_next = nsp;
2257 2257 else
2258 2258 hostp->nh_shrlist = nsp;
2259 2259
2260 2260 nlm_shres_destroy_item(nsp_del);
2261 2261 continue;
2262 2262 }
2263 2263
2264 2264 nsp_prev = nsp;
2265 2265 nsp = nsp->ns_next;
2266 2266 }
2267 2267
2268 2268 mutex_exit(&hostp->nh_lock);
2269 2269 }
2270 2270
2271 2271 /*
2272 2272 * Get a _copy_ of the list of all active share reservations
2273 2273 * made by the given host.
2274 2274 * NOTE: the list function returns _must_ be released using
2275 2275 * nlm_free_shrlist().
2276 2276 */
2277 2277 struct nlm_shres *
2278 2278 nlm_get_active_shres(struct nlm_host *hostp)
2279 2279 {
2280 2280 struct nlm_shres *nsp, *nslist = NULL;
2281 2281
2282 2282 mutex_enter(&hostp->nh_lock);
2283 2283 for (nsp = hostp->nh_shrlist; nsp != NULL; nsp = nsp->ns_next) {
2284 2284 struct nlm_shres *nsp_new;
2285 2285
2286 2286 nsp_new = nlm_shres_create_item(nsp->ns_shr, nsp->ns_vp);
2287 2287 nsp_new->ns_next = nslist;
2288 2288 nslist = nsp_new;
2289 2289 }
2290 2290
2291 2291 mutex_exit(&hostp->nh_lock);
2292 2292 return (nslist);
2293 2293 }
2294 2294
2295 2295 /*
2296 2296 * Free memory allocated for the active share reservations
2297 2297 * list created by nlm_get_active_shres() function.
2298 2298 */
2299 2299 void
2300 2300 nlm_free_shrlist(struct nlm_shres *nslist)
2301 2301 {
2302 2302 struct nlm_shres *nsp;
2303 2303
2304 2304 while (nslist != NULL) {
2305 2305 nsp = nslist;
2306 2306 nslist = nslist->ns_next;
2307 2307
2308 2308 nlm_shres_destroy_item(nsp);
2309 2309 }
2310 2310 }
2311 2311
2312 2312 static bool_t
2313 2313 nlm_shres_equal(struct shrlock *shrp1, struct shrlock *shrp2)
2314 2314 {
2315 2315 if (shrp1->s_sysid == shrp2->s_sysid &&
2316 2316 shrp1->s_pid == shrp2->s_pid &&
2317 2317 shrp1->s_own_len == shrp2->s_own_len &&
2318 2318 bcmp(shrp1->s_owner, shrp2->s_owner,
2319 2319 shrp1->s_own_len) == 0)
2320 2320 return (TRUE);
2321 2321
2322 2322 return (FALSE);
2323 2323 }
2324 2324
2325 2325 static struct nlm_shres *
2326 2326 nlm_shres_create_item(struct shrlock *shrp, vnode_t *vp)
2327 2327 {
2328 2328 struct nlm_shres *nsp;
2329 2329
2330 2330 nsp = kmem_alloc(sizeof (*nsp), KM_SLEEP);
2331 2331 nsp->ns_shr = kmem_alloc(sizeof (*shrp), KM_SLEEP);
2332 2332 bcopy(shrp, nsp->ns_shr, sizeof (*shrp));
2333 2333 nsp->ns_shr->s_owner = kmem_alloc(shrp->s_own_len, KM_SLEEP);
2334 2334 bcopy(shrp->s_owner, nsp->ns_shr->s_owner, shrp->s_own_len);
2335 2335 nsp->ns_vp = vp;
2336 2336
2337 2337 return (nsp);
2338 2338 }
2339 2339
2340 2340 static void
2341 2341 nlm_shres_destroy_item(struct nlm_shres *nsp)
2342 2342 {
2343 2343 kmem_free(nsp->ns_shr->s_owner,
2344 2344 nsp->ns_shr->s_own_len);
2345 2345 kmem_free(nsp->ns_shr, sizeof (struct shrlock));
2346 2346 kmem_free(nsp, sizeof (*nsp));
2347 2347 }
2348 2348
2349 2349 /*
2350 2350 * Called by klmmod.c when lockd adds a network endpoint
2351 2351 * on which we should begin RPC services.
2352 2352 */
2353 2353 int
2354 2354 nlm_svc_add_ep(struct file *fp, const char *netid, struct knetconfig *knc)
2355 2355 {
2356 2356 SVCMASTERXPRT *xprt = NULL;
2357 2357 int error;
2358 2358
2359 2359 error = svc_tli_kcreate(fp, 0, (char *)netid, NULL, &xprt,
2360 2360 &nlm_sct, NULL, NLM_SVCPOOL_ID, FALSE);
2361 2361 if (error != 0)
2362 2362 return (error);
2363 2363
2364 2364 (void) nlm_knc_to_netid(knc);
2365 2365 return (0);
2366 2366 }
2367 2367
2368 2368 /*
2369 2369 * Start NLM service.
2370 2370 */
2371 2371 int
2372 2372 nlm_svc_starting(struct nlm_globals *g, struct file *fp,
2373 2373 const char *netid, struct knetconfig *knc)
2374 2374 {
2375 2375 int error;
2376 2376 enum clnt_stat stat;
2377 2377
2378 2378 VERIFY(g->run_status == NLM_ST_STARTING);
2379 2379 VERIFY(g->nlm_gc_thread == NULL);
2380 2380
2381 2381 if (g->nlm_v4_only) {
2382 2382 NLM_WARN("Zone %d has no rpcbind, NLM is v4 only", getzoneid());
2383 2383 bzero(&g->nlm_nsm, sizeof (struct nlm_nsm));
2384 2384 g->nlm_nsm.ns_addr_handle = (void *)-1;
2385 2385 goto v4_only;
2386 2386 }
2387 2387
2388 2388 error = nlm_nsm_init_local(&g->nlm_nsm);
2389 2389 if (error != 0) {
2390 2390 NLM_ERR("Failed to initialize NSM handler "
2391 2391 "(error=%d)\n", error);
2392 2392 g->run_status = NLM_ST_DOWN;
2393 2393 return (error);
2394 2394 }
2395 2395
2396 2396 error = EIO;
2397 2397
2398 2398 /*
2399 2399 * Create an NLM garbage collector thread that will
2400 2400 * clean up stale vholds and hosts objects.
2401 2401 */
2402 2402 g->nlm_gc_thread = zthread_create(NULL, 0, nlm_gc,
2403 2403 g, 0, minclsyspri);
2404 2404
2405 2405 /*
2406 2406 * Send SIMU_CRASH to local statd to report that
2407 2407 * NLM started, so that statd can report other hosts
2408 2408 * about NLM state change.
2409 2409 */
2410 2410
2411 2411 stat = nlm_nsm_simu_crash(&g->nlm_nsm);
2412 2412 if (stat != RPC_SUCCESS) {
2413 2413 NLM_ERR("Failed to connect to local statd "
2414 2414 "(rpcerr=%d)\n", stat);
2415 2415 goto shutdown_lm;
2416 2416 }
2417 2417
2418 2418 stat = nlm_nsm_stat(&g->nlm_nsm, &g->nsm_state);
2419 2419 if (stat != RPC_SUCCESS) {
2420 2420 NLM_ERR("Failed to get the status of local statd "
2421 2421 "(rpcerr=%d)\n", stat);
2422 2422 goto shutdown_lm;
2423 2423 }
2424 2424 v4_only:
2425 2425
2426 2426 g->grace_threshold = ddi_get_lbolt() +
2427 2427 SEC_TO_TICK(g->grace_period);
2428 2428
2429 2429 /* Register endpoint used for communications with local NLM */
2430 2430 error = nlm_svc_add_ep(fp, netid, knc);
2431 2431 if (error != 0)
2432 2432 goto shutdown_lm;
2433 2433
2434 2434 (void) svc_pool_control(NLM_SVCPOOL_ID,
2435 2435 SVCPSET_SHUTDOWN_PROC, (void *)nlm_pool_shutdown);
2436 2436 g->run_status = NLM_ST_UP;
2437 2437 return (0);
2438 2438
2439 2439 shutdown_lm:
2440 2440 mutex_enter(&g->lock);
2441 2441 g->run_status = NLM_ST_STOPPING;
2442 2442 mutex_exit(&g->lock);
2443 2443
2444 2444 nlm_svc_stopping(g);
2445 2445 return (error);
2446 2446 }
2447 2447
2448 2448 /*
2449 2449 * Called when the server pool is destroyed, so that
2450 2450 * all transports are closed and no any server threads
2451 2451 * exist.
2452 2452 *
2453 2453 * Just call lm_shutdown() to shut NLM down properly.
2454 2454 */
2455 2455 static void
2456 2456 nlm_pool_shutdown(void)
2457 2457 {
2458 2458 (void) lm_shutdown();
2459 2459 }
2460 2460
2461 2461 /*
2462 2462 * Stop NLM service, cleanup all resources
2463 2463 * NLM owns at the moment.
2464 2464 *
2465 2465 * NOTE: NFS code can call NLM while it's
2466 2466 * stopping or even if it's shut down. Any attempt
2467 2467 * to lock file either on client or on the server
2468 2468 * will fail if NLM isn't in NLM_ST_UP state.
2469 2469 */
2470 2470 void
2471 2471 nlm_svc_stopping(struct nlm_globals *g)
2472 2472 {
2473 2473 mutex_enter(&g->lock);
2474 2474 ASSERT(g->run_status == NLM_ST_STOPPING);
2475 2475
2476 2476 /*
2477 2477 * Ask NLM GC thread to exit and wait until it dies.
2478 2478 */
2479 2479 cv_signal(&g->nlm_gc_sched_cv);
2480 2480 while (g->nlm_gc_thread != NULL)
2481 2481 cv_wait(&g->nlm_gc_finish_cv, &g->lock);
2482 2482
2483 2483 mutex_exit(&g->lock);
2484 2484
2485 2485 /*
2486 2486 * Cleanup locks owned by NLM hosts.
2487 2487 * NOTE: New hosts won't be created while
2488 2488 * NLM is stopping.
2489 2489 */
2490 2490 while (!avl_is_empty(&g->nlm_hosts_tree)) {
2491 2491 struct nlm_host *hostp;
2492 2492 int busy_hosts = 0;
2493 2493
2494 2494 /*
2495 2495 * Iterate through all NLM hosts in the system
2496 2496 * and drop the locks they own by force.
2497 2497 */
2498 2498 hostp = avl_first(&g->nlm_hosts_tree);
2499 2499 while (hostp != NULL) {
2500 2500 /* Cleanup all client and server side locks */
2501 2501 nlm_client_cancel_all(g, hostp);
2502 2502 nlm_host_notify_server(hostp, 0);
2503 2503
2504 2504 mutex_enter(&hostp->nh_lock);
2505 2505 nlm_host_gc_vholds(hostp);
2506 2506 if (hostp->nh_refs > 0 || nlm_host_has_locks(hostp)) {
2507 2507 /*
2508 2508 * Oh, it seems the host is still busy, let
2509 2509 * it some time to release and go to the
2510 2510 * next one.
2511 2511 */
2512 2512
2513 2513 mutex_exit(&hostp->nh_lock);
2514 2514 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
2515 2515 busy_hosts++;
2516 2516 continue;
2517 2517 }
2518 2518
2519 2519 mutex_exit(&hostp->nh_lock);
2520 2520 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
2521 2521 }
2522 2522
2523 2523 /*
2524 2524 * All hosts go to nlm_idle_hosts list after
2525 2525 * all locks they own are cleaned up and last refereces
2526 2526 * were dropped. Just destroy all hosts in nlm_idle_hosts
2527 2527 * list, they can not be removed from there while we're
2528 2528 * in stopping state.
2529 2529 */
2530 2530 while ((hostp = TAILQ_FIRST(&g->nlm_idle_hosts)) != NULL) {
2531 2531 nlm_host_unregister(g, hostp);
2532 2532 nlm_host_destroy(hostp);
2533 2533 }
2534 2534
2535 2535 if (busy_hosts > 0) {
2536 2536 /*
2537 2537 * There're some hosts that weren't cleaned
2538 2538 * up. Probably they're in resource cleanup
2539 2539 * process. Give them some time to do drop
2540 2540 * references.
2541 2541 */
2542 2542 delay(MSEC_TO_TICK(500));
2543 2543 }
2544 2544 }
2545 2545
2546 2546 ASSERT(TAILQ_EMPTY(&g->nlm_slocks));
2547 2547
2548 2548 /* If started with rpcbind (the normal case) */
2549 2549 if (g->nlm_nsm.ns_addr_handle != (void *)-1)
2550 2550 nlm_nsm_fini(&g->nlm_nsm);
2551 2551 g->lockd_pid = 0;
2552 2552 g->run_status = NLM_ST_DOWN;
2553 2553 }
2554 2554
2555 2555 /*
2556 2556 * Returns TRUE if the given vnode has
2557 2557 * any active or sleeping locks.
2558 2558 */
2559 2559 int
2560 2560 nlm_vp_active(const vnode_t *vp)
2561 2561 {
2562 2562 struct nlm_globals *g;
2563 2563 struct nlm_host *hostp;
2564 2564 struct nlm_vhold *nvp;
2565 2565 int active = 0;
2566 2566
2567 2567 g = zone_getspecific(nlm_zone_key, curzone);
2568 2568
2569 2569 /*
2570 2570 * Server side NLM has locks on the given vnode
2571 2571 * if there exist a vhold object that holds
2572 2572 * the given vnode "vp" in one of NLM hosts.
2573 2573 */
2574 2574 mutex_enter(&g->lock);
2575 2575 hostp = avl_first(&g->nlm_hosts_tree);
2576 2576 while (hostp != NULL) {
2577 2577 mutex_enter(&hostp->nh_lock);
2578 2578 nvp = nlm_vhold_find_locked(hostp, vp);
2579 2579 mutex_exit(&hostp->nh_lock);
2580 2580 if (nvp != NULL) {
2581 2581 active = 1;
2582 2582 break;
2583 2583 }
2584 2584
2585 2585 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
2586 2586 }
2587 2587
2588 2588 mutex_exit(&g->lock);
2589 2589 return (active);
2590 2590 }
2591 2591
2592 2592 /*
2593 2593 * Called right before NFS export is going to
2594 2594 * dissapear. The function finds all vnodes
2595 2595 * belonging to the given export and cleans
2596 2596 * all remote locks and share reservations
2597 2597 * on them.
2598 2598 */
2599 2599 void
2600 2600 nlm_zone_unexport(struct nlm_globals *g, struct exportinfo *exi)
2601 2601 {
2602 2602 struct nlm_host *hostp;
2603 2603
2604 2604 mutex_enter(&g->lock);
2605 2605 if (g->run_status != NLM_ST_UP) {
2606 2606 /* nothing to do */
2607 2607 mutex_exit(&g->lock);
2608 2608 return;
2609 2609 }
2610 2610
2611 2611 hostp = avl_first(&g->nlm_hosts_tree);
2612 2612 while (hostp != NULL) {
2613 2613 struct nlm_vhold *nvp;
2614 2614
2615 2615 if (hostp->nh_flags & NLM_NH_INIDLE) {
2616 2616 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
2617 2617 hostp->nh_flags &= ~NLM_NH_INIDLE;
2618 2618 }
2619 2619 hostp->nh_refs++;
2620 2620
2621 2621 mutex_exit(&g->lock);
2622 2622
2623 2623 mutex_enter(&hostp->nh_lock);
2624 2624 TAILQ_FOREACH(nvp, &hostp->nh_vholds_list, nv_link) {
2625 2625 vnode_t *vp;
2626 2626
2627 2627 nvp->nv_refcnt++;
2628 2628 mutex_exit(&hostp->nh_lock);
2629 2629
2630 2630 vp = nvp->nv_vp;
2631 2631
2632 2632 if (!EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid))
2633 2633 goto next_iter;
2634 2634
2635 2635 /*
2636 2636 * Ok, it we found out that vnode vp is under
2637 2637 * control by the exportinfo exi, now we need
2638 2638 * to drop all locks from this vnode, let's
2639 2639 * do it.
2640 2640 */
2641 2641 nlm_vhold_clean(nvp, hostp->nh_sysid);
2642 2642
2643 2643 next_iter:
2644 2644 mutex_enter(&hostp->nh_lock);
2645 2645 nvp->nv_refcnt--;
2646 2646 }
2647 2647 mutex_exit(&hostp->nh_lock);
2648 2648
2649 2649 mutex_enter(&g->lock);
2650 2650 nlm_host_release_locked(g, hostp);
2651 2651
2652 2652 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
2653 2653 }
2654 2654
2655 2655 mutex_exit(&g->lock);
2656 2656 }
2657 2657
2658 2658 void
2659 2659 nlm_unexport(struct exportinfo *exi)
2660 2660 {
2661 2661 struct nlm_globals *g;
2662 2662
2663 2663 rw_enter(&lm_lck, RW_READER);
2664 2664 TAILQ_FOREACH(g, &nlm_zones_list, nlm_link) {
2665 2665 if (g->nlm_zoneid == exi->exi_zoneid) {
2666 2666 /*
2667 2667 * NOTE: If we want to drop lm_lock before
2668 2668 * calling nlm_zone_unexport(), we should break,
2669 2669 * and have a post-rw_exit() snippit like:
2670 2670 * if (g != NULL)
2671 2671 * nlm_zone_unexport(g, exi);
2672 2672 */
2673 2673 nlm_zone_unexport(g, exi);
2674 2674 break; /* Only going to match once! */
2675 2675 }
2676 2676 }
2677 2677 rw_exit(&lm_lck);
2678 2678 }
2679 2679
2680 2680 /*
2681 2681 * Allocate new unique sysid.
2682 2682 * In case of failure (no available sysids)
2683 2683 * return LM_NOSYSID.
2684 2684 */
2685 2685 sysid_t
2686 2686 nlm_sysid_alloc(void)
2687 2687 {
2688 2688 sysid_t ret_sysid = LM_NOSYSID;
2689 2689
2690 2690 rw_enter(&lm_lck, RW_WRITER);
2691 2691 if (nlm_sysid_nidx > LM_SYSID_MAX)
2692 2692 nlm_sysid_nidx = LM_SYSID;
2693 2693
2694 2694 if (!BT_TEST(nlm_sysid_bmap, nlm_sysid_nidx)) {
2695 2695 BT_SET(nlm_sysid_bmap, nlm_sysid_nidx);
2696 2696 ret_sysid = nlm_sysid_nidx++;
2697 2697 } else {
2698 2698 index_t id;
2699 2699
2700 2700 id = bt_availbit(nlm_sysid_bmap, NLM_BMAP_NITEMS);
2701 2701 if (id > 0) {
2702 2702 nlm_sysid_nidx = id + 1;
2703 2703 ret_sysid = id;
2704 2704 BT_SET(nlm_sysid_bmap, id);
2705 2705 }
2706 2706 }
2707 2707
2708 2708 rw_exit(&lm_lck);
2709 2709 return (ret_sysid);
2710 2710 }
2711 2711
2712 2712 void
2713 2713 nlm_sysid_free(sysid_t sysid)
2714 2714 {
2715 2715 ASSERT(sysid >= LM_SYSID && sysid <= LM_SYSID_MAX);
2716 2716
2717 2717 rw_enter(&lm_lck, RW_WRITER);
2718 2718 ASSERT(BT_TEST(nlm_sysid_bmap, sysid));
2719 2719 BT_CLEAR(nlm_sysid_bmap, sysid);
2720 2720 rw_exit(&lm_lck);
2721 2721 }
2722 2722
2723 2723 /*
2724 2724 * Return true if the request came from a local caller.
2725 2725 * By necessity, this "knows" the netid names invented
2726 2726 * in lm_svc() and nlm_netid_from_knetconfig().
2727 2727 */
2728 2728 bool_t
2729 2729 nlm_caller_is_local(SVCXPRT *transp)
2730 2730 {
2731 2731 char *netid;
2732 2732 struct netbuf *rtaddr;
2733 2733
2734 2734 netid = svc_getnetid(transp);
2735 2735 rtaddr = svc_getrpccaller(transp);
2736 2736
2737 2737 if (netid == NULL)
2738 2738 return (FALSE);
2739 2739
2740 2740 if (strcmp(netid, "ticlts") == 0 ||
2741 2741 strcmp(netid, "ticotsord") == 0)
2742 2742 return (TRUE);
2743 2743
2744 2744 if (strcmp(netid, "tcp") == 0 || strcmp(netid, "udp") == 0) {
2745 2745 struct sockaddr_in *sin = (void *)rtaddr->buf;
2746 2746 if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
2747 2747 return (TRUE);
2748 2748 }
2749 2749 if (strcmp(netid, "tcp6") == 0 || strcmp(netid, "udp6") == 0) {
2750 2750 struct sockaddr_in6 *sin6 = (void *)rtaddr->buf;
2751 2751 if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
2752 2752 return (TRUE);
2753 2753 }
2754 2754
2755 2755 return (FALSE); /* unknown transport */
2756 2756 }
2757 2757
2758 2758 /*
2759 2759 * Get netid string correspondig to the given knetconfig.
2760 2760 * If not done already, save knc->knc_rdev in our table.
2761 2761 */
2762 2762 const char *
2763 2763 nlm_knc_to_netid(struct knetconfig *knc)
2764 2764 {
2765 2765 int i;
2766 2766 dev_t rdev;
2767 2767 struct nlm_knc *nc;
2768 2768 const char *netid = NULL;
2769 2769
2770 2770 rw_enter(&lm_lck, RW_READER);
2771 2771 for (i = 0; i < NLM_KNCS; i++) {
2772 2772 nc = &nlm_netconfigs[i];
2773 2773
2774 2774 if (nc->n_knc.knc_semantics == knc->knc_semantics &&
2775 2775 strcmp(nc->n_knc.knc_protofmly,
2776 2776 knc->knc_protofmly) == 0) {
2777 2777 netid = nc->n_netid;
2778 2778 rdev = nc->n_knc.knc_rdev;
2779 2779 break;
2780 2780 }
2781 2781 }
2782 2782 rw_exit(&lm_lck);
2783 2783
2784 2784 if (netid != NULL && rdev == NODEV) {
2785 2785 rw_enter(&lm_lck, RW_WRITER);
2786 2786 if (nc->n_knc.knc_rdev == NODEV)
2787 2787 nc->n_knc.knc_rdev = knc->knc_rdev;
2788 2788 rw_exit(&lm_lck);
2789 2789 }
2790 2790
2791 2791 return (netid);
2792 2792 }
2793 2793
2794 2794 /*
2795 2795 * Get a knetconfig corresponding to the given netid.
2796 2796 * If there's no knetconfig for this netid, ENOENT
2797 2797 * is returned.
2798 2798 */
2799 2799 int
2800 2800 nlm_knc_from_netid(const char *netid, struct knetconfig *knc)
2801 2801 {
2802 2802 int i, ret;
2803 2803
2804 2804 ret = ENOENT;
2805 2805 for (i = 0; i < NLM_KNCS; i++) {
2806 2806 struct nlm_knc *nknc;
2807 2807
2808 2808 nknc = &nlm_netconfigs[i];
2809 2809 if (strcmp(netid, nknc->n_netid) == 0 &&
2810 2810 nknc->n_knc.knc_rdev != NODEV) {
2811 2811 *knc = nknc->n_knc;
2812 2812 ret = 0;
2813 2813 break;
2814 2814 }
2815 2815 }
2816 2816
2817 2817 return (ret);
2818 2818 }
2819 2819
2820 2820 void
2821 2821 nlm_cprsuspend(void)
2822 2822 {
2823 2823 struct nlm_globals *g;
2824 2824
2825 2825 rw_enter(&lm_lck, RW_READER);
2826 2826 TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
2827 2827 nlm_suspend_zone(g);
2828 2828
2829 2829 rw_exit(&lm_lck);
2830 2830 }
2831 2831
2832 2832 void
2833 2833 nlm_cprresume(void)
2834 2834 {
2835 2835 struct nlm_globals *g;
2836 2836
2837 2837 rw_enter(&lm_lck, RW_READER);
2838 2838 TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
2839 2839 nlm_resume_zone(g);
2840 2840
2841 2841 rw_exit(&lm_lck);
2842 2842 }
2843 2843
2844 2844 void
2845 2845 nlm_nsm_clnt_init(CLIENT *clnt, struct nlm_nsm *nsm)
2846 2846 {
2847 2847 (void) clnt_tli_kinit(clnt, &nsm->ns_knc, &nsm->ns_addr, 0,
2848 2848 NLM_RPC_RETRIES, zone_kcred());
2849 2849 }
2850 2850
2851 2851 void
2852 2852 nlm_netbuf_to_netobj(struct netbuf *addr, int *family, netobj *obj)
2853 2853 {
2854 2854 /* LINTED pointer alignment */
2855 2855 struct sockaddr *sa = (struct sockaddr *)addr->buf;
2856 2856
2857 2857 *family = sa->sa_family;
2858 2858
2859 2859 switch (sa->sa_family) {
2860 2860 case AF_INET: {
2861 2861 /* LINTED pointer alignment */
2862 2862 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
2863 2863
2864 2864 obj->n_len = sizeof (sin->sin_addr);
2865 2865 obj->n_bytes = (char *)&sin->sin_addr;
2866 2866 break;
2867 2867 }
2868 2868
2869 2869 case AF_INET6: {
2870 2870 /* LINTED pointer alignment */
2871 2871 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
2872 2872
2873 2873 obj->n_len = sizeof (sin6->sin6_addr);
2874 2874 obj->n_bytes = (char *)&sin6->sin6_addr;
2875 2875 break;
2876 2876 }
2877 2877
2878 2878 default:
2879 2879 VERIFY(0);
2880 2880 break;
2881 2881 }
2882 2882 }
|
↓ open down ↓ |
1880 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX