Instead of l3 (arp/ipv4) pushing packets into interface's queue, make them register functions that interface can use to ask l3 for packets.
115 lines
3.8 KiB
C++
115 lines
3.8 KiB
C++
/*
|
|
* Copyright (C) 2014 Cloudius Systems, Ltd.
|
|
*
|
|
*/
|
|
|
|
#include "net.hh"
|
|
#include <utility>
|
|
#include "toeplitz.hh"
|
|
|
|
using std::move;
|
|
|
|
namespace net {
|
|
|
|
l3_protocol::l3_protocol(interface* netif, eth_protocol_num proto_num, packet_provider_type func)
|
|
: _netif(netif), _proto_num(proto_num) {
|
|
_netif->register_packet_provider(std::move(func));
|
|
}
|
|
|
|
subscription<packet, ethernet_address> l3_protocol::receive(
|
|
std::function<future<> (packet p, ethernet_address from)> rx_fn,
|
|
std::function<bool (forward_hash&, packet&, size_t)> forward) {
|
|
return _netif->register_l3(_proto_num, std::move(rx_fn), std::move(forward));
|
|
};
|
|
|
|
interface::interface(std::shared_ptr<device> dev)
|
|
: _dev(dev)
|
|
, _rx(_dev->receive([this] (packet p) { return dispatch_packet(std::move(p)); }))
|
|
, _hw_address(_dev->hw_address())
|
|
, _hw_features(_dev->hw_features()) {
|
|
dev->local_queue().register_packet_provider([this, idx = 0u] () mutable {
|
|
std::experimental::optional<packet> p;
|
|
for (size_t i = 0; i < _pkt_providers.size(); i++) {
|
|
auto l3p = _pkt_providers[idx++]();
|
|
if (idx == _pkt_providers.size())
|
|
idx = 0;
|
|
if (l3p) {
|
|
auto l3pv = std::move(l3p.value());
|
|
auto eh = l3pv.p.prepend_header<eth_hdr>();
|
|
eh->dst_mac = l3pv.to;
|
|
eh->src_mac = _hw_address;
|
|
eh->eth_proto = uint16_t(l3pv.proto_num);
|
|
*eh = hton(*eh);
|
|
p = std::move(l3pv.p);
|
|
return p;
|
|
}
|
|
}
|
|
return p;
|
|
});
|
|
}
|
|
|
|
subscription<packet, ethernet_address>
|
|
interface::register_l3(eth_protocol_num proto_num,
|
|
std::function<future<> (packet p, ethernet_address from)> next,
|
|
std::function<bool (forward_hash&, packet& p, size_t)> forward) {
|
|
auto i = _proto_map.emplace(std::piecewise_construct, std::make_tuple(uint16_t(proto_num)), std::forward_as_tuple(std::move(forward)));
|
|
assert(i.second);
|
|
l3_rx_stream& l3_rx = i.first->second;
|
|
return l3_rx.packet_stream.listen(std::move(next));
|
|
}
|
|
|
|
unsigned interface::hash2cpu(uint32_t hash) {
|
|
return _dev->hash2cpu(hash);
|
|
}
|
|
|
|
void interface::forward(unsigned cpuid, packet p) {
|
|
static __thread unsigned queue_depth;
|
|
|
|
if (queue_depth < 1000) {
|
|
queue_depth++;
|
|
auto src_cpu = engine.cpu_id();
|
|
smp::submit_to(cpuid, [this, p = std::move(p), src_cpu]() mutable {
|
|
_dev->l2receive(p.free_on_cpu(src_cpu));
|
|
}).then([] {
|
|
queue_depth--;
|
|
});
|
|
}
|
|
}
|
|
|
|
future<> interface::dispatch_packet(packet p) {
|
|
auto eh = p.get_header<eth_hdr>();
|
|
if (eh) {
|
|
auto i = _proto_map.find(ntoh(eh->eth_proto));
|
|
if (i != _proto_map.end()) {
|
|
l3_rx_stream& l3 = i->second;
|
|
auto fw = _dev->forward_dst(engine.cpu_id(), [&p, &l3] () {
|
|
auto hwrss = p.rss_hash();
|
|
if (hwrss) {
|
|
return hwrss.value();
|
|
} else {
|
|
forward_hash data;
|
|
if (l3.forward(data, p, sizeof(eth_hdr))) {
|
|
return toeplitz_hash(rsskey, data);
|
|
}
|
|
return 0u;
|
|
}
|
|
});
|
|
if (fw != engine.cpu_id()) {
|
|
forward(fw, std::move(p));
|
|
} else {
|
|
auto h = ntoh(*eh);
|
|
auto from = h.src_mac;
|
|
p.trim_front(sizeof(*eh));
|
|
// avoid chaining, since queue lenth is unlimited
|
|
// drop instead.
|
|
if (l3.ready.available()) {
|
|
l3.ready = l3.packet_stream.produce(std::move(p), from);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return make_ready_future<>();
|
|
}
|
|
|
|
}
|