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
50char active_ncp[NWAM_MAX_NAME_LEN];
51nwam_ncp_handle_t active_ncph = NULL;
52int64_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 */
57pthread_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 */
64uint64_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 */
71boolean_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 */
87void
88nwamd_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
153void
154nwamd_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 */
176void
177nwamd_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
210int
211nwamd_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
226struct 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 */
233static int
234find_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_link.nwamd_link_activation_mode !=
241	    NWAM_ACTIVATION_MODE_PRIORITIZED)
242		return (0);
243
244	priority = ncu->ncu_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 */
254boolean_t
255nwamd_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 */
286struct 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 */
313static int
314nwamd_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_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_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_link.nwamd_link_priority_group;
357	priority_mode = ncu->ncu_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
519void
520nwamd_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
575void
576nwamd_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 */
609void
610nwamd_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 */
627boolean_t
628nwamd_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
690void
691nwamd_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
709void
710nwamd_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