xref: /illumos-gate/usr/src/cmd/isns/isnsd/esi.c (revision 4c17c04f214dbe4ce468ba6e06def8cbefbead74)
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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <time.h>
33 #include <signal.h>
34 #include <poll.h>
35 
36 #include "isns_server.h"
37 #include "isns_cache.h"
38 #include "isns_obj.h"
39 #include "isns_pdu.h"
40 #include "isns_func.h"
41 #include "isns_qry.h"
42 #include "isns_msgq.h"
43 #include "isns_log.h"
44 #include "isns_sched.h"
45 #include "isns_scn.h"
46 #include "isns_esi.h"
47 
48 /*
49  * global variables.
50  */
51 
52 /*
53  * local variables.
54  */
55 static ev_t *ev_list = NULL;
56 
57 static uint32_t stopwatch = 0;
58 static pthread_mutex_t stw_mtx = PTHREAD_MUTEX_INITIALIZER;
59 
60 static int wakeup = 0;
61 static pthread_mutex_t idl_mtx = PTHREAD_MUTEX_INITIALIZER;
62 static pthread_cond_t idl_cond = PTHREAD_COND_INITIALIZER;
63 
64 /*
65  * external variables.
66  */
67 extern const int UID_ATTR_INDEX[MAX_OBJ_TYPE_FOR_SIZE];
68 
69 extern boolean_t time_to_exit;
70 
71 extern msg_queue_t *sys_q;
72 
73 extern uint64_t esi_threshold;
74 
75 #ifdef DEBUG
76 extern void dump_pdu1(isns_pdu_t *);
77 #endif
78 
79 /*
80  * local functions.
81  */
82 static void *esi_monitor(void *);
83 
84 /*
85  * ****************************************************************************
86  *
87  * new_esi_portal:
88  *	Make a new portal for ESI event.
89  *
90  * uid	- the portal object UID.
91  * ip6	- the portal IPv6 format IP address.
92  * port	- the portal port.
93  * esip	- the ESI port.
94  * return - the new ESI portal.
95  *
96  * ****************************************************************************
97  */
98 static esi_portal_t *
99 new_esi_portal(
100 	uint32_t uid,
101 	in6_addr_t *ip6,
102 	uint32_t port,
103 	uint32_t esip
104 )
105 {
106 	esi_portal_t *p;
107 
108 	p = (esi_portal_t *)malloc(sizeof (esi_portal_t));
109 	if (p != NULL) {
110 		if (((int *)ip6)[0] == 0x00 &&
111 		    ((int *)ip6)[1] == 0x00 &&
112 		    ((uchar_t *)ip6)[8] == 0x00 &&
113 		    ((uchar_t *)ip6)[9] == 0x00 &&
114 		    ((uchar_t *)ip6)[10] == 0xFF &&
115 		    ((uchar_t *)ip6)[11] == 0xFF) {
116 			p->sz = sizeof (in_addr_t);
117 			p->ip4 = ((uint32_t *)ip6)[3];
118 		} else {
119 			p->sz = sizeof (in6_addr_t);
120 		}
121 		p->ip6 = ip6;
122 		p->port = port;
123 		p->esip = esip;
124 		p->ref = uid;
125 		p->so = 0;
126 		p->next = NULL;
127 	}
128 
129 	return (p);
130 }
131 
132 /*
133  * ****************************************************************************
134  *
135  * free_esi_portal:
136  *	Free a list of portal of one ESI event.
137  *
138  * p	- the ESI portal.
139  *
140  * ****************************************************************************
141  */
142 static void
143 free_esi_portal(
144 	esi_portal_t *p
145 )
146 {
147 	esi_portal_t *n;
148 
149 	while (p != NULL) {
150 		n = p->next;
151 		free(p->ip6);
152 		free(p);
153 		p = n;
154 	}
155 }
156 
157 /*
158  * ****************************************************************************
159  *
160  * ev_new:
161  *	Make a new ESI event.
162  *
163  * uid	- the Entity object UID.
164  * eid	- the Entity object name.
165  * len	- the length of the name.
166  * return - the ESI event.
167  *
168  * ****************************************************************************
169  */
170 static ev_t *
171 ev_new(
172 	uint32_t uid,
173 	uchar_t *eid,
174 	uint32_t len
175 )
176 {
177 	ev_t *ev;
178 
179 	ev = (ev_t *)malloc(sizeof (ev_t));
180 	if (ev != NULL) {
181 		if (pthread_mutex_init(&ev->mtx, NULL) != 0 ||
182 		    (ev->eid = (uchar_t *)malloc(len)) == NULL) {
183 			free(ev);
184 			return (NULL);
185 		}
186 		ev->uid = uid;
187 		(void) strcpy((char *)ev->eid, (char *)eid);
188 		ev->eid_len = len;
189 		/* initialization time */
190 		ev->flags = EV_FLAG_INIT;
191 	}
192 
193 	return (ev);
194 }
195 
196 /*
197  * ****************************************************************************
198  *
199  * cb_portal_uids:
200  *	Callback function which makes a copy of the portal child object
201  *	UIDs from a Network Entity object.
202  *
203  * p1	- the Network Entity object.
204  * p2	- the lookup control data.
205  * return - the number of portal object UIDs.
206  *
207  * ****************************************************************************
208  */
209 static int
210 cb_portal_uids(
211 	void *p1,
212 	void *p2
213 )
214 {
215 	isns_obj_t *obj = (isns_obj_t *)p1;
216 	lookup_ctrl_t *lcp = (lookup_ctrl_t *)p2;
217 
218 	isns_attr_t *attr;
219 
220 	uint32_t *cuidp;
221 
222 	uint32_t num = 0;
223 	uint32_t *p = NULL;
224 
225 	cuidp = get_child_t(obj, OBJ_PORTAL);
226 	if (cuidp != NULL) {
227 		p = (uint32_t *)malloc(*cuidp * sizeof (*p));
228 		if (p != NULL) {
229 			num = *cuidp ++;
230 			(void) memcpy(p, cuidp, num * sizeof (*p));
231 			lcp->data[1].ptr = (uchar_t *)p;
232 		}
233 	}
234 
235 	attr = &obj->attrs[ATTR_INDEX_ENTITY(ISNS_ENTITY_REG_PERIOD_ATTR_ID)];
236 	if (attr->tag != 0 && attr->value.ui != 0) {
237 		lcp->data[2].ui = attr->value.ui;
238 	} else {
239 		/* just one second before the end of the world */
240 		lcp->data[2].ui = INFINITY - 1;
241 	}
242 
243 	return (num);
244 }
245 
246 /*
247  * ****************************************************************************
248  *
249  * cb_esi_portal:
250  *	Callback function which gets ESI port number and ESI interval
251  *	from a portal object.
252  *
253  * p1	- the Portal object.
254  * p2	- the lookup control data.
255  * return - the ESI interval.
256  *
257  * ****************************************************************************
258  */
259 static int
260 cb_esi_portal(
261 	void *p1,
262 	void *p2
263 )
264 {
265 	uint32_t intval = 0;
266 
267 	isns_obj_t *obj;
268 	lookup_ctrl_t *lcp;
269 
270 	in6_addr_t *ip;
271 	uint32_t esip;
272 
273 	isns_attr_t *attr;
274 
275 	if (cb_clone_attrs(p1, p2) == 0) {
276 		obj = (isns_obj_t *)p1;
277 		lcp = (lookup_ctrl_t *)p2;
278 		ip = lcp->data[1].ip;
279 		esip = lcp->data[2].ui;
280 		if (esip != 0) {
281 			attr = &obj->attrs[ATTR_INDEX_PORTAL(
282 			    ISNS_PORTAL_PORT_ATTR_ID)];
283 			lcp->data[0].ui = attr->value.ui;
284 			attr = &obj->attrs[ATTR_INDEX_PORTAL(
285 			    ISNS_ESI_INTERVAL_ATTR_ID)];
286 			if (attr->tag != 0 && attr->value.ui != 0) {
287 				intval = attr->value.ui;
288 			} else {
289 				intval = DEFAULT_ESI_INTVAL;
290 			}
291 		} else {
292 			free(ip);
293 		}
294 	}
295 
296 	return ((int)intval);
297 }
298 
299 /*
300  * ****************************************************************************
301  *
302  * extract_esi_portal:
303  *	Extract a list of portal which have an ESI port for an Entity.
304  *
305  * uid	- the Entity object UID.
306  * intval - the ESI interval for returnning.
307  * return - the list of portals.
308  *
309  * ****************************************************************************
310  */
311 static esi_portal_t *
312 extract_esi_portal(
313 	uint32_t uid,
314 	uint32_t *intval
315 )
316 {
317 	esi_portal_t *list = NULL;
318 	esi_portal_t *p;
319 
320 	lookup_ctrl_t lc;
321 
322 	uint32_t num_of_portal;
323 	uint32_t *portal_uids;
324 
325 	uint32_t intv;
326 
327 	/* prepare for looking up entity object */
328 	SET_UID_LCP(&lc, OBJ_ENTITY, uid);
329 	lc.data[1].ptr = NULL;
330 	lc.data[2].ui = INFINITY - 1;
331 
332 	/* get the array of the portal uid(s) */
333 	num_of_portal = (uint32_t)cache_lookup(&lc, NULL, cb_portal_uids);
334 	portal_uids = (uint32_t *)lc.data[1].ptr;
335 	*intval = lc.data[2].ui;
336 
337 	/* prepare for looking up portal object(s) */
338 	SET_UID_LCP(&lc, OBJ_PORTAL, 0);
339 	lc.id[1] = ISNS_PORTAL_IP_ADDR_ATTR_ID;
340 	lc.id[2] = ISNS_ESI_PORT_ATTR_ID;
341 	FOR_EACH_OBJS(portal_uids, num_of_portal, uid, {
342 		if (uid != 0) {
343 			lc.data[0].ui = uid;
344 			intv = cache_lookup(&lc, NULL, cb_esi_portal);
345 			if (intv != 0) {
346 				p = new_esi_portal(uid,
347 				    (in6_addr_t *)lc.data[1].ip,
348 				    lc.data[0].ui, lc.data[2].ui);
349 				if (p != NULL) {
350 					p->next = list;
351 					list = p;
352 					if (*intval > intv) {
353 						*intval = intv;
354 					}
355 				}
356 			}
357 		}
358 	});
359 
360 	/* free up the portal uid array */
361 	free(portal_uids);
362 
363 	return (list);
364 }
365 
366 /*
367  * ****************************************************************************
368  *
369  * ev_add:
370  *	Add an ESI event.
371  *
372  * ev	- the ESI event.
373  * init	- 0: initialization time, otherwise not.
374  * return - error code.
375  *
376  * ****************************************************************************
377  */
378 static int
379 ev_add(
380 	ev_t *ev,
381 	int init
382 )
383 {
384 	uint32_t intval;
385 	esi_portal_t *p;
386 
387 	double rnd;
388 	uint32_t t = 0;
389 
390 	/* get the portal(s) which are registered for ESI monitoring */
391 	/* and the second interval for ESI or registration expiration */
392 	p = extract_esi_portal(ev->uid, &intval);
393 	ev->intval = intval;
394 	if (p != NULL) {
395 		ev->type = EV_ESI;
396 		ev->portal = p;
397 		/* avoid running everything at the same time */
398 		if (init != 0) {
399 			/* generate random number within range (0, 1] */
400 			rnd = (rand() + 1) / (double)(RAND_MAX + 1);
401 			t = (uint32_t)(intval * rnd);
402 		}
403 	} else {
404 		/* no portal is registered for ESI monitoring, make */
405 		/* an entry for entity registration expiration */
406 		ev->type = EV_REG_EXP;
407 		ev->portal = NULL;
408 		if (init != 0) {
409 			t = intval;
410 		}
411 	}
412 
413 	/* schedule the event */
414 	return (el_add(ev, t, NULL));
415 }
416 
417 /*
418  * global functions.
419  */
420 
421 /*
422  * ****************************************************************************
423  *
424  * sigalrm:
425  *	The signal handler for SIGALRM, the ESI proc uses the SIGALRM
426  *	for waking up to perform the client status inquery.
427  *
428  * sig	- the signal.
429  *
430  * ****************************************************************************
431  */
432 /*ARGSUSED*/
433 void
434 sigalrm(
435 	int sig
436 )
437 {
438 	/* wake up the idle */
439 	(void) pthread_mutex_lock(&idl_mtx);
440 	wakeup = 1; /* wake up naturally */
441 	(void) pthread_cond_signal(&idl_cond);
442 	(void) pthread_mutex_unlock(&idl_mtx);
443 }
444 
445 /*
446  * ****************************************************************************
447  *
448  * esi_load:
449  *	Load an ESI event from data store.
450  *
451  * uid	- the Entity object UID.
452  * eid	- the Entity object name.
453  * len	- the length of the name.
454  * return - error code.
455  *
456  * ****************************************************************************
457  */
458 int
459 esi_load(
460 	uint32_t uid,
461 	uchar_t *eid,
462 	uint32_t len
463 )
464 {
465 	int ec = 0;
466 
467 	/* make a new event */
468 	ev_t *ev = ev_new(uid, eid, len);
469 
470 	/* put the new event to the list */
471 	if (ev != NULL) {
472 		ev->next = ev_list;
473 		ev_list = ev;
474 	} else {
475 		ec = ISNS_RSP_INTERNAL_ERROR;
476 	}
477 
478 	return (ec);
479 }
480 
481 /*
482  * ****************************************************************************
483  *
484  * verify_esi_portal:
485  *	Verify ESI port and add the ESI entries after the ESI are loaded.
486  *
487  * return - error code.
488  *
489  * ****************************************************************************
490  */
491 int
492 verify_esi_portal(
493 )
494 {
495 	int ec = 0;
496 
497 	ev_t *ev;
498 
499 	/* add each event from the list */
500 	while (ev_list != NULL && ec == 0) {
501 		ev = ev_list;
502 		ev_list = ev->next;
503 		ev->next = NULL;
504 		ec = ev_add(ev, 1);
505 	}
506 
507 	return (ec);
508 }
509 
510 /*
511  * ****************************************************************************
512  *
513  * esi_add:
514  *	Add a new ESI event when a new Entity is registered.
515  *
516  * uid	- the Entity object UID.
517  * eid	- the Entity object name.
518  * len	- the length of the name.
519  * return - error code.
520  *
521  * ****************************************************************************
522  */
523 int
524 esi_add(
525 	uint32_t uid,
526 	uchar_t *eid,
527 	uint32_t len
528 )
529 {
530 	int ec = 0;
531 
532 	/* make a new event */
533 	ev_t *ev = ev_new(uid, eid, len);
534 
535 	if (ev != NULL) {
536 		/* interrupt idle */
537 		ev->flags |= EV_FLAG_WAKEUP;
538 		ec = ev_add(ev, 0);
539 	} else {
540 		ec = ISNS_RSP_INTERNAL_ERROR;
541 	}
542 
543 	return (ec);
544 }
545 
546 /*
547  * ****************************************************************************
548  *
549  * esi_remove:
550  *	Remove an ESI event immediately.
551  *
552  * uid	- the Entity object UID.
553  * return - always successful.
554  *
555  * ****************************************************************************
556  */
557 int
558 esi_remove(
559 	uint32_t uid
560 )
561 {
562 	(void) el_remove(uid, 0, 0);
563 
564 	return (0);
565 }
566 
567 /*
568  * ****************************************************************************
569  *
570  * esi_remove_obj:
571  *	Update an ESI event when a Entity object or a Portal object is
572  *	removed from server. If the object is being removed because of
573  *	ESI failure, the ESI event will be removed with a pending time,
574  *	otherwise, the ESI will be removed immediately.
575  *
576  * obj	- the object being removed.
577  * pending - the pending flag.
578  * return - always successful.
579  *
580  * ****************************************************************************
581  */
582 int
583 esi_remove_obj(
584 	const isns_obj_t *obj,
585 	int pending
586 )
587 {
588 	uint32_t puid, uid;
589 
590 	switch (obj->type) {
591 	case OBJ_PORTAL:
592 		puid = get_parent_uid(obj);
593 		uid = get_obj_uid(obj);
594 		break;
595 	case OBJ_ENTITY:
596 		puid = get_obj_uid(obj);
597 		uid = 0;
598 		break;
599 	default:
600 		puid = 0;
601 		break;
602 	}
603 
604 	if (puid != 0) {
605 		(void) el_remove(puid, uid, pending);
606 	}
607 
608 	return (0);
609 }
610 
611 /*
612  * ****************************************************************************
613  *
614  * get_stopwatch:
615  *	Get the stopwatch. It might need to signal the condition to
616  *	wake up the idle so the stopwatch gets updated.
617  *
618  * flag	- wake up flag.
619  * return - the stopwatch.
620  *
621  * ****************************************************************************
622  */
623 uint32_t
624 get_stopwatch(
625 	int flag
626 )
627 {
628 	uint32_t t;
629 
630 	/* not re-schedule, wake up idle */
631 	(void) pthread_mutex_lock(&idl_mtx);
632 	if (flag != 0) {
633 		wakeup = 2; /* wake up manually */
634 		(void) pthread_cond_signal(&idl_cond);
635 	} else {
636 		wakeup = 0; /* clear previous interruption */
637 	}
638 	(void) pthread_mutex_unlock(&idl_mtx);
639 
640 	/* get most current time */
641 	(void) pthread_mutex_lock(&stw_mtx);
642 	t = stopwatch;
643 	(void) pthread_mutex_unlock(&stw_mtx);
644 
645 	return (t);
646 }
647 
648 /*
649  * ****************************************************************************
650  *
651  * ev_intval:
652  *	Get the time interval of an ESI event.
653  *
654  * p	- the ESI event.
655  * return - the time interval.
656  *
657  * ****************************************************************************
658  */
659 uint32_t
660 ev_intval(
661 	void *p
662 )
663 {
664 	return (((ev_t *)p)->intval);
665 }
666 
667 /*
668  * ****************************************************************************
669  *
670  * ev_match:
671  *	Check the ESI event maching an Entity object.
672  *
673  * p	- the ESI event.
674  * uid	- the Entity object UID.
675  * return - 1: match, otherwise not.
676  *
677  * ****************************************************************************
678  */
679 int
680 ev_match(
681 	void *p,
682 	uint32_t uid
683 )
684 {
685 	if (((ev_t *)p)->uid == uid) {
686 		return (1);
687 	} else {
688 		return (0);
689 	}
690 }
691 
692 /*
693  * ****************************************************************************
694  *
695  * ev_remove:
696  *	Remove a portal or an ESI event. If all of ESI portal has been
697  *	removed, the ESI event will be marked as removal pending, which
698  *	will result in removing the Entity object after the pending time.
699  *
700  * p	- the ESI event.
701  * portal_uid	- the Portal object UID.
702  * flag	- 0: the ESI is currently in use, otherwise it is scheduled.
703  * pending	- flag for the ESI removal pending.
704  * return - 0: the ESI is physically removed, otherwise not.
705  *
706  * ****************************************************************************
707  */
708 int
709 ev_remove(
710 	void *p,
711 	uint32_t portal_uid,
712 	int flag,
713 	int pending
714 )
715 {
716 	ev_t *ev = (ev_t *)p;
717 	esi_portal_t **pp, *portal;
718 
719 	int has_portal = 0;
720 	int state;
721 
722 	/* remove one portal only */
723 	if (portal_uid != 0) {
724 		pp = &ev->portal;
725 		portal = *pp;
726 		while (portal != NULL) {
727 			/* found the match portal */
728 			if (portal->ref == portal_uid) {
729 				/* mark it as removed */
730 				portal->ref = 0;
731 				if (flag != 0) {
732 					/* not in use, remove it physically */
733 					*pp = portal->next;
734 					portal->next = NULL;
735 					free_esi_portal(portal);
736 				} else {
737 					pp = &portal->next;
738 				}
739 			} else {
740 				/* one or more esi portals are available */
741 				if (portal->ref != 0) {
742 					has_portal = 1;
743 				}
744 				pp = &portal->next;
745 			}
746 			portal = *pp;
747 		}
748 	}
749 
750 	/* no portal available */
751 	if (has_portal == 0) {
752 		state = (pending << 1) | flag;
753 		switch (state) {
754 		case 0x0:
755 			/* mark the event as removed */
756 			ev->flags |= EV_FLAG_REMOVE;
757 			isnslog(LOG_DEBUG, "ev_remove",
758 			    "%s [%d] is marked as removed.",
759 			    ev->type == EV_ESI ? "ESI" : "REG_EXP",
760 			    ev->uid);
761 			break;
762 		case 0x1:
763 			/* physically remove the event */
764 			ev_free(ev);
765 			break;
766 		case 0x2:
767 		case 0x3:
768 			/* mark the event as removal pending */
769 			isnslog(LOG_DEBUG, "ev_remove",
770 			    "%s [%d] is marked as removal pending.",
771 			    ev->type == EV_ESI ? "ESI" : "REG_EXP",
772 			    ev->uid);
773 			ev->flags |= EV_FLAG_REM_P1;
774 			has_portal = 1;
775 			break;
776 		default:
777 			break;
778 		}
779 	} else {
780 		isnslog(LOG_DEBUG, "ev_remove", "%s [%d] removed portal %d.",
781 		    ev->type == EV_ESI ? "ESI" : "REG_EXP",
782 		    ev->uid, portal_uid);
783 	}
784 
785 	return (has_portal);
786 }
787 
788 /*
789  * ****************************************************************************
790  *
791  * ev_free:
792  *	Free an ESI event.
793  *
794  * p	- the ESI event.
795  *
796  * ****************************************************************************
797  */
798 void
799 ev_free(
800 	void *p
801 )
802 {
803 	ev_t *ev = (ev_t *)p;
804 
805 	/* free up all of portals */
806 	free_esi_portal(ev->portal);
807 
808 	isnslog(LOG_DEBUG, "ev_free",
809 	    "%s [%d] is physically removed.",
810 	    ev->type == EV_ESI ? "ESI" : "REG_EXP",
811 	    ev->uid);
812 
813 	free(ev->eid);
814 
815 	/* free the event */
816 	free(ev);
817 }
818 
819 /*
820  * ****************************************************************************
821  *
822  * evf_init:
823  *	Check the initial flag of an ESI event.
824  *
825  * p	- the ESI event.
826  * return - 0: not initial, otherwise yes.
827  *
828  * ****************************************************************************
829  */
830 int
831 evf_init(
832 	void *p
833 )
834 {
835 	return (((ev_t *)p)->flags & EV_FLAG_INIT);
836 }
837 
838 /*
839  * ****************************************************************************
840  *
841  * evf_again:
842  *	Check the again flag of an ESI event.
843  *	(this flag might be eliminated and use the init flag.)
844  *
845  * p	- the ESI event.
846  * return - 0: not again, otherwise yes.
847  *
848  * ****************************************************************************
849  */
850 int
851 evf_again(
852 	void *p
853 )
854 {
855 	return (((ev_t *)p)->flags & EV_FLAG_AGAIN);
856 }
857 
858 /*
859  * ****************************************************************************
860  *
861  * evf_wakeup:
862  *	Check the wakeup flag of an ESI event. The idle might need to
863  *	wake up before the event is scheduled.
864  *
865  * p	- the ESI event.
866  * return - 0: no wakeup, otherwise yes.
867  *
868  * ****************************************************************************
869  */
870 int
871 evf_wakeup(
872 	void *p
873 )
874 {
875 	return (((ev_t *)p)->flags & EV_FLAG_WAKEUP);
876 }
877 
878 /*
879  * ****************************************************************************
880  *
881  * evf_rem:
882  *	Check the removal flag of an ESI event. The ESI entry might be
883  *	marked as removal.
884  *
885  * p	- the ESI event.
886  * return - 0: not removed, otherwise yes.
887  *
888  * ****************************************************************************
889  */
890 int
891 evf_rem(
892 	void *p
893 )
894 {
895 	return (((ev_t *)p)->flags & EV_FLAG_REMOVE);
896 }
897 
898 /*
899  * ****************************************************************************
900  *
901  * evf_rem_pending:
902  *	Check the removal pending flag of an ESI event. The ESI entry
903  *	might be marked as removal pending. If it is, we will switch the
904  *	event type and change the time interval.
905  *
906  * p	- the ESI event.
907  * return - 0: not removal pending, otherwise yes.
908  *
909  * ****************************************************************************
910  */
911 int
912 evf_rem_pending(
913 	void *p
914 )
915 {
916 	ev_t *ev = (ev_t *)p;
917 	if ((ev->flags & EV_FLAG_REM_P) != 0) {
918 		if (ev->type != EV_REG_EXP) {
919 			isnslog(LOG_DEBUG, "ev_rem_pending",
920 			    "%s [%d] is changed to REG_EXP.",
921 			    ev->type == EV_ESI ? "ESI" : "REG_EXP",
922 			    ev->uid);
923 			ev->type = EV_REG_EXP;
924 			ev->intval *= 2; /* after 2 ESI interval */
925 		}
926 		return (1);
927 	}
928 
929 	return (0);
930 }
931 
932 /*
933  * ****************************************************************************
934  *
935  * evf_zero:
936  *	Reset the event flag.
937  *
938  * p	- the ESI event.
939  *
940  * ****************************************************************************
941  */
942 void
943 evf_zero(
944 	void *p
945 )
946 {
947 	ev_t *ev = (ev_t *)p;
948 
949 	/* not acutally clear it, need to set again flag */
950 	/* and keep the removal pending flag */
951 	ev->flags = EV_FLAG_AGAIN | (ev->flags & EV_FLAG_REM_P);
952 }
953 
954 /*
955  * ****************************************************************************
956  *
957  * evl_append:
958  *	Append an ESI event to the list, the list contains all of
959  *	ESI events which are being processed at present.
960  *
961  * p	- the ESI event.
962  *
963  * ****************************************************************************
964  */
965 void
966 evl_append(
967 	void *p
968 )
969 {
970 	ev_t *ev;
971 
972 	ev = (ev_t *)p;
973 	ev->next = ev_list;
974 	ev_list = ev;
975 }
976 
977 /*
978  * ****************************************************************************
979  *
980  * evl_strip:
981  *	Strip off an ESI event from the list after the event is being
982  *	processed, it will be scheduled in the scheduler.
983  *
984  * p	- the ESI event.
985  *
986  * ****************************************************************************
987  */
988 void
989 evl_strip(
990 	void *p
991 )
992 {
993 	ev_t **evp = &ev_list;
994 	ev_t *ev = *evp;
995 
996 	while (ev != NULL) {
997 		if (ev == p) {
998 			*evp = ev->next;
999 			break;
1000 		}
1001 		evp = &ev->next;
1002 		ev = *evp;
1003 	}
1004 }
1005 
1006 /*
1007  * ****************************************************************************
1008  *
1009  * evl_remove:
1010  *	Remove an ESI event or a portal of an ESI event from the event list.
1011  *
1012  * id1	- the Entity object UID.
1013  * id2	- the Portal object UID.
1014  * pending - the pending flag.
1015  * return - 1: found a match event, otherwise not.
1016  *
1017  * ****************************************************************************
1018  */
1019 int
1020 evl_remove(
1021 	uint32_t id1,
1022 	uint32_t id2,
1023 	int pending
1024 )
1025 {
1026 	ev_t *ev = ev_list;
1027 
1028 	while (ev != NULL) {
1029 		/* found it */
1030 		if (ev_match(ev, id1) != 0) {
1031 			/* lock the event */
1032 			(void) pthread_mutex_lock(&ev->mtx);
1033 			/* mark it as removed */
1034 			(void) ev_remove(ev, id2, 0, pending);
1035 			/* unlock the event */
1036 			(void) pthread_mutex_unlock(&ev->mtx);
1037 			/* tell caller removal is done */
1038 			return (1);
1039 		}
1040 		ev = ev->next;
1041 	}
1042 
1043 	/* not found it */
1044 	return (0);
1045 }
1046 
1047 #define	ALARM_MAX	(21427200)
1048 
1049 /*
1050  * ****************************************************************************
1051  *
1052  * idle:
1053  *	Idle for certain amount of time or a wakeup signal is recieved.
1054  *
1055  * t	- the idle time.
1056  * return - the time that idle left.
1057  *
1058  * ****************************************************************************
1059  */
1060 static int
1061 idle(
1062 	uint32_t t
1063 )
1064 {
1065 	uint32_t t1, t2, t3 = 0;
1066 	int idl_int = 0;
1067 
1068 	/* hold the mutex for stopwatch update */
1069 	(void) pthread_mutex_lock(&stw_mtx);
1070 
1071 	do {
1072 		if (t > ALARM_MAX) {
1073 			t1 = ALARM_MAX;
1074 		} else {
1075 			t1 = t;
1076 		}
1077 
1078 		/* start alarm */
1079 		(void) alarm(t1);
1080 
1081 		/* hold the mutex for idle condition */
1082 		(void) pthread_mutex_lock(&idl_mtx);
1083 
1084 		/* wait on condition variable to wake up idle */
1085 		while (wakeup == 0) {
1086 			(void) pthread_cond_wait(&idl_cond, &idl_mtx);
1087 		}
1088 		if (wakeup == 2) {
1089 			idl_int = 1;
1090 		}
1091 		/* clean wakeup flag */
1092 		wakeup = 0;
1093 
1094 		/* release the mutex for idle condition */
1095 		(void) pthread_mutex_unlock(&idl_mtx);
1096 
1097 		/* stop alarm */
1098 		t2 = alarm(0);
1099 
1100 		/* seconds actually slept */
1101 		t3 += t1 - t2;
1102 		t -= t3;
1103 	} while (t > 0 && idl_int == 0);
1104 
1105 	/* increate the stopwatch by the actually slept time */
1106 	stopwatch += t3;
1107 
1108 	/* release the mutex after stopwatch is updated */
1109 	(void) pthread_mutex_unlock(&stw_mtx);
1110 
1111 	/* return the amount of time which is not slept */
1112 	return (t);
1113 }
1114 
1115 /*
1116  * ****************************************************************************
1117  *
1118  * ev_ex:
1119  *	Execute an event. To inquiry the client status or
1120  *	perform registration expiration.
1121  *
1122  * ev	- the event.
1123  *
1124  * ****************************************************************************
1125  */
1126 static void
1127 ev_ex(
1128 	ev_t *ev
1129 )
1130 {
1131 	pthread_t tid;
1132 
1133 	switch (ev->type) {
1134 	case EV_ESI:
1135 		if (pthread_create(&tid, NULL,
1136 		    esi_monitor, (void *)ev) != 0) {
1137 			isnslog(LOG_DEBUG, "ev_ex", "pthread_create() failed.");
1138 			/* reschedule for next occurence */
1139 			(void) el_add(ev, 0, NULL);
1140 		} else {
1141 			/* increase the thread ref count */
1142 			inc_thr_count();
1143 		}
1144 		break;
1145 	case EV_REG_EXP:
1146 		(void) queue_msg_set(sys_q, REG_EXP, (void *)ev);
1147 		break;
1148 	default:
1149 		break;
1150 	}
1151 }
1152 
1153 /*
1154  * ****************************************************************************
1155  *
1156  * esi_proc:
1157  *	ESI thread entry, which:
1158  *	1: fetch an event from schedule,
1159  *	2: idle for some time,
1160  *	3: execute the event or re-schedule it,
1161  *	4: repeat from step 1 before server is being shutdown.
1162  *
1163  * arg	- the thread argument.
1164  *
1165  * ****************************************************************************
1166  */
1167 /*ARGSUSED*/
1168 void *
1169 esi_proc(
1170 	void *arg
1171 )
1172 {
1173 	uint32_t t, t1, pt;
1174 	ev_t *ev;
1175 
1176 	void *evp;
1177 
1178 	while (time_to_exit == B_FALSE) {
1179 		ev = (ev_t *)el_first(&pt);
1180 
1181 		/* caculate the idle time */
1182 		if (ev != NULL) {
1183 			if (pt > stopwatch) {
1184 				t = pt - stopwatch;
1185 			} else {
1186 				t = 0;
1187 			}
1188 		} else {
1189 			t = INFINITY;
1190 		}
1191 
1192 		do {
1193 			/* block for a certain amount of time */
1194 			if (t > 0) {
1195 				isnslog(LOG_DEBUG, "esi_proc",
1196 				    "idle for %d seconds.", t);
1197 				t1 = idle(t);
1198 			} else {
1199 				t1 = 0;
1200 			}
1201 			if (t1 > 0) {
1202 				isnslog(LOG_DEBUG, "esi_proc",
1203 				    "idle interrupted after idle for "
1204 				    "%d seconds.", t - t1);
1205 			}
1206 			if (time_to_exit != B_FALSE) {
1207 				ev = NULL; /* force break */
1208 			} else if (ev != NULL) {
1209 				if (t1 > 0) {
1210 					/* not naturally waken up */
1211 					/* reschedule current event */
1212 					evp = NULL;
1213 					(void) el_add(ev, pt, &evp);
1214 					ev = (ev_t *)evp;
1215 					t = t1;
1216 				} else {
1217 					/* excute */
1218 					isnslog(LOG_DEBUG, "esi_proc",
1219 					    "excute the cron job[%d].",
1220 					    ev->uid);
1221 					ev_ex(ev);
1222 					ev = NULL;
1223 				}
1224 			}
1225 		} while (ev != NULL);
1226 	}
1227 
1228 	return (NULL);
1229 }
1230 
1231 /*
1232  * ****************************************************************************
1233  *
1234  * esi_ping:
1235  *	Ping the client with the ESI retry threshold for status inquiry.
1236  *
1237  * so	- the socket descriptor.
1238  * pdu	- the ESI packet.
1239  * pl	- the length of packet.
1240  * return - 1: status inquired, otherwise not.
1241  *
1242  * ****************************************************************************
1243  */
1244 static int
1245 esi_ping(
1246 	int so,
1247 	isns_pdu_t *pdu,
1248 	size_t pl
1249 )
1250 {
1251 	int try_cnt = 0;
1252 	isns_pdu_t *rsp = NULL;
1253 	size_t rsp_sz;
1254 
1255 	int alive = 0;
1256 
1257 	do {
1258 		if (isns_send_pdu(so, pdu, pl) == 0) {
1259 			if (isns_rcv_pdu(so, &rsp, &rsp_sz,
1260 			    ISNS_RCV_SHORT_TIMEOUT) > 0) {
1261 #ifdef DEBUG
1262 				dump_pdu1(rsp);
1263 #endif
1264 				alive = 1;
1265 				break;
1266 			}
1267 		} else {
1268 			/* retry after 1 second */
1269 			(void) sleep(1);
1270 		}
1271 		try_cnt ++;
1272 	} while (try_cnt < esi_threshold);
1273 
1274 	if (rsp != NULL) {
1275 		free(rsp);
1276 	}
1277 
1278 	return (alive);
1279 }
1280 
1281 /*
1282  * ****************************************************************************
1283  *
1284  * esi_monitor:
1285  *	Child thread for client status mornitoring.
1286  *
1287  * arg	- the ESI event.
1288  *
1289  * ****************************************************************************
1290  */
1291 static void *
1292 esi_monitor(
1293 	void *arg
1294 )
1295 {
1296 	ev_t *ev = (ev_t *)arg;
1297 
1298 	esi_portal_t *p;
1299 	int so;
1300 
1301 	isns_pdu_t *pdu = NULL;
1302 	size_t sz;
1303 	size_t pl;
1304 	size_t half;
1305 
1306 	time_t t;
1307 
1308 	int feedback;
1309 
1310 	/* lock the event for esi monitoring */
1311 	(void) pthread_mutex_lock(&ev->mtx);
1312 
1313 	if (evf_rem(ev) != 0) {
1314 		goto mon_done;
1315 	} else if (evf_rem_pending(ev) != 0) {
1316 		goto mon_done;
1317 	}
1318 
1319 	/* timestamp */
1320 	t = time(NULL);
1321 
1322 	/* allocate ESI PDU */
1323 	if (pdu_reset_esi(&pdu, &pl, &sz) != 0 ||
1324 	    pdu_add_tlv(&pdu, &pl, &sz,
1325 	    ISNS_TIMESTAMP_ATTR_ID, 8, (void *)&t, 1) != 0 ||
1326 	    pdu_add_tlv(&pdu, &pl, &sz,
1327 	    ISNS_EID_ATTR_ID, ev->eid_len, (void *)ev->eid, 0) != 0) {
1328 		/* no memory, will retry later */
1329 		goto mon_done;
1330 	}
1331 
1332 	/* set pdu head */
1333 	pdu->version = htons((uint16_t)ISNSP_VERSION);
1334 	pdu->func_id = htons((uint16_t)ISNS_ESI);
1335 	pdu->xid = htons(get_server_xid());
1336 
1337 	/* keep the current lenght of the playload */
1338 	half = pl;
1339 
1340 	p = ev->portal;
1341 	while (p != NULL) {
1342 		if (p->ref != 0 &&
1343 		    /* skip IPv6 portal */
1344 		    p->sz != sizeof (in6_addr_t) &&
1345 		    pdu_add_tlv(&pdu, &pl, &sz,
1346 		    ISNS_PORTAL_IP_ADDR_ATTR_ID,
1347 		    sizeof (in6_addr_t), (void *)p->ip6, 0) == 0 &&
1348 		    pdu_add_tlv(&pdu, &pl, &sz,
1349 		    ISNS_PORTAL_PORT_ATTR_ID,
1350 		    4, (void *)p->port, 0) == 0) {
1351 			/* connect once */
1352 			so = connect_to(p->sz, p->ip4, p->ip6, p->esip);
1353 			if (so != -1) {
1354 				feedback = esi_ping(so, pdu, pl);
1355 				(void) close(so);
1356 				/* p->so = so; */
1357 			} else {
1358 				/* cannot connect, portal is dead */
1359 				feedback = 0;
1360 			}
1361 			if (feedback == 0) {
1362 				isnslog(LOG_DEBUG, "esi_monitor",
1363 				    "ESI ping failed.");
1364 				(void) queue_msg_set(sys_q, DEAD_PORTAL,
1365 				    (void *)p->ref);
1366 			} else {
1367 				goto mon_done;
1368 			}
1369 		}
1370 		pl = half;
1371 		p = p->next;
1372 	}
1373 
1374 mon_done:
1375 	/* unlock the event after esi monitoring is done */
1376 	(void) pthread_mutex_unlock(&ev->mtx);
1377 
1378 	/* clean up pdu */
1379 	if (pdu != NULL) {
1380 		free(pdu);
1381 	}
1382 
1383 	/* set reschedule flags */
1384 	ev->flags |= EV_FLAG_WAKEUP;
1385 
1386 	/* reschedule for next occurence */
1387 	(void) el_add(ev, 0, NULL);
1388 
1389 	/* decrease the thread ref count */
1390 	dec_thr_count();
1391 
1392 	return (NULL);
1393 }
1394 
1395 /*
1396  * ****************************************************************************
1397  *
1398  * portal_dies:
1399  *	Handles the dead portal that ESI detected.
1400  *
1401  * uid	- the Portal object UID.
1402  *
1403  * ****************************************************************************
1404  */
1405 void
1406 portal_dies(
1407 	uint32_t uid
1408 )
1409 {
1410 	int ec = 0;
1411 
1412 	lookup_ctrl_t lc;
1413 
1414 	/* prepare the lookup control for deregistration */
1415 	SET_UID_LCP(&lc, OBJ_PORTAL, uid);
1416 
1417 	/* lock the cache for object deregistration */
1418 	(void) cache_lock_write();
1419 
1420 	/* deregister the portal */
1421 	ec = dereg_object(&lc, 1);
1422 
1423 	/* unlock cache and sync with data store */
1424 	(void) cache_unlock_sync(ec);
1425 }
1426 
1427 /*
1428  * ****************************************************************************
1429  *
1430  * portal_dies:
1431  *	Handles the Entity registration expiration.
1432  *
1433  * p	- the ESI event.
1434  *
1435  * ****************************************************************************
1436  */
1437 void
1438 reg_expiring(
1439 	void *p
1440 )
1441 {
1442 	int ec = 0;
1443 	ev_t *ev = (ev_t *)p;
1444 	lookup_ctrl_t lc;
1445 
1446 	/* prepare the lookup control for deregistration */
1447 	SET_UID_LCP(&lc, OBJ_ENTITY, ev->uid);
1448 
1449 	/* lock the cache for object deregistration */
1450 	(void) cache_lock_write();
1451 
1452 	if (evf_rem(ev) == 0) {
1453 		/* deregister the entity */
1454 		ec = dereg_object(&lc, 0);
1455 
1456 		/* unlock cache and sync with data store */
1457 		ec = cache_unlock_sync(ec);
1458 
1459 		if (ec == 0) {
1460 			/* successfuk, mark ev as removed */
1461 			ev->flags |= EV_FLAG_REMOVE;
1462 		} else {
1463 			/* failed, retry after 3 mintues */
1464 			ev->intval = 3 * 60;
1465 			isnslog(LOG_DEBUG, "reg_expiring",
1466 			    "dereg failed, retry after 3 mintues.");
1467 		}
1468 	} else {
1469 		/* ev is marked as removed, no need to dereg */
1470 		(void) cache_unlock_nosync();
1471 	}
1472 
1473 	/* reschedule it for next occurence */
1474 	(void) el_add(ev, 0, NULL);
1475 }
1476