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