added networking components, hosts are next
This commit is contained in:
162
src/network/switch/switch_buffer.h
Normal file
162
src/network/switch/switch_buffer.h
Normal file
@@ -0,0 +1,162 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user