/* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #ifndef NET_HH_ #define NET_HH_ #include "core/reactor.hh" #include "core/deleter.hh" #include "core/queue.hh" #include "core/stream.hh" #include "core/scollectd.hh" #include "ethernet.hh" #include "packet.hh" #include "const.hh" #include namespace net { class packet; class interface; class device; class qp; class l3_protocol; class forward_hash { uint8_t data[64]; size_t end_idx = 0; public: size_t size() const { return end_idx; } void push_back(uint8_t b) { assert(end_idx < sizeof(data)); data[end_idx++] = b; } void push_back(uint16_t b) { push_back(uint8_t(b)); push_back(uint8_t(b >> 8)); } void push_back(uint32_t b) { push_back(uint16_t(b)); push_back(uint16_t(b >> 16)); } const uint8_t& operator[](size_t idx) const { return data[idx]; } }; struct hw_features { // Enable tx ip header checksum offload bool tx_csum_ip_offload = false; // Enable tx l4 (TCP or UDP) checksum offload bool tx_csum_l4_offload = false; // Enable rx checksum offload bool rx_csum_offload = false; // Enable tx TCP segment offload bool tx_tso = false; // Enable tx UDP fragmentation offload bool tx_ufo = false; // Maximum Transmission Unit uint16_t mtu = 1500; // Maximun packet len when TCP/UDP offload is enabled uint16_t max_packet_len = net::ip_packet_len_max - net::eth_hdr_len; }; class l3_protocol { public: struct l3packet { eth_protocol_num proto_num; ethernet_address to; packet p; }; using packet_provider_type = std::function ()>; private: interface* _netif; eth_protocol_num _proto_num; public: explicit l3_protocol(interface* netif, eth_protocol_num proto_num, packet_provider_type func); subscription receive( std::function (packet, ethernet_address)> rx_fn, std::function forward); private: friend class interface; }; class interface { struct l3_rx_stream { stream packet_stream; future<> ready; std::function forward; l3_rx_stream(std::function&& fw) : ready(packet_stream.started()), forward(fw) {} }; std::unordered_map _proto_map; std::shared_ptr _dev; subscription _rx; ethernet_address _hw_address; net::hw_features _hw_features; std::vector _pkt_providers; private: future<> dispatch_packet(packet p); public: explicit interface(std::shared_ptr dev); ethernet_address hw_address() { return _hw_address; } net::hw_features hw_features() { return _hw_features; } subscription register_l3(eth_protocol_num proto_num, std::function (packet p, ethernet_address from)> next, std::function forward); void forward(unsigned cpuid, packet p); unsigned hash2cpu(uint32_t hash); void register_packet_provider(l3_protocol::packet_provider_type func) { _pkt_providers.push_back(std::move(func)); } friend class l3_protocol; }; class qp { using packet_provider_type = std::function ()>; std::vector _pkt_providers; std::vector proxies; circular_buffer _proxy_packetq; stream _rx_stream; reactor::poller _tx_poller; circular_buffer _tx_packetq; uint64_t _packets_snt = 0; uint64_t _packets_rcv = 0; uint64_t _last_tx_bunch = 0; uint64_t _last_rx_bunch = 0; std::vector _collectd_regs; protected: void update_rx_count(uint64_t count) { _last_rx_bunch = count; _packets_rcv += count; } public: qp() : _tx_poller([this] { poll_tx(); return true; }), _collectd_regs({ // queue_length value:GAUGE:0:U // Absolute value of num packets in last tx bunch. scollectd::add_polled_metric(scollectd::type_instance_id("network" , scollectd::per_cpu_plugin_instance , "queue_length", "tx-packet-queue") , scollectd::make_typed(scollectd::data_type::GAUGE, _last_tx_bunch) ), // total_operations value:DERIVE:0:U scollectd::add_polled_metric(scollectd::type_instance_id("network" , scollectd::per_cpu_plugin_instance , "total_operations", "tx-packets") , scollectd::make_typed(scollectd::data_type::DERIVE, _packets_snt) ), // queue_length value:GAUGE:0:U // Absolute value of num packets in last rx bunch. scollectd::add_polled_metric(scollectd::type_instance_id("network" , scollectd::per_cpu_plugin_instance , "queue_length", "rx-packet-queue") , scollectd::make_typed(scollectd::data_type::GAUGE, _last_rx_bunch) ), // total_operations value:DERIVE:0:U scollectd::add_polled_metric(scollectd::type_instance_id("network" , scollectd::per_cpu_plugin_instance , "total_operations", "rx-packets") , scollectd::make_typed(scollectd::data_type::DERIVE, _packets_rcv) ), }) {} virtual ~qp() {} virtual future<> send(packet p) = 0; virtual uint32_t send(circular_buffer& p) { uint32_t sent = 0; while (!p.empty()) { send(std::move(p.front())); p.pop_front(); sent++; } return sent; } virtual void rx_start() {}; bool may_forward() { return !proxies.empty(); } void add_proxy(unsigned cpu) { if(proxies.empty()) { register_packet_provider([this] { std::experimental::optional p; if (!_proxy_packetq.empty()) { p = std::move(_proxy_packetq.front()); _proxy_packetq.pop_front(); } return p; }); } proxies.push_back(cpu); } void proxy_send(packet p) { _proxy_packetq.push_back(std::move(p)); } void register_packet_provider(packet_provider_type func) { _pkt_providers.push_back(std::move(func)); } void poll_tx() { if (_tx_packetq.size() < 16) { // refill send queue from upper layers uint32_t work; do { work = 0; for (auto&& pr : _pkt_providers) { auto p = pr(); if (p) { work++; _tx_packetq.push_back(std::move(p.value())); if (_tx_packetq.size() == 128) { break; } } } } while (work && _tx_packetq.size() < 128); } if (!_tx_packetq.empty()) { _last_tx_bunch = send(_tx_packetq); _packets_snt += _last_tx_bunch; } } friend class device; }; class device { protected: std::unique_ptr _queues; size_t _rss_table_bits = 0; public: device() { _queues = std::make_unique(smp::count); } virtual ~device() {}; qp& queue_for_cpu(unsigned cpu) { return *_queues[cpu]; } qp& local_queue() { return queue_for_cpu(engine.cpu_id()); } void l2receive(packet p) { _queues[engine.cpu_id()]->_rx_stream.produce(std::move(p)); } subscription receive(std::function (packet)> next_packet) { auto sub = _queues[engine.cpu_id()]->_rx_stream.listen(std::move(next_packet)); _queues[engine.cpu_id()]->rx_start(); return std::move(sub); } virtual ethernet_address hw_address() = 0; virtual net::hw_features hw_features() = 0; virtual uint16_t hw_queues_count() { return 1; } virtual future<> link_ready() { return make_ready_future<>(); } virtual std::unique_ptr init_local_queue(boost::program_options::variables_map opts, uint16_t qid) = 0; virtual unsigned hash2qid(uint32_t hash) { return hash % hw_queues_count(); } void set_local_queue(std::unique_ptr dev) { assert(!_queues[engine.cpu_id()]); _queues[engine.cpu_id()] = dev.get(); engine.at_destroy([dev = std::move(dev)] {}); } template unsigned forward_dst(unsigned src_cpuid, Func&& hashfn) { auto& qp = queue_for_cpu(src_cpuid); if (!qp.may_forward()) { return src_cpuid; } auto hash = hashfn() >> _rss_table_bits; auto idx = hash % (qp.proxies.size() + 1); return idx ? qp.proxies[idx - 1] : src_cpuid; } virtual unsigned hash2cpu(uint32_t hash) { // there is an assumption here that qid == cpu_id which will // not necessary be true in the future return forward_dst(hash2qid(hash), [hash] { return hash; }); } }; } #endif /* NET_HH_ */