Bluetooth: l2cap: introduce l2cap_conn ref-counting
[deliverable/linux.git] / net / bluetooth / l2cap_core.c
index 7c7e9321f1ea4263e0c51e792a14e0454d4a0e12..be9ad89339cd513f1f556072044e095f332c7b78 100644 (file)
@@ -571,7 +571,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
                chan->conn = NULL;
 
                if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP)
-                       hci_conn_put(conn->hcon);
+                       hci_conn_drop(conn->hcon);
 
                if (mgr && mgr->bredr_chan == chan)
                        mgr->bredr_chan = NULL;
@@ -1486,7 +1486,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
        }
 
        hcon->l2cap_data = NULL;
-       kfree(conn);
+       conn->hchan = NULL;
+       l2cap_conn_put(conn);
 }
 
 static void security_timeout(struct work_struct *work)
@@ -1502,12 +1503,12 @@ static void security_timeout(struct work_struct *work)
        }
 }
 
-static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
+static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
 {
        struct l2cap_conn *conn = hcon->l2cap_data;
        struct hci_chan *hchan;
 
-       if (conn || status)
+       if (conn)
                return conn;
 
        hchan = hci_chan_create(hcon);
@@ -1520,8 +1521,10 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
                return NULL;
        }
 
+       kref_init(&conn->ref);
        hcon->l2cap_data = conn;
        conn->hcon = hcon;
+       hci_conn_get(conn->hcon);
        conn->hchan = hchan;
 
        BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
@@ -1558,6 +1561,26 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
        return conn;
 }
 
+static void l2cap_conn_free(struct kref *ref)
+{
+       struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
+
+       hci_conn_put(conn->hcon);
+       kfree(conn);
+}
+
+void l2cap_conn_get(struct l2cap_conn *conn)
+{
+       kref_get(&conn->ref);
+}
+EXPORT_SYMBOL(l2cap_conn_get);
+
+void l2cap_conn_put(struct l2cap_conn *conn)
+{
+       kref_put(&conn->ref, l2cap_conn_free);
+}
+EXPORT_SYMBOL(l2cap_conn_put);
+
 /* ---- Socket interface ---- */
 
 /* Find socket with psm and source / destination bdaddr.
@@ -1695,9 +1718,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                goto done;
        }
 
-       conn = l2cap_conn_add(hcon, 0);
+       conn = l2cap_conn_add(hcon);
        if (!conn) {
-               hci_conn_put(hcon);
+               hci_conn_drop(hcon);
                err = -ENOMEM;
                goto done;
        }
@@ -1707,7 +1730,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
 
                if (!list_empty(&conn->chan_l)) {
                        err = -EBUSY;
-                       hci_conn_put(hcon);
+                       hci_conn_drop(hcon);
                }
 
                if (err)
@@ -6313,7 +6336,7 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
        BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
 
        if (!status) {
-               conn = l2cap_conn_add(hcon, status);
+               conn = l2cap_conn_add(hcon);
                if (conn)
                        l2cap_conn_ready(conn);
        } else {
@@ -6482,7 +6505,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
                goto drop;
 
        if (!conn)
-               conn = l2cap_conn_add(hcon, 0);
+               conn = l2cap_conn_add(hcon);
 
        if (!conn)
                goto drop;
This page took 0.030216 seconds and 5 git commands to generate.