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