]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_control.c
xnu-6153.101.6.tar.gz
[apple/xnu.git] / bsd / kern / kern_control.c
index e41d1f103d8528c6e6fe6a870a0bb5bde632ca56..5430ff820668713051fcbbdfc64c29e9d230065a 100644 (file)
@@ -85,6 +85,14 @@ struct kctl {
        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;
@@ -94,6 +102,10 @@ struct ctl_cb {
        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
@@ -225,6 +237,12 @@ u_int32_t ctl_debug = 0;
 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;
@@ -353,24 +371,45 @@ ctl_sofreelastref(struct socket *so)
 }
 
 /*
- * 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
@@ -384,6 +423,7 @@ ctl_detach(struct socket *so)
 
        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)) {
@@ -398,8 +438,12 @@ ctl_detach(struct socket *so)
        }
 
        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;
 }
 
@@ -526,6 +570,9 @@ ctl_setup_kctl(struct socket *so, struct sockaddr *nam, struct proc *p)
 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;
@@ -550,6 +597,7 @@ ctl_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
 
        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) {
@@ -570,7 +618,8 @@ ctl_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
        socket_lock(so, 0);
 
 out:
-       clt_kcb_decrement_use_count(kcb);
+       ctl_kcb_done_clearing(kcb);
+       ctl_kcb_decrement_use_count(kcb);
        return error;
 }
 
@@ -586,6 +635,14 @@ ctl_connect(struct socket *so, struct sockaddr *nam, struct proc *p)
 
        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) {
@@ -604,6 +661,9 @@ ctl_connect(struct socket *so, struct sockaddr *nam, struct proc *p)
                goto end;
        }
        soisconnected(so);
+#if DEVELOPMENT || DEBUG
+       kcb->status = KCTL_CONNECTED;
+#endif /* DEVELOPMENT || DEBUG */
 
 end:
        if (error && kcb->kctl->disconnect) {
@@ -622,6 +682,9 @@ end:
        }
        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;
@@ -632,7 +695,8 @@ end:
                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;
 }
 
@@ -644,6 +708,7 @@ ctl_disconnect(struct socket *so)
        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) {
@@ -654,6 +719,9 @@ ctl_disconnect(struct socket *so)
                }
 
                soisdisconnected(so);
+#if DEVELOPMENT || DEBUG
+               kcb->status = KCTL_DISCONNECTED;
+#endif /* DEVELOPMENT || DEBUG */
 
                socket_unlock(so, 0);
                lck_mtx_lock(ctl_mtx);
@@ -667,7 +735,8 @@ ctl_disconnect(struct socket *so)
                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;
 }
@@ -759,7 +828,7 @@ ctl_usr_rcvd(struct socket *so, int flags)
        ctl_sbrcv_trim(so);
 
 out:
-       clt_kcb_decrement_use_count(kcb);
+       ctl_kcb_decrement_use_count(kcb);
        return error;
 }
 
@@ -803,7 +872,7 @@ ctl_send(struct socket *so, int flags, struct mbuf *m,
        if (error != 0) {
                OSIncrementAtomic64((SInt64 *)&kctlstat.kcs_send_fail);
        }
-       clt_kcb_decrement_use_count(kcb);
+       ctl_kcb_decrement_use_count(kcb);
 
        return error;
 }
@@ -867,7 +936,7 @@ ctl_send_list(struct socket *so, int flags, struct mbuf *m,
        if (error != 0) {
                OSIncrementAtomic64((SInt64 *)&kctlstat.kcs_send_list_fail);
        }
-       clt_kcb_decrement_use_count(kcb);
+       ctl_kcb_decrement_use_count(kcb);
 
        return error;
 }
@@ -1376,7 +1445,7 @@ ctl_ctloutput(struct socket *so, struct sockopt *sopt)
        }
 
 out:
-       clt_kcb_decrement_use_count(kcb);
+       ctl_kcb_decrement_use_count(kcb);
        return error;
 }