1
0
Fork 0

Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth

Johan Hedberg says:

====================
pull request: bluetooth 2015-10-16

First of all, sorry for the late set of patches for the 4.3 cycle. We
just finished an intensive week of testing at the Bluetooth UnPlugFest
and discovered (and fixed) issues there. Unfortunately a few issues
affect 4.3-rc5 in a way that they break existing Bluetooth LE mouse and
keyboard support.

The regressions result from supporting LE privacy in conjunction with
scanning for Resolvable Private Addresses before connecting. A feature
that has been tested heavily (including automated unit tests), but sadly
some regressions slipped in. The UnPlugFest with its multitude of test
platforms is a good battle testing ground for uncovering every corner
case.

The patches in this pull request focus only on fixing the regressions in
4.3-rc5. The patches look a bit larger since we also added comments in
the critical sections of the fixes to improve clarity.

I would appreciate if we can get these regression fixes to Linus
quickly. Please let me know if there are any issues pulling. Thanks.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
hifive-unleashed-5.1
David S. Miller 2015-10-18 22:23:33 -07:00
commit a5d6f7dd30
4 changed files with 99 additions and 42 deletions

View File

@ -91,10 +91,50 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn)
* autoconnect action, remove them completely. If they are, just unmark
* them as waiting for connection, by clearing explicit_connect field.
*/
if (params->auto_connect == HCI_AUTO_CONN_EXPLICIT)
params->explicit_connect = false;
list_del_init(&params->action);
switch (params->auto_connect) {
case HCI_AUTO_CONN_EXPLICIT:
hci_conn_params_del(conn->hdev, bdaddr, bdaddr_type);
else
params->explicit_connect = false;
/* return instead of break to avoid duplicate scan update */
return;
case HCI_AUTO_CONN_DIRECT:
case HCI_AUTO_CONN_ALWAYS:
list_add(&params->action, &conn->hdev->pend_le_conns);
break;
case HCI_AUTO_CONN_REPORT:
list_add(&params->action, &conn->hdev->pend_le_reports);
break;
default:
break;
}
hci_update_background_scan(conn->hdev);
}
static void hci_conn_cleanup(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
hci_chan_list_flush(conn);
hci_conn_hash_del(hdev, conn);
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
hci_conn_del_sysfs(conn);
debugfs_remove_recursive(conn->debugfs);
hci_dev_put(hdev);
hci_conn_put(conn);
}
/* This function requires the caller holds hdev->lock */
@ -102,8 +142,13 @@ static void hci_connect_le_scan_remove(struct hci_conn *conn)
{
hci_connect_le_scan_cleanup(conn);
hci_conn_hash_del(conn->hdev, conn);
hci_update_background_scan(conn->hdev);
/* We can't call hci_conn_del here since that would deadlock
* with trying to call cancel_delayed_work_sync(&conn->disc_work).
* Instead, call just hci_conn_cleanup() which contains the bare
* minimum cleanup operations needed for a connection in this
* state.
*/
hci_conn_cleanup(conn);
}
static void hci_acl_create_connection(struct hci_conn *conn)
@ -581,27 +626,17 @@ int hci_conn_del(struct hci_conn *conn)
}
}
hci_chan_list_flush(conn);
if (conn->amp_mgr)
amp_mgr_put(conn->amp_mgr);
hci_conn_hash_del(hdev, conn);
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
skb_queue_purge(&conn->data_q);
hci_conn_del_sysfs(conn);
debugfs_remove_recursive(conn->debugfs);
if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
hci_dev_put(hdev);
hci_conn_put(conn);
/* Remove the connection from the list and cleanup its remaining
* state. This is a separate function since for some cases like
* BT_CONNECT_SCAN we *only* want the cleanup part without the
* rest of hci_conn_del.
*/
hci_conn_cleanup(conn);
return 0;
}
@ -973,15 +1008,23 @@ static int hci_explicit_conn_params_set(struct hci_request *req,
if (is_connected(hdev, addr, addr_type))
return -EISCONN;
params = hci_conn_params_add(hdev, addr, addr_type);
if (!params)
return -EIO;
params = hci_conn_params_lookup(hdev, addr, addr_type);
if (!params) {
params = hci_conn_params_add(hdev, addr, addr_type);
if (!params)
return -ENOMEM;
/* If we created new params, or existing params were marked as disabled,
* mark them to be used just once to connect.
*/
if (params->auto_connect == HCI_AUTO_CONN_DISABLED) {
/* If we created new params, mark them to be deleted in
* hci_connect_le_scan_cleanup. It's different case than
* existing disabled params, those will stay after cleanup.
*/
params->auto_connect = HCI_AUTO_CONN_EXPLICIT;
}
/* We're trying to connect, so make sure params are at pend_le_conns */
if (params->auto_connect == HCI_AUTO_CONN_DISABLED ||
params->auto_connect == HCI_AUTO_CONN_REPORT ||
params->auto_connect == HCI_AUTO_CONN_EXPLICIT) {
list_del_init(&params->action);
list_add(&params->action, &hdev->pend_le_conns);
}

View File

@ -2861,13 +2861,6 @@ struct hci_conn_params *hci_explicit_connect_lookup(struct hci_dev *hdev,
return param;
}
list_for_each_entry(param, &hdev->pend_le_reports, action) {
if (bacmp(&param->addr, addr) == 0 &&
param->addr_type == addr_type &&
param->explicit_connect)
return param;
}
return NULL;
}

View File

@ -55,7 +55,12 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
wake_up_bit(&hdev->flags, HCI_INQUIRY);
hci_dev_lock(hdev);
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
/* Set discovery state to stopped if we're not doing LE active
* scanning.
*/
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
hdev->le_scan_type != LE_SCAN_ACTIVE)
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
hci_dev_unlock(hdev);
hci_conn_check_pending(hdev);
@ -4648,8 +4653,8 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
/* If we're not connectable only connect devices that we have in
* our pend_le_conns list.
*/
params = hci_explicit_connect_lookup(hdev, addr, addr_type);
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, addr,
addr_type);
if (!params)
return NULL;

View File

@ -3545,6 +3545,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
auth_type);
} else {
u8 addr_type;
struct hci_conn_params *p;
/* Convert from L2CAP channel address type to HCI address type
*/
@ -3562,7 +3563,10 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
* If connection parameters already exist, then they
* will be kept and this function does nothing.
*/
hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type);
p = hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type);
if (p->auto_connect == HCI_AUTO_CONN_EXPLICIT)
p->auto_connect = HCI_AUTO_CONN_DISABLED;
conn = hci_connect_le_scan(hdev, &cp->addr.bdaddr,
addr_type, sec_level,
@ -6117,14 +6121,21 @@ static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr,
__hci_update_background_scan(req);
break;
case HCI_AUTO_CONN_REPORT:
list_add(&params->action, &hdev->pend_le_reports);
if (params->explicit_connect)
list_add(&params->action, &hdev->pend_le_conns);
else
list_add(&params->action, &hdev->pend_le_reports);
__hci_update_background_scan(req);
break;
case HCI_AUTO_CONN_DIRECT:
case HCI_AUTO_CONN_ALWAYS:
if (!is_connected(hdev, addr, addr_type)) {
list_add(&params->action, &hdev->pend_le_conns);
__hci_update_background_scan(req);
/* If we are in scan phase of connecting, we were
* already added to pend_le_conns and scanning.
*/
if (params->auto_connect != HCI_AUTO_CONN_EXPLICIT)
__hci_update_background_scan(req);
}
break;
}
@ -6379,7 +6390,8 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
goto unlock;
}
if (params->auto_connect == HCI_AUTO_CONN_DISABLED) {
if (params->auto_connect == HCI_AUTO_CONN_DISABLED ||
params->auto_connect == HCI_AUTO_CONN_EXPLICIT) {
err = cmd->cmd_complete(cmd,
MGMT_STATUS_INVALID_PARAMS);
mgmt_pending_remove(cmd);
@ -6415,6 +6427,10 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
continue;
device_removed(sk, hdev, &p->addr, p->addr_type);
if (p->explicit_connect) {
p->auto_connect = HCI_AUTO_CONN_EXPLICIT;
continue;
}
list_del(&p->action);
list_del(&p->list);
kfree(p);