u_int32_t lastunit;
};
+#if DEVELOPMENT || DEBUG
+enum ctl_status {
+ KCTL_DISCONNECTED = 0,
+ KCTL_CONNECTING = 1,
+ KCTL_CONNECTED = 2
+};
+#endif /* DEVELOPMENT || DEBUG */
+
struct ctl_cb {
TAILQ_ENTRY(ctl_cb) next; /* controller chain */
lck_mtx_t *mtx;
struct sockaddr_ctl sac;
u_int32_t usecount;
u_int32_t kcb_usecount;
+ u_int32_t require_clearing_count;
+#if DEVELOPMENT || DEBUG
+ enum ctl_status status;
+#endif /* DEVELOPMENT || DEBUG */
};
#ifndef ROUNDUP64
SYSCTL_INT(_net_systm_kctl, OID_AUTO, debug,
CTLFLAG_RW | CTLFLAG_LOCKED, &ctl_debug, 0, "");
+#if DEVELOPMENT || DEBUG
+u_int32_t ctl_panic_debug = 0;
+SYSCTL_INT(_net_systm_kctl, OID_AUTO, panicdebug,
+ CTLFLAG_RW | CTLFLAG_LOCKED, &ctl_panic_debug, 0, "");
+#endif /* DEVELOPMENT || DEBUG */
+
#define KCTL_TBL_INC 16
static uintptr_t kctl_tbl_size = 0;
}
/*
- * Use this function to serialize calls into the kctl subsystem
+ * Use this function and ctl_kcb_require_clearing to serialize
+ * critical 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) {
+ while (kcb->require_clearing_count > 0) {
+ msleep(&kcb->require_clearing_count, mutex_held, PSOCK | PCATCH, "kcb_require_clearing", NULL);
+ }
+ kcb->kcb_usecount++;
+}
+
+static void
+ctl_kcb_require_clearing(struct ctl_cb *kcb, lck_mtx_t *mutex_held)
+{
+ assert(kcb->kcb_usecount != 0);
+ kcb->require_clearing_count++;
+ kcb->kcb_usecount--;
+ while (kcb->kcb_usecount > 0) { // we need to wait until no one else is running
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)
+ctl_kcb_done_clearing(struct ctl_cb *kcb)
+{
+ assert(kcb->require_clearing_count != 0);
+ kcb->require_clearing_count--;
+ wakeup((caddr_t)&kcb->require_clearing_count);
+}
+
+static void
+ctl_kcb_decrement_use_count(struct ctl_cb *kcb)
{
assert(kcb->kcb_usecount != 0);
kcb->kcb_usecount--;
- wakeup_one((caddr_t)&kcb->kcb_usecount);
+ wakeup((caddr_t)&kcb->kcb_usecount);
}
static int
lck_mtx_t *mtx_held = socket_getlock(so, PR_F_WILLUNLOCK);
ctl_kcb_increment_use_count(kcb, mtx_held);
+ ctl_kcb_require_clearing(kcb, mtx_held);
if (kcb->kctl != NULL && kcb->kctl->bind != NULL &&
kcb->userdata != NULL && !(so->so_state & SS_ISCONNECTED)) {
}
soisdisconnected(so);
+#if DEVELOPMENT || DEBUG
+ kcb->status = KCTL_DISCONNECTED;
+#endif /* DEVELOPMENT || DEBUG */
so->so_flags |= SOF_PCBCLEARING;
- clt_kcb_decrement_use_count(kcb);
+ ctl_kcb_done_clearing(kcb);
+ ctl_kcb_decrement_use_count(kcb);
return 0;
}
done:
if (error) {
soisdisconnected(so);
+#if DEVELOPMENT || DEBUG
+ kcb->status = KCTL_DISCONNECTED;
+#endif /* DEVELOPMENT || DEBUG */
lck_mtx_lock(ctl_mtx);
TAILQ_REMOVE(&kctl->kcb_head, kcb, next);
kcb->kctl = NULL;
lck_mtx_t *mtx_held = socket_getlock(so, PR_F_WILLUNLOCK);
ctl_kcb_increment_use_count(kcb, mtx_held);
+ ctl_kcb_require_clearing(kcb, mtx_held);
error = ctl_setup_kctl(so, nam, p);
if (error) {
socket_lock(so, 0);
out:
- clt_kcb_decrement_use_count(kcb);
+ ctl_kcb_done_clearing(kcb);
+ ctl_kcb_decrement_use_count(kcb);
return error;
}
lck_mtx_t *mtx_held = socket_getlock(so, PR_F_WILLUNLOCK);
ctl_kcb_increment_use_count(kcb, mtx_held);
+ ctl_kcb_require_clearing(kcb, mtx_held);
+
+#if DEVELOPMENT || DEBUG
+ if (kcb->status != KCTL_DISCONNECTED && ctl_panic_debug) {
+ panic("kctl already connecting/connected");
+ }
+ kcb->status = KCTL_CONNECTING;
+#endif /* DEVELOPMENT || DEBUG */
error = ctl_setup_kctl(so, nam, p);
if (error) {
goto end;
}
soisconnected(so);
+#if DEVELOPMENT || DEBUG
+ kcb->status = KCTL_CONNECTED;
+#endif /* DEVELOPMENT || DEBUG */
end:
if (error && kcb->kctl->disconnect) {
}
if (error) {
soisdisconnected(so);
+#if DEVELOPMENT || DEBUG
+ kcb->status = KCTL_DISCONNECTED;
+#endif /* DEVELOPMENT || DEBUG */
lck_mtx_lock(ctl_mtx);
TAILQ_REMOVE(&kcb->kctl->kcb_head, kcb, next);
kcb->kctl = NULL;
lck_mtx_unlock(ctl_mtx);
}
out:
- clt_kcb_decrement_use_count(kcb);
+ ctl_kcb_done_clearing(kcb);
+ ctl_kcb_decrement_use_count(kcb);
return error;
}
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);
+ ctl_kcb_require_clearing(kcb, mtx_held);
struct kctl *kctl = kcb->kctl;
if (kctl && kctl->disconnect) {
}
soisdisconnected(so);
+#if DEVELOPMENT || DEBUG
+ kcb->status = KCTL_DISCONNECTED;
+#endif /* DEVELOPMENT || DEBUG */
socket_unlock(so, 0);
lck_mtx_lock(ctl_mtx);
kctlstat.kcs_gencnt++;
lck_mtx_unlock(ctl_mtx);
socket_lock(so, 0);
- clt_kcb_decrement_use_count(kcb);
+ ctl_kcb_done_clearing(kcb);
+ ctl_kcb_decrement_use_count(kcb);
}
return 0;
}
ctl_sbrcv_trim(so);
out:
- clt_kcb_decrement_use_count(kcb);
+ ctl_kcb_decrement_use_count(kcb);
return error;
}
if (error != 0) {
OSIncrementAtomic64((SInt64 *)&kctlstat.kcs_send_fail);
}
- clt_kcb_decrement_use_count(kcb);
+ ctl_kcb_decrement_use_count(kcb);
return error;
}
if (error != 0) {
OSIncrementAtomic64((SInt64 *)&kctlstat.kcs_send_list_fail);
}
- clt_kcb_decrement_use_count(kcb);
+ ctl_kcb_decrement_use_count(kcb);
return error;
}
}
out:
- clt_kcb_decrement_use_count(kcb);
+ ctl_kcb_decrement_use_count(kcb);
return error;
}