xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/ncp.c (revision 6ba597c5)
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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <arpa/inet.h>
28 #include <assert.h>
29 #include <libdllink.h>
30 #include <libdlstat.h>
31 #include <libnwam.h>
32 #include <libscf.h>
33 #include <netinet/in.h>
34 #include <stdlib.h>
35 #include <sys/socket.h>
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #include <values.h>
39 
40 #include "conditions.h"
41 #include "events.h"
42 #include "objects.h"
43 #include "ncp.h"
44 #include "ncu.h"
45 #include "util.h"
46 
47 /*
48  * ncp.c - handles NCP actions.
49  */
50 
51 char active_ncp[NWAM_MAX_NAME_LEN];
52 nwam_ncp_handle_t active_ncph = NULL;
53 int64_t current_ncu_priority_group = INVALID_PRIORITY_GROUP;
54 /*
55  * active_ncp_mutex protects active_ncp, active_ncph and
56  * current_ncu_priority_group.
57  */
58 pthread_mutex_t active_ncp_mutex = PTHREAD_MUTEX_INITIALIZER;
59 
60 /*
61  * The variable ncu_wait_time specifies how long to wait to obtain a
62  * DHCP lease before giving up on that NCU and moving on to the next/lower
63  * priority-group.
64  */
65 uint64_t ncu_wait_time = NCU_WAIT_TIME_DEFAULT;
66 
67 /*
68  * Specifies if this is the first time the NCP has been enabled. True
69  * on startup so that we can differentiate between when we start up
70  * with a given NCP versus when we are asked to reenable it.
71  */
72 boolean_t initial_ncp_enable = B_TRUE;
73 
74 /*
75  * nwamd_ncp_handle_enable_event() should be called in the event handling
76  * loop in response to an _ENABLE event, triggered as a result of an
77  * nwam_ncp_enable() call from a libnwam consumer.  To enable the new NCP,
78  * we first call nwamd_fini_ncus() on the old NCP.  This results in enqueueing
79  * of a set of _FINI events for each NCU.  These events are handled and in
80  * order to tear down config, (online*, uninitialized) state change events
81  * are created and consumed directly by the fini event handler (these events
82  * are not enqueued as this would result in state events for the old NCP
83  * appearing after the new NCP has been enabled.  After the _FINI events are
84  * enqueued, we enqueue an NCP _OBJECT_STATE event for the new NCP.  Since
85  * it is enqueued after the _FINI events, we are guaranteed no events for the
86  * old NCP will appear after the new NCP is activated.
87  */
88 void
89 nwamd_ncp_handle_enable_event(nwamd_event_t event)
90 {
91 	char *new_ncp = event->event_object;
92 	nwam_ncp_handle_t new_ncph;
93 	nwam_error_t err;
94 
95 	if (new_ncp[0] == '\0')
96 		return;
97 
98 	(void) pthread_mutex_lock(&active_ncp_mutex);
99 	if (strcmp(active_ncp, new_ncp) == 0 && !initial_ncp_enable) {
100 		nlog(LOG_DEBUG, "nwamd_ncp_handle_enable_event: "
101 		    "%s is already active", new_ncp);
102 		(void) pthread_mutex_unlock(&active_ncp_mutex);
103 		return;
104 	}
105 	(void) pthread_mutex_unlock(&active_ncp_mutex);
106 
107 	nlog(LOG_DEBUG, "nwamd_ncp_handle_enable_event: activating NCP %s",
108 	    new_ncp);
109 
110 	/*
111 	 * To activate new NCP, run nwamd_fini_ncus(), reset the active
112 	 * priority-group, set the active_ncp property and refresh the
113 	 * daemon.  The refresh action will trigger a re-read of the NCUs
114 	 * for the activated NCP.
115 	 */
116 
117 	nwamd_fini_ncus();
118 
119 	err = nwam_ncp_read(new_ncp, 0, &new_ncph);
120 	switch (err) {
121 	case NWAM_ENTITY_NOT_FOUND:
122 		err = nwam_ncp_create(new_ncp, 0, &new_ncph);
123 		break;
124 	case NWAM_SUCCESS:
125 		break;
126 	default:
127 		nlog(LOG_ERR, "nwamd_ncp_handle_enable_event: error %s",
128 		    nwam_strerror(err));
129 		return;
130 	}
131 	nwam_ncp_free(new_ncph);
132 
133 	if (err == NWAM_SUCCESS) {
134 		nwamd_object_set_state(NWAM_OBJECT_TYPE_NCP, new_ncp,
135 		    NWAM_STATE_ONLINE, NWAM_AUX_STATE_ACTIVE);
136 	} else {
137 		nlog(LOG_ERR, "nwamd_ncp_handle_enable_event: error %s",
138 		    nwam_strerror(err));
139 		return;
140 	}
141 }
142 
143 void
144 nwamd_ncp_handle_action_event(nwamd_event_t event)
145 {
146 	switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
147 	case NWAM_ACTION_ENABLE:
148 		nwamd_ncp_handle_enable_event(event);
149 		break;
150 	case NWAM_ACTION_ADD:
151 	case NWAM_ACTION_DESTROY:
152 		/* nothing to do */
153 		break;
154 	default:
155 		nlog(LOG_INFO, "nwam_ncp_handle_action_event: "
156 		    "unexpected action");
157 		nwamd_event_do_not_send(event);
158 		break;
159 	}
160 }
161 
162 /*
163  * The only state events we create are (online, active) events which are
164  * generated as part of an NCP enable action (see above).
165  */
166 void
167 nwamd_ncp_handle_state_event(nwamd_event_t event)
168 {
169 	char *new_ncp = event->event_object;
170 	nwam_ncp_handle_t new_ncph, old_ncph;
171 	nwam_error_t err;
172 
173 	/* The NCP to be activated should always exist. */
174 	if ((err = nwam_ncp_read(new_ncp, 0, &new_ncph)) != NWAM_SUCCESS) {
175 		nlog(LOG_ERR, "nwamd_ncp_handle_state_event: "
176 		    "cannot read NCP %s: : %s", new_ncp, nwam_strerror(err));
177 		nwamd_event_do_not_send(event);
178 		return;
179 	}
180 
181 	/*
182 	 * To activate new NCP, reset the active priority-group, set the
183 	 * active_ncp property and refresh the daemon.  The refresh action will
184 	 * trigger a re-read of the NCUs for the activated NCP.
185 	 */
186 	(void) pthread_mutex_lock(&active_ncp_mutex);
187 	old_ncph = active_ncph;
188 	active_ncph = new_ncph;
189 	nwam_ncp_free(old_ncph);
190 	current_ncu_priority_group = INVALID_PRIORITY_GROUP;
191 	(void) strlcpy(active_ncp, event->event_object,
192 	    sizeof (active_ncp));
193 	(void) pthread_mutex_unlock(&active_ncp_mutex);
194 	(void) nwamd_set_string_property(OUR_FMRI, OUR_PG,
195 	    OUR_ACTIVE_NCP_PROP_NAME, new_ncp);
196 	(void) smf_refresh_instance(OUR_FMRI);
197 	initial_ncp_enable = B_FALSE;
198 }
199 
200 int
201 nwamd_ncp_action(const char *ncp, nwam_action_t action)
202 {
203 	nwamd_event_t event = nwamd_event_init_object_action
204 	    (NWAM_OBJECT_TYPE_NCP, ncp, NULL, action);
205 	if (event == NULL)
206 		return (1);
207 	nwamd_event_enqueue(event);
208 	return (0);
209 }
210 
211 /*
212  * Below this point are routines handling NCU prioritization
213  * policy for the active NCP.
214  */
215 
216 struct priority_group_cbarg {
217 	uint64_t minpriority;
218 	uint64_t currpriority;
219 	boolean_t found;
220 };
221 
222 /* Callback used to find next pg in NCP that is >= start_pg */
223 static int
224 find_next_priority_group_cb(nwamd_object_t object, void *data)
225 {
226 	struct priority_group_cbarg *cbarg = data;
227 	uint64_t priority;
228 	nwamd_ncu_t *ncu = object->nwamd_object_data;
229 
230 	if (ncu->ncu_node.u_link.nwamd_link_activation_mode !=
231 	    NWAM_ACTIVATION_MODE_PRIORITIZED)
232 		return (0);
233 
234 	priority = ncu->ncu_node.u_link.nwamd_link_priority_group;
235 
236 	if (priority >= cbarg->minpriority && priority < cbarg->currpriority) {
237 		cbarg->found = B_TRUE;
238 		cbarg->currpriority = priority;
239 	}
240 	return (0);
241 }
242 
243 
244 /* Set current_pg to next pg in NCP that is >= start_pg */
245 boolean_t
246 nwamd_ncp_find_next_priority_group(int64_t minpriority,
247     int64_t *nextpriorityp)
248 {
249 	struct priority_group_cbarg cbarg;
250 
251 	cbarg.minpriority = minpriority;
252 	cbarg.currpriority = MAXINT;
253 	cbarg.found = B_FALSE;
254 
255 	(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
256 	    find_next_priority_group_cb, &cbarg);
257 
258 	if (cbarg.found) {
259 		nlog(LOG_DEBUG, "nwamd_ncp_find_next_priority_group: "
260 		    "next priority group >= %lld is %lld",
261 		    minpriority, cbarg.currpriority);
262 		*nextpriorityp = cbarg.currpriority;
263 		return (B_TRUE);
264 	} else {
265 		nlog(LOG_DEBUG, "nwamd_ncp_find_next_priority_group: "
266 		    "no priority groups >= %lld exist", minpriority);
267 		return (B_FALSE);
268 	}
269 }
270 
271 /*
272  * Struct for walking NCUs in the selected priority group.  We count
273  * how many of the exclusive, all and shared NCUs are online, and
274  * if activate_or_deactivate is true, we either activate or deactivate
275  * (depending on the value of activate) offline/online NCUs.
276  */
277 struct nwamd_ncu_check_walk_arg {
278 	boolean_t manual;	/* enable manual NCUs only */
279 	int64_t priority_group; /* interested priority-group for this walk */
280 	uint64_t exclusive_ncus;
281 	uint64_t exclusive_online_ncus;
282 	uint64_t shared_ncus;
283 	uint64_t shared_online_ncus;
284 	uint64_t all_ncus;
285 	uint64_t all_online_ncus;
286 	boolean_t activate_or_deactivate;
287 	boolean_t activate;
288 };
289 
290 /*
291  * This function serves a number of purposes:
292  * - it supports activation/deactivation of manual NCUs in the current NCP
293  * (when wa->manual is true, wa->activate determines if we activate or
294  * deactivate the current NCU)
295  * - it supports checking/activation of a particular priority group in
296  * the active NCP. This works as follows:
297  *
298  * Count up numbers of exclusive, shared and all NCUs, and how many of each
299  * are online.  If an NCU is waiting for IP address to be assigned, it is
300  * also considered online.  If activate_or_deactivate is true, we also
301  * either activate (if activate is true) or deactivate prioritized NCUs
302  * that are offline or online.
303  */
304 static int
305 nwamd_ncu_check_or_activate(nwamd_object_t object, void *data)
306 {
307 	struct nwamd_ncu_check_walk_arg *wa = data;
308 	nwamd_ncu_t *ncu;
309 	uint64_t priority_group, priority_mode;
310 	nwamd_object_t if_obj;
311 	nwam_state_t state, if_state;
312 	nwam_aux_state_t aux_state, if_aux_state;
313 	char *name;
314 
315 	state = object->nwamd_object_state;
316 	aux_state = object->nwamd_object_aux_state;
317 	name = object->nwamd_object_name;
318 	ncu = object->nwamd_object_data;
319 
320 	/* skip NCUs in UNINITIALIZED state */
321 	if (state == NWAM_STATE_UNINITIALIZED) {
322 		nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
323 		    "skipping uninitialized ncu %s", name);
324 		return (0);
325 	}
326 	if (!wa->manual && wa->priority_group == INVALID_PRIORITY_GROUP)
327 		return (0);
328 
329 	if (ncu->ncu_type != NWAM_NCU_TYPE_LINK) {
330 		nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
331 		    "skipping interface NCU %s", name);
332 		return (0);
333 	}
334 	if (!wa->manual && ncu->ncu_node.u_link.nwamd_link_activation_mode !=
335 	    NWAM_ACTIVATION_MODE_PRIORITIZED) {
336 		nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
337 		    "skipping non-prioritized NCU %s", name);
338 		return (0);
339 	}
340 	if (wa->manual && ncu->ncu_node.u_link.nwamd_link_activation_mode !=
341 	    NWAM_ACTIVATION_MODE_MANUAL) {
342 		nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
343 		    "skipping non-manual NCU %s", name);
344 		return (0);
345 	}
346 
347 	priority_group = ncu->ncu_node.u_link.nwamd_link_priority_group;
348 	priority_mode = ncu->ncu_node.u_link.nwamd_link_priority_mode;
349 	/* Only work with NCUs in the requested priority-group */
350 	if (!wa->manual && priority_group != wa->priority_group) {
351 		nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
352 		    "skipping NCU %s in different priority-group", name);
353 		return (0);
354 	}
355 	/* Get the state of the corresponding interface NCU */
356 	if ((if_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_INTERFACE,
357 	    ncu->ncu_name)) == NULL) {
358 		nlog(LOG_ERR, "nwamd_ncu_check_or_activate: "
359 		    "interface NCU of %s not found, skipping", name);
360 		return (0);
361 	}
362 	if_state = if_obj->nwamd_object_state;
363 	if_aux_state = if_obj->nwamd_object_aux_state;
364 	nwamd_object_release(if_obj);
365 
366 	nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: %s ncu %s",
367 	    wa->activate_or_deactivate ?
368 	    (wa->activate ? "activating" : "deactivating") :
369 	    "checking", name);
370 
371 	if (wa->manual) {
372 		if (wa->activate_or_deactivate && wa->activate) {
373 			if (state == NWAM_STATE_OFFLINE && ncu->ncu_enabled) {
374 				nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
375 				    "moving NCU %s to offline* from offline",
376 				    name);
377 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
378 				    name, NWAM_STATE_OFFLINE_TO_ONLINE,
379 				    NWAM_AUX_STATE_INITIALIZED);
380 			}
381 			if (state != NWAM_STATE_DISABLED &&
382 			    !ncu->ncu_enabled) {
383 				nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
384 				    "moving NCU %s to online* (disabling)",
385 				    name);
386 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
387 				    name, NWAM_STATE_ONLINE_TO_OFFLINE,
388 				    NWAM_AUX_STATE_MANUAL_DISABLE);
389 			}
390 		}
391 		return (0);
392 	}
393 	switch (priority_mode) {
394 	case NWAM_PRIORITY_MODE_EXCLUSIVE:
395 		wa->exclusive_ncus++;
396 		if (state == NWAM_STATE_ONLINE &&
397 		    (if_state == NWAM_STATE_ONLINE ||
398 		    if_aux_state == NWAM_AUX_STATE_IF_WAITING_FOR_ADDR))
399 			wa->exclusive_online_ncus++;
400 
401 		/*
402 		 * For exclusive NCUs, we activate offline NCUs as long
403 		 * as no other exclusive NCUs are active.
404 		 */
405 		if (wa->activate_or_deactivate && wa->activate) {
406 			if (state == NWAM_STATE_OFFLINE &&
407 			    wa->exclusive_online_ncus == 0) {
408 				nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
409 				    "moving NCU %s to offline* from offline",
410 				    name);
411 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
412 				    name, NWAM_STATE_OFFLINE_TO_ONLINE,
413 				    NWAM_AUX_STATE_INITIALIZED);
414 			}
415 		}
416 		if (wa->activate_or_deactivate && !wa->activate) {
417 			if (aux_state != NWAM_AUX_STATE_CONDITIONS_NOT_MET) {
418 				nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
419 				    "deactivating NCU %s", name);
420 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
421 				    name, NWAM_STATE_ONLINE_TO_OFFLINE,
422 				    NWAM_AUX_STATE_CONDITIONS_NOT_MET);
423 			}
424 		}
425 		/*
426 		 * If we are activating or checking the priority group and
427 		 * too many exclusive NCUs are online, take this NCU down.
428 		 */
429 		if ((wa->activate_or_deactivate && wa->activate) ||
430 		    !wa->activate_or_deactivate) {
431 			if (state == NWAM_STATE_ONLINE &&
432 			    if_state == NWAM_STATE_ONLINE &&
433 			    wa->exclusive_online_ncus > 1) {
434 				nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
435 				    "moving NCU %s to online* since another "
436 				    "NCU is already active",
437 				    name);
438 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
439 				    name, NWAM_STATE_ONLINE_TO_OFFLINE,
440 				    NWAM_AUX_STATE_CONDITIONS_NOT_MET);
441 			}
442 		}
443 		break;
444 	case NWAM_PRIORITY_MODE_SHARED:
445 		wa->shared_ncus++;
446 		if (state == NWAM_STATE_ONLINE &&
447 		    (if_state == NWAM_STATE_ONLINE ||
448 		    if_aux_state == NWAM_AUX_STATE_IF_WAITING_FOR_ADDR))
449 			wa->shared_online_ncus++;
450 
451 		if (wa->activate_or_deactivate && wa->activate) {
452 			if (state == NWAM_STATE_OFFLINE) {
453 				nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
454 				    "activating NCU %s", name);
455 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
456 				    name, NWAM_STATE_OFFLINE_TO_ONLINE,
457 				    NWAM_AUX_STATE_INITIALIZED);
458 			}
459 		}
460 		if (wa->activate_or_deactivate && !wa->activate) {
461 			if (aux_state != NWAM_AUX_STATE_CONDITIONS_NOT_MET) {
462 				nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
463 				    "deactivating NCU %s", name);
464 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
465 				    name, NWAM_STATE_ONLINE_TO_OFFLINE,
466 				    NWAM_AUX_STATE_CONDITIONS_NOT_MET);
467 			}
468 		}
469 		break;
470 	case NWAM_PRIORITY_MODE_ALL:
471 		wa->all_ncus++;
472 		if (state == NWAM_STATE_ONLINE &&
473 		    (if_state == NWAM_STATE_ONLINE ||
474 		    if_aux_state == NWAM_AUX_STATE_IF_WAITING_FOR_ADDR))
475 			wa->all_online_ncus++;
476 
477 		/*
478 		 * For "all" NCUs, activate/deactivate all offline/online
479 		 * NCUs.
480 		 */
481 		if (wa->activate_or_deactivate && wa->activate) {
482 			if (state == NWAM_STATE_OFFLINE) {
483 				nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
484 				    "activating NCU %s", name);
485 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
486 				    name, NWAM_STATE_OFFLINE_TO_ONLINE,
487 				    NWAM_AUX_STATE_INITIALIZED);
488 			}
489 		}
490 		if (wa->activate_or_deactivate && !wa->activate) {
491 			if (aux_state != NWAM_AUX_STATE_CONDITIONS_NOT_MET) {
492 				nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
493 				    "deactivating NCU %s", name);
494 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
495 				    name, NWAM_STATE_ONLINE_TO_OFFLINE,
496 				    NWAM_AUX_STATE_CONDITIONS_NOT_MET);
497 			}
498 		}
499 
500 		break;
501 	default:
502 		nlog(LOG_ERR, "nwamd_ncu_check_or_activate: "
503 		    "invalid priority-mode");
504 		break;
505 	}
506 
507 	return (0);
508 }
509 
510 void
511 nwamd_ncp_activate_priority_group(int64_t priority)
512 {
513 	struct nwamd_ncu_check_walk_arg wa;
514 	nwamd_event_t check_event, priority_event;
515 
516 	if (priority == INVALID_PRIORITY_GROUP)
517 		return;
518 
519 	(void) pthread_mutex_lock(&active_ncp_mutex);
520 	if (priority == current_ncu_priority_group) {
521 		(void) pthread_mutex_unlock(&active_ncp_mutex);
522 		return;
523 	}
524 	(void) pthread_mutex_unlock(&active_ncp_mutex);
525 
526 	nlog(LOG_DEBUG, "nwamd_ncp_activate_priority_group: "
527 	    "activating priority group %lld", priority);
528 
529 	wa.manual = B_FALSE;
530 	wa.priority_group = priority;
531 	wa.exclusive_ncus = 0;
532 	wa.exclusive_online_ncus = 0;
533 	wa.shared_ncus = 0;
534 	wa.shared_online_ncus = 0;
535 	wa.all_ncus = 0;
536 	wa.all_online_ncus = 0;
537 	wa.activate_or_deactivate = B_TRUE;
538 	wa.activate = B_TRUE;
539 
540 	if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
541 	    nwamd_ncu_check_or_activate, &wa) != 0) {
542 		nlog(LOG_ERR, "nwamd_ncp_activate_priority_group: "
543 		    "nwamd_walk_objects() failed");
544 		return;
545 	}
546 
547 	/*
548 	 * Enqueue event to update current_ncu_priority_group and send to
549 	 * any event listeners.
550 	 */
551 	priority_event = nwamd_event_init_priority_group_change(priority);
552 	if (priority_event == NULL)
553 		return;
554 	nwamd_event_enqueue(priority_event);
555 
556 	/*
557 	 * Now we've activated a new priority group, enqueue an event
558 	 * to check up on the state of this priority group.
559 	 */
560 	check_event = nwamd_event_init_ncu_check();
561 	if (check_event == NULL)
562 		return;
563 	nwamd_event_enqueue_timed(check_event, ncu_wait_time);
564 }
565 
566 void
567 nwamd_ncp_deactivate_priority_group(int64_t priority)
568 {
569 	struct nwamd_ncu_check_walk_arg wa;
570 
571 	if (priority == INVALID_PRIORITY_GROUP)
572 		return;
573 
574 	nlog(LOG_DEBUG, "nwamd_ncp_deactivate_priority_group: "
575 	    "deactivating priority group %lld", priority);
576 
577 	wa.manual = B_FALSE;
578 	wa.priority_group = priority;
579 	wa.exclusive_ncus = 0;
580 	wa.exclusive_online_ncus = 0;
581 	wa.shared_ncus = 0;
582 	wa.shared_online_ncus = 0;
583 	wa.all_ncus = 0;
584 	wa.all_online_ncus = 0;
585 	wa.activate_or_deactivate = B_TRUE;
586 	wa.activate = B_FALSE;
587 
588 	if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
589 	    nwamd_ncu_check_or_activate, &wa) != 0) {
590 		nlog(LOG_ERR, "nwamd_ncp_deactivate_priority_group: "
591 		    "nwamd_walk_objects() failed");
592 		return;
593 	}
594 }
595 
596 /*
597  * This function deactivates all priority groups at level 'priority' and lower
598  * (which is, numerically, all priorities >= priority).
599  */
600 void
601 nwamd_ncp_deactivate_priority_group_all(int64_t priority)
602 {
603 	if (priority == INVALID_PRIORITY_GROUP)
604 		return;
605 
606 	nlog(LOG_DEBUG, "nwamd_ncp_deactivate_priority_group_all: "
607 	    "deactivating priority group less than or equal to %lld", priority);
608 
609 	do {
610 		nwamd_ncp_deactivate_priority_group(priority);
611 	} while (nwamd_ncp_find_next_priority_group(priority + 1, &priority));
612 }
613 
614 /*
615  * Returns 'true' if it found the highest priority group no higher then what
616  * is passed that should be activated and sets *priority to that.
617  */
618 boolean_t
619 nwamd_ncp_check_priority_group(int64_t *priority)
620 {
621 	struct nwamd_ncu_check_walk_arg wa;
622 	boolean_t conditions_met = B_FALSE;
623 
624 	nlog(LOG_DEBUG, "nwamd_ncp_check_priority_group: "
625 	    "checking priority group %lld", *priority);
626 
627 	if (*priority == INVALID_PRIORITY_GROUP) {
628 		if (!nwamd_ncp_find_next_priority_group(0, priority))
629 			return (B_FALSE);
630 	}
631 
632 	while (!conditions_met) {
633 		(void) memset(&wa, 0, sizeof (wa));
634 		wa.manual = B_FALSE;
635 		wa.priority_group = *priority;
636 		wa.activate_or_deactivate = B_FALSE;
637 
638 		if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
639 		    nwamd_ncu_check_or_activate, &wa) != 0) {
640 			nlog(LOG_ERR, "nwamd_ncp_check_priority_group: "
641 			    "nwamd_walk_objects() failed");
642 			return (B_FALSE);
643 		}
644 
645 		/*
646 		 * Are activation conditons satisifed? In other words:
647 		 * - exactly one of the exclusive NCUs is online
648 		 * - 1 or more shared NCUs are online
649 		 * - all of the all NCUs are online.
650 		 * If any of these is untrue, conditions are not satisfied.
651 		 */
652 		conditions_met = B_TRUE;
653 		if (wa.exclusive_ncus > 0 && wa.exclusive_online_ncus != 1)
654 			conditions_met = B_FALSE;
655 		if (wa.shared_ncus > 0 && wa.shared_online_ncus == 0)
656 			conditions_met = B_FALSE;
657 		if (wa.all_ncus > 0 && wa.all_ncus != wa.all_online_ncus)
658 			conditions_met = B_FALSE;
659 		if (wa.exclusive_online_ncus == 0 &&
660 		    wa.shared_online_ncus == 0 && wa.all_online_ncus == 0)
661 			conditions_met = B_FALSE;
662 
663 		if (conditions_met) {
664 			return (B_TRUE);
665 		} else {
666 			/*
667 			 * If there is a next pg, activate it. If not, do
668 			 * nothing - we're stuck here unless an event occurs
669 			 * for our or a higher pg.
670 			 */
671 			if (!nwamd_ncp_find_next_priority_group
672 			    (wa.priority_group + 1, priority)) {
673 				nlog(LOG_DEBUG, "ran out of prio groups");
674 				return (B_FALSE);
675 			}
676 		}
677 	}
678 	return (B_FALSE);
679 }
680 
681 void
682 nwamd_ncp_activate_manual_ncus(void)
683 {
684 	struct nwamd_ncu_check_walk_arg wa;
685 
686 	nlog(LOG_DEBUG, "nwamd_ncp_activate_manual_ncus: activating NCUs");
687 
688 	wa.manual = B_TRUE;
689 	wa.activate_or_deactivate = B_TRUE;
690 	wa.activate = B_TRUE;
691 
692 	if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
693 	    nwamd_ncu_check_or_activate, &wa) != 0) {
694 		nlog(LOG_ERR, "nwamd_ncp_activate_manual_ncus: "
695 		    "nwamd_walk_objects() failed");
696 		return;
697 	}
698 }
699 
700 void
701 nwamd_create_ncu_check_event(uint64_t when)
702 {
703 	nwamd_event_t check_event = nwamd_event_init_ncu_check();
704 	if (check_event != NULL)
705 		nwamd_event_enqueue_timed(check_event, when);
706 }
707