You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2016/11/17 02:31:54 UTC

[2/5] incubator-mynewt-core git commit: BLE Host - Eliminate spurious time expirations.

BLE Host - Eliminate spurious time expirations.

Prior to this change, the host timer would expire at least once per
second to clean up expired procedures.  This approach was used in lieu
of more diligent bookkeeping.  Functionally, this is OK, since these
timers don't need to be particularly precise.  However, this approach is
bad for power management, as it requires the processor to periodically
wake up from deep sleep when there is no work to do.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/d0cc8330
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/d0cc8330
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/d0cc8330

Branch: refs/heads/develop
Commit: d0cc83304baacb1626a5540323316bf9a6f094ed
Parents: 2fcb28b
Author: Christopher Collins <cc...@apache.org>
Authored: Wed Nov 16 16:29:44 2016 -0800
Committer: Christopher Collins <cc...@apache.org>
Committed: Wed Nov 16 18:31:44 2016 -0800

----------------------------------------------------------------------
 net/nimble/host/src/ble_gap.c           |  69 ++++++++---------
 net/nimble/host/src/ble_gap_priv.h      |   2 +-
 net/nimble/host/src/ble_gatt_priv.h     |   2 +-
 net/nimble/host/src/ble_gattc.c         |  46 +++++++-----
 net/nimble/host/src/ble_hs.c            | 106 ++++++++++++++-------------
 net/nimble/host/src/ble_hs_priv.h       |   2 +-
 net/nimble/host/src/ble_l2cap_sig.c     |  10 +--
 net/nimble/host/src/ble_sm.c            |  43 ++++++-----
 net/nimble/host/src/ble_sm_priv.h       |   4 +-
 net/nimble/host/test/src/ble_gap_test.c |  28 +++----
 10 files changed, 161 insertions(+), 151 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d0cc8330/net/nimble/host/src/ble_gap.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap.c b/net/nimble/host/src/ble_gap.c
index ca53187..6d83a9b 100644
--- a/net/nimble/host/src/ble_gap.c
+++ b/net/nimble/host/src/ble_gap.c
@@ -508,6 +508,8 @@ ble_gap_master_reset_state(void)
     ble_gap_master.op = BLE_GAP_OP_NULL;
     ble_gap_master.exp_set = 0;
     ble_gap_master.conn.cancel = 0;
+
+    ble_hs_timer_resched();
 }
 
 static void
@@ -515,6 +517,8 @@ ble_gap_slave_reset_state(void)
 {
     ble_gap_slave.op = BLE_GAP_OP_NULL;
     ble_gap_slave.exp_set = 0;
+
+    ble_hs_timer_resched();
 }
 
 static void
@@ -697,6 +701,17 @@ ble_gap_slave_ticks_until_exp(void)
     return 0;
 }
 
+/**
+ * Finds the update procedure that expires soonest.
+ *
+ * @param out_ticks_from_now    On success, the ticks until the update
+ *                                  procedure's expiry time gets written here.
+ *
+ * @return                      The connection handle of the update procedure
+ *                                  that expires soonest, or
+ *                                  BLE_HS_CONN_HANDLE_NONE if there are no
+ *                                  active update procedures.
+ */
 static uint16_t
 ble_gap_update_next_exp(int32_t *out_ticks_from_now)
 {
@@ -732,38 +747,13 @@ ble_gap_update_next_exp(int32_t *out_ticks_from_now)
 
 }
 
-static uint32_t
-ble_gap_update_ticks_until_exp(void)
-{
-    int32_t ticks;
-
-    ble_gap_update_next_exp(&ticks);
-    return ticks;
-}
-
-static void
-ble_gap_heartbeat_sched(void)
-{
-    int32_t mst_ticks;
-    int32_t slv_ticks;
-    int32_t upd_ticks;
-    int32_t ticks;
-
-    mst_ticks = ble_gap_master_ticks_until_exp();
-    slv_ticks = ble_gap_slave_ticks_until_exp();
-    upd_ticks = ble_gap_update_ticks_until_exp();
-    ticks = min(min(mst_ticks, slv_ticks), upd_ticks);
-
-    ble_hs_heartbeat_sched(ticks);
-}
-
 static void
 ble_gap_master_set_timer(uint32_t ticks_from_now)
 {
     ble_gap_master.exp_os_ticks = os_time_get() + ticks_from_now;
     ble_gap_master.exp_set = 1;
 
-    ble_gap_heartbeat_sched();
+    ble_hs_timer_resched();
 }
 
 static void
@@ -772,7 +762,7 @@ ble_gap_slave_set_timer(uint32_t ticks_from_now)
     ble_gap_slave.exp_os_ticks = os_time_get() + ticks_from_now;
     ble_gap_slave.exp_set = 1;
 
-    ble_gap_heartbeat_sched();
+    ble_hs_timer_resched();
 }
 
 /**
@@ -1251,7 +1241,7 @@ ble_gap_rx_l2cap_update_req(uint16_t conn_handle,
 }
 
 static int32_t
-ble_gap_master_heartbeat(void)
+ble_gap_master_timer(void)
 {
     uint32_t ticks_until_exp;
     int rc;
@@ -1303,7 +1293,7 @@ ble_gap_master_heartbeat(void)
 }
 
 static int32_t
-ble_gap_slave_heartbeat(void)
+ble_gap_slave_timer(void)
 {
     uint32_t ticks_until_exp;
     int rc;
@@ -1333,7 +1323,7 @@ ble_gap_slave_heartbeat(void)
 }
 
 static int32_t
-ble_gap_update_heartbeat(void)
+ble_gap_update_timer(void)
 {
     struct ble_gap_update_entry *entry;
     int32_t ticks_until_exp;
@@ -1361,23 +1351,21 @@ ble_gap_update_heartbeat(void)
 }
 
 /**
- * Handles timed-out master procedures.
- *
- * Called by the heartbeat timer; executed at least once a second.
+ * Handles timed-out GAP procedures.
  *
  * @return                      The number of ticks until this function should
  *                                  be called again.
  */
 int32_t
-ble_gap_heartbeat(void)
+ble_gap_timer(void)
 {
     int32_t update_ticks;
     int32_t master_ticks;
     int32_t slave_ticks;
 
-    master_ticks = ble_gap_master_heartbeat();
-    slave_ticks = ble_gap_slave_heartbeat();
-    update_ticks = ble_gap_update_heartbeat();
+    master_ticks = ble_gap_master_timer();
+    slave_ticks = ble_gap_slave_timer();
+    update_ticks = ble_gap_update_timer();
 
     return min(min(master_ticks, slave_ticks), update_ticks);
 }
@@ -1569,6 +1557,7 @@ done:
     if (rc != 0) {
         STATS_INC(ble_gap_stats, adv_set_fields_fail);
     }
+
     return rc;
 }
 
@@ -2167,6 +2156,7 @@ done:
     if (rc != 0) {
         STATS_INC(ble_gap_stats, discover_cancel_fail);
     }
+
     return rc;
 }
 
@@ -2718,6 +2708,7 @@ ble_gap_update_entry_remove(uint16_t conn_handle)
         } else {
             SLIST_NEXT(prev, next) = SLIST_NEXT(entry, next);
         }
+        ble_hs_timer_resched();
     }
 
     return entry;
@@ -2941,7 +2932,9 @@ ble_gap_update_params(uint16_t conn_handle,
 done:
     ble_hs_unlock();
 
-    if (rc != 0) {
+    if (rc == 0) {
+        ble_hs_timer_resched();
+    } else {
         ble_gap_update_entry_free(entry);
 
         if (l2cap_params.itvl_min != 0) {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d0cc8330/net/nimble/host/src/ble_gap_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap_priv.h b/net/nimble/host/src/ble_gap_priv.h
index 8b73d4c..a1f7429 100644
--- a/net/nimble/host/src/ble_gap_priv.h
+++ b/net/nimble/host/src/ble_gap_priv.h
@@ -97,7 +97,7 @@ void ble_gap_mtu_event(uint16_t conn_handle, uint16_t cid, uint16_t mtu);
 int ble_gap_master_in_progress(void);
 
 void ble_gap_conn_broken(uint16_t conn_handle, int reason);
-int32_t ble_gap_heartbeat(void);
+int32_t ble_gap_timer(void);
 
 int ble_gap_init(void);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d0cc8330/net/nimble/host/src/ble_gatt_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gatt_priv.h b/net/nimble/host/src/ble_gatt_priv.h
index e14cf2c..110ced8 100644
--- a/net/nimble/host/src/ble_gatt_priv.h
+++ b/net/nimble/host/src/ble_gatt_priv.h
@@ -129,7 +129,7 @@ void ble_gattc_rx_find_info_idata(uint16_t conn_handle,
 void ble_gattc_rx_find_info_complete(uint16_t conn_handle, int status);
 void ble_gattc_connection_txable(uint16_t conn_handle);
 void ble_gattc_connection_broken(uint16_t conn_handle);
-int32_t ble_gattc_heartbeat(void);
+int32_t ble_gattc_timer(void);
 
 int ble_gattc_any_jobs(void);
 int ble_gattc_init(void);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d0cc8330/net/nimble/host/src/ble_gattc.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gattc.c b/net/nimble/host/src/ble_gattc.c
index 360299d..1e93841 100644
--- a/net/nimble/host/src/ble_gattc.c
+++ b/net/nimble/host/src/ble_gattc.c
@@ -316,13 +316,14 @@ static const struct ble_gattc_rx_exec_entry {
     { BLE_GATT_OP_WRITE_RELIABLE,   ble_gattc_write_reliable_rx_exec },
 };
 
-/* Maintains the list of active GATT client procedures. */
 static os_membuf_t ble_gattc_proc_mem[
     OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_GATT_MAX_PROCS),
                     sizeof (struct ble_gattc_proc))
 ];
 
 static struct os_mempool ble_gattc_proc_pool;
+
+/* The list of active GATT client procedures. */
 static struct ble_gattc_proc_list ble_gattc_procs;
 
 /* Statistics. */
@@ -646,6 +647,7 @@ static void
 ble_gattc_proc_set_timer(struct ble_gattc_proc *proc)
 {
     proc->exp_os_ticks = os_time_get() + BLE_GATT_UNRESPONSIVE_TIMEOUT;
+    ble_hs_timer_resched();
 }
 
 static void
@@ -781,13 +783,18 @@ ble_gattc_extract_by_conn_op(uint16_t conn_handle, uint8_t op,
     ble_hs_unlock();
 }
 
-static void
+/**
+ * @return                      The number of ticks until the next expiration
+ *                                  occurs.
+ */
+static int32_t
 ble_gattc_extract_expired(struct ble_gattc_proc_list *dst_list)
 {
     struct ble_gattc_proc *proc;
     struct ble_gattc_proc *prev;
     struct ble_gattc_proc *next;
     uint32_t now;
+    int32_t next_exp_in;
     int32_t time_diff;
 
     /* Only the parent task is allowed to remove entries from the list. */
@@ -796,6 +803,9 @@ ble_gattc_extract_expired(struct ble_gattc_proc_list *dst_list)
     now = os_time_get();
     STAILQ_INIT(dst_list);
 
+    /* Assume each event is either expired or has infinite duration. */
+    next_exp_in = BLE_HS_FOREVER;
+
     ble_hs_lock();
 
     prev = NULL;
@@ -803,14 +813,19 @@ ble_gattc_extract_expired(struct ble_gattc_proc_list *dst_list)
     while (proc != NULL) {
         next = STAILQ_NEXT(proc, next);
 
-        time_diff = now - proc->exp_os_ticks;
-        if (time_diff >= 0) {
+        time_diff = proc->exp_os_ticks - now;
+        if (time_diff <= 0) {
+            /* Procedure has expired; move it to the destination list. */
             if (prev == NULL) {
                 STAILQ_REMOVE_HEAD(&ble_gattc_procs, next);
             } else {
                 STAILQ_REMOVE_AFTER(&ble_gattc_procs, prev, next);
             }
             STAILQ_INSERT_TAIL(dst_list, proc, next);
+        } else {
+            if (time_diff < next_exp_in) {
+                next_exp_in = time_diff;
+            }
         }
 
         prev = proc;
@@ -818,6 +833,8 @@ ble_gattc_extract_expired(struct ble_gattc_proc_list *dst_list)
     }
 
     ble_hs_unlock();
+
+    return next_exp_in;
 }
 
 static struct ble_gattc_proc *
@@ -909,28 +926,23 @@ ble_gattc_fail_procs(uint16_t conn_handle, uint8_t op, int status)
 }
 
 /**
- * Applies periodic checks and actions to all active procedures.
- *
- * All procedures that have been expecting a response for longer than 30
- * seconds are aborted and their corresponding connection is terminated.
- *
- * Called by the heartbeat timer; executed at least once a second.
+ * Times out expired GATT client procedures.
  *
  * @return                      The number of ticks until this function should
- *                                  be called again; currently always
- *                                  UINT32_MAX.
+ *                                  be called again.
  */
 int32_t
-ble_gattc_heartbeat(void)
+ble_gattc_timer(void)
 {
     struct ble_gattc_proc_list exp_list;
     struct ble_gattc_proc *proc;
+    int32_t ticks_until_exp;
 
     /* Remove timed-out procedures from the main list and insert them into a
-     * temporary list.  For any stalled procedures, set their pending bit so
-     * they can be retried.
+     * temporary list.  This function also calculates the number of ticks until
+     * the next expiration will occur.
      */
-    ble_gattc_extract_expired(&exp_list);
+    ticks_until_exp = ble_gattc_extract_expired(&exp_list);
 
     /* Terminate the connection associated with each timed-out procedure. */
     while ((proc = STAILQ_FIRST(&exp_list)) != NULL) {
@@ -941,7 +953,7 @@ ble_gattc_heartbeat(void)
         ble_gattc_proc_free(proc);
     }
 
-    return BLE_HS_FOREVER;
+    return ticks_until_exp;
 }
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d0cc8330/net/nimble/host/src/ble_hs.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs.c b/net/nimble/host/src/ble_hs.c
index 7f7383b..fd45511 100644
--- a/net/nimble/host/src/ble_hs.c
+++ b/net/nimble/host/src/ble_hs.c
@@ -25,6 +25,7 @@
 #include "bsp/bsp.h"
 #include "stats/stats.h"
 #include "os/os.h"
+#include "console/console.h"
 #include "nimble/ble_hci_trans.h"
 #include "ble_hs_priv.h"
 
@@ -36,6 +37,7 @@ static void ble_hs_event_rx_hci_ev(struct os_event *ev);
 static void ble_hs_event_tx_notify(struct os_event *ev);
 static void ble_hs_event_reset(struct os_event *ev);
 static void ble_hs_event_start(struct os_event *ev);
+static void ble_hs_timer_sched(int32_t ticks_from_now);
 
 struct os_mempool ble_hs_hci_ev_pool;
 static os_membuf_t ble_hs_hci_os_event_buf[
@@ -70,7 +72,7 @@ static struct os_task *ble_hs_parent_task;
  * Handles unresponsive timeouts and periodic retries in case of resource
  * shortage.
  */
-static struct os_callout ble_hs_heartbeat_timer;
+static struct os_callout ble_hs_timer_timer;
 
 /* Shared queue that the host uses for work items. */
 static struct os_eventq *ble_hs_evq;
@@ -208,36 +210,6 @@ ble_hs_clear_data_queue(struct os_mqueue *mqueue)
     }
 }
 
-
-static void
-ble_hs_heartbeat_timer_reset(uint32_t ticks)
-{
-    int rc;
-
-    rc = os_callout_reset(&ble_hs_heartbeat_timer, ticks);
-    BLE_HS_DBG_ASSERT_EVAL(rc == 0);
-}
-
-void
-ble_hs_heartbeat_sched(int32_t ticks_from_now)
-{
-    os_time_t abs_time;
-
-    if (ticks_from_now == BLE_HS_FOREVER) {
-        return;
-    }
-
-    /* Reset heartbeat timer if it is not currently scheduled or if the
-     * specified time is sooner than the current expiration time.
-     */
-    abs_time = os_time_get() + ticks_from_now;
-    if (!os_callout_queued(&ble_hs_heartbeat_timer) ||
-        OS_TIME_TICK_LT(abs_time, ble_hs_heartbeat_timer.c_ticks)) {
-
-        ble_hs_heartbeat_timer_reset(ticks_from_now);
-    }
-}
-
 /**
  * Indicates whether the host has synchronized with the controller.
  * Synchronization must occur before any host procedures can be performed.
@@ -272,7 +244,7 @@ ble_hs_sync(void)
         ble_hs_sync_state = BLE_HS_SYNC_STATE_BAD;
     }
 
-    ble_hs_heartbeat_sched(BLE_HS_SYNC_RETRY_RATE);
+    ble_hs_timer_sched(BLE_HS_SYNC_RETRY_RATE);
 
     if (rc == 0) {
         STATS_INC(ble_hs_stats, sync);
@@ -318,11 +290,11 @@ ble_hs_reset(void)
 }
 
 /**
- * Called once a second by the ble_hs heartbeat timer.  Handles unresponsive
- * timeouts and periodic retries in case of resource shortage.
+ * Called when the host timer expires.  Handles unresponsive timeouts and
+ * periodic retries in case of resource shortage.
  */
 static void
-ble_hs_heartbeat(struct os_event *ev)
+ble_hs_timer_exp(struct os_event *ev)
 {
     int32_t ticks_until_next;
 
@@ -331,24 +303,56 @@ ble_hs_heartbeat(struct os_event *ev)
         return;
     }
 
-    /* Ensure the timer expires at least once in the next second.
-     * XXX: This is not very power efficient.  We will need separate timers for
-     * each module.
-     */
-    ticks_until_next = BLE_HS_HEARTBEAT_OS_TICKS;
-    ble_hs_heartbeat_sched(ticks_until_next);
+    ticks_until_next = ble_gattc_timer();
+    ble_hs_timer_sched(ticks_until_next);
+
+    ticks_until_next = ble_gap_timer();
+    ble_hs_timer_sched(ticks_until_next);
 
-    ticks_until_next = ble_gattc_heartbeat();
-    ble_hs_heartbeat_sched(ticks_until_next);
+    ticks_until_next = ble_l2cap_sig_timer();
+    ble_hs_timer_sched(ticks_until_next);
 
-    ticks_until_next = ble_gap_heartbeat();
-    ble_hs_heartbeat_sched(ticks_until_next);
+    ticks_until_next = ble_sm_timer();
+    ble_hs_timer_sched(ticks_until_next);
+}
+
+static void
+ble_hs_timer_timer_reset(uint32_t ticks)
+{
+    int rc;
+
+    rc = os_callout_reset(&ble_hs_timer_timer, ticks);
+    BLE_HS_DBG_ASSERT_EVAL(rc == 0);
+}
+
+static void
+ble_hs_timer_sched(int32_t ticks_from_now)
+{
+    os_time_t abs_time;
 
-    ticks_until_next = ble_l2cap_sig_heartbeat();
-    ble_hs_heartbeat_sched(ticks_until_next);
+    if (ticks_from_now == BLE_HS_FOREVER) {
+        return;
+    }
+
+    /* Reset timer if it is not currently scheduled or if the specified time is
+     * sooner than the previous expiration time.
+     */
+    abs_time = os_time_get() + ticks_from_now;
+    if (!os_callout_queued(&ble_hs_timer_timer) ||
+        OS_TIME_TICK_LT(abs_time, ble_hs_timer_timer.c_ticks)) {
 
-    ticks_until_next = ble_sm_heartbeat();
-    ble_hs_heartbeat_sched(ticks_until_next);
+        ble_hs_timer_timer_reset(ticks_from_now);
+        console_printf("TICKS_UNTIL_NEXT: %d\n", (int)ticks_from_now);
+    }
+}
+
+void
+ble_hs_timer_resched(void)
+{
+    /* Reschedule the timer to run immediately.  The timer callback will query
+     * each module for an up-to-date expiration time.
+     */
+    ble_hs_timer_timer_reset(0);
 }
 
 static void
@@ -457,8 +461,8 @@ ble_hs_start(void)
 
     ble_hs_parent_task = os_sched_get_current_task();
 
-    os_callout_init(&ble_hs_heartbeat_timer, ble_hs_evq_get(),
-                    ble_hs_heartbeat, NULL);
+    os_callout_init(&ble_hs_timer_timer, ble_hs_evq_get(),
+                    ble_hs_timer_exp, NULL);
 
     rc = ble_att_svr_start();
     if (rc != 0) {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d0cc8330/net/nimble/host/src/ble_hs_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_priv.h b/net/nimble/host/src/ble_hs_priv.h
index 3ac8388..905386b 100644
--- a/net/nimble/host/src/ble_hs_priv.h
+++ b/net/nimble/host/src/ble_hs_priv.h
@@ -109,7 +109,7 @@ void ble_hs_lock(void);
 void ble_hs_unlock(void);
 void ble_hs_sched_reset(int reason);
 void ble_hs_hw_error(uint8_t hw_code);
-void ble_hs_heartbeat_sched(int32_t ticks);
+void ble_hs_timer_resched(void);
 void ble_hs_notifications_sched(void);
 
 #if MYNEWT_VAL(LOG_LEVEL) <= LOG_LEVEL_DEBUG

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d0cc8330/net/nimble/host/src/ble_l2cap_sig.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_sig.c b/net/nimble/host/src/ble_l2cap_sig.c
index 7954be9..3b548c0 100644
--- a/net/nimble/host/src/ble_l2cap_sig.c
+++ b/net/nimble/host/src/ble_l2cap_sig.c
@@ -601,16 +601,10 @@ ble_l2cap_sig_conn_broken(uint16_t conn_handle, int reason)
 }
 
 /**
- * Applies periodic checks and actions to all active procedures.
- *
- * All procedures that have been expecting a response for longer than 30
- * seconds are aborted and their corresponding connection is terminated.
- *
- * Called by the timer timer; executed at least once a second.
+ * Terminates expired procedures.
  *
  * @return                      The number of ticks until this function should
- *                                  be called again; currently always
- *                                  UINT32_MAX.
+ *                                  be called again.
  */
 int32_t
 ble_l2cap_sig_timer(void)

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d0cc8330/net/nimble/host/src/ble_sm.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_sm.c b/net/nimble/host/src/ble_sm.c
index c561315..3421b06 100644
--- a/net/nimble/host/src/ble_sm.c
+++ b/net/nimble/host/src/ble_sm.c
@@ -349,6 +349,7 @@ ble_sm_proc_set_timer(struct ble_sm_proc *proc)
 {
     /* Set a timeout of 30 seconds. */
     proc->exp_os_ticks = os_time_get() + BLE_SM_TIMEOUT_OS_TICKS;
+    ble_hs_timer_resched();
 }
 
 static ble_sm_rx_fn *
@@ -601,18 +602,22 @@ ble_sm_insert(struct ble_sm_proc *proc)
     STAILQ_INSERT_HEAD(&ble_sm_procs, proc, next);
 }
 
-static void
+static int32_t
 ble_sm_extract_expired(struct ble_sm_proc_list *dst_list)
 {
     struct ble_sm_proc *proc;
     struct ble_sm_proc *prev;
     struct ble_sm_proc *next;
     uint32_t now;
+    int32_t next_exp_in;
     int32_t time_diff;
 
     now = os_time_get();
     STAILQ_INIT(dst_list);
 
+    /* Assume each event is either expired or has infinite duration. */
+    next_exp_in = BLE_HS_FOREVER;
+
     ble_hs_lock();
 
     prev = NULL;
@@ -620,14 +625,19 @@ ble_sm_extract_expired(struct ble_sm_proc_list *dst_list)
     while (proc != NULL) {
         next = STAILQ_NEXT(proc, next);
 
-        time_diff = now - proc->exp_os_ticks;
-        if (time_diff >= 0) {
+        time_diff = proc->exp_os_ticks - now;
+        if (time_diff <= 0) {
+            /* Procedure has expired; move it to the destination list. */
             if (prev == NULL) {
                 STAILQ_REMOVE_HEAD(&ble_sm_procs, next);
             } else {
                 STAILQ_REMOVE_AFTER(&ble_sm_procs, prev, next);
             }
             STAILQ_INSERT_HEAD(dst_list, proc, next);
+        } else {
+            if (time_diff < next_exp_in) {
+                next_exp_in = time_diff;
+            }
         }
 
         prev = proc;
@@ -637,6 +647,8 @@ ble_sm_extract_expired(struct ble_sm_proc_list *dst_list)
     ble_sm_dbg_assert_no_cycles();
 
     ble_hs_unlock();
+
+    return next_exp_in;
 }
 
 static void
@@ -1980,27 +1992,23 @@ ble_sm_fail_rx(uint16_t conn_handle, uint8_t op, struct os_mbuf **om,
  *****************************************************************************/
 
 /**
- * Applies periodic checks and actions to all active procedures.
- *
- * All procedures that have been expecting a response for longer than 30
- * seconds are aborted.
- *
- * Called by the heartbeat timer; executed at least once a second.
+ * Times out expired SM procedures.
  *
  * @return                      The number of ticks until this function should
- *                                  be called again; currently always
- *                                  UINT32_MAX.
+ *                                  be called again.
  */
 int32_t
-ble_sm_heartbeat(void)
+ble_sm_timer(void)
 {
     struct ble_sm_proc_list exp_list;
     struct ble_sm_proc *proc;
+    int32_t ticks_until_exp;
 
-    /* Remove all timed out procedures and insert them into a temporary
-     * list.
+    /* Remove timed-out procedures from the main list and insert them into a
+     * temporary list.  This function also calculates the number of ticks until
+     * the next expiration will occur.
      */
-    ble_sm_extract_expired(&exp_list);
+    ticks_until_exp = ble_sm_extract_expired(&exp_list);
 
     /* Notify application of each failure and free the corresponding procedure
      * object.
@@ -2014,7 +2022,7 @@ ble_sm_heartbeat(void)
         ble_sm_proc_free(proc);
     }
 
-    return BLE_HS_FOREVER;
+    return ticks_until_exp;
 }
 
 /**
@@ -2028,8 +2036,7 @@ ble_sm_pair_initiate(uint16_t conn_handle)
 
     /* Make sure a procedure isn't already in progress for this connection. */
     ble_hs_lock();
-    proc = ble_sm_proc_find(conn_handle, BLE_SM_PROC_STATE_NONE,
-                                  -1, NULL);
+    proc = ble_sm_proc_find(conn_handle, BLE_SM_PROC_STATE_NONE, -1, NULL);
     if (proc != NULL) {
         res.app_status = BLE_HS_EALREADY;
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d0cc8330/net/nimble/host/src/ble_sm_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_sm_priv.h b/net/nimble/host/src/ble_sm_priv.h
index 1b9ff55..ba8a0be 100644
--- a/net/nimble/host/src/ble_sm_priv.h
+++ b/net/nimble/host/src/ble_sm_priv.h
@@ -456,7 +456,7 @@ void ble_sm_ia_ra(struct ble_sm_proc *proc,
                   uint8_t *out_iat, uint8_t *out_ia,
                   uint8_t *out_rat, uint8_t *out_ra);
 
-int32_t ble_sm_heartbeat(void);
+int32_t ble_sm_timer(void);
 void ble_sm_connection_broken(uint16_t conn_handle);
 int ble_sm_pair_initiate(uint16_t conn_handle);
 int ble_sm_slave_initiate(uint16_t conn_handle);
@@ -475,7 +475,7 @@ int ble_sm_init(void);
 #define ble_sm_ltk_req_rx(evt) ((void)(evt))
 #define ble_sm_enc_key_refresh_rx(evt) ((void)(evt))
 
-#define ble_sm_heartbeat() BLE_HS_FOREVER
+#define ble_sm_timer() BLE_HS_FOREVER
 #define ble_sm_connection_broken(conn_handle)
 #define ble_sm_pair_initiate(conn_handle)   BLE_HS_ENOTSUP
 #define ble_sm_slave_initiate(conn_handle)  BLE_HS_ENOTSUP

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d0cc8330/net/nimble/host/test/src/ble_gap_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/test/src/ble_gap_test.c b/net/nimble/host/test/src/ble_gap_test.c
index 8acf2f2..994b2a1 100644
--- a/net/nimble/host/test/src/ble_gap_test.c
+++ b/net/nimble/host/test/src/ble_gap_test.c
@@ -1907,7 +1907,7 @@ ble_gap_test_util_update_no_l2cap_tmo(struct ble_gap_upd_params *params,
 
     /* Advance 29 seconds; ensure no timeout reported. */
     os_time_advance(29 * OS_TICKS_PER_SEC);
-    ble_gap_heartbeat();
+    ble_gap_timer();
     TEST_ASSERT(ble_gap_test_event.type == 0xff);
 
     /* Advance 30th second; ensure timeout reported. */
@@ -1918,7 +1918,7 @@ ble_gap_test_util_update_no_l2cap_tmo(struct ble_gap_upd_params *params,
      */
     ble_hs_test_util_set_ack_disconnect(0);
 
-    ble_gap_heartbeat();
+    ble_gap_timer();
 
     /* Verify terminate was sent. */
     ble_gap_test_util_verify_tx_disconnect();
@@ -1997,8 +1997,8 @@ ble_gap_test_util_update_l2cap_tmo(struct ble_gap_upd_params *params,
 
     /* Advance 29 seconds; ensure no timeout reported. */
     os_time_advance(29 * OS_TICKS_PER_SEC);
-    ble_gap_heartbeat();
-    ble_l2cap_sig_heartbeat();
+    ble_gap_timer();
+    ble_l2cap_sig_timer();
     TEST_ASSERT(ble_gap_test_event.type == 0xff);
 
     /* Advance 30th second; ensure timeout reported. */
@@ -2009,8 +2009,8 @@ ble_gap_test_util_update_l2cap_tmo(struct ble_gap_upd_params *params,
      */
     ble_hs_test_util_set_ack_disconnect(0);
 
-    ble_gap_heartbeat();
-    ble_l2cap_sig_heartbeat();
+    ble_gap_timer();
+    ble_l2cap_sig_timer();
 
     /* Verify terminate was sent. */
     ble_gap_test_util_verify_tx_disconnect();
@@ -2579,12 +2579,12 @@ ble_gap_test_util_conn_forever(void)
                              NULL, 0);
 
     /* Ensure no pending GAP event. */
-    ticks_from_now = ble_gap_heartbeat();
+    ticks_from_now = ble_gap_timer();
     TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER);
 
     /* Advance 100 seconds; ensure no timeout reported. */
     os_time_advance(100 * OS_TICKS_PER_SEC);
-    ble_gap_heartbeat();
+    ble_gap_timer();
     TEST_ASSERT(ble_gap_test_event.type == 0xff);
     TEST_ASSERT(ble_gap_conn_active());
 }
@@ -2609,7 +2609,7 @@ ble_gap_test_util_conn_timeout(int32_t duration_ms)
     /* Ensure next GAP event is at the expected time. */
     rc = os_time_ms_to_ticks(duration_ms, &duration_ticks);
     TEST_ASSERT_FATAL(rc == 0);
-    ticks_from_now = ble_gap_heartbeat();
+    ticks_from_now = ble_gap_timer();
     TEST_ASSERT(ticks_from_now == duration_ticks);
 
     /* Advance duration ms; ensure timeout event does not get reported before
@@ -2624,14 +2624,14 @@ ble_gap_test_util_conn_timeout(int32_t duration_ms)
 
     TEST_ASSERT(ble_gap_test_event.type == 0xff);
 
-    ticks_from_now = ble_gap_heartbeat();
+    ticks_from_now = ble_gap_timer();
     TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER);
 
     /* Ensure cancel create connection command was sent. */
     ble_hs_test_util_verify_tx_create_conn_cancel();
 
     /* Ensure timer has been stopped. */
-    ticks_from_now = ble_gap_heartbeat();
+    ticks_from_now = ble_gap_timer();
     TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER);
 
     /* Receive the connection complete event indicating a successful cancel. */
@@ -2663,7 +2663,7 @@ ble_gap_test_util_disc_forever(void)
                           NULL, -1, 0);
 
     /* Ensure no pending GAP event. */
-    ticks_from_now = ble_gap_heartbeat();
+    ticks_from_now = ble_gap_timer();
     TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER);
 
     /* Advance 100 seconds; ensure no timeout reported. */
@@ -2692,7 +2692,7 @@ ble_gap_test_util_disc_timeout(int32_t duration_ms)
     /* Ensure next GAP event is at the expected time. */
     rc = os_time_ms_to_ticks(duration_ms, &duration_ticks);
     TEST_ASSERT_FATAL(rc == 0);
-    ticks_from_now = ble_gap_heartbeat();
+    ticks_from_now = ble_gap_timer();
     TEST_ASSERT(ticks_from_now == duration_ticks);
 
     /* Advance duration ms; ensure timeout event was reported. */
@@ -2702,7 +2702,7 @@ ble_gap_test_util_disc_timeout(int32_t duration_ms)
         ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE,
                                     BLE_HCI_OCF_LE_SET_SCAN_ENABLE),
         0);
-    ticks_from_now = ble_gap_heartbeat();
+    ticks_from_now = ble_gap_timer();
     TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER);
 
     TEST_ASSERT(ble_gap_test_disc_event_type == BLE_GAP_EVENT_DISC_COMPLETE);