#include <sys/mutex.h>
#include <sys/debug.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/kmem.h>
#include <sys/thread.h>
#include <sys/id_space.h>
#include <sys/avl.h>
#include <sys/list.h>
#include <sys/sysmacros.h>
#include <sys/proc.h>
#include <sys/ctfs.h>
#include <sys/contract_impl.h>
#include <sys/contract/process_impl.h>
#include <sys/dditypes.h>
#include <sys/contract/device_impl.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/cmn_err.h>
#include <sys/model.h>
#include <sys/policy.h>
#include <sys/zone.h>
#include <sys/task.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
extern rctl_hndl_t rc_project_contract;
static id_space_t *contract_ids;
static avl_tree_t contract_avl;
static kmutex_t contract_lock;
int ct_ntypes = CTT_MAXTYPE;
static ct_type_t *ct_types_static[CTT_MAXTYPE];
ct_type_t **ct_types = ct_types_static;
int ct_debug;
static void cte_queue_create(ct_equeue_t *, ct_listnum_t, int, int);
static void cte_queue_destroy(ct_equeue_t *);
static void cte_queue_drain(ct_equeue_t *, int);
static void cte_trim(ct_equeue_t *, contract_t *);
static void cte_copy(ct_equeue_t *, ct_equeue_t *);
int
contract_compar(const void *x, const void *y)
{
const contract_t *ct1 = x;
const contract_t *ct2 = y;
if (ct1->ct_id < ct2->ct_id)
return (-1);
if (ct1->ct_id > ct2->ct_id)
return (1);
return (0);
}
void
contract_init(void)
{
contract_ids = id_space_create("contracts", 1, INT_MAX);
avl_create(&contract_avl, contract_compar, sizeof (contract_t),
offsetof(contract_t, ct_ctavl));
mutex_init(&contract_lock, NULL, MUTEX_DEFAULT, NULL);
contract_process_init();
contract_device_init();
avl_create(&p0.p_ct_held, contract_compar, sizeof (contract_t),
offsetof(contract_t, ct_ctlist));
}
static void
contract_dtor(contract_t *ct)
{
cte_queue_destroy(&ct->ct_events);
list_destroy(&ct->ct_vnodes);
mutex_destroy(&ct->ct_reflock);
mutex_destroy(&ct->ct_lock);
mutex_destroy(&ct->ct_evtlock);
}
int
contract_ctor(contract_t *ct, ct_type_t *type, ct_template_t *tmpl, void *data,
ctflags_t flags, proc_t *author, int canfail)
{
avl_index_t where;
klwp_t *curlwp = ttolwp(curthread);
ASSERT(author == curproc);
mutex_init(&ct->ct_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ct->ct_reflock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ct->ct_evtlock, NULL, MUTEX_DEFAULT, NULL);
ct->ct_id = id_alloc(contract_ids);
cte_queue_create(&ct->ct_events, CTEL_CONTRACT, 20, 0);
list_create(&ct->ct_vnodes, sizeof (contract_vnode_t),
offsetof(contract_vnode_t, ctv_node));
ct->ct_ref = 2;
ct->ct_cuid = crgetuid(CRED());
ct->ct_type = type;
ct->ct_data = data;
gethrestime(&ct->ct_ctime);
ct->ct_state = CTS_OWNED;
ct->ct_flags = flags;
ct->ct_regent = author->p_ct_process ?
&author->p_ct_process->conp_contract : NULL;
ct->ct_ev_info = tmpl->ctmpl_ev_info;
ct->ct_ev_crit = tmpl->ctmpl_ev_crit;
ct->ct_cookie = tmpl->ctmpl_cookie;
ct->ct_owner = author;
ct->ct_ntime.ctm_total = -1;
ct->ct_qtime.ctm_total = -1;
ct->ct_nevent = NULL;
mutex_enter(&author->p_lock);
mutex_enter(&contract_lock);
if (canfail && rctl_test(rc_project_contract,
author->p_task->tk_proj->kpj_rctls, author, 1,
RCA_SAFE) & RCT_DENY) {
id_free(contract_ids, ct->ct_id);
mutex_exit(&contract_lock);
mutex_exit(&author->p_lock);
ct->ct_events.ctq_flags |= CTQ_DEAD;
contract_dtor(ct);
return (1);
}
ct->ct_proj = author->p_task->tk_proj;
ct->ct_proj->kpj_data.kpd_contract++;
(void) project_hold(ct->ct_proj);
mutex_exit(&contract_lock);
ct->ct_zoneid = author->p_zone->zone_id;
ct->ct_czuniqid = ct->ct_mzuniqid = author->p_zone->zone_uniqid;
VERIFY(avl_find(&author->p_ct_held, ct, &where) == NULL);
avl_insert(&author->p_ct_held, ct, where);
mutex_exit(&author->p_lock);
mutex_enter(&contract_lock);
VERIFY(avl_find(&contract_avl, ct, &where) == NULL);
avl_insert(&contract_avl, ct, where);
mutex_exit(&contract_lock);
mutex_enter(&type->ct_type_lock);
VERIFY(avl_find(&type->ct_type_avl, ct, &where) == NULL);
avl_insert(&type->ct_type_avl, ct, where);
type->ct_type_timestruc = ct->ct_ctime;
mutex_exit(&type->ct_type_lock);
if (curlwp->lwp_ct_latest[type->ct_type_index])
contract_rele(curlwp->lwp_ct_latest[type->ct_type_index]);
curlwp->lwp_ct_latest[type->ct_type_index] = ct;
return (0);
}
void
contract_rele(contract_t *ct)
{
uint64_t nref;
mutex_enter(&ct->ct_reflock);
ASSERT(ct->ct_ref > 0);
nref = --ct->ct_ref;
mutex_exit(&ct->ct_reflock);
if (nref == 0) {
ASSERT(ct->ct_owner == NULL);
ASSERT(ct->ct_evcnt == 0);
mutex_enter(&contract_lock);
avl_remove(&contract_avl, ct);
mutex_exit(&contract_lock);
mutex_enter(&ct->ct_type->ct_type_lock);
avl_remove(&ct->ct_type->ct_type_avl, ct);
mutex_exit(&ct->ct_type->ct_type_lock);
id_free(contract_ids, ct->ct_id);
mutex_enter(&contract_lock);
ct->ct_proj->kpj_data.kpd_contract--;
project_rele(ct->ct_proj);
mutex_exit(&contract_lock);
contract_dtor(ct);
ct->ct_type->ct_type_ops->contop_free(ct);
}
}
void
contract_hold(contract_t *ct)
{
mutex_enter(&ct->ct_reflock);
ASSERT(ct->ct_ref < UINT64_MAX);
ct->ct_ref++;
mutex_exit(&ct->ct_reflock);
}
uint64_t
contract_getzuniqid(contract_t *ct)
{
uint64_t zuniqid;
mutex_enter(&ct->ct_reflock);
zuniqid = ct->ct_mzuniqid;
mutex_exit(&ct->ct_reflock);
return (zuniqid);
}
void
contract_setzuniqid(contract_t *ct, uint64_t zuniqid)
{
mutex_enter(&ct->ct_reflock);
ct->ct_mzuniqid = zuniqid;
mutex_exit(&ct->ct_reflock);
}
int
contract_abandon(contract_t *ct, proc_t *p, int explicit)
{
ct_equeue_t *q = NULL;
contract_t *parent = &p->p_ct_process->conp_contract;
int inherit = 0;
VERIFY(p == curproc);
mutex_enter(&ct->ct_lock);
if (!explicit && (ct->ct_flags & CTF_INHERIT) &&
contract_process_accept(parent)) {
mutex_exit(&ct->ct_lock);
mutex_enter(&parent->ct_lock);
mutex_enter(&ct->ct_lock);
inherit = 1;
}
if (ct->ct_owner != p) {
mutex_exit(&ct->ct_lock);
if (inherit)
mutex_exit(&parent->ct_lock);
return (EINVAL);
}
mutex_enter(&p->p_lock);
if (explicit)
avl_remove(&p->p_ct_held, ct);
ct->ct_owner = NULL;
mutex_exit(&p->p_lock);
if (p->p_ct_equeue)
q = p->p_ct_equeue[ct->ct_type->ct_type_index];
contract_hold(ct);
if (inherit) {
ct->ct_state = CTS_INHERITED;
VERIFY(ct->ct_regent == parent);
contract_process_take(parent, ct);
mutex_exit(&ct->ct_lock);
mutex_exit(&parent->ct_lock);
} else {
ct->ct_regent = NULL;
ct->ct_type->ct_type_ops->contop_abandon(ct);
}
if (q) {
mutex_enter(&q->ctq_lock);
cte_trim(q, ct);
mutex_exit(&q->ctq_lock);
}
contract_rele(ct);
return (0);
}
int
contract_newct(contract_t *ct)
{
return (ct->ct_type->ct_type_ops->contop_newct(ct));
}
int
contract_adopt(contract_t *ct, proc_t *p)
{
avl_index_t where;
ct_equeue_t *q;
contract_t *parent;
ASSERT(p == curproc);
(void) contract_type_pbundle(ct->ct_type, p);
mutex_enter(&ct->ct_lock);
parent = ct->ct_regent;
if (ct->ct_state != CTS_INHERITED ||
&p->p_ct_process->conp_contract != parent ||
p->p_zone->zone_uniqid != ct->ct_czuniqid) {
mutex_exit(&ct->ct_lock);
return (EINVAL);
}
mutex_exit(&ct->ct_lock);
mutex_enter(&parent->ct_lock);
mutex_enter(&ct->ct_lock);
if (ct->ct_state != CTS_INHERITED) {
mutex_exit(&parent->ct_lock);
mutex_exit(&ct->ct_lock);
return (EBUSY);
}
ASSERT(ct->ct_regent == parent);
ct->ct_state = CTS_OWNED;
contract_process_adopt(ct, p);
mutex_enter(&p->p_lock);
ct->ct_owner = p;
VERIFY(avl_find(&p->p_ct_held, ct, &where) == NULL);
avl_insert(&p->p_ct_held, ct, where);
mutex_exit(&p->p_lock);
ASSERT(ct->ct_owner->p_ct_equeue);
ASSERT(ct->ct_owner->p_ct_equeue[ct->ct_type->ct_type_index]);
q = ct->ct_owner->p_ct_equeue[ct->ct_type->ct_type_index];
cte_copy(&ct->ct_events, q);
mutex_exit(&ct->ct_lock);
return (0);
}
int
contract_ack(contract_t *ct, uint64_t evid, int ack)
{
ct_kevent_t *ev;
list_t *queue = &ct->ct_events.ctq_events;
int error = ESRCH;
int nego = 0;
uint_t evtype;
ASSERT(ack == CT_ACK || ack == CT_NACK);
mutex_enter(&ct->ct_lock);
mutex_enter(&ct->ct_events.ctq_lock);
for (ev = list_head(queue); ev; ev = list_next(queue, ev)) {
if (ev->cte_id == evid) {
if (ev->cte_flags & CTE_NEG)
nego = 1;
else if (ack == CT_NACK)
break;
if ((ev->cte_flags & (CTE_INFO | CTE_ACK)) == 0) {
ev->cte_flags |= CTE_ACK;
ct->ct_evcnt--;
evtype = ev->cte_type;
error = 0;
}
break;
}
}
mutex_exit(&ct->ct_events.ctq_lock);
mutex_exit(&ct->ct_lock);
if (error || !nego)
return (error);
if (ack == CT_ACK)
error = ct->ct_type->ct_type_ops->contop_ack(ct, evtype, evid);
else
error = ct->ct_type->ct_type_ops->contop_nack(ct, evtype, evid);
return (error);
}
int
contract_ack_inval(contract_t *ct, uint_t evtype, uint64_t evid)
{
cmn_err(CE_PANIC, "contract_ack_inval: unsupported call: ctid: %u",
ct->ct_id);
return (ENOSYS);
}
int
contract_qack_inval(contract_t *ct, uint_t evtype, uint64_t evid)
{
cmn_err(CE_PANIC, "contract_ack_inval: unsupported call: ctid: %u",
ct->ct_id);
return (ENOSYS);
}
int
contract_qack_notsup(contract_t *ct, uint_t evtype, uint64_t evid)
{
return (ERANGE);
}
int
contract_qack(contract_t *ct, uint64_t evid)
{
ct_kevent_t *ev;
list_t *queue = &ct->ct_events.ctq_events;
int nego = 0;
uint_t evtype;
mutex_enter(&ct->ct_lock);
mutex_enter(&ct->ct_events.ctq_lock);
for (ev = list_head(queue); ev; ev = list_next(queue, ev)) {
if (ev->cte_id == evid) {
if ((ev->cte_flags & (CTE_NEG | CTE_ACK)) == CTE_NEG) {
evtype = ev->cte_type;
nego = 1;
}
break;
}
}
mutex_exit(&ct->ct_events.ctq_lock);
mutex_exit(&ct->ct_lock);
if (!nego)
return (ESRCH);
return (ct->ct_type->ct_type_ops->contop_qack(ct, evtype, evid));
}
void
contract_orphan(contract_t *ct)
{
ct_kevent_t *ev;
list_t *queue = &ct->ct_events.ctq_events;
ASSERT(MUTEX_HELD(&ct->ct_lock));
ASSERT(ct->ct_state != CTS_ORPHAN);
mutex_enter(&ct->ct_events.ctq_lock);
ct->ct_state = CTS_ORPHAN;
for (ev = list_head(queue); ev; ev = list_next(queue, ev)) {
if ((ev->cte_flags & (CTE_INFO | CTE_ACK)) == 0) {
ev->cte_flags |= CTE_ACK;
ct->ct_evcnt--;
}
}
mutex_exit(&ct->ct_events.ctq_lock);
ASSERT(ct->ct_evcnt == 0);
}
void
contract_destroy(contract_t *ct)
{
ASSERT(MUTEX_HELD(&ct->ct_lock));
ASSERT(ct->ct_state != CTS_DEAD);
ASSERT(ct->ct_owner == NULL);
ct->ct_state = CTS_DEAD;
cte_queue_drain(&ct->ct_events, 1);
mutex_exit(&ct->ct_lock);
mutex_enter(&ct->ct_type->ct_type_events.ctq_lock);
cte_trim(&ct->ct_type->ct_type_events, ct);
mutex_exit(&ct->ct_type->ct_type_events.ctq_lock);
mutex_enter(&ct->ct_lock);
ct->ct_type->ct_type_ops->contop_destroy(ct);
mutex_exit(&ct->ct_lock);
contract_rele(ct);
}
vnode_t *
contract_vnode_get(contract_t *ct, vfs_t *vfsp)
{
contract_vnode_t *ctv;
vnode_t *vp = NULL;
mutex_enter(&ct->ct_lock);
for (ctv = list_head(&ct->ct_vnodes); ctv != NULL;
ctv = list_next(&ct->ct_vnodes, ctv))
if (ctv->ctv_vnode->v_vfsp == vfsp) {
vp = ctv->ctv_vnode;
VN_HOLD(vp);
break;
}
mutex_exit(&ct->ct_lock);
return (vp);
}
void
contract_vnode_set(contract_t *ct, contract_vnode_t *ctv, vnode_t *vnode)
{
mutex_enter(&ct->ct_lock);
ctv->ctv_vnode = vnode;
list_insert_head(&ct->ct_vnodes, ctv);
mutex_exit(&ct->ct_lock);
}
int
contract_vnode_clear(contract_t *ct, contract_vnode_t *ctv)
{
vnode_t *vp = ctv->ctv_vnode;
int result;
mutex_enter(&ct->ct_lock);
mutex_enter(&vp->v_lock);
if (vp->v_count == 1) {
list_remove(&ct->ct_vnodes, ctv);
result = 1;
} else {
VN_RELE_LOCKED(vp);
result = 0;
}
mutex_exit(&vp->v_lock);
mutex_exit(&ct->ct_lock);
return (result);
}
void
contract_exit(proc_t *p)
{
contract_t *ct;
void *cookie = NULL;
int i;
ASSERT(p == curproc);
ASSERT(MUTEX_NOT_HELD(&p->p_lock));
while ((ct = avl_destroy_nodes(&p->p_ct_held, &cookie)) != NULL)
VERIFY(contract_abandon(ct, p, 0) == 0);
if (p->p_ct_equeue) {
for (i = 0; i < CTT_MAXTYPE; i++)
if (p->p_ct_equeue[i])
cte_queue_drain(p->p_ct_equeue[i], 0);
kmem_free(p->p_ct_equeue, CTT_MAXTYPE * sizeof (ct_equeue_t *));
}
}
static int
get_time_left(struct ct_time *t)
{
clock_t ticks_elapsed;
int secs_elapsed;
if (t->ctm_total == -1)
return (-1);
ticks_elapsed = ddi_get_lbolt() - t->ctm_start;
secs_elapsed = t->ctm_total - (drv_hztousec(ticks_elapsed)/MICROSEC);
return (secs_elapsed > 0 ? secs_elapsed : 0);
}
void
contract_status_common(contract_t *ct, zone_t *zone, void *status,
model_t model)
{
STRUCT_HANDLE(ct_status, lstatus);
STRUCT_SET_HANDLE(lstatus, model, status);
ASSERT(MUTEX_HELD(&ct->ct_lock));
if (zone->zone_uniqid == GLOBAL_ZONEUNIQID ||
zone->zone_uniqid == ct->ct_czuniqid) {
zone_t *czone;
zoneid_t zoneid = -1;
if (zone->zone_uniqid == ct->ct_czuniqid ||
ct->ct_czuniqid == GLOBAL_ZONEUNIQID) {
zoneid = ct->ct_zoneid;
} else if ((czone = zone_find_by_id(ct->ct_zoneid)) != NULL) {
if (czone->zone_uniqid == ct->ct_mzuniqid)
zoneid = ct->ct_zoneid;
zone_rele(czone);
}
STRUCT_FSET(lstatus, ctst_zoneid, zoneid);
STRUCT_FSET(lstatus, ctst_holder,
(ct->ct_state == CTS_OWNED) ? ct->ct_owner->p_pid :
(ct->ct_state == CTS_INHERITED) ? ct->ct_regent->ct_id : 0);
STRUCT_FSET(lstatus, ctst_state, ct->ct_state);
} else {
STRUCT_FSET(lstatus, ctst_zoneid, zone->zone_id);
STRUCT_FSET(lstatus, ctst_holder, (ct->ct_state < CTS_ORPHAN) ?
zone->zone_zsched->p_pid : 0);
STRUCT_FSET(lstatus, ctst_state, (ct->ct_state < CTS_ORPHAN) ?
CTS_OWNED : ct->ct_state);
}
STRUCT_FSET(lstatus, ctst_nevents, ct->ct_evcnt);
STRUCT_FSET(lstatus, ctst_ntime, get_time_left(&ct->ct_ntime));
STRUCT_FSET(lstatus, ctst_qtime, get_time_left(&ct->ct_qtime));
STRUCT_FSET(lstatus, ctst_nevid,
ct->ct_nevent ? ct->ct_nevent->cte_id : 0);
STRUCT_FSET(lstatus, ctst_critical, ct->ct_ev_crit);
STRUCT_FSET(lstatus, ctst_informative, ct->ct_ev_info);
STRUCT_FSET(lstatus, ctst_cookie, ct->ct_cookie);
STRUCT_FSET(lstatus, ctst_type, ct->ct_type->ct_type_index);
STRUCT_FSET(lstatus, ctst_id, ct->ct_id);
}
static int
contract_checkcred(contract_t *ct, const cred_t *cr)
{
proc_t *p;
int fail = 1;
mutex_enter(&ct->ct_lock);
if ((p = ct->ct_owner) != NULL) {
mutex_enter(&p->p_crlock);
fail = crgetuid(cr) != crgetuid(p->p_cred);
mutex_exit(&p->p_crlock);
}
mutex_exit(&ct->ct_lock);
return (!fail);
}
int
contract_owned(contract_t *ct, const cred_t *cr, int locked)
{
int owner, cmatch, zmatch;
uint64_t zuniqid, mzuniqid;
uid_t euid;
ASSERT(locked || MUTEX_NOT_HELD(&ct->ct_lock));
zuniqid = curproc->p_zone->zone_uniqid;
mzuniqid = contract_getzuniqid(ct);
euid = crgetuid(cr);
owner = (ct->ct_owner == curproc);
cmatch = (zuniqid == ct->ct_czuniqid) &&
((ct->ct_cuid == euid) || (!locked && contract_checkcred(ct, cr)));
zmatch = (ct->ct_czuniqid != mzuniqid) && (zuniqid == mzuniqid) &&
(crgetuid(kcred) == euid);
return (owner || cmatch || zmatch);
}
ct_type_t *
contract_type_init(ct_typeid_t type, const char *name, contops_t *ops,
ct_f_default_t *dfault)
{
ct_type_t *result;
ASSERT(type < CTT_MAXTYPE);
result = kmem_alloc(sizeof (ct_type_t), KM_SLEEP);
mutex_init(&result->ct_type_lock, NULL, MUTEX_DEFAULT, NULL);
avl_create(&result->ct_type_avl, contract_compar, sizeof (contract_t),
offsetof(contract_t, ct_cttavl));
cte_queue_create(&result->ct_type_events, CTEL_BUNDLE, 20, 0);
result->ct_type_name = name;
result->ct_type_ops = ops;
result->ct_type_default = dfault;
result->ct_type_evid = 0;
gethrestime(&result->ct_type_timestruc);
result->ct_type_index = type;
ct_types[type] = result;
return (result);
}
int
contract_type_count(ct_type_t *type)
{
ulong_t count;
mutex_enter(&type->ct_type_lock);
count = avl_numnodes(&type->ct_type_avl);
mutex_exit(&type->ct_type_lock);
return (count);
}
ctid_t
contract_type_max(ct_type_t *type)
{
contract_t *ct;
ctid_t res;
mutex_enter(&type->ct_type_lock);
ct = avl_last(&type->ct_type_avl);
res = ct ? ct->ct_id : -1;
mutex_exit(&type->ct_type_lock);
return (res);
}
ctid_t
contract_max(void)
{
contract_t *ct;
ctid_t res;
mutex_enter(&contract_lock);
ct = avl_last(&contract_avl);
res = ct ? ct->ct_id : -1;
mutex_exit(&contract_lock);
return (res);
}
static ctid_t
contract_lookup_common(avl_tree_t *tree, uint64_t zuniqid, ctid_t current)
{
contract_t template, *ct;
avl_index_t where;
ctid_t res;
template.ct_id = current;
ct = avl_find(tree, &template, &where);
if (ct == NULL)
ct = avl_nearest(tree, where, AVL_AFTER);
if (zuniqid != GLOBAL_ZONEUNIQID)
while (ct && (contract_getzuniqid(ct) != zuniqid))
ct = AVL_NEXT(tree, ct);
res = ct ? ct->ct_id : -1;
return (res);
}
ctid_t
contract_type_lookup(ct_type_t *type, uint64_t zuniqid, ctid_t current)
{
ctid_t res;
mutex_enter(&type->ct_type_lock);
res = contract_lookup_common(&type->ct_type_avl, zuniqid, current);
mutex_exit(&type->ct_type_lock);
return (res);
}
ctid_t
contract_lookup(uint64_t zuniqid, ctid_t current)
{
ctid_t res;
mutex_enter(&contract_lock);
res = contract_lookup_common(&contract_avl, zuniqid, current);
mutex_exit(&contract_lock);
return (res);
}
ctid_t
contract_plookup(proc_t *p, ctid_t current, uint64_t zuniqid)
{
contract_t template, *ct;
avl_index_t where;
ctid_t res;
template.ct_id = current;
if (zuniqid != GLOBAL_ZONEUNIQID &&
(p->p_flag & (SSYS|SZONETOP)) == (SSYS|SZONETOP)) {
mutex_enter(&contract_lock);
ct = avl_find(&contract_avl, &template, &where);
if (ct == NULL)
ct = avl_nearest(&contract_avl, where, AVL_AFTER);
while (ct && !(ct->ct_state < CTS_ORPHAN &&
contract_getzuniqid(ct) == zuniqid &&
ct->ct_czuniqid == GLOBAL_ZONEUNIQID))
ct = AVL_NEXT(&contract_avl, ct);
res = ct ? ct->ct_id : -1;
mutex_exit(&contract_lock);
} else {
mutex_enter(&p->p_lock);
ct = avl_find(&p->p_ct_held, &template, &where);
if (ct == NULL)
ct = avl_nearest(&p->p_ct_held, where, AVL_AFTER);
res = ct ? ct->ct_id : -1;
mutex_exit(&p->p_lock);
}
return (res);
}
static contract_t *
contract_ptr_common(avl_tree_t *tree, ctid_t id, uint64_t zuniqid)
{
contract_t template, *ct;
template.ct_id = id;
ct = avl_find(tree, &template, NULL);
if (ct == NULL || (zuniqid != GLOBAL_ZONEUNIQID &&
contract_getzuniqid(ct) != zuniqid)) {
return (NULL);
}
mutex_enter(&ct->ct_reflock);
if (ct->ct_ref) {
ct->ct_ref++;
mutex_exit(&ct->ct_reflock);
} else {
mutex_exit(&ct->ct_reflock);
ct = NULL;
}
return (ct);
}
contract_t *
contract_type_ptr(ct_type_t *type, ctid_t id, uint64_t zuniqid)
{
contract_t *ct;
mutex_enter(&type->ct_type_lock);
ct = contract_ptr_common(&type->ct_type_avl, id, zuniqid);
mutex_exit(&type->ct_type_lock);
return (ct);
}
contract_t *
contract_ptr(ctid_t id, uint64_t zuniqid)
{
contract_t *ct;
mutex_enter(&contract_lock);
ct = contract_ptr_common(&contract_avl, id, zuniqid);
mutex_exit(&contract_lock);
return (ct);
}
void
contract_type_time(ct_type_t *type, timestruc_t *time)
{
mutex_enter(&type->ct_type_lock);
*time = type->ct_type_timestruc;
mutex_exit(&type->ct_type_lock);
}
ct_equeue_t *
contract_type_bundle(ct_type_t *type)
{
return (&type->ct_type_events);
}
ct_equeue_t *
contract_type_pbundle(ct_type_t *type, proc_t *pp)
{
if (pp->p_ct_equeue == NULL) {
size_t size = CTT_MAXTYPE * sizeof (ct_equeue_t *);
ct_equeue_t **qa = kmem_zalloc(size, KM_SLEEP);
mutex_enter(&pp->p_lock);
if (pp->p_ct_equeue)
kmem_free(qa, size);
else
pp->p_ct_equeue = qa;
mutex_exit(&pp->p_lock);
}
if (pp->p_ct_equeue[type->ct_type_index] == NULL) {
ct_equeue_t *q = kmem_zalloc(sizeof (ct_equeue_t), KM_SLEEP);
cte_queue_create(q, CTEL_PBUNDLE, 20, 1);
mutex_enter(&pp->p_lock);
if (pp->p_ct_equeue[type->ct_type_index])
cte_queue_drain(q, 0);
else
pp->p_ct_equeue[type->ct_type_index] = q;
mutex_exit(&pp->p_lock);
}
return (pp->p_ct_equeue[type->ct_type_index]);
}
int
ctparam_copyin(const void *uaddr, ct_kparam_t *kparam, int flag, int cmd)
{
uint32_t size;
void *ubuf;
ct_param_t *param = &kparam->param;
STRUCT_DECL(ct_param, uarg);
STRUCT_INIT(uarg, flag);
if (copyin(uaddr, STRUCT_BUF(uarg), STRUCT_SIZE(uarg)))
return (EFAULT);
size = STRUCT_FGET(uarg, ctpm_size);
ubuf = STRUCT_FGETP(uarg, ctpm_value);
if (size > CT_PARAM_MAX_SIZE || size == 0)
return (EINVAL);
kparam->ctpm_kbuf = kmem_alloc(size, KM_SLEEP);
if (cmd == CT_TSET) {
if (copyin(ubuf, kparam->ctpm_kbuf, size)) {
kmem_free(kparam->ctpm_kbuf, size);
return (EFAULT);
}
}
param->ctpm_id = STRUCT_FGET(uarg, ctpm_id);
param->ctpm_size = size;
param->ctpm_value = ubuf;
kparam->ret_size = 0;
return (0);
}
int
ctparam_copyout(ct_kparam_t *kparam, void *uaddr, int flag)
{
int r = 0;
ct_param_t *param = &kparam->param;
STRUCT_DECL(ct_param, uarg);
STRUCT_INIT(uarg, flag);
STRUCT_FSET(uarg, ctpm_id, param->ctpm_id);
STRUCT_FSET(uarg, ctpm_size, kparam->ret_size);
STRUCT_FSETP(uarg, ctpm_value, param->ctpm_value);
if (copyout(STRUCT_BUF(uarg), uaddr, STRUCT_SIZE(uarg))) {
r = EFAULT;
goto error;
}
if (copyout(kparam->ctpm_kbuf, param->ctpm_value,
MIN(kparam->ret_size, param->ctpm_size))) {
r = EFAULT;
}
error:
kmem_free(kparam->ctpm_kbuf, param->ctpm_size);
return (r);
}
void
ctmpl_free(ct_template_t *template)
{
mutex_destroy(&template->ctmpl_lock);
template->ctmpl_ops->ctop_free(template);
}
ct_template_t *
ctmpl_dup(ct_template_t *template)
{
ct_template_t *new;
if (template == NULL)
return (NULL);
new = template->ctmpl_ops->ctop_dup(template);
mutex_exit(&template->ctmpl_lock);
return (new);
}
int
ctmpl_set(ct_template_t *template, ct_kparam_t *kparam, const cred_t *cr)
{
int result = 0;
ct_param_t *param = &kparam->param;
uint64_t param_value;
param_value = 0;
if (param->ctpm_id == CTP_COOKIE ||
param->ctpm_id == CTP_EV_INFO ||
param->ctpm_id == CTP_EV_CRITICAL) {
if (param->ctpm_size < sizeof (uint64_t)) {
return (EINVAL);
} else {
param_value = *(uint64_t *)kparam->ctpm_kbuf;
}
}
mutex_enter(&template->ctmpl_lock);
switch (param->ctpm_id) {
case CTP_COOKIE:
template->ctmpl_cookie = param_value;
break;
case CTP_EV_INFO:
if (param_value & ~(uint64_t)template->ctmpl_ops->allevents)
result = EINVAL;
else
template->ctmpl_ev_info = param_value;
break;
case CTP_EV_CRITICAL:
if (param_value & ~(uint64_t)template->ctmpl_ops->allevents) {
result = EINVAL;
break;
} else if ((~template->ctmpl_ev_crit & param_value) == 0) {
template->ctmpl_ev_crit = param_value;
break;
}
default:
result = template->ctmpl_ops->ctop_set(template, kparam, cr);
}
mutex_exit(&template->ctmpl_lock);
return (result);
}
int
ctmpl_get(ct_template_t *template, ct_kparam_t *kparam)
{
int result = 0;
ct_param_t *param = &kparam->param;
uint64_t *param_value;
param_value = NULL;
if (param->ctpm_id == CTP_COOKIE ||
param->ctpm_id == CTP_EV_INFO ||
param->ctpm_id == CTP_EV_CRITICAL) {
if (param->ctpm_size < sizeof (uint64_t)) {
return (EINVAL);
} else {
param_value = kparam->ctpm_kbuf;
kparam->ret_size = sizeof (uint64_t);
}
}
mutex_enter(&template->ctmpl_lock);
switch (param->ctpm_id) {
case CTP_COOKIE:
if (param_value != NULL)
*param_value = template->ctmpl_cookie;
break;
case CTP_EV_INFO:
if (param_value != NULL)
*param_value = template->ctmpl_ev_info;
break;
case CTP_EV_CRITICAL:
if (param_value != NULL)
*param_value = template->ctmpl_ev_crit;
break;
default:
result = template->ctmpl_ops->ctop_get(template, kparam);
}
mutex_exit(&template->ctmpl_lock);
return (result);
}
static void
ctmpl_makecurrent(ct_template_t *template, ct_template_t *new)
{
klwp_t *curlwp = ttolwp(curthread);
proc_t *p = curproc;
ct_template_t *old;
mutex_enter(&p->p_lock);
old = curlwp->lwp_ct_active[template->ctmpl_type->ct_type_index];
curlwp->lwp_ct_active[template->ctmpl_type->ct_type_index] = new;
mutex_exit(&p->p_lock);
if (old)
ctmpl_free(old);
}
void
ctmpl_activate(ct_template_t *template)
{
ctmpl_makecurrent(template, ctmpl_dup(template));
}
void
ctmpl_clear(ct_template_t *template)
{
ctmpl_makecurrent(template, NULL);
}
int
ctmpl_create(ct_template_t *template, ctid_t *ctidp)
{
return (template->ctmpl_ops->ctop_create(template, ctidp));
}
void
ctmpl_init(ct_template_t *new, ctmplops_t *ops, ct_type_t *type, void *data)
{
mutex_init(&new->ctmpl_lock, NULL, MUTEX_DEFAULT, NULL);
new->ctmpl_ops = ops;
new->ctmpl_type = type;
new->ctmpl_data = data;
new->ctmpl_ev_info = new->ctmpl_ev_crit = 0;
new->ctmpl_cookie = 0;
}
void
ctmpl_copy(ct_template_t *new, ct_template_t *old)
{
mutex_init(&new->ctmpl_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_enter(&old->ctmpl_lock);
new->ctmpl_ops = old->ctmpl_ops;
new->ctmpl_type = old->ctmpl_type;
new->ctmpl_ev_crit = old->ctmpl_ev_crit;
new->ctmpl_ev_info = old->ctmpl_ev_info;
new->ctmpl_cookie = old->ctmpl_cookie;
}
int
ctmpl_create_inval(ct_template_t *template, ctid_t *ctidp)
{
return (EINVAL);
}
static void
cte_queue_create(ct_equeue_t *q, ct_listnum_t list, int maxinf, int dynamic)
{
mutex_init(&q->ctq_lock, NULL, MUTEX_DEFAULT, NULL);
q->ctq_listno = list;
list_create(&q->ctq_events, sizeof (ct_kevent_t),
offsetof(ct_kevent_t, cte_nodes[list].ctm_node));
list_create(&q->ctq_listeners, sizeof (ct_listener_t),
offsetof(ct_listener_t, ctl_allnode));
list_create(&q->ctq_tail, sizeof (ct_listener_t),
offsetof(ct_listener_t, ctl_tailnode));
gethrestime(&q->ctq_atime);
q->ctq_nlisteners = 0;
q->ctq_nreliable = 0;
q->ctq_ninf = 0;
q->ctq_max = maxinf;
q->ctq_flags = dynamic ? CTQ_REFFED : 0;
}
static void
cte_queue_destroy(ct_equeue_t *q)
{
ASSERT(q->ctq_flags & CTQ_DEAD);
ASSERT(q->ctq_nlisteners == 0);
ASSERT(q->ctq_nreliable == 0);
list_destroy(&q->ctq_events);
list_destroy(&q->ctq_listeners);
list_destroy(&q->ctq_tail);
mutex_destroy(&q->ctq_lock);
if (q->ctq_flags & CTQ_REFFED)
kmem_free(q, sizeof (ct_equeue_t));
}
static void
cte_hold(ct_kevent_t *e)
{
mutex_enter(&e->cte_lock);
ASSERT(e->cte_refs > 0);
e->cte_refs++;
mutex_exit(&e->cte_lock);
}
static void
cte_rele(ct_kevent_t *e)
{
mutex_enter(&e->cte_lock);
ASSERT(e->cte_refs > 0);
if (--e->cte_refs) {
mutex_exit(&e->cte_lock);
return;
}
contract_rele(e->cte_contract);
mutex_destroy(&e->cte_lock);
nvlist_free(e->cte_data);
nvlist_free(e->cte_gdata);
kmem_free(e, sizeof (ct_kevent_t));
}
static void
cte_qrele(ct_equeue_t *q, ct_listener_t *l, ct_kevent_t *e)
{
ct_member_t *member = &e->cte_nodes[q->ctq_listno];
ASSERT(MUTEX_HELD(&q->ctq_lock));
if (l->ctl_flags & CTLF_RELIABLE)
member->ctm_nreliable--;
if ((--member->ctm_refs == 0) && member->ctm_trimmed) {
member->ctm_trimmed = 0;
list_remove(&q->ctq_events, e);
cte_rele(e);
}
}
static ct_kevent_t *
cte_qmove(ct_equeue_t *q, ct_listener_t *l, ct_kevent_t *e)
{
ct_kevent_t *olde;
ASSERT(MUTEX_HELD(&q->ctq_lock));
ASSERT(l->ctl_equeue == q);
if ((olde = l->ctl_position) == NULL)
list_remove(&q->ctq_tail, l);
while (e != NULL && e->cte_nodes[q->ctq_listno].ctm_trimmed)
e = list_next(&q->ctq_events, e);
if (e != NULL) {
e->cte_nodes[q->ctq_listno].ctm_refs++;
if (l->ctl_flags & CTLF_RELIABLE)
e->cte_nodes[q->ctq_listno].ctm_nreliable++;
} else {
list_insert_tail(&q->ctq_tail, l);
}
l->ctl_position = e;
if (olde)
cte_qrele(q, l, olde);
return (e);
}
static int
cte_checkcred(ct_equeue_t *q, ct_kevent_t *e, const cred_t *cr)
{
int result;
contract_t *ct = e->cte_contract;
cte_hold(e);
mutex_exit(&q->ctq_lock);
result = curproc->p_zone->zone_uniqid == ct->ct_czuniqid &&
contract_checkcred(ct, cr);
mutex_enter(&q->ctq_lock);
cte_rele(e);
return (result);
}
static int
cte_qreadable(ct_equeue_t *q, ct_listener_t *l, const cred_t *cr,
uint64_t zuniqid, int crit)
{
ct_kevent_t *e, *next;
contract_t *ct;
ASSERT(MUTEX_HELD(&q->ctq_lock));
ASSERT(l->ctl_equeue == q);
if (l->ctl_flags & CTLF_COPYOUT)
return (1);
next = l->ctl_position;
while (e = cte_qmove(q, l, next)) {
ct = e->cte_contract;
if ((crit && (e->cte_flags & (CTE_INFO | CTE_ACK))) ||
(cr != NULL && zuniqid != GLOBAL_ZONEUNIQID &&
zuniqid != contract_getzuniqid(ct))) {
next = list_next(&q->ctq_events, e);
} else if (cr != NULL && !contract_owned(ct, cr, B_TRUE)) {
if (e->cte_contract->ct_owner == NULL &&
!secpolicy_contract_observer_choice(cr))
next = list_next(&q->ctq_events, e);
else if (cte_checkcred(q, e, cr) &&
l->ctl_position == e)
break;
else if (l->ctl_position == e)
if (secpolicy_contract_observer_choice(cr))
break;
else
next = list_next(&q->ctq_events, e);
else
next = l->ctl_position;
} else {
break;
}
}
return ((l->ctl_flags & CTLF_COPYOUT) || (l->ctl_position == NULL));
}
static void
cte_qwakeup(ct_equeue_t *q, ct_kevent_t *e)
{
ct_listener_t *l;
ASSERT(MUTEX_HELD(&q->ctq_lock));
while (l = list_head(&q->ctq_tail)) {
list_remove(&q->ctq_tail, l);
e->cte_nodes[q->ctq_listno].ctm_refs++;
if (l->ctl_flags & CTLF_RELIABLE)
e->cte_nodes[q->ctq_listno].ctm_nreliable++;
l->ctl_position = e;
cv_signal(&l->ctl_cv);
pollwakeup(&l->ctl_pollhead, POLLIN);
}
}
static void
cte_copy(ct_equeue_t *q, ct_equeue_t *newq)
{
ct_kevent_t *e, *first = NULL;
VERIFY(q->ctq_listno == CTEL_CONTRACT);
VERIFY(newq->ctq_listno == CTEL_PBUNDLE);
mutex_enter(&q->ctq_lock);
mutex_enter(&newq->ctq_lock);
for (e = list_head(&q->ctq_events); e != NULL;
e = list_next(&q->ctq_events, e)) {
if ((e->cte_flags & (CTE_INFO | CTE_ACK)) == 0) {
if (first == NULL)
first = e;
if (!list_link_active((list_node_t *)
((uintptr_t)e + newq->ctq_events.list_offset))) {
list_insert_tail(&newq->ctq_events, e);
cte_hold(e);
}
}
}
mutex_exit(&q->ctq_lock);
if (first)
cte_qwakeup(newq, first);
mutex_exit(&newq->ctq_lock);
}
static void
cte_trim(ct_equeue_t *q, contract_t *ct)
{
ct_kevent_t *e, *next;
int flags, stopper;
int start = 1;
VERIFY(MUTEX_HELD(&q->ctq_lock));
for (e = list_head(&q->ctq_events); e != NULL; e = next) {
next = list_next(&q->ctq_events, e);
flags = e->cte_flags;
stopper = (q->ctq_listno != CTEL_PBUNDLE) &&
(e->cte_nodes[q->ctq_listno].ctm_nreliable > 0);
if (e->cte_nodes[q->ctq_listno].ctm_refs == 0) {
if ((start && (flags & (CTE_INFO | CTE_ACK))) ||
(e->cte_contract == ct)) {
list_remove(&q->ctq_events, e);
cte_rele(e);
}
} else if ((e->cte_contract == ct) && !stopper) {
ASSERT(q->ctq_nlisteners != 0);
e->cte_nodes[q->ctq_listno].ctm_trimmed = 1;
} else if (ct && !stopper) {
start = 0;
} else {
break;
}
}
}
static void
cte_queue_drain(ct_equeue_t *q, int ack)
{
ct_kevent_t *e, *next;
ct_listener_t *l;
mutex_enter(&q->ctq_lock);
for (e = list_head(&q->ctq_events); e != NULL; e = next) {
next = list_next(&q->ctq_events, e);
if (ack && ((e->cte_flags & (CTE_INFO | CTE_ACK)) == 0)) {
mutex_enter(&e->cte_lock);
e->cte_flags |= CTE_ACK;
mutex_exit(&e->cte_lock);
ASSERT(MUTEX_HELD(&e->cte_contract->ct_lock));
e->cte_contract->ct_evcnt--;
}
list_remove(&q->ctq_events, e);
e->cte_nodes[q->ctq_listno].ctm_refs = 0;
e->cte_nodes[q->ctq_listno].ctm_nreliable = 0;
e->cte_nodes[q->ctq_listno].ctm_trimmed = 0;
cte_rele(e);
}
for (l = list_head(&q->ctq_listeners); l;
l = list_next(&q->ctq_listeners, l)) {
l->ctl_flags |= CTLF_DEAD;
if (l->ctl_position) {
l->ctl_position = NULL;
list_insert_tail(&q->ctq_tail, l);
}
cv_broadcast(&l->ctl_cv);
}
q->ctq_flags |= CTQ_DEAD;
if ((q->ctq_flags & CTQ_REFFED) && (q->ctq_nlisteners == 0))
cte_queue_destroy(q);
else
mutex_exit(&q->ctq_lock);
}
static void
cte_publish(ct_equeue_t *q, ct_kevent_t *e, timespec_t *tsp, boolean_t mayexist)
{
ASSERT(MUTEX_HELD(&q->ctq_lock));
q->ctq_atime = *tsp;
if (mayexist && list_link_active((list_node_t *)((uintptr_t)e +
q->ctq_events.list_offset))) {
mutex_exit(&q->ctq_lock);
cte_rele(e);
return;
}
if (((q->ctq_nlisteners == 0) && (e->cte_flags & (CTE_INFO|CTE_ACK))) ||
(q->ctq_flags & CTQ_DEAD)) {
mutex_exit(&q->ctq_lock);
cte_rele(e);
return;
}
VERIFY(!list_link_active((list_node_t *)
((uintptr_t)e + q->ctq_events.list_offset)));
list_insert_tail(&q->ctq_events, e);
cte_qwakeup(q, e);
cte_trim(q, NULL);
mutex_exit(&q->ctq_lock);
}
uint64_t
cte_publish_all(contract_t *ct, ct_kevent_t *e, nvlist_t *data, nvlist_t *gdata)
{
ct_equeue_t *q;
timespec_t ts;
uint64_t evid;
ct_kevent_t *negev;
int negend;
e->cte_contract = ct;
e->cte_data = data;
e->cte_gdata = gdata;
e->cte_refs = 3;
evid = e->cte_id = atomic_inc_64_nv(&ct->ct_type->ct_type_evid);
contract_hold(ct);
negend = 0;
if (e->cte_flags & CTE_NEG) {
cte_hold(e);
ct->ct_nevent = e;
} else if (e->cte_type == CT_EV_NEGEND) {
negend = 1;
}
gethrestime(&ts);
mutex_enter(&ct->ct_evtlock);
mutex_enter(&ct->ct_lock);
mutex_enter(&ct->ct_events.ctq_lock);
if ((e->cte_flags & CTE_INFO) == 0) {
if (ct->ct_state >= CTS_ORPHAN)
e->cte_flags |= CTE_ACK;
else
ct->ct_evcnt++;
}
mutex_exit(&ct->ct_lock);
cte_publish(&ct->ct_events, e, &ts, B_FALSE);
mutex_enter(&ct->ct_type->ct_type_events.ctq_lock);
cte_publish(&ct->ct_type->ct_type_events, e, &ts, B_FALSE);
mutex_enter(&ct->ct_lock);
if (ct->ct_owner) {
ASSERT(ct->ct_owner->p_ct_equeue);
ASSERT(ct->ct_owner->p_ct_equeue[ct->ct_type->ct_type_index]);
q = ct->ct_owner->p_ct_equeue[ct->ct_type->ct_type_index];
mutex_enter(&q->ctq_lock);
mutex_exit(&ct->ct_lock);
cte_publish(q, e, &ts, B_TRUE);
} else {
mutex_exit(&ct->ct_lock);
cte_rele(e);
}
if (negend) {
mutex_enter(&ct->ct_lock);
negev = ct->ct_nevent;
ct->ct_nevent = NULL;
cte_rele(negev);
mutex_exit(&ct->ct_lock);
}
mutex_exit(&ct->ct_evtlock);
return (evid);
}
void
cte_add_listener(ct_equeue_t *q, ct_listener_t *l)
{
cv_init(&l->ctl_cv, NULL, CV_DEFAULT, NULL);
l->ctl_equeue = q;
l->ctl_position = NULL;
l->ctl_flags = 0;
mutex_enter(&q->ctq_lock);
list_insert_head(&q->ctq_tail, l);
list_insert_head(&q->ctq_listeners, l);
q->ctq_nlisteners++;
mutex_exit(&q->ctq_lock);
}
void
cte_remove_listener(ct_listener_t *l)
{
ct_equeue_t *q = l->ctl_equeue;
ct_kevent_t *e;
mutex_enter(&q->ctq_lock);
ASSERT((l->ctl_flags & (CTLF_COPYOUT|CTLF_RESET)) == 0);
if ((e = l->ctl_position) != NULL)
cte_qrele(q, l, e);
else
list_remove(&q->ctq_tail, l);
l->ctl_position = NULL;
q->ctq_nlisteners--;
list_remove(&q->ctq_listeners, l);
if (l->ctl_flags & CTLF_RELIABLE)
q->ctq_nreliable--;
if ((q->ctq_flags & CTQ_REFFED) && (q->ctq_flags & CTQ_DEAD) &&
(q->ctq_nlisteners == 0)) {
cte_queue_destroy(q);
} else {
cte_trim(q, NULL);
mutex_exit(&q->ctq_lock);
}
}
void
cte_reset_listener(ct_listener_t *l)
{
ct_equeue_t *q = l->ctl_equeue;
mutex_enter(&q->ctq_lock);
if (l->ctl_flags & CTLF_COPYOUT)
l->ctl_flags |= CTLF_RESET;
(void) cte_qmove(q, l, list_head(&q->ctq_events));
cv_broadcast(&l->ctl_cv);
pollwakeup(&l->ctl_pollhead, POLLIN);
mutex_exit(&q->ctq_lock);
}
int
cte_next_event(ct_listener_t *l, uint64_t id)
{
ct_equeue_t *q = l->ctl_equeue;
ct_kevent_t *old;
mutex_enter(&q->ctq_lock);
if (l->ctl_flags & CTLF_COPYOUT)
l->ctl_flags |= CTLF_RESET;
if (((old = l->ctl_position) != NULL) && (old->cte_id == id))
(void) cte_qmove(q, l, list_next(&q->ctq_events, old));
mutex_exit(&q->ctq_lock);
return (0);
}
int
cte_get_event(ct_listener_t *l, int nonblock, void *uaddr, const cred_t *cr,
uint64_t zuniqid, int crit)
{
ct_equeue_t *q = l->ctl_equeue;
ct_kevent_t *temp;
int result = 0;
int partial = 0;
size_t size, gsize, len;
model_t mdl = get_udatamodel();
STRUCT_DECL(ct_event, ev);
STRUCT_INIT(ev, mdl);
mutex_enter(&q->ctq_lock);
while (cte_qreadable(q, l, cr, zuniqid, crit)) {
if (nonblock) {
result = EAGAIN;
goto error;
}
if (q->ctq_flags & CTQ_DEAD) {
result = EIDRM;
goto error;
}
result = cv_wait_sig(&l->ctl_cv, &q->ctq_lock);
if (result == 0) {
result = EINTR;
goto error;
}
}
temp = l->ctl_position;
cte_hold(temp);
l->ctl_flags |= CTLF_COPYOUT;
mutex_exit(&q->ctq_lock);
result = copyin(uaddr, STRUCT_BUF(ev), STRUCT_SIZE(ev));
if (result)
goto copyerr;
size = gsize = 0;
if (temp->cte_data) {
VERIFY(nvlist_size(temp->cte_data, &size,
NV_ENCODE_NATIVE) == 0);
ASSERT(size != 0);
}
if (zuniqid == GLOBAL_ZONEUNIQID && temp->cte_gdata) {
VERIFY(nvlist_size(temp->cte_gdata, &gsize,
NV_ENCODE_NATIVE) == 0);
ASSERT(gsize != 0);
}
len = size + gsize;
if (len) {
if (STRUCT_FGET(ev, ctev_nbytes) >= len) {
char *buf = kmem_alloc(len, KM_SLEEP);
if (size)
VERIFY(nvlist_pack(temp->cte_data, &buf, &size,
NV_ENCODE_NATIVE, KM_SLEEP) == 0);
if (gsize) {
char *tmp = buf + size;
VERIFY(nvlist_pack(temp->cte_gdata, &tmp,
&gsize, NV_ENCODE_NATIVE, KM_SLEEP) == 0);
}
ASSERT(size + gsize == len);
result = copyout(buf, STRUCT_FGETP(ev, ctev_buffer),
len);
kmem_free(buf, len);
if (result)
goto copyerr;
} else {
partial = 1;
}
}
STRUCT_FSET(ev, ctev_id, temp->cte_contract->ct_id);
STRUCT_FSET(ev, ctev_evid, temp->cte_id);
STRUCT_FSET(ev, ctev_cttype,
temp->cte_contract->ct_type->ct_type_index);
STRUCT_FSET(ev, ctev_flags, temp->cte_flags &
(CTE_ACK|CTE_INFO|CTE_NEG));
STRUCT_FSET(ev, ctev_type, temp->cte_type);
STRUCT_FSET(ev, ctev_nbytes, len);
STRUCT_FSET(ev, ctev_goffset, size);
result = copyout(STRUCT_BUF(ev), uaddr, STRUCT_SIZE(ev));
copyerr:
mutex_enter(&q->ctq_lock);
if (result)
result = EFAULT;
else if (!partial && ((l->ctl_flags & CTLF_RESET) == 0) &&
(l->ctl_position == temp))
(void) cte_qmove(q, l, list_next(&q->ctq_events, temp));
l->ctl_flags &= ~(CTLF_COPYOUT|CTLF_RESET);
cv_signal(&l->ctl_cv);
cte_rele(temp);
error:
mutex_exit(&q->ctq_lock);
return (result);
}
int
cte_set_reliable(ct_listener_t *l, const cred_t *cr)
{
ct_equeue_t *q = l->ctl_equeue;
int error;
if ((error = secpolicy_contract_event(cr)) != 0)
return (error);
mutex_enter(&q->ctq_lock);
if ((l->ctl_flags & CTLF_RELIABLE) == 0) {
l->ctl_flags |= CTLF_RELIABLE;
q->ctq_nreliable++;
if (l->ctl_position != NULL)
l->ctl_position->cte_nodes[q->ctq_listno].
ctm_nreliable++;
}
mutex_exit(&q->ctq_lock);
return (0);
}