void *userdata;
struct sockaddr_ctl sac;
u_int32_t usecount;
+ u_int32_t kcb_usecount;
};
#ifndef ROUNDUP64
return 0;
}
+/*
+ * Use this function to serialize calls into the kctl subsystem
+ */
+static void
+ctl_kcb_increment_use_count(struct ctl_cb *kcb, lck_mtx_t *mutex_held)
+{
+ LCK_MTX_ASSERT(mutex_held, LCK_MTX_ASSERT_OWNED);
+ while (kcb->kcb_usecount > 0) {
+ msleep(&kcb->kcb_usecount, mutex_held, PSOCK | PCATCH, "kcb_usecount", NULL);
+ }
+ kcb->kcb_usecount++;
+}
+
+static void
+clt_kcb_decrement_use_count(struct ctl_cb *kcb)
+{
+ assert(kcb->kcb_usecount != 0);
+ kcb->kcb_usecount--;
+ wakeup_one((caddr_t)&kcb->kcb_usecount);
+}
+
static int
ctl_detach(struct socket *so)
{
return 0;
}
+ lck_mtx_t *mtx_held = socket_getlock(so, PR_F_WILLUNLOCK);
+ ctl_kcb_increment_use_count(kcb, mtx_held);
+
if (kcb->kctl != NULL && kcb->kctl->bind != NULL &&
kcb->userdata != NULL && !(so->so_state & SS_ISCONNECTED)) {
// The unit was bound, but not connected
soisdisconnected(so);
so->so_flags |= SOF_PCBCLEARING;
+ clt_kcb_decrement_use_count(kcb);
return 0;
}
panic("ctl_bind so_pcb null\n");
}
+ lck_mtx_t *mtx_held = socket_getlock(so, PR_F_WILLUNLOCK);
+ ctl_kcb_increment_use_count(kcb, mtx_held);
+
error = ctl_setup_kctl(so, nam, p);
if (error) {
- return error;
+ goto out;
}
if (kcb->kctl == NULL) {
}
if (kcb->kctl->bind == NULL) {
- return EINVAL;
+ error = EINVAL;
+ goto out;
}
socket_unlock(so, 0);
error = (*kcb->kctl->bind)(kcb->kctl->kctlref, &kcb->sac, &kcb->userdata);
socket_lock(so, 0);
+out:
+ clt_kcb_decrement_use_count(kcb);
return error;
}
panic("ctl_connect so_pcb null\n");
}
+ lck_mtx_t *mtx_held = socket_getlock(so, PR_F_WILLUNLOCK);
+ ctl_kcb_increment_use_count(kcb, mtx_held);
+
error = ctl_setup_kctl(so, nam, p);
if (error) {
- return error;
+ goto out;
}
if (kcb->kctl == NULL) {
kctlstat.kcs_conn_fail++;
lck_mtx_unlock(ctl_mtx);
}
+out:
+ clt_kcb_decrement_use_count(kcb);
return error;
}
struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
if ((kcb = (struct ctl_cb *)so->so_pcb)) {
+ lck_mtx_t *mtx_held = socket_getlock(so, PR_F_WILLUNLOCK);
+ ctl_kcb_increment_use_count(kcb, mtx_held);
struct kctl *kctl = kcb->kctl;
if (kctl && kctl->disconnect) {
kctlstat.kcs_gencnt++;
lck_mtx_unlock(ctl_mtx);
socket_lock(so, 0);
+ clt_kcb_decrement_use_count(kcb);
}
return 0;
}
static int
ctl_usr_rcvd(struct socket *so, int flags)
{
+ int error = 0;
struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
struct kctl *kctl;
+ if (kcb == NULL) {
+ return ENOTCONN;
+ }
+
+ lck_mtx_t *mtx_held = socket_getlock(so, PR_F_WILLUNLOCK);
+ ctl_kcb_increment_use_count(kcb, mtx_held);
+
if ((kctl = kcb->kctl) == NULL) {
- return EINVAL;
+ error = EINVAL;
+ goto out;
}
if (kctl->rcvd) {
ctl_sbrcv_trim(so);
- return 0;
+out:
+ clt_kcb_decrement_use_count(kcb);
+ return error;
}
static int
error = ENOTCONN;
}
+ lck_mtx_t *mtx_held = socket_getlock(so, PR_F_WILLUNLOCK);
+ ctl_kcb_increment_use_count(kcb, mtx_held);
+
if (error == 0 && (kctl = kcb->kctl) == NULL) {
error = EINVAL;
}
if (error != 0) {
OSIncrementAtomic64((SInt64 *)&kctlstat.kcs_send_fail);
}
+ clt_kcb_decrement_use_count(kcb);
+
return error;
}
error = ENOTCONN;
}
+ lck_mtx_t *mtx_held = socket_getlock(so, PR_F_WILLUNLOCK);
+ ctl_kcb_increment_use_count(kcb, mtx_held);
+
if (error == 0 && (kctl = kcb->kctl) == NULL) {
error = EINVAL;
}
if (error != 0) {
OSIncrementAtomic64((SInt64 *)&kctlstat.kcs_send_list_fail);
}
+ clt_kcb_decrement_use_count(kcb);
+
return error;
}
return EINVAL;
}
+ lck_mtx_t *mtx_held = socket_getlock(so, PR_F_WILLUNLOCK);
+ ctl_kcb_increment_use_count(kcb, mtx_held);
+
switch (sopt->sopt_dir) {
case SOPT_SET:
if (kctl->setopt == NULL) {
- return ENOTSUP;
+ error = ENOTSUP;
+ goto out;
}
if (sopt->sopt_valsize != 0) {
MALLOC(data, void *, sopt->sopt_valsize, M_TEMP,
M_WAITOK | M_ZERO);
if (data == NULL) {
- return ENOMEM;
+ error = ENOMEM;
+ goto out;
}
error = sooptcopyin(sopt, data,
sopt->sopt_valsize, sopt->sopt_valsize);
case SOPT_GET:
if (kctl->getopt == NULL) {
- return ENOTSUP;
+ error = ENOTSUP;
+ goto out;
}
if (sopt->sopt_valsize && sopt->sopt_val) {
MALLOC(data, void *, sopt->sopt_valsize, M_TEMP,
M_WAITOK | M_ZERO);
if (data == NULL) {
- return ENOMEM;
+ error = ENOMEM;
+ goto out;
}
/*
* 4108337 - copy user data in case the
}
break;
}
+
+out:
+ clt_kcb_decrement_use_count(kcb);
return error;
}