Skip to content

Commit

Permalink
Bluetooth: Convert IRK list to RCU
Browse files Browse the repository at this point in the history
This patch set converts the hdev->identity_resolving_keys list to use
RCU to eliminate the need to use hci_dev_lock/unlock.

An additional change that must be done is to remove use of
CRYPTO_ALG_ASYNC for the hdev-specific AES crypto context. The reason is
that this context is used for matching RPAs and the loop that does the
matching is under the RCU read lock, i.e. is an atomic section which
cannot sleep.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
  • Loading branch information
Johan Hedberg authored and Marcel Holtmann committed Nov 15, 2014
1 parent 970d0f1 commit adae20c
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 24 deletions.
1 change: 1 addition & 0 deletions include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ struct smp_ltk {

struct smp_irk {
struct list_head list;
struct rcu_head rcu;
bdaddr_t rpa;
bdaddr_t bdaddr;
u8 addr_type;
Expand Down
46 changes: 27 additions & 19 deletions net/bluetooth/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -748,16 +748,15 @@ static const struct file_operations white_list_fops = {
static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
struct list_head *p, *n;
struct smp_irk *irk;

hci_dev_lock(hdev);
list_for_each_safe(p, n, &hdev->identity_resolving_keys) {
struct smp_irk *irk = list_entry(p, struct smp_irk, list);
rcu_read_lock();
list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
&irk->bdaddr, irk->addr_type,
16, irk->val, &irk->rpa);
}
hci_dev_unlock(hdev);
rcu_read_unlock();

return 0;
}
Expand Down Expand Up @@ -3114,11 +3113,11 @@ void hci_smp_ltks_clear(struct hci_dev *hdev)

void hci_smp_irks_clear(struct hci_dev *hdev)
{
struct smp_irk *k, *tmp;
struct smp_irk *k;

list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
list_del(&k->list);
kfree(k);
list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
list_del_rcu(&k->list);
kfree_rcu(k, rcu);
}
}

Expand Down Expand Up @@ -3221,17 +3220,22 @@ struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa)
{
struct smp_irk *irk;

list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
if (!bacmp(&irk->rpa, rpa))
rcu_read_lock();
list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
if (!bacmp(&irk->rpa, rpa)) {
rcu_read_unlock();
return irk;
}
}

list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
if (smp_irk_matches(hdev, irk->val, rpa)) {
bacpy(&irk->rpa, rpa);
rcu_read_unlock();
return irk;
}
}
rcu_read_unlock();

return NULL;
}
Expand All @@ -3245,11 +3249,15 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
if (addr_type == ADDR_LE_DEV_RANDOM && (bdaddr->b[5] & 0xc0) != 0xc0)
return NULL;

list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
rcu_read_lock();
list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
if (addr_type == irk->addr_type &&
bacmp(bdaddr, &irk->bdaddr) == 0)
bacmp(bdaddr, &irk->bdaddr) == 0) {
rcu_read_unlock();
return irk;
}
}
rcu_read_unlock();

return NULL;
}
Expand Down Expand Up @@ -3344,7 +3352,7 @@ struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
bacpy(&irk->bdaddr, bdaddr);
irk->addr_type = addr_type;

list_add(&irk->list, &hdev->identity_resolving_keys);
list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
}

memcpy(irk->val, val, 16);
Expand Down Expand Up @@ -3390,16 +3398,16 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)

void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type)
{
struct smp_irk *k, *tmp;
struct smp_irk *k;

list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type)
continue;

BT_DBG("%s removing %pMR", hdev->name, bdaddr);

list_del(&k->list);
kfree(k);
list_del_rcu(&k->list);
kfree_rcu(k, rcu);
}
}

Expand Down
10 changes: 5 additions & 5 deletions net/bluetooth/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,8 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
}

if (smp->remote_irk) {
list_del(&smp->remote_irk->list);
kfree(smp->remote_irk);
list_del_rcu(&smp->remote_irk->list);
kfree_rcu(smp->remote_irk, rcu);
}
}

Expand Down Expand Up @@ -655,8 +655,8 @@ static void smp_notify_keys(struct l2cap_conn *conn)
* just remove it.
*/
if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
list_del(&smp->remote_irk->list);
kfree(smp->remote_irk);
list_del_rcu(&smp->remote_irk->list);
kfree_rcu(smp->remote_irk, rcu);
smp->remote_irk = NULL;
}
}
Expand Down Expand Up @@ -1696,7 +1696,7 @@ int smp_register(struct hci_dev *hdev)

BT_DBG("%s", hdev->name);

tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0);
if (IS_ERR(tfm_aes)) {
int err = PTR_ERR(tfm_aes);
BT_ERR("Unable to create crypto context");
Expand Down

0 comments on commit adae20c

Please sign in to comment.