166 lines
5.7 KiB
C++
166 lines
5.7 KiB
C++
#ifndef NETWORK_SWITCH_SWITCH_BUFFER_H
|
|
#define NETWORK_SWITCH_SWITCH_BUFFER_H
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <optional>
|
|
|
|
#include "core/time.h"
|
|
#include "core/types.h"
|
|
#include "core/simulator.h"
|
|
#include "core/error.h"
|
|
#include "network/packet.h"
|
|
#include "network/link.h"
|
|
|
|
namespace dofs {
|
|
|
|
class NetworkSwitch;
|
|
|
|
// Base class for switch egress buffering with weighted priorities.
|
|
// Implementations: SharedBuffer, DedicatedBuffer.
|
|
class SwitchBuffer {
|
|
public:
|
|
struct Queued {
|
|
Packet pkt;
|
|
PortId egress;
|
|
FlowPriority prio;
|
|
Time enq_time;
|
|
Bytes size_bytes;
|
|
};
|
|
|
|
virtual ~SwitchBuffer() = default;
|
|
|
|
// --- API: enqueue & drain ---
|
|
// NOTE: ECN/drop decisions are expected to be done *before* enqueue;
|
|
// we still guard and return false on capacity violations.
|
|
virtual bool enqueue_packet(const Packet& pkt,
|
|
PortId egress,
|
|
FlowPriority prio) = 0;
|
|
|
|
// Attempt to send exactly one packet on 'port' using weighted priorities.
|
|
// Returns true if a packet was reserved and scheduled.
|
|
virtual bool drain_one(PortId port) = 0;
|
|
|
|
// --- Accessors (implemented in .cc) ---
|
|
Bytes buffer_size() const noexcept;
|
|
SwitchBufferType type() const noexcept;
|
|
uint16_t port_cnt() const noexcept;
|
|
Bytes port_buffered(PortId p) const noexcept;
|
|
const std::vector<Bytes> &ports_buffered() const noexcept;
|
|
|
|
// Priority shares: percentages [0..100]. Sum must be 100.
|
|
uint8_t share_ctrl() const noexcept;
|
|
uint8_t share_mice() const noexcept;
|
|
uint8_t share_elephant() const noexcept;
|
|
|
|
// --- Mutators for priority shares (implemented in .cc) ---
|
|
// If the 3 shares don't sum to 100, we normalize proportionally.
|
|
void set_share_ctrl(uint8_t pct) noexcept;
|
|
void set_share_mice(uint8_t pct) noexcept;
|
|
void set_share_elephant(uint8_t pct) noexcept;
|
|
|
|
// Owner & env
|
|
const Simulator *simulator() const noexcept;
|
|
const NetworkSwitch *owner() const noexcept;
|
|
|
|
// Link wiring (egress side). Size must equal port_cnt().
|
|
void set_egress_links(const std::vector<Link*> &links);
|
|
|
|
protected:
|
|
SwitchBuffer(Simulator* const sim,
|
|
NetworkSwitch* const owner,
|
|
SwitchBufferType t,
|
|
Bytes total_bytes,
|
|
uint16_t ports);
|
|
|
|
// Per-priority index and helpers
|
|
static constexpr int PRI_COUNT = 3;
|
|
static constexpr int PRI_CTRL = 0;
|
|
static constexpr int PRI_MICE = 1;
|
|
static constexpr int PRI_ELE = 2;
|
|
|
|
static int to_idx(FlowPriority p) noexcept {
|
|
switch (p) {
|
|
case FlowPriority::CTRL:
|
|
return PRI_CTRL;
|
|
|
|
case FlowPriority::MICE:
|
|
return PRI_MICE;
|
|
|
|
case FlowPriority::ELEPHANT:
|
|
return PRI_ELE;
|
|
|
|
default:
|
|
return PRI_CTRL;
|
|
}
|
|
|
|
return PRI_ELE; // defensive
|
|
}
|
|
|
|
// Weighted Deficit Round Robin across priorities (per port):
|
|
// Maintain byte-deficits per prio; each drain attempt adds quantum
|
|
// proportional to the configured share. Pick first prio whose head fits.
|
|
struct PerPortSched {
|
|
std::array<int64_t, PRI_COUNT> deficit_bytes {0, 0, 0};
|
|
int next_pick = 0; // rotating pointer among priorities
|
|
};
|
|
|
|
// Quantum baseline (bytes) per "share unit". Calibrated to MSS-ish.
|
|
// Effective quantum = share_pct * QUANTUM_UNIT.
|
|
static constexpr int64_t QUANTUM_UNIT = 128; // bytes per % share
|
|
|
|
// For derived classes
|
|
bool drain_one_common(PortId port);
|
|
|
|
// Per-port self-scheduling
|
|
void schedule_drain_if_needed(PortId port);
|
|
void drain_once(PortId port); // event-loop callback
|
|
|
|
// Derived classes must expose queues and capacity guards:
|
|
|
|
// Access the 3 per-priority queues for a port.
|
|
virtual std::array<std::deque<Queued>, PRI_COUNT> &queues_for(PortId p) = 0;
|
|
virtual const std::array<std::deque<Queued>, PRI_COUNT> &queues_for(
|
|
PortId p) const = 0;
|
|
|
|
// Capacity checks and accounting; return false if not enough space.
|
|
virtual bool on_enqueue_cap_check(PortId port, Bytes sz) = 0;
|
|
virtual void on_enqueue_commit(PortId port, Bytes sz) = 0;
|
|
virtual void on_dequeue_commit(PortId port, Bytes sz) = 0;
|
|
|
|
// Attempt reservation+schedule on the egress link for (port, packet).
|
|
// Returns the reservation finish time if successful (for scheduling the next drain).
|
|
std::optional<Time> try_reserve_and_send(PortId port, Queued &q);
|
|
|
|
// Helpers
|
|
Bytes queued_bytes_total() const noexcept;
|
|
Bytes queued_bytes_port(PortId p) const noexcept;
|
|
|
|
protected:
|
|
Simulator *const _sim;
|
|
NetworkSwitch *const _owner;
|
|
|
|
SwitchBufferType _type;
|
|
Bytes _buffer_bytes;
|
|
uint16_t _port_cnt;
|
|
|
|
// Egress link per port (owned by switch; not by buffer)
|
|
std::vector<Link *> _egress_links;
|
|
|
|
std::vector<bool> _drain_scheduled;
|
|
|
|
// Per-port accounting and sched state
|
|
std::vector<Bytes> _per_port_bytes;
|
|
std::vector<PerPortSched> _sched; // per-port deficits
|
|
|
|
// Priority shares (percentages). Maintained to sum to 100.
|
|
uint8_t _share_ctrl;
|
|
uint8_t _share_mice;
|
|
uint8_t _share_ele;
|
|
};
|
|
|
|
} // namespace dofs
|
|
|
|
#endif // NETWORK_SWITCH_SWITCH_BUFFER_H
|