--- a/sys/netgraph7/bluetooth/socket/ng_btsocket_rfcomm.c +++ b/sys/netgraph7/bluetooth/socket/ng_btsocket_rfcomm.c @@ -3549,29 +3549,26 @@ static void ng_btsocket_rfcomm_process_timeout(void *xpcb) { ng_btsocket_rfcomm_pcb_p pcb = (ng_btsocket_rfcomm_pcb_p) xpcb; - lockmgr(&pcb->pcb_lock, LK_EXCLUSIVE); + /* + * DF-3476: do NOT block on pcb_lock here. This is a callout + * callback: while we would be blocked waiting for pcb_lock, + * CALLOUT_INPROG stays set, and any pcb_lock holder calling + * ng_btsocket_rfcomm_untimeout() -> callout_stop() (synchronous) + * sleeps forever waiting for this callback to return. That is an + * AB-BA deadlock (softclock thread vs task/user thread). + * + * Instead, only set the TIMEDOUT flag (u_int16_t store) and let + * the RFCOMM task do all state processing under pcb_lock - it + * already handles TIMEDOUT in ng_btsocket_rfcomm_session_process_pcb() + * for every state that arms a timeout. If untimeout() won the race + * (TIMO already cleared), do nothing: the DLC was killed/stopped + * before we fired. + */ + if (!(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)) + return; pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMO; pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT; - switch (pcb->state) { - case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING: - case NG_BTSOCKET_RFCOMM_DLC_CONNECTING: - pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING; - break; - - case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT: - case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING: - break; - - default: - panic( - "%s: DLC timeout in invalid state, dlci=%d, state=%d, flags=%#x\n", - __func__, pcb->dlci, pcb->state, pcb->flags); - break; - } + /* CONFIGURING/CONNECTING -> DISCONNECTING transition is done by + * the task (process_pcb) when it sees TIMEDOUT under pcb_lock; + * add the transition there to preserve behaviour. */ ng_btsocket_rfcomm_task_wakeup(); - - lockmgr(&pcb->pcb_lock, LK_RELEASE); } *** NOTE: the matching task-side addition (in process_pcb, under pcb_lock): *** case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING: *** case NG_BTSOCKET_RFCOMM_DLC_CONNECTING: *** if (pcb->flags & TIMEDOUT) *** pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING; (or kill) *** break;