summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKlement Sekera <ksekera@cisco.com>2016-12-08 05:19:14 +0100
committerDamjan Marion <dmarion.lists@gmail.com>2016-12-09 20:10:02 +0000
commit637b9c453161bfd551e0c04db78109d3d452a69a (patch)
treed86c56ad644347e73a3cc495abbd230139a04dc8
parent0279b29f327c15a1c6b2d8ede228790c1a7d3814 (diff)
BFD: handle timing wheel prematurely firing events
Improve handling of timeouts. Add a workaround for when timing wheel fires an event a tiny amount of time before it should actually be fired. Don't delete unneeded events at all from timing wheel, instead ignoring unexpected events. Enable the skipped BFD test, which passes now. Change-Id: I6ffd4fc0ba7a049ffe63bb0e5290641a7300dd6f Signed-off-by: Klement Sekera <ksekera@cisco.com>
-rw-r--r--test/test_bfd.py6
-rw-r--r--vnet/vnet/bfd/bfd_debug.h41
-rw-r--r--vnet/vnet/bfd/bfd_main.c97
-rw-r--r--vnet/vnet/bfd/bfd_main.h14
-rw-r--r--vnet/vnet/bfd/bfd_udp.c2
5 files changed, 88 insertions, 72 deletions
diff --git a/test/test_bfd.py b/test/test_bfd.py
index b48c3cc4e46..bf0e88ddcdd 100644
--- a/test/test_bfd.py
+++ b/test/test_bfd.py
@@ -252,14 +252,14 @@ class BFDTestCase(VppTestCase):
e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
self.verify_event(e, expected_state=BFDState.down)
- @unittest.skip("this test is not working yet")
def test_large_required_min_rx(self):
+ """ large remote RequiredMinRxInterval """
self.bfd_session_up()
- interval = 5000000
+ interval = 3000000
self.test_session.update(required_min_rx_interval=interval)
self.test_session.send_packet()
now = time.time()
- count = 1
+ count = 0
while time.time() < now + interval / 1000000:
try:
p = self.wait_for_bfd_packet()
diff --git a/vnet/vnet/bfd/bfd_debug.h b/vnet/vnet/bfd/bfd_debug.h
index 0072ff8fe82..707ebab2ddd 100644
--- a/vnet/vnet/bfd/bfd_debug.h
+++ b/vnet/vnet/bfd/bfd_debug.h
@@ -23,32 +23,30 @@
#define BFD_DEBUG (0)
#if BFD_DEBUG
-#define BFD_DEBUG_FILE_DEF \
- static const char *__file = NULL; \
- if (!__file) \
- { \
- __file = strrchr (__FILE__, '/'); \
- if (__file) \
- { \
- ++__file; \
- } \
- else \
- { \
- __file = __FILE__; \
- } \
- }
+#define BFD_DEBUG_FILE_DEF \
+ static const char *__file = NULL; \
+ { \
+ __file = strrchr (__FILE__, '/'); \
+ if (__file) \
+ { \
+ ++__file; \
+ } \
+ else \
+ { \
+ __file = __FILE__; \
+ } \
+ }
#define BFD_DBG(fmt, ...) \
do \
{ \
BFD_DEBUG_FILE_DEF \
- u8 *_s = NULL; \
+ static u8 *_s = NULL; \
vlib_main_t *vm = vlib_get_main (); \
_s = format (_s, "%6.02f:DBG:%s:%d:%s():" fmt, vlib_time_now (vm), \
__file, __LINE__, __func__, ##__VA_ARGS__); \
- printf ("%s\n", _s); \
- fflush (stdout); \
- vec_free (_s); \
+ printf ("%.*s\n", vec_len (_s), _s); \
+ vec_reset_length (_s); \
} \
while (0);
@@ -56,13 +54,12 @@
do \
{ \
BFD_DEBUG_FILE_DEF \
- u8 *_s = NULL; \
+ static u8 *_s = NULL; \
vlib_main_t *vm = vlib_get_main (); \
_s = format (_s, "%6.02f:ERR:%s:%d:%s():" fmt, vlib_time_now (vm), \
__file, __LINE__, __func__, ##__VA_ARGS__); \
- printf ("%s\n", _s); \
- fflush (stdout); \
- vec_free (_s); \
+ printf ("%.*s\n", vec_len (_s), _s); \
+ vec_reset_length (_s); \
} \
while (0);
diff --git a/vnet/vnet/bfd/bfd_main.c b/vnet/vnet/bfd/bfd_main.c
index ffc04ee4df0..e25eadfc510 100644
--- a/vnet/vnet/bfd/bfd_main.c
+++ b/vnet/vnet/bfd/bfd_main.c
@@ -129,6 +129,17 @@ bfd_calc_next_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now)
bs->tx_timeout_clocks = now +
(1 - .25 * (random_f64 (&bm->random_seed))) *
bs->transmit_interval_clocks;
+ if (bs->tx_timeout_clocks < now)
+ {
+ /* huh, we've missed it already, skip the missed events */
+ const u64 missed =
+ (now - bs->tx_timeout_clocks) / bs->transmit_interval_clocks;
+ BFD_ERR ("Missed %lu transmit events (now is %lu, calc "
+ "tx_timeout is %lu)!",
+ missed, now, bs->tx_timeout_clocks);
+ bs->tx_timeout_clocks +=
+ (missed + 1) * bs->transmit_interval_clocks;
+ }
}
else
{
@@ -137,6 +148,17 @@ bfd_calc_next_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now)
now +
(.9 - .15 * (random_f64 (&bm->random_seed))) *
bs->transmit_interval_clocks;
+ if (bs->tx_timeout_clocks < now)
+ {
+ /* huh, we've missed it already, skip the missed events */
+ const u64 missed =
+ (now - bs->tx_timeout_clocks) / bs->transmit_interval_clocks;
+ BFD_ERR ("Missed %lu transmit events (now is %lu, calc "
+ "tx_timeout is %lu)!",
+ missed, now, bs->tx_timeout_clocks);
+ bs->tx_timeout_clocks +=
+ (missed + 1) * bs->transmit_interval_clocks;
+ }
}
}
else
@@ -200,12 +222,11 @@ bfd_set_timer (bfd_main_t * bm, bfd_session_t * bs, u64 now,
BFD_DBG ("bs_idx=%u, tx_timeout=%lu, rx_timeout=%lu, next=%s", bs->bs_idx,
bs->tx_timeout_clocks, rx_timeout,
next == bs->tx_timeout_clocks ? "tx" : "rx");
- if (next && (next < bs->wheel_time_clocks || !bs->wheel_time_clocks))
+ /* sometimes the wheel expires an event a bit sooner than requested, account
+ for that here */
+ if (next && (now + bm->wheel_inaccuracy > bs->wheel_time_clocks ||
+ next < bs->wheel_time_clocks || !bs->wheel_time_clocks))
{
- if (bs->wheel_time_clocks)
- {
- timing_wheel_delete (&bm->wheel, bs->bs_idx);
- }
bs->wheel_time_clocks = next;
BFD_DBG ("timing_wheel_insert(%p, %lu (%ld clocks/%.2fs in the "
"future), %u);",
@@ -238,6 +259,22 @@ bfd_set_desired_min_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now,
bfd_set_timer (bm, bs, now, handling_wakeup);
}
+static void
+bfd_set_remote_required_min_rx (bfd_main_t * bm, bfd_session_t * bs,
+ u64 now,
+ u32 remote_required_min_rx_us,
+ int handling_wakeup)
+{
+ bs->remote_min_rx_us = remote_required_min_rx_us;
+ bs->remote_min_rx_clocks = bfd_us_to_clocks (bm, bs->remote_min_rx_us);
+ BFD_DBG ("Set remote min rx to %uus/%lu clocks/%.2fs", bs->remote_min_rx_us,
+ bs->remote_min_rx_clocks, bs->remote_min_rx_clocks / bm->cpu_cps);
+ bfd_recalc_detection_time (bm, bs);
+ bfd_recalc_tx_interval (bm, bs);
+ bfd_calc_next_tx (bm, bs, now);
+ bfd_set_timer (bm, bs, now, handling_wakeup);
+}
+
void
bfd_session_start (bfd_main_t * bm, bfd_session_t * bs)
{
@@ -470,7 +507,9 @@ bfd_send_periodic (vlib_main_t * vm, vlib_node_runtime_t * rt,
bfd.SessionState is Up, and bfd.RemoteSessionState is Up) and a Poll
Sequence is not being transmitted.
*/
- if (now >= bs->tx_timeout_clocks)
+ /* sometimes the wheel expires an event a bit sooner than requested, account
+ for that here */
+ if (now + bm->wheel_inaccuracy >= bs->tx_timeout_clocks)
{
BFD_DBG ("Send periodic control frame for bs_idx=%lu", bs->bs_idx);
vlib_buffer_t *b = bfd_create_frame (vm, rt, bs);
@@ -484,7 +523,9 @@ bfd_send_periodic (vlib_main_t * vm, vlib_node_runtime_t * rt,
}
else
{
- BFD_DBG ("No need to send control frame now");
+ BFD_DBG
+ ("No need to send control frame now, now is %lu, tx_timeout is %lu",
+ now, bs->tx_timeout_clocks);
}
bfd_set_timer (bm, bs, now, handling_wakeup);
}
@@ -502,7 +543,10 @@ static void
bfd_check_rx_timeout (bfd_main_t * bm, bfd_session_t * bs, u64 now,
int handling_wakeup)
{
- if (bs->last_rx_clocks + bs->detection_time_clocks < now)
+ /* sometimes the wheel expires an event a bit sooner than requested, account
+ for that here */
+ if (bs->last_rx_clocks + bs->detection_time_clocks <=
+ now + bm->wheel_inaccuracy)
{
BFD_DBG ("Rx timeout, session goes down");
bfd_set_diag (bs, BFD_DIAG_CODE_det_time_exp);
@@ -538,31 +582,6 @@ bfd_on_timeout (vlib_main_t * vm, vlib_node_runtime_t * rt, bfd_main_t * bm,
}
/*
- * bfd input routine
- */
-bfd_error_t
-bfd_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0)
-{
- // bfd_main_t *bm = &bfd_main;
- bfd_error_t e;
-
- /* find our interface */
- bfd_session_t *s = NULL;
- // bfd_get_intf (lm, vnet_buffer (b0)->sw_if_index[VLIB_RX]);
-
- if (!s)
- {
- /* bfd disabled on this interface, we're done */
- return BFD_ERROR_DISABLED;
- }
-
- /* Actually scan the packet */
- e = BFD_ERROR_NONE; // bfd_packet_scan (lm, n, vlib_buffer_get_current (b0));
-
- return e;
-}
-
-/*
* bfd process node function
*/
static uword
@@ -637,7 +656,6 @@ bfd_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
if (!pool_is_free_index (bm->sessions, bs_idx))
{
bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
- bs->wheel_time_clocks = 0; /* no longer scheduled */
bfd_on_timeout (vm, rt, bm, bs, now);
}
}
@@ -714,6 +732,7 @@ bfd_main_init (vlib_main_t * vm)
BFD_DBG ("cps is %.2f", bm->cpu_cps);
const u64 now = clib_cpu_time_now ();
timing_wheel_init (&bm->wheel, now, bm->cpu_cps);
+ bm->wheel_inaccuracy = 2 << bm->wheel.log2_clocks_per_bin;
return 0;
}
@@ -864,14 +883,12 @@ bfd_consume_pkt (bfd_main_t * bm, const bfd_pkt_t * pkt, u32 bs_idx)
bs->remote_discr = pkt->my_disc;
bs->remote_state = bfd_pkt_get_state (pkt);
bs->remote_demand = bfd_pkt_get_demand (pkt);
- bs->remote_min_rx_us = clib_net_to_host_u32 (pkt->req_min_rx);
- bs->remote_min_rx_clocks = bfd_us_to_clocks (bm, bs->remote_min_rx_us);
- BFD_DBG ("Set remote min rx to %lu clocks/%.2fs", bs->remote_min_rx_clocks,
- bs->remote_min_rx_clocks / bm->cpu_cps);
+ u64 now = clib_cpu_time_now ();
+ bs->last_rx_clocks = now;
bs->remote_desired_min_tx_us = clib_net_to_host_u32 (pkt->des_min_tx);
bs->remote_detect_mult = pkt->head.detect_mult;
- bfd_recalc_detection_time (bm, bs);
- bs->last_rx_clocks = clib_cpu_time_now ();
+ bfd_set_remote_required_min_rx (bm, bs, now,
+ clib_net_to_host_u32 (pkt->req_min_rx), 0);
/* FIXME
If the Required Min Echo RX Interval field is zero, the
transmission of Echo packets, if any, MUST cease.
diff --git a/vnet/vnet/bfd/bfd_main.h b/vnet/vnet/bfd/bfd_main.h
index 727903bd286..c72ea92a70f 100644
--- a/vnet/vnet/bfd/bfd_main.h
+++ b/vnet/vnet/bfd/bfd_main.h
@@ -49,7 +49,7 @@ typedef enum
typedef struct
{
/* index in bfd_main.sessions pool */
- uword bs_idx;
+ u32 bs_idx;
/* session state */
bfd_state_e local_state;
@@ -93,10 +93,13 @@ typedef struct
/* 1 if remote system sets demand mode, 0 otherwise */
u8 remote_demand;
+ /* local detect multiplier */
u8 local_detect_mult;
+
+ /* remote detect multiplier */
u8 remote_detect_mult;
- /* set to value of timer in timing wheel, 0 if not set */
+ /* set to value of timer in timing wheel, 0 if never set */
u64 wheel_time_clocks;
/* transmit interval */
@@ -134,6 +137,9 @@ typedef struct
/* timing wheel for scheduling timeouts */
timing_wheel_t wheel;
+ /* timing wheel inaccuracy, in clocks */
+ u64 wheel_inaccuracy;
+
/* hashmap - bfd session by discriminator */
u32 *session_by_disc;
@@ -150,9 +156,6 @@ typedef struct
/* for generating random numbers */
u32 random_seed;
- /* pool of event subscribers */
- //event_subscriber_t *subscribers;
-
} bfd_main_t;
extern bfd_main_t bfd_main;
@@ -184,7 +187,6 @@ enum
BFD_EVENT_NEW_SESSION,
} bfd_process_event_e;
-bfd_error_t bfd_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0);
u8 *bfd_input_format_trace (u8 * s, va_list * args);
bfd_session_t *bfd_get_session (bfd_main_t * bm, bfd_transport_t t);
diff --git a/vnet/vnet/bfd/bfd_udp.c b/vnet/vnet/bfd/bfd_udp.c
index 9d75e3ad9e0..3c747d86a10 100644
--- a/vnet/vnet/bfd/bfd_udp.c
+++ b/vnet/vnet/bfd/bfd_udp.c
@@ -442,7 +442,7 @@ static bfd_udp_error_t bfd_udp4_scan (vlib_main_t *vm, vlib_node_runtime_t *rt,
BFD_ERR ("BFD session lookup failed - no session matches BFD pkt");
return BFD_UDP_ERROR_BAD;
}
- BFD_DBG ("BFD session found, bs_idx=%d", bs->bs_idx);
+ BFD_DBG ("BFD session found, bs_idx=%u", bs->bs_idx);
if (!bfd_verify_pkt_session (pkt, b->current_length, bs))
{
return BFD_UDP_ERROR_BAD;