xref: /titanic_53/usr/src/uts/common/os/contract.c (revision d170b13ab825d81e5f4efc7b970b75c163482b8c)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
525e8c5aaSvikram  * Common Development and Distribution License (the "License").
625e8c5aaSvikram  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
227b209c2cSacruz  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * Contracts
307c478bd9Sstevel@tonic-gate  * ---------
317c478bd9Sstevel@tonic-gate  *
327c478bd9Sstevel@tonic-gate  * Contracts are a primitive which enrich the relationships between
337c478bd9Sstevel@tonic-gate  * processes and system resources.  The primary purpose of contracts is
347c478bd9Sstevel@tonic-gate  * to provide a means for the system to negotiate the departure from a
357c478bd9Sstevel@tonic-gate  * binding relationship (e.g. pages locked in memory or a thread bound
367c478bd9Sstevel@tonic-gate  * to processor), but they can also be used as a purely asynchronous
377c478bd9Sstevel@tonic-gate  * error reporting mechanism as they are with process contracts.
387c478bd9Sstevel@tonic-gate  *
397c478bd9Sstevel@tonic-gate  * More information on how one interfaces with contracts and what
407c478bd9Sstevel@tonic-gate  * contracts can do for you can be found in:
417c478bd9Sstevel@tonic-gate  *   PSARC 2003/193 Solaris Contracts
427c478bd9Sstevel@tonic-gate  *   PSARC 2004/460 Contracts addendum
437c478bd9Sstevel@tonic-gate  *
447c478bd9Sstevel@tonic-gate  * This file contains the core contracts framework.  By itself it is
457c478bd9Sstevel@tonic-gate  * useless: it depends the contracts filesystem (ctfs) to provide an
467c478bd9Sstevel@tonic-gate  * interface to user processes and individual contract types to
477c478bd9Sstevel@tonic-gate  * implement the process/resource relationships.
487c478bd9Sstevel@tonic-gate  *
497c478bd9Sstevel@tonic-gate  * Data structure overview
507c478bd9Sstevel@tonic-gate  * -----------------------
517c478bd9Sstevel@tonic-gate  *
527c478bd9Sstevel@tonic-gate  * A contract is represented by a contract_t, which itself points to an
537c478bd9Sstevel@tonic-gate  * encapsulating contract-type specific contract object.  A contract_t
547c478bd9Sstevel@tonic-gate  * contains the contract's static identity (including its terms), its
557c478bd9Sstevel@tonic-gate  * linkage to various bookkeeping structures, the contract-specific
567c478bd9Sstevel@tonic-gate  * event queue, and a reference count.
577c478bd9Sstevel@tonic-gate  *
587c478bd9Sstevel@tonic-gate  * A contract template is represented by a ct_template_t, which, like a
597c478bd9Sstevel@tonic-gate  * contract, points to an encapsulating contract-type specific template
607c478bd9Sstevel@tonic-gate  * object.  A ct_template_t contains the template's terms.
617c478bd9Sstevel@tonic-gate  *
627c478bd9Sstevel@tonic-gate  * An event queue is represented by a ct_equeue_t, and consists of a
637c478bd9Sstevel@tonic-gate  * list of events, a list of listeners, and a list of listeners who are
647c478bd9Sstevel@tonic-gate  * waiting for new events (affectionately referred to as "tail
657c478bd9Sstevel@tonic-gate  * listeners").  There are three queue types, defined by ct_listnum_t
667c478bd9Sstevel@tonic-gate  * (an enum).  An event may be on one of each type of queue
677c478bd9Sstevel@tonic-gate  * simultaneously; the list linkage used by a queue is determined by
687c478bd9Sstevel@tonic-gate  * its type.
697c478bd9Sstevel@tonic-gate  *
707c478bd9Sstevel@tonic-gate  * An event is represented by a ct_kevent_t, which contains mostly
717c478bd9Sstevel@tonic-gate  * static event data (e.g. id, payload).  It also has an array of
727c478bd9Sstevel@tonic-gate  * ct_member_t structures, each of which contains a list_node_t and
737c478bd9Sstevel@tonic-gate  * represent the event's linkage in a specific event queue.
747c478bd9Sstevel@tonic-gate  *
757c478bd9Sstevel@tonic-gate  * Each open of an event endpoint results in the creation of a new
767c478bd9Sstevel@tonic-gate  * listener, represented by a ct_listener_t.  In addition to linkage
777c478bd9Sstevel@tonic-gate  * into the aforementioned lists in the event_queue, a ct_listener_t
787c478bd9Sstevel@tonic-gate  * contains a pointer to the ct_kevent_t it is currently positioned at
797c478bd9Sstevel@tonic-gate  * as well as a set of status flags and other administrative data.
807c478bd9Sstevel@tonic-gate  *
817c478bd9Sstevel@tonic-gate  * Each process has a list of contracts it owns, p_ct_held; a pointer
827c478bd9Sstevel@tonic-gate  * to the process contract it is a member of, p_ct_process; the linkage
837c478bd9Sstevel@tonic-gate  * for that membership, p_ct_member; and an array of event queue
847c478bd9Sstevel@tonic-gate  * structures representing the process bundle queues.
857c478bd9Sstevel@tonic-gate  *
867c478bd9Sstevel@tonic-gate  * Each LWP has an array of its active templates, lwp_ct_active; and
877c478bd9Sstevel@tonic-gate  * the most recently created contracts, lwp_ct_latest.
887c478bd9Sstevel@tonic-gate  *
897c478bd9Sstevel@tonic-gate  * A process contract has a list of member processes and a list of
907c478bd9Sstevel@tonic-gate  * inherited contracts.
917c478bd9Sstevel@tonic-gate  *
927c478bd9Sstevel@tonic-gate  * There is a system-wide list of all contracts, as well as per-type
937c478bd9Sstevel@tonic-gate  * lists of contracts.
947c478bd9Sstevel@tonic-gate  *
957c478bd9Sstevel@tonic-gate  * Lock ordering overview
967c478bd9Sstevel@tonic-gate  * ----------------------
977c478bd9Sstevel@tonic-gate  *
987c478bd9Sstevel@tonic-gate  * Locks at the top are taken first:
997c478bd9Sstevel@tonic-gate  *
1007c478bd9Sstevel@tonic-gate  *                   ct_evtlock
1017c478bd9Sstevel@tonic-gate  *                   regent ct_lock
1027c478bd9Sstevel@tonic-gate  *                   member ct_lock
1037c478bd9Sstevel@tonic-gate  *                   pidlock
1047c478bd9Sstevel@tonic-gate  *                   p_lock
1057c478bd9Sstevel@tonic-gate  *    contract ctq_lock         contract_lock
1067c478bd9Sstevel@tonic-gate  *    pbundle ctq_lock
1077c478bd9Sstevel@tonic-gate  *    cte_lock
1087c478bd9Sstevel@tonic-gate  *                   ct_reflock
1097c478bd9Sstevel@tonic-gate  *
1107c478bd9Sstevel@tonic-gate  * contract_lock and ctq_lock/cte_lock are not currently taken at the
1117c478bd9Sstevel@tonic-gate  * same time.
1127c478bd9Sstevel@tonic-gate  *
1137c478bd9Sstevel@tonic-gate  * Reference counting and locking
1147c478bd9Sstevel@tonic-gate  * ------------------------------
1157c478bd9Sstevel@tonic-gate  *
1167c478bd9Sstevel@tonic-gate  * A contract has a reference count, protected by ct_reflock.
1177c478bd9Sstevel@tonic-gate  * (ct_reflock is also used in a couple other places where atomic
1187c478bd9Sstevel@tonic-gate  * access to a variable is needed in an innermost context).  A process
1197c478bd9Sstevel@tonic-gate  * maintains a hold on each contract it owns.  A process contract has a
1207c478bd9Sstevel@tonic-gate  * hold on each contract is has inherited.  Each event has a hold on
1217c478bd9Sstevel@tonic-gate  * the contract which generated it.  Process contract templates have
1227c478bd9Sstevel@tonic-gate  * holds on the contracts referred to by their transfer terms.  CTFS
1237c478bd9Sstevel@tonic-gate  * contract directory nodes have holds on contracts.  Lastly, various
1247c478bd9Sstevel@tonic-gate  * code paths may temporarily take holds on contracts to prevent them
1257c478bd9Sstevel@tonic-gate  * from disappearing while other processing is going on.  It is
1267c478bd9Sstevel@tonic-gate  * important to note that the global contract lists do not hold
1277c478bd9Sstevel@tonic-gate  * references on contracts; a contract is removed from these structures
1287c478bd9Sstevel@tonic-gate  * atomically with the release of its last reference.
1297c478bd9Sstevel@tonic-gate  *
1307c478bd9Sstevel@tonic-gate  * At a given point in time, a contract can either be owned by a
1317c478bd9Sstevel@tonic-gate  * process, inherited by a regent process contract, or orphaned.  A
1327c478bd9Sstevel@tonic-gate  * contract_t's  owner and regent pointers, ct_owner and ct_regent, are
1337c478bd9Sstevel@tonic-gate  * protected by its ct_lock.  The linkage in the holder's (holder =
1347c478bd9Sstevel@tonic-gate  * owner or regent) list of contracts, ct_ctlist, is protected by
1357c478bd9Sstevel@tonic-gate  * whatever lock protects the holder's data structure.  In order for
1367c478bd9Sstevel@tonic-gate  * these two directions to remain consistent, changing the holder of a
1377c478bd9Sstevel@tonic-gate  * contract requires that both locks be held.
1387c478bd9Sstevel@tonic-gate  *
1397c478bd9Sstevel@tonic-gate  * Events also have reference counts.  There is one hold on an event
1407c478bd9Sstevel@tonic-gate  * per queue it is present on, in addition to those needed for the
1417c478bd9Sstevel@tonic-gate  * usual sundry reasons.  Individual listeners are associated with
1427c478bd9Sstevel@tonic-gate  * specific queues, and increase a queue-specific reference count
1437c478bd9Sstevel@tonic-gate  * stored in the ct_member_t structure.
1447c478bd9Sstevel@tonic-gate  *
1457c478bd9Sstevel@tonic-gate  * The dynamic contents of an event (reference count and flags) are
1467c478bd9Sstevel@tonic-gate  * protected by its cte_lock, while the contents of the embedded
1477c478bd9Sstevel@tonic-gate  * ct_member_t structures are protected by the locks of the queues they
1487c478bd9Sstevel@tonic-gate  * are linked into.  A ct_listener_t's contents are also protected by
1497c478bd9Sstevel@tonic-gate  * its event queue's ctq_lock.
1507c478bd9Sstevel@tonic-gate  *
1517c478bd9Sstevel@tonic-gate  * Resource controls
1527c478bd9Sstevel@tonic-gate  * -----------------
1537c478bd9Sstevel@tonic-gate  *
1547c478bd9Sstevel@tonic-gate  * Control:      project.max-contracts (rc_project_contract)
1557c478bd9Sstevel@tonic-gate  * Description:  Maximum number of contracts allowed a project.
1567c478bd9Sstevel@tonic-gate  *
1577c478bd9Sstevel@tonic-gate  *   When a contract is created, the project's allocation is tested and
1587c478bd9Sstevel@tonic-gate  *   (assuming success) increased.  When the last reference to a
1597c478bd9Sstevel@tonic-gate  *   contract is released, the creating project's allocation is
1607c478bd9Sstevel@tonic-gate  *   decreased.
1617c478bd9Sstevel@tonic-gate  */
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate #include <sys/mutex.h>
1647c478bd9Sstevel@tonic-gate #include <sys/debug.h>
1657c478bd9Sstevel@tonic-gate #include <sys/types.h>
1667c478bd9Sstevel@tonic-gate #include <sys/param.h>
1677c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
1687c478bd9Sstevel@tonic-gate #include <sys/thread.h>
1697c478bd9Sstevel@tonic-gate #include <sys/id_space.h>
1707c478bd9Sstevel@tonic-gate #include <sys/avl.h>
1717c478bd9Sstevel@tonic-gate #include <sys/list.h>
1727c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
1737c478bd9Sstevel@tonic-gate #include <sys/proc.h>
1747c478bd9Sstevel@tonic-gate #include <sys/contract_impl.h>
1757c478bd9Sstevel@tonic-gate #include <sys/contract/process_impl.h>
17625e8c5aaSvikram #include <sys/dditypes.h>
17725e8c5aaSvikram #include <sys/contract/device_impl.h>
1787c478bd9Sstevel@tonic-gate #include <sys/systm.h>
1797c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
1807c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
1817c478bd9Sstevel@tonic-gate #include <sys/model.h>
1827c478bd9Sstevel@tonic-gate #include <sys/policy.h>
1837c478bd9Sstevel@tonic-gate #include <sys/zone.h>
1847c478bd9Sstevel@tonic-gate #include <sys/task.h>
18525e8c5aaSvikram #include <sys/ddi.h>
18625e8c5aaSvikram #include <sys/sunddi.h>
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate extern rctl_hndl_t rc_project_contract;
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate static id_space_t	*contract_ids;
1917c478bd9Sstevel@tonic-gate static avl_tree_t	contract_avl;
1927c478bd9Sstevel@tonic-gate static kmutex_t		contract_lock;
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate int			ct_ntypes = CTT_MAXTYPE;
1957c478bd9Sstevel@tonic-gate static ct_type_t	*ct_types_static[CTT_MAXTYPE];
1967c478bd9Sstevel@tonic-gate ct_type_t		**ct_types = ct_types_static;
19725e8c5aaSvikram int			ct_debug;
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate static void cte_queue_create(ct_equeue_t *, ct_listnum_t, int, int);
2007c478bd9Sstevel@tonic-gate static void cte_queue_destroy(ct_equeue_t *);
2017c478bd9Sstevel@tonic-gate static void cte_queue_drain(ct_equeue_t *, int);
2027c478bd9Sstevel@tonic-gate static void cte_trim(ct_equeue_t *, contract_t *);
2037c478bd9Sstevel@tonic-gate static void cte_copy(ct_equeue_t *, ct_equeue_t *);
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate /*
2067c478bd9Sstevel@tonic-gate  * contract_compar
2077c478bd9Sstevel@tonic-gate  *
2087c478bd9Sstevel@tonic-gate  * A contract comparator which sorts on contract ID.
2097c478bd9Sstevel@tonic-gate  */
2107c478bd9Sstevel@tonic-gate int
2117c478bd9Sstevel@tonic-gate contract_compar(const void *x, const void *y)
2127c478bd9Sstevel@tonic-gate {
2137c478bd9Sstevel@tonic-gate 	const contract_t *ct1 = x;
2147c478bd9Sstevel@tonic-gate 	const contract_t *ct2 = y;
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	if (ct1->ct_id < ct2->ct_id)
2177c478bd9Sstevel@tonic-gate 		return (-1);
2187c478bd9Sstevel@tonic-gate 	if (ct1->ct_id > ct2->ct_id)
2197c478bd9Sstevel@tonic-gate 		return (1);
2207c478bd9Sstevel@tonic-gate 	return (0);
2217c478bd9Sstevel@tonic-gate }
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate /*
2247c478bd9Sstevel@tonic-gate  * contract_init
2257c478bd9Sstevel@tonic-gate  *
2267c478bd9Sstevel@tonic-gate  * Initializes the contract subsystem, the specific contract types, and
2277c478bd9Sstevel@tonic-gate  * process 0.
2287c478bd9Sstevel@tonic-gate  */
2297c478bd9Sstevel@tonic-gate void
2307c478bd9Sstevel@tonic-gate contract_init(void)
2317c478bd9Sstevel@tonic-gate {
2327c478bd9Sstevel@tonic-gate 	/*
2337c478bd9Sstevel@tonic-gate 	 * Initialize contract subsystem.
2347c478bd9Sstevel@tonic-gate 	 */
2357c478bd9Sstevel@tonic-gate 	contract_ids = id_space_create("contracts", 1, INT_MAX);
2367c478bd9Sstevel@tonic-gate 	avl_create(&contract_avl, contract_compar, sizeof (contract_t),
2377c478bd9Sstevel@tonic-gate 	    offsetof(contract_t, ct_ctavl));
2387c478bd9Sstevel@tonic-gate 	mutex_init(&contract_lock, NULL, MUTEX_DEFAULT, NULL);
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	/*
2417c478bd9Sstevel@tonic-gate 	 * Initialize contract types.
2427c478bd9Sstevel@tonic-gate 	 */
2437c478bd9Sstevel@tonic-gate 	contract_process_init();
24425e8c5aaSvikram 	contract_device_init();
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	/*
2477c478bd9Sstevel@tonic-gate 	 * Initialize p0/lwp0 contract state.
2487c478bd9Sstevel@tonic-gate 	 */
2497c478bd9Sstevel@tonic-gate 	avl_create(&p0.p_ct_held, contract_compar, sizeof (contract_t),
2507c478bd9Sstevel@tonic-gate 	    offsetof(contract_t, ct_ctlist));
2517c478bd9Sstevel@tonic-gate }
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate /*
2547c478bd9Sstevel@tonic-gate  * contract_dtor
2557c478bd9Sstevel@tonic-gate  *
2567c478bd9Sstevel@tonic-gate  * Performs basic destruction of the common portions of a contract.
2577c478bd9Sstevel@tonic-gate  * Called from the failure path of contract_ctor and from
2587c478bd9Sstevel@tonic-gate  * contract_rele.
2597c478bd9Sstevel@tonic-gate  */
2607c478bd9Sstevel@tonic-gate static void
2617c478bd9Sstevel@tonic-gate contract_dtor(contract_t *ct)
2627c478bd9Sstevel@tonic-gate {
2637c478bd9Sstevel@tonic-gate 	cte_queue_destroy(&ct->ct_events);
2647c478bd9Sstevel@tonic-gate 	list_destroy(&ct->ct_vnodes);
2657c478bd9Sstevel@tonic-gate 	mutex_destroy(&ct->ct_reflock);
2667c478bd9Sstevel@tonic-gate 	mutex_destroy(&ct->ct_lock);
2677c478bd9Sstevel@tonic-gate 	mutex_destroy(&ct->ct_evtlock);
2687c478bd9Sstevel@tonic-gate }
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate /*
2717c478bd9Sstevel@tonic-gate  * contract_ctor
2727c478bd9Sstevel@tonic-gate  *
2737c478bd9Sstevel@tonic-gate  * Called by a contract type to initialize a contract.  Fails if the
2747c478bd9Sstevel@tonic-gate  * max-contract resource control would have been exceeded.  After a
2757c478bd9Sstevel@tonic-gate  * successful call to contract_ctor, the contract is unlocked and
2767c478bd9Sstevel@tonic-gate  * visible in all namespaces; any type-specific initialization should
2777c478bd9Sstevel@tonic-gate  * be completed before calling contract_ctor.  Returns 0 on success.
2787c478bd9Sstevel@tonic-gate  *
2797c478bd9Sstevel@tonic-gate  * Because not all callers can tolerate failure, a 0 value for canfail
2807c478bd9Sstevel@tonic-gate  * instructs contract_ctor to ignore the project.max-contracts resource
2817c478bd9Sstevel@tonic-gate  * control.  Obviously, this "out" should only be employed by callers
2827c478bd9Sstevel@tonic-gate  * who are sufficiently constrained in other ways (e.g. newproc).
2837c478bd9Sstevel@tonic-gate  */
2847c478bd9Sstevel@tonic-gate int
2857c478bd9Sstevel@tonic-gate contract_ctor(contract_t *ct, ct_type_t *type, ct_template_t *tmpl, void *data,
2867c478bd9Sstevel@tonic-gate     ctflags_t flags, proc_t *author, int canfail)
2877c478bd9Sstevel@tonic-gate {
2887c478bd9Sstevel@tonic-gate 	avl_index_t where;
2897c478bd9Sstevel@tonic-gate 	klwp_t *curlwp = ttolwp(curthread);
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 	ASSERT(author == curproc);
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	mutex_init(&ct->ct_lock, NULL, MUTEX_DEFAULT, NULL);
2947c478bd9Sstevel@tonic-gate 	mutex_init(&ct->ct_reflock, NULL, MUTEX_DEFAULT, NULL);
2957c478bd9Sstevel@tonic-gate 	mutex_init(&ct->ct_evtlock, NULL, MUTEX_DEFAULT, NULL);
2967c478bd9Sstevel@tonic-gate 	ct->ct_id = id_alloc(contract_ids);
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	cte_queue_create(&ct->ct_events, CTEL_CONTRACT, 20, 0);
2997c478bd9Sstevel@tonic-gate 	list_create(&ct->ct_vnodes, sizeof (contract_vnode_t),
3007c478bd9Sstevel@tonic-gate 	    offsetof(contract_vnode_t, ctv_node));
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	/*
3037c478bd9Sstevel@tonic-gate 	 * Instance data
3047c478bd9Sstevel@tonic-gate 	 */
3057c478bd9Sstevel@tonic-gate 	ct->ct_ref = 2;		/* one for the holder, one for "latest" */
3067c478bd9Sstevel@tonic-gate 	ct->ct_cuid = crgetuid(CRED());
3077c478bd9Sstevel@tonic-gate 	ct->ct_type = type;
3087c478bd9Sstevel@tonic-gate 	ct->ct_data = data;
3097c478bd9Sstevel@tonic-gate 	gethrestime(&ct->ct_ctime);
3107c478bd9Sstevel@tonic-gate 	ct->ct_state = CTS_OWNED;
3117c478bd9Sstevel@tonic-gate 	ct->ct_flags = flags;
3127c478bd9Sstevel@tonic-gate 	ct->ct_regent = author->p_ct_process ?
3137c478bd9Sstevel@tonic-gate 	    &author->p_ct_process->conp_contract : NULL;
3147c478bd9Sstevel@tonic-gate 	ct->ct_ev_info = tmpl->ctmpl_ev_info;
3157c478bd9Sstevel@tonic-gate 	ct->ct_ev_crit = tmpl->ctmpl_ev_crit;
3167c478bd9Sstevel@tonic-gate 	ct->ct_cookie = tmpl->ctmpl_cookie;
3177c478bd9Sstevel@tonic-gate 	ct->ct_owner = author;
31825e8c5aaSvikram 	ct->ct_ntime.ctm_total = -1;
31925e8c5aaSvikram 	ct->ct_qtime.ctm_total = -1;
32025e8c5aaSvikram 	ct->ct_nevent = NULL;
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate 	/*
3237c478bd9Sstevel@tonic-gate 	 * Test project.max-contracts.
3247c478bd9Sstevel@tonic-gate 	 */
3257c478bd9Sstevel@tonic-gate 	mutex_enter(&author->p_lock);
3267c478bd9Sstevel@tonic-gate 	mutex_enter(&contract_lock);
3277c478bd9Sstevel@tonic-gate 	if (canfail && rctl_test(rc_project_contract,
3287c478bd9Sstevel@tonic-gate 	    author->p_task->tk_proj->kpj_rctls, author, 1,
3297c478bd9Sstevel@tonic-gate 	    RCA_SAFE) & RCT_DENY) {
3307c478bd9Sstevel@tonic-gate 		id_free(contract_ids, ct->ct_id);
3317c478bd9Sstevel@tonic-gate 		mutex_exit(&contract_lock);
3327c478bd9Sstevel@tonic-gate 		mutex_exit(&author->p_lock);
3337c478bd9Sstevel@tonic-gate 		ct->ct_events.ctq_flags |= CTQ_DEAD;
3347c478bd9Sstevel@tonic-gate 		contract_dtor(ct);
3357c478bd9Sstevel@tonic-gate 		return (1);
3367c478bd9Sstevel@tonic-gate 	}
3377c478bd9Sstevel@tonic-gate 	ct->ct_proj = author->p_task->tk_proj;
3387c478bd9Sstevel@tonic-gate 	ct->ct_proj->kpj_data.kpd_contract++;
3397c478bd9Sstevel@tonic-gate 	(void) project_hold(ct->ct_proj);
3407c478bd9Sstevel@tonic-gate 	mutex_exit(&contract_lock);
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 	/*
3437c478bd9Sstevel@tonic-gate 	 * Insert into holder's avl of contracts.
3447c478bd9Sstevel@tonic-gate 	 * We use an avl not because order is important, but because
3457c478bd9Sstevel@tonic-gate 	 * readdir of /proc/contracts requires we be able to use a
3467c478bd9Sstevel@tonic-gate 	 * scalar as an index into the process's list of contracts
3477c478bd9Sstevel@tonic-gate 	 */
3487c478bd9Sstevel@tonic-gate 	ct->ct_zoneid = author->p_zone->zone_id;
3497c478bd9Sstevel@tonic-gate 	ct->ct_czuniqid = ct->ct_mzuniqid = author->p_zone->zone_uniqid;
3507c478bd9Sstevel@tonic-gate 	VERIFY(avl_find(&author->p_ct_held, ct, &where) == NULL);
3517c478bd9Sstevel@tonic-gate 	avl_insert(&author->p_ct_held, ct, where);
3527c478bd9Sstevel@tonic-gate 	mutex_exit(&author->p_lock);
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 	/*
3557c478bd9Sstevel@tonic-gate 	 * Insert into global contract AVL
3567c478bd9Sstevel@tonic-gate 	 */
3577c478bd9Sstevel@tonic-gate 	mutex_enter(&contract_lock);
3587c478bd9Sstevel@tonic-gate 	VERIFY(avl_find(&contract_avl, ct, &where) == NULL);
3597c478bd9Sstevel@tonic-gate 	avl_insert(&contract_avl, ct, where);
3607c478bd9Sstevel@tonic-gate 	mutex_exit(&contract_lock);
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	/*
3637c478bd9Sstevel@tonic-gate 	 * Insert into type AVL
3647c478bd9Sstevel@tonic-gate 	 */
3657c478bd9Sstevel@tonic-gate 	mutex_enter(&type->ct_type_lock);
3667c478bd9Sstevel@tonic-gate 	VERIFY(avl_find(&type->ct_type_avl, ct, &where) == NULL);
3677c478bd9Sstevel@tonic-gate 	avl_insert(&type->ct_type_avl, ct, where);
3687c478bd9Sstevel@tonic-gate 	type->ct_type_timestruc = ct->ct_ctime;
3697c478bd9Sstevel@tonic-gate 	mutex_exit(&type->ct_type_lock);
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	if (curlwp->lwp_ct_latest[type->ct_type_index])
3727c478bd9Sstevel@tonic-gate 		contract_rele(curlwp->lwp_ct_latest[type->ct_type_index]);
3737c478bd9Sstevel@tonic-gate 	curlwp->lwp_ct_latest[type->ct_type_index] = ct;
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 	return (0);
3767c478bd9Sstevel@tonic-gate }
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate /*
3797c478bd9Sstevel@tonic-gate  * contract_rele
3807c478bd9Sstevel@tonic-gate  *
3817c478bd9Sstevel@tonic-gate  * Releases a reference to a contract.  If the caller had the last
3827c478bd9Sstevel@tonic-gate  * reference, the contract is removed from all namespaces, its
3837c478bd9Sstevel@tonic-gate  * allocation against the max-contracts resource control is released,
3847c478bd9Sstevel@tonic-gate  * and the contract type's free entry point is invoked for any
3857c478bd9Sstevel@tonic-gate  * type-specific deconstruction and to (presumably) free the object.
3867c478bd9Sstevel@tonic-gate  */
3877c478bd9Sstevel@tonic-gate void
3887c478bd9Sstevel@tonic-gate contract_rele(contract_t *ct)
3897c478bd9Sstevel@tonic-gate {
3907c478bd9Sstevel@tonic-gate 	uint64_t nref;
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_reflock);
3937c478bd9Sstevel@tonic-gate 	ASSERT(ct->ct_ref > 0);
3947c478bd9Sstevel@tonic-gate 	nref = --ct->ct_ref;
3957c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_reflock);
3967c478bd9Sstevel@tonic-gate 	if (nref == 0) {
3977c478bd9Sstevel@tonic-gate 		/*
3987c478bd9Sstevel@tonic-gate 		 * ct_owner is cleared when it drops its reference.
3997c478bd9Sstevel@tonic-gate 		 */
4007c478bd9Sstevel@tonic-gate 		ASSERT(ct->ct_owner == NULL);
4017c478bd9Sstevel@tonic-gate 		ASSERT(ct->ct_evcnt == 0);
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 		/*
4047c478bd9Sstevel@tonic-gate 		 * Remove from global contract AVL
4057c478bd9Sstevel@tonic-gate 		 */
4067c478bd9Sstevel@tonic-gate 		mutex_enter(&contract_lock);
4077c478bd9Sstevel@tonic-gate 		avl_remove(&contract_avl, ct);
4087c478bd9Sstevel@tonic-gate 		mutex_exit(&contract_lock);
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 		/*
4117c478bd9Sstevel@tonic-gate 		 * Remove from type AVL
4127c478bd9Sstevel@tonic-gate 		 */
4137c478bd9Sstevel@tonic-gate 		mutex_enter(&ct->ct_type->ct_type_lock);
4147c478bd9Sstevel@tonic-gate 		avl_remove(&ct->ct_type->ct_type_avl, ct);
4157c478bd9Sstevel@tonic-gate 		mutex_exit(&ct->ct_type->ct_type_lock);
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 		/*
4187c478bd9Sstevel@tonic-gate 		 * Release the contract's ID
4197c478bd9Sstevel@tonic-gate 		 */
4207c478bd9Sstevel@tonic-gate 		id_free(contract_ids, ct->ct_id);
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 		/*
4237c478bd9Sstevel@tonic-gate 		 * Release project hold
4247c478bd9Sstevel@tonic-gate 		 */
4257c478bd9Sstevel@tonic-gate 		mutex_enter(&contract_lock);
4267c478bd9Sstevel@tonic-gate 		ct->ct_proj->kpj_data.kpd_contract--;
4277c478bd9Sstevel@tonic-gate 		project_rele(ct->ct_proj);
4287c478bd9Sstevel@tonic-gate 		mutex_exit(&contract_lock);
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 		/*
4317c478bd9Sstevel@tonic-gate 		 * Free the contract
4327c478bd9Sstevel@tonic-gate 		 */
4337c478bd9Sstevel@tonic-gate 		contract_dtor(ct);
4347c478bd9Sstevel@tonic-gate 		ct->ct_type->ct_type_ops->contop_free(ct);
4357c478bd9Sstevel@tonic-gate 	}
4367c478bd9Sstevel@tonic-gate }
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate /*
4397c478bd9Sstevel@tonic-gate  * contract_hold
4407c478bd9Sstevel@tonic-gate  *
4417c478bd9Sstevel@tonic-gate  * Adds a reference to a contract
4427c478bd9Sstevel@tonic-gate  */
4437c478bd9Sstevel@tonic-gate void
4447c478bd9Sstevel@tonic-gate contract_hold(contract_t *ct)
4457c478bd9Sstevel@tonic-gate {
4467c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_reflock);
4477c478bd9Sstevel@tonic-gate 	ASSERT(ct->ct_ref < UINT64_MAX);
4487c478bd9Sstevel@tonic-gate 	ct->ct_ref++;
4497c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_reflock);
4507c478bd9Sstevel@tonic-gate }
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate /*
4537c478bd9Sstevel@tonic-gate  * contract_getzuniqid
4547c478bd9Sstevel@tonic-gate  *
4557c478bd9Sstevel@tonic-gate  * Get a contract's zone unique ID.  Needed because 64-bit reads and
4567c478bd9Sstevel@tonic-gate  * writes aren't atomic on x86.  Since there are contexts where we are
4577c478bd9Sstevel@tonic-gate  * unable to take ct_lock, we instead use ct_reflock; in actuality any
4587c478bd9Sstevel@tonic-gate  * lock would do.
4597c478bd9Sstevel@tonic-gate  */
4607c478bd9Sstevel@tonic-gate uint64_t
4617c478bd9Sstevel@tonic-gate contract_getzuniqid(contract_t *ct)
4627c478bd9Sstevel@tonic-gate {
4637c478bd9Sstevel@tonic-gate 	uint64_t zuniqid;
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_reflock);
4667c478bd9Sstevel@tonic-gate 	zuniqid = ct->ct_mzuniqid;
4677c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_reflock);
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	return (zuniqid);
4707c478bd9Sstevel@tonic-gate }
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate /*
4737c478bd9Sstevel@tonic-gate  * contract_setzuniqid
4747c478bd9Sstevel@tonic-gate  *
4757c478bd9Sstevel@tonic-gate  * Sets a contract's zone unique ID.   See contract_getzuniqid.
4767c478bd9Sstevel@tonic-gate  */
4777c478bd9Sstevel@tonic-gate void
4787c478bd9Sstevel@tonic-gate contract_setzuniqid(contract_t *ct, uint64_t zuniqid)
4797c478bd9Sstevel@tonic-gate {
4807c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_reflock);
4817c478bd9Sstevel@tonic-gate 	ct->ct_mzuniqid = zuniqid;
4827c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_reflock);
4837c478bd9Sstevel@tonic-gate }
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate /*
4867c478bd9Sstevel@tonic-gate  * contract_abandon
4877c478bd9Sstevel@tonic-gate  *
4887c478bd9Sstevel@tonic-gate  * Abandons the specified contract.  If "explicit" is clear, the
4897c478bd9Sstevel@tonic-gate  * contract was implicitly abandoned (by process exit) and should be
4907c478bd9Sstevel@tonic-gate  * inherited if its terms allow it and its owner was a member of a
4917c478bd9Sstevel@tonic-gate  * regent contract.  Otherwise, the contract type's abandon entry point
4927c478bd9Sstevel@tonic-gate  * is invoked to either destroy or orphan the contract.
4937c478bd9Sstevel@tonic-gate  */
4947c478bd9Sstevel@tonic-gate int
4957c478bd9Sstevel@tonic-gate contract_abandon(contract_t *ct, proc_t *p, int explicit)
4967c478bd9Sstevel@tonic-gate {
4977c478bd9Sstevel@tonic-gate 	ct_equeue_t *q = NULL;
4987c478bd9Sstevel@tonic-gate 	contract_t *parent = &p->p_ct_process->conp_contract;
4997c478bd9Sstevel@tonic-gate 	int inherit = 0;
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	ASSERT(p == curproc);
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	/*
5067c478bd9Sstevel@tonic-gate 	 * Multiple contract locks are taken contract -> subcontract.
5077c478bd9Sstevel@tonic-gate 	 * Check if the contract will be inherited so we can acquire
5087c478bd9Sstevel@tonic-gate 	 * all the necessary locks before making sensitive changes.
5097c478bd9Sstevel@tonic-gate 	 */
5107c478bd9Sstevel@tonic-gate 	if (!explicit && (ct->ct_flags & CTF_INHERIT) &&
5117c478bd9Sstevel@tonic-gate 	    contract_process_accept(parent)) {
5127c478bd9Sstevel@tonic-gate 		mutex_exit(&ct->ct_lock);
5137c478bd9Sstevel@tonic-gate 		mutex_enter(&parent->ct_lock);
5147c478bd9Sstevel@tonic-gate 		mutex_enter(&ct->ct_lock);
5157c478bd9Sstevel@tonic-gate 		inherit = 1;
5167c478bd9Sstevel@tonic-gate 	}
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 	if (ct->ct_owner != p) {
5197c478bd9Sstevel@tonic-gate 		mutex_exit(&ct->ct_lock);
5207c478bd9Sstevel@tonic-gate 		if (inherit)
5217c478bd9Sstevel@tonic-gate 			mutex_exit(&parent->ct_lock);
5227c478bd9Sstevel@tonic-gate 		return (EINVAL);
5237c478bd9Sstevel@tonic-gate 	}
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
5267c478bd9Sstevel@tonic-gate 	if (explicit)
5277c478bd9Sstevel@tonic-gate 		avl_remove(&p->p_ct_held, ct);
5287c478bd9Sstevel@tonic-gate 	ct->ct_owner = NULL;
5297c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 	/*
5327c478bd9Sstevel@tonic-gate 	 * Since we can't call cte_trim with the contract lock held,
5337c478bd9Sstevel@tonic-gate 	 * we grab the queue pointer here.
5347c478bd9Sstevel@tonic-gate 	 */
5357c478bd9Sstevel@tonic-gate 	if (p->p_ct_equeue)
5367c478bd9Sstevel@tonic-gate 		q = p->p_ct_equeue[ct->ct_type->ct_type_index];
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 	/*
5397c478bd9Sstevel@tonic-gate 	 * contop_abandon may destroy the contract so we rely on it to
5407c478bd9Sstevel@tonic-gate 	 * drop ct_lock.  We retain a reference on the contract so that
5417c478bd9Sstevel@tonic-gate 	 * the cte_trim which follows functions properly.  Even though
5427c478bd9Sstevel@tonic-gate 	 * cte_trim doesn't dereference the contract pointer, it is
5437c478bd9Sstevel@tonic-gate 	 * still necessary to retain a reference to the contract so
5447c478bd9Sstevel@tonic-gate 	 * that we don't trim events which are sent by a subsequently
5457c478bd9Sstevel@tonic-gate 	 * allocated contract infortuitously located at the same address.
5467c478bd9Sstevel@tonic-gate 	 */
5477c478bd9Sstevel@tonic-gate 	contract_hold(ct);
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	if (inherit) {
5507c478bd9Sstevel@tonic-gate 		ct->ct_state = CTS_INHERITED;
5517c478bd9Sstevel@tonic-gate 		ASSERT(ct->ct_regent == parent);
5527c478bd9Sstevel@tonic-gate 		contract_process_take(parent, ct);
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 		/*
5557c478bd9Sstevel@tonic-gate 		 * We are handing off the process's reference to the
5567c478bd9Sstevel@tonic-gate 		 * parent contract.  For this reason, the order in
5577c478bd9Sstevel@tonic-gate 		 * which we drop the contract locks is also important.
5587c478bd9Sstevel@tonic-gate 		 */
5597c478bd9Sstevel@tonic-gate 		mutex_exit(&ct->ct_lock);
5607c478bd9Sstevel@tonic-gate 		mutex_exit(&parent->ct_lock);
5617c478bd9Sstevel@tonic-gate 	} else {
5627c478bd9Sstevel@tonic-gate 		ct->ct_regent = NULL;
5637c478bd9Sstevel@tonic-gate 		ct->ct_type->ct_type_ops->contop_abandon(ct);
5647c478bd9Sstevel@tonic-gate 	}
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 	/*
5677c478bd9Sstevel@tonic-gate 	 * ct_lock has been dropped; we can safely trim the event
5687c478bd9Sstevel@tonic-gate 	 * queue now.
5697c478bd9Sstevel@tonic-gate 	 */
5707c478bd9Sstevel@tonic-gate 	if (q) {
5717c478bd9Sstevel@tonic-gate 		mutex_enter(&q->ctq_lock);
5727c478bd9Sstevel@tonic-gate 		cte_trim(q, ct);
5737c478bd9Sstevel@tonic-gate 		mutex_exit(&q->ctq_lock);
5747c478bd9Sstevel@tonic-gate 	}
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 	contract_rele(ct);
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	return (0);
5797c478bd9Sstevel@tonic-gate }
5807c478bd9Sstevel@tonic-gate 
58125e8c5aaSvikram int
58225e8c5aaSvikram contract_newct(contract_t *ct)
58325e8c5aaSvikram {
58425e8c5aaSvikram 	return (ct->ct_type->ct_type_ops->contop_newct(ct));
58525e8c5aaSvikram }
58625e8c5aaSvikram 
5877c478bd9Sstevel@tonic-gate /*
5887c478bd9Sstevel@tonic-gate  * contract_adopt
5897c478bd9Sstevel@tonic-gate  *
5907c478bd9Sstevel@tonic-gate  * Adopts a contract.  After a successful call to this routine, the
5917c478bd9Sstevel@tonic-gate  * previously inherited contract will belong to the calling process,
5927c478bd9Sstevel@tonic-gate  * and its events will have been appended to its new owner's process
5937c478bd9Sstevel@tonic-gate  * bundle queue.
5947c478bd9Sstevel@tonic-gate  */
5957c478bd9Sstevel@tonic-gate int
5967c478bd9Sstevel@tonic-gate contract_adopt(contract_t *ct, proc_t *p)
5977c478bd9Sstevel@tonic-gate {
5987c478bd9Sstevel@tonic-gate 	avl_index_t where;
5997c478bd9Sstevel@tonic-gate 	ct_equeue_t *q;
6007c478bd9Sstevel@tonic-gate 	contract_t *parent;
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 	ASSERT(p == curproc);
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 	/*
6057c478bd9Sstevel@tonic-gate 	 * Ensure the process has an event queue.  Checked by ASSERTs
6067c478bd9Sstevel@tonic-gate 	 * below.
6077c478bd9Sstevel@tonic-gate 	 */
6087c478bd9Sstevel@tonic-gate 	(void) contract_type_pbundle(ct->ct_type, p);
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
6117c478bd9Sstevel@tonic-gate 	parent = ct->ct_regent;
6127c478bd9Sstevel@tonic-gate 	if (ct->ct_state != CTS_INHERITED ||
6137c478bd9Sstevel@tonic-gate 	    &p->p_ct_process->conp_contract != parent ||
6147c478bd9Sstevel@tonic-gate 	    p->p_zone->zone_uniqid != ct->ct_czuniqid) {
6157c478bd9Sstevel@tonic-gate 		mutex_exit(&ct->ct_lock);
6167c478bd9Sstevel@tonic-gate 		return (EINVAL);
6177c478bd9Sstevel@tonic-gate 	}
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	/*
6207c478bd9Sstevel@tonic-gate 	 * Multiple contract locks are taken contract -> subcontract.
6217c478bd9Sstevel@tonic-gate 	 */
6227c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
6237c478bd9Sstevel@tonic-gate 	mutex_enter(&parent->ct_lock);
6247c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	/*
6277c478bd9Sstevel@tonic-gate 	 * It is possible that the contract was adopted by someone else
6287c478bd9Sstevel@tonic-gate 	 * while its lock was dropped.  It isn't possible for the
6297c478bd9Sstevel@tonic-gate 	 * contract to have been inherited by a different regent
6307c478bd9Sstevel@tonic-gate 	 * contract.
6317c478bd9Sstevel@tonic-gate 	 */
6327c478bd9Sstevel@tonic-gate 	if (ct->ct_state != CTS_INHERITED) {
6337c478bd9Sstevel@tonic-gate 		mutex_exit(&parent->ct_lock);
6347c478bd9Sstevel@tonic-gate 		mutex_exit(&ct->ct_lock);
6357c478bd9Sstevel@tonic-gate 		return (EBUSY);
6367c478bd9Sstevel@tonic-gate 	}
6377c478bd9Sstevel@tonic-gate 	ASSERT(ct->ct_regent == parent);
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 	ct->ct_state = CTS_OWNED;
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	contract_process_adopt(ct, p);
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
6447c478bd9Sstevel@tonic-gate 	ct->ct_owner = p;
6457c478bd9Sstevel@tonic-gate 	VERIFY(avl_find(&p->p_ct_held, ct, &where) == NULL);
6467c478bd9Sstevel@tonic-gate 	avl_insert(&p->p_ct_held, ct, where);
6477c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 	ASSERT(ct->ct_owner->p_ct_equeue);
6507c478bd9Sstevel@tonic-gate 	ASSERT(ct->ct_owner->p_ct_equeue[ct->ct_type->ct_type_index]);
6517c478bd9Sstevel@tonic-gate 	q = ct->ct_owner->p_ct_equeue[ct->ct_type->ct_type_index];
6527c478bd9Sstevel@tonic-gate 	cte_copy(&ct->ct_events, q);
6537c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 	return (0);
6567c478bd9Sstevel@tonic-gate }
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate /*
6597c478bd9Sstevel@tonic-gate  * contract_ack
6607c478bd9Sstevel@tonic-gate  *
6617c478bd9Sstevel@tonic-gate  * Acknowledges receipt of a critical event.
6627c478bd9Sstevel@tonic-gate  */
6637c478bd9Sstevel@tonic-gate int
66425e8c5aaSvikram contract_ack(contract_t *ct, uint64_t evid, int ack)
6657c478bd9Sstevel@tonic-gate {
6667c478bd9Sstevel@tonic-gate 	ct_kevent_t *ev;
6677c478bd9Sstevel@tonic-gate 	list_t *queue = &ct->ct_events.ctq_events;
6687c478bd9Sstevel@tonic-gate 	int error = ESRCH;
66925e8c5aaSvikram 	int nego = 0;
67025e8c5aaSvikram 	uint_t evtype;
67125e8c5aaSvikram 
67225e8c5aaSvikram 	ASSERT(ack == CT_ACK || ack == CT_NACK);
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
6757c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_events.ctq_lock);
6767c478bd9Sstevel@tonic-gate 	/*
6777c478bd9Sstevel@tonic-gate 	 * We are probably ACKing something near the head of the queue.
6787c478bd9Sstevel@tonic-gate 	 */
6797c478bd9Sstevel@tonic-gate 	for (ev = list_head(queue); ev; ev = list_next(queue, ev)) {
6807c478bd9Sstevel@tonic-gate 		if (ev->cte_id == evid) {
68125e8c5aaSvikram 			if (ev->cte_flags & CTE_NEG)
68225e8c5aaSvikram 				nego = 1;
68325e8c5aaSvikram 			else if (ack == CT_NACK)
68425e8c5aaSvikram 				break;
6857c478bd9Sstevel@tonic-gate 			if ((ev->cte_flags & (CTE_INFO | CTE_ACK)) == 0) {
6867c478bd9Sstevel@tonic-gate 				ev->cte_flags |= CTE_ACK;
6877c478bd9Sstevel@tonic-gate 				ct->ct_evcnt--;
68825e8c5aaSvikram 				evtype = ev->cte_type;
6897c478bd9Sstevel@tonic-gate 				error = 0;
6907c478bd9Sstevel@tonic-gate 			}
6917c478bd9Sstevel@tonic-gate 			break;
6927c478bd9Sstevel@tonic-gate 		}
6937c478bd9Sstevel@tonic-gate 	}
6947c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_events.ctq_lock);
6957c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
6967c478bd9Sstevel@tonic-gate 
69725e8c5aaSvikram 	/*
69825e8c5aaSvikram 	 * Not all critical events are negotiation events, however
69925e8c5aaSvikram 	 * every negotiation event is a critical event. NEGEND events
70025e8c5aaSvikram 	 * are critical events but are not negotiation events
70125e8c5aaSvikram 	 */
70225e8c5aaSvikram 	if (error || !nego)
7037c478bd9Sstevel@tonic-gate 		return (error);
70425e8c5aaSvikram 
70525e8c5aaSvikram 	if (ack == CT_ACK)
70625e8c5aaSvikram 		error = ct->ct_type->ct_type_ops->contop_ack(ct, evtype, evid);
70725e8c5aaSvikram 	else
70825e8c5aaSvikram 		error = ct->ct_type->ct_type_ops->contop_nack(ct, evtype, evid);
70925e8c5aaSvikram 
71025e8c5aaSvikram 	return (error);
71125e8c5aaSvikram }
71225e8c5aaSvikram 
71325e8c5aaSvikram /*ARGSUSED*/
71425e8c5aaSvikram int
71525e8c5aaSvikram contract_ack_inval(contract_t *ct, uint_t evtype, uint64_t evid)
71625e8c5aaSvikram {
71725e8c5aaSvikram 	cmn_err(CE_PANIC, "contract_ack_inval: unsupported call: ctid: %u",
71825e8c5aaSvikram 	    ct->ct_id);
71925e8c5aaSvikram 	return (ENOSYS);
72025e8c5aaSvikram }
72125e8c5aaSvikram 
72225e8c5aaSvikram /*ARGSUSED*/
72325e8c5aaSvikram int
72425e8c5aaSvikram contract_qack_inval(contract_t *ct, uint_t evtype, uint64_t evid)
72525e8c5aaSvikram {
72625e8c5aaSvikram 	cmn_err(CE_PANIC, "contract_ack_inval: unsupported call: ctid: %u",
72725e8c5aaSvikram 	    ct->ct_id);
72825e8c5aaSvikram 	return (ENOSYS);
72925e8c5aaSvikram }
73025e8c5aaSvikram 
73125e8c5aaSvikram /*ARGSUSED*/
73225e8c5aaSvikram int
73325e8c5aaSvikram contract_qack_notsup(contract_t *ct, uint_t evtype, uint64_t evid)
73425e8c5aaSvikram {
73525e8c5aaSvikram 	return (ERANGE);
73625e8c5aaSvikram }
73725e8c5aaSvikram 
73825e8c5aaSvikram /*
73925e8c5aaSvikram  * contract_qack
74025e8c5aaSvikram  *
74125e8c5aaSvikram  * Asks that negotiations be extended by another time quantum
74225e8c5aaSvikram  */
74325e8c5aaSvikram int
74425e8c5aaSvikram contract_qack(contract_t *ct, uint64_t evid)
74525e8c5aaSvikram {
74625e8c5aaSvikram 	ct_kevent_t *ev;
74725e8c5aaSvikram 	list_t *queue = &ct->ct_events.ctq_events;
74825e8c5aaSvikram 	int nego = 0;
74925e8c5aaSvikram 	uint_t evtype;
75025e8c5aaSvikram 
75125e8c5aaSvikram 	mutex_enter(&ct->ct_lock);
75225e8c5aaSvikram 	mutex_enter(&ct->ct_events.ctq_lock);
75325e8c5aaSvikram 
75425e8c5aaSvikram 	for (ev = list_head(queue); ev; ev = list_next(queue, ev)) {
75525e8c5aaSvikram 		if (ev->cte_id == evid) {
75625e8c5aaSvikram 			if ((ev->cte_flags & (CTE_NEG | CTE_ACK)) == CTE_NEG) {
75725e8c5aaSvikram 				evtype = ev->cte_type;
75825e8c5aaSvikram 				nego = 1;
75925e8c5aaSvikram 			}
76025e8c5aaSvikram 			break;
76125e8c5aaSvikram 		}
76225e8c5aaSvikram 	}
76325e8c5aaSvikram 	mutex_exit(&ct->ct_events.ctq_lock);
76425e8c5aaSvikram 	mutex_exit(&ct->ct_lock);
76525e8c5aaSvikram 
76625e8c5aaSvikram 	/*
76725e8c5aaSvikram 	 * Only a negotiated event (which is by definition also a critical
76825e8c5aaSvikram 	 * event) which has not yet been acknowledged can provide
76925e8c5aaSvikram 	 * time quanta to a negotiating owner process.
77025e8c5aaSvikram 	 */
77125e8c5aaSvikram 	if (!nego)
77225e8c5aaSvikram 		return (ESRCH);
77325e8c5aaSvikram 
77425e8c5aaSvikram 	return (ct->ct_type->ct_type_ops->contop_qack(ct, evtype, evid));
7757c478bd9Sstevel@tonic-gate }
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate /*
7787c478bd9Sstevel@tonic-gate  * contract_orphan
7797c478bd9Sstevel@tonic-gate  *
7807c478bd9Sstevel@tonic-gate  * Icky-poo.  This is a process-contract special, used to ACK all
7817c478bd9Sstevel@tonic-gate  * critical messages when a contract is orphaned.
7827c478bd9Sstevel@tonic-gate  */
7837c478bd9Sstevel@tonic-gate void
7847c478bd9Sstevel@tonic-gate contract_orphan(contract_t *ct)
7857c478bd9Sstevel@tonic-gate {
7867c478bd9Sstevel@tonic-gate 	ct_kevent_t *ev;
7877c478bd9Sstevel@tonic-gate 	list_t *queue = &ct->ct_events.ctq_events;
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ct->ct_lock));
7907c478bd9Sstevel@tonic-gate 	ASSERT(ct->ct_state != CTS_ORPHAN);
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_events.ctq_lock);
7937c478bd9Sstevel@tonic-gate 	ct->ct_state = CTS_ORPHAN;
7947c478bd9Sstevel@tonic-gate 	for (ev = list_head(queue); ev; ev = list_next(queue, ev)) {
7957c478bd9Sstevel@tonic-gate 		if ((ev->cte_flags & (CTE_INFO | CTE_ACK)) == 0) {
7967c478bd9Sstevel@tonic-gate 			ev->cte_flags |= CTE_ACK;
7977c478bd9Sstevel@tonic-gate 			ct->ct_evcnt--;
7987c478bd9Sstevel@tonic-gate 		}
7997c478bd9Sstevel@tonic-gate 	}
8007c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_events.ctq_lock);
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 	ASSERT(ct->ct_evcnt == 0);
8037c478bd9Sstevel@tonic-gate }
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate /*
8067c478bd9Sstevel@tonic-gate  * contract_destroy
8077c478bd9Sstevel@tonic-gate  *
8087c478bd9Sstevel@tonic-gate  * Explicit contract destruction.  Called when contract is empty.
8097c478bd9Sstevel@tonic-gate  * The contract will actually stick around until all of its events are
8107c478bd9Sstevel@tonic-gate  * removed from the bundle and and process bundle queues, and all fds
8117c478bd9Sstevel@tonic-gate  * which refer to it are closed.  See contract_dtor if you are looking
8127c478bd9Sstevel@tonic-gate  * for what destroys the contract structure.
8137c478bd9Sstevel@tonic-gate  */
8147c478bd9Sstevel@tonic-gate void
8157c478bd9Sstevel@tonic-gate contract_destroy(contract_t *ct)
8167c478bd9Sstevel@tonic-gate {
8177c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ct->ct_lock));
8187c478bd9Sstevel@tonic-gate 	ASSERT(ct->ct_state != CTS_DEAD);
8197c478bd9Sstevel@tonic-gate 	ASSERT(ct->ct_owner == NULL);
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate 	ct->ct_state = CTS_DEAD;
8227c478bd9Sstevel@tonic-gate 	cte_queue_drain(&ct->ct_events, 1);
8237c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
8247c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_type->ct_type_events.ctq_lock);
8257c478bd9Sstevel@tonic-gate 	cte_trim(&ct->ct_type->ct_type_events, ct);
8267c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_type->ct_type_events.ctq_lock);
8277c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
8287c478bd9Sstevel@tonic-gate 	ct->ct_type->ct_type_ops->contop_destroy(ct);
8297c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
8307c478bd9Sstevel@tonic-gate 	contract_rele(ct);
8317c478bd9Sstevel@tonic-gate }
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate /*
8347c478bd9Sstevel@tonic-gate  * contract_vnode_get
8357c478bd9Sstevel@tonic-gate  *
8367c478bd9Sstevel@tonic-gate  * Obtains the contract directory vnode for this contract, if there is
8377c478bd9Sstevel@tonic-gate  * one.  The caller must VN_RELE the vnode when they are through using
8387c478bd9Sstevel@tonic-gate  * it.
8397c478bd9Sstevel@tonic-gate  */
8407c478bd9Sstevel@tonic-gate vnode_t *
8417c478bd9Sstevel@tonic-gate contract_vnode_get(contract_t *ct, vfs_t *vfsp)
8427c478bd9Sstevel@tonic-gate {
8437c478bd9Sstevel@tonic-gate 	contract_vnode_t *ctv;
8447c478bd9Sstevel@tonic-gate 	vnode_t *vp = NULL;
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
8477c478bd9Sstevel@tonic-gate 	for (ctv = list_head(&ct->ct_vnodes); ctv != NULL;
8487c478bd9Sstevel@tonic-gate 	    ctv = list_next(&ct->ct_vnodes, ctv))
8497c478bd9Sstevel@tonic-gate 		if (ctv->ctv_vnode->v_vfsp == vfsp) {
8507c478bd9Sstevel@tonic-gate 			vp = ctv->ctv_vnode;
8517c478bd9Sstevel@tonic-gate 			VN_HOLD(vp);
8527c478bd9Sstevel@tonic-gate 			break;
8537c478bd9Sstevel@tonic-gate 		}
8547c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
8557c478bd9Sstevel@tonic-gate 	return (vp);
8567c478bd9Sstevel@tonic-gate }
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate /*
8597c478bd9Sstevel@tonic-gate  * contract_vnode_set
8607c478bd9Sstevel@tonic-gate  *
8617c478bd9Sstevel@tonic-gate  * Sets the contract directory vnode for this contract.  We don't hold
8627c478bd9Sstevel@tonic-gate  * a reference on the vnode because we don't want to prevent it from
8637c478bd9Sstevel@tonic-gate  * being freed.  The vnode's inactive entry point will take care of
8647c478bd9Sstevel@tonic-gate  * notifying us when it should be removed.
8657c478bd9Sstevel@tonic-gate  */
8667c478bd9Sstevel@tonic-gate void
8677c478bd9Sstevel@tonic-gate contract_vnode_set(contract_t *ct, contract_vnode_t *ctv, vnode_t *vnode)
8687c478bd9Sstevel@tonic-gate {
8697c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
8707c478bd9Sstevel@tonic-gate 	ctv->ctv_vnode = vnode;
8717c478bd9Sstevel@tonic-gate 	list_insert_head(&ct->ct_vnodes, ctv);
8727c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
8737c478bd9Sstevel@tonic-gate }
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate /*
8767c478bd9Sstevel@tonic-gate  * contract_vnode_clear
8777c478bd9Sstevel@tonic-gate  *
8787c478bd9Sstevel@tonic-gate  * Removes this vnode as the contract directory vnode for this
8797c478bd9Sstevel@tonic-gate  * contract.  Called from a contract directory's inactive entry point,
8807c478bd9Sstevel@tonic-gate  * this may return 0 indicating that the vnode gained another reference
8817c478bd9Sstevel@tonic-gate  * because of a simultaneous call to contract_vnode_get.
8827c478bd9Sstevel@tonic-gate  */
8837c478bd9Sstevel@tonic-gate int
8847c478bd9Sstevel@tonic-gate contract_vnode_clear(contract_t *ct, contract_vnode_t *ctv)
8857c478bd9Sstevel@tonic-gate {
8867c478bd9Sstevel@tonic-gate 	vnode_t *vp = ctv->ctv_vnode;
8877c478bd9Sstevel@tonic-gate 	int result;
8887c478bd9Sstevel@tonic-gate 
8897c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
8907c478bd9Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
8917c478bd9Sstevel@tonic-gate 	if (vp->v_count == 1) {
8927c478bd9Sstevel@tonic-gate 		list_remove(&ct->ct_vnodes, ctv);
8937c478bd9Sstevel@tonic-gate 		result = 1;
8947c478bd9Sstevel@tonic-gate 	} else {
8957c478bd9Sstevel@tonic-gate 		vp->v_count--;
8967c478bd9Sstevel@tonic-gate 		result = 0;
8977c478bd9Sstevel@tonic-gate 	}
8987c478bd9Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
8997c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 	return (result);
9027c478bd9Sstevel@tonic-gate }
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate /*
9057c478bd9Sstevel@tonic-gate  * contract_exit
9067c478bd9Sstevel@tonic-gate  *
9077c478bd9Sstevel@tonic-gate  * Abandons all contracts held by process p, and drains process p's
9087c478bd9Sstevel@tonic-gate  * bundle queues.  Called on process exit.
9097c478bd9Sstevel@tonic-gate  */
9107c478bd9Sstevel@tonic-gate void
9117c478bd9Sstevel@tonic-gate contract_exit(proc_t *p)
9127c478bd9Sstevel@tonic-gate {
9137c478bd9Sstevel@tonic-gate 	contract_t *ct;
9147c478bd9Sstevel@tonic-gate 	void *cookie = NULL;
9157c478bd9Sstevel@tonic-gate 	int i;
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 	ASSERT(p == curproc);
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	/*
9207c478bd9Sstevel@tonic-gate 	 * Abandon held contracts.  contract_abandon knows enough not
9217c478bd9Sstevel@tonic-gate 	 * to remove the contract from the list a second time.  We are
9227c478bd9Sstevel@tonic-gate 	 * exiting, so no locks are needed here.  But because
9237c478bd9Sstevel@tonic-gate 	 * contract_abandon will take p_lock, we need to make sure we
9247c478bd9Sstevel@tonic-gate 	 * aren't holding it.
9257c478bd9Sstevel@tonic-gate 	 */
9267c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_NOT_HELD(&p->p_lock));
9277c478bd9Sstevel@tonic-gate 	while ((ct = avl_destroy_nodes(&p->p_ct_held, &cookie)) != NULL)
9287c478bd9Sstevel@tonic-gate 		VERIFY(contract_abandon(ct, p, 0) == 0);
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 	/*
9317c478bd9Sstevel@tonic-gate 	 * Drain pbundles.  Because a process bundle queue could have
9327c478bd9Sstevel@tonic-gate 	 * been passed to another process, they may not be freed right
9337c478bd9Sstevel@tonic-gate 	 * away.
9347c478bd9Sstevel@tonic-gate 	 */
9357c478bd9Sstevel@tonic-gate 	if (p->p_ct_equeue) {
9367c478bd9Sstevel@tonic-gate 		for (i = 0; i < CTT_MAXTYPE; i++)
9377c478bd9Sstevel@tonic-gate 			if (p->p_ct_equeue[i])
9387c478bd9Sstevel@tonic-gate 				cte_queue_drain(p->p_ct_equeue[i], 0);
9397c478bd9Sstevel@tonic-gate 		kmem_free(p->p_ct_equeue, CTT_MAXTYPE * sizeof (ct_equeue_t *));
9407c478bd9Sstevel@tonic-gate 	}
9417c478bd9Sstevel@tonic-gate }
9427c478bd9Sstevel@tonic-gate 
94325e8c5aaSvikram static int
94425e8c5aaSvikram get_time_left(struct ct_time *t)
94525e8c5aaSvikram {
94625e8c5aaSvikram 	clock_t ticks_elapsed;
94725e8c5aaSvikram 	int secs_elapsed;
94825e8c5aaSvikram 
94925e8c5aaSvikram 	if (t->ctm_total == -1)
95025e8c5aaSvikram 		return (-1);
95125e8c5aaSvikram 
95225e8c5aaSvikram 	ticks_elapsed = ddi_get_lbolt() - t->ctm_start;
95325e8c5aaSvikram 	secs_elapsed = t->ctm_total - (drv_hztousec(ticks_elapsed)/MICROSEC);
95425e8c5aaSvikram 	return (secs_elapsed > 0 ? secs_elapsed : 0);
95525e8c5aaSvikram }
95625e8c5aaSvikram 
9577c478bd9Sstevel@tonic-gate /*
9587c478bd9Sstevel@tonic-gate  * contract_status_common
9597c478bd9Sstevel@tonic-gate  *
9607c478bd9Sstevel@tonic-gate  * Populates a ct_status structure.  Used by contract types in their
9617c478bd9Sstevel@tonic-gate  * status entry points and ctfs when only common information is
9627c478bd9Sstevel@tonic-gate  * requested.
9637c478bd9Sstevel@tonic-gate  */
9647c478bd9Sstevel@tonic-gate void
9657c478bd9Sstevel@tonic-gate contract_status_common(contract_t *ct, zone_t *zone, void *status,
9667c478bd9Sstevel@tonic-gate     model_t model)
9677c478bd9Sstevel@tonic-gate {
9687c478bd9Sstevel@tonic-gate 	STRUCT_HANDLE(ct_status, lstatus);
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 	STRUCT_SET_HANDLE(lstatus, model, status);
9717c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ct->ct_lock));
9727c478bd9Sstevel@tonic-gate 	if (zone->zone_uniqid == GLOBAL_ZONEUNIQID ||
9737c478bd9Sstevel@tonic-gate 	    zone->zone_uniqid == ct->ct_czuniqid) {
9747c478bd9Sstevel@tonic-gate 		zone_t *czone;
9757c478bd9Sstevel@tonic-gate 		zoneid_t zoneid = -1;
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate 		/*
9787c478bd9Sstevel@tonic-gate 		 * Contracts don't have holds on the zones they were
9797c478bd9Sstevel@tonic-gate 		 * created by.  If the contract's zone no longer
9807c478bd9Sstevel@tonic-gate 		 * exists, we say its zoneid is -1.
9817c478bd9Sstevel@tonic-gate 		 */
9827c478bd9Sstevel@tonic-gate 		if (zone->zone_uniqid == ct->ct_czuniqid ||
9837c478bd9Sstevel@tonic-gate 		    ct->ct_czuniqid == GLOBAL_ZONEUNIQID) {
9847c478bd9Sstevel@tonic-gate 			zoneid = ct->ct_zoneid;
9857c478bd9Sstevel@tonic-gate 		} else if ((czone = zone_find_by_id(ct->ct_zoneid)) != NULL) {
9867c478bd9Sstevel@tonic-gate 			if (czone->zone_uniqid == ct->ct_mzuniqid)
9877c478bd9Sstevel@tonic-gate 				zoneid = ct->ct_zoneid;
9887c478bd9Sstevel@tonic-gate 			zone_rele(czone);
9897c478bd9Sstevel@tonic-gate 		}
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 		STRUCT_FSET(lstatus, ctst_zoneid, zoneid);
9927c478bd9Sstevel@tonic-gate 		STRUCT_FSET(lstatus, ctst_holder,
9937c478bd9Sstevel@tonic-gate 		    (ct->ct_state == CTS_OWNED) ? ct->ct_owner->p_pid :
9947c478bd9Sstevel@tonic-gate 		    (ct->ct_state == CTS_INHERITED) ? ct->ct_regent->ct_id : 0);
9957c478bd9Sstevel@tonic-gate 		STRUCT_FSET(lstatus, ctst_state, ct->ct_state);
9967c478bd9Sstevel@tonic-gate 	} else {
9977c478bd9Sstevel@tonic-gate 		/*
9987c478bd9Sstevel@tonic-gate 		 * We are looking at a contract which was created by a
9997c478bd9Sstevel@tonic-gate 		 * process outside of our zone.  We provide fake zone,
10007c478bd9Sstevel@tonic-gate 		 * holder, and state information.
10017c478bd9Sstevel@tonic-gate 		 */
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 		STRUCT_FSET(lstatus, ctst_zoneid, zone->zone_id);
10047c478bd9Sstevel@tonic-gate 		/*
10057c478bd9Sstevel@tonic-gate 		 * Since "zone" can't disappear until the calling ctfs
10067c478bd9Sstevel@tonic-gate 		 * is unmounted, zone_zsched must be valid.
10077c478bd9Sstevel@tonic-gate 		 */
10087c478bd9Sstevel@tonic-gate 		STRUCT_FSET(lstatus, ctst_holder, (ct->ct_state < CTS_ORPHAN) ?
10097c478bd9Sstevel@tonic-gate 		    zone->zone_zsched->p_pid : 0);
10107c478bd9Sstevel@tonic-gate 		STRUCT_FSET(lstatus, ctst_state, (ct->ct_state < CTS_ORPHAN) ?
10117c478bd9Sstevel@tonic-gate 		    CTS_OWNED : ct->ct_state);
10127c478bd9Sstevel@tonic-gate 	}
10137c478bd9Sstevel@tonic-gate 	STRUCT_FSET(lstatus, ctst_nevents, ct->ct_evcnt);
101425e8c5aaSvikram 	STRUCT_FSET(lstatus, ctst_ntime, get_time_left(&ct->ct_ntime));
101525e8c5aaSvikram 	STRUCT_FSET(lstatus, ctst_qtime, get_time_left(&ct->ct_qtime));
10167c478bd9Sstevel@tonic-gate 	STRUCT_FSET(lstatus, ctst_nevid,
10177c478bd9Sstevel@tonic-gate 	    ct->ct_nevent ? ct->ct_nevent->cte_id : 0);
10187c478bd9Sstevel@tonic-gate 	STRUCT_FSET(lstatus, ctst_critical, ct->ct_ev_crit);
10197c478bd9Sstevel@tonic-gate 	STRUCT_FSET(lstatus, ctst_informative, ct->ct_ev_info);
10207c478bd9Sstevel@tonic-gate 	STRUCT_FSET(lstatus, ctst_cookie, ct->ct_cookie);
10217c478bd9Sstevel@tonic-gate 	STRUCT_FSET(lstatus, ctst_type, ct->ct_type->ct_type_index);
10227c478bd9Sstevel@tonic-gate 	STRUCT_FSET(lstatus, ctst_id, ct->ct_id);
10237c478bd9Sstevel@tonic-gate }
10247c478bd9Sstevel@tonic-gate 
10257c478bd9Sstevel@tonic-gate /*
10267c478bd9Sstevel@tonic-gate  * contract_checkcred
10277c478bd9Sstevel@tonic-gate  *
10287c478bd9Sstevel@tonic-gate  * Determines if the specified contract is owned by a process with the
10297c478bd9Sstevel@tonic-gate  * same effective uid as the specified credential.  The caller must
10307c478bd9Sstevel@tonic-gate  * ensure that the uid spaces are the same.  Returns 1 on success.
10317c478bd9Sstevel@tonic-gate  */
10327c478bd9Sstevel@tonic-gate static int
10337c478bd9Sstevel@tonic-gate contract_checkcred(contract_t *ct, const cred_t *cr)
10347c478bd9Sstevel@tonic-gate {
10357c478bd9Sstevel@tonic-gate 	proc_t *p;
10367c478bd9Sstevel@tonic-gate 	int fail = 1;
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
10397c478bd9Sstevel@tonic-gate 	if ((p = ct->ct_owner) != NULL) {
10407c478bd9Sstevel@tonic-gate 		mutex_enter(&p->p_crlock);
10417c478bd9Sstevel@tonic-gate 		fail = crgetuid(cr) != crgetuid(p->p_cred);
10427c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_crlock);
10437c478bd9Sstevel@tonic-gate 	}
10447c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate 	return (!fail);
10477c478bd9Sstevel@tonic-gate }
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate /*
10507c478bd9Sstevel@tonic-gate  * contract_owned
10517c478bd9Sstevel@tonic-gate  *
10527c478bd9Sstevel@tonic-gate  * Determines if the specified credential can view an event generated
10537c478bd9Sstevel@tonic-gate  * by the specified contract.  If locked is set, the contract's ct_lock
10547c478bd9Sstevel@tonic-gate  * is held and the caller will need to do additional work to determine
10557c478bd9Sstevel@tonic-gate  * if they truly can see the event.  Returns 1 on success.
10567c478bd9Sstevel@tonic-gate  */
10577c478bd9Sstevel@tonic-gate int
10587c478bd9Sstevel@tonic-gate contract_owned(contract_t *ct, const cred_t *cr, int locked)
10597c478bd9Sstevel@tonic-gate {
10607c478bd9Sstevel@tonic-gate 	int owner, cmatch, zmatch;
10617c478bd9Sstevel@tonic-gate 	uint64_t zuniqid, mzuniqid;
10627c478bd9Sstevel@tonic-gate 	uid_t euid;
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 	ASSERT(locked || MUTEX_NOT_HELD(&ct->ct_lock));
10657c478bd9Sstevel@tonic-gate 
10667c478bd9Sstevel@tonic-gate 	zuniqid = curproc->p_zone->zone_uniqid;
10677c478bd9Sstevel@tonic-gate 	mzuniqid = contract_getzuniqid(ct);
10687c478bd9Sstevel@tonic-gate 	euid = crgetuid(cr);
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 	/*
10717c478bd9Sstevel@tonic-gate 	 * owner: we own the contract
10727c478bd9Sstevel@tonic-gate 	 * cmatch: we are in the creator's (and holder's) zone and our
10737c478bd9Sstevel@tonic-gate 	 *   uid matches the creator's or holder's
10747c478bd9Sstevel@tonic-gate 	 * zmatch: we are in the effective zone of a contract created
10757c478bd9Sstevel@tonic-gate 	 *   in the global zone, and our uid matches that of the
10767c478bd9Sstevel@tonic-gate 	 *   virtualized holder's (zsched/kcred)
10777c478bd9Sstevel@tonic-gate 	 */
10787c478bd9Sstevel@tonic-gate 	owner = (ct->ct_owner == curproc);
10797c478bd9Sstevel@tonic-gate 	cmatch = (zuniqid == ct->ct_czuniqid) &&
10807c478bd9Sstevel@tonic-gate 	    ((ct->ct_cuid == euid) || (!locked && contract_checkcred(ct, cr)));
10817c478bd9Sstevel@tonic-gate 	zmatch = (ct->ct_czuniqid != mzuniqid) && (zuniqid == mzuniqid) &&
10827c478bd9Sstevel@tonic-gate 	    (crgetuid(kcred) == euid);
10837c478bd9Sstevel@tonic-gate 
10847c478bd9Sstevel@tonic-gate 	return (owner || cmatch || zmatch);
10857c478bd9Sstevel@tonic-gate }
10867c478bd9Sstevel@tonic-gate 
10877c478bd9Sstevel@tonic-gate 
10887c478bd9Sstevel@tonic-gate /*
10897c478bd9Sstevel@tonic-gate  * contract_type_init
10907c478bd9Sstevel@tonic-gate  *
10917c478bd9Sstevel@tonic-gate  * Called by contract types to register themselves with the contracts
10927c478bd9Sstevel@tonic-gate  * framework.
10937c478bd9Sstevel@tonic-gate  */
10947c478bd9Sstevel@tonic-gate ct_type_t *
10957c478bd9Sstevel@tonic-gate contract_type_init(ct_typeid_t type, const char *name, contops_t *ops,
10967c478bd9Sstevel@tonic-gate     ct_f_default_t *dfault)
10977c478bd9Sstevel@tonic-gate {
10987c478bd9Sstevel@tonic-gate 	ct_type_t *result;
10997c478bd9Sstevel@tonic-gate 
11007c478bd9Sstevel@tonic-gate 	ASSERT(type < CTT_MAXTYPE);
11017c478bd9Sstevel@tonic-gate 
11027c478bd9Sstevel@tonic-gate 	result = kmem_alloc(sizeof (ct_type_t), KM_SLEEP);
11037c478bd9Sstevel@tonic-gate 
11047c478bd9Sstevel@tonic-gate 	mutex_init(&result->ct_type_lock, NULL, MUTEX_DEFAULT, NULL);
11057c478bd9Sstevel@tonic-gate 	avl_create(&result->ct_type_avl, contract_compar, sizeof (contract_t),
11067c478bd9Sstevel@tonic-gate 	    offsetof(contract_t, ct_cttavl));
11077c478bd9Sstevel@tonic-gate 	cte_queue_create(&result->ct_type_events, CTEL_BUNDLE, 20, 0);
11087c478bd9Sstevel@tonic-gate 	result->ct_type_name = name;
11097c478bd9Sstevel@tonic-gate 	result->ct_type_ops = ops;
11107c478bd9Sstevel@tonic-gate 	result->ct_type_default = dfault;
11117c478bd9Sstevel@tonic-gate 	result->ct_type_evid = 0;
11127c478bd9Sstevel@tonic-gate 	gethrestime(&result->ct_type_timestruc);
11137c478bd9Sstevel@tonic-gate 	result->ct_type_index = type;
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate 	ct_types[type] = result;
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 	return (result);
11187c478bd9Sstevel@tonic-gate }
11197c478bd9Sstevel@tonic-gate 
11207c478bd9Sstevel@tonic-gate /*
11217c478bd9Sstevel@tonic-gate  * contract_type_count
11227c478bd9Sstevel@tonic-gate  *
11237c478bd9Sstevel@tonic-gate  * Obtains the number of contracts of a particular type.
11247c478bd9Sstevel@tonic-gate  */
11257c478bd9Sstevel@tonic-gate int
11267c478bd9Sstevel@tonic-gate contract_type_count(ct_type_t *type)
11277c478bd9Sstevel@tonic-gate {
11287c478bd9Sstevel@tonic-gate 	ulong_t count;
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate 	mutex_enter(&type->ct_type_lock);
11317c478bd9Sstevel@tonic-gate 	count = avl_numnodes(&type->ct_type_avl);
11327c478bd9Sstevel@tonic-gate 	mutex_exit(&type->ct_type_lock);
11337c478bd9Sstevel@tonic-gate 
11347c478bd9Sstevel@tonic-gate 	return (count);
11357c478bd9Sstevel@tonic-gate }
11367c478bd9Sstevel@tonic-gate 
11377c478bd9Sstevel@tonic-gate /*
11387c478bd9Sstevel@tonic-gate  * contract_type_max
11397c478bd9Sstevel@tonic-gate  *
11407c478bd9Sstevel@tonic-gate  * Obtains the maximum contract id of of a particular type.
11417c478bd9Sstevel@tonic-gate  */
11427c478bd9Sstevel@tonic-gate ctid_t
11437c478bd9Sstevel@tonic-gate contract_type_max(ct_type_t *type)
11447c478bd9Sstevel@tonic-gate {
11457c478bd9Sstevel@tonic-gate 	contract_t *ct;
11467c478bd9Sstevel@tonic-gate 	ctid_t res;
11477c478bd9Sstevel@tonic-gate 
11487c478bd9Sstevel@tonic-gate 	mutex_enter(&type->ct_type_lock);
11497c478bd9Sstevel@tonic-gate 	ct = avl_last(&type->ct_type_avl);
11507c478bd9Sstevel@tonic-gate 	res = ct ? ct->ct_id : -1;
11517c478bd9Sstevel@tonic-gate 	mutex_exit(&type->ct_type_lock);
11527c478bd9Sstevel@tonic-gate 
11537c478bd9Sstevel@tonic-gate 	return (res);
11547c478bd9Sstevel@tonic-gate }
11557c478bd9Sstevel@tonic-gate 
11567c478bd9Sstevel@tonic-gate /*
11577c478bd9Sstevel@tonic-gate  * contract_max
11587c478bd9Sstevel@tonic-gate  *
11597c478bd9Sstevel@tonic-gate  * Obtains the maximum contract id.
11607c478bd9Sstevel@tonic-gate  */
11617c478bd9Sstevel@tonic-gate ctid_t
11627c478bd9Sstevel@tonic-gate contract_max(void)
11637c478bd9Sstevel@tonic-gate {
11647c478bd9Sstevel@tonic-gate 	contract_t *ct;
11657c478bd9Sstevel@tonic-gate 	ctid_t res;
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate 	mutex_enter(&contract_lock);
11687c478bd9Sstevel@tonic-gate 	ct = avl_last(&contract_avl);
11697c478bd9Sstevel@tonic-gate 	res = ct ? ct->ct_id : -1;
11707c478bd9Sstevel@tonic-gate 	mutex_exit(&contract_lock);
11717c478bd9Sstevel@tonic-gate 
11727c478bd9Sstevel@tonic-gate 	return (res);
11737c478bd9Sstevel@tonic-gate }
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate /*
11767c478bd9Sstevel@tonic-gate  * contract_lookup_common
11777c478bd9Sstevel@tonic-gate  *
11787c478bd9Sstevel@tonic-gate  * Common code for contract_lookup and contract_type_lookup.  Takes a
11797c478bd9Sstevel@tonic-gate  * pointer to an AVL tree to search in.  Should be called with the
11807c478bd9Sstevel@tonic-gate  * appropriate tree-protecting lock held (unfortunately unassertable).
11817c478bd9Sstevel@tonic-gate  */
11827c478bd9Sstevel@tonic-gate static ctid_t
11837c478bd9Sstevel@tonic-gate contract_lookup_common(avl_tree_t *tree, uint64_t zuniqid, ctid_t current)
11847c478bd9Sstevel@tonic-gate {
11857c478bd9Sstevel@tonic-gate 	contract_t template, *ct;
11867c478bd9Sstevel@tonic-gate 	avl_index_t where;
11877c478bd9Sstevel@tonic-gate 	ctid_t res;
11887c478bd9Sstevel@tonic-gate 
11897c478bd9Sstevel@tonic-gate 	template.ct_id = current;
11907c478bd9Sstevel@tonic-gate 	ct = avl_find(tree, &template, &where);
11917c478bd9Sstevel@tonic-gate 	if (ct == NULL)
11927c478bd9Sstevel@tonic-gate 		ct = avl_nearest(tree, where, AVL_AFTER);
11937c478bd9Sstevel@tonic-gate 	if (zuniqid != GLOBAL_ZONEUNIQID)
11947c478bd9Sstevel@tonic-gate 		while (ct && (contract_getzuniqid(ct) != zuniqid))
11957c478bd9Sstevel@tonic-gate 			ct = AVL_NEXT(tree, ct);
11967c478bd9Sstevel@tonic-gate 	res = ct ? ct->ct_id : -1;
11977c478bd9Sstevel@tonic-gate 
11987c478bd9Sstevel@tonic-gate 	return (res);
11997c478bd9Sstevel@tonic-gate }
12007c478bd9Sstevel@tonic-gate 
12017c478bd9Sstevel@tonic-gate /*
12027c478bd9Sstevel@tonic-gate  * contract_type_lookup
12037c478bd9Sstevel@tonic-gate  *
12047c478bd9Sstevel@tonic-gate  * Returns the next type contract after the specified id, visible from
12057c478bd9Sstevel@tonic-gate  * the specified zone.
12067c478bd9Sstevel@tonic-gate  */
12077c478bd9Sstevel@tonic-gate ctid_t
12087c478bd9Sstevel@tonic-gate contract_type_lookup(ct_type_t *type, uint64_t zuniqid, ctid_t current)
12097c478bd9Sstevel@tonic-gate {
12107c478bd9Sstevel@tonic-gate 	ctid_t res;
12117c478bd9Sstevel@tonic-gate 
12127c478bd9Sstevel@tonic-gate 	mutex_enter(&type->ct_type_lock);
12137c478bd9Sstevel@tonic-gate 	res = contract_lookup_common(&type->ct_type_avl, zuniqid, current);
12147c478bd9Sstevel@tonic-gate 	mutex_exit(&type->ct_type_lock);
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate 	return (res);
12177c478bd9Sstevel@tonic-gate }
12187c478bd9Sstevel@tonic-gate 
12197c478bd9Sstevel@tonic-gate /*
12207c478bd9Sstevel@tonic-gate  * contract_lookup
12217c478bd9Sstevel@tonic-gate  *
12227c478bd9Sstevel@tonic-gate  * Returns the next contract after the specified id, visible from the
12237c478bd9Sstevel@tonic-gate  * specified zone.
12247c478bd9Sstevel@tonic-gate  */
12257c478bd9Sstevel@tonic-gate ctid_t
12267c478bd9Sstevel@tonic-gate contract_lookup(uint64_t zuniqid, ctid_t current)
12277c478bd9Sstevel@tonic-gate {
12287c478bd9Sstevel@tonic-gate 	ctid_t res;
12297c478bd9Sstevel@tonic-gate 
12307c478bd9Sstevel@tonic-gate 	mutex_enter(&contract_lock);
12317c478bd9Sstevel@tonic-gate 	res = contract_lookup_common(&contract_avl, zuniqid, current);
12327c478bd9Sstevel@tonic-gate 	mutex_exit(&contract_lock);
12337c478bd9Sstevel@tonic-gate 
12347c478bd9Sstevel@tonic-gate 	return (res);
12357c478bd9Sstevel@tonic-gate }
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate /*
12387c478bd9Sstevel@tonic-gate  * contract_plookup
12397c478bd9Sstevel@tonic-gate  *
12407c478bd9Sstevel@tonic-gate  * Returns the next contract held by process p after the specified id,
12417c478bd9Sstevel@tonic-gate  * visible from the specified zone.  Made complicated by the fact that
12427c478bd9Sstevel@tonic-gate  * contracts visible in a zone but held by processes outside of the
12437c478bd9Sstevel@tonic-gate  * zone need to appear as being held by zsched to zone members.
12447c478bd9Sstevel@tonic-gate  */
12457c478bd9Sstevel@tonic-gate ctid_t
12467c478bd9Sstevel@tonic-gate contract_plookup(proc_t *p, ctid_t current, uint64_t zuniqid)
12477c478bd9Sstevel@tonic-gate {
12487c478bd9Sstevel@tonic-gate 	contract_t template, *ct;
12497c478bd9Sstevel@tonic-gate 	avl_index_t where;
12507c478bd9Sstevel@tonic-gate 	ctid_t res;
12517c478bd9Sstevel@tonic-gate 
12527c478bd9Sstevel@tonic-gate 	template.ct_id = current;
12537c478bd9Sstevel@tonic-gate 	if (zuniqid != GLOBAL_ZONEUNIQID &&
12547c478bd9Sstevel@tonic-gate 	    (p->p_flag & (SSYS|SZONETOP)) == (SSYS|SZONETOP)) {
12557c478bd9Sstevel@tonic-gate 		/* This is inelegant. */
12567c478bd9Sstevel@tonic-gate 		mutex_enter(&contract_lock);
12577c478bd9Sstevel@tonic-gate 		ct = avl_find(&contract_avl, &template, &where);
12587c478bd9Sstevel@tonic-gate 		if (ct == NULL)
12597c478bd9Sstevel@tonic-gate 			ct = avl_nearest(&contract_avl, where, AVL_AFTER);
12607c478bd9Sstevel@tonic-gate 		while (ct && !(ct->ct_state < CTS_ORPHAN &&
12617c478bd9Sstevel@tonic-gate 		    contract_getzuniqid(ct) == zuniqid &&
12627c478bd9Sstevel@tonic-gate 		    ct->ct_czuniqid == GLOBAL_ZONEUNIQID))
12637c478bd9Sstevel@tonic-gate 			ct = AVL_NEXT(&contract_avl, ct);
12647c478bd9Sstevel@tonic-gate 		res = ct ? ct->ct_id : -1;
12657c478bd9Sstevel@tonic-gate 		mutex_exit(&contract_lock);
12667c478bd9Sstevel@tonic-gate 	} else {
12677c478bd9Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
12687c478bd9Sstevel@tonic-gate 		ct = avl_find(&p->p_ct_held, &template, &where);
12697c478bd9Sstevel@tonic-gate 		if (ct == NULL)
12707c478bd9Sstevel@tonic-gate 			ct = avl_nearest(&p->p_ct_held, where, AVL_AFTER);
12717c478bd9Sstevel@tonic-gate 		res = ct ? ct->ct_id : -1;
12727c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
12737c478bd9Sstevel@tonic-gate 	}
12747c478bd9Sstevel@tonic-gate 
12757c478bd9Sstevel@tonic-gate 	return (res);
12767c478bd9Sstevel@tonic-gate }
12777c478bd9Sstevel@tonic-gate 
12787c478bd9Sstevel@tonic-gate /*
12797c478bd9Sstevel@tonic-gate  * contract_ptr_common
12807c478bd9Sstevel@tonic-gate  *
12817c478bd9Sstevel@tonic-gate  * Common code for contract_ptr and contract_type_ptr.  Takes a pointer
12827c478bd9Sstevel@tonic-gate  * to an AVL tree to search in.  Should be called with the appropriate
12837c478bd9Sstevel@tonic-gate  * tree-protecting lock held (unfortunately unassertable).
12847c478bd9Sstevel@tonic-gate  */
12857c478bd9Sstevel@tonic-gate static contract_t *
12867c478bd9Sstevel@tonic-gate contract_ptr_common(avl_tree_t *tree, ctid_t id, uint64_t zuniqid)
12877c478bd9Sstevel@tonic-gate {
12887c478bd9Sstevel@tonic-gate 	contract_t template, *ct;
12897c478bd9Sstevel@tonic-gate 
12907c478bd9Sstevel@tonic-gate 	template.ct_id = id;
12917c478bd9Sstevel@tonic-gate 	ct = avl_find(tree, &template, NULL);
12927c478bd9Sstevel@tonic-gate 	if (ct == NULL || (zuniqid != GLOBAL_ZONEUNIQID &&
12937c478bd9Sstevel@tonic-gate 	    contract_getzuniqid(ct) != zuniqid)) {
12947c478bd9Sstevel@tonic-gate 		return (NULL);
12957c478bd9Sstevel@tonic-gate 	}
12967c478bd9Sstevel@tonic-gate 
12977c478bd9Sstevel@tonic-gate 	/*
12987c478bd9Sstevel@tonic-gate 	 * Check to see if a thread is in the window in contract_rele
12997c478bd9Sstevel@tonic-gate 	 * between dropping the reference count and removing the
13007c478bd9Sstevel@tonic-gate 	 * contract from the type AVL.
13017c478bd9Sstevel@tonic-gate 	 */
13027c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_reflock);
13037c478bd9Sstevel@tonic-gate 	if (ct->ct_ref) {
13047c478bd9Sstevel@tonic-gate 		ct->ct_ref++;
13057c478bd9Sstevel@tonic-gate 		mutex_exit(&ct->ct_reflock);
13067c478bd9Sstevel@tonic-gate 	} else {
13077c478bd9Sstevel@tonic-gate 		mutex_exit(&ct->ct_reflock);
13087c478bd9Sstevel@tonic-gate 		ct = NULL;
13097c478bd9Sstevel@tonic-gate 	}
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate 	return (ct);
13127c478bd9Sstevel@tonic-gate }
13137c478bd9Sstevel@tonic-gate 
13147c478bd9Sstevel@tonic-gate /*
13157c478bd9Sstevel@tonic-gate  * contract_type_ptr
13167c478bd9Sstevel@tonic-gate  *
13177c478bd9Sstevel@tonic-gate  * Returns a pointer to the contract with the specified id.  The
13187c478bd9Sstevel@tonic-gate  * contract is held, so the caller needs to release the reference when
13197c478bd9Sstevel@tonic-gate  * it is through with the contract.
13207c478bd9Sstevel@tonic-gate  */
13217c478bd9Sstevel@tonic-gate contract_t *
13227c478bd9Sstevel@tonic-gate contract_type_ptr(ct_type_t *type, ctid_t id, uint64_t zuniqid)
13237c478bd9Sstevel@tonic-gate {
13247c478bd9Sstevel@tonic-gate 	contract_t *ct;
13257c478bd9Sstevel@tonic-gate 
13267c478bd9Sstevel@tonic-gate 	mutex_enter(&type->ct_type_lock);
13277c478bd9Sstevel@tonic-gate 	ct = contract_ptr_common(&type->ct_type_avl, id, zuniqid);
13287c478bd9Sstevel@tonic-gate 	mutex_exit(&type->ct_type_lock);
13297c478bd9Sstevel@tonic-gate 
13307c478bd9Sstevel@tonic-gate 	return (ct);
13317c478bd9Sstevel@tonic-gate }
13327c478bd9Sstevel@tonic-gate 
13337c478bd9Sstevel@tonic-gate /*
13347c478bd9Sstevel@tonic-gate  * contract_ptr
13357c478bd9Sstevel@tonic-gate  *
13367c478bd9Sstevel@tonic-gate  * Returns a pointer to the contract with the specified id.  The
13377c478bd9Sstevel@tonic-gate  * contract is held, so the caller needs to release the reference when
13387c478bd9Sstevel@tonic-gate  * it is through with the contract.
13397c478bd9Sstevel@tonic-gate  */
13407c478bd9Sstevel@tonic-gate contract_t *
13417c478bd9Sstevel@tonic-gate contract_ptr(ctid_t id, uint64_t zuniqid)
13427c478bd9Sstevel@tonic-gate {
13437c478bd9Sstevel@tonic-gate 	contract_t *ct;
13447c478bd9Sstevel@tonic-gate 
13457c478bd9Sstevel@tonic-gate 	mutex_enter(&contract_lock);
13467c478bd9Sstevel@tonic-gate 	ct = contract_ptr_common(&contract_avl, id, zuniqid);
13477c478bd9Sstevel@tonic-gate 	mutex_exit(&contract_lock);
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate 	return (ct);
13507c478bd9Sstevel@tonic-gate }
13517c478bd9Sstevel@tonic-gate 
13527c478bd9Sstevel@tonic-gate /*
13537c478bd9Sstevel@tonic-gate  * contract_type_time
13547c478bd9Sstevel@tonic-gate  *
13557c478bd9Sstevel@tonic-gate  * Obtains the last time a contract of a particular type was created.
13567c478bd9Sstevel@tonic-gate  */
13577c478bd9Sstevel@tonic-gate void
13587c478bd9Sstevel@tonic-gate contract_type_time(ct_type_t *type, timestruc_t *time)
13597c478bd9Sstevel@tonic-gate {
13607c478bd9Sstevel@tonic-gate 	mutex_enter(&type->ct_type_lock);
13617c478bd9Sstevel@tonic-gate 	*time = type->ct_type_timestruc;
13627c478bd9Sstevel@tonic-gate 	mutex_exit(&type->ct_type_lock);
13637c478bd9Sstevel@tonic-gate }
13647c478bd9Sstevel@tonic-gate 
13657c478bd9Sstevel@tonic-gate /*
13667c478bd9Sstevel@tonic-gate  * contract_type_bundle
13677c478bd9Sstevel@tonic-gate  *
13687c478bd9Sstevel@tonic-gate  * Obtains a type's bundle queue.
13697c478bd9Sstevel@tonic-gate  */
13707c478bd9Sstevel@tonic-gate ct_equeue_t *
13717c478bd9Sstevel@tonic-gate contract_type_bundle(ct_type_t *type)
13727c478bd9Sstevel@tonic-gate {
13737c478bd9Sstevel@tonic-gate 	return (&type->ct_type_events);
13747c478bd9Sstevel@tonic-gate }
13757c478bd9Sstevel@tonic-gate 
13767c478bd9Sstevel@tonic-gate /*
13777c478bd9Sstevel@tonic-gate  * contract_type_pbundle
13787c478bd9Sstevel@tonic-gate  *
13797c478bd9Sstevel@tonic-gate  * Obtain's a process's bundle queue.  If one doesn't exist, one is
13807c478bd9Sstevel@tonic-gate  * created.  Often used simply to ensure that a bundle queue is
13817c478bd9Sstevel@tonic-gate  * allocated.
13827c478bd9Sstevel@tonic-gate  */
13837c478bd9Sstevel@tonic-gate ct_equeue_t *
13847c478bd9Sstevel@tonic-gate contract_type_pbundle(ct_type_t *type, proc_t *pp)
13857c478bd9Sstevel@tonic-gate {
13867c478bd9Sstevel@tonic-gate 	/*
13877c478bd9Sstevel@tonic-gate 	 * If there isn't an array of bundle queues, allocate one.
13887c478bd9Sstevel@tonic-gate 	 */
13897c478bd9Sstevel@tonic-gate 	if (pp->p_ct_equeue == NULL) {
13907c478bd9Sstevel@tonic-gate 		size_t size = CTT_MAXTYPE * sizeof (ct_equeue_t *);
13917c478bd9Sstevel@tonic-gate 		ct_equeue_t **qa = kmem_zalloc(size, KM_SLEEP);
13927c478bd9Sstevel@tonic-gate 
13937c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->p_lock);
13947c478bd9Sstevel@tonic-gate 		if (pp->p_ct_equeue)
13957c478bd9Sstevel@tonic-gate 			kmem_free(qa, size);
13967c478bd9Sstevel@tonic-gate 		else
13977c478bd9Sstevel@tonic-gate 			pp->p_ct_equeue = qa;
13987c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->p_lock);
13997c478bd9Sstevel@tonic-gate 	}
14007c478bd9Sstevel@tonic-gate 
14017c478bd9Sstevel@tonic-gate 	/*
14027c478bd9Sstevel@tonic-gate 	 * If there isn't a bundle queue of the required type, allocate
14037c478bd9Sstevel@tonic-gate 	 * one.
14047c478bd9Sstevel@tonic-gate 	 */
14057c478bd9Sstevel@tonic-gate 	if (pp->p_ct_equeue[type->ct_type_index] == NULL) {
14067c478bd9Sstevel@tonic-gate 		ct_equeue_t *q = kmem_zalloc(sizeof (ct_equeue_t), KM_SLEEP);
14077c478bd9Sstevel@tonic-gate 		cte_queue_create(q, CTEL_PBUNDLE, 20, 1);
14087c478bd9Sstevel@tonic-gate 
14097c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->p_lock);
14107c478bd9Sstevel@tonic-gate 		if (pp->p_ct_equeue[type->ct_type_index])
14117c478bd9Sstevel@tonic-gate 			cte_queue_drain(q, 0);
14127c478bd9Sstevel@tonic-gate 		else
14137c478bd9Sstevel@tonic-gate 			pp->p_ct_equeue[type->ct_type_index] = q;
14147c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->p_lock);
14157c478bd9Sstevel@tonic-gate 	}
14167c478bd9Sstevel@tonic-gate 
14177c478bd9Sstevel@tonic-gate 	return (pp->p_ct_equeue[type->ct_type_index]);
14187c478bd9Sstevel@tonic-gate }
14197c478bd9Sstevel@tonic-gate 
14207c478bd9Sstevel@tonic-gate /*
14217c478bd9Sstevel@tonic-gate  * ctmpl_free
14227c478bd9Sstevel@tonic-gate  *
14237c478bd9Sstevel@tonic-gate  * Frees a template.
14247c478bd9Sstevel@tonic-gate  */
14257c478bd9Sstevel@tonic-gate void
14267c478bd9Sstevel@tonic-gate ctmpl_free(ct_template_t *template)
14277c478bd9Sstevel@tonic-gate {
14287c478bd9Sstevel@tonic-gate 	mutex_destroy(&template->ctmpl_lock);
14297c478bd9Sstevel@tonic-gate 	template->ctmpl_ops->ctop_free(template);
14307c478bd9Sstevel@tonic-gate }
14317c478bd9Sstevel@tonic-gate 
14327c478bd9Sstevel@tonic-gate /*
14337c478bd9Sstevel@tonic-gate  * ctmpl_dup
14347c478bd9Sstevel@tonic-gate  *
14357c478bd9Sstevel@tonic-gate  * Creates a copy of a template.
14367c478bd9Sstevel@tonic-gate  */
14377c478bd9Sstevel@tonic-gate ct_template_t *
14387c478bd9Sstevel@tonic-gate ctmpl_dup(ct_template_t *template)
14397c478bd9Sstevel@tonic-gate {
14407c478bd9Sstevel@tonic-gate 	ct_template_t *new;
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 	if (template == NULL)
14437c478bd9Sstevel@tonic-gate 		return (NULL);
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate 	new = template->ctmpl_ops->ctop_dup(template);
14467c478bd9Sstevel@tonic-gate 	/*
14477c478bd9Sstevel@tonic-gate 	 * ctmpl_lock was taken by ctop_dup's call to ctmpl_copy and
14487c478bd9Sstevel@tonic-gate 	 * should have remain held until now.
14497c478bd9Sstevel@tonic-gate 	 */
14507c478bd9Sstevel@tonic-gate 	mutex_exit(&template->ctmpl_lock);
14517c478bd9Sstevel@tonic-gate 
14527c478bd9Sstevel@tonic-gate 	return (new);
14537c478bd9Sstevel@tonic-gate }
14547c478bd9Sstevel@tonic-gate 
14557c478bd9Sstevel@tonic-gate /*
14567c478bd9Sstevel@tonic-gate  * ctmpl_set
14577c478bd9Sstevel@tonic-gate  *
14587c478bd9Sstevel@tonic-gate  * Sets the requested terms of a template.
14597c478bd9Sstevel@tonic-gate  */
14607c478bd9Sstevel@tonic-gate int
14617c478bd9Sstevel@tonic-gate ctmpl_set(ct_template_t *template, ct_param_t *param, const cred_t *cr)
14627c478bd9Sstevel@tonic-gate {
14637c478bd9Sstevel@tonic-gate 	int result = 0;
1464*d170b13aSacruz 	uint64_t param_value;
1465*d170b13aSacruz 
1466*d170b13aSacruz 	if (param->ctpm_id == CTP_COOKIE ||
1467*d170b13aSacruz 	    param->ctpm_id == CTP_EV_INFO ||
1468*d170b13aSacruz 	    param->ctpm_id == CTP_EV_CRITICAL) {
1469*d170b13aSacruz 		if (param->ctpm_size < sizeof (uint64_t)) {
1470*d170b13aSacruz 			return (EINVAL);
1471*d170b13aSacruz 		} else {
1472*d170b13aSacruz 			param_value = *(uint64_t *)param->ctpm_value;
1473*d170b13aSacruz 		}
1474*d170b13aSacruz 	}
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate 	mutex_enter(&template->ctmpl_lock);
14777c478bd9Sstevel@tonic-gate 	switch (param->ctpm_id) {
14787c478bd9Sstevel@tonic-gate 	case CTP_COOKIE:
14797b209c2cSacruz 		template->ctmpl_cookie = param_value;
14807c478bd9Sstevel@tonic-gate 		break;
14817c478bd9Sstevel@tonic-gate 	case CTP_EV_INFO:
14827b209c2cSacruz 		if (param_value & ~(uint64_t)template->ctmpl_ops->allevents)
14837c478bd9Sstevel@tonic-gate 			result = EINVAL;
14847c478bd9Sstevel@tonic-gate 		else
14857b209c2cSacruz 			template->ctmpl_ev_info = param_value;
14867c478bd9Sstevel@tonic-gate 		break;
14877c478bd9Sstevel@tonic-gate 	case CTP_EV_CRITICAL:
14887b209c2cSacruz 		if (param_value & ~(uint64_t)template->ctmpl_ops->allevents) {
14897c478bd9Sstevel@tonic-gate 			result = EINVAL;
14907c478bd9Sstevel@tonic-gate 			break;
14917b209c2cSacruz 		} else if ((~template->ctmpl_ev_crit & param_value) == 0) {
14927c478bd9Sstevel@tonic-gate 			/*
14937c478bd9Sstevel@tonic-gate 			 * Assume that a pure reduction of the critical
14947c478bd9Sstevel@tonic-gate 			 * set is allowed by the contract type.
14957c478bd9Sstevel@tonic-gate 			 */
14967b209c2cSacruz 			template->ctmpl_ev_crit = param_value;
14977c478bd9Sstevel@tonic-gate 			break;
14987c478bd9Sstevel@tonic-gate 		}
14997c478bd9Sstevel@tonic-gate 		/*
15007c478bd9Sstevel@tonic-gate 		 * There may be restrictions on what we can make
15017c478bd9Sstevel@tonic-gate 		 * critical, so we defer to the judgement of the
15027c478bd9Sstevel@tonic-gate 		 * contract type.
15037c478bd9Sstevel@tonic-gate 		 */
15047c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
15057c478bd9Sstevel@tonic-gate 	default:
15067c478bd9Sstevel@tonic-gate 		result = template->ctmpl_ops->ctop_set(template, param, cr);
15077c478bd9Sstevel@tonic-gate 	}
15087c478bd9Sstevel@tonic-gate 	mutex_exit(&template->ctmpl_lock);
15097c478bd9Sstevel@tonic-gate 
15107c478bd9Sstevel@tonic-gate 	return (result);
15117c478bd9Sstevel@tonic-gate }
15127c478bd9Sstevel@tonic-gate 
15137c478bd9Sstevel@tonic-gate /*
15147c478bd9Sstevel@tonic-gate  * ctmpl_get
15157c478bd9Sstevel@tonic-gate  *
15167c478bd9Sstevel@tonic-gate  * Obtains the requested terms from a template.
1517*d170b13aSacruz  *
1518*d170b13aSacruz  * If the term requested is a variable-sized term and the buffer
1519*d170b13aSacruz  * provided is too small for the data, we truncate the data and return
1520*d170b13aSacruz  * the buffer size necessary to fit the term in param->ctpm_size. If the
1521*d170b13aSacruz  * term requested is fix-sized (uint64_t) and the buffer provided is too
1522*d170b13aSacruz  * small, we return EINVAL.  This should never happen if you're using
1523*d170b13aSacruz  * libcontract(3LIB), only if you call ioctl with a hand constructed
1524*d170b13aSacruz  * ct_param_t argument.
1525*d170b13aSacruz  *
1526*d170b13aSacruz  * Currently, only contract specific parameters have variable-sized
1527*d170b13aSacruz  * parameters.
15287c478bd9Sstevel@tonic-gate  */
15297c478bd9Sstevel@tonic-gate int
15307c478bd9Sstevel@tonic-gate ctmpl_get(ct_template_t *template, ct_param_t *param)
15317c478bd9Sstevel@tonic-gate {
15327c478bd9Sstevel@tonic-gate 	int result = 0;
1533*d170b13aSacruz 	uint64_t *param_value;
1534*d170b13aSacruz 
1535*d170b13aSacruz 	if (param->ctpm_id == CTP_COOKIE ||
1536*d170b13aSacruz 	    param->ctpm_id == CTP_EV_INFO ||
1537*d170b13aSacruz 	    param->ctpm_id == CTP_EV_CRITICAL) {
1538*d170b13aSacruz 		if (param->ctpm_size < sizeof (uint64_t)) {
1539*d170b13aSacruz 			return (EINVAL);
1540*d170b13aSacruz 		} else {
1541*d170b13aSacruz 			param_value = param->ctpm_value;
1542*d170b13aSacruz 			param->ctpm_size = sizeof (uint64_t);
1543*d170b13aSacruz 		}
1544*d170b13aSacruz 	}
15457c478bd9Sstevel@tonic-gate 
15467c478bd9Sstevel@tonic-gate 	mutex_enter(&template->ctmpl_lock);
15477c478bd9Sstevel@tonic-gate 	switch (param->ctpm_id) {
15487c478bd9Sstevel@tonic-gate 	case CTP_COOKIE:
15497b209c2cSacruz 		*param_value = template->ctmpl_cookie;
15507c478bd9Sstevel@tonic-gate 		break;
15517c478bd9Sstevel@tonic-gate 	case CTP_EV_INFO:
15527b209c2cSacruz 		*param_value = template->ctmpl_ev_info;
15537c478bd9Sstevel@tonic-gate 		break;
15547c478bd9Sstevel@tonic-gate 	case CTP_EV_CRITICAL:
15557b209c2cSacruz 		*param_value = template->ctmpl_ev_crit;
15567c478bd9Sstevel@tonic-gate 		break;
15577c478bd9Sstevel@tonic-gate 	default:
15587c478bd9Sstevel@tonic-gate 		result = template->ctmpl_ops->ctop_get(template, param);
15597c478bd9Sstevel@tonic-gate 	}
15607c478bd9Sstevel@tonic-gate 	mutex_exit(&template->ctmpl_lock);
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 	return (result);
15637c478bd9Sstevel@tonic-gate }
15647c478bd9Sstevel@tonic-gate 
15657c478bd9Sstevel@tonic-gate /*
15667c478bd9Sstevel@tonic-gate  * ctmpl_makecurrent
15677c478bd9Sstevel@tonic-gate  *
15687c478bd9Sstevel@tonic-gate  * Used by ctmpl_activate and ctmpl_clear to set the current thread's
15697c478bd9Sstevel@tonic-gate  * active template.  Frees the old active template, if there was one.
15707c478bd9Sstevel@tonic-gate  */
15717c478bd9Sstevel@tonic-gate static void
15727c478bd9Sstevel@tonic-gate ctmpl_makecurrent(ct_template_t *template, ct_template_t *new)
15737c478bd9Sstevel@tonic-gate {
15747c478bd9Sstevel@tonic-gate 	klwp_t *curlwp = ttolwp(curthread);
15757c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
15767c478bd9Sstevel@tonic-gate 	ct_template_t *old;
15777c478bd9Sstevel@tonic-gate 
15787c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
15797c478bd9Sstevel@tonic-gate 	old = curlwp->lwp_ct_active[template->ctmpl_type->ct_type_index];
15807c478bd9Sstevel@tonic-gate 	curlwp->lwp_ct_active[template->ctmpl_type->ct_type_index] = new;
15817c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
15827c478bd9Sstevel@tonic-gate 
15837c478bd9Sstevel@tonic-gate 	if (old)
15847c478bd9Sstevel@tonic-gate 		ctmpl_free(old);
15857c478bd9Sstevel@tonic-gate }
15867c478bd9Sstevel@tonic-gate 
15877c478bd9Sstevel@tonic-gate /*
15887c478bd9Sstevel@tonic-gate  * ctmpl_activate
15897c478bd9Sstevel@tonic-gate  *
15907c478bd9Sstevel@tonic-gate  * Copy the specified template as the current thread's activate
15917c478bd9Sstevel@tonic-gate  * template of that type.
15927c478bd9Sstevel@tonic-gate  */
15937c478bd9Sstevel@tonic-gate void
15947c478bd9Sstevel@tonic-gate ctmpl_activate(ct_template_t *template)
15957c478bd9Sstevel@tonic-gate {
15967c478bd9Sstevel@tonic-gate 	ctmpl_makecurrent(template, ctmpl_dup(template));
15977c478bd9Sstevel@tonic-gate }
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate /*
16007c478bd9Sstevel@tonic-gate  * ctmpl_clear
16017c478bd9Sstevel@tonic-gate  *
16027c478bd9Sstevel@tonic-gate  * Clears the current thread's activate template of the same type as
16037c478bd9Sstevel@tonic-gate  * the specified template.
16047c478bd9Sstevel@tonic-gate  */
16057c478bd9Sstevel@tonic-gate void
16067c478bd9Sstevel@tonic-gate ctmpl_clear(ct_template_t *template)
16077c478bd9Sstevel@tonic-gate {
16087c478bd9Sstevel@tonic-gate 	ctmpl_makecurrent(template, NULL);
16097c478bd9Sstevel@tonic-gate }
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate /*
16127c478bd9Sstevel@tonic-gate  * ctmpl_create
16137c478bd9Sstevel@tonic-gate  *
16147c478bd9Sstevel@tonic-gate  * Creates a new contract using the specified template.
16157c478bd9Sstevel@tonic-gate  */
16167c478bd9Sstevel@tonic-gate int
161725e8c5aaSvikram ctmpl_create(ct_template_t *template, ctid_t *ctidp)
16187c478bd9Sstevel@tonic-gate {
161925e8c5aaSvikram 	return (template->ctmpl_ops->ctop_create(template, ctidp));
16207c478bd9Sstevel@tonic-gate }
16217c478bd9Sstevel@tonic-gate 
16227c478bd9Sstevel@tonic-gate /*
16237c478bd9Sstevel@tonic-gate  * ctmpl_init
16247c478bd9Sstevel@tonic-gate  *
16257c478bd9Sstevel@tonic-gate  * Initializes the common portion of a new contract template.
16267c478bd9Sstevel@tonic-gate  */
16277c478bd9Sstevel@tonic-gate void
16287c478bd9Sstevel@tonic-gate ctmpl_init(ct_template_t *new, ctmplops_t *ops, ct_type_t *type, void *data)
16297c478bd9Sstevel@tonic-gate {
16307c478bd9Sstevel@tonic-gate 	mutex_init(&new->ctmpl_lock, NULL, MUTEX_DEFAULT, NULL);
16317c478bd9Sstevel@tonic-gate 	new->ctmpl_ops = ops;
16327c478bd9Sstevel@tonic-gate 	new->ctmpl_type = type;
16337c478bd9Sstevel@tonic-gate 	new->ctmpl_data = data;
16347c478bd9Sstevel@tonic-gate 	new->ctmpl_ev_info = new->ctmpl_ev_crit = 0;
16357c478bd9Sstevel@tonic-gate 	new->ctmpl_cookie = 0;
16367c478bd9Sstevel@tonic-gate }
16377c478bd9Sstevel@tonic-gate 
16387c478bd9Sstevel@tonic-gate /*
16397c478bd9Sstevel@tonic-gate  * ctmpl_copy
16407c478bd9Sstevel@tonic-gate  *
16417c478bd9Sstevel@tonic-gate  * Copies the common portions of a contract template.  Intended for use
16427c478bd9Sstevel@tonic-gate  * by a contract type's ctop_dup template op.  Returns with the old
16437c478bd9Sstevel@tonic-gate  * template's lock held, which will should remain held until the
16447c478bd9Sstevel@tonic-gate  * template op returns (it is dropped by ctmpl_dup).
16457c478bd9Sstevel@tonic-gate  */
16467c478bd9Sstevel@tonic-gate void
16477c478bd9Sstevel@tonic-gate ctmpl_copy(ct_template_t *new, ct_template_t *old)
16487c478bd9Sstevel@tonic-gate {
16497c478bd9Sstevel@tonic-gate 	mutex_init(&new->ctmpl_lock, NULL, MUTEX_DEFAULT, NULL);
16507c478bd9Sstevel@tonic-gate 	mutex_enter(&old->ctmpl_lock);
16517c478bd9Sstevel@tonic-gate 	new->ctmpl_ops = old->ctmpl_ops;
16527c478bd9Sstevel@tonic-gate 	new->ctmpl_type = old->ctmpl_type;
16537c478bd9Sstevel@tonic-gate 	new->ctmpl_ev_crit = old->ctmpl_ev_crit;
16547c478bd9Sstevel@tonic-gate 	new->ctmpl_ev_info = old->ctmpl_ev_info;
16557c478bd9Sstevel@tonic-gate 	new->ctmpl_cookie = old->ctmpl_cookie;
16567c478bd9Sstevel@tonic-gate }
16577c478bd9Sstevel@tonic-gate 
16587c478bd9Sstevel@tonic-gate /*
16597c478bd9Sstevel@tonic-gate  * ctmpl_create_inval
16607c478bd9Sstevel@tonic-gate  *
16617c478bd9Sstevel@tonic-gate  * Returns EINVAL.  Provided for the convenience of those contract
16627c478bd9Sstevel@tonic-gate  * types which don't support ct_tmpl_create(3contract) and would
16637c478bd9Sstevel@tonic-gate  * otherwise need to create their own stub for the ctop_create template
16647c478bd9Sstevel@tonic-gate  * op.
16657c478bd9Sstevel@tonic-gate  */
16667c478bd9Sstevel@tonic-gate /*ARGSUSED*/
16677c478bd9Sstevel@tonic-gate int
166825e8c5aaSvikram ctmpl_create_inval(ct_template_t *template, ctid_t *ctidp)
16697c478bd9Sstevel@tonic-gate {
16707c478bd9Sstevel@tonic-gate 	return (EINVAL);
16717c478bd9Sstevel@tonic-gate }
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 
16747c478bd9Sstevel@tonic-gate /*
16757c478bd9Sstevel@tonic-gate  * cte_queue_create
16767c478bd9Sstevel@tonic-gate  *
16777c478bd9Sstevel@tonic-gate  * Initializes a queue of a particular type.  If dynamic is set, the
16787c478bd9Sstevel@tonic-gate  * queue is to be freed when its last listener is removed after being
16797c478bd9Sstevel@tonic-gate  * drained.
16807c478bd9Sstevel@tonic-gate  */
16817c478bd9Sstevel@tonic-gate static void
16827c478bd9Sstevel@tonic-gate cte_queue_create(ct_equeue_t *q, ct_listnum_t list, int maxinf, int dynamic)
16837c478bd9Sstevel@tonic-gate {
16847c478bd9Sstevel@tonic-gate 	mutex_init(&q->ctq_lock, NULL, MUTEX_DEFAULT, NULL);
16857c478bd9Sstevel@tonic-gate 	q->ctq_listno = list;
16867c478bd9Sstevel@tonic-gate 	list_create(&q->ctq_events, sizeof (ct_kevent_t),
16877c478bd9Sstevel@tonic-gate 	    offsetof(ct_kevent_t, cte_nodes[list].ctm_node));
16887c478bd9Sstevel@tonic-gate 	list_create(&q->ctq_listeners, sizeof (ct_listener_t),
16897c478bd9Sstevel@tonic-gate 	    offsetof(ct_listener_t, ctl_allnode));
16907c478bd9Sstevel@tonic-gate 	list_create(&q->ctq_tail, sizeof (ct_listener_t),
16917c478bd9Sstevel@tonic-gate 	    offsetof(ct_listener_t, ctl_tailnode));
16927c478bd9Sstevel@tonic-gate 	gethrestime(&q->ctq_atime);
16937c478bd9Sstevel@tonic-gate 	q->ctq_nlisteners = 0;
16947c478bd9Sstevel@tonic-gate 	q->ctq_nreliable = 0;
16957c478bd9Sstevel@tonic-gate 	q->ctq_ninf = 0;
16967c478bd9Sstevel@tonic-gate 	q->ctq_max = maxinf;
16977c478bd9Sstevel@tonic-gate 
16987c478bd9Sstevel@tonic-gate 	/*
16997c478bd9Sstevel@tonic-gate 	 * Bundle queues and contract queues are embedded in other
17007c478bd9Sstevel@tonic-gate 	 * structures and are implicitly referenced counted by virtue
17017c478bd9Sstevel@tonic-gate 	 * of their vnodes' indirect hold on their contracts.  Process
17027c478bd9Sstevel@tonic-gate 	 * bundle queues are dynamically allocated and may persist
17037c478bd9Sstevel@tonic-gate 	 * after the death of the process, so they must be explicitly
17047c478bd9Sstevel@tonic-gate 	 * reference counted.
17057c478bd9Sstevel@tonic-gate 	 */
17067c478bd9Sstevel@tonic-gate 	q->ctq_flags = dynamic ? CTQ_REFFED : 0;
17077c478bd9Sstevel@tonic-gate }
17087c478bd9Sstevel@tonic-gate 
17097c478bd9Sstevel@tonic-gate /*
17107c478bd9Sstevel@tonic-gate  * cte_queue_destroy
17117c478bd9Sstevel@tonic-gate  *
17127c478bd9Sstevel@tonic-gate  * Destroys the specified queue.  The queue is freed if referenced
17137c478bd9Sstevel@tonic-gate  * counted.
17147c478bd9Sstevel@tonic-gate  */
17157c478bd9Sstevel@tonic-gate static void
17167c478bd9Sstevel@tonic-gate cte_queue_destroy(ct_equeue_t *q)
17177c478bd9Sstevel@tonic-gate {
17187c478bd9Sstevel@tonic-gate 	ASSERT(q->ctq_flags & CTQ_DEAD);
17197c478bd9Sstevel@tonic-gate 	ASSERT(q->ctq_nlisteners == 0);
17207c478bd9Sstevel@tonic-gate 	ASSERT(q->ctq_nreliable == 0);
17217c478bd9Sstevel@tonic-gate 	list_destroy(&q->ctq_events);
17227c478bd9Sstevel@tonic-gate 	list_destroy(&q->ctq_listeners);
17237c478bd9Sstevel@tonic-gate 	list_destroy(&q->ctq_tail);
17247c478bd9Sstevel@tonic-gate 	mutex_destroy(&q->ctq_lock);
17257c478bd9Sstevel@tonic-gate 	if (q->ctq_flags & CTQ_REFFED)
17267c478bd9Sstevel@tonic-gate 		kmem_free(q, sizeof (ct_equeue_t));
17277c478bd9Sstevel@tonic-gate }
17287c478bd9Sstevel@tonic-gate 
17297c478bd9Sstevel@tonic-gate /*
17307c478bd9Sstevel@tonic-gate  * cte_hold
17317c478bd9Sstevel@tonic-gate  *
17327c478bd9Sstevel@tonic-gate  * Takes a hold on the specified event.
17337c478bd9Sstevel@tonic-gate  */
17347c478bd9Sstevel@tonic-gate static void
17357c478bd9Sstevel@tonic-gate cte_hold(ct_kevent_t *e)
17367c478bd9Sstevel@tonic-gate {
17377c478bd9Sstevel@tonic-gate 	mutex_enter(&e->cte_lock);
17387c478bd9Sstevel@tonic-gate 	ASSERT(e->cte_refs > 0);
17397c478bd9Sstevel@tonic-gate 	e->cte_refs++;
17407c478bd9Sstevel@tonic-gate 	mutex_exit(&e->cte_lock);
17417c478bd9Sstevel@tonic-gate }
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate /*
17447c478bd9Sstevel@tonic-gate  * cte_rele
17457c478bd9Sstevel@tonic-gate  *
17467c478bd9Sstevel@tonic-gate  * Releases a hold on the specified event.  If the caller had the last
17477c478bd9Sstevel@tonic-gate  * reference, frees the event and releases its hold on the contract
17487c478bd9Sstevel@tonic-gate  * that generated it.
17497c478bd9Sstevel@tonic-gate  */
17507c478bd9Sstevel@tonic-gate static void
17517c478bd9Sstevel@tonic-gate cte_rele(ct_kevent_t *e)
17527c478bd9Sstevel@tonic-gate {
17537c478bd9Sstevel@tonic-gate 	mutex_enter(&e->cte_lock);
17547c478bd9Sstevel@tonic-gate 	ASSERT(e->cte_refs > 0);
17557c478bd9Sstevel@tonic-gate 	if (--e->cte_refs) {
17567c478bd9Sstevel@tonic-gate 		mutex_exit(&e->cte_lock);
17577c478bd9Sstevel@tonic-gate 		return;
17587c478bd9Sstevel@tonic-gate 	}
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate 	contract_rele(e->cte_contract);
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 	mutex_destroy(&e->cte_lock);
17637c478bd9Sstevel@tonic-gate 	if (e->cte_data)
17647c478bd9Sstevel@tonic-gate 		nvlist_free(e->cte_data);
17657c478bd9Sstevel@tonic-gate 	if (e->cte_gdata)
17667c478bd9Sstevel@tonic-gate 		nvlist_free(e->cte_gdata);
17677c478bd9Sstevel@tonic-gate 	kmem_free(e, sizeof (ct_kevent_t));
17687c478bd9Sstevel@tonic-gate }
17697c478bd9Sstevel@tonic-gate 
17707c478bd9Sstevel@tonic-gate /*
17717c478bd9Sstevel@tonic-gate  * cte_qrele
17727c478bd9Sstevel@tonic-gate  *
17737c478bd9Sstevel@tonic-gate  * Remove this listener's hold on the specified event, removing and
17747c478bd9Sstevel@tonic-gate  * releasing the queue's hold on the event if appropriate.
17757c478bd9Sstevel@tonic-gate  */
17767c478bd9Sstevel@tonic-gate static void
17777c478bd9Sstevel@tonic-gate cte_qrele(ct_equeue_t *q, ct_listener_t *l, ct_kevent_t *e)
17787c478bd9Sstevel@tonic-gate {
17797c478bd9Sstevel@tonic-gate 	ct_member_t *member = &e->cte_nodes[q->ctq_listno];
17807c478bd9Sstevel@tonic-gate 
17817c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&q->ctq_lock));
17827c478bd9Sstevel@tonic-gate 
17837c478bd9Sstevel@tonic-gate 	if (l->ctl_flags & CTLF_RELIABLE)
17847c478bd9Sstevel@tonic-gate 		member->ctm_nreliable--;
17857c478bd9Sstevel@tonic-gate 	if ((--member->ctm_refs == 0) && member->ctm_trimmed) {
17867c478bd9Sstevel@tonic-gate 		member->ctm_trimmed = 0;
17877c478bd9Sstevel@tonic-gate 		list_remove(&q->ctq_events, e);
17887c478bd9Sstevel@tonic-gate 		cte_rele(e);
17897c478bd9Sstevel@tonic-gate 	}
17907c478bd9Sstevel@tonic-gate }
17917c478bd9Sstevel@tonic-gate 
17927c478bd9Sstevel@tonic-gate /*
17937c478bd9Sstevel@tonic-gate  * cte_qmove
17947c478bd9Sstevel@tonic-gate  *
17957c478bd9Sstevel@tonic-gate  * Move this listener to the specified event in the queue.
17967c478bd9Sstevel@tonic-gate  */
17977c478bd9Sstevel@tonic-gate static ct_kevent_t *
17987c478bd9Sstevel@tonic-gate cte_qmove(ct_equeue_t *q, ct_listener_t *l, ct_kevent_t *e)
17997c478bd9Sstevel@tonic-gate {
18007c478bd9Sstevel@tonic-gate 	ct_kevent_t *olde;
18017c478bd9Sstevel@tonic-gate 
18027c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&q->ctq_lock));
18037c478bd9Sstevel@tonic-gate 	ASSERT(l->ctl_equeue == q);
18047c478bd9Sstevel@tonic-gate 
18057c478bd9Sstevel@tonic-gate 	if ((olde = l->ctl_position) == NULL)
18067c478bd9Sstevel@tonic-gate 		list_remove(&q->ctq_tail, l);
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 	while (e != NULL && e->cte_nodes[q->ctq_listno].ctm_trimmed)
18097c478bd9Sstevel@tonic-gate 		e = list_next(&q->ctq_events, e);
18107c478bd9Sstevel@tonic-gate 
18117c478bd9Sstevel@tonic-gate 	if (e != NULL) {
18127c478bd9Sstevel@tonic-gate 		e->cte_nodes[q->ctq_listno].ctm_refs++;
18137c478bd9Sstevel@tonic-gate 		if (l->ctl_flags & CTLF_RELIABLE)
18147c478bd9Sstevel@tonic-gate 			e->cte_nodes[q->ctq_listno].ctm_nreliable++;
18157c478bd9Sstevel@tonic-gate 	} else {
18167c478bd9Sstevel@tonic-gate 		list_insert_tail(&q->ctq_tail, l);
18177c478bd9Sstevel@tonic-gate 	}
18187c478bd9Sstevel@tonic-gate 
18197c478bd9Sstevel@tonic-gate 	l->ctl_position = e;
18207c478bd9Sstevel@tonic-gate 	if (olde)
18217c478bd9Sstevel@tonic-gate 		cte_qrele(q, l, olde);
18227c478bd9Sstevel@tonic-gate 
18237c478bd9Sstevel@tonic-gate 	return (e);
18247c478bd9Sstevel@tonic-gate }
18257c478bd9Sstevel@tonic-gate 
18267c478bd9Sstevel@tonic-gate /*
18277c478bd9Sstevel@tonic-gate  * cte_checkcred
18287c478bd9Sstevel@tonic-gate  *
18297c478bd9Sstevel@tonic-gate  * Determines if the specified event's contract is owned by a process
18307c478bd9Sstevel@tonic-gate  * with the same effective uid as the specified credential.  Called
18317c478bd9Sstevel@tonic-gate  * after a failed call to contract_owned with locked set.  Because it
18327c478bd9Sstevel@tonic-gate  * drops the queue lock, its caller (cte_qreadable) needs to make sure
18337c478bd9Sstevel@tonic-gate  * we're still in the same place after we return.  Returns 1 on
18347c478bd9Sstevel@tonic-gate  * success.
18357c478bd9Sstevel@tonic-gate  */
18367c478bd9Sstevel@tonic-gate static int
18377c478bd9Sstevel@tonic-gate cte_checkcred(ct_equeue_t *q, ct_kevent_t *e, const cred_t *cr)
18387c478bd9Sstevel@tonic-gate {
18397c478bd9Sstevel@tonic-gate 	int result;
18407c478bd9Sstevel@tonic-gate 	contract_t *ct = e->cte_contract;
18417c478bd9Sstevel@tonic-gate 
18427c478bd9Sstevel@tonic-gate 	cte_hold(e);
18437c478bd9Sstevel@tonic-gate 	mutex_exit(&q->ctq_lock);
18447c478bd9Sstevel@tonic-gate 	result = curproc->p_zone->zone_uniqid == ct->ct_czuniqid &&
18457c478bd9Sstevel@tonic-gate 	    contract_checkcred(ct, cr);
18467c478bd9Sstevel@tonic-gate 	mutex_enter(&q->ctq_lock);
18477c478bd9Sstevel@tonic-gate 	cte_rele(e);
18487c478bd9Sstevel@tonic-gate 
18497c478bd9Sstevel@tonic-gate 	return (result);
18507c478bd9Sstevel@tonic-gate }
18517c478bd9Sstevel@tonic-gate 
18527c478bd9Sstevel@tonic-gate /*
18537c478bd9Sstevel@tonic-gate  * cte_qreadable
18547c478bd9Sstevel@tonic-gate  *
18557c478bd9Sstevel@tonic-gate  * Ensures that the listener is pointing to a valid event that the
18567c478bd9Sstevel@tonic-gate  * caller has the credentials to read.  Returns 0 if we can read the
18577c478bd9Sstevel@tonic-gate  * event we're pointing to.
18587c478bd9Sstevel@tonic-gate  */
18597c478bd9Sstevel@tonic-gate static int
18607c478bd9Sstevel@tonic-gate cte_qreadable(ct_equeue_t *q, ct_listener_t *l, const cred_t *cr,
18617c478bd9Sstevel@tonic-gate     uint64_t zuniqid, int crit)
18627c478bd9Sstevel@tonic-gate {
18637c478bd9Sstevel@tonic-gate 	ct_kevent_t *e, *next;
18647c478bd9Sstevel@tonic-gate 	contract_t *ct;
18657c478bd9Sstevel@tonic-gate 
18667c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&q->ctq_lock));
18677c478bd9Sstevel@tonic-gate 	ASSERT(l->ctl_equeue == q);
18687c478bd9Sstevel@tonic-gate 
18697c478bd9Sstevel@tonic-gate 	if (l->ctl_flags & CTLF_COPYOUT)
18707c478bd9Sstevel@tonic-gate 		return (1);
18717c478bd9Sstevel@tonic-gate 
18727c478bd9Sstevel@tonic-gate 	next = l->ctl_position;
18737c478bd9Sstevel@tonic-gate 	while (e = cte_qmove(q, l, next)) {
18747c478bd9Sstevel@tonic-gate 		ct = e->cte_contract;
18757c478bd9Sstevel@tonic-gate 		/*
18767c478bd9Sstevel@tonic-gate 		 * Check obvious things first.  If we are looking for a
18777c478bd9Sstevel@tonic-gate 		 * critical message, is this one?  If we aren't in the
18787c478bd9Sstevel@tonic-gate 		 * global zone, is this message meant for us?
18797c478bd9Sstevel@tonic-gate 		 */
18807c478bd9Sstevel@tonic-gate 		if ((crit && (e->cte_flags & (CTE_INFO | CTE_ACK))) ||
18817c478bd9Sstevel@tonic-gate 		    (cr != NULL && zuniqid != GLOBAL_ZONEUNIQID &&
18827c478bd9Sstevel@tonic-gate 		    zuniqid != contract_getzuniqid(ct))) {
18837c478bd9Sstevel@tonic-gate 
18847c478bd9Sstevel@tonic-gate 			next = list_next(&q->ctq_events, e);
18857c478bd9Sstevel@tonic-gate 
18867c478bd9Sstevel@tonic-gate 		/*
18877c478bd9Sstevel@tonic-gate 		 * Next, see if our effective uid equals that of owner
18887c478bd9Sstevel@tonic-gate 		 * or author of the contract.  Since we are holding the
18897c478bd9Sstevel@tonic-gate 		 * queue lock, contract_owned can't always check if we
18907c478bd9Sstevel@tonic-gate 		 * have the same effective uid as the contract's
18917c478bd9Sstevel@tonic-gate 		 * owner.  If it comes to that, it fails and we take
18927c478bd9Sstevel@tonic-gate 		 * the slow(er) path.
18937c478bd9Sstevel@tonic-gate 		 */
18947c478bd9Sstevel@tonic-gate 		} else if (cr != NULL && !contract_owned(ct, cr, B_TRUE)) {
18957c478bd9Sstevel@tonic-gate 
18967c478bd9Sstevel@tonic-gate 			/*
18977c478bd9Sstevel@tonic-gate 			 * At this point we either don't have any claim
18987c478bd9Sstevel@tonic-gate 			 * to this contract or we match the effective
18997c478bd9Sstevel@tonic-gate 			 * uid of the owner but couldn't tell.  We
19007c478bd9Sstevel@tonic-gate 			 * first test for a NULL holder so that events
19017c478bd9Sstevel@tonic-gate 			 * from orphans and inherited contracts avoid
19027c478bd9Sstevel@tonic-gate 			 * the penalty phase.
19037c478bd9Sstevel@tonic-gate 			 */
19047c478bd9Sstevel@tonic-gate 			if (e->cte_contract->ct_owner == NULL &&
19057c478bd9Sstevel@tonic-gate 			    !secpolicy_contract_observer_choice(cr))
19067c478bd9Sstevel@tonic-gate 				next = list_next(&q->ctq_events, e);
19077c478bd9Sstevel@tonic-gate 
19087c478bd9Sstevel@tonic-gate 			/*
19097c478bd9Sstevel@tonic-gate 			 * cte_checkcred will juggle locks to see if we
19107c478bd9Sstevel@tonic-gate 			 * have the same uid as the event's contract's
19117c478bd9Sstevel@tonic-gate 			 * current owner.  If it succeeds, we have to
19127c478bd9Sstevel@tonic-gate 			 * make sure we are in the same point in the
19137c478bd9Sstevel@tonic-gate 			 * queue.
19147c478bd9Sstevel@tonic-gate 			 */
19157c478bd9Sstevel@tonic-gate 			else if (cte_checkcred(q, e, cr) &&
19167c478bd9Sstevel@tonic-gate 			    l->ctl_position == e)
19177c478bd9Sstevel@tonic-gate 				break;
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate 			/*
19207c478bd9Sstevel@tonic-gate 			 * cte_checkcred failed; see if we're in the
19217c478bd9Sstevel@tonic-gate 			 * same place.
19227c478bd9Sstevel@tonic-gate 			 */
19237c478bd9Sstevel@tonic-gate 			else if (l->ctl_position == e)
19247c478bd9Sstevel@tonic-gate 				if (secpolicy_contract_observer_choice(cr))
19257c478bd9Sstevel@tonic-gate 					break;
19267c478bd9Sstevel@tonic-gate 				else
19277c478bd9Sstevel@tonic-gate 					next = list_next(&q->ctq_events, e);
19287c478bd9Sstevel@tonic-gate 
19297c478bd9Sstevel@tonic-gate 			/*
19307c478bd9Sstevel@tonic-gate 			 * cte_checkcred failed, and our position was
19317c478bd9Sstevel@tonic-gate 			 * changed.  Start from there.
19327c478bd9Sstevel@tonic-gate 			 */
19337c478bd9Sstevel@tonic-gate 			else
19347c478bd9Sstevel@tonic-gate 				next = l->ctl_position;
19357c478bd9Sstevel@tonic-gate 		} else {
19367c478bd9Sstevel@tonic-gate 			break;
19377c478bd9Sstevel@tonic-gate 		}
19387c478bd9Sstevel@tonic-gate 	}
19397c478bd9Sstevel@tonic-gate 
19407c478bd9Sstevel@tonic-gate 	/*
19417c478bd9Sstevel@tonic-gate 	 * We check for CTLF_COPYOUT again in case we dropped the queue
19427c478bd9Sstevel@tonic-gate 	 * lock in cte_checkcred.
19437c478bd9Sstevel@tonic-gate 	 */
19447c478bd9Sstevel@tonic-gate 	return ((l->ctl_flags & CTLF_COPYOUT) || (l->ctl_position == NULL));
19457c478bd9Sstevel@tonic-gate }
19467c478bd9Sstevel@tonic-gate 
19477c478bd9Sstevel@tonic-gate /*
19487c478bd9Sstevel@tonic-gate  * cte_qwakeup
19497c478bd9Sstevel@tonic-gate  *
19507c478bd9Sstevel@tonic-gate  * Wakes up any waiting listeners and points them at the specified event.
19517c478bd9Sstevel@tonic-gate  */
19527c478bd9Sstevel@tonic-gate static void
19537c478bd9Sstevel@tonic-gate cte_qwakeup(ct_equeue_t *q, ct_kevent_t *e)
19547c478bd9Sstevel@tonic-gate {
19557c478bd9Sstevel@tonic-gate 	ct_listener_t *l;
19567c478bd9Sstevel@tonic-gate 
19577c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&q->ctq_lock));
19587c478bd9Sstevel@tonic-gate 
19597c478bd9Sstevel@tonic-gate 	while (l = list_head(&q->ctq_tail)) {
19607c478bd9Sstevel@tonic-gate 		list_remove(&q->ctq_tail, l);
19617c478bd9Sstevel@tonic-gate 		e->cte_nodes[q->ctq_listno].ctm_refs++;
19627c478bd9Sstevel@tonic-gate 		if (l->ctl_flags & CTLF_RELIABLE)
19637c478bd9Sstevel@tonic-gate 			e->cte_nodes[q->ctq_listno].ctm_nreliable++;
19647c478bd9Sstevel@tonic-gate 		l->ctl_position = e;
19657c478bd9Sstevel@tonic-gate 		cv_signal(&l->ctl_cv);
19667c478bd9Sstevel@tonic-gate 		pollwakeup(&l->ctl_pollhead, POLLIN);
19677c478bd9Sstevel@tonic-gate 	}
19687c478bd9Sstevel@tonic-gate }
19697c478bd9Sstevel@tonic-gate 
19707c478bd9Sstevel@tonic-gate /*
19717c478bd9Sstevel@tonic-gate  * cte_copy
19727c478bd9Sstevel@tonic-gate  *
19737c478bd9Sstevel@tonic-gate  * Copies events from the specified contract event queue to the
19747c478bd9Sstevel@tonic-gate  * end of the specified process bundle queue.  Only called from
19757c478bd9Sstevel@tonic-gate  * contract_adopt.
19767c478bd9Sstevel@tonic-gate  *
19777c478bd9Sstevel@tonic-gate  * We copy to the end of the target queue instead of mixing the events
19787c478bd9Sstevel@tonic-gate  * in their proper order because otherwise the act of adopting a
19797c478bd9Sstevel@tonic-gate  * contract would require a process to reset all process bundle
19807c478bd9Sstevel@tonic-gate  * listeners it needed to see the new events.  This would, in turn,
19817c478bd9Sstevel@tonic-gate  * require the process to keep track of which preexisting events had
19827c478bd9Sstevel@tonic-gate  * already been processed.
19837c478bd9Sstevel@tonic-gate  */
19847c478bd9Sstevel@tonic-gate static void
19857c478bd9Sstevel@tonic-gate cte_copy(ct_equeue_t *q, ct_equeue_t *newq)
19867c478bd9Sstevel@tonic-gate {
19877c478bd9Sstevel@tonic-gate 	ct_kevent_t *e, *first = NULL;
19887c478bd9Sstevel@tonic-gate 
19897c478bd9Sstevel@tonic-gate 	ASSERT(q->ctq_listno == CTEL_CONTRACT);
19907c478bd9Sstevel@tonic-gate 	ASSERT(newq->ctq_listno == CTEL_PBUNDLE);
19917c478bd9Sstevel@tonic-gate 
19927c478bd9Sstevel@tonic-gate 	mutex_enter(&q->ctq_lock);
19937c478bd9Sstevel@tonic-gate 	mutex_enter(&newq->ctq_lock);
19947c478bd9Sstevel@tonic-gate 
19957c478bd9Sstevel@tonic-gate 	/*
19967c478bd9Sstevel@tonic-gate 	 * For now, only copy critical events.
19977c478bd9Sstevel@tonic-gate 	 */
19987c478bd9Sstevel@tonic-gate 	for (e = list_head(&q->ctq_events); e != NULL;
19997c478bd9Sstevel@tonic-gate 	    e = list_next(&q->ctq_events, e)) {
20007c478bd9Sstevel@tonic-gate 		if ((e->cte_flags & (CTE_INFO | CTE_ACK)) == 0) {
20017c478bd9Sstevel@tonic-gate 			if (first == NULL)
20027c478bd9Sstevel@tonic-gate 				first = e;
20037c478bd9Sstevel@tonic-gate 			list_insert_tail(&newq->ctq_events, e);
20047c478bd9Sstevel@tonic-gate 			cte_hold(e);
20057c478bd9Sstevel@tonic-gate 		}
20067c478bd9Sstevel@tonic-gate 	}
20077c478bd9Sstevel@tonic-gate 
20087c478bd9Sstevel@tonic-gate 	mutex_exit(&q->ctq_lock);
20097c478bd9Sstevel@tonic-gate 
20107c478bd9Sstevel@tonic-gate 	if (first)
20117c478bd9Sstevel@tonic-gate 		cte_qwakeup(newq, first);
20127c478bd9Sstevel@tonic-gate 
20137c478bd9Sstevel@tonic-gate 	mutex_exit(&newq->ctq_lock);
20147c478bd9Sstevel@tonic-gate }
20157c478bd9Sstevel@tonic-gate 
20167c478bd9Sstevel@tonic-gate /*
20177c478bd9Sstevel@tonic-gate  * cte_trim
20187c478bd9Sstevel@tonic-gate  *
20197c478bd9Sstevel@tonic-gate  * Trims unneeded events from an event queue.  Algorithm works as
20207c478bd9Sstevel@tonic-gate  * follows:
20217c478bd9Sstevel@tonic-gate  *
20227c478bd9Sstevel@tonic-gate  *   Removes all informative and acknowledged critical events until the
20237c478bd9Sstevel@tonic-gate  *   first referenced event is found.
20247c478bd9Sstevel@tonic-gate  *
20257c478bd9Sstevel@tonic-gate  *   If a contract is specified, removes all events (regardless of
20267c478bd9Sstevel@tonic-gate  *   acknowledgement) generated by that contract until the first event
20277c478bd9Sstevel@tonic-gate  *   referenced by a reliable listener is found.  Reference events are
20287c478bd9Sstevel@tonic-gate  *   removed by marking them "trimmed".  Such events will be removed
20297c478bd9Sstevel@tonic-gate  *   when the last reference is dropped and will be skipped by future
20307c478bd9Sstevel@tonic-gate  *   listeners.
20317c478bd9Sstevel@tonic-gate  *
20327c478bd9Sstevel@tonic-gate  * This is pretty basic.  Ideally this should remove from the middle of
20337c478bd9Sstevel@tonic-gate  * the list (i.e. beyond the first referenced event), and even
20347c478bd9Sstevel@tonic-gate  * referenced events.
20357c478bd9Sstevel@tonic-gate  */
20367c478bd9Sstevel@tonic-gate static void
20377c478bd9Sstevel@tonic-gate cte_trim(ct_equeue_t *q, contract_t *ct)
20387c478bd9Sstevel@tonic-gate {
20397c478bd9Sstevel@tonic-gate 	ct_kevent_t *e, *next;
20407c478bd9Sstevel@tonic-gate 	int flags, stopper;
20417c478bd9Sstevel@tonic-gate 	int start = 1;
20427c478bd9Sstevel@tonic-gate 
20437c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&q->ctq_lock));
20447c478bd9Sstevel@tonic-gate 
20457c478bd9Sstevel@tonic-gate 	for (e = list_head(&q->ctq_events); e != NULL; e = next) {
20467c478bd9Sstevel@tonic-gate 		next = list_next(&q->ctq_events, e);
20477c478bd9Sstevel@tonic-gate 		flags = e->cte_flags;
20487c478bd9Sstevel@tonic-gate 		stopper = (q->ctq_listno != CTEL_PBUNDLE) &&
20497c478bd9Sstevel@tonic-gate 		    (e->cte_nodes[q->ctq_listno].ctm_nreliable > 0);
20507c478bd9Sstevel@tonic-gate 		if (e->cte_nodes[q->ctq_listno].ctm_refs == 0) {
20517c478bd9Sstevel@tonic-gate 			if ((start && (flags & (CTE_INFO | CTE_ACK))) ||
20527c478bd9Sstevel@tonic-gate 			    (e->cte_contract == ct)) {
20537c478bd9Sstevel@tonic-gate 				/*
20547c478bd9Sstevel@tonic-gate 				 * Toss informative and ACKed critical messages.
20557c478bd9Sstevel@tonic-gate 				 */
20567c478bd9Sstevel@tonic-gate 				list_remove(&q->ctq_events, e);
20577c478bd9Sstevel@tonic-gate 				cte_rele(e);
20587c478bd9Sstevel@tonic-gate 			}
20597c478bd9Sstevel@tonic-gate 		} else if ((e->cte_contract == ct) && !stopper) {
20607c478bd9Sstevel@tonic-gate 			ASSERT(q->ctq_nlisteners != 0);
20617c478bd9Sstevel@tonic-gate 			e->cte_nodes[q->ctq_listno].ctm_trimmed = 1;
20627c478bd9Sstevel@tonic-gate 		} else if (ct && !stopper) {
20637c478bd9Sstevel@tonic-gate 			start = 0;
20647c478bd9Sstevel@tonic-gate 		} else {
20657c478bd9Sstevel@tonic-gate 			/*
20667c478bd9Sstevel@tonic-gate 			 * Don't free messages past the first reader.
20677c478bd9Sstevel@tonic-gate 			 */
20687c478bd9Sstevel@tonic-gate 			break;
20697c478bd9Sstevel@tonic-gate 		}
20707c478bd9Sstevel@tonic-gate 	}
20717c478bd9Sstevel@tonic-gate }
20727c478bd9Sstevel@tonic-gate 
20737c478bd9Sstevel@tonic-gate /*
20747c478bd9Sstevel@tonic-gate  * cte_queue_drain
20757c478bd9Sstevel@tonic-gate  *
20767c478bd9Sstevel@tonic-gate  * Drain all events from the specified queue, and mark it dead.  If
20777c478bd9Sstevel@tonic-gate  * "ack" is set, acknowledge any critical events we find along the
20787c478bd9Sstevel@tonic-gate  * way.
20797c478bd9Sstevel@tonic-gate  */
20807c478bd9Sstevel@tonic-gate static void
20817c478bd9Sstevel@tonic-gate cte_queue_drain(ct_equeue_t *q, int ack)
20827c478bd9Sstevel@tonic-gate {
20837c478bd9Sstevel@tonic-gate 	ct_kevent_t *e, *next;
20847c478bd9Sstevel@tonic-gate 	ct_listener_t *l;
20857c478bd9Sstevel@tonic-gate 
20867c478bd9Sstevel@tonic-gate 	mutex_enter(&q->ctq_lock);
20877c478bd9Sstevel@tonic-gate 
20887c478bd9Sstevel@tonic-gate 	for (e = list_head(&q->ctq_events); e != NULL; e = next) {
20897c478bd9Sstevel@tonic-gate 		next = list_next(&q->ctq_events, e);
20907c478bd9Sstevel@tonic-gate 		if (ack && ((e->cte_flags & (CTE_INFO | CTE_ACK)) == 0)) {
20917c478bd9Sstevel@tonic-gate 			/*
20927c478bd9Sstevel@tonic-gate 			 * Make sure critical messages are eventually
20937c478bd9Sstevel@tonic-gate 			 * removed from the bundle queues.
20947c478bd9Sstevel@tonic-gate 			 */
20957c478bd9Sstevel@tonic-gate 			mutex_enter(&e->cte_lock);
20967c478bd9Sstevel@tonic-gate 			e->cte_flags |= CTE_ACK;
20977c478bd9Sstevel@tonic-gate 			mutex_exit(&e->cte_lock);
20987c478bd9Sstevel@tonic-gate 			ASSERT(MUTEX_HELD(&e->cte_contract->ct_lock));
20997c478bd9Sstevel@tonic-gate 			e->cte_contract->ct_evcnt--;
21007c478bd9Sstevel@tonic-gate 		}
21017c478bd9Sstevel@tonic-gate 		list_remove(&q->ctq_events, e);
21027c478bd9Sstevel@tonic-gate 		e->cte_nodes[q->ctq_listno].ctm_refs = 0;
21037c478bd9Sstevel@tonic-gate 		e->cte_nodes[q->ctq_listno].ctm_nreliable = 0;
21047c478bd9Sstevel@tonic-gate 		e->cte_nodes[q->ctq_listno].ctm_trimmed = 0;
21057c478bd9Sstevel@tonic-gate 		cte_rele(e);
21067c478bd9Sstevel@tonic-gate 	}
21077c478bd9Sstevel@tonic-gate 
21087c478bd9Sstevel@tonic-gate 	/*
21097c478bd9Sstevel@tonic-gate 	 * This is necessary only because of CTEL_PBUNDLE listeners;
21107c478bd9Sstevel@tonic-gate 	 * the events they point to can move from one pbundle to
21117c478bd9Sstevel@tonic-gate 	 * another.  Fortunately, this only happens if the contract is
21127c478bd9Sstevel@tonic-gate 	 * inherited, which (in turn) only happens if the process
21137c478bd9Sstevel@tonic-gate 	 * exits, which means it's an all-or-nothing deal.  If this
21147c478bd9Sstevel@tonic-gate 	 * wasn't the case, we would instead need to keep track of
21157c478bd9Sstevel@tonic-gate 	 * listeners on a per-event basis, not just a per-queue basis.
21167c478bd9Sstevel@tonic-gate 	 * This would have the side benefit of letting us clean up
21177c478bd9Sstevel@tonic-gate 	 * trimmed events sooner (i.e. immediately), but would
21187c478bd9Sstevel@tonic-gate 	 * unfortunately make events even bigger than they already
21197c478bd9Sstevel@tonic-gate 	 * are.
21207c478bd9Sstevel@tonic-gate 	 */
21217c478bd9Sstevel@tonic-gate 	for (l = list_head(&q->ctq_listeners); l;
21227c478bd9Sstevel@tonic-gate 	    l = list_next(&q->ctq_listeners, l)) {
21237c478bd9Sstevel@tonic-gate 		l->ctl_flags |= CTLF_DEAD;
21247c478bd9Sstevel@tonic-gate 		if (l->ctl_position) {
21257c478bd9Sstevel@tonic-gate 			l->ctl_position = NULL;
21267c478bd9Sstevel@tonic-gate 			list_insert_tail(&q->ctq_tail, l);
21277c478bd9Sstevel@tonic-gate 		}
21287c478bd9Sstevel@tonic-gate 		cv_broadcast(&l->ctl_cv);
21297c478bd9Sstevel@tonic-gate 	}
21307c478bd9Sstevel@tonic-gate 
21317c478bd9Sstevel@tonic-gate 	/*
21327c478bd9Sstevel@tonic-gate 	 * Disallow events.
21337c478bd9Sstevel@tonic-gate 	 */
21347c478bd9Sstevel@tonic-gate 	q->ctq_flags |= CTQ_DEAD;
21357c478bd9Sstevel@tonic-gate 
21367c478bd9Sstevel@tonic-gate 	/*
21377c478bd9Sstevel@tonic-gate 	 * If we represent the last reference to a reference counted
21387c478bd9Sstevel@tonic-gate 	 * process bundle queue, free it.
21397c478bd9Sstevel@tonic-gate 	 */
21407c478bd9Sstevel@tonic-gate 	if ((q->ctq_flags & CTQ_REFFED) && (q->ctq_nlisteners == 0))
21417c478bd9Sstevel@tonic-gate 		cte_queue_destroy(q);
21427c478bd9Sstevel@tonic-gate 	else
21437c478bd9Sstevel@tonic-gate 		mutex_exit(&q->ctq_lock);
21447c478bd9Sstevel@tonic-gate }
21457c478bd9Sstevel@tonic-gate 
21467c478bd9Sstevel@tonic-gate /*
21477c478bd9Sstevel@tonic-gate  * cte_publish
21487c478bd9Sstevel@tonic-gate  *
21497c478bd9Sstevel@tonic-gate  * Publishes an event to a specific queue.  Only called by
21507c478bd9Sstevel@tonic-gate  * cte_publish_all.
21517c478bd9Sstevel@tonic-gate  */
21527c478bd9Sstevel@tonic-gate static void
21537c478bd9Sstevel@tonic-gate cte_publish(ct_equeue_t *q, ct_kevent_t *e, timespec_t *tsp)
21547c478bd9Sstevel@tonic-gate {
21557c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&q->ctq_lock));
21567c478bd9Sstevel@tonic-gate 
21577c478bd9Sstevel@tonic-gate 	q->ctq_atime = *tsp;
21587c478bd9Sstevel@tonic-gate 
21597c478bd9Sstevel@tonic-gate 	/*
21607c478bd9Sstevel@tonic-gate 	 * Don't publish if the event is informative and there aren't
21617c478bd9Sstevel@tonic-gate 	 * any listeners, or if the queue has been shut down.
21627c478bd9Sstevel@tonic-gate 	 */
21637c478bd9Sstevel@tonic-gate 	if (((q->ctq_nlisteners == 0) && (e->cte_flags & (CTE_INFO|CTE_ACK))) ||
21647c478bd9Sstevel@tonic-gate 	    (q->ctq_flags & CTQ_DEAD)) {
21657c478bd9Sstevel@tonic-gate 		mutex_exit(&q->ctq_lock);
21667c478bd9Sstevel@tonic-gate 		cte_rele(e);
21677c478bd9Sstevel@tonic-gate 		return;
21687c478bd9Sstevel@tonic-gate 	}
21697c478bd9Sstevel@tonic-gate 
21707c478bd9Sstevel@tonic-gate 	/*
21717c478bd9Sstevel@tonic-gate 	 * Enqueue event
21727c478bd9Sstevel@tonic-gate 	 */
21737c478bd9Sstevel@tonic-gate 	list_insert_tail(&q->ctq_events, e);
21747c478bd9Sstevel@tonic-gate 
21757c478bd9Sstevel@tonic-gate 	/*
21767c478bd9Sstevel@tonic-gate 	 * Check for waiting listeners
21777c478bd9Sstevel@tonic-gate 	 */
21787c478bd9Sstevel@tonic-gate 	cte_qwakeup(q, e);
21797c478bd9Sstevel@tonic-gate 
21807c478bd9Sstevel@tonic-gate 	/*
21817c478bd9Sstevel@tonic-gate 	 * Trim unnecessary events from the queue.
21827c478bd9Sstevel@tonic-gate 	 */
21837c478bd9Sstevel@tonic-gate 	cte_trim(q, NULL);
21847c478bd9Sstevel@tonic-gate 	mutex_exit(&q->ctq_lock);
21857c478bd9Sstevel@tonic-gate }
21867c478bd9Sstevel@tonic-gate 
21877c478bd9Sstevel@tonic-gate /*
21887c478bd9Sstevel@tonic-gate  * cte_publish_all
21897c478bd9Sstevel@tonic-gate  *
21907c478bd9Sstevel@tonic-gate  * Publish an event to all necessary event queues.  The event, e, must
21917c478bd9Sstevel@tonic-gate  * be zallocated by the caller, and the event's flags and type must be
21927c478bd9Sstevel@tonic-gate  * set.  The rest of the event's fields are initialized here.
21937c478bd9Sstevel@tonic-gate  */
219425e8c5aaSvikram uint64_t
21957c478bd9Sstevel@tonic-gate cte_publish_all(contract_t *ct, ct_kevent_t *e, nvlist_t *data, nvlist_t *gdata)
21967c478bd9Sstevel@tonic-gate {
21977c478bd9Sstevel@tonic-gate 	ct_equeue_t *q;
21987c478bd9Sstevel@tonic-gate 	timespec_t ts;
219925e8c5aaSvikram 	uint64_t evid;
220025e8c5aaSvikram 	ct_kevent_t *negev;
220125e8c5aaSvikram 	int negend;
22027c478bd9Sstevel@tonic-gate 
22037c478bd9Sstevel@tonic-gate 	e->cte_contract = ct;
22047c478bd9Sstevel@tonic-gate 	e->cte_data = data;
22057c478bd9Sstevel@tonic-gate 	e->cte_gdata = gdata;
22067c478bd9Sstevel@tonic-gate 	e->cte_refs = 3;
220725e8c5aaSvikram 	evid = e->cte_id = atomic_add_64_nv(&ct->ct_type->ct_type_evid, 1);
22087c478bd9Sstevel@tonic-gate 	contract_hold(ct);
22097c478bd9Sstevel@tonic-gate 
221025e8c5aaSvikram 	/*
221125e8c5aaSvikram 	 * For a negotiation event we set the ct->ct_nevent field of the
221225e8c5aaSvikram 	 * contract for the duration of the negotiation
221325e8c5aaSvikram 	 */
221425e8c5aaSvikram 	negend = 0;
221525e8c5aaSvikram 	if (e->cte_flags & CTE_NEG) {
221625e8c5aaSvikram 		cte_hold(e);
221725e8c5aaSvikram 		ct->ct_nevent = e;
221825e8c5aaSvikram 	} else if (e->cte_type == CT_EV_NEGEND) {
221925e8c5aaSvikram 		negend = 1;
222025e8c5aaSvikram 	}
222125e8c5aaSvikram 
22227c478bd9Sstevel@tonic-gate 	gethrestime(&ts);
22237c478bd9Sstevel@tonic-gate 
22247c478bd9Sstevel@tonic-gate 	/*
22257c478bd9Sstevel@tonic-gate 	 * ct_evtlock simply (and only) ensures that two events sent
22267c478bd9Sstevel@tonic-gate 	 * from the same contract are delivered to all queues in the
22277c478bd9Sstevel@tonic-gate 	 * same order.
22287c478bd9Sstevel@tonic-gate 	 */
22297c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_evtlock);
22307c478bd9Sstevel@tonic-gate 
22317c478bd9Sstevel@tonic-gate 	/*
22327c478bd9Sstevel@tonic-gate 	 * CTEL_CONTRACT - First deliver to the contract queue, acking
22337c478bd9Sstevel@tonic-gate 	 * the event if the contract has been orphaned.
22347c478bd9Sstevel@tonic-gate 	 */
22357c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
22367c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_events.ctq_lock);
22377c478bd9Sstevel@tonic-gate 	if ((e->cte_flags & CTE_INFO) == 0) {
22387c478bd9Sstevel@tonic-gate 		if (ct->ct_state >= CTS_ORPHAN)
22397c478bd9Sstevel@tonic-gate 			e->cte_flags |= CTE_ACK;
22407c478bd9Sstevel@tonic-gate 		else
22417c478bd9Sstevel@tonic-gate 			ct->ct_evcnt++;
22427c478bd9Sstevel@tonic-gate 	}
22437c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
22447c478bd9Sstevel@tonic-gate 	cte_publish(&ct->ct_events, e, &ts);
22457c478bd9Sstevel@tonic-gate 
22467c478bd9Sstevel@tonic-gate 	/*
22477c478bd9Sstevel@tonic-gate 	 * CTEL_BUNDLE - Next deliver to the contract type's bundle
22487c478bd9Sstevel@tonic-gate 	 * queue.
22497c478bd9Sstevel@tonic-gate 	 */
22507c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_type->ct_type_events.ctq_lock);
22517c478bd9Sstevel@tonic-gate 	cte_publish(&ct->ct_type->ct_type_events, e, &ts);
22527c478bd9Sstevel@tonic-gate 
22537c478bd9Sstevel@tonic-gate 	/*
22547c478bd9Sstevel@tonic-gate 	 * CTEL_PBUNDLE - Finally, if the contract has an owner,
22557c478bd9Sstevel@tonic-gate 	 * deliver to the owner's process bundle queue.
22567c478bd9Sstevel@tonic-gate 	 */
22577c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
22587c478bd9Sstevel@tonic-gate 	if (ct->ct_owner) {
22597c478bd9Sstevel@tonic-gate 		/*
22607c478bd9Sstevel@tonic-gate 		 * proc_exit doesn't free event queues until it has
22617c478bd9Sstevel@tonic-gate 		 * abandoned all contracts.
22627c478bd9Sstevel@tonic-gate 		 */
22637c478bd9Sstevel@tonic-gate 		ASSERT(ct->ct_owner->p_ct_equeue);
22647c478bd9Sstevel@tonic-gate 		ASSERT(ct->ct_owner->p_ct_equeue[ct->ct_type->ct_type_index]);
22657c478bd9Sstevel@tonic-gate 		q = ct->ct_owner->p_ct_equeue[ct->ct_type->ct_type_index];
22667c478bd9Sstevel@tonic-gate 		mutex_enter(&q->ctq_lock);
22677c478bd9Sstevel@tonic-gate 		mutex_exit(&ct->ct_lock);
22687c478bd9Sstevel@tonic-gate 		cte_publish(q, e, &ts);
22697c478bd9Sstevel@tonic-gate 	} else {
22707c478bd9Sstevel@tonic-gate 		mutex_exit(&ct->ct_lock);
22717c478bd9Sstevel@tonic-gate 		cte_rele(e);
22727c478bd9Sstevel@tonic-gate 	}
22737c478bd9Sstevel@tonic-gate 
227425e8c5aaSvikram 	if (negend) {
227525e8c5aaSvikram 		mutex_enter(&ct->ct_lock);
227625e8c5aaSvikram 		negev = ct->ct_nevent;
227725e8c5aaSvikram 		ct->ct_nevent = NULL;
227825e8c5aaSvikram 		cte_rele(negev);
227925e8c5aaSvikram 		mutex_exit(&ct->ct_lock);
228025e8c5aaSvikram 	}
228125e8c5aaSvikram 
22827c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_evtlock);
228325e8c5aaSvikram 
228425e8c5aaSvikram 	return (evid);
22857c478bd9Sstevel@tonic-gate }
22867c478bd9Sstevel@tonic-gate 
22877c478bd9Sstevel@tonic-gate /*
22887c478bd9Sstevel@tonic-gate  * cte_add_listener
22897c478bd9Sstevel@tonic-gate  *
22907c478bd9Sstevel@tonic-gate  * Add a new listener to an event queue.
22917c478bd9Sstevel@tonic-gate  */
22927c478bd9Sstevel@tonic-gate void
22937c478bd9Sstevel@tonic-gate cte_add_listener(ct_equeue_t *q, ct_listener_t *l)
22947c478bd9Sstevel@tonic-gate {
22957c478bd9Sstevel@tonic-gate 	cv_init(&l->ctl_cv, NULL, CV_DEFAULT, NULL);
22967c478bd9Sstevel@tonic-gate 	l->ctl_equeue = q;
22977c478bd9Sstevel@tonic-gate 	l->ctl_position = NULL;
22987c478bd9Sstevel@tonic-gate 	l->ctl_flags = 0;
22997c478bd9Sstevel@tonic-gate 
23007c478bd9Sstevel@tonic-gate 	mutex_enter(&q->ctq_lock);
23017c478bd9Sstevel@tonic-gate 	list_insert_head(&q->ctq_tail, l);
23027c478bd9Sstevel@tonic-gate 	list_insert_head(&q->ctq_listeners, l);
23037c478bd9Sstevel@tonic-gate 	q->ctq_nlisteners++;
23047c478bd9Sstevel@tonic-gate 	mutex_exit(&q->ctq_lock);
23057c478bd9Sstevel@tonic-gate }
23067c478bd9Sstevel@tonic-gate 
23077c478bd9Sstevel@tonic-gate /*
23087c478bd9Sstevel@tonic-gate  * cte_remove_listener
23097c478bd9Sstevel@tonic-gate  *
23107c478bd9Sstevel@tonic-gate  * Remove a listener from an event queue.  No other queue activities
23117c478bd9Sstevel@tonic-gate  * (e.g. cte_get event) may be in progress at this endpoint when this
23127c478bd9Sstevel@tonic-gate  * is called.
23137c478bd9Sstevel@tonic-gate  */
23147c478bd9Sstevel@tonic-gate void
23157c478bd9Sstevel@tonic-gate cte_remove_listener(ct_listener_t *l)
23167c478bd9Sstevel@tonic-gate {
23177c478bd9Sstevel@tonic-gate 	ct_equeue_t *q = l->ctl_equeue;
23187c478bd9Sstevel@tonic-gate 	ct_kevent_t *e;
23197c478bd9Sstevel@tonic-gate 
23207c478bd9Sstevel@tonic-gate 	mutex_enter(&q->ctq_lock);
23217c478bd9Sstevel@tonic-gate 
23227c478bd9Sstevel@tonic-gate 	ASSERT((l->ctl_flags & (CTLF_COPYOUT|CTLF_RESET)) == 0);
23237c478bd9Sstevel@tonic-gate 
23247c478bd9Sstevel@tonic-gate 	if ((e = l->ctl_position) != NULL)
23257c478bd9Sstevel@tonic-gate 		cte_qrele(q, l, e);
23267c478bd9Sstevel@tonic-gate 	else
23277c478bd9Sstevel@tonic-gate 		list_remove(&q->ctq_tail, l);
23287c478bd9Sstevel@tonic-gate 	l->ctl_position = NULL;
23297c478bd9Sstevel@tonic-gate 
23307c478bd9Sstevel@tonic-gate 	q->ctq_nlisteners--;
23317c478bd9Sstevel@tonic-gate 	list_remove(&q->ctq_listeners, l);
23327c478bd9Sstevel@tonic-gate 
23337c478bd9Sstevel@tonic-gate 	if (l->ctl_flags & CTLF_RELIABLE)
23347c478bd9Sstevel@tonic-gate 		q->ctq_nreliable--;
23357c478bd9Sstevel@tonic-gate 
23367c478bd9Sstevel@tonic-gate 	/*
23377c478bd9Sstevel@tonic-gate 	 * If we are a the last listener of a dead reference counted
23387c478bd9Sstevel@tonic-gate 	 * queue (i.e. a process bundle) we free it.  Otherwise we just
23397c478bd9Sstevel@tonic-gate 	 * trim any events which may have been kept around for our
23407c478bd9Sstevel@tonic-gate 	 * benefit.
23417c478bd9Sstevel@tonic-gate 	 */
23427c478bd9Sstevel@tonic-gate 	if ((q->ctq_flags & CTQ_REFFED) && (q->ctq_flags & CTQ_DEAD) &&
23437c478bd9Sstevel@tonic-gate 	    (q->ctq_nlisteners == 0)) {
23447c478bd9Sstevel@tonic-gate 		cte_queue_destroy(q);
23457c478bd9Sstevel@tonic-gate 	} else {
23467c478bd9Sstevel@tonic-gate 		cte_trim(q, NULL);
23477c478bd9Sstevel@tonic-gate 		mutex_exit(&q->ctq_lock);
23487c478bd9Sstevel@tonic-gate 	}
23497c478bd9Sstevel@tonic-gate }
23507c478bd9Sstevel@tonic-gate 
23517c478bd9Sstevel@tonic-gate /*
23527c478bd9Sstevel@tonic-gate  * cte_reset_listener
23537c478bd9Sstevel@tonic-gate  *
23547c478bd9Sstevel@tonic-gate  * Moves a listener's queue pointer to the beginning of the queue.
23557c478bd9Sstevel@tonic-gate  */
23567c478bd9Sstevel@tonic-gate void
23577c478bd9Sstevel@tonic-gate cte_reset_listener(ct_listener_t *l)
23587c478bd9Sstevel@tonic-gate {
23597c478bd9Sstevel@tonic-gate 	ct_equeue_t *q = l->ctl_equeue;
23607c478bd9Sstevel@tonic-gate 
23617c478bd9Sstevel@tonic-gate 	mutex_enter(&q->ctq_lock);
23627c478bd9Sstevel@tonic-gate 
23637c478bd9Sstevel@tonic-gate 	/*
23647c478bd9Sstevel@tonic-gate 	 * We allow an asynchronous reset because it doesn't make a
23657c478bd9Sstevel@tonic-gate 	 * whole lot of sense to make reset block or fail.  We already
23667c478bd9Sstevel@tonic-gate 	 * have most of the mechanism needed thanks to queue trimming,
23677c478bd9Sstevel@tonic-gate 	 * so implementing it isn't a big deal.
23687c478bd9Sstevel@tonic-gate 	 */
23697c478bd9Sstevel@tonic-gate 	if (l->ctl_flags & CTLF_COPYOUT)
23707c478bd9Sstevel@tonic-gate 		l->ctl_flags |= CTLF_RESET;
23717c478bd9Sstevel@tonic-gate 
23727c478bd9Sstevel@tonic-gate 	(void) cte_qmove(q, l, list_head(&q->ctq_events));
23737c478bd9Sstevel@tonic-gate 
23747c478bd9Sstevel@tonic-gate 	/*
23757c478bd9Sstevel@tonic-gate 	 * Inform blocked readers.
23767c478bd9Sstevel@tonic-gate 	 */
23777c478bd9Sstevel@tonic-gate 	cv_broadcast(&l->ctl_cv);
23787c478bd9Sstevel@tonic-gate 	pollwakeup(&l->ctl_pollhead, POLLIN);
23797c478bd9Sstevel@tonic-gate 	mutex_exit(&q->ctq_lock);
23807c478bd9Sstevel@tonic-gate }
23817c478bd9Sstevel@tonic-gate 
23827c478bd9Sstevel@tonic-gate /*
23837c478bd9Sstevel@tonic-gate  * cte_next_event
23847c478bd9Sstevel@tonic-gate  *
23857c478bd9Sstevel@tonic-gate  * Moves the event pointer for the specified listener to the next event
23867c478bd9Sstevel@tonic-gate  * on the queue.  To avoid races, this movement only occurs if the
23877c478bd9Sstevel@tonic-gate  * specified event id matches that of the current event.  This is used
23887c478bd9Sstevel@tonic-gate  * primarily to skip events that have been read but whose extended data
23897c478bd9Sstevel@tonic-gate  * haven't been copied out.
23907c478bd9Sstevel@tonic-gate  */
23917c478bd9Sstevel@tonic-gate int
23927c478bd9Sstevel@tonic-gate cte_next_event(ct_listener_t *l, uint64_t id)
23937c478bd9Sstevel@tonic-gate {
23947c478bd9Sstevel@tonic-gate 	ct_equeue_t *q = l->ctl_equeue;
23957c478bd9Sstevel@tonic-gate 	ct_kevent_t *old;
23967c478bd9Sstevel@tonic-gate 
23977c478bd9Sstevel@tonic-gate 	mutex_enter(&q->ctq_lock);
23987c478bd9Sstevel@tonic-gate 
23997c478bd9Sstevel@tonic-gate 	if (l->ctl_flags & CTLF_COPYOUT)
24007c478bd9Sstevel@tonic-gate 		l->ctl_flags |= CTLF_RESET;
24017c478bd9Sstevel@tonic-gate 
24027c478bd9Sstevel@tonic-gate 	if (((old = l->ctl_position) != NULL) && (old->cte_id == id))
24037c478bd9Sstevel@tonic-gate 		(void) cte_qmove(q, l, list_next(&q->ctq_events, old));
24047c478bd9Sstevel@tonic-gate 
24057c478bd9Sstevel@tonic-gate 	mutex_exit(&q->ctq_lock);
24067c478bd9Sstevel@tonic-gate 
24077c478bd9Sstevel@tonic-gate 	return (0);
24087c478bd9Sstevel@tonic-gate }
24097c478bd9Sstevel@tonic-gate 
24107c478bd9Sstevel@tonic-gate /*
24117c478bd9Sstevel@tonic-gate  * cte_get_event
24127c478bd9Sstevel@tonic-gate  *
24137c478bd9Sstevel@tonic-gate  * Reads an event from an event endpoint.  If "nonblock" is clear, we
24147c478bd9Sstevel@tonic-gate  * block until a suitable event is ready.  If "crit" is set, we only
24157c478bd9Sstevel@tonic-gate  * read critical events.  Note that while "cr" is the caller's cred,
24167c478bd9Sstevel@tonic-gate  * "zuniqid" is the unique id of the zone the calling contract
24177c478bd9Sstevel@tonic-gate  * filesystem was mounted in.
24187c478bd9Sstevel@tonic-gate  */
24197c478bd9Sstevel@tonic-gate int
24207c478bd9Sstevel@tonic-gate cte_get_event(ct_listener_t *l, int nonblock, void *uaddr, const cred_t *cr,
24217c478bd9Sstevel@tonic-gate     uint64_t zuniqid, int crit)
24227c478bd9Sstevel@tonic-gate {
24237c478bd9Sstevel@tonic-gate 	ct_equeue_t *q = l->ctl_equeue;
24247c478bd9Sstevel@tonic-gate 	ct_kevent_t *temp;
24257c478bd9Sstevel@tonic-gate 	int result = 0;
24267c478bd9Sstevel@tonic-gate 	int partial = 0;
24277c478bd9Sstevel@tonic-gate 	size_t size, gsize, len;
24287c478bd9Sstevel@tonic-gate 	model_t mdl = get_udatamodel();
24297c478bd9Sstevel@tonic-gate 	STRUCT_DECL(ct_event, ev);
24307c478bd9Sstevel@tonic-gate 	STRUCT_INIT(ev, mdl);
24317c478bd9Sstevel@tonic-gate 
24327c478bd9Sstevel@tonic-gate 	/*
24337c478bd9Sstevel@tonic-gate 	 * cte_qreadable checks for CTLF_COPYOUT as well as ensures
24347c478bd9Sstevel@tonic-gate 	 * that there exists, and we are pointing to, an appropriate
24357c478bd9Sstevel@tonic-gate 	 * event.  It may temporarily drop ctq_lock, but that doesn't
24367c478bd9Sstevel@tonic-gate 	 * really matter to us.
24377c478bd9Sstevel@tonic-gate 	 */
24387c478bd9Sstevel@tonic-gate 	mutex_enter(&q->ctq_lock);
24397c478bd9Sstevel@tonic-gate 	while (cte_qreadable(q, l, cr, zuniqid, crit)) {
24407c478bd9Sstevel@tonic-gate 		if (nonblock) {
24417c478bd9Sstevel@tonic-gate 			result = EAGAIN;
24427c478bd9Sstevel@tonic-gate 			goto error;
24437c478bd9Sstevel@tonic-gate 		}
24447c478bd9Sstevel@tonic-gate 		if (q->ctq_flags & CTQ_DEAD) {
24457c478bd9Sstevel@tonic-gate 			result = EIDRM;
24467c478bd9Sstevel@tonic-gate 			goto error;
24477c478bd9Sstevel@tonic-gate 		}
24487c478bd9Sstevel@tonic-gate 		result = cv_wait_sig(&l->ctl_cv, &q->ctq_lock);
24497c478bd9Sstevel@tonic-gate 		if (result == 0) {
24507c478bd9Sstevel@tonic-gate 			result = EINTR;
24517c478bd9Sstevel@tonic-gate 			goto error;
24527c478bd9Sstevel@tonic-gate 		}
24537c478bd9Sstevel@tonic-gate 	}
24547c478bd9Sstevel@tonic-gate 	temp = l->ctl_position;
24557c478bd9Sstevel@tonic-gate 	cte_hold(temp);
24567c478bd9Sstevel@tonic-gate 	l->ctl_flags |= CTLF_COPYOUT;
24577c478bd9Sstevel@tonic-gate 	mutex_exit(&q->ctq_lock);
24587c478bd9Sstevel@tonic-gate 
24597c478bd9Sstevel@tonic-gate 	/*
24607c478bd9Sstevel@tonic-gate 	 * We now have an event.  Copy in the user event structure to
24617c478bd9Sstevel@tonic-gate 	 * see how much space we have to work with.
24627c478bd9Sstevel@tonic-gate 	 */
24637c478bd9Sstevel@tonic-gate 	result = copyin(uaddr, STRUCT_BUF(ev), STRUCT_SIZE(ev));
24647c478bd9Sstevel@tonic-gate 	if (result)
24657c478bd9Sstevel@tonic-gate 		goto copyerr;
24667c478bd9Sstevel@tonic-gate 
24677c478bd9Sstevel@tonic-gate 	/*
24687c478bd9Sstevel@tonic-gate 	 * Determine what data we have and what the user should be
24697c478bd9Sstevel@tonic-gate 	 * allowed to see.
24707c478bd9Sstevel@tonic-gate 	 */
24717c478bd9Sstevel@tonic-gate 	size = gsize = 0;
24727c478bd9Sstevel@tonic-gate 	if (temp->cte_data) {
24737c478bd9Sstevel@tonic-gate 		VERIFY(nvlist_size(temp->cte_data, &size,
24747c478bd9Sstevel@tonic-gate 		    NV_ENCODE_NATIVE) == 0);
24757c478bd9Sstevel@tonic-gate 		ASSERT(size != 0);
24767c478bd9Sstevel@tonic-gate 	}
24777c478bd9Sstevel@tonic-gate 	if (zuniqid == GLOBAL_ZONEUNIQID && temp->cte_gdata) {
24787c478bd9Sstevel@tonic-gate 		VERIFY(nvlist_size(temp->cte_gdata, &gsize,
24797c478bd9Sstevel@tonic-gate 		    NV_ENCODE_NATIVE) == 0);
24807c478bd9Sstevel@tonic-gate 		ASSERT(gsize != 0);
24817c478bd9Sstevel@tonic-gate 	}
24827c478bd9Sstevel@tonic-gate 
24837c478bd9Sstevel@tonic-gate 	/*
24847c478bd9Sstevel@tonic-gate 	 * If we have enough space, copy out the extended event data.
24857c478bd9Sstevel@tonic-gate 	 */
24867c478bd9Sstevel@tonic-gate 	len = size + gsize;
24877c478bd9Sstevel@tonic-gate 	if (len) {
24887c478bd9Sstevel@tonic-gate 		if (STRUCT_FGET(ev, ctev_nbytes) >= len) {
24897c478bd9Sstevel@tonic-gate 			char *buf = kmem_alloc(len, KM_SLEEP);
24907c478bd9Sstevel@tonic-gate 
24917c478bd9Sstevel@tonic-gate 			if (size)
24927c478bd9Sstevel@tonic-gate 				VERIFY(nvlist_pack(temp->cte_data, &buf, &size,
24937c478bd9Sstevel@tonic-gate 				    NV_ENCODE_NATIVE, KM_SLEEP) == 0);
24947c478bd9Sstevel@tonic-gate 			if (gsize) {
24957c478bd9Sstevel@tonic-gate 				char *tmp = buf + size;
24967c478bd9Sstevel@tonic-gate 
24977c478bd9Sstevel@tonic-gate 				VERIFY(nvlist_pack(temp->cte_gdata, &tmp,
24987c478bd9Sstevel@tonic-gate 				    &gsize, NV_ENCODE_NATIVE, KM_SLEEP) == 0);
24997c478bd9Sstevel@tonic-gate 			}
25007c478bd9Sstevel@tonic-gate 
25017c478bd9Sstevel@tonic-gate 			/* This shouldn't have changed */
25027c478bd9Sstevel@tonic-gate 			ASSERT(size + gsize == len);
25037c478bd9Sstevel@tonic-gate 			result = copyout(buf, STRUCT_FGETP(ev, ctev_buffer),
25047c478bd9Sstevel@tonic-gate 			    len);
25057c478bd9Sstevel@tonic-gate 			kmem_free(buf, len);
25067c478bd9Sstevel@tonic-gate 			if (result)
25077c478bd9Sstevel@tonic-gate 				goto copyerr;
25087c478bd9Sstevel@tonic-gate 		} else {
25097c478bd9Sstevel@tonic-gate 			partial = 1;
25107c478bd9Sstevel@tonic-gate 		}
25117c478bd9Sstevel@tonic-gate 	}
25127c478bd9Sstevel@tonic-gate 
25137c478bd9Sstevel@tonic-gate 	/*
25147c478bd9Sstevel@tonic-gate 	 * Copy out the common event data.
25157c478bd9Sstevel@tonic-gate 	 */
25167c478bd9Sstevel@tonic-gate 	STRUCT_FSET(ev, ctev_id, temp->cte_contract->ct_id);
25177c478bd9Sstevel@tonic-gate 	STRUCT_FSET(ev, ctev_evid, temp->cte_id);
25187c478bd9Sstevel@tonic-gate 	STRUCT_FSET(ev, ctev_cttype,
25197c478bd9Sstevel@tonic-gate 	    temp->cte_contract->ct_type->ct_type_index);
252025e8c5aaSvikram 	STRUCT_FSET(ev, ctev_flags, temp->cte_flags &
252125e8c5aaSvikram 	    (CTE_ACK|CTE_INFO|CTE_NEG));
25227c478bd9Sstevel@tonic-gate 	STRUCT_FSET(ev, ctev_type, temp->cte_type);
25237c478bd9Sstevel@tonic-gate 	STRUCT_FSET(ev, ctev_nbytes, len);
25247c478bd9Sstevel@tonic-gate 	STRUCT_FSET(ev, ctev_goffset, size);
25257c478bd9Sstevel@tonic-gate 	result = copyout(STRUCT_BUF(ev), uaddr, STRUCT_SIZE(ev));
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate copyerr:
25287c478bd9Sstevel@tonic-gate 	/*
25297c478bd9Sstevel@tonic-gate 	 * Only move our location in the queue if all copyouts were
25307c478bd9Sstevel@tonic-gate 	 * successful, the caller provided enough space for the entire
25317c478bd9Sstevel@tonic-gate 	 * event, and our endpoint wasn't reset or otherwise moved by
25327c478bd9Sstevel@tonic-gate 	 * another thread.
25337c478bd9Sstevel@tonic-gate 	 */
25347c478bd9Sstevel@tonic-gate 	mutex_enter(&q->ctq_lock);
25357c478bd9Sstevel@tonic-gate 	if (result)
25367c478bd9Sstevel@tonic-gate 		result = EFAULT;
25377c478bd9Sstevel@tonic-gate 	else if (!partial && ((l->ctl_flags & CTLF_RESET) == 0) &&
25387c478bd9Sstevel@tonic-gate 	    (l->ctl_position == temp))
25397c478bd9Sstevel@tonic-gate 		(void) cte_qmove(q, l, list_next(&q->ctq_events, temp));
25407c478bd9Sstevel@tonic-gate 	l->ctl_flags &= ~(CTLF_COPYOUT|CTLF_RESET);
25417c478bd9Sstevel@tonic-gate 	/*
25427c478bd9Sstevel@tonic-gate 	 * Signal any readers blocked on our CTLF_COPYOUT.
25437c478bd9Sstevel@tonic-gate 	 */
25447c478bd9Sstevel@tonic-gate 	cv_signal(&l->ctl_cv);
25457c478bd9Sstevel@tonic-gate 	cte_rele(temp);
25467c478bd9Sstevel@tonic-gate 
25477c478bd9Sstevel@tonic-gate error:
25487c478bd9Sstevel@tonic-gate 	mutex_exit(&q->ctq_lock);
25497c478bd9Sstevel@tonic-gate 	return (result);
25507c478bd9Sstevel@tonic-gate }
25517c478bd9Sstevel@tonic-gate 
25527c478bd9Sstevel@tonic-gate /*
25537c478bd9Sstevel@tonic-gate  * cte_set_reliable
25547c478bd9Sstevel@tonic-gate  *
25557c478bd9Sstevel@tonic-gate  * Requests that events be reliably delivered to an event endpoint.
25567c478bd9Sstevel@tonic-gate  * Unread informative and acknowledged critical events will not be
25577c478bd9Sstevel@tonic-gate  * removed from the queue until this listener reads or skips them.
25587c478bd9Sstevel@tonic-gate  * Because a listener could maliciously request reliable delivery and
25597c478bd9Sstevel@tonic-gate  * then do nothing, this requires that PRIV_CONTRACT_EVENT be in the
25607c478bd9Sstevel@tonic-gate  * caller's effective set.
25617c478bd9Sstevel@tonic-gate  */
25627c478bd9Sstevel@tonic-gate int
25637c478bd9Sstevel@tonic-gate cte_set_reliable(ct_listener_t *l, const cred_t *cr)
25647c478bd9Sstevel@tonic-gate {
25657c478bd9Sstevel@tonic-gate 	ct_equeue_t *q = l->ctl_equeue;
25667c478bd9Sstevel@tonic-gate 	int error;
25677c478bd9Sstevel@tonic-gate 
25687c478bd9Sstevel@tonic-gate 	if ((error = secpolicy_contract_event(cr)) != 0)
25697c478bd9Sstevel@tonic-gate 		return (error);
25707c478bd9Sstevel@tonic-gate 
25717c478bd9Sstevel@tonic-gate 	mutex_enter(&q->ctq_lock);
25727c478bd9Sstevel@tonic-gate 	if ((l->ctl_flags & CTLF_RELIABLE) == 0) {
25737c478bd9Sstevel@tonic-gate 		l->ctl_flags |= CTLF_RELIABLE;
25747c478bd9Sstevel@tonic-gate 		q->ctq_nreliable++;
25757c478bd9Sstevel@tonic-gate 		if (l->ctl_position != NULL)
25767c478bd9Sstevel@tonic-gate 			l->ctl_position->cte_nodes[q->ctq_listno].
25777c478bd9Sstevel@tonic-gate 			    ctm_nreliable++;
25787c478bd9Sstevel@tonic-gate 	}
25797c478bd9Sstevel@tonic-gate 	mutex_exit(&q->ctq_lock);
25807c478bd9Sstevel@tonic-gate 
25817c478bd9Sstevel@tonic-gate 	return (0);
25827c478bd9Sstevel@tonic-gate }
2583