Bluetooth: Hold ref on hci_conn when setting up A2MP fixed channel
Take a reference on the hci_conn and do not de-reference l2cap_conn
while setting up the A2MP fixed channel. l2cap_conn is not reference
counted and may go away before the channel is set up.
This fixes scenario where the ACL disconnects (and l2cap_conn goes
away) while amp_conn_ind worker is running or is on the workqueue
waiting to run.
Change-Id: I10fc6d9b146fcc5e010f26a046f7e0570f2b93dd
CRs-fixed: 347079
Signed-off-by: Peter Krystad <pkrystad@codeaurora.org>
diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h
index 0a2849a..ec517b0 100644
--- a/include/net/bluetooth/amp.h
+++ b/include/net/bluetooth/amp.h
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2010-2011 Code Aurora Forum. All rights reserved.
+ Copyright (c) 2010-2012 Code Aurora Forum. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 and
@@ -115,7 +115,7 @@
void amp_exit(void);
/* L2CAP-AMP fixed channel interface */
-void amp_conn_ind(struct l2cap_conn *conn, struct sk_buff *skb);
+void amp_conn_ind(struct hci_conn *hcon, struct sk_buff *skb);
/* L2CAP-AMP link interface */
void amp_create_physical(struct l2cap_conn *conn, struct sock *sk);
@@ -256,7 +256,7 @@
};
struct amp_work_conn_ind {
struct work_struct work;
- struct l2cap_conn *conn;
+ struct hci_conn *hcon;
struct sk_buff *skb;
};
struct amp_work_create_physical {
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
index 0a3b91d..df80c42 100644
--- a/net/bluetooth/amp.c
+++ b/net/bluetooth/amp.c
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2010-2011 Code Aurora Forum. All rights reserved.
+ Copyright (c) 2010-2012 Code Aurora Forum. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 and
@@ -87,15 +87,15 @@
return found;
}
-static struct amp_mgr *get_create_amp_mgr(struct l2cap_conn *conn,
+static struct amp_mgr *get_create_amp_mgr(struct hci_conn *hcon,
struct sk_buff *skb)
{
struct amp_mgr *mgr;
write_lock(&_mgr_list_lock);
list_for_each_entry(mgr, &_mgr_list, list) {
- if (mgr->l2cap_conn == conn) {
- BT_DBG("conn %p found %p", conn, mgr);
+ if (mgr->l2cap_conn == hcon->l2cap_data) {
+ BT_DBG("found %p", mgr);
write_unlock(&_mgr_list_lock);
goto gc_finished;
}
@@ -106,13 +106,13 @@
if (!mgr)
return NULL;
- mgr->l2cap_conn = conn;
+ mgr->l2cap_conn = hcon->l2cap_data;
mgr->next_ident = 1;
INIT_LIST_HEAD(&mgr->ctx_list);
rwlock_init(&mgr->ctx_list_lock);
mgr->skb = skb;
- BT_DBG("conn %p mgr %p", conn, mgr);
- mgr->a2mp_sock = open_fixed_channel(conn->src, conn->dst);
+ BT_DBG("hcon %p mgr %p", hcon, mgr);
+ mgr->a2mp_sock = open_fixed_channel(&hcon->hdev->bdaddr, &hcon->dst);
if (!mgr->a2mp_sock) {
kfree(mgr);
return NULL;
@@ -484,7 +484,7 @@
struct amp_ctx *ctx = NULL;
BT_DBG("conn %p", conn);
- mgr = get_create_amp_mgr(conn, NULL);
+ mgr = get_create_amp_mgr(conn->hcon, NULL);
if (!mgr)
goto cp_finished;
BT_DBG("mgr %p", mgr);
@@ -514,7 +514,7 @@
if (!hdev)
goto ap_finished;
BT_DBG("hdev %p", hdev);
- mgr = get_create_amp_mgr(lcon, NULL);
+ mgr = get_create_amp_mgr(lcon->hcon, NULL);
if (!mgr)
goto ap_finished;
BT_DBG("mgr %p", mgr);
@@ -1773,12 +1773,13 @@
static void conn_ind_worker(struct work_struct *w)
{
struct amp_work_conn_ind *work = (struct amp_work_conn_ind *) w;
- struct l2cap_conn *conn = work->conn;
+ struct hci_conn *hcon = work->hcon;
struct sk_buff *skb = work->skb;
struct amp_mgr *mgr;
- mgr = get_create_amp_mgr(conn, skb);
+ mgr = get_create_amp_mgr(hcon, skb);
BT_DBG("mgr %p", mgr);
+ hci_conn_put(hcon);
kfree(work);
}
@@ -1804,17 +1805,20 @@
/* L2CAP Fixed Channel interface */
-void amp_conn_ind(struct l2cap_conn *conn, struct sk_buff *skb)
+void amp_conn_ind(struct hci_conn *hcon, struct sk_buff *skb)
{
struct amp_work_conn_ind *work;
- BT_DBG("conn %p, skb %p", conn, skb);
+ BT_DBG("hcon %p, skb %p", hcon, skb);
work = kmalloc(sizeof(*work), GFP_ATOMIC);
if (work) {
INIT_WORK((struct work_struct *) work, conn_ind_worker);
- work->conn = conn;
+ hci_conn_hold(hcon);
+ work->hcon = hcon;
work->skb = skb;
- if (queue_work(amp_workqueue, (struct work_struct *) work) == 0)
+ if (!queue_work(amp_workqueue, (struct work_struct *) work)) {
+ hci_conn_put(hcon);
kfree(work);
+ }
}
}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 0ad64a0..739b04c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -7361,7 +7361,7 @@
bh_unlock_sock(sk);
} else if (cid == L2CAP_CID_A2MP) {
BT_DBG("A2MP");
- amp_conn_ind(conn, skb);
+ amp_conn_ind(conn->hcon, skb);
} else {
BT_DBG("unknown cid 0x%4.4x", cid);
kfree_skb(skb);