Skip to content

Commit

Permalink
Bluetooth: advmon offload MSFT remove monitor
Browse files Browse the repository at this point in the history
Implements the monitor removal functionality for advertising monitor
offloading to MSFT controllers. Supply handle = 0 to remove all
monitors.

Signed-off-by: Archie Pusaka <apusaka@chromium.org>
Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
Reviewed-by: Yun-Hao Chung <howardchung@google.com>
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
  • Loading branch information
Archie Pusaka authored and Marcel Holtmann committed Jan 25, 2021
1 parent a2a4ded commit 66bd095
Show file tree
Hide file tree
Showing 5 changed files with 323 additions and 50 deletions.
8 changes: 6 additions & 2 deletions include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1332,11 +1332,13 @@ int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance);
void hci_adv_instances_set_rpa_expired(struct hci_dev *hdev, bool rpa_expired);

void hci_adv_monitors_clear(struct hci_dev *hdev);
void hci_free_adv_monitor(struct adv_monitor *monitor);
void hci_free_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor);
int hci_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status);
int hci_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status);
bool hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
int *err);
int hci_remove_adv_monitor(struct hci_dev *hdev, u16 handle);
bool hci_remove_single_adv_monitor(struct hci_dev *hdev, u16 handle, int *err);
bool hci_remove_all_adv_monitor(struct hci_dev *hdev, int *err);
bool hci_is_adv_monitoring(struct hci_dev *hdev);
int hci_get_adv_monitor_offload_ext(struct hci_dev *hdev);

Expand Down Expand Up @@ -1813,8 +1815,10 @@ void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev,
u8 instance);
void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev,
u8 instance);
void mgmt_adv_monitor_removed(struct hci_dev *hdev, u16 handle);
int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip);
int mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status);
int mgmt_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status);

u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
u16 to_multiplier);
Expand Down
119 changes: 96 additions & 23 deletions net/bluetooth/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -3051,21 +3051,34 @@ void hci_adv_monitors_clear(struct hci_dev *hdev)
int handle;

idr_for_each_entry(&hdev->adv_monitors_idr, monitor, handle)
hci_free_adv_monitor(monitor);
hci_free_adv_monitor(hdev, monitor);

idr_destroy(&hdev->adv_monitors_idr);
}

void hci_free_adv_monitor(struct adv_monitor *monitor)
/* Frees the monitor structure and do some bookkeepings.
* This function requires the caller holds hdev->lock.
*/
void hci_free_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor)
{
struct adv_pattern *pattern;
struct adv_pattern *tmp;

if (!monitor)
return;

list_for_each_entry_safe(pattern, tmp, &monitor->patterns, list)
list_for_each_entry_safe(pattern, tmp, &monitor->patterns, list) {
list_del(&pattern->list);
kfree(pattern);
}

if (monitor->handle)
idr_remove(&hdev->adv_monitors_idr, monitor->handle);

if (monitor->state != ADV_MONITOR_STATE_NOT_REGISTERED) {
hdev->adv_monitors_cnt--;
mgmt_adv_monitor_removed(hdev, monitor->handle);
}

kfree(monitor);
}
Expand All @@ -3075,6 +3088,11 @@ int hci_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status)
return mgmt_add_adv_patterns_monitor_complete(hdev, status);
}

int hci_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status)
{
return mgmt_remove_adv_monitor_complete(hdev, status);
}

/* Assigns handle to a monitor, and if offloading is supported and power is on,
* also attempts to forward the request to the controller.
* Returns true if request is forwarded (result is pending), false otherwise.
Expand Down Expand Up @@ -3122,39 +3140,94 @@ bool hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
return (*err == 0);
}

static int free_adv_monitor(int id, void *ptr, void *data)
/* Attempts to tell the controller and free the monitor. If somehow the
* controller doesn't have a corresponding handle, remove anyway.
* Returns true if request is forwarded (result is pending), false otherwise.
* This function requires the caller holds hdev->lock.
*/
static bool hci_remove_adv_monitor(struct hci_dev *hdev,
struct adv_monitor *monitor,
u16 handle, int *err)
{
struct hci_dev *hdev = data;
struct adv_monitor *monitor = ptr;
*err = 0;

idr_remove(&hdev->adv_monitors_idr, monitor->handle);
hci_free_adv_monitor(monitor);
hdev->adv_monitors_cnt--;
switch (hci_get_adv_monitor_offload_ext(hdev)) {
case HCI_ADV_MONITOR_EXT_NONE: /* also goes here when powered off */
goto free_monitor;
case HCI_ADV_MONITOR_EXT_MSFT:
*err = msft_remove_monitor(hdev, monitor, handle);
break;
}

return 0;
/* In case no matching handle registered, just free the monitor */
if (*err == -ENOENT)
goto free_monitor;

return (*err == 0);

free_monitor:
if (*err == -ENOENT)
bt_dev_warn(hdev, "Removing monitor with no matching handle %d",
monitor->handle);
hci_free_adv_monitor(hdev, monitor);

*err = 0;
return false;
}

/* This function requires the caller holds hdev->lock */
int hci_remove_adv_monitor(struct hci_dev *hdev, u16 handle)
/* Returns true if request is forwarded (result is pending), false otherwise.
* This function requires the caller holds hdev->lock.
*/
bool hci_remove_single_adv_monitor(struct hci_dev *hdev, u16 handle, int *err)
{
struct adv_monitor *monitor = idr_find(&hdev->adv_monitors_idr, handle);
bool pending;

if (!monitor) {
*err = -EINVAL;
return false;
}

pending = hci_remove_adv_monitor(hdev, monitor, handle, err);
if (!*err && !pending)
hci_update_background_scan(hdev);

bt_dev_dbg(hdev, "%s remove monitor handle %d, status %d, %spending",
hdev->name, handle, *err, pending ? "" : "not ");

return pending;
}

/* Returns true if request is forwarded (result is pending), false otherwise.
* This function requires the caller holds hdev->lock.
*/
bool hci_remove_all_adv_monitor(struct hci_dev *hdev, int *err)
{
struct adv_monitor *monitor;
int idr_next_id = 0;
bool pending = false;
bool update = false;

*err = 0;

if (handle) {
monitor = idr_find(&hdev->adv_monitors_idr, handle);
while (!*err && !pending) {
monitor = idr_get_next(&hdev->adv_monitors_idr, &idr_next_id);
if (!monitor)
return -ENOENT;
break;

idr_remove(&hdev->adv_monitors_idr, monitor->handle);
hci_free_adv_monitor(monitor);
hdev->adv_monitors_cnt--;
} else {
/* Remove all monitors if handle is 0. */
idr_for_each(&hdev->adv_monitors_idr, &free_adv_monitor, hdev);
pending = hci_remove_adv_monitor(hdev, monitor, 0, err);

if (!*err && !pending)
update = true;
}

hci_update_background_scan(hdev);
if (update)
hci_update_background_scan(hdev);

return 0;
bt_dev_dbg(hdev, "%s remove all monitors status %d, %spending",
hdev->name, *err, pending ? "" : "not ");

return pending;
}

/* This function requires the caller holds hdev->lock */
Expand Down
110 changes: 89 additions & 21 deletions net/bluetooth/mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -4167,14 +4167,24 @@ static void mgmt_adv_monitor_added(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_ADV_MONITOR_ADDED, hdev, &ev, sizeof(ev), sk);
}

static void mgmt_adv_monitor_removed(struct sock *sk, struct hci_dev *hdev,
u16 handle)
void mgmt_adv_monitor_removed(struct hci_dev *hdev, u16 handle)
{
struct mgmt_ev_adv_monitor_added ev;
struct mgmt_ev_adv_monitor_removed ev;
struct mgmt_pending_cmd *cmd;
struct sock *sk_skip = NULL;
struct mgmt_cp_remove_adv_monitor *cp;

cmd = pending_find(MGMT_OP_REMOVE_ADV_MONITOR, hdev);
if (cmd) {
cp = cmd->param;

if (cp->monitor_handle)
sk_skip = cmd->sk;
}

ev.monitor_handle = cpu_to_le16(handle);

mgmt_event(MGMT_EV_ADV_MONITOR_REMOVED, hdev, &ev, sizeof(ev), sk);
mgmt_event(MGMT_EV_ADV_MONITOR_REMOVED, hdev, &ev, sizeof(ev), sk_skip);
}

static int read_adv_mon_features(struct sock *sk, struct hci_dev *hdev,
Expand Down Expand Up @@ -4324,8 +4334,8 @@ static int __add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
return 0;

unlock:
hci_free_adv_monitor(hdev, m);
hci_dev_unlock(hdev);
hci_free_adv_monitor(m);
return mgmt_cmd_status(sk, hdev->id, op, status);
}

Expand Down Expand Up @@ -4459,42 +4469,100 @@ static int add_adv_patterns_monitor_rssi(struct sock *sk, struct hci_dev *hdev,
MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI);
}

int mgmt_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_rp_remove_adv_monitor rp;
struct mgmt_cp_remove_adv_monitor *cp;
struct mgmt_pending_cmd *cmd;
int err = 0;

hci_dev_lock(hdev);

cmd = pending_find(MGMT_OP_REMOVE_ADV_MONITOR, hdev);
if (!cmd)
goto done;

cp = cmd->param;
rp.monitor_handle = cp->monitor_handle;

if (!status)
hci_update_background_scan(hdev);

err = mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);

done:
hci_dev_unlock(hdev);
bt_dev_dbg(hdev, "remove monitor %d complete, status %d",
rp.monitor_handle, status);

return err;
}

static int remove_adv_monitor(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_remove_adv_monitor *cp = data;
struct mgmt_rp_remove_adv_monitor rp;
unsigned int prev_adv_monitors_cnt;
u16 handle;
int err;
struct mgmt_pending_cmd *cmd;
u16 handle = __le16_to_cpu(cp->monitor_handle);
int err, status;
bool pending;

BT_DBG("request for %s", hdev->name);
rp.monitor_handle = cp->monitor_handle;

hci_dev_lock(hdev);

handle = __le16_to_cpu(cp->monitor_handle);
prev_adv_monitors_cnt = hdev->adv_monitors_cnt;
if (pending_find(MGMT_OP_SET_LE, hdev) ||
pending_find(MGMT_OP_REMOVE_ADV_MONITOR, hdev) ||
pending_find(MGMT_OP_ADD_ADV_PATTERNS_MONITOR, hdev) ||
pending_find(MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI, hdev)) {
status = MGMT_STATUS_BUSY;
goto unlock;
}

err = hci_remove_adv_monitor(hdev, handle);
if (err == -ENOENT) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADV_MONITOR,
MGMT_STATUS_INVALID_INDEX);
cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_ADV_MONITOR, hdev, data, len);
if (!cmd) {
status = MGMT_STATUS_NO_RESOURCES;
goto unlock;
}

if (hdev->adv_monitors_cnt < prev_adv_monitors_cnt)
mgmt_adv_monitor_removed(sk, hdev, handle);
if (handle)
pending = hci_remove_single_adv_monitor(hdev, handle, &err);
else
pending = hci_remove_all_adv_monitor(hdev, &err);

hci_dev_unlock(hdev);
if (err) {
mgmt_pending_remove(cmd);

rp.monitor_handle = cp->monitor_handle;
if (err == -ENOENT)
status = MGMT_STATUS_INVALID_INDEX;
else
status = MGMT_STATUS_FAILED;

goto unlock;
}

/* monitor can be removed without forwarding request to controller */
if (!pending) {
mgmt_pending_remove(cmd);
hci_dev_unlock(hdev);

return mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_REMOVE_ADV_MONITOR,
MGMT_STATUS_SUCCESS,
&rp, sizeof(rp));
}

return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_ADV_MONITOR,
MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
hci_dev_unlock(hdev);
return 0;

unlock:
hci_dev_unlock(hdev);
return err;
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADV_MONITOR,
status);
}

static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
Expand Down
Loading

0 comments on commit 66bd095

Please sign in to comment.