From 58921a2040d45ebb21aec653ca9867ba79c88e88 Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Wed, 16 Feb 2011 10:43:20 +0100 Subject: Bluetooth: Add counter for not acked HCI commands Adds counter for HCI commands which were sent but are not yet acked. This is to prevent race conditions in scenarios where HCI commands are sent between complete event and command status event, i.e. last sent HCI command is not accounted in credits number returned by command status event. ST-Ericsson Linux next: not tested, ER? ST-Ericsson ID: 323271 ST-Ericsson FOSS-OUT-ID: Trivial Change-Id: I74d1f31ab79ee07406858451c94273301eee6e64 Signed-off-by: Andrzej Kaczmarek Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/16344 Reviewed-by: Lukasz RYMANOWSKI Reviewed-by: Par-Gunnar HJALMDAHL Reviewed-by: Henrik POSSUNG Reviewed-by: Srinidhi KASAGAR --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 30 ++++++++++++++++++++---------- net/bluetooth/hci_event.c | 6 ++++++ 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 96e7d466b9e..d3da8048652 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -95,6 +95,7 @@ struct hci_dev { unsigned long quirks; atomic_t cmd_cnt; + atomic_t cmd_not_ack; unsigned int acl_cnt; unsigned int sco_cnt; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2f768de8701..d51b79aaba1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -503,6 +503,7 @@ int hci_dev_open(__u16 dev) if (!test_bit(HCI_RAW, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); + atomic_set(&hdev->cmd_not_ack, 0); set_bit(HCI_INIT, &hdev->flags); //__hci_request(hdev, hci_reset_req, 0, HZ); @@ -572,6 +573,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Reset device */ skb_queue_purge(&hdev->cmd_q); atomic_set(&hdev->cmd_cnt, 1); + atomic_set(&hdev->cmd_not_ack, 0); if (!test_bit(HCI_RAW, &hdev->flags)) { set_bit(HCI_INIT, &hdev->flags); __hci_request(hdev, hci_reset_req, 0, @@ -645,6 +647,7 @@ int hci_dev_reset(__u16 dev) hdev->flush(hdev); atomic_set(&hdev->cmd_cnt, 1); + atomic_set(&hdev->cmd_not_ack, 0); hdev->acl_cnt = 0; hdev->sco_cnt = 0; if (!test_bit(HCI_RAW, &hdev->flags)) @@ -1633,24 +1636,31 @@ static void hci_cmd_task(unsigned long arg) struct hci_dev *hdev = (struct hci_dev *) arg; struct sk_buff *skb; - BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt)); + BT_DBG("%s cnt %d not_ack %d", hdev->name, atomic_read(&hdev->cmd_cnt), + atomic_read(&hdev->cmd_not_ack)); if (!atomic_read(&hdev->cmd_cnt) && time_after(jiffies, hdev->cmd_last_tx + HZ)) { BT_ERR("%s command tx timeout", hdev->name); atomic_set(&hdev->cmd_cnt, 1); + atomic_add_unless(&hdev->cmd_not_ack, -1, 0); } /* Send queued commands */ - if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) { - kfree_skb(hdev->sent_cmd); + if (atomic_read(&hdev->cmd_cnt) > atomic_read(&hdev->cmd_not_ack)) { + skb = skb_dequeue(&hdev->cmd_q); + if (skb) { + kfree_skb(hdev->sent_cmd); - if ((hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC))) { - atomic_dec(&hdev->cmd_cnt); - hci_send_frame(skb); - hdev->cmd_last_tx = jiffies; - } else { - skb_queue_head(&hdev->cmd_q, skb); - tasklet_schedule(&hdev->cmd_task); + hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC); + if (hdev->sent_cmd) { + atomic_dec(&hdev->cmd_cnt); + atomic_inc(&hdev->cmd_not_ack); + hci_send_frame(skb); + hdev->cmd_last_tx = jiffies; + } else { + skb_queue_head(&hdev->cmd_q, skb); + tasklet_schedule(&hdev->cmd_task); + } } } } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 23bedb7467c..0cb609ad72b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1319,6 +1319,9 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk break; } + if (opcode != 0x0000) + atomic_add_unless(&hdev->cmd_not_ack, -1, 0); + if (ev->ncmd) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) @@ -1385,6 +1388,9 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) break; } + if (opcode != 0x0000) + atomic_add_unless(&hdev->cmd_not_ack, -1, 0); + if (ev->ncmd) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) -- cgit v1.2.3