fixed some style issues, added tooling for docs
This commit is contained in:
9
docs/core/error.md
Normal file
9
docs/core/error.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# core/error.h
|
||||||
|
|
||||||
|
## Free functions
|
||||||
|
|
||||||
|
### `std::mutex &error_mutex() noexcept;`
|
||||||
|
### `inline T&& show(std::string_view name, T&& value) noexcept { ... }`
|
||||||
|
### `std::lock_guard<std::mutex> lock(error_mutex());`
|
||||||
|
### `inline T&& eval_and_show(std::string_view expr, T&& value) noexcept { ... }`
|
||||||
|
### `std::lock_guard<std::mutex> lock(error_mutex());`
|
||||||
10
docs/core/host.md
Normal file
10
docs/core/host.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# core/host.h
|
||||||
|
|
||||||
|
## class NetworkNic — public interface
|
||||||
|
|
||||||
|
### `Host(Simulator *const sim, NodeId id) noexcept;`
|
||||||
|
### `virtual ~Host() = default;`
|
||||||
|
### `NetworkNic *nic() const noexcept { ... }`
|
||||||
|
### `void attach_nic(NetworkNic* nic) noexcept;`
|
||||||
|
### `void detach_nic(NetworkNic* nic) noexcept;`
|
||||||
|
### `Host(const Host &) = delete;`
|
||||||
13
docs/core/logger.md
Normal file
13
docs/core/logger.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# core/logger.h
|
||||||
|
|
||||||
|
## class Logger — public interface
|
||||||
|
|
||||||
|
### `Logger(std::string_view path, bool append) noexcept;`
|
||||||
|
### `~Logger() noexcept;`
|
||||||
|
### `Logger(const Logger &) = delete;`
|
||||||
|
### `Logger(Logger &&) = delete;`
|
||||||
|
### `bool is_open() const noexcept { ... }`
|
||||||
|
### `void write_line(std::string_view line) noexcept;`
|
||||||
|
### `void flush() noexcept;`
|
||||||
|
### `void close() noexcept;`
|
||||||
|
### `std::string_view path() const noexcept { ... }`
|
||||||
13
docs/core/node.md
Normal file
13
docs/core/node.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# core/node.h
|
||||||
|
|
||||||
|
## class Node — public interface
|
||||||
|
|
||||||
|
### `Node(Simulator *const sim, NodeId id, NodeType type) noexcept;`
|
||||||
|
### `virtual ~Node() = default;`
|
||||||
|
### `NodeId id() const noexcept;`
|
||||||
|
### `NodeStatus status() const noexcept;`
|
||||||
|
### `NodeType type() const noexcept;`
|
||||||
|
### `void set_status(NodeStatus s) noexcept;`
|
||||||
|
### `void boot(Time boottime_ns);`
|
||||||
|
### `void reboot(Time boottime_ns);`
|
||||||
|
### `Node(const Node &) = delete;`
|
||||||
16
docs/core/rng.md
Normal file
16
docs/core/rng.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# core/rng.h
|
||||||
|
|
||||||
|
## class Rng — public interface
|
||||||
|
|
||||||
|
### `: _eng(seed) { ... }`
|
||||||
|
### `void seed(seed_type s) noexcept { ... }`
|
||||||
|
### `_eng.seed(s);`
|
||||||
|
### `double uniform01() { ... }`
|
||||||
|
### `Int uniform_range(Int lo_inclusive, Int hi_exclusive) { ... }`
|
||||||
|
### `Int uniform_range(Int hi_exclusive) { ... }`
|
||||||
|
### `double uniform_range(double lo_inclusive, double hi_exclusive) { ... }`
|
||||||
|
### `std::uint64_t poisson(double lambda) { ... }`
|
||||||
|
### `T choose_weighted(const std::vector<std::pair<double, T>>& items) { ... }`
|
||||||
|
### `return choose_weighted_impl(items.begin(), items.end());`
|
||||||
|
### `T choose_weighted(std::initializer_list<std::pair<double, T>> items) { ... }`
|
||||||
|
### `return choose_weighted_impl(items.begin(), items.end());`
|
||||||
27
docs/core/simulator.md
Normal file
27
docs/core/simulator.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# core/simulator.h
|
||||||
|
|
||||||
|
## Free functions
|
||||||
|
|
||||||
|
### `std::numeric_limits<InstanceId>::max();`
|
||||||
|
### `std::numeric_limits<LinkId>::max();`
|
||||||
|
|
||||||
|
## class Rng — public interface
|
||||||
|
|
||||||
|
### `Simulator() = default;`
|
||||||
|
### `static std::pair<InstanceId, Simulator *> create_simulator(InstanceId id);`
|
||||||
|
### `static Simulator *get_simulator(InstanceId id) noexcept;`
|
||||||
|
### `Time now() const noexcept;`
|
||||||
|
### `EventId schedule_at(Time abs_time, F&& f, Args&&... args) { ... }`
|
||||||
|
### `_event_pq.push(std::move(it));`
|
||||||
|
### `EventId schedule_after(Time delay, F&& f, Args&&... args) { ... }`
|
||||||
|
### `bool cancel(EventId id);`
|
||||||
|
### `bool run_next();`
|
||||||
|
### `void run_until(Time end_time);`
|
||||||
|
### `void lock() noexcept;`
|
||||||
|
### `bool is_locked() const noexcept { ... }`
|
||||||
|
### `void flush_after(Time grace) noexcept;`
|
||||||
|
### `Rng* create_rng(std::uint64_t seed);`
|
||||||
|
### `Rng* get_rng() noexcept;`
|
||||||
|
### `Rng const* get_rng() const noexcept;`
|
||||||
|
### `Link* get_link(LinkId id) noexcept;`
|
||||||
|
### `Link const* get_link(LinkId id) const noexcept;`
|
||||||
36
docs/core/time.md
Normal file
36
docs/core/time.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# core/time.h
|
||||||
|
|
||||||
|
## Free functions
|
||||||
|
|
||||||
|
### `constexpr Time operator""_ns(unsigned long long v) noexcept { ... }`
|
||||||
|
### `return Time::from_ns(static_cast<Time::rep>(v));`
|
||||||
|
### `constexpr Time operator""_us(unsigned long long v) noexcept { ... }`
|
||||||
|
### `return Time::from_us(static_cast<Time::rep>(v));`
|
||||||
|
### `constexpr Time operator""_ms(unsigned long long v) noexcept { ... }`
|
||||||
|
### `return Time::from_ms(static_cast<Time::rep>(v));`
|
||||||
|
### `constexpr Time operator""_s (unsigned long long v) noexcept { ... }`
|
||||||
|
### `return Time::from_s (static_cast<Time::rep>(v));`
|
||||||
|
|
||||||
|
## class Time — public interface
|
||||||
|
|
||||||
|
### `constexpr Time() : _nsec(0) { ... }`
|
||||||
|
### `explicit constexpr Time(rep ns) : _nsec(ns) { ... }`
|
||||||
|
### `static constexpr Time from_ns(rep ns) noexcept { ... }`
|
||||||
|
### `return Time(ns);`
|
||||||
|
### `static constexpr Time from_us(rep us) noexcept { ... }`
|
||||||
|
### `return Time(us * 1000ULL);`
|
||||||
|
### `static constexpr Time from_ms(rep ms) noexcept { ... }`
|
||||||
|
### `return Time(ms * 1000ULL * 1000ULL);`
|
||||||
|
### `static constexpr Time from_s (rep s ) noexcept { ... }`
|
||||||
|
### `return Time(s * 1000ULL * 1000ULL * 1000ULL);`
|
||||||
|
### `constexpr rep ns() const noexcept { ... }`
|
||||||
|
### `constexpr rep count() const noexcept { ... }`
|
||||||
|
### `static constexpr rep us_to_ns(rep us) noexcept { ... }`
|
||||||
|
### `static constexpr rep ms_to_ns(rep ms) noexcept { ... }`
|
||||||
|
### `return Time(a._nsec + b._nsec);`
|
||||||
|
### `return Time(a._nsec * b._nsec);`
|
||||||
|
### `return safe_sub(a, b);`
|
||||||
|
### `if (a._nsec < b._nsec) { ... }`
|
||||||
|
### `return Time(a._nsec - b._nsec);`
|
||||||
|
### `constexpr Time unsafe_sub(Time t) const noexcept { ... }`
|
||||||
|
### `return Time(this->_nsec - t._nsec);`
|
||||||
13
docs/core/timer.md
Normal file
13
docs/core/timer.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# core/timer.h
|
||||||
|
|
||||||
|
## class Timer — public interface
|
||||||
|
|
||||||
|
### `Timer() { ... }`
|
||||||
|
### `init();`
|
||||||
|
### `void init() noexcept { ... }`
|
||||||
|
### `_start = clock::now();`
|
||||||
|
### `Time start() const noexcept { ... }`
|
||||||
|
### `auto tp = _start.time_since_epoch();`
|
||||||
|
### `Time now() const noexcept { ... }`
|
||||||
|
### `auto tp = clock::now().time_since_epoch();`
|
||||||
|
### `Time elapsed() const noexcept { ... }`
|
||||||
6
docs/core/types.md
Normal file
6
docs/core/types.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# core/types.h
|
||||||
|
|
||||||
|
## Free functions
|
||||||
|
|
||||||
|
### `inline IPv4Addr ipv4(NodeId n, PortId p) noexcept { ... }`
|
||||||
|
### `return (static_cast<uint32_t>(n) << 16) | static_cast<uint32_t>(p);`
|
||||||
19
docs/hosts/mgmt_msg.md
Normal file
19
docs/hosts/mgmt_msg.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# hosts/mgmt_msg.h
|
||||||
|
|
||||||
|
## class MgmtMsg — public interface
|
||||||
|
|
||||||
|
### `virtual ~MgmtMsg() = default;`
|
||||||
|
|
||||||
|
## class HeartbeatMsg — public interface
|
||||||
|
|
||||||
|
### `: subscriber_id(sid), status(st), generated_at(t) { ... }`
|
||||||
|
### `MgmtKind kind() const noexcept override { ... }`
|
||||||
|
|
||||||
|
## class JobFinishedMsg — public interface
|
||||||
|
|
||||||
|
### `: flow_id(fid), finished_at(t) { ... }`
|
||||||
|
### `MgmtKind kind() const noexcept override { ... }`
|
||||||
|
|
||||||
|
## class EndSimulationMsg — public interface
|
||||||
|
|
||||||
|
### `MgmtKind kind() const noexcept override { ... }`
|
||||||
20
docs/hosts/policies.md
Normal file
20
docs/hosts/policies.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# hosts/policies.h
|
||||||
|
|
||||||
|
## class PubBasePolicy — public interface
|
||||||
|
|
||||||
|
### `virtual ~PubBasePolicy() = default;`
|
||||||
|
|
||||||
|
## class PubRRPolicy — public interface
|
||||||
|
|
||||||
|
### `: _ranges(std::move(ranges)) { ... }`
|
||||||
|
### `validate_and_build();`
|
||||||
|
### `PacketGroups select_multicast_groups(PacketGroups update_groups_mask) override { ... }`
|
||||||
|
### `for (auto const& r : _ranges) { ... }`
|
||||||
|
|
||||||
|
## class SubBasePolicy — public interface
|
||||||
|
|
||||||
|
### `virtual ~SubBasePolicy() = default;`
|
||||||
|
|
||||||
|
## class SubDummyPolicy — public interface
|
||||||
|
|
||||||
|
### `~SubDummyPolicy() override = default;`
|
||||||
10
docs/hosts/publisher.md
Normal file
10
docs/hosts/publisher.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# hosts/publisher.h
|
||||||
|
|
||||||
|
## class Publisher — public interface
|
||||||
|
|
||||||
|
### `void recv_update(Bytes size, PacketGroups update_groups_mask) noexcept;`
|
||||||
|
### `void set_status(NodeStatus s, Time new_latency = Time{}) noexcept;`
|
||||||
|
### `virtual void recv_mgmt_msg(MgmtMsgPtr msg) noexcept override;`
|
||||||
|
### `virtual void recv_frame(const Packet& frame) override;`
|
||||||
|
### `uint64_t updates_in() const noexcept { ... }`
|
||||||
|
### `uint64_t bytes_out() const noexcept { ... }`
|
||||||
8
docs/hosts/subscriber.md
Normal file
8
docs/hosts/subscriber.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# hosts/subscriber.h
|
||||||
|
|
||||||
|
## class Publisher — public interface
|
||||||
|
|
||||||
|
### `virtual void recv_mgmt_msg(MgmtMsgPtr msg) noexcept override;`
|
||||||
|
### `void recv_frame(const Packet& frame) override;`
|
||||||
|
### `void set_status(NodeStatus s) noexcept;`
|
||||||
|
### `void set_publisher(Publisher* p) noexcept { ... }`
|
||||||
19
docs/network/link.md
Normal file
19
docs/network/link.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# network/link.h
|
||||||
|
|
||||||
|
## class Link — public interface
|
||||||
|
|
||||||
|
### `void send_pkt(Packet &pkt, NodeId caller);`
|
||||||
|
### `void schedule_delivery_after(Packet &pkt, NodeId caller, Time after);`
|
||||||
|
### `Time next_available(NodeId sender) const noexcept;`
|
||||||
|
### `std::optional<Reservation> reserve(Bytes bytes, NodeId sender) noexcept;`
|
||||||
|
### `Time serialization_time(Bytes bytes) const noexcept { ... }`
|
||||||
|
### `return serialization_time(bytes, _bandwidth_gbps_cur);`
|
||||||
|
### `Time propagation_latency() const noexcept { ... }`
|
||||||
|
### `LinkId id() const noexcept { ... }`
|
||||||
|
### `LinkStatus status() const noexcept { ... }`
|
||||||
|
### `double bandwidth_gbps() const noexcept { ... }`
|
||||||
|
### `NodeId src_id() const noexcept { ... }`
|
||||||
|
### `NodeId dst_id() const noexcept { ... }`
|
||||||
|
### `PortId src_port() const noexcept { ... }`
|
||||||
|
### `PortId dst_port() const noexcept { ... }`
|
||||||
|
### `static Time serialization_time(Bytes bytes, double gbps) noexcept;`
|
||||||
11
docs/network/network_nic.md
Normal file
11
docs/network/network_nic.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# network/network_nic.h
|
||||||
|
|
||||||
|
## class NetworkNic — public interface
|
||||||
|
|
||||||
|
### `virtual void recv_pkt(Packet &pkt, PortId ingress) override;`
|
||||||
|
### `void attach_host(Host* host) noexcept;`
|
||||||
|
### `void detach_host(Host* host) noexcept;`
|
||||||
|
### `void set_status(NodeStatus s, Time new_latency = Time(0)) noexcept;`
|
||||||
|
### `const NicTelemetry &telemetry() const noexcept { ... }`
|
||||||
|
### `void set_port_blacklisted(PortId port, bool blacklisted) noexcept;`
|
||||||
|
### `bool is_port_blacklisted(PortId port) const noexcept;`
|
||||||
6
docs/network/network_node.md
Normal file
6
docs/network/network_node.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# network/network_node.h
|
||||||
|
|
||||||
|
## class NetworkNode — public interface
|
||||||
|
|
||||||
|
### `explicit NetworkNode(Simulator *const sim, NodeId id, NodeType type) noexcept;`
|
||||||
|
### `virtual ~NetworkNode() = default;`
|
||||||
9
docs/network/network_switch.md
Normal file
9
docs/network/network_switch.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# network/network_switch.h
|
||||||
|
|
||||||
|
## class NetworkSwitch — public interface
|
||||||
|
|
||||||
|
### `virtual void recv_pkt(Packet &pkt, PortId ingress) override;`
|
||||||
|
### `void set_status(NodeStatus s, Time new_forward_latency = Time(0)) noexcept;`
|
||||||
|
### `NodeStatus get_status() const noexcept { ... }`
|
||||||
|
### `return status();`
|
||||||
|
### `uint16_t port_cnt() const noexcept { ... }`
|
||||||
18
docs/network/nic/congestion_control.md
Normal file
18
docs/network/nic/congestion_control.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# network/nic/congestion_control.h
|
||||||
|
|
||||||
|
## class CongestionControl — public interface
|
||||||
|
|
||||||
|
### `explicit CongestionControl(Bytes init_cwnd, Bytes max_cwnd) noexcept;`
|
||||||
|
### `virtual ~CongestionControl() = default;`
|
||||||
|
### `Bytes cwnd() const noexcept { ... }`
|
||||||
|
### `Bytes cwnd_max() const noexcept { ... }`
|
||||||
|
|
||||||
|
## class DCQCN — public interface
|
||||||
|
|
||||||
|
### `explicit DCQCN(Bytes init_cwnd, Bytes max_cwnd) noexcept;`
|
||||||
|
### `virtual void update(const Packet& pkt, Time rtt) noexcept override;`
|
||||||
|
|
||||||
|
## class NSCC — public interface
|
||||||
|
|
||||||
|
### `explicit NSCC(Bytes init_cwnd, Bytes max_cwnd) noexcept;`
|
||||||
|
### `virtual void update(const Packet& pkt, Time rtt) noexcept override;`
|
||||||
12
docs/network/nic/load_balance.md
Normal file
12
docs/network/nic/load_balance.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# network/nic/load_balance.h
|
||||||
|
|
||||||
|
## class LoadBalance — public interface
|
||||||
|
|
||||||
|
### `explicit LoadBalance(Rng *const rng) noexcept : _rng(rng) { ... }`
|
||||||
|
### `virtual ~LoadBalance() = default;`
|
||||||
|
|
||||||
|
## class LBRandomPacketSpraying — public interface
|
||||||
|
|
||||||
|
### `explicit LBRandomPacketSpraying(Rng *const rng) noexcept : LoadBalance(rng) { ... }`
|
||||||
|
### `virtual void update(const Packet& pkt) noexcept override;`
|
||||||
|
### `virtual uint16_t get_entropy(const Packet& context) noexcept override;`
|
||||||
35
docs/network/packet.md
Normal file
35
docs/network/packet.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# network/packet.h
|
||||||
|
|
||||||
|
## class Packet — public interface
|
||||||
|
|
||||||
|
### `NodeId src_node() const noexcept;`
|
||||||
|
### `PortId src_port() const noexcept;`
|
||||||
|
### `NodeId dst_node() const noexcept;`
|
||||||
|
### `PortId dst_port() const noexcept;`
|
||||||
|
### `PacketProtocol protocol() const noexcept;`
|
||||||
|
### `PacketSeq seq() const noexcept;`
|
||||||
|
### `FlowId flow_id() const noexcept;`
|
||||||
|
### `uint32_t entropy() const noexcept;`
|
||||||
|
### `void set_src_node(NodeId n) noexcept;`
|
||||||
|
### `void set_src_port(PortId p) noexcept;`
|
||||||
|
### `void set_dst_node(NodeId n) noexcept;`
|
||||||
|
### `void set_dst_port(PortId p) noexcept;`
|
||||||
|
### `void set_seq(PacketSeq s) noexcept;`
|
||||||
|
### `void set_flow_id(FlowId f) noexcept;`
|
||||||
|
### `void set_entropy(uint32_t e) noexcept;`
|
||||||
|
### `void set_protocol(PacketProtocol p) noexcept;`
|
||||||
|
### `void set_payload_size(Bytes size) noexcept;`
|
||||||
|
### `void set_ecn_enabled(bool v) noexcept;`
|
||||||
|
### `void set_ecn_marked(bool v) noexcept;`
|
||||||
|
### `void set_eof(bool v) noexcept;`
|
||||||
|
### `bool is_ecn_enabled() const noexcept;`
|
||||||
|
### `bool is_ecn() const noexcept;`
|
||||||
|
### `bool is_eof() const noexcept;`
|
||||||
|
### `FlowPriority priority() const noexcept;`
|
||||||
|
### `uint8_t priority_raw() const noexcept;`
|
||||||
|
### `Bytes header_size() const noexcept;`
|
||||||
|
### `Bytes payload_size() const noexcept;`
|
||||||
|
### `Bytes total_size() const noexcept;`
|
||||||
|
### `PacketGroups groups() const noexcept;`
|
||||||
|
### `void set_groups(PacketGroups g) noexcept;`
|
||||||
|
### `void add_groups(PacketGroups gmask) noexcept;`
|
||||||
5
docs/network/switch/dedicated_buffer.md
Normal file
5
docs/network/switch/dedicated_buffer.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# network/switch/dedicated_buffer.h
|
||||||
|
|
||||||
|
## class DedicatedBuffer — public interface
|
||||||
|
|
||||||
|
### `bool drain_one(PortId port) override;`
|
||||||
5
docs/network/switch/ecn_dedicated_red.md
Normal file
5
docs/network/switch/ecn_dedicated_red.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# network/switch/ecn_dedicated_red.h
|
||||||
|
|
||||||
|
## class DedicatedREDEngine — public interface
|
||||||
|
|
||||||
|
### `_rng(rng) { ... }`
|
||||||
5
docs/network/switch/ecn_engine.md
Normal file
5
docs/network/switch/ecn_engine.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# network/switch/ecn_engine.h
|
||||||
|
|
||||||
|
## class SwitchBuffer — public interface
|
||||||
|
|
||||||
|
### `virtual ~ECNEngine() = default;`
|
||||||
5
docs/network/switch/ecn_shared_red.md
Normal file
5
docs/network/switch/ecn_shared_red.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# network/switch/ecn_shared_red.h
|
||||||
|
|
||||||
|
## class SharedREDEngine — public interface
|
||||||
|
|
||||||
|
### `_avg_port_bytes() { ... }`
|
||||||
15
docs/network/switch/multicast_table.md
Normal file
15
docs/network/switch/multicast_table.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# network/switch/multicast_table.h
|
||||||
|
|
||||||
|
## class MulticastTable — public interface
|
||||||
|
|
||||||
|
### `MulticastTable();`
|
||||||
|
### `bool add_tree(std::size_t group_id, uint16_t tree_id);`
|
||||||
|
### `bool delete_tree(std::size_t group_id, uint16_t tree_id);`
|
||||||
|
### `bool add_child_port(std::size_t group_id, uint16_t tree_id, PortId out_port);`
|
||||||
|
### `bool delete_child_port(std::size_t group_id, uint16_t tree_id, PortId out_port);`
|
||||||
|
### `bool set_parent(std::size_t group_id, uint16_t tree_id, PortId parent);`
|
||||||
|
### `bool set_weight(std::size_t group_id, uint16_t tree_id, uint8_t w);`
|
||||||
|
### `bool set_epoch(std::size_t group_id, uint16_t tree_id, uint8_t epoch);`
|
||||||
|
### `const std::vector<McTree> *trees_of(std::size_t group_id) const;`
|
||||||
|
### `std::size_t group_count() const noexcept;`
|
||||||
|
### `std::vector<PortId> get_port_list(PacketGroups groups) const;`
|
||||||
5
docs/network/switch/shared_buffer.md
Normal file
5
docs/network/switch/shared_buffer.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# network/switch/shared_buffer.h
|
||||||
|
|
||||||
|
## class SharedBuffer — public interface
|
||||||
|
|
||||||
|
### `virtual bool drain_one(PortId port) override;`
|
||||||
19
docs/network/switch/switch_buffer.md
Normal file
19
docs/network/switch/switch_buffer.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# network/switch/switch_buffer.h
|
||||||
|
|
||||||
|
## class NetworkSwitch — public interface
|
||||||
|
|
||||||
|
### `virtual ~SwitchBuffer() = default;`
|
||||||
|
### `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;`
|
||||||
|
### `uint8_t share_ctrl() const noexcept;`
|
||||||
|
### `uint8_t share_mice() const noexcept;`
|
||||||
|
### `uint8_t share_elephant() const noexcept;`
|
||||||
|
### `void set_share_ctrl(uint8_t pct) noexcept;`
|
||||||
|
### `void set_share_mice(uint8_t pct) noexcept;`
|
||||||
|
### `void set_share_elephant(uint8_t pct) noexcept;`
|
||||||
|
### `const Simulator *simulator() const noexcept;`
|
||||||
|
### `const NetworkSwitch *owner() const noexcept;`
|
||||||
|
### `void set_egress_links(const std::vector<Link*> &links);`
|
||||||
8
docs/network/switch/unicast_table.md
Normal file
8
docs/network/switch/unicast_table.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# network/switch/unicast_table.h
|
||||||
|
|
||||||
|
## class UnicastTable — public interface
|
||||||
|
|
||||||
|
### `UnicastTable() = default;`
|
||||||
|
### `std::vector<PortId> get_port_list(NodeId dst_node, PortId dst_port) const;`
|
||||||
|
### `bool add_entry(NodeId dst_node, PortId dst_port, PortId out_port);`
|
||||||
|
### `bool delete_entry(NodeId dst_node, PortId dst_port, PortId out_port);`
|
||||||
@@ -18,14 +18,14 @@ void log_error(std::string_view type,
|
|||||||
std::mutex &error_mutex() noexcept;
|
std::mutex &error_mutex() noexcept;
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
inline T&& show(std::string_view name, T&& value) noexcept {
|
inline T&&show(std::string_view name, T&&value) noexcept {
|
||||||
std::lock_guard<std::mutex> lock(error_mutex());
|
std::lock_guard<std::mutex> lock(error_mutex());
|
||||||
std::cerr << name << '=' << value << '\n';
|
std::cerr << name << '=' << value << '\n';
|
||||||
return std::forward<T>(value);
|
return std::forward<T>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
inline T&& eval_and_show(std::string_view expr, T&& value) noexcept {
|
inline T&&eval_and_show(std::string_view expr, T&&value) noexcept {
|
||||||
std::lock_guard<std::mutex> lock(error_mutex());
|
std::lock_guard<std::mutex> lock(error_mutex());
|
||||||
std::cerr << expr << '=' << value << '\n';
|
std::cerr << expr << '=' << value << '\n';
|
||||||
return std::forward<T>(value);
|
return std::forward<T>(value);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Host::Host(Simulator *const sim, NodeId id) noexcept
|
|||||||
Node::set_status(NodeStatus::OK);
|
Node::set_status(NodeStatus::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::attach_nic(NetworkNic* nic) noexcept {
|
void Host::attach_nic(NetworkNic *nic) noexcept {
|
||||||
if (_nic && _nic != nic) {
|
if (_nic && _nic != nic) {
|
||||||
log_error("ERROR", "Host::attach_nic called while NIC already attached");
|
log_error("ERROR", "Host::attach_nic called while NIC already attached");
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ void Host::attach_nic(NetworkNic* nic) noexcept {
|
|||||||
_nic = nic;
|
_nic = nic;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::detach_nic(NetworkNic* nic) noexcept {
|
void Host::detach_nic(NetworkNic *nic) noexcept {
|
||||||
if (_nic && _nic != nic) {
|
if (_nic && _nic != nic) {
|
||||||
log_error("ERROR", "Host::detach_nic called with non-matching NIC pointer");
|
log_error("ERROR", "Host::detach_nic called with non-matching NIC pointer");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NIC lifecycle hooks (called by the NIC)
|
// NIC lifecycle hooks (called by the NIC)
|
||||||
void attach_nic(NetworkNic* nic) noexcept;
|
void attach_nic(NetworkNic *nic) noexcept;
|
||||||
void detach_nic(NetworkNic* nic) noexcept;
|
void detach_nic(NetworkNic *nic) noexcept;
|
||||||
|
|
||||||
// Data-plane completion: a whole flow has arrived.
|
// Data-plane completion: a whole flow has arrived.
|
||||||
virtual void recv_flow(NodeId src,
|
virtual void recv_flow(NodeId src,
|
||||||
@@ -32,7 +32,7 @@ public:
|
|||||||
Bytes flow_size) = 0;
|
Bytes flow_size) = 0;
|
||||||
|
|
||||||
// Control/telemetry interrupt: ACK/NACK/TRIM_BACK, etc.
|
// Control/telemetry interrupt: ACK/NACK/TRIM_BACK, etc.
|
||||||
virtual void recv_frame(const Packet& frame) = 0;
|
virtual void recv_frame(const Packet &frame) = 0;
|
||||||
|
|
||||||
virtual void recv_mgmt_msg(std::unique_ptr<struct MgmtMsg> msg) = 0;
|
virtual void recv_mgmt_msg(std::unique_ptr<struct MgmtMsg> msg) = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ void Node::set_status(NodeStatus s) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline bool schedule_status_ok_after(Simulator *const sim, Time delay_ns,
|
static inline bool schedule_status_ok_after(Simulator *const sim, Time delay_ns,
|
||||||
Node* self) {
|
Node *self) {
|
||||||
if (!sim) {
|
if (!sim) {
|
||||||
log_error("ERROR", "Node: simulator instance not found for boot/reboot");
|
log_error("ERROR", "Node: simulator instance not found for boot/reboot");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T choose_weighted(const std::vector<std::pair<double, T>>& items) {
|
T choose_weighted(const std::vector<std::pair<double, T>> &items) {
|
||||||
return choose_weighted_impl(items.begin(), items.end());
|
return choose_weighted_impl(items.begin(), items.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
namespace dofs {
|
namespace dofs {
|
||||||
|
|
||||||
bool Simulator::Cmp::operator()(const Item& a, const Item& b) const noexcept {
|
bool Simulator::Cmp::operator()(const Item &a, const Item &b) const noexcept {
|
||||||
if (a.when != b.when)
|
if (a.when != b.when)
|
||||||
return a.when > b.when;
|
return a.when > b.when;
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ bool Simulator::run_next() {
|
|||||||
|
|
||||||
void Simulator::run_until(Time end_time) {
|
void Simulator::run_until(Time end_time) {
|
||||||
while (!_event_pq.empty()) {
|
while (!_event_pq.empty()) {
|
||||||
const Item& top = _event_pq.top();
|
const Item &top = _event_pq.top();
|
||||||
|
|
||||||
if (end_time < top.when)
|
if (end_time < top.when)
|
||||||
break;
|
break;
|
||||||
@@ -67,7 +67,7 @@ void Simulator::flush_after(Time grace) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Simulator>>& Simulator::_registry() {
|
std::vector<std::unique_ptr<Simulator>> &Simulator::_registry() {
|
||||||
static std::vector<std::unique_ptr<Simulator>> reg;
|
static std::vector<std::unique_ptr<Simulator>> reg;
|
||||||
return reg;
|
return reg;
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ std::pair<InstanceId, Simulator *> Simulator::create_simulator(InstanceId id) {
|
|||||||
return {INVALID_INSTANCE_ID, nullptr};
|
return {INVALID_INSTANCE_ID, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& reg = _registry();
|
auto ® = _registry();
|
||||||
|
|
||||||
if (static_cast<size_t>(id) >= reg.size()) {
|
if (static_cast<size_t>(id) >= reg.size()) {
|
||||||
reg.resize(static_cast<size_t>(id) + 1);
|
reg.resize(static_cast<size_t>(id) + 1);
|
||||||
@@ -85,7 +85,7 @@ std::pair<InstanceId, Simulator *> Simulator::create_simulator(InstanceId id) {
|
|||||||
|
|
||||||
if (!reg[static_cast<size_t>(id)]) {
|
if (!reg[static_cast<size_t>(id)]) {
|
||||||
auto sim = std::make_unique<Simulator>();
|
auto sim = std::make_unique<Simulator>();
|
||||||
Simulator* ptr = sim.get();
|
Simulator *ptr = sim.get();
|
||||||
reg[static_cast<size_t>(id)] = std::move(sim);
|
reg[static_cast<size_t>(id)] = std::move(sim);
|
||||||
return {id, ptr};
|
return {id, ptr};
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ Simulator * Simulator::get_simulator(InstanceId id) noexcept {
|
|||||||
if (id == INVALID_INSTANCE_ID)
|
if (id == INVALID_INSTANCE_ID)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto& reg = _registry();
|
auto ® = _registry();
|
||||||
const size_t idx = static_cast<size_t>(id);
|
const size_t idx = static_cast<size_t>(id);
|
||||||
|
|
||||||
if (idx >= reg.size())
|
if (idx >= reg.size())
|
||||||
@@ -122,8 +122,8 @@ Rng const * Simulator::get_rng() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pair<LinkId, Link *> Simulator::create_link(
|
std::pair<LinkId, Link *> Simulator::create_link(
|
||||||
NetworkNode* a, PortId a_port,
|
NetworkNode *a, PortId a_port,
|
||||||
NetworkNode* b, PortId b_port,
|
NetworkNode *b, PortId b_port,
|
||||||
Time latency,
|
Time latency,
|
||||||
double bandwidth_gbps) {
|
double bandwidth_gbps) {
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ std::pair<LinkId, Link *> Simulator::create_link(
|
|||||||
latency,
|
latency,
|
||||||
bandwidth_gbps));
|
bandwidth_gbps));
|
||||||
|
|
||||||
Link* raw = up.get();
|
Link *raw = up.get();
|
||||||
_links.push_back(std::move(up));
|
_links.push_back(std::move(up));
|
||||||
return {id, raw};
|
return {id, raw};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Cmp {
|
struct Cmp {
|
||||||
bool operator()(const Item& a, const Item& b) const noexcept;
|
bool operator()(const Item &a, const Item &b) const noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::priority_queue<Item, std::vector<Item>, Cmp> _event_pq;
|
std::priority_queue<Item, std::vector<Item>, Cmp> _event_pq;
|
||||||
@@ -63,7 +63,7 @@ public:
|
|||||||
Time now() const noexcept;
|
Time now() const noexcept;
|
||||||
|
|
||||||
template <class F, class... Args>
|
template <class F, class... Args>
|
||||||
EventId schedule_at(Time abs_time, F&& f, Args&&... args) {
|
EventId schedule_at(Time abs_time, F&&f, Args&&... args) {
|
||||||
if (_locked)
|
if (_locked)
|
||||||
return NULL_EVENT;
|
return NULL_EVENT;
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class F, class... Args>
|
template <class F, class... Args>
|
||||||
EventId schedule_after(Time delay, F&& f, Args&&... args) {
|
EventId schedule_after(Time delay, F&&f, Args&&... args) {
|
||||||
return schedule_at(_now + delay, std::forward<F>(f),
|
return schedule_at(_now + delay, std::forward<F>(f),
|
||||||
std::forward<Args>(args)...);
|
std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ public:
|
|||||||
void run_until(Time end_time);
|
void run_until(Time end_time);
|
||||||
|
|
||||||
// ----- Termination helpers -----
|
// ----- Termination helpers -----
|
||||||
// Prevent any future schedule_* calls from enqueuing events.
|
// Prevent any future schedule_ *calls from enqueuing events.
|
||||||
void lock() noexcept;
|
void lock() noexcept;
|
||||||
bool is_locked() const noexcept {
|
bool is_locked() const noexcept {
|
||||||
return _locked;
|
return _locked;
|
||||||
@@ -100,21 +100,21 @@ public:
|
|||||||
void flush_after(Time grace) noexcept;
|
void flush_after(Time grace) noexcept;
|
||||||
|
|
||||||
// ---------- Object management ----------
|
// ---------- Object management ----------
|
||||||
Rng* create_rng(std::uint64_t seed);
|
Rng *create_rng(std::uint64_t seed);
|
||||||
Rng* get_rng() noexcept;
|
Rng *get_rng() noexcept;
|
||||||
Rng const* get_rng() const noexcept;
|
Rng const *get_rng() const noexcept;
|
||||||
|
|
||||||
std::pair<LinkId, Link *> create_link(NetworkNode* a, PortId a_port,
|
std::pair<LinkId, Link *> create_link(NetworkNode *a, PortId a_port,
|
||||||
NetworkNode* b, PortId b_port,
|
NetworkNode *b, PortId b_port,
|
||||||
Time latency,
|
Time latency,
|
||||||
double bandwidth_gbps);
|
double bandwidth_gbps);
|
||||||
|
|
||||||
Link* get_link(LinkId id) noexcept;
|
Link *get_link(LinkId id) noexcept;
|
||||||
Link const* get_link(LinkId id) const noexcept;
|
Link const *get_link(LinkId id) const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <class F, class... Args>
|
template <class F, class... Args>
|
||||||
static auto make_callable(F&& f, Args&&... args) {
|
static auto make_callable(F&&f, Args&&... args) {
|
||||||
using Fn = std::decay_t<F>;
|
using Fn = std::decay_t<F>;
|
||||||
using Tup = std::tuple<std::decay_t<Args>...>;
|
using Tup = std::tuple<std::decay_t<Args>...>;
|
||||||
return [fn = Fn(std::forward<F>(f)),
|
return [fn = Fn(std::forward<F>(f)),
|
||||||
@@ -123,7 +123,7 @@ private:
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<std::unique_ptr<Simulator>>& _registry();
|
static std::vector<std::unique_ptr<Simulator>> &_registry();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dofs
|
} // namespace dofs
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public:
|
|||||||
PacketGroups select_multicast_groups(PacketGroups update_groups_mask) override {
|
PacketGroups select_multicast_groups(PacketGroups update_groups_mask) override {
|
||||||
PacketGroups result = 0;
|
PacketGroups result = 0;
|
||||||
|
|
||||||
for (auto const& r : _ranges) {
|
for (auto const &r : _ranges) {
|
||||||
if (!group_present(update_groups_mask, r.update_group))
|
if (!group_present(update_groups_mask, r.update_group))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void validate_and_build() {
|
void validate_and_build() {
|
||||||
for (auto const& r : _ranges) {
|
for (auto const &r : _ranges) {
|
||||||
assert(r.low_bit <= r.high_bit);
|
assert(r.low_bit <= r.high_bit);
|
||||||
assert(r.high_bit < 128 && r.low_bit < 128);
|
assert(r.high_bit < 128 && r.low_bit < 128);
|
||||||
(void)r;
|
(void)r;
|
||||||
@@ -68,7 +68,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init all cursors to 0 for determinism.
|
// init all cursors to 0 for determinism.
|
||||||
for (auto const& r : _ranges)
|
for (auto const &r : _ranges)
|
||||||
_rr_cursor[r.update_group] = 0;
|
_rr_cursor[r.update_group] = 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ void Publisher::on_staging_timer() noexcept {
|
|||||||
if (mcast_mask == 0) {
|
if (mcast_mask == 0) {
|
||||||
DOFS_ERROR("publisher", "policy produced empty multicast mask");
|
DOFS_ERROR("publisher", "policy produced empty multicast mask");
|
||||||
} else {
|
} else {
|
||||||
auto* n = this->nic();
|
auto *n = this->nic();
|
||||||
|
|
||||||
if (!n) {
|
if (!n) {
|
||||||
DOFS_ERROR("publisher", "no NIC attached; dropping update");
|
DOFS_ERROR("publisher", "no NIC attached; dropping update");
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace dofs {
|
|||||||
|
|
||||||
class Publisher final : public Host {
|
class Publisher final : public Host {
|
||||||
public:
|
public:
|
||||||
Publisher(Simulator* sim,
|
Publisher(Simulator *sim,
|
||||||
NodeId id,
|
NodeId id,
|
||||||
Time update_latency_base,
|
Time update_latency_base,
|
||||||
std::unique_ptr<PubBasePolicy> policy,
|
std::unique_ptr<PubBasePolicy> policy,
|
||||||
@@ -34,7 +34,7 @@ public:
|
|||||||
// Host overrides (data-plane callbacks)
|
// Host overrides (data-plane callbacks)
|
||||||
virtual void recv_flow(NodeId src, FlowId flow, FlowPriority prio,
|
virtual void recv_flow(NodeId src, FlowId flow, FlowPriority prio,
|
||||||
Bytes flow_size) override;
|
Bytes flow_size) override;
|
||||||
virtual void recv_frame(const Packet& frame) override;
|
virtual void recv_frame(const Packet &frame) override;
|
||||||
|
|
||||||
// Telemetry
|
// Telemetry
|
||||||
uint64_t updates_in() const noexcept {
|
uint64_t updates_in() const noexcept {
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
namespace dofs {
|
namespace dofs {
|
||||||
|
|
||||||
Subscriber::Subscriber(Simulator* sim,
|
Subscriber::Subscriber(Simulator *sim,
|
||||||
NodeId id,
|
NodeId id,
|
||||||
Publisher* publisher,
|
Publisher *publisher,
|
||||||
std::unique_ptr<SubBasePolicy> policy,
|
std::unique_ptr<SubBasePolicy> policy,
|
||||||
Time mgmt_latency,
|
Time mgmt_latency,
|
||||||
Time heartbeat_period) noexcept
|
Time heartbeat_period) noexcept
|
||||||
@@ -54,7 +54,7 @@ void Subscriber::on_heartbeat_timer() noexcept {
|
|||||||
const NodeId sid = this->id();
|
const NodeId sid = this->id();
|
||||||
const NodeStatus st = NodeStatus::OK;
|
const NodeStatus st = NodeStatus::OK;
|
||||||
const Time gen = _sim->now();
|
const Time gen = _sim->now();
|
||||||
Publisher* const pub = _publisher;
|
Publisher *const pub = _publisher;
|
||||||
_sim->schedule_after(_mgmt_latency, [pub, sid, st, gen]() {
|
_sim->schedule_after(_mgmt_latency, [pub, sid, st, gen]() {
|
||||||
pub->recv_mgmt_msg(std::make_unique<HeartbeatMsg>(sid, st, gen));
|
pub->recv_mgmt_msg(std::make_unique<HeartbeatMsg>(sid, st, gen));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ class Publisher;
|
|||||||
|
|
||||||
class Subscriber final : public Host {
|
class Subscriber final : public Host {
|
||||||
public:
|
public:
|
||||||
Subscriber(Simulator* sim,
|
Subscriber(Simulator *sim,
|
||||||
NodeId id,
|
NodeId id,
|
||||||
Publisher* publisher,
|
Publisher *publisher,
|
||||||
std::unique_ptr<SubBasePolicy> policy,
|
std::unique_ptr<SubBasePolicy> policy,
|
||||||
Time mgmt_latency,
|
Time mgmt_latency,
|
||||||
Time heartbeat_period) noexcept;
|
Time heartbeat_period) noexcept;
|
||||||
@@ -27,12 +27,12 @@ public:
|
|||||||
// Host overrides (data-plane callbacks)
|
// Host overrides (data-plane callbacks)
|
||||||
void recv_flow(NodeId src, FlowId flow, FlowPriority prio,
|
void recv_flow(NodeId src, FlowId flow, FlowPriority prio,
|
||||||
Bytes flow_size) override;
|
Bytes flow_size) override;
|
||||||
void recv_frame(const Packet& frame) override;
|
void recv_frame(const Packet &frame) override;
|
||||||
|
|
||||||
void set_status(NodeStatus s) noexcept;
|
void set_status(NodeStatus s) noexcept;
|
||||||
|
|
||||||
// For future: swap publisher pointer
|
// For future: swap publisher pointer
|
||||||
void set_publisher(Publisher* p) noexcept {
|
void set_publisher(Publisher *p) noexcept {
|
||||||
_publisher = p;
|
_publisher = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ std::optional<Link::Reservation> Link::reserve(Bytes bytes,
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
Time& cursor = (d.value() == Dir::AtoB) ? _next_available_ab :
|
Time &cursor = (d.value() == Dir::AtoB) ? _next_available_ab :
|
||||||
_next_available_ba;
|
_next_available_ba;
|
||||||
|
|
||||||
const Time now = _sim->now();
|
const Time now = _sim->now();
|
||||||
@@ -122,7 +122,7 @@ void Link::send_pkt(Packet &pkt, NodeId caller) {
|
|||||||
rsv->finish.unsafe_sub(now) : Time(0);
|
rsv->finish.unsafe_sub(now) : Time(0);
|
||||||
const Time total_delay = to_finish + _latency_cur;
|
const Time total_delay = to_finish + _latency_cur;
|
||||||
|
|
||||||
NetworkNode* target_node = (d.value() == Dir::AtoB) ? _b : _a;
|
NetworkNode *target_node = (d.value() == Dir::AtoB) ? _b : _a;
|
||||||
PortId target_ingress = (d.value() == Dir::AtoB) ? _b_port : _a_port;
|
PortId target_ingress = (d.value() == Dir::AtoB) ? _b_port : _a_port;
|
||||||
|
|
||||||
_sim->schedule_after(total_delay,
|
_sim->schedule_after(total_delay,
|
||||||
@@ -142,7 +142,7 @@ void Link::schedule_delivery_after(Packet &pkt, NodeId caller, Time after) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkNode* target_node = (d.value() == Dir::AtoB) ? _b : _a;
|
NetworkNode *target_node = (d.value() == Dir::AtoB) ? _b : _a;
|
||||||
PortId target_ingress = (d.value() == Dir::AtoB) ? _b_port : _a_port;
|
PortId target_ingress = (d.value() == Dir::AtoB) ? _b_port : _a_port;
|
||||||
|
|
||||||
_sim->schedule_after(after,
|
_sim->schedule_after(after,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ NetworkNic::NetworkNic(Simulator *const sim,
|
|||||||
LBType lb_type,
|
LBType lb_type,
|
||||||
Bytes cc_init_cwnd,
|
Bytes cc_init_cwnd,
|
||||||
Bytes cc_max_cwnd,
|
Bytes cc_max_cwnd,
|
||||||
const NicSchedulingWeights& schedw) noexcept
|
const NicSchedulingWeights &schedw) noexcept
|
||||||
: NetworkNode(sim, id, NodeType::NIC),
|
: NetworkNode(sim, id, NodeType::NIC),
|
||||||
_buf(buf),
|
_buf(buf),
|
||||||
_port_cnt(total_ports),
|
_port_cnt(total_ports),
|
||||||
@@ -63,7 +63,7 @@ NetworkNic::NetworkNic(Simulator *const sim,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& pq : _ports) {
|
for (auto &pq : _ports) {
|
||||||
pq.b_control = _schedw.control;
|
pq.b_control = _schedw.control;
|
||||||
pq.b_retrans = _schedw.retrans;
|
pq.b_retrans = _schedw.retrans;
|
||||||
pq.b_mice = _schedw.mice;
|
pq.b_mice = _schedw.mice;
|
||||||
@@ -71,14 +71,14 @@ NetworkNic::NetworkNic(Simulator *const sim,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkNic::attach_host(Host* host) noexcept {
|
void NetworkNic::attach_host(Host *host) noexcept {
|
||||||
_host = host;
|
_host = host;
|
||||||
|
|
||||||
if (_host)
|
if (_host)
|
||||||
_host->attach_nic(this);
|
_host->attach_nic(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkNic::detach_host(Host* host) noexcept {
|
void NetworkNic::detach_host(Host *host) noexcept {
|
||||||
if (_host && _host != host) {
|
if (_host && _host != host) {
|
||||||
log_error("ERROR", "NetworkNic::detach_host pointer mismatch");
|
log_error("ERROR", "NetworkNic::detach_host pointer mismatch");
|
||||||
return;
|
return;
|
||||||
@@ -123,7 +123,7 @@ std::uint64_t NetworkNic::txkey(FlowId f, PacketSeq s) noexcept {
|
|||||||
static_cast<std::uint64_t>(static_cast<std::uint16_t>(s));
|
static_cast<std::uint64_t>(static_cast<std::uint16_t>(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkNic::record_tx_timestamp(const Packet& pkt) noexcept {
|
void NetworkNic::record_tx_timestamp(const Packet &pkt) noexcept {
|
||||||
if (!_sim)
|
if (!_sim)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -131,8 +131,8 @@ void NetworkNic::record_tx_timestamp(const Packet& pkt) noexcept {
|
|||||||
_tx_sent_at[key] = _sim->now();
|
_tx_sent_at[key] = _sim->now();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkNic::lookup_rtt_and_erase(const Packet& ack_like, Time now,
|
bool NetworkNic::lookup_rtt_and_erase(const Packet &ack_like, Time now,
|
||||||
Time& out_rtt) noexcept {
|
Time &out_rtt) noexcept {
|
||||||
const auto key = txkey(ack_like.flow_id(), ack_like.seq());
|
const auto key = txkey(ack_like.flow_id(), ack_like.seq());
|
||||||
auto it = _tx_sent_at.find(key);
|
auto it = _tx_sent_at.find(key);
|
||||||
|
|
||||||
@@ -350,7 +350,7 @@ Packet NetworkNic::make_data_packet(NodeId dst, PortId out_port,
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet NetworkNic::make_ack_packet(const Packet& rx_data,
|
Packet NetworkNic::make_ack_packet(const Packet &rx_data,
|
||||||
PortId ingress) noexcept {
|
PortId ingress) noexcept {
|
||||||
Packet ack(rx_data.dst_node(), rx_data.dst_port(),
|
Packet ack(rx_data.dst_node(), rx_data.dst_port(),
|
||||||
rx_data.src_node(), rx_data.src_port(),
|
rx_data.src_node(), rx_data.src_port(),
|
||||||
@@ -372,7 +372,7 @@ Packet NetworkNic::make_nack_packet(NodeId peer, FlowId flow, PacketSeq miss,
|
|||||||
return nack;
|
return nack;
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet NetworkNic::make_trim_back_response(const Packet& trim,
|
Packet NetworkNic::make_trim_back_response(const Packet &trim,
|
||||||
PortId ingress) noexcept {
|
PortId ingress) noexcept {
|
||||||
Packet tb(trim.dst_node(), trim.dst_port(),
|
Packet tb(trim.dst_node(), trim.dst_port(),
|
||||||
trim.src_node(), trim.src_port(),
|
trim.src_node(), trim.src_port(),
|
||||||
@@ -384,7 +384,7 @@ Packet NetworkNic::make_trim_back_response(const Packet& trim,
|
|||||||
return tb;
|
return tb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkNic::schedule_ack(const Packet& rx_data, PortId ingress) noexcept {
|
void NetworkNic::schedule_ack(const Packet &rx_data, PortId ingress) noexcept {
|
||||||
if (!_sim)
|
if (!_sim)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -414,7 +414,7 @@ void NetworkNic::schedule_nack(NodeId peer, FlowId flow, PacketSeq missing_seq,
|
|||||||
_telemetry.tx_nacks++;
|
_telemetry.tx_nacks++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkNic::schedule_trim_back_response(const Packet& trim,
|
void NetworkNic::schedule_trim_back_response(const Packet &trim,
|
||||||
PortId ingress) noexcept {
|
PortId ingress) noexcept {
|
||||||
if (!_sim)
|
if (!_sim)
|
||||||
return;
|
return;
|
||||||
@@ -497,7 +497,7 @@ void NetworkNic::enqueue_packet(PortId port, QClass cls, Packet pkt) noexcept {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& pq = _ports[port];
|
auto &pq = _ports[port];
|
||||||
|
|
||||||
switch (cls) {
|
switch (cls) {
|
||||||
case QClass::CONTROL:
|
case QClass::CONTROL:
|
||||||
@@ -521,7 +521,7 @@ void NetworkNic::enqueue_packet(PortId port, QClass cls, Packet pkt) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NetworkNic::schedule_port_if_needed(PortId port) noexcept {
|
void NetworkNic::schedule_port_if_needed(PortId port) noexcept {
|
||||||
auto& pq = _ports[port];
|
auto &pq = _ports[port];
|
||||||
|
|
||||||
if (pq.scheduled || !_sim)
|
if (pq.scheduled || !_sim)
|
||||||
return;
|
return;
|
||||||
@@ -532,8 +532,8 @@ void NetworkNic::schedule_port_if_needed(PortId port) noexcept {
|
|||||||
this, port);
|
this, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkNic::pick_next_qclass(const PortQueues& pq,
|
bool NetworkNic::pick_next_qclass(const PortQueues &pq,
|
||||||
QClass& out_cls) const noexcept {
|
QClass &out_cls) const noexcept {
|
||||||
if (!pq.control.empty() && pq.b_control > Bytes(0)) {
|
if (!pq.control.empty() && pq.b_control > Bytes(0)) {
|
||||||
out_cls = QClass::CONTROL;
|
out_cls = QClass::CONTROL;
|
||||||
return true;
|
return true;
|
||||||
@@ -558,9 +558,9 @@ bool NetworkNic::pick_next_qclass(const PortQueues& pq,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkNic::try_send_one(PortId port, QClass cls) noexcept {
|
bool NetworkNic::try_send_one(PortId port, QClass cls) noexcept {
|
||||||
auto& pq = _ports[port];
|
auto &pq = _ports[port];
|
||||||
std::deque<Packet> *q = nullptr;
|
std::deque<Packet> *q = nullptr;
|
||||||
Bytes* budget = nullptr;
|
Bytes *budget = nullptr;
|
||||||
|
|
||||||
switch (cls) {
|
switch (cls) {
|
||||||
case QClass::CONTROL:
|
case QClass::CONTROL:
|
||||||
@@ -594,7 +594,7 @@ bool NetworkNic::try_send_one(PortId port, QClass cls) noexcept {
|
|||||||
|
|
||||||
if (!is_ctrl && _cc) {
|
if (!is_ctrl && _cc) {
|
||||||
const Bytes next_bytes = pkt.total_size();
|
const Bytes next_bytes = pkt.total_size();
|
||||||
Bytes& out = _tx_outstanding[pkt.flow_id()];
|
Bytes &out = _tx_outstanding[pkt.flow_id()];
|
||||||
|
|
||||||
if (!_cc->is_allowed_to_send(out, next_bytes)) {
|
if (!_cc->is_allowed_to_send(out, next_bytes)) {
|
||||||
q->push_front(std::move(pkt));
|
q->push_front(std::move(pkt));
|
||||||
@@ -625,7 +625,7 @@ bool NetworkNic::try_send_one(PortId port, QClass cls) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NetworkNic::port_drain_task(PortId port) noexcept {
|
void NetworkNic::port_drain_task(PortId port) noexcept {
|
||||||
auto& pq = _ports[port];
|
auto &pq = _ports[port];
|
||||||
|
|
||||||
// Attempt to send while there is any budget and any queue has packets.
|
// Attempt to send while there is any budget and any queue has packets.
|
||||||
// Refill budgets when all depleted or all queues empty.
|
// Refill budgets when all depleted or all queues empty.
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ public:
|
|||||||
LBType lb_type,
|
LBType lb_type,
|
||||||
Bytes cc_init_cwnd,
|
Bytes cc_init_cwnd,
|
||||||
Bytes cc_max_cwnd,
|
Bytes cc_max_cwnd,
|
||||||
const NicSchedulingWeights& schedw) noexcept;
|
const NicSchedulingWeights &schedw) noexcept;
|
||||||
|
|
||||||
virtual void recv_pkt(Packet &pkt, PortId ingress) override;
|
virtual void recv_pkt(Packet &pkt, PortId ingress) override;
|
||||||
|
|
||||||
void attach_host(Host* host) noexcept;
|
void attach_host(Host *host) noexcept;
|
||||||
void detach_host(Host* host) noexcept;
|
void detach_host(Host *host) noexcept;
|
||||||
|
|
||||||
// Host API (TX):
|
// Host API (TX):
|
||||||
void send_flow(NodeId dst, Bytes size,
|
void send_flow(NodeId dst, Bytes size,
|
||||||
@@ -83,24 +83,24 @@ private:
|
|||||||
void schedule_port_if_needed(PortId port) noexcept;
|
void schedule_port_if_needed(PortId port) noexcept;
|
||||||
void port_drain_task(PortId port) noexcept;
|
void port_drain_task(PortId port) noexcept;
|
||||||
|
|
||||||
bool pick_next_qclass(const PortQueues& pq, QClass& out_cls) const noexcept;
|
bool pick_next_qclass(const PortQueues &pq, QClass &out_cls) const noexcept;
|
||||||
|
|
||||||
bool try_send_one(PortId port, QClass cls) noexcept;
|
bool try_send_one(PortId port, QClass cls) noexcept;
|
||||||
|
|
||||||
void enqueue_packet(PortId port, QClass cls, Packet pkt) noexcept;
|
void enqueue_packet(PortId port, QClass cls, Packet pkt) noexcept;
|
||||||
|
|
||||||
void schedule_ack(const Packet& rx_data, PortId ingress) noexcept;
|
void schedule_ack(const Packet &rx_data, PortId ingress) noexcept;
|
||||||
void schedule_nack(NodeId peer, FlowId flow, PacketSeq missing_seq,
|
void schedule_nack(NodeId peer, FlowId flow, PacketSeq missing_seq,
|
||||||
FlowPriority prio, PortId ingress) noexcept;
|
FlowPriority prio, PortId ingress) noexcept;
|
||||||
void schedule_trim_back_response(const Packet& trim, PortId ingress) noexcept;
|
void schedule_trim_back_response(const Packet &trim, PortId ingress) noexcept;
|
||||||
|
|
||||||
bool is_elephant(Bytes sz) const noexcept;
|
bool is_elephant(Bytes sz) const noexcept;
|
||||||
|
|
||||||
static inline std::uint64_t txkey(FlowId f, PacketSeq s) noexcept;
|
static inline std::uint64_t txkey(FlowId f, PacketSeq s) noexcept;
|
||||||
|
|
||||||
void record_tx_timestamp(const Packet& pkt) noexcept;
|
void record_tx_timestamp(const Packet &pkt) noexcept;
|
||||||
bool lookup_rtt_and_erase(const Packet& ack_like, Time now,
|
bool lookup_rtt_and_erase(const Packet &ack_like, Time now,
|
||||||
Time& out_rtt) noexcept;
|
Time &out_rtt) noexcept;
|
||||||
|
|
||||||
PortId pick_src_port_for_flow(NodeId dst) noexcept;
|
PortId pick_src_port_for_flow(NodeId dst) noexcept;
|
||||||
|
|
||||||
@@ -109,11 +109,11 @@ private:
|
|||||||
|
|
||||||
Packet make_data_packet(NodeId dst, PortId out_port, FlowPriority prio,
|
Packet make_data_packet(NodeId dst, PortId out_port, FlowPriority prio,
|
||||||
FlowId fid, PacketSeq seq, Bytes payload) noexcept;
|
FlowId fid, PacketSeq seq, Bytes payload) noexcept;
|
||||||
Packet make_ack_packet(const Packet& rx_data, PortId ingress) noexcept;
|
Packet make_ack_packet(const Packet &rx_data, PortId ingress) noexcept;
|
||||||
Packet make_nack_packet(NodeId peer, FlowId flow, PacketSeq miss,
|
Packet make_nack_packet(NodeId peer, FlowId flow, PacketSeq miss,
|
||||||
FlowPriority prio,
|
FlowPriority prio,
|
||||||
PortId ingress) noexcept;
|
PortId ingress) noexcept;
|
||||||
Packet make_trim_back_response(const Packet& trim, PortId ingress) noexcept;
|
Packet make_trim_back_response(const Packet &trim, PortId ingress) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Host *_host {nullptr};
|
Host *_host {nullptr};
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ void NetworkSwitch::enqueue_one(Packet pkt, PortId egress,
|
|||||||
(void)_buf->enqueue_packet(pkt, egress, prio);
|
(void)_buf->enqueue_packet(pkt, egress, prio);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkSwitch::build_uc_egress(const Packet& pkt,
|
void NetworkSwitch::build_uc_egress(const Packet &pkt,
|
||||||
std::vector<PortId> &out) const {
|
std::vector<PortId> &out) const {
|
||||||
// Unicast: consult table; if multiple candidates, ECMP-pick one by entropy
|
// Unicast: consult table; if multiple candidates, ECMP-pick one by entropy
|
||||||
std::vector<PortId> candidates =
|
std::vector<PortId> candidates =
|
||||||
@@ -168,7 +168,7 @@ void NetworkSwitch::build_uc_egress(const Packet& pkt,
|
|||||||
out.push_back(candidates[idx % candidates.size()]);
|
out.push_back(candidates[idx % candidates.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkSwitch::build_mc_fanout(const Packet& pkt, PortId ingress,
|
void NetworkSwitch::build_mc_fanout(const Packet &pkt, PortId ingress,
|
||||||
std::vector<PortId> &fanout) const {
|
std::vector<PortId> &fanout) const {
|
||||||
if (!_rt)
|
if (!_rt)
|
||||||
return;
|
return;
|
||||||
@@ -180,7 +180,7 @@ void NetworkSwitch::build_mc_fanout(const Packet& pkt, PortId ingress,
|
|||||||
if (!group_bit_set(mask, gid))
|
if (!group_bit_set(mask, gid))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto* trees = _rt->multicast.trees_of(gid);
|
const auto *trees = _rt->multicast.trees_of(gid);
|
||||||
|
|
||||||
if (!trees || trees->empty())
|
if (!trees || trees->empty())
|
||||||
continue;
|
continue;
|
||||||
@@ -201,7 +201,7 @@ void NetworkSwitch::build_mc_fanout(const Packet& pkt, PortId ingress,
|
|||||||
chosen_idx %= k;
|
chosen_idx %= k;
|
||||||
}
|
}
|
||||||
|
|
||||||
const McTree& t = (*trees)[chosen_idx];
|
const McTree &t = (*trees)[chosen_idx];
|
||||||
|
|
||||||
if (t.parent_port.has_value() && ingress != t.parent_port.value()) {
|
if (t.parent_port.has_value() && ingress != t.parent_port.value()) {
|
||||||
// Skip this group's replication from the wrong direction
|
// Skip this group's replication from the wrong direction
|
||||||
|
|||||||
@@ -51,11 +51,11 @@ private:
|
|||||||
|
|
||||||
// Build multicast fanout: pick exactly one tree per active group bit,
|
// Build multicast fanout: pick exactly one tree per active group bit,
|
||||||
// optionally enforcing RPF via parent_port, and union child ports.
|
// optionally enforcing RPF via parent_port, and union child ports.
|
||||||
void build_mc_fanout(const Packet& pkt, PortId ingress,
|
void build_mc_fanout(const Packet &pkt, PortId ingress,
|
||||||
std::vector<PortId> &fanout) const;
|
std::vector<PortId> &fanout) const;
|
||||||
|
|
||||||
// Build unicast egress list: if multiple candidates, ECMP-pick one via hash_ecmp.
|
// Build unicast egress list: if multiple candidates, ECMP-pick one via hash_ecmp.
|
||||||
void build_uc_egress(const Packet& pkt, std::vector<PortId> &out) const;
|
void build_uc_egress(const Packet &pkt, std::vector<PortId> &out) const;
|
||||||
|
|
||||||
static void merge_sorted_unique(std::vector<PortId> &base,
|
static void merge_sorted_unique(std::vector<PortId> &base,
|
||||||
const std::vector<PortId> &add);
|
const std::vector<PortId> &add);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ add_library(dofs_nic STATIC)
|
|||||||
target_include_directories(dofs_nic
|
target_include_directories(dofs_nic
|
||||||
PUBLIC
|
PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src> # "network/...", "core/..."
|
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src> # "network/...", "core/..."
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/network> # ** enables "nic/..." **
|
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/network> # **enables "nic/..." **
|
||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:include>
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ add_library(dofs_nic_headers_tooling STATIC ${NIC_STUBS})
|
|||||||
target_include_directories(dofs_nic_headers_tooling
|
target_include_directories(dofs_nic_headers_tooling
|
||||||
PRIVATE
|
PRIVATE
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>
|
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/network> # ** not src/network/nic **
|
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/network> # **not src/network/nic **
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(dofs_nic_headers_tooling PRIVATE dofs_nic dofs_network dofs_core)
|
target_link_libraries(dofs_nic_headers_tooling PRIVATE dofs_nic dofs_network dofs_core)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ bool CongestionControl::is_allowed_to_send(Bytes bytes_outstanding,
|
|||||||
DCQCN::DCQCN(Bytes init_cwnd, Bytes max_cwnd) noexcept
|
DCQCN::DCQCN(Bytes init_cwnd, Bytes max_cwnd) noexcept
|
||||||
: CongestionControl(init_cwnd, max_cwnd) {}
|
: CongestionControl(init_cwnd, max_cwnd) {}
|
||||||
|
|
||||||
void DCQCN::update(const Packet& pkt, Time rtt) noexcept {
|
void DCQCN::update(const Packet &pkt, Time rtt) noexcept {
|
||||||
// Extremely simple placeholder logic:
|
// Extremely simple placeholder logic:
|
||||||
// - If ECN marked or TRIM_BACK/NACK, cut cwnd multiplicatively.
|
// - If ECN marked or TRIM_BACK/NACK, cut cwnd multiplicatively.
|
||||||
// - If ACK of data (no ECN), increase cwnd additively (slowly).
|
// - If ACK of data (no ECN), increase cwnd additively (slowly).
|
||||||
@@ -67,7 +67,7 @@ void DCQCN::update(const Packet& pkt, Time rtt) noexcept {
|
|||||||
NSCC::NSCC(Bytes init_cwnd, Bytes max_cwnd) noexcept
|
NSCC::NSCC(Bytes init_cwnd, Bytes max_cwnd) noexcept
|
||||||
: CongestionControl(init_cwnd, max_cwnd) {}
|
: CongestionControl(init_cwnd, max_cwnd) {}
|
||||||
|
|
||||||
void NSCC::update(const Packet& pkt, Time rtt) noexcept {
|
void NSCC::update(const Packet &pkt, Time rtt) noexcept {
|
||||||
if (pkt.protocol() == PacketProtocol::HEADER_TRIM_BACK ||
|
if (pkt.protocol() == PacketProtocol::HEADER_TRIM_BACK ||
|
||||||
pkt.protocol() == PacketProtocol::NACK ||
|
pkt.protocol() == PacketProtocol::NACK ||
|
||||||
pkt.is_ecn()) {
|
pkt.is_ecn()) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public:
|
|||||||
explicit CongestionControl(Bytes init_cwnd, Bytes max_cwnd) noexcept;
|
explicit CongestionControl(Bytes init_cwnd, Bytes max_cwnd) noexcept;
|
||||||
virtual ~CongestionControl() = default;
|
virtual ~CongestionControl() = default;
|
||||||
|
|
||||||
virtual void update(const Packet& pkt, Time rtt) noexcept = 0;
|
virtual void update(const Packet &pkt, Time rtt) noexcept = 0;
|
||||||
|
|
||||||
virtual bool is_allowed_to_send(Bytes bytes_outstanding,
|
virtual bool is_allowed_to_send(Bytes bytes_outstanding,
|
||||||
Bytes next_bytes) const noexcept;
|
Bytes next_bytes) const noexcept;
|
||||||
@@ -33,13 +33,13 @@ protected:
|
|||||||
class DCQCN final : public CongestionControl {
|
class DCQCN final : public CongestionControl {
|
||||||
public:
|
public:
|
||||||
explicit DCQCN(Bytes init_cwnd, Bytes max_cwnd) noexcept;
|
explicit DCQCN(Bytes init_cwnd, Bytes max_cwnd) noexcept;
|
||||||
virtual void update(const Packet& pkt, Time rtt) noexcept override;
|
virtual void update(const Packet &pkt, Time rtt) noexcept override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NSCC final : public CongestionControl {
|
class NSCC final : public CongestionControl {
|
||||||
public:
|
public:
|
||||||
explicit NSCC(Bytes init_cwnd, Bytes max_cwnd) noexcept;
|
explicit NSCC(Bytes init_cwnd, Bytes max_cwnd) noexcept;
|
||||||
virtual void update(const Packet& pkt, Time rtt) noexcept override;
|
virtual void update(const Packet &pkt, Time rtt) noexcept override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dofs
|
} // namespace dofs
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
namespace dofs {
|
namespace dofs {
|
||||||
|
|
||||||
void LBRandomPacketSpraying::update(const Packet& pkt) noexcept {
|
void LBRandomPacketSpraying::update(const Packet &pkt) noexcept {
|
||||||
(void)pkt;
|
(void)pkt;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t LBRandomPacketSpraying::get_entropy(const Packet& context) noexcept {
|
uint16_t LBRandomPacketSpraying::get_entropy(const Packet &context) noexcept {
|
||||||
(void)context;
|
(void)context;
|
||||||
|
|
||||||
if (!_rng)
|
if (!_rng)
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ public:
|
|||||||
explicit LoadBalance(Rng *const rng) noexcept : _rng(rng) {}
|
explicit LoadBalance(Rng *const rng) noexcept : _rng(rng) {}
|
||||||
virtual ~LoadBalance() = default;
|
virtual ~LoadBalance() = default;
|
||||||
|
|
||||||
virtual void update(const Packet& pkt) noexcept = 0;
|
virtual void update(const Packet &pkt) noexcept = 0;
|
||||||
|
|
||||||
// Produce a 16-bit entropy value for the next packet.
|
// Produce a 16-bit entropy value for the next packet.
|
||||||
virtual uint16_t get_entropy(const Packet& context) noexcept = 0;
|
virtual uint16_t get_entropy(const Packet &context) noexcept = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Rng *const _rng;
|
Rng *const _rng;
|
||||||
@@ -27,8 +27,8 @@ protected:
|
|||||||
class LBRandomPacketSpraying final : public LoadBalance {
|
class LBRandomPacketSpraying final : public LoadBalance {
|
||||||
public:
|
public:
|
||||||
explicit LBRandomPacketSpraying(Rng *const rng) noexcept : LoadBalance(rng) {}
|
explicit LBRandomPacketSpraying(Rng *const rng) noexcept : LoadBalance(rng) {}
|
||||||
virtual void update(const Packet& pkt) noexcept override;
|
virtual void update(const Packet &pkt) noexcept override;
|
||||||
virtual uint16_t get_entropy(const Packet& context) noexcept override;
|
virtual uint16_t get_entropy(const Packet &context) noexcept override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dofs
|
} // namespace dofs
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ add_library(dofs_switch STATIC)
|
|||||||
target_include_directories(dofs_switch
|
target_include_directories(dofs_switch
|
||||||
PUBLIC
|
PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src> # "network/...", "core/..."
|
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src> # "network/...", "core/..."
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/network> # ** enables "switch/..." **
|
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/network> # **enables "switch/..." **
|
||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:include>
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ add_library(dofs_switch_headers_tooling STATIC ${SWITCH_STUBS})
|
|||||||
target_include_directories(dofs_switch_headers_tooling
|
target_include_directories(dofs_switch_headers_tooling
|
||||||
PRIVATE
|
PRIVATE
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>
|
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/network> # ** not src/network/switch **
|
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/network> # **not src/network/switch **
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(dofs_switch_headers_tooling PRIVATE dofs_switch dofs_network dofs_core)
|
target_link_libraries(dofs_switch_headers_tooling PRIVATE dofs_switch dofs_network dofs_core)
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace dofs {
|
namespace dofs {
|
||||||
|
|
||||||
DedicatedBuffer::DedicatedBuffer(Simulator* const sim,
|
DedicatedBuffer::DedicatedBuffer(Simulator *const sim,
|
||||||
NetworkSwitch* const owner,
|
NetworkSwitch *const owner,
|
||||||
Bytes total_bytes,
|
Bytes total_bytes,
|
||||||
uint16_t ports)
|
uint16_t ports)
|
||||||
: SwitchBuffer(sim, owner, SwitchBufferType::DEDICATED, total_bytes, ports),
|
: SwitchBuffer(sim, owner, SwitchBufferType::DEDICATED, total_bytes, ports),
|
||||||
@@ -38,7 +38,7 @@ void DedicatedBuffer::on_dequeue_commit(PortId port, Bytes sz) {
|
|||||||
_per_port_bytes[port] -= sz;
|
_per_port_bytes[port] -= sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DedicatedBuffer::enqueue_packet(const Packet& pkt, PortId egress,
|
bool DedicatedBuffer::enqueue_packet(const Packet &pkt, PortId egress,
|
||||||
FlowPriority prio) {
|
FlowPriority prio) {
|
||||||
if (egress >= _port_cnt)
|
if (egress >= _port_cnt)
|
||||||
return false;
|
return false;
|
||||||
@@ -49,7 +49,7 @@ bool DedicatedBuffer::enqueue_packet(const Packet& pkt, PortId egress,
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
Queued q{pkt, egress, prio, _sim->now(), sz};
|
Queued q{pkt, egress, prio, _sim->now(), sz};
|
||||||
auto& qset = queues_for(egress);
|
auto &qset = queues_for(egress);
|
||||||
qset[to_idx(prio)].push_back(std::move(q));
|
qset[to_idx(prio)].push_back(std::move(q));
|
||||||
on_enqueue_commit(egress, sz);
|
on_enqueue_commit(egress, sz);
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ namespace dofs {
|
|||||||
// Each port has a hard cap; no borrowing.
|
// Each port has a hard cap; no borrowing.
|
||||||
class DedicatedBuffer : public SwitchBuffer {
|
class DedicatedBuffer : public SwitchBuffer {
|
||||||
public:
|
public:
|
||||||
DedicatedBuffer(Simulator* const sim,
|
DedicatedBuffer(Simulator *const sim,
|
||||||
NetworkSwitch* const owner,
|
NetworkSwitch *const owner,
|
||||||
Bytes total_bytes,
|
Bytes total_bytes,
|
||||||
uint16_t ports);
|
uint16_t ports);
|
||||||
|
|
||||||
bool enqueue_packet(const Packet& pkt, PortId egress,
|
bool enqueue_packet(const Packet &pkt, PortId egress,
|
||||||
FlowPriority prio) override;
|
FlowPriority prio) override;
|
||||||
bool drain_one(PortId port) override;
|
bool drain_one(PortId port) override;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
namespace dofs {
|
namespace dofs {
|
||||||
|
|
||||||
Packet &DedicatedREDEngine::process_packet(Packet &pkt,
|
Packet &DedicatedREDEngine::process_packet(Packet &pkt,
|
||||||
SwitchBuffer* buf) noexcept {
|
SwitchBuffer *buf) noexcept {
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return pkt;
|
return pkt;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public:
|
|||||||
|
|
||||||
// Decide ECN mark / header-trim for a packet about to enter 'buf'.
|
// Decide ECN mark / header-trim for a packet about to enter 'buf'.
|
||||||
// Returns the same packet reference, possibly modified in-place.
|
// Returns the same packet reference, possibly modified in-place.
|
||||||
virtual Packet &process_packet(Packet &pkt, SwitchBuffer* buf) noexcept = 0;
|
virtual Packet &process_packet(Packet &pkt, SwitchBuffer *buf) noexcept = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Helpers for derived engines
|
// Helpers for derived engines
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ bool MulticastTable::add_tree(std::size_t group_id, uint16_t tree_id) {
|
|||||||
if (group_id >= _groups.size())
|
if (group_id >= _groups.size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto& trees = _groups[group_id];
|
auto &trees = _groups[group_id];
|
||||||
auto it = std::find_if(trees.begin(), trees.end(),
|
auto it = std::find_if(trees.begin(), trees.end(),
|
||||||
[&](const McTree & t) {
|
[&](const McTree & t) {
|
||||||
return t.tree_id == tree_id;
|
return t.tree_id == tree_id;
|
||||||
@@ -39,7 +39,7 @@ bool MulticastTable::delete_tree(std::size_t group_id, uint16_t tree_id) {
|
|||||||
if (group_id >= _groups.size())
|
if (group_id >= _groups.size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto& trees = _groups[group_id];
|
auto &trees = _groups[group_id];
|
||||||
auto it = std::find_if(trees.begin(), trees.end(),
|
auto it = std::find_if(trees.begin(), trees.end(),
|
||||||
[&](const McTree & t) {
|
[&](const McTree & t) {
|
||||||
return t.tree_id == tree_id;
|
return t.tree_id == tree_id;
|
||||||
@@ -57,7 +57,7 @@ bool MulticastTable::add_child_port(std::size_t group_id, uint16_t tree_id,
|
|||||||
if (group_id >= _groups.size())
|
if (group_id >= _groups.size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto& trees = _groups[group_id];
|
auto &trees = _groups[group_id];
|
||||||
auto it = std::find_if(trees.begin(), trees.end(),
|
auto it = std::find_if(trees.begin(), trees.end(),
|
||||||
[&](const McTree & t) {
|
[&](const McTree & t) {
|
||||||
return t.tree_id == tree_id;
|
return t.tree_id == tree_id;
|
||||||
@@ -74,7 +74,7 @@ bool MulticastTable::delete_child_port(std::size_t group_id, uint16_t tree_id,
|
|||||||
if (group_id >= _groups.size())
|
if (group_id >= _groups.size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto& trees = _groups[group_id];
|
auto &trees = _groups[group_id];
|
||||||
auto it = std::find_if(trees.begin(), trees.end(),
|
auto it = std::find_if(trees.begin(), trees.end(),
|
||||||
[&](const McTree & t) {
|
[&](const McTree & t) {
|
||||||
return t.tree_id == tree_id;
|
return t.tree_id == tree_id;
|
||||||
@@ -91,7 +91,7 @@ bool MulticastTable::set_parent(std::size_t group_id, uint16_t tree_id,
|
|||||||
if (group_id >= _groups.size())
|
if (group_id >= _groups.size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto& trees = _groups[group_id];
|
auto &trees = _groups[group_id];
|
||||||
auto it = std::find_if(trees.begin(), trees.end(),
|
auto it = std::find_if(trees.begin(), trees.end(),
|
||||||
[&](const McTree & t) {
|
[&](const McTree & t) {
|
||||||
return t.tree_id == tree_id;
|
return t.tree_id == tree_id;
|
||||||
@@ -109,7 +109,7 @@ bool MulticastTable::set_weight(std::size_t group_id, uint16_t tree_id,
|
|||||||
if (group_id >= _groups.size())
|
if (group_id >= _groups.size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto& trees = _groups[group_id];
|
auto &trees = _groups[group_id];
|
||||||
auto it = std::find_if(trees.begin(), trees.end(),
|
auto it = std::find_if(trees.begin(), trees.end(),
|
||||||
[&](const McTree & t) {
|
[&](const McTree & t) {
|
||||||
return t.tree_id == tree_id;
|
return t.tree_id == tree_id;
|
||||||
@@ -127,7 +127,7 @@ bool MulticastTable::set_epoch(std::size_t group_id, uint16_t tree_id,
|
|||||||
if (group_id >= _groups.size())
|
if (group_id >= _groups.size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto& trees = _groups[group_id];
|
auto &trees = _groups[group_id];
|
||||||
auto it = std::find_if(trees.begin(), trees.end(),
|
auto it = std::find_if(trees.begin(), trees.end(),
|
||||||
[&](const McTree & t) {
|
[&](const McTree & t) {
|
||||||
return t.tree_id == tree_id;
|
return t.tree_id == tree_id;
|
||||||
@@ -158,7 +158,7 @@ std::vector<PortId> MulticastTable::get_port_list(PacketGroups groups) const {
|
|||||||
if (!bit_set)
|
if (!bit_set)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto& trees = _groups[gid];
|
const auto &trees = _groups[gid];
|
||||||
auto it = std::find_if(trees.begin(), trees.end(),
|
auto it = std::find_if(trees.begin(), trees.end(),
|
||||||
[](const McTree & t) {
|
[](const McTree & t) {
|
||||||
return t.tree_id == 0;
|
return t.tree_id == 0;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace dofs {
|
namespace dofs {
|
||||||
|
|
||||||
SharedBuffer::SharedBuffer(Simulator* const sim,
|
SharedBuffer::SharedBuffer(Simulator *const sim,
|
||||||
NetworkSwitch* const owner,
|
NetworkSwitch *const owner,
|
||||||
Bytes total_bytes,
|
Bytes total_bytes,
|
||||||
uint16_t ports)
|
uint16_t ports)
|
||||||
: SwitchBuffer(sim, owner, SwitchBufferType::SHARED, total_bytes, ports),
|
: SwitchBuffer(sim, owner, SwitchBufferType::SHARED, total_bytes, ports),
|
||||||
@@ -36,7 +36,7 @@ void SharedBuffer::on_dequeue_commit(PortId port, Bytes sz) {
|
|||||||
_per_port_bytes[port] -= sz;
|
_per_port_bytes[port] -= sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SharedBuffer::enqueue_packet(const Packet& pkt, PortId egress,
|
bool SharedBuffer::enqueue_packet(const Packet &pkt, PortId egress,
|
||||||
FlowPriority prio) {
|
FlowPriority prio) {
|
||||||
if (egress >= _port_cnt)
|
if (egress >= _port_cnt)
|
||||||
return false;
|
return false;
|
||||||
@@ -47,7 +47,7 @@ bool SharedBuffer::enqueue_packet(const Packet& pkt, PortId egress,
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
Queued q{pkt, egress, prio, _sim->now(), sz};
|
Queued q{pkt, egress, prio, _sim->now(), sz};
|
||||||
auto& qset = queues_for(egress);
|
auto &qset = queues_for(egress);
|
||||||
qset[to_idx(prio)].push_back(std::move(q));
|
qset[to_idx(prio)].push_back(std::move(q));
|
||||||
on_enqueue_commit(egress, sz);
|
on_enqueue_commit(egress, sz);
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ namespace dofs {
|
|||||||
// Only guard is the total pool (_buffer_bytes). Per-port usage tracked for stats.
|
// Only guard is the total pool (_buffer_bytes). Per-port usage tracked for stats.
|
||||||
class SharedBuffer : public SwitchBuffer {
|
class SharedBuffer : public SwitchBuffer {
|
||||||
public:
|
public:
|
||||||
SharedBuffer(Simulator* const sim,
|
SharedBuffer(Simulator *const sim,
|
||||||
NetworkSwitch* const owner,
|
NetworkSwitch *const owner,
|
||||||
Bytes total_bytes,
|
Bytes total_bytes,
|
||||||
uint16_t ports);
|
uint16_t ports);
|
||||||
|
|
||||||
virtual bool enqueue_packet(const Packet& pkt, PortId egress,
|
virtual bool enqueue_packet(const Packet &pkt, PortId egress,
|
||||||
FlowPriority prio) override;
|
FlowPriority prio) override;
|
||||||
virtual bool drain_one(PortId port) override;
|
virtual bool drain_one(PortId port) override;
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
namespace dofs {
|
namespace dofs {
|
||||||
|
|
||||||
SwitchBuffer::SwitchBuffer(Simulator* const sim,
|
SwitchBuffer::SwitchBuffer(Simulator *const sim,
|
||||||
NetworkSwitch* const owner,
|
NetworkSwitch *const owner,
|
||||||
SwitchBufferType t,
|
SwitchBufferType t,
|
||||||
Bytes total_bytes,
|
Bytes total_bytes,
|
||||||
uint16_t ports)
|
uint16_t ports)
|
||||||
@@ -122,13 +122,13 @@ Bytes SwitchBuffer::queued_bytes_port(PortId p) const noexcept {
|
|||||||
return port_buffered(p);
|
return port_buffered(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Time> SwitchBuffer::try_reserve_and_send(PortId port, Queued& q) {
|
std::optional<Time> SwitchBuffer::try_reserve_and_send(PortId port, Queued &q) {
|
||||||
if (port >= _egress_links.size() || _egress_links[port] == nullptr) {
|
if (port >= _egress_links.size() || _egress_links[port] == nullptr) {
|
||||||
log_error("ERROR", "SwitchBuffer: egress link missing on port");
|
log_error("ERROR", "SwitchBuffer: egress link missing on port");
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
Link* link = _egress_links[port];
|
Link *link = _egress_links[port];
|
||||||
|
|
||||||
auto r = link->reserve(q.size_bytes, _owner->id());
|
auto r = link->reserve(q.size_bytes, _owner->id());
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ void SwitchBuffer::schedule_drain_if_needed(PortId port) {
|
|||||||
if (queued_bytes_port(port) == Bytes(0))
|
if (queued_bytes_port(port) == Bytes(0))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Link* link = _egress_links[port];
|
Link *link = _egress_links[port];
|
||||||
|
|
||||||
if (!link)
|
if (!link)
|
||||||
return;
|
return;
|
||||||
@@ -180,7 +180,7 @@ void SwitchBuffer::drain_once(PortId port) {
|
|||||||
|
|
||||||
// No progress: either queues empty or link not reservable now.
|
// No progress: either queues empty or link not reservable now.
|
||||||
// If queues are still non-empty, retry later at next-available.
|
// If queues are still non-empty, retry later at next-available.
|
||||||
auto& qs = queues_for(port);
|
auto &qs = queues_for(port);
|
||||||
bool any = !qs[PRI_CTRL].empty() || !qs[PRI_MICE].empty() ||
|
bool any = !qs[PRI_CTRL].empty() || !qs[PRI_MICE].empty() ||
|
||||||
!qs[PRI_ELE].empty();
|
!qs[PRI_ELE].empty();
|
||||||
|
|
||||||
@@ -193,8 +193,8 @@ bool SwitchBuffer::drain_one_common(PortId port) {
|
|||||||
if (port >= _port_cnt)
|
if (port >= _port_cnt)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto& queues = queues_for(port);
|
auto &queues = queues_for(port);
|
||||||
auto& sched = _sched[port];
|
auto &sched = _sched[port];
|
||||||
|
|
||||||
auto head_size = [&](int pri) -> Bytes {
|
auto head_size = [&](int pri) -> Bytes {
|
||||||
if (queues[pri].empty())
|
if (queues[pri].empty())
|
||||||
@@ -212,7 +212,7 @@ bool SwitchBuffer::drain_one_common(PortId port) {
|
|||||||
Bytes sz = head_size(pri);
|
Bytes sz = head_size(pri);
|
||||||
|
|
||||||
if (sz > Bytes(0) && sched.deficit_bytes[pri] >= int64_t(sz)) {
|
if (sz > Bytes(0) && sched.deficit_bytes[pri] >= int64_t(sz)) {
|
||||||
auto& q = queues[pri].front();
|
auto &q = queues[pri].front();
|
||||||
auto finish_opt = try_reserve_and_send(port, q);
|
auto finish_opt = try_reserve_and_send(port, q);
|
||||||
|
|
||||||
if (finish_opt.has_value()) {
|
if (finish_opt.has_value()) {
|
||||||
@@ -221,7 +221,7 @@ bool SwitchBuffer::drain_one_common(PortId port) {
|
|||||||
on_dequeue_commit(port, sz);
|
on_dequeue_commit(port, sz);
|
||||||
sched.next_pick = (pri + 1) % PRI_COUNT;
|
sched.next_pick = (pri + 1) % PRI_COUNT;
|
||||||
|
|
||||||
Link* link = _egress_links[port];
|
Link *link = _egress_links[port];
|
||||||
const Time na = link->next_available(_owner->id());
|
const Time na = link->next_available(_owner->id());
|
||||||
const Time now = _sim->now();
|
const Time now = _sim->now();
|
||||||
const Time delay = (na > now) ? na.unsafe_sub(now) : Time(0);
|
const Time delay = (na > now) ? na.unsafe_sub(now) : Time(0);
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ public:
|
|||||||
virtual ~SwitchBuffer() = default;
|
virtual ~SwitchBuffer() = default;
|
||||||
|
|
||||||
// --- API: enqueue & drain ---
|
// --- API: enqueue & drain ---
|
||||||
// NOTE: ECN/drop decisions are expected to be done *before* enqueue;
|
// NOTE: ECN/drop decisions are expected to be done *before *enqueue;
|
||||||
// we still guard and return false on capacity violations.
|
// we still guard and return false on capacity violations.
|
||||||
virtual bool enqueue_packet(const Packet& pkt,
|
virtual bool enqueue_packet(const Packet &pkt,
|
||||||
PortId egress,
|
PortId egress,
|
||||||
FlowPriority prio) = 0;
|
FlowPriority prio) = 0;
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ public:
|
|||||||
void set_egress_links(const std::vector<Link*> &links);
|
void set_egress_links(const std::vector<Link*> &links);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SwitchBuffer(Simulator* const sim,
|
SwitchBuffer(Simulator *const sim,
|
||||||
NetworkSwitch* const owner,
|
NetworkSwitch *const owner,
|
||||||
SwitchBufferType t,
|
SwitchBufferType t,
|
||||||
Bytes total_bytes,
|
Bytes total_bytes,
|
||||||
uint16_t ports);
|
uint16_t ports);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ std::vector<PortId> UnicastTable::get_port_list(NodeId dst_node,
|
|||||||
bool UnicastTable::add_entry(NodeId dst_node, PortId dst_port,
|
bool UnicastTable::add_entry(NodeId dst_node, PortId dst_port,
|
||||||
PortId out_port) {
|
PortId out_port) {
|
||||||
const IPv4Addr k = ipv4(dst_node, dst_port);
|
const IPv4Addr k = ipv4(dst_node, dst_port);
|
||||||
auto& vec = _table[k];
|
auto &vec = _table[k];
|
||||||
return insert_sorted_unique(vec, out_port);
|
return insert_sorted_unique(vec, out_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
255
tools/extract_api.py
Executable file
255
tools/extract_api.py
Executable file
@@ -0,0 +1,255 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
extract_api.py
|
||||||
|
Scan C++ headers in ./src for public interfaces (public class/struct methods
|
||||||
|
and free function declarations) and emit markdown snippets mirroring the src/
|
||||||
|
tree into ./docs.
|
||||||
|
|
||||||
|
Each interface line is rendered as:
|
||||||
|
### `signature`
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
cd dofs
|
||||||
|
python3 tools/extract_api.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
SRC_DIR = REPO_ROOT / "src"
|
||||||
|
DOCS_DIR = REPO_ROOT / "docs"
|
||||||
|
|
||||||
|
HEADER_EXTS = {".h", ".hh", ".hpp", ".hxx"}
|
||||||
|
|
||||||
|
# ---------- Utilities ----------
|
||||||
|
|
||||||
|
def read_text(p: Path) -> str:
|
||||||
|
try:
|
||||||
|
return p.read_text(encoding="utf-8", errors="ignore")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[WARN] failed reading {p}: {e}", file=sys.stderr)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def strip_comments(code: str) -> str:
|
||||||
|
# Remove /* ... */ (including multiline) and // ... to end of line
|
||||||
|
no_block = re.sub(r"/\*.*?\*/", "", code, flags=re.S)
|
||||||
|
no_line = re.sub(r"//.*?$", "", no_block, flags=re.M)
|
||||||
|
return no_line
|
||||||
|
|
||||||
|
def collapse_ws(s: str) -> str:
|
||||||
|
return re.sub(r"\s+", " ", s).strip()
|
||||||
|
|
||||||
|
def ensure_dir(path: Path):
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# ---------- Heuristic extractors ----------
|
||||||
|
|
||||||
|
ACCESS_RE = re.compile(r"^\s*(public|private|protected)\s*:\s*$")
|
||||||
|
CLASS_START_RE = re.compile(r"^\s*(class|struct)\s+([A-Za-z_]\w*)\b")
|
||||||
|
POSSIBLE_FUNC_DECL_RE = re.compile(
|
||||||
|
r"""^[^;{}()]*\b
|
||||||
|
(?!typedef\b)(?!using\b)(?!friend\b)
|
||||||
|
[A-Za-z_~]\w*\s*
|
||||||
|
\(
|
||||||
|
[^;]* # params
|
||||||
|
\)
|
||||||
|
(?:\s*(?:const|noexcept|override|final|=0|=\s*default|=\s*delete))*\s*
|
||||||
|
;
|
||||||
|
\s*$""",
|
||||||
|
re.X,
|
||||||
|
)
|
||||||
|
POSSIBLE_INLINE_DEF_RE = re.compile(
|
||||||
|
r"""^[^;{}()]*\b
|
||||||
|
(?!typedef\b)(?!using\b)(?!friend\b)
|
||||||
|
[A-Za-z_~]\w*\s*
|
||||||
|
\(
|
||||||
|
[^{;]* # params
|
||||||
|
\)
|
||||||
|
(?:\s*(?:const|noexcept|override|final))*\s*
|
||||||
|
\{""",
|
||||||
|
re.X,
|
||||||
|
)
|
||||||
|
SKIP_PREFIXES = ("#define", "#include", "static_assert", "enum ", "enum class ",
|
||||||
|
"template<", "namespace ", "using ", "typedef ", "friend ",
|
||||||
|
"struct ", "class ")
|
||||||
|
|
||||||
|
def extract_public_methods(lines, is_struct_default_public: bool):
|
||||||
|
public = is_struct_default_public
|
||||||
|
out = []
|
||||||
|
depth = 0
|
||||||
|
for raw in lines:
|
||||||
|
line = raw.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Track nested braces to avoid confusing nested scopes
|
||||||
|
depth += raw.count("{")
|
||||||
|
depth -= raw.count("}")
|
||||||
|
|
||||||
|
m = ACCESS_RE.match(line)
|
||||||
|
if m and depth >= 0:
|
||||||
|
public = (m.group(1) == "public")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not public:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.startswith(SKIP_PREFIXES) or line.endswith(":"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if POSSIBLE_FUNC_DECL_RE.match(line):
|
||||||
|
out.append(collapse_ws(line))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if POSSIBLE_INLINE_DEF_RE.match(line):
|
||||||
|
sig = line.split("{", 1)[0].rstrip()
|
||||||
|
out.append(collapse_ws(sig) + " { ... }")
|
||||||
|
continue
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
def extract_free_function_decls(code: str):
|
||||||
|
# Remove class/struct bodies to avoid capturing methods
|
||||||
|
scrubbed = []
|
||||||
|
toks = code.splitlines()
|
||||||
|
in_class = False
|
||||||
|
brace_balance = 0
|
||||||
|
for line in toks:
|
||||||
|
if not in_class:
|
||||||
|
if CLASS_START_RE.match(line):
|
||||||
|
in_class = True
|
||||||
|
brace_balance = line.count("{") - line.count("}")
|
||||||
|
scrubbed.append("")
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
brace_balance += line.count("{") - line.count("}")
|
||||||
|
if brace_balance <= 0:
|
||||||
|
in_class = False
|
||||||
|
scrubbed.append("")
|
||||||
|
continue
|
||||||
|
scrubbed.append(line)
|
||||||
|
|
||||||
|
text = "\n".join(scrubbed)
|
||||||
|
|
||||||
|
out = []
|
||||||
|
for raw in text.splitlines():
|
||||||
|
line = raw.strip()
|
||||||
|
if not line or line.startswith(SKIP_PREFIXES):
|
||||||
|
continue
|
||||||
|
if POSSIBLE_FUNC_DECL_RE.match(line):
|
||||||
|
out.append(collapse_ws(line))
|
||||||
|
elif POSSIBLE_INLINE_DEF_RE.match(line):
|
||||||
|
sig = line.split("{", 1)[0].rstrip()
|
||||||
|
out.append(collapse_ws(sig) + " { ... }")
|
||||||
|
return out
|
||||||
|
|
||||||
|
def split_top_level_classes(code: str):
|
||||||
|
lines = code.splitlines()
|
||||||
|
results = []
|
||||||
|
i = 0
|
||||||
|
while i < len(lines):
|
||||||
|
m = CLASS_START_RE.match(lines[i])
|
||||||
|
if not m:
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
kind, name = m.group(1), m.group(2)
|
||||||
|
# Find opening brace on same or subsequent lines
|
||||||
|
j = i
|
||||||
|
if "{" not in lines[j]:
|
||||||
|
j += 1
|
||||||
|
while j < len(lines) and "{" not in lines[j]:
|
||||||
|
j += 1
|
||||||
|
if j >= len(lines):
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
# Capture until matching close
|
||||||
|
depth = 0
|
||||||
|
body = []
|
||||||
|
while j < len(lines):
|
||||||
|
depth += lines[j].count("{")
|
||||||
|
depth -= lines[j].count("}")
|
||||||
|
body.append(lines[j])
|
||||||
|
if depth <= 0 and "}" in lines[j]:
|
||||||
|
break
|
||||||
|
j += 1
|
||||||
|
body_inner = body[1:-1] if body else []
|
||||||
|
results.append((name, kind == "struct", body_inner))
|
||||||
|
i = j + 1
|
||||||
|
return results
|
||||||
|
|
||||||
|
# ---------- Main per-file processing ----------
|
||||||
|
|
||||||
|
def process_header(path: Path):
|
||||||
|
raw = read_text(path)
|
||||||
|
if not raw:
|
||||||
|
return None
|
||||||
|
|
||||||
|
code = strip_comments(raw)
|
||||||
|
|
||||||
|
# Collect classes
|
||||||
|
class_entries = []
|
||||||
|
for cname, is_struct, body in split_top_level_classes(code):
|
||||||
|
methods = extract_public_methods(body, is_struct_default_public=is_struct)
|
||||||
|
if methods:
|
||||||
|
class_entries.append((cname, methods))
|
||||||
|
|
||||||
|
# Collect free function decls
|
||||||
|
free_funcs = extract_free_function_decls(code)
|
||||||
|
|
||||||
|
if not class_entries and not free_funcs:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Build markdown with ### `signature` items
|
||||||
|
rel = path.relative_to(SRC_DIR)
|
||||||
|
md_lines = []
|
||||||
|
md_lines.append(f"# {rel.as_posix()}")
|
||||||
|
md_lines.append("")
|
||||||
|
|
||||||
|
if free_funcs:
|
||||||
|
md_lines.append("## Free functions")
|
||||||
|
md_lines.append("")
|
||||||
|
for sig in free_funcs:
|
||||||
|
md_lines.append(f"### `{sig}`")
|
||||||
|
md_lines.append("")
|
||||||
|
|
||||||
|
for cname, methods in class_entries:
|
||||||
|
md_lines.append(f"## class {cname} — public interface")
|
||||||
|
md_lines.append("")
|
||||||
|
for sig in methods:
|
||||||
|
md_lines.append(f"### `{sig}`")
|
||||||
|
md_lines.append("")
|
||||||
|
|
||||||
|
return "\n".join(md_lines)
|
||||||
|
|
||||||
|
def write_markdown(src_header: Path, content: str):
|
||||||
|
rel = src_header.relative_to(SRC_DIR)
|
||||||
|
out_path = DOCS_DIR / rel
|
||||||
|
out_path = out_path.with_suffix(".md")
|
||||||
|
ensure_dir(out_path)
|
||||||
|
out_path.write_text(content, encoding="utf-8")
|
||||||
|
return out_path
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if not SRC_DIR.exists():
|
||||||
|
print(f"[ERR] src/ not found at {SRC_DIR}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
generated = 0
|
||||||
|
for path in SRC_DIR.rglob("*"):
|
||||||
|
if not path.is_file():
|
||||||
|
continue
|
||||||
|
if path.suffix.lower() not in HEADER_EXTS:
|
||||||
|
continue
|
||||||
|
result = process_header(path)
|
||||||
|
if result:
|
||||||
|
out = write_markdown(path, result)
|
||||||
|
generated += 1
|
||||||
|
print(f"[OK] {out.relative_to(REPO_ROOT)}")
|
||||||
|
|
||||||
|
if generated == 0:
|
||||||
|
print("[INFO] no public interfaces detected (heuristics may have filtered everything)]")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user