Bluetooth: Convert link keys list to use RCU
authorJohan Hedberg <johan.hedberg@intel.com>
Wed, 19 Nov 2014 13:22:22 +0000 (15:22 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Wed, 19 Nov 2014 15:19:47 +0000 (16:19 +0100)
This patch converts the hdev->link_keys list to be protected through
RCU, thereby eliminating the need to hold the hdev lock while accessing
the list.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/hci_core.h
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c

index a805b3d97c0b467d55fc7812c280a61e03771558..396c09840fdffb656e844cc69940ecc94ebb36f3 100644 (file)
@@ -130,6 +130,7 @@ struct smp_irk {
 
 struct link_key {
        struct list_head list;
+       struct rcu_head rcu;
        bdaddr_t bdaddr;
        u8 type;
        u8 val[HCI_LINK_KEY_SIZE];
index a67a4b8e4e1cc4ca859032eb357c21a82a509c3a..5c319a49a5a4ca8e510a28fe553b5c59c74c2a15 100644 (file)
@@ -274,15 +274,13 @@ static const struct file_operations inquiry_cache_fops = {
 static int link_keys_show(struct seq_file *f, void *ptr)
 {
        struct hci_dev *hdev = f->private;
-       struct list_head *p, *n;
+       struct link_key *key;
 
-       hci_dev_lock(hdev);
-       list_for_each_safe(p, n, &hdev->link_keys) {
-               struct link_key *key = list_entry(p, struct link_key, list);
+       rcu_read_lock();
+       list_for_each_entry_rcu(key, &hdev->link_keys, list)
                seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
                           HCI_LINK_KEY_SIZE, key->val, key->pin_len);
-       }
-       hci_dev_unlock(hdev);
+       rcu_read_unlock();
 
        return 0;
 }
@@ -3101,15 +3099,11 @@ void hci_uuids_clear(struct hci_dev *hdev)
 
 void hci_link_keys_clear(struct hci_dev *hdev)
 {
-       struct list_head *p, *n;
-
-       list_for_each_safe(p, n, &hdev->link_keys) {
-               struct link_key *key;
-
-               key = list_entry(p, struct link_key, list);
+       struct link_key *key;
 
-               list_del(p);
-               kfree(key);
+       list_for_each_entry_rcu(key, &hdev->link_keys, list) {
+               list_del_rcu(&key->list);
+               kfree_rcu(key, rcu);
        }
 }
 
@@ -3137,9 +3131,14 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
 {
        struct link_key *k;
 
-       list_for_each_entry(k, &hdev->link_keys, list)
-               if (bacmp(bdaddr, &k->bdaddr) == 0)
+       rcu_read_lock();
+       list_for_each_entry_rcu(k, &hdev->link_keys, list) {
+               if (bacmp(bdaddr, &k->bdaddr) == 0) {
+                       rcu_read_unlock();
                        return k;
+               }
+       }
+       rcu_read_unlock();
 
        return NULL;
 }
@@ -3290,7 +3289,7 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
                key = kzalloc(sizeof(*key), GFP_KERNEL);
                if (!key)
                        return NULL;
-               list_add(&key->list, &hdev->link_keys);
+               list_add_rcu(&key->list, &hdev->link_keys);
        }
 
        BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type);
@@ -3383,8 +3382,8 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
 
        BT_DBG("%s removing %pMR", hdev->name, bdaddr);
 
-       list_del(&key->list);
-       kfree(key);
+       list_del_rcu(&key->list);
+       kfree_rcu(key, rcu);
 
        return 0;
 }
index 54680fd39608cfbf0ec0e67909258d3ed4e35040..bd0a801206651ec17c4767a14e1333b1594c8681 100644 (file)
@@ -3324,8 +3324,8 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
         */
        if (key->type == HCI_LK_DEBUG_COMBINATION &&
            !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) {
-               list_del(&key->list);
-               kfree(key);
+               list_del_rcu(&key->list);
+               kfree_rcu(key, rcu);
        } else if (conn) {
                if (persistent)
                        clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);