--- /home/maxx/dfbsd/dfbsd/sys/netbt/hci_link.c 2026-06-29 12:51:19.000000000 +0000 +++ hci_link.c 2026-10-11 12:11:04.653039910 +0000 @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -783,13 +784,41 @@ if (acl == NULL || acl->hl_state != HCI_LINK_OPEN) return NULL; - LIST_FOREACH(pcb, &sco_pcb, sp_next) { - if ((pcb->sp_flags & SP_LISTENING) == 0) - continue; + { + /* Snapshot + pin the listening candidates under the lock: + * newconn() can block (sonewconn) so it must run without + * the spinlock, but the pcb must stay allocated across it. + * XXX 16 simultaneous SCO listeners is beyond any use case. + */ + struct sco_pcb *cand[16]; + int n = 0, i; + + new = NULL; + + spin_lock(&sco_pcblock); + LIST_FOREACH(pcb, &sco_pcb, sp_next) { + if ((pcb->sp_flags & SP_LISTENING) == 0) + continue; + if (n == (int)NELEM(cand)) + break; + sco_pcb_hold(pcb); + cand[n++] = pcb; + } + spin_unlock(&sco_pcblock); - new = (*pcb->sp_proto->newconn)(pcb->sp_upper, &laddr, &raddr); + for (i = 0; i < n; i++) { + pcb = cand[i]; + new = (*pcb->sp_proto->newconn)(pcb->sp_upper, + &laddr, &raddr); + if (new != NULL) + break; + sco_pcb_rele(pcb); + } if (new == NULL) - continue; + return NULL; + + for (; i < n; i++) + sco_pcb_rele(cand[i]); /* * Ok, got new pcb so we can start a new link and fill --- /home/maxx/dfbsd/dfbsd/sys/netbt/sco.h 2026-06-29 12:51:19.000000000 +0000 +++ sco.h 2026-10-11 11:32:46.290598700 +0000 @@ -43,6 +43,7 @@ */ struct sco_pcb { struct hci_link *sp_link; /* SCO link */ + int sp_refcnt; /* list ref + walker pins */ unsigned int sp_flags; /* flags */ bdaddr_t sp_laddr; /* local address */ bdaddr_t sp_raddr; /* remote address */ @@ -57,6 +58,9 @@ LIST_HEAD(sco_pcb_list, sco_pcb); extern struct sco_pcb_list sco_pcb; +extern struct spinlock sco_pcblock; +void sco_pcb_hold(struct sco_pcb *); +void sco_pcb_rele(struct sco_pcb *); /* sp_flags */ #define SP_LISTENING (1<<0) /* is listening pcb */ --- /home/maxx/dfbsd/dfbsd/sys/netbt/sco_upper.c 2026-06-29 12:51:19.000000000 +0000 +++ sco_upper.c 2026-10-11 11:58:19.242892084 +0000 @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,21 @@ */ struct sco_pcb_list sco_pcb = LIST_HEAD_INITIALIZER(sco_pcb); +struct spinlock sco_pcblock = SPINLOCK_INITIALIZER(sco_pcblock, "sco_pcblock"); + +void +sco_pcb_hold(struct sco_pcb *pcb) +{ + /* atomic: races rele() which runs without the list lock */ + atomic_add_int(&pcb->sp_refcnt, 1); +} + +void +sco_pcb_rele(struct sco_pcb *pcb) +{ + if (atomic_fetchadd_int(&pcb->sp_refcnt, -1) == 1) + kfree(pcb, M_BLUETOOTH); +} /* * sco_attach(handle, proto, upper) @@ -71,10 +87,13 @@ if (pcb == NULL) return ENOMEM; + pcb->sp_refcnt = 1; /* the list's reference */ pcb->sp_proto = proto; pcb->sp_upper = upper; + spin_lock(&sco_pcblock); LIST_INSERT_HEAD(&sco_pcb, pcb, sp_next); + spin_unlock(&sco_pcblock); *handle = pcb; return 0; @@ -242,8 +261,10 @@ pcb->sp_link = NULL; } + spin_lock(&sco_pcblock); LIST_REMOVE(pcb, sp_next); - kfree(pcb, M_BLUETOOTH); + spin_unlock(&sco_pcblock); + sco_pcb_rele(pcb); /* may defer the kfree to the last walker */ return 0; }