+ /* Resets the decay when state changes to STATE_UP
+ * and decay_min_rx is configured. */
+ if (bfd->state == STATE_UP && bfd->decay_min_rx) {
+ bfd_decay_update(bfd);
+ }
+ }
+
+ /* Updates the forwarding flag. */
+ bfd_forwarding__(bfd);
+}
+
+static uint64_t
+bfd_rx_packets(const struct bfd *bfd) OVS_REQUIRES(mutex)
+{
+ struct netdev_stats stats;
+
+ if (!netdev_get_stats(bfd->netdev, &stats)) {
+ return stats.rx_packets;
+ } else {
+ return 0;
+ }
+}
+
+/* Decays the bfd->min_rx to bfd->decay_min_rx when 'diff' is less than
+ * the 'expect' value. */
+static void
+bfd_try_decay(struct bfd *bfd) OVS_REQUIRES(mutex)
+{
+ int64_t diff, expect;
+
+ /* The 'diff' is the difference between current interface rx_packets
+ * stats and last-time check. The 'expect' is the recorded number of
+ * bfd control packets received within an approximately decay_min_rx
+ * (2000 ms if decay_min_rx is less than 2000 ms) interval.
+ *
+ * Since the update of rx_packets stats at interface happens
+ * asynchronously to the bfd_rx_packets() function, the 'diff' value
+ * can be jittered. Thusly, we double the decay_rx_ctl to provide
+ * more wiggle room. */
+ diff = bfd_rx_packets(bfd) - bfd->decay_rx_packets;
+ expect = 2 * MAX(bfd->decay_rx_ctl, 1);
+ bfd->in_decay = diff <= expect ? true : false;
+ bfd_decay_update(bfd);
+}
+
+/* Updates the rx_packets, decay_rx_ctl and decay_detect_time. */
+static void
+bfd_decay_update(struct bfd * bfd) OVS_REQUIRES(mutex)
+{
+ bfd->decay_rx_packets = bfd_rx_packets(bfd);
+ bfd->decay_rx_ctl = 0;
+ bfd->decay_detect_time = MAX(bfd->decay_min_rx, 2000) + time_msec();
+}
+
+/* Checks if there are packets received during the time since last call.
+ * If forwarding_if_rx is enabled and packets are received, updates the
+ * forwarding_if_rx_detect_time. */
+static void
+bfd_check_rx(struct bfd *bfd) OVS_REQUIRES(mutex)
+{
+ uint64_t rx_packets = bfd_rx_packets(bfd);
+ int64_t diff;
+
+ diff = rx_packets - bfd->rx_packets;
+ bfd->rx_packets = rx_packets;
+ if (diff < 0) {
+ VLOG_INFO_RL(&rl, "rx_packets count is smaller than last time.");
+ }
+ if (bfd->forwarding_if_rx && diff > 0) {
+ bfd_forwarding_if_rx_update(bfd);