xref: /titanic_52/usr/src/uts/common/avs/ns/rdc/rdc_health.c (revision 3270659f55e0928d6edec3d26217cc29398a8149)
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