1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright (c) 2016 by Delphix. All rights reserved. 25 */ 26 27 /* 28 * RDC interface health monitoring code. 29 */ 30 31 #include <sys/types.h> 32 #include <sys/ksynch.h> 33 #include <sys/errno.h> 34 #include <sys/debug.h> 35 #include <sys/cmn_err.h> 36 #include <sys/kmem.h> 37 38 #include <sys/errno.h> 39 40 #ifdef _SunOS_2_6 41 /* 42 * on 2.6 both dki_lock.h and rpc/types.h define bool_t so we 43 * define enum_t here as it is all we need from rpc/types.h 44 * anyway and make it look like we included it. Yuck. 45 */ 46 #define _RPC_TYPES_H 47 typedef int enum_t; 48 #else 49 #ifndef DS_DDICT 50 #include <rpc/types.h> 51 #endif 52 #endif /* _SunOS_2_6 */ 53 54 #include <sys/ddi.h> 55 #include <sys/nsc_thread.h> 56 #ifdef DS_DDICT 57 #include <sys/nsctl/contract.h> 58 #endif 59 #include <sys/nsctl/nsctl.h> 60 61 #include <sys/unistat/spcs_s.h> 62 #include <sys/unistat/spcs_s_k.h> 63 #include <sys/unistat/spcs_errors.h> 64 65 #include "rdc_io.h" 66 #include "rdc_clnt.h" 67 68 69 /* 70 * Forward declarations. 71 */ 72 73 static void rdc_update_health(rdc_if_t *); 74 75 /* 76 * Global data. 77 */ 78 79 /* 80 * These structures are added when a new host name is introduced to the 81 * kernel. They never disappear (but that won't waste much space at all). 82 */ 83 typedef struct rdc_link_down { 84 char host[MAX_RDC_HOST_SIZE]; /* The host name of this link */ 85 int waiting; /* A user is waiting to be woken up */ 86 int link_down; /* The current state of the link */ 87 struct rdc_link_down *next; /* Chain */ 88 kcondvar_t syncd_cv; /* Syncd wakeup */ 89 kmutex_t syncd_mutex; /* Lock for syncd_cv */ 90 } rdc_link_down_t; 91 static rdc_link_down_t *rdc_link_down = NULL; 92 93 int rdc_health_thres = RDC_HEALTH_THRESHOLD; 94 rdc_if_t *rdc_if_top; 95 96 97 /* 98 * IPv6 addresses are represented as 16bit hexadecimal integers 99 * separated by colons. Contiguous runs of zeros can be abbreviated by 100 * double colons: 101 * FF02:0:0:0:0:1:200E:8C6C 102 * | 103 * v 104 * FF02::1:200E:8C6C 105 */ 106 void 107 rdc_if_ipv6(const uint16_t *addr, char *buf) 108 { 109 const int end = 8; /* 8 shorts, 128 bits in an IPv6 address */ 110 int i; 111 112 for (i = 0; i < end; i++) { 113 if (i > 0) 114 (void) sprintf(buf, "%s:", buf); 115 116 if (addr[i] != 0 || i == 0 || i == (end - 1)) { 117 /* first, last, or non-zero value */ 118 (void) sprintf(buf, "%s%x", buf, (int)addr[i]); 119 } else { 120 if ((i + 1) < end && addr[i + 1] != 0) { 121 /* single zero */ 122 (void) sprintf(buf, "%s%x", buf, (int)addr[i]); 123 } else { 124 /* skip contiguous zeros */ 125 while ((i + 1) < end && addr[i + 1] == 0) 126 i++; 127 } 128 } 129 } 130 } 131 132 static void 133 rdc_if_xxx(rdc_if_t *ip, char *updown) 134 { 135 if (strcmp("inet6", ip->srv->ri_knconf->knc_protofmly) == 0) { 136 uint16_t *this = (uint16_t *)ip->ifaddr.buf; 137 uint16_t *other = (uint16_t *)ip->r_ifaddr.buf; 138 char this_str[256], other_str[256]; 139 140 bzero(this_str, sizeof (this_str)); 141 bzero(other_str, sizeof (other_str)); 142 rdc_if_ipv6(&this[4], this_str); 143 rdc_if_ipv6(&other[4], other_str); 144 145 cmn_err(CE_NOTE, "!SNDR: Interface %s <==> %s : %s", 146 this_str, other_str, updown); 147 } else { 148 uchar_t *this = (uchar_t *)ip->ifaddr.buf; 149 uchar_t *other = (uchar_t *)ip->r_ifaddr.buf; 150 151 cmn_err(CE_NOTE, 152 "!SNDR: Interface %d.%d.%d.%d <==> %d.%d.%d.%d : %s", 153 (int)this[4], (int)this[5], (int)this[6], (int)this[7], 154 (int)other[4], (int)other[5], (int)other[6], (int)other[7], 155 updown); 156 } 157 } 158 159 160 static void 161 rdc_if_down(rdc_if_t *ip) 162 { 163 rdc_if_xxx(ip, "Down"); 164 } 165 166 167 static void 168 rdc_if_up(rdc_if_t *ip) 169 { 170 rdc_if_xxx(ip, "Up"); 171 } 172 173 174 /* 175 * Health monitor for a single interface. 176 * 177 * The secondary sends ping RPCs to the primary. 178 * The primary just stores the results and updates its structures. 179 */ 180 static void 181 rdc_health_thread(void *arg) 182 { 183 rdc_if_t *ip = (rdc_if_t *)arg; 184 struct rdc_ping ping; 185 struct rdc_ping6 ping6; 186 struct timeval t; 187 int down = 1; 188 int ret, err; 189 int sec = 0; 190 char ifaddr[RDC_MAXADDR]; 191 char r_ifaddr[RDC_MAXADDR]; 192 uint16_t *sp; 193 194 bcopy(ip->ifaddr.buf, ifaddr, ip->ifaddr.len); 195 sp = (uint16_t *)ifaddr; 196 *sp = htons(*sp); 197 bcopy(ip->r_ifaddr.buf, r_ifaddr, ip->r_ifaddr.len); 198 sp = (uint16_t *)r_ifaddr; 199 *sp = htons(*sp); 200 201 while ((ip->exiting != 1) && (net_exit != ATM_EXIT)) { 202 delay(HZ); 203 204 /* setup RPC timeout */ 205 206 t.tv_sec = rdc_rpc_tmout; 207 t.tv_usec = 0; 208 209 if (ip->issecondary && !ip->no_ping) { 210 if (ip->rpc_version < RDC_VERSION7) { 211 bcopy(ip->r_ifaddr.buf, ping6.p_ifaddr, 212 RDC_MAXADDR); 213 /* primary ifaddr */ 214 bcopy(ip->ifaddr.buf, ping6.s_ifaddr, 215 RDC_MAXADDR); 216 /* secondary ifaddr */ 217 err = rdc_clnt_call_any(ip->srv, ip, 218 RDCPROC_PING4, xdr_rdc_ping6, 219 (char *)&ping6, xdr_int, (char *)&ret, &t); 220 } else { 221 ping.p_ifaddr.buf = r_ifaddr; 222 ping.p_ifaddr.len = ip->r_ifaddr.len; 223 ping.p_ifaddr.maxlen = ip->r_ifaddr.len; 224 ping.s_ifaddr.buf = ifaddr; 225 ping.s_ifaddr.len = ip->ifaddr.len; 226 ping.s_ifaddr.maxlen = ip->ifaddr.len; 227 err = rdc_clnt_call_any(ip->srv, ip, 228 RDCPROC_PING4, xdr_rdc_ping, (char *)&ping, 229 xdr_int, (char *)&ret, &t); 230 } 231 232 233 if (err || ret) { 234 /* RPC failed - link is down */ 235 if (!down && !ip->isprimary) { 236 /* 237 * don't print messages if also 238 * a primary - the primary will 239 * take care of it. 240 */ 241 rdc_if_down(ip); 242 down = 1; 243 } 244 rdc_dump_alloc_bufs(ip); 245 ip->no_ping = 1; 246 247 /* 248 * Start back at the max possible version 249 * since the remote server could come back 250 * on a different protocol version. 251 */ 252 mutex_enter(&rdc_ping_lock); 253 ip->rpc_version = RDC_VERS_MAX; 254 mutex_exit(&rdc_ping_lock); 255 } else { 256 if (down && !ip->isprimary) { 257 /* 258 * was failed, but now ok 259 * 260 * don't print messages if also 261 * a primary - the primary will 262 * take care of it. 263 */ 264 rdc_if_up(ip); 265 down = 0; 266 } 267 } 268 } 269 if (!ip->isprimary && down && ++sec == 5) { 270 sec = 0; 271 rdc_dump_alloc_bufs(ip); 272 } 273 274 if (ip->isprimary) 275 rdc_update_health(ip); 276 } 277 278 /* signal that this thread is done */ 279 ip->exiting = 2; 280 } 281 282 283 int 284 rdc_isactive_if(struct netbuf *addr, struct netbuf *r_addr) 285 { 286 rdc_if_t *ip; 287 int rc = 0; 288 289 /* search for existing interface structure */ 290 291 mutex_enter(&rdc_ping_lock); 292 for (ip = rdc_if_top; ip; ip = ip->next) { 293 if (ip->exiting != 0) 294 continue; 295 if (((bcmp(ip->ifaddr.buf, addr->buf, addr->len) == 0) && 296 (bcmp(ip->r_ifaddr.buf, r_addr->buf, r_addr->len) == 0)) || 297 ((bcmp(ip->r_ifaddr.buf, addr->buf, addr->len) == 0) && 298 (bcmp(ip->ifaddr.buf, r_addr->buf, r_addr->len) == 0))) { 299 /* found matching interface structure */ 300 if (ip->isprimary && !ip->if_down) { 301 rc = 1; 302 } else if (ip->issecondary && !ip->no_ping) { 303 rc = 1; 304 } 305 break; 306 } 307 } 308 mutex_exit(&rdc_ping_lock); 309 return (rc); 310 } 311 312 /* 313 * Set the rdc rpc version of the rdc_if_t. 314 * 315 * Called from incoming rpc calls which start before 316 * the health service becomes established. 317 */ 318 void 319 rdc_set_if_vers(rdc_u_info_t *urdc, rpcvers_t vers) 320 { 321 rdc_if_t *ip; 322 struct netbuf *addr, *r_addr; 323 324 if (rdc_get_vflags(urdc) & RDC_PRIMARY) { 325 addr = &(urdc->primary.addr); 326 r_addr = &(urdc->secondary.addr); 327 } else { 328 addr = &(urdc->secondary.addr); 329 r_addr = &(urdc->primary.addr); 330 } 331 332 /* search for existing interface structure */ 333 334 mutex_enter(&rdc_ping_lock); 335 for (ip = rdc_if_top; ip; ip = ip->next) { 336 if (ip->exiting != 0) 337 continue; 338 if (((bcmp(ip->ifaddr.buf, addr->buf, addr->len) == 0) && 339 (bcmp(ip->r_ifaddr.buf, r_addr->buf, r_addr->len) == 0)) || 340 ((bcmp(ip->r_ifaddr.buf, addr->buf, addr->len) == 0) && 341 (bcmp(ip->ifaddr.buf, r_addr->buf, r_addr->len) == 0))) { 342 /* found matching interface structure */ 343 ip->rpc_version = vers; 344 #ifdef DEBUG 345 cmn_err(CE_NOTE, "!rdc intf %p rpc version set to %u", 346 (void *)ip, vers); 347 #endif 348 break; 349 } 350 } 351 mutex_exit(&rdc_ping_lock); 352 } 353 354 /* 355 * Free all the rdc_link_down structures (only at module unload time) 356 */ 357 void 358 rdc_link_down_free() 359 { 360 rdc_link_down_t *p; 361 rdc_link_down_t *q; 362 363 if (rdc_link_down == NULL) 364 return; 365 366 for (p = rdc_link_down->next; p != rdc_link_down; ) { 367 q = p; 368 p = p->next; 369 kmem_free(q, sizeof (*q)); 370 } 371 kmem_free(rdc_link_down, sizeof (*q)); 372 rdc_link_down = NULL; 373 } 374 375 376 /* 377 * Look up the supplied hostname in the rdc_link_down chain. Add a new 378 * entry if it isn't found. Return a pointer to the new or found entry. 379 */ 380 static rdc_link_down_t * 381 rdc_lookup_host(char *host) 382 { 383 rdc_link_down_t *p; 384 385 mutex_enter(&rdc_ping_lock); 386 387 if (rdc_link_down == NULL) { 388 rdc_link_down = kmem_zalloc(sizeof (*rdc_link_down), KM_SLEEP); 389 rdc_link_down->next = rdc_link_down; 390 } 391 392 for (p = rdc_link_down->next; p != rdc_link_down; p = p->next) { 393 if (strcmp(host, p->host) == 0) { 394 /* Match */ 395 mutex_exit(&rdc_ping_lock); 396 return (p); 397 } 398 } 399 400 /* No match, must create a new entry */ 401 402 p = kmem_zalloc(sizeof (*p), KM_SLEEP); 403 p->link_down = 1; 404 p->next = rdc_link_down->next; 405 rdc_link_down->next = p; 406 (void) strncpy(p->host, host, MAX_RDC_HOST_SIZE); 407 mutex_init(&p->syncd_mutex, NULL, MUTEX_DRIVER, NULL); 408 cv_init(&p->syncd_cv, NULL, CV_DRIVER, NULL); 409 410 mutex_exit(&rdc_ping_lock); 411 return (p); 412 } 413 414 415 /* 416 * Handle the RDC_LINK_DOWN ioctl. 417 * The user specifies which host they're interested in. 418 * This function is woken up when the link to that host goes down. 419 */ 420 421 /* ARGSUSED3 */ 422 int 423 _rdc_link_down(void *arg, int mode, spcs_s_info_t kstatus, int *rvp) 424 { 425 char host[MAX_RDC_HOST_SIZE]; 426 rdc_link_down_t *syncdp; 427 clock_t timeout = RDC_SYNC_EVENT_TIMEOUT * 2; /* 2 min */ 428 int rc = 0; 429 430 if (ddi_copyin(arg, host, MAX_RDC_HOST_SIZE, mode)) 431 return (EFAULT); 432 433 434 syncdp = rdc_lookup_host(host); 435 436 mutex_enter(&syncdp->syncd_mutex); 437 if (!syncdp->link_down) { 438 syncdp->waiting = 1; 439 if (cv_timedwait_sig(&syncdp->syncd_cv, &syncdp->syncd_mutex, 440 nsc_lbolt() + timeout) == 0) { 441 /* Woken by a signal, not a link down event */ 442 syncdp->waiting = 0; 443 rc = EAGAIN; 444 spcs_s_add(kstatus, rc); 445 } 446 447 } 448 mutex_exit(&syncdp->syncd_mutex); 449 450 return (rc); 451 } 452 453 454 /* 455 * Add an RDC set to an interface 456 * 457 * If the interface is new, add it to the list of interfaces. 458 */ 459 rdc_if_t * 460 rdc_add_to_if(rdc_srv_t *svp, struct netbuf *addr, struct netbuf *r_addr, 461 int primary) 462 { 463 rdc_if_t *new, *ip; 464 465 if ((addr->buf == NULL) || (r_addr->buf == NULL)) 466 return (NULL); 467 468 /* setup a new interface structure */ 469 new = (rdc_if_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 470 if (!new) 471 return (NULL); 472 473 dup_rdc_netbuf(addr, &new->ifaddr); 474 dup_rdc_netbuf(r_addr, &new->r_ifaddr); 475 new->rpc_version = RDC_VERS_MAX; 476 new->srv = rdc_create_svinfo(svp->ri_hostname, &svp->ri_addr, 477 svp->ri_knconf); 478 new->old_pulse = -1; 479 new->new_pulse = 0; 480 481 if (!new->srv) { 482 free_rdc_netbuf(&new->r_ifaddr); 483 free_rdc_netbuf(&new->ifaddr); 484 kmem_free(new, sizeof (*new)); 485 return (NULL); 486 } 487 488 /* search for existing interface structure */ 489 490 mutex_enter(&rdc_ping_lock); 491 492 for (ip = rdc_if_top; ip; ip = ip->next) { 493 if ((bcmp(ip->ifaddr.buf, addr->buf, addr->len) == 0) && 494 (bcmp(ip->r_ifaddr.buf, r_addr->buf, r_addr->len) == 0) && 495 ip->exiting == 0) { 496 /* found matching interface structure */ 497 break; 498 } 499 } 500 501 if (!ip) { 502 /* add new into the chain */ 503 504 new->next = rdc_if_top; 505 rdc_if_top = new; 506 ip = new; 507 508 /* start daemon */ 509 510 ip->last = nsc_time(); 511 ip->deadness = 1; 512 ip->if_down = 1; 513 514 if (nsc_create_process(rdc_health_thread, ip, TRUE)) { 515 mutex_exit(&rdc_ping_lock); 516 return (NULL); 517 } 518 } 519 520 /* mark usage type */ 521 522 if (primary) { 523 ip->isprimary = 1; 524 } else { 525 ip->issecondary = 1; 526 ip->no_ping = 0; 527 } 528 529 mutex_exit(&rdc_ping_lock); 530 531 /* throw away new if it was not used */ 532 533 if (ip != new) { 534 free_rdc_netbuf(&new->r_ifaddr); 535 free_rdc_netbuf(&new->ifaddr); 536 rdc_destroy_svinfo(new->srv); 537 kmem_free(new, sizeof (*new)); 538 } 539 540 return (ip); 541 } 542 543 544 /* 545 * Update an interface following the removal of an RDC set. 546 * 547 * If there are no more RDC sets using the interface, delete it from 548 * the list of interfaces. 549 * 550 * Either clear krdc->intf, or ensure !IS_CONFIGURED(krdc) before calling this. 551 */ 552 void 553 rdc_remove_from_if(rdc_if_t *ip) 554 { 555 rdc_k_info_t *krdc; 556 rdc_u_info_t *urdc; 557 rdc_if_t **ipp; 558 int pfound = 0; 559 int sfound = 0; 560 int delete = 1; 561 int index; 562 563 mutex_enter(&rdc_ping_lock); 564 565 /* 566 * search for RDC sets using this interface and update 567 * the isprimary and issecondary flags. 568 */ 569 570 for (index = 0; index < rdc_max_sets; index++) { 571 krdc = &rdc_k_info[index]; 572 urdc = &rdc_u_info[index]; 573 if (IS_CONFIGURED(krdc) && krdc->intf == ip) { 574 delete = 0; 575 576 if (rdc_get_vflags(urdc) & RDC_PRIMARY) { 577 pfound = 1; 578 } else { 579 sfound = 1; 580 } 581 582 if (pfound && sfound) 583 break; 584 } 585 } 586 587 ip->isprimary = pfound; 588 ip->issecondary = sfound; 589 590 if (!delete || ip->exiting > 0) { 591 mutex_exit(&rdc_ping_lock); 592 return; 593 } 594 595 /* mark and wait for daemon to exit */ 596 597 ip->exiting = 1; 598 599 mutex_exit(&rdc_ping_lock); 600 601 while (ip->exiting == 1) 602 delay(drv_usectohz(10)); 603 604 mutex_enter(&rdc_ping_lock); 605 606 ASSERT(ip->exiting == 2); 607 608 /* remove from chain */ 609 610 for (ipp = &rdc_if_top; *ipp; ipp = &((*ipp)->next)) { 611 if (*ipp == ip) { 612 *ipp = ip->next; 613 break; 614 } 615 } 616 617 mutex_exit(&rdc_ping_lock); 618 619 /* free unused interface structure */ 620 621 free_rdc_netbuf(&ip->r_ifaddr); 622 free_rdc_netbuf(&ip->ifaddr); 623 rdc_destroy_svinfo(ip->srv); 624 kmem_free(ip, sizeof (*ip)); 625 } 626 627 628 /* 629 * Check the status of the link to the secondary, and optionally update 630 * the primary-side ping variables. 631 * 632 * For use on a primary only. 633 * 634 * Returns: 635 * TRUE - interface up. 636 * FALSE - interface down. 637 */ 638 int 639 rdc_check_secondary(rdc_if_t *ip, int update) 640 { 641 int rc = TRUE; 642 643 if (!ip || !ip->isprimary) { 644 #ifdef DEBUG 645 cmn_err(CE_WARN, 646 "!rdc_check_secondary: ip %p, isprimary %d, issecondary %d", 647 (void *) ip, ip ? ip->isprimary : 0, 648 ip ? ip->issecondary : 0); 649 #endif 650 return (FALSE); 651 } 652 653 if (!ip->deadness) { 654 #ifdef DEBUG 655 cmn_err(CE_WARN, "!rdc_check_secondary: ip %p, ip->deadness %d", 656 (void *) ip, ip->deadness); 657 #endif 658 return (FALSE); 659 } 660 661 if (!update) { 662 /* quick look */ 663 return ((ip->deadness > rdc_health_thres) ? FALSE : TRUE); 664 } 665 666 /* update (slow) with lock */ 667 668 mutex_enter(&rdc_ping_lock); 669 670 if (ip->old_pulse == ip->new_pulse) { 671 /* 672 * ping has not been received since last update 673 * or we have not yet been pinged, 674 * the health thread has started only as a 675 * local client so far, not so on the other side 676 */ 677 678 if (ip->last != nsc_time()) { 679 /* time has passed, so move closer to death */ 680 681 ip->last = nsc_time(); 682 ip->deadness++; 683 684 if (ip->deadness <= 0) { 685 /* avoid the wrap */ 686 ip->deadness = rdc_health_thres + 1; 687 } 688 } 689 690 if (ip->deadness > rdc_health_thres) { 691 rc = FALSE; 692 /* 693 * Start back at the max possible version 694 * since the remote server could come back 695 * on a different protocol version. 696 */ 697 ip->rpc_version = RDC_VERS_MAX; 698 } 699 } else { 700 ip->old_pulse = ip->new_pulse; 701 } 702 703 mutex_exit(&rdc_ping_lock); 704 return (rc); 705 } 706 707 708 /* 709 * Update the interface structure with the latest ping info, and 710 * perform interface up/down transitions if required. 711 * 712 * For use on a primary only. 713 */ 714 static void 715 rdc_update_health(rdc_if_t *ip) 716 { 717 rdc_k_info_t *krdc; 718 rdc_u_info_t *urdc; 719 int index; 720 rdc_link_down_t *syncdp; 721 722 if (!ip->isprimary) { 723 #ifdef DEBUG 724 cmn_err(CE_WARN, 725 "!rdc_update_health: ip %p, isprimary %d, issecondary %d", 726 (void *) ip, ip ? ip->isprimary : 0, 727 ip ? ip->issecondary : 0); 728 #endif 729 return; 730 } 731 732 if (!rdc_check_secondary(ip, TRUE)) { 733 /* interface down */ 734 if (!ip->if_down) { 735 rdc_if_down(ip); 736 ip->if_down = 1; 737 738 /* scan rdc sets and update status */ 739 740 for (index = 0; index < rdc_max_sets; index++) { 741 krdc = &rdc_k_info[index]; 742 urdc = &rdc_u_info[index]; 743 744 if (IS_ENABLED(urdc) && (krdc->intf == ip) && 745 (rdc_get_vflags(urdc) & RDC_PRIMARY) && 746 !(rdc_get_vflags(urdc) & RDC_LOGGING)) { 747 /* mark down */ 748 749 rdc_group_enter(krdc); 750 /* 751 * check for possible race with 752 * with delete logic 753 */ 754 if (!IS_ENABLED(urdc)) { 755 rdc_group_exit(krdc); 756 continue; 757 } 758 rdc_group_log(krdc, RDC_NOFLUSH | 759 RDC_NOREMOTE | RDC_QUEUING, 760 "hm detected secondary " 761 "interface down"); 762 763 rdc_group_exit(krdc); 764 765 /* dump async queues */ 766 rdc_dump_queue(index); 767 } 768 } 769 770 /* dump allocated bufs */ 771 rdc_dump_alloc_bufs(ip); 772 } 773 774 syncdp = rdc_lookup_host(ip->srv->ri_hostname); 775 mutex_enter(&syncdp->syncd_mutex); 776 if (syncdp->link_down == 0) { 777 /* Link has gone down, notify rdcsyncd daemon */ 778 syncdp->link_down = 1; 779 if (syncdp->waiting) { 780 syncdp->waiting = 0; 781 cv_signal(&syncdp->syncd_cv); 782 } 783 } 784 mutex_exit(&syncdp->syncd_mutex); 785 } else { 786 /* interface up */ 787 if (ip->if_down && ip->isprimary) { 788 rdc_if_up(ip); 789 ip->if_down = 0; 790 } 791 792 syncdp = rdc_lookup_host(ip->srv->ri_hostname); 793 mutex_enter(&syncdp->syncd_mutex); 794 if (syncdp->link_down) { 795 /* Link has come back up */ 796 syncdp->link_down = 0; 797 } 798 mutex_exit(&syncdp->syncd_mutex); 799 } 800 }