Commit 44b41686 authored by Andreas Schmidt's avatar Andreas Schmidt
Browse files

Add first BBR pieces.

parent 43734ead
Loading
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
add_library(PRRT ../defines.h
        block.c block.h
        bbr.c bbr.h
        channelStateInformation.c channelStateInformation.h
        clock.c clock.h
        codingParams.c codingParams.h
@@ -20,6 +21,6 @@ add_library(PRRT ../defines.h
        types/packetTimeout.c types/packetTimeout.h
        types/lossStatistics.c types/lossStatistics.h
        types/packet.c types/packet.h
        vdmcode/block_code.c vdmcode/block_code.h)
        vdmcode/block_code.c vdmcode/block_code.h types/packetTracking.c types/packetTracking.h types/rateSample.c types/rateSample.h)

target_link_libraries(PRRT rt)

prrt/proto/bbr.c

0 → 100644
+280 −0
Original line number Diff line number Diff line
#include "bbr.h"
#include "../util/dbg.h"
#include "../util/common.h"
#include "receiver.h"


uint32_t BBR_Inflight(BBR* bbr, float gain)
{
    if (bbr->rtprop == Inf)
        return InitialCwnd; /* no valid RTT samples yet */
    uint32_t quanta = 3 * bbr->send_quantum;
    uint32_t estimated_bdp = bbr->bw * bbr->rtprop;
    return (uint32_t)(gain * estimated_bdp + quanta);
}

void BBR_EnterStartup(BBR* bbr)
{
    bbr->state = STARTUP;
    bbr->pacing_gain = BBRHighGain;
    bbr->cwnd_gain = BBRHighGain;
}

void BBR_UpdateBtlBw(BBR* bbr, PrrtRateSample* rs, prrtByteCount_t packet_delivered, prrtByteCount_t delivered)
{
    if (packet_delivered >= bbr->next_round_delivered) {
        bbr->next_round_delivered = delivered;
        bbr->round_count++;
        bbr->round_start = true;
    } else {
        bbr->round_start = false;
    }
    if ((rs->delivery_rate >= bbr->bw || !rs->is_app_limited) && rs->delivery_rate != 0) {
        bbr->bw = (uint32_t)WindowedFilter_push(bbr->btlBwFilter, (uint32_t)rs->delivery_rate);
        debug(DEBUG_BBR, "Current BtlBw: %u, RS delivery rate: %u", bbr->bw, (uint32_t)rs->delivery_rate);
    }
}

void BBR_CheckFullPipe(BBR* bbr, PrrtRateSample* rs)
{
    if (bbr->filled_pipe || !bbr->round_start || rs->is_app_limited)
        return;  // no need to check for a full pipe now
    if (bbr->bw >= bbr->full_bw * 1.25) {  // BBR.BtlBw still growing?
        bbr->full_bw = bbr->bw;    // record new baseline level
        bbr->full_bw_count = 0;
        return;
    }
    bbr->full_bw_count++; // another round w/o much growth
    if (bbr->full_bw_count >= 3)
        bbr->filled_pipe = true;
}

bool BBR_IsNextCyclePhase(BBR* bbr, prrtSequenceNumber_t packets_lost, prrtSequenceNumber_t prior_inflight)
{
    bool is_full_length = (PrrtClock_get_current_time_us() - bbr->cycle_stamp) > bbr->rtprop;
    if (bbr->pacing_gain == 1)
        return is_full_length;
    if (bbr->pacing_gain > 1)
        return is_full_length && (packets_lost > 0 || prior_inflight >= BBR_Inflight(bbr, bbr->pacing_gain));
    return is_full_length || (prior_inflight <= BBR_Inflight(bbr, 1.0));
}

void BBR_AdvanceCyclePhase(BBR* bbr)
{
    bbr->cycle_stamp = PrrtClock_get_current_time_us();
    bbr->cycle_index = (uint8_t )((bbr->cycle_index + 1) % BBRGainCycleLen);
    float pacing_gain_cycle[BBRGainCycleLen] = {1.25, 0.75, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    bbr->pacing_gain = pacing_gain_cycle[bbr->cycle_index];
    debug(DEBUG_BBR, "Advanced cycle with gain: %f", bbr->pacing_gain);
}

void BBR_CheckCyclePhase(BBR* bbr, prrtSequenceNumber_t packets_lost, prrtSequenceNumber_t prior_inflight) {
    if (bbr->state == PROBE_BW && BBR_IsNextCyclePhase(bbr, packets_lost, prior_inflight))
        BBR_AdvanceCyclePhase(bbr);
}

void BBR_EnterProbeBW(BBR* bbr)
{
    bbr->state = PROBE_BW;
    bbr->pacing_gain = 1;
    bbr->cwnd_gain = 2;
    bbr->cycle_index = (uint8_t)(BBRGainCycleLen - 1 - (random() % 7));
    BBR_AdvanceCyclePhase(bbr);
}

void BBR_CheckDrain(BBR* bbr, prrtSequenceNumber_t packets_in_flight)
{
    if (bbr->state == STARTUP && bbr->filled_pipe) {
        //Drain
        bbr->state = DRAIN;
        bbr->pacing_gain = 1 / BBRHighGain;  // pace slowly
        bbr->cwnd_gain = BBRHighGain;    // maintain cwnd
    }
    if (bbr->state == DRAIN && packets_in_flight <= BBR_Inflight(bbr, 1.0))
        BBR_EnterProbeBW(bbr);  // we estimate queue is drained
}

void BBR_ExitProbeRTT(BBR* bbr)
{
    if (bbr->filled_pipe)
        BBR_EnterProbeBW(bbr);
    else
        BBR_EnterStartup(bbr);
}

uint32_t BBR_SaveCwnd(BBR* bbr)
{
    //if (!InLossRecovery() && bbr->state != PROBE_RTT)
    if (bbr->state != PROBE_RTT)
        return bbr->cwnd;
    return MAX(bbr->prior_cwnd ,bbr->cwnd);
}

void BBR_RestoreCwnd(BBR* bbr)
{
    bbr->cwnd = bbr->cwnd > bbr->prior_cwnd ? bbr->cwnd : bbr->prior_cwnd;
}

void BBR_UpdateRTprop(BBR* bbr, prrtTimedelta_t rtt, prrtByteCount_t delivered, prrtSequenceNumber_t packets_in_flight)
{
    bbr->rtprop_expired = PrrtClock_get_current_time_us() > (bbr->rtprop_stamp + RTpropFilterLen);
    if (rtt >= 0 && (rtt <= bbr->rtprop || bbr->rtprop_expired)) {
        bbr->rtprop = rtt;
        bbr->rtprop_stamp = PrrtClock_get_current_time_us();
    }

    if (bbr->state != PROBE_RTT && bbr->rtprop_expired && !bbr->idle_restart) {
        bbr->state = PROBE_RTT;
        bbr->pacing_gain = 1;
        bbr->cwnd_gain = 1;
        BBR_SaveCwnd(bbr);
        bbr->probe_rtt_done_stamp = 0;
    }

    if (bbr->state == PROBE_RTT) {
        /* Ignore low rate samples during ProbeRTT: */
        prrtTimestamp_t now = PrrtClock_get_current_time_us();
        if (bbr->probe_rtt_done_stamp == 0 && packets_in_flight <= BBRMinPipeCwnd) {
            bbr->probe_rtt_done_stamp = now + ProbeRTTDuration;
            bbr->probe_rtt_round_done = false;
            bbr->next_round_delivered = delivered;
        } else if (bbr->probe_rtt_done_stamp != 0) {
            if (bbr->round_start)
                bbr->probe_rtt_round_done = true;
            if (bbr->probe_rtt_round_done && (now > bbr->probe_rtt_done_stamp)) {
                bbr->rtprop_stamp = now;
                BBR_RestoreCwnd(bbr);
                BBR_ExitProbeRTT(bbr);
            }
        }
    }
    bbr->idle_restart = false;
}

void BBR_UpdateModel(BBR* bbr, PrrtChannelStateInformation* csi, PrrtRateSample* rs, PrrtPacketTracking* packetTracking)
{
    BBR_UpdateBtlBw(bbr, rs, packetTracking->bytes_delivered, packetTracking->delivered);
    BBR_CheckCyclePhase(bbr, packetTracking->packets_lost, packetTracking->prior_inflight);
    BBR_CheckFullPipe(bbr, rs);
    BBR_CheckDrain(bbr, packetTracking->packets_lost);
    BBR_UpdateRTprop(bbr, PrrtChannelStateInformation_get_rtprop(csi), packetTracking->delivered, packetTracking->packets_in_flight);
}

void BBR_UpdateTargetCwnd(BBR* bbr)
{
    bbr->target_cwnd = BBR_Inflight(bbr, bbr->cwnd_gain);
}

void BBR_ModulateCwndForProbeRTT(BBR* bbr)
{
    if (bbr->state == PROBE_RTT)
        bbr->cwnd = MIN(bbr->cwnd, BBRMinPipeCwnd);
}

void BBR_ModulateCwndForRecovery(BBR* bbr, prrtSequenceNumber_t packets_lost, prrtSequenceNumber_t packets_in_flight, prrtSequenceNumber_t packets_delivered)
{
    if (packets_lost > 0)
        bbr->cwnd = MAX(bbr->cwnd - packets_lost, 1);
    if (bbr->packet_conservation)
        bbr->cwnd = MAX(bbr->cwnd, packets_in_flight + packets_delivered);
}


void BBR_SetCwnd(BBR* bbr, PrrtPacketTracking* packetTracking)
{
    BBR_UpdateTargetCwnd(bbr);
    BBR_ModulateCwndForRecovery(bbr, packetTracking->packets_lost, packetTracking->packets_in_flight, packetTracking->packets_delivered);
    if (!bbr->packet_conservation) {
        if (bbr->filled_pipe)
            bbr->cwnd = MIN(bbr->cwnd + packetTracking->packets_delivered, bbr->target_cwnd);
        else if (bbr->cwnd < bbr->target_cwnd || packetTracking->delivered < InitialCwnd)
            bbr->cwnd = bbr->cwnd + packetTracking->packets_delivered;
        bbr->cwnd = MAX(bbr->cwnd, BBRMinPipeCwnd);
    }
    BBR_ModulateCwndForProbeRTT(bbr);
    debug(DEBUG_BBR, "New cwnd: %u, State: %u", bbr->cwnd, bbr->state);
}

void BBR_SetPacingRateWithGain(BBR* bbr, float pacing_gain)
{
    uint32_t rate = (uint32_t) (pacing_gain * (float)bbr->bw);
    debug(DEBUG_BBR, "Current rate: %u, Pacing gain: %f, BtlBw: %u, Calc Rate: %u, Filled pipe: %u", bbr->pacing_rate,
          pacing_gain, bbr->bw, rate, bbr->filled_pipe);
    if (rate != 0 && (bbr->filled_pipe || rate > bbr->pacing_rate))
        bbr->pacing_rate = rate;
}

void BBR_SetPacingRate(BBR* bbr)
{
    BBR_SetPacingRateWithGain(bbr, bbr->pacing_gain);
}

void BBR_OnACK(BBR* bbr, PrrtChannelStateInformation* csi, PrrtRateSample* rs, PrrtPacketTracking* packetTracking)
{
    BBR_UpdateModel(bbr, csi, rs, packetTracking);

    BBR_SetPacingRate(bbr);

    BBR_SetCwnd(bbr, packetTracking);
}

BBR* BBR_Init()
{
    BBR* bbr = calloc(1, sizeof(BBR));
    check_mem(bbr);
    bbr->btlBwFilter = WindowedFilter_Init(true);
    //bbr->rtprop = SRTT ? SRTT : Inf;
    bbr->rtprop = UINT32_MAX;
    bbr->rtprop_stamp = PrrtClock_get_current_time_us();
    bbr->probe_rtt_done_stamp = 0;
    bbr->probe_rtt_round_done = false;
    bbr->packet_conservation = false;
    bbr->prior_cwnd = 0;
    bbr->idle_restart = false;

    //Init round counting
    bbr->next_round_delivered = 0;
    bbr->round_start = false;
    bbr->round_count = 0;

    //Init full pipe
    bbr->filled_pipe = false;
    bbr->full_bw = 0;
    bbr->full_bw_count = 0;

    //Init pacing rate
    float nominal_bandwidth = InitialCwnd / (bbr->has_seen_rtt ? bbr->rtt : 1);
    bbr->pacing_rate = (uint32_t)(bbr->pacing_gain * nominal_bandwidth);
    BBR_EnterStartup(bbr);
    return bbr;

    error:
    PERROR("Failed to init BBR%s.", "");
    return NULL;
}

void BBR_destroy(BBR* bbr)
{
    WindowedFilter_destroy(bbr->btlBwFilter);
    free(bbr);
}

uint32_t BBR_getPacingRate(BBR* bbr)
{
    return bbr->pacing_rate;
}

uint32_t BBR_getCwnd(BBR* bbr)
{
    return bbr->cwnd;
}

uint32_t BBR_getBtlBw(BBR* bbr)
{
    return bbr->bw;
}

uint32_t BBR_getRTProp(BBR* bbr)
{
    return bbr->rtprop;
}
 No newline at end of file

prrt/proto/bbr.h

0 → 100644
+83 −0
Original line number Diff line number Diff line
#ifndef PRRT_BBR_H
#define PRRT_BBR_H

#include <stdint.h>
#include "stdbool.h"
#include "types/packet.h"
#include "clock.h"
#include "channelStateInformation.h"
#include "types/packetTracking.h"
#include "types/rateSample.h"
#include "../util/windowedFilter.h"

#define BtlBwFilterLen 10
#define RTpropFilterLen 10000000    //10s
#define BBRHighGain ((2885 / 1000) + 1)
#define ProbeRTTInterval 10
#define InitialCwnd 10
#define BBRGainCycleLen 8
#define BBRMinPipeCwnd 4
//#define BBRMinPipeCwnd 200
#define ProbeRTTDuration 200000 //200ms
#define Inf 0 //TODO: What is Inf

enum bbr_state {
    STARTUP,
    DRAIN,
    PROBE_BW,
    PROBE_RTT
};

typedef struct bbr {
    uint32_t rtprop;
    prrtTimestamp_t rtprop_stamp;
    uint32_t probe_rtt_done_stamp;
    bool probe_rtt_round_done;
    bool packet_conservation;
    uint32_t prior_cwnd;
    uint32_t cwnd;
    uint32_t target_cwnd;
    bool idle_restart;

    enum bbr_state state;

    float pacing_gain;
    float cwnd_gain;

    bool filled_pipe;
    uint64_t full_bw;
    uint32_t full_bw_count;

    uint32_t pacing_rate;
    bool has_seen_rtt;
    uint32_t rtt;

    uint32_t next_round_delivered;
    bool round_start;
    uint32_t round_count;

    uint32_t next_rtt_delivered;
    uint32_t rtt_count;
    bool rtprop_expired;

    prrtTimestamp_t cycle_stamp;
    uint8_t cycle_index;
    float* pacing_gain_cycle;

    uint32_t send_quantum;

    uint32_t bw;

    WindowedFilter* btlBwFilter;
} BBR;

BBR* BBR_Init();
void BBR_OnACK(BBR* bbr, PrrtChannelStateInformation* csi, PrrtRateSample* rs, PrrtPacketTracking* recv_stats);
void BBR_destroy(BBR* bbr);

uint32_t BBR_getPacingRate(BBR* bbr);
uint32_t BBR_getCwnd(BBR* bbr);
uint32_t BBR_getBtlBw(BBR* bbr);
uint32_t BBR_getRTProp(BBR* bbr);

#endif //PRRT_BBR_H
+0 −10
Original line number Diff line number Diff line
@@ -56,16 +56,6 @@ void PrrtChannelStateInformation_update_delivery_rate(PrrtChannelStateInformatio
    pthread_mutex_lock(&csi->lock);
    prrtDeliveryRate_t rate = rateSample->delivery_rate;
    csi->deliveryRate = rate;

    if(csi->btlbw_rounds > csi->btlbw_filter_length_rtts) {
        csi->btlbw = 0;
    }
    csi->btlbw_rounds += 1;

    if(csi->btlbw < rate) {
        csi->btlbw = rate;
        csi->btlbw_rounds = 0;
    }
    pthread_mutex_unlock(&csi->lock);
}
void PrrtChannelStateInformation_update_app_limited(PrrtChannelStateInformation *csi, bool appLimited) {
+1 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
#define PRRT_CHANNELSTATEINFORMATION_H

#include <stdbool.h>
#include "types/rateSample.h"
#include "types/packet.h"

typedef struct prrtChannelStateInformation {
Loading