Bluetooth: Mesh: Refactor proxy adv
Refactors proxy advertising implementation to allow fair sharing of advertising resources between all subnets. In the new implementation, each subnet is agnostic to any other subnet that might have active proxy advertisement work. When proxy advertisement is triggered, the implementation will first check how many subnets that has active work. If there are more than one active subnet, a maximum timeslot will be calculated to ensure that all active subnets get access to the medium. The implementation will then poll the next eligible subnet for a proxy advertising request. If the duration of this request exceeds the maximum timeslot, the duration for the next advertisement will be set to the maximum value. If a proxy advertisement for a subnet is interrupted by other advertising activity, the implementation will now ensure that the interrupted proxy adv continues from the point where it was interrupted so that the subnet gets to utilize the entire allocated timeslot. This PR also alters the priv_proxy_net_id_multi Bsim test to align with the refactored proxy advertising scheme. Signed-off-by: Anders Storrø <anders.storro@nordicsemi.no>
This commit is contained in:
parent
fe70e50d41
commit
6c67ab3a63
2 changed files with 191 additions and 153 deletions
|
@ -625,7 +625,7 @@ static int net_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool advertise_subnet(struct bt_mesh_subnet *sub)
|
||||
static bool is_sub_proxy_active(struct bt_mesh_subnet *sub)
|
||||
{
|
||||
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
|
||||
return false;
|
||||
|
@ -633,44 +633,18 @@ static bool advertise_subnet(struct bt_mesh_subnet *sub)
|
|||
|
||||
return (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING ||
|
||||
#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV)
|
||||
sub->solicited ||
|
||||
(bt_mesh_od_priv_proxy_get() > 0 && sub->solicited) ||
|
||||
#endif
|
||||
bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED ||
|
||||
bt_mesh_priv_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED);
|
||||
}
|
||||
|
||||
static struct bt_mesh_subnet *next_sub(void)
|
||||
static bool active_proxy_sub_cnt_cb(struct bt_mesh_subnet *sub, void *cb_data)
|
||||
{
|
||||
struct bt_mesh_subnet *sub = NULL;
|
||||
int *cnt = cb_data;
|
||||
|
||||
if (!beacon_sub) {
|
||||
beacon_sub = bt_mesh_subnet_next(NULL);
|
||||
if (!beacon_sub) {
|
||||
/* No valid subnets */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
sub = beacon_sub;
|
||||
do {
|
||||
if (advertise_subnet(sub)) {
|
||||
beacon_sub = sub;
|
||||
return sub;
|
||||
}
|
||||
|
||||
sub = bt_mesh_subnet_next(sub);
|
||||
} while (sub != beacon_sub);
|
||||
|
||||
/* No subnets to advertise on */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool sub_count_cb(struct bt_mesh_subnet *sub, void *cb_data)
|
||||
{
|
||||
int *count = cb_data;
|
||||
|
||||
if (advertise_subnet(sub)) {
|
||||
(*count)++;
|
||||
if (is_sub_proxy_active(sub)) {
|
||||
(*cnt)++;
|
||||
}
|
||||
|
||||
/* Don't stop until we've visited all subnets.
|
||||
|
@ -679,155 +653,225 @@ static bool sub_count_cb(struct bt_mesh_subnet *sub, void *cb_data)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int sub_count(void)
|
||||
static int active_proxy_sub_cnt_get(void)
|
||||
{
|
||||
int count = 0;
|
||||
int cnt = 0;
|
||||
|
||||
(void)bt_mesh_subnet_find(sub_count_cb, &count);
|
||||
(void)bt_mesh_subnet_find(active_proxy_sub_cnt_cb, &cnt);
|
||||
|
||||
return count;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static void proxy_adv_timeout_eval(struct bt_mesh_subnet *sub)
|
||||
{
|
||||
int32_t time_passed;
|
||||
|
||||
if (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING) {
|
||||
time_passed = k_uptime_get_32() - sub->node_id_start;
|
||||
if (time_passed > (NODE_ID_TIMEOUT - MSEC_PER_SEC)) {
|
||||
bt_mesh_proxy_identity_stop(sub);
|
||||
LOG_DBG("Node ID stopped for subnet %d after %dms", sub->net_idx,
|
||||
time_passed);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV)
|
||||
static void gatt_proxy_solicited(struct bt_mesh_subnet *sub)
|
||||
{
|
||||
int64_t now = k_uptime_get();
|
||||
int64_t timeout = 0;
|
||||
int32_t remaining;
|
||||
|
||||
if (sub->priv_net_id_sent > 0) {
|
||||
timeout = sub->priv_net_id_sent +
|
||||
MSEC_PER_SEC * (int64_t) bt_mesh_od_priv_proxy_get();
|
||||
remaining = MIN(timeout - now, INT32_MAX);
|
||||
} else {
|
||||
remaining = MSEC_PER_SEC * bt_mesh_od_priv_proxy_get();
|
||||
}
|
||||
|
||||
if ((timeout > 0 && now > timeout) || (remaining / MSEC_PER_SEC < 1)) {
|
||||
LOG_DBG("Advertising Private Network ID timed out "
|
||||
"after solicitation");
|
||||
if (bt_mesh_od_priv_proxy_get() > 0 && sub->solicited && sub->priv_net_id_sent) {
|
||||
time_passed = k_uptime_get_32() - sub->priv_net_id_sent;
|
||||
if (time_passed > ((MSEC_PER_SEC * bt_mesh_od_priv_proxy_get()) - MSEC_PER_SEC)) {
|
||||
sub->priv_net_id_sent = 0;
|
||||
sub->solicited = false;
|
||||
} else {
|
||||
LOG_DBG("Advertising Private Network ID for %ds"
|
||||
"(%d remaining)",
|
||||
bt_mesh_od_priv_proxy_get(),
|
||||
remaining / MSEC_PER_SEC);
|
||||
priv_net_id_adv(sub, remaining);
|
||||
|
||||
if (!sub->priv_net_id_sent) {
|
||||
sub->priv_net_id_sent = now;
|
||||
}
|
||||
LOG_DBG("Private Network ID stopped for subnet %d after %dms on "
|
||||
"solicitation",
|
||||
sub->net_idx, time_passed);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int gatt_proxy_advertise(struct bt_mesh_subnet *sub)
|
||||
enum proxy_adv_evt {
|
||||
NET_ID,
|
||||
PRIV_NET_ID,
|
||||
NODE_ID,
|
||||
PRIV_NODE_ID,
|
||||
OD_PRIV_NET_ID,
|
||||
};
|
||||
|
||||
struct proxy_adv_request {
|
||||
int32_t duration;
|
||||
enum proxy_adv_evt evt;
|
||||
};
|
||||
|
||||
static bool proxy_adv_request_get(struct bt_mesh_subnet *sub, struct proxy_adv_request *request)
|
||||
{
|
||||
int32_t remaining = SYS_FOREVER_MS;
|
||||
int subnet_count;
|
||||
int err = -EBUSY;
|
||||
bool planned = false;
|
||||
if (!sub) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV)
|
||||
if (bt_mesh_od_priv_proxy_get() > 0 && sub->solicited) {
|
||||
int32_t timeout = MSEC_PER_SEC * (int32_t)bt_mesh_od_priv_proxy_get();
|
||||
|
||||
request->evt = OD_PRIV_NET_ID;
|
||||
request->duration = !sub->priv_net_id_sent
|
||||
? timeout
|
||||
: timeout - (k_uptime_get_32() - sub->priv_net_id_sent);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING) {
|
||||
request->duration = NODE_ID_TIMEOUT - (k_uptime_get_32() - sub->node_id_start);
|
||||
request->evt =
|
||||
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
||||
sub->priv_beacon_ctx.node_id ? PRIV_NODE_ID :
|
||||
#endif
|
||||
NODE_ID;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bt_mesh_priv_gatt_proxy_get() == BT_MESH_FEATURE_ENABLED) {
|
||||
request->evt = PRIV_NET_ID;
|
||||
request->duration = PROXY_RANDOM_UPDATE_INTERVAL;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bt_mesh_gatt_proxy_get() == BT_MESH_FEATURE_ENABLED) {
|
||||
request->evt = NET_ID;
|
||||
request->duration = SYS_FOREVER_MS;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct bt_mesh_subnet *adv_sub_get_next(struct bt_mesh_subnet *sub_start,
|
||||
struct proxy_adv_request *request)
|
||||
{
|
||||
struct bt_mesh_subnet *sub_temp = sub_start;
|
||||
|
||||
do {
|
||||
if (proxy_adv_request_get(sub_temp, request)) {
|
||||
return sub_temp;
|
||||
}
|
||||
|
||||
sub_temp = bt_mesh_subnet_next(sub_temp);
|
||||
} while (sub_temp != sub_start);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct {
|
||||
int32_t start;
|
||||
struct bt_mesh_subnet *sub;
|
||||
struct proxy_adv_request request;
|
||||
} sub_adv;
|
||||
|
||||
static int gatt_proxy_advertise(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
int32_t max_adv_duration;
|
||||
int cnt;
|
||||
struct bt_mesh_subnet *sub;
|
||||
struct proxy_adv_request request;
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
/* Close proxy activity that has timed out on all subnets */
|
||||
bt_mesh_subnet_foreach(proxy_adv_timeout_eval);
|
||||
|
||||
if (!bt_mesh_proxy_has_avail_conn()) {
|
||||
LOG_DBG("Connectable advertising deferred (max connections)");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sub = beacon_sub ? beacon_sub : bt_mesh_subnet_next(beacon_sub);
|
||||
if (!sub) {
|
||||
LOG_WRN("No subnets to advertise on");
|
||||
cnt = active_proxy_sub_cnt_get();
|
||||
if (!cnt) {
|
||||
LOG_DBG("No subnets to advertise proxy on");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
subnet_count = sub_count();
|
||||
LOG_DBG("sub_count %u", subnet_count);
|
||||
if (subnet_count > 1) {
|
||||
int32_t max_timeout;
|
||||
} else if (cnt > 1) {
|
||||
/** There is more than one subnet that requires proxy adv,
|
||||
* and the adv resources must be shared.
|
||||
*/
|
||||
|
||||
/* We use NODE_ID_TIMEOUT as a starting point since it may
|
||||
* be less than 60 seconds. Divide this period into at least
|
||||
* 6 slices, but make sure that a slice is at least one
|
||||
* 6 slices, but make sure that a slice is more than one
|
||||
* second long (to avoid excessive rotation).
|
||||
*/
|
||||
max_timeout = NODE_ID_TIMEOUT / MAX(subnet_count, 6);
|
||||
max_timeout = MAX(max_timeout, 1 * MSEC_PER_SEC);
|
||||
max_adv_duration = NODE_ID_TIMEOUT / MAX(cnt, 6);
|
||||
max_adv_duration = MAX(max_adv_duration, MSEC_PER_SEC + 20);
|
||||
|
||||
if (remaining > max_timeout || remaining == SYS_FOREVER_MS) {
|
||||
remaining = max_timeout;
|
||||
/* Check if the previous subnet finished its allocated timeslot */
|
||||
if ((sub_adv.request.duration != SYS_FOREVER_MS) &&
|
||||
proxy_adv_request_get(sub_adv.sub, &request) &&
|
||||
(sub_adv.request.evt == request.evt)) {
|
||||
int32_t time_passed = k_uptime_get_32() - sub_adv.start;
|
||||
|
||||
if (time_passed < sub_adv.request.duration &&
|
||||
((sub_adv.request.duration - time_passed) >= MSEC_PER_SEC)) {
|
||||
sub = sub_adv.sub;
|
||||
request.duration = sub_adv.request.duration - time_passed;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < subnet_count; i++) {
|
||||
|
||||
if (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING) {
|
||||
uint32_t active = k_uptime_get_32() - sub->node_id_start;
|
||||
bool priv_node_id = false;
|
||||
|
||||
if (active < NODE_ID_TIMEOUT) {
|
||||
remaining = MIN(remaining, NODE_ID_TIMEOUT - active);
|
||||
LOG_DBG("Node ID active for %u ms, %d ms remaining",
|
||||
active, remaining);
|
||||
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
||||
priv_node_id = sub->priv_beacon_ctx.node_id;
|
||||
#endif
|
||||
if (priv_node_id) {
|
||||
err = priv_node_id_adv(sub, remaining);
|
||||
} else {
|
||||
err = node_id_adv(sub, remaining);
|
||||
}
|
||||
planned = true;
|
||||
} else {
|
||||
bt_mesh_proxy_identity_stop(sub);
|
||||
LOG_DBG("Node ID stopped");
|
||||
sub = adv_sub_get_next(bt_mesh_subnet_next(sub_adv.sub), &request);
|
||||
if (!sub) {
|
||||
LOG_ERR("Could not find subnet to advertise");
|
||||
return -ENOENT;
|
||||
}
|
||||
end:
|
||||
if (cnt > 1) {
|
||||
request.duration = (request.duration == SYS_FOREVER_MS)
|
||||
? max_adv_duration
|
||||
: MIN(request.duration, max_adv_duration);
|
||||
}
|
||||
|
||||
/* MshPRTv1.1: section 7.2.2.2.1:
|
||||
* "A node that does not support the Proxy feature or
|
||||
* has the GATT Proxy state disabled shall not advertise with Network ID."
|
||||
*/
|
||||
if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED) {
|
||||
if (IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS) &&
|
||||
(bt_mesh_priv_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED)) {
|
||||
/* MshPRTv1.1: 7.2.2.2.4: The Random
|
||||
* field should be updated every 10 minutes. Limit advertising to
|
||||
* 10 minutes to ensure regeneration of a new random value at least
|
||||
* that often.
|
||||
*/
|
||||
if (remaining == SYS_FOREVER_MS ||
|
||||
remaining > PROXY_RANDOM_UPDATE_INTERVAL) {
|
||||
remaining = PROXY_RANDOM_UPDATE_INTERVAL;
|
||||
}
|
||||
|
||||
err = priv_net_id_adv(sub, remaining);
|
||||
planned = true;
|
||||
} else if (bt_mesh_gatt_proxy_get() == BT_MESH_FEATURE_ENABLED) {
|
||||
err = net_id_adv(sub, remaining);
|
||||
planned = true;
|
||||
}
|
||||
/* Save current state for next iteration */
|
||||
sub_adv.start = k_uptime_get_32();
|
||||
sub_adv.sub = sub;
|
||||
sub_adv.request = request;
|
||||
|
||||
switch (request.evt) {
|
||||
case NET_ID:
|
||||
err = net_id_adv(sub, request.duration);
|
||||
break;
|
||||
#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV)
|
||||
else if (bt_mesh_od_priv_proxy_get() > 0 &&
|
||||
sub->solicited) {
|
||||
gatt_proxy_solicited(sub);
|
||||
case OD_PRIV_NET_ID:
|
||||
if (!sub->priv_net_id_sent) {
|
||||
sub->priv_net_id_sent = k_uptime_get();
|
||||
}
|
||||
/* Fall through */
|
||||
#endif
|
||||
case PRIV_NET_ID:
|
||||
err = priv_net_id_adv(sub, request.duration);
|
||||
break;
|
||||
case NODE_ID:
|
||||
err = node_id_adv(sub, request.duration);
|
||||
break;
|
||||
case PRIV_NODE_ID:
|
||||
err = priv_node_id_adv(sub, request.duration);
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Unexpected proxy adv evt: %d", request.evt);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
beacon_sub = bt_mesh_subnet_next(sub);
|
||||
|
||||
if (planned) {
|
||||
LOG_DBG("Advertising %d ms for net_idx 0x%04x", remaining, sub->net_idx);
|
||||
if (err) {
|
||||
LOG_ERR("Advertising proxy failed (err: %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
sub = beacon_sub;
|
||||
}
|
||||
|
||||
return 0;
|
||||
LOG_DBG("Advertising %d ms for net_idx 0x%04x", request.duration, sub->net_idx);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
|
||||
|
@ -1152,7 +1196,7 @@ int bt_mesh_proxy_adv_start(void)
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return gatt_proxy_advertise(next_sub());
|
||||
return gatt_proxy_advertise();
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
|
|
|
@ -1763,12 +1763,6 @@ static void test_tx_priv_multi_net_id(void)
|
|||
{
|
||||
tx_priv_common_init(PP_MULT_NET_ID_WAIT_TIME);
|
||||
|
||||
/* TODO: This should be removed as soon as
|
||||
* SNB/proxy service advertising issue has
|
||||
* been resolved.
|
||||
*/
|
||||
bt_mesh_beacon_set(false);
|
||||
|
||||
/* Add second network */
|
||||
ASSERT_OK_MSG(bt_mesh_subnet_add(TEST_NET_IDX2, test_net_key_secondary),
|
||||
"Failed to add second subnet");
|
||||
|
@ -1819,8 +1813,8 @@ static void test_rx_priv_multi_net_id(void)
|
|||
|
||||
/* Verify last Net ID adv result */
|
||||
ASSERT_IN_RANGE(k_uptime_get() - net_ctx[old_idx].start,
|
||||
MAX_TIMEOUT - 1000, MAX_TIMEOUT);
|
||||
ASSERT_IN_RANGE(net_ctx[old_idx].recv_cnt, 9, 10);
|
||||
MAX_TIMEOUT - 1000, MAX_TIMEOUT + 1000);
|
||||
ASSERT_IN_RANGE(net_ctx[old_idx].recv_cnt, 9, 12);
|
||||
net_ctx[old_idx].recv_cnt = 0;
|
||||
old_idx = i;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue