--- include/linux/workqueue.h | 1 + kernel/workqueue.c | 23 ++++++++++++++++++++--- net/bluetooth/hci_core.c | 19 +++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -312,6 +312,7 @@ enum { __WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */ __WQ_ORDERED = 1 << 17, /* internal: workqueue is ordered */ __WQ_LEGACY = 1 << 18, /* internal: create*_workqueue() */ + __WQ_DESTROYING = 1 << 19, WQ_MAX_ACTIVE = 512, /* I like 512, better ideas? */ WQ_MAX_UNBOUND_PER_CPU = 4, /* 4 * #cpus for unbound wq */ --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1366,6 +1366,9 @@ static void __queue_work(int cpu, struct unsigned int work_flags; unsigned int req_cpu = cpu; + if (WARN_ON(wq->flags & __WQ_DESTROYING)) + return; + /* * While a work item is PENDING && off queue, a task trying to * steal the PENDING will busy-loop waiting for it to either get @@ -4010,6 +4013,7 @@ void destroy_workqueue(struct workqueue_ int node; /* drain it before proceeding with destruction */ + wq->flags |= __WQ_DESTROYING; drain_workqueue(wq); /* sanity checks */ @@ -4024,9 +4028,22 @@ void destroy_workqueue(struct workqueue_ } } - if (WARN_ON((pwq != wq->dfl_pwq) && (pwq->refcnt > 1)) || - WARN_ON(pwq->nr_active) || - WARN_ON(!list_empty(&pwq->delayed_works))) { + if (WARN_ON((pwq != wq->dfl_pwq) && (pwq->refcnt > 1))) { + pr_info("%s: pwq=%p wq->dfl_pwq=%p pwq->refcnt=%d\n", + __func__, pwq, wq->dfl_pwq, + pwq->refcnt); + mutex_unlock(&wq->mutex); + return; + } + + if (WARN_ON(pwq->nr_active)) { + pr_info("%s: %ps\n", __func__, wq); + mutex_unlock(&wq->mutex); + return; + } + + if (WARN_ON(!list_empty(&pwq->delayed_works))) { + pr_info("%s: %ps\n", __func__, wq); mutex_unlock(&wq->mutex); return; } --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2467,6 +2467,8 @@ static void hci_cmd_timeout(struct work_ struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_timer.work); + if (WARN_ON(hci_dev_test_flag(hdev, HCI_UNREGISTER))) + return; if (hdev->sent_cmd) { struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data; u16 opcode = __le16_to_cpu(sent->opcode); @@ -3225,6 +3227,11 @@ int hci_recv_frame(struct hci_dev *hdev, return -ENXIO; } + if (WARN_ON(hci_dev_test_flag(hdev, HCI_UNREGISTER))) { + kfree_skb(skb); + return -ENXIO; + } + if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT && hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT && hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) { @@ -3248,6 +3255,9 @@ EXPORT_SYMBOL(hci_recv_frame); /* Receive diagnostic message from HCI drivers */ int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb) { + if (WARN_ON(hci_dev_test_flag(hdev, HCI_UNREGISTER))) + return -ENXIO; + /* Mark as diagnostic packet */ hci_skb_pkt_type(skb) = HCI_DIAG_PKT; @@ -3326,6 +3336,9 @@ int hci_send_cmd(struct hci_dev *hdev, _ { struct sk_buff *skb; + if (WARN_ON(hci_dev_test_flag(hdev, HCI_UNREGISTER))) + return -ENXIO; + BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); skb = hci_prepare_cmd(hdev, opcode, plen, param); @@ -3461,6 +3474,9 @@ void hci_send_acl(struct hci_chan *chan, { struct hci_dev *hdev = chan->conn->hdev; + if (WARN_ON(hci_dev_test_flag(hdev, HCI_UNREGISTER))) + return; + BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags); hci_queue_acl(chan, &chan->data_q, skb, flags); @@ -3474,6 +3490,9 @@ void hci_send_sco(struct hci_conn *conn, struct hci_dev *hdev = conn->hdev; struct hci_sco_hdr hdr; + if (WARN_ON(hci_dev_test_flag(hdev, HCI_UNREGISTER))) + return; + BT_DBG("%s len %d", hdev->name, skb->len); hdr.handle = cpu_to_le16(conn->handle);