Bluetooth: Add extra device reference counting for connections
authorMarcel Holtmann <marcel@holtmann.org>
Sat, 22 Aug 2009 21:19:26 +0000 (14:19 -0700)
committerMarcel Holtmann <marcel@holtmann.org>
Sat, 22 Aug 2009 21:19:26 +0000 (14:19 -0700)
The device model itself has no real usable reference counting at the
moment and this causes problems if parents are deleted before their
children. The device model itself handles the memory details of this
correctly, but the uevent order is not consistent. This causes various
problems for systems like HAL or even X.

So until device_put() does a proper cleanup, the device for Bluetooth
connection will be protected with an extra reference counting to ensure
the correct order of uevents when connections are terminated.

This is not an automatic feature. Higher Bluetooth layers like HIDP or
BNEP should grab this new reference to ensure that their uevents are
send before the ones from the parent device.

Based on a report by Brian Rogers <brian@xyzw.org>

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/hci_core.h
net/bluetooth/hci_conn.c
net/bluetooth/hci_event.c

index c4ca4228b0830bc542d1f1d635987c86d03e3f11..25b8a0345a6aa78a669850b5b3814bd42071078f 100644 (file)
@@ -187,6 +187,7 @@ struct hci_conn {
        struct work_struct work_del;
 
        struct device   dev;
+       atomic_t        devref;
 
        struct hci_dev  *hdev;
        void            *l2cap_data;
@@ -339,6 +340,9 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
 void hci_conn_enter_active_mode(struct hci_conn *conn);
 void hci_conn_enter_sniff_mode(struct hci_conn *conn);
 
+void hci_conn_hold_device(struct hci_conn *conn);
+void hci_conn_put_device(struct hci_conn *conn);
+
 static inline void hci_conn_hold(struct hci_conn *conn)
 {
        atomic_inc(&conn->refcnt);
index fa47d5d84f5c8de7efaa5c404c90436a3b00e239..a9750984f772a164ebb26cb7af1da01e46ea2cd4 100644 (file)
@@ -246,6 +246,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
        if (hdev->notify)
                hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
 
+       atomic_set(&conn->devref, 0);
+
        hci_conn_init_sysfs(conn);
 
        tasklet_enable(&hdev->tx_task);
@@ -288,7 +290,7 @@ int hci_conn_del(struct hci_conn *conn)
 
        skb_queue_purge(&conn->data_q);
 
-       hci_conn_del_sysfs(conn);
+       hci_conn_put_device(conn);
 
        hci_dev_put(hdev);
 
@@ -583,6 +585,19 @@ void hci_conn_check_pending(struct hci_dev *hdev)
        hci_dev_unlock(hdev);
 }
 
+void hci_conn_hold_device(struct hci_conn *conn)
+{
+       atomic_inc(&conn->devref);
+}
+EXPORT_SYMBOL(hci_conn_hold_device);
+
+void hci_conn_put_device(struct hci_conn *conn)
+{
+       if (atomic_dec_and_test(&conn->devref))
+               hci_conn_del_sysfs(conn);
+}
+EXPORT_SYMBOL(hci_conn_put_device);
+
 int hci_get_conn_list(void __user *arg)
 {
        struct hci_conn_list_req req, *cl;
index 184ba0a88ec0883e93b4cd919b9b9c10f66c4fca..e99fe385fba293d6a7fe2c558b7d454f669f52b8 100644 (file)
@@ -887,6 +887,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                } else
                        conn->state = BT_CONNECTED;
 
+               hci_conn_hold_device(conn);
                hci_conn_add_sysfs(conn);
 
                if (test_bit(HCI_AUTH, &hdev->flags))
@@ -1693,6 +1694,7 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu
                conn->handle = __le16_to_cpu(ev->handle);
                conn->state  = BT_CONNECTED;
 
+               hci_conn_hold_device(conn);
                hci_conn_add_sysfs(conn);
                break;
 
This page took 0.028583 seconds and 5 git commands to generate.