#ifndef NETWORK_SWITCH_SWITCH_BUFFER_H #define NETWORK_SWITCH_SWITCH_BUFFER_H #include #include #include #include #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 &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 &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 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, PRI_COUNT> &queues_for(PortId p) = 0; virtual const std::array, 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