From 73b8f983184a60693599660f748c9a0e803f68a4 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Wed, 5 Nov 2014 14:29:14 +0100 Subject: [PATCH 1/9] xen: use nr_ents instead of numeric constant in netfront header Signed-off-by: Glauber Costa --- net/xenfront.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/xenfront.hh b/net/xenfront.hh index 8a8114697d..f43a944f95 100644 --- a/net/xenfront.hh +++ b/net/xenfront.hh @@ -72,7 +72,7 @@ class front_ring { public: class entries { private: - std::array _entries; + std::array::nr_ents> _entries; front_ring *_ring; public: entries(front_ring *ring) : _ring(ring) {} From 63c8db870fda03dda4d0488ed32679c4f7d3e527 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Wed, 5 Nov 2014 22:30:25 +0100 Subject: [PATCH 2/9] xen: remove debug printfs As packet flow is working reasonably now, most of the prints can go. Signed-off-by: Glauber Costa --- core/xen/evtchn.cc | 1 - net/xenfront.cc | 2 -- 2 files changed, 3 deletions(-) diff --git a/core/xen/evtchn.cc b/core/xen/evtchn.cc index 149c23f1a2..4f5e0eb42f 100644 --- a/core/xen/evtchn.cc +++ b/core/xen/evtchn.cc @@ -85,7 +85,6 @@ public: }; void kernel_evtchn::make_ready(void *arg) { - printf("Got an interrupt!\n"); int fd = reinterpret_cast(arg); uint64_t one = 1; ::write(fd, &one, sizeof(one)); diff --git a/net/xenfront.cc b/net/xenfront.cc index b77467c002..c2f0563876 100644 --- a/net/xenfront.cc +++ b/net/xenfront.cc @@ -139,7 +139,6 @@ xenfront_net_device::send(packet _p) { _tx_ring._sring->req_event++; if ((frag + 1) == p.nr_frags()) { - printf("NOTIFY!!\n"); _evtchn->notify(_tx_evtchn); return make_ready_future<>(); } else { @@ -162,7 +161,6 @@ future front_ring::free_idx() { future<> xenfront_net_device::queue_rx_packet() { - printf("Got at queue\n"); auto rsp_cons = _rx_ring.rsp_cons; rmb(); auto rsp_prod = _rx_ring._sring->rsp_prod; From ee172e36c1d041548af2c5f5317f7235f4c6771f Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Wed, 5 Nov 2014 22:31:55 +0100 Subject: [PATCH 3/9] xen: enhance gntref Enhance gntref with some useful operations. Also provide a default object that represents an invalid grant. Signed-off-by: Glauber Costa --- core/xen/gntalloc.cc | 4 +++- core/xen/gntalloc.hh | 10 +++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/core/xen/gntalloc.cc b/core/xen/gntalloc.cc index 2c43f57743..72bedaaa74 100644 --- a/core/xen/gntalloc.cc +++ b/core/xen/gntalloc.cc @@ -8,6 +8,8 @@ #include "osv_xen.hh" #include "gntalloc.hh" +gntref invalid_ref; + // FIXME: Most of the destructors are yet to be coded // @@ -153,7 +155,7 @@ gntref kernel_gntalloc::alloc_ref() { throw std::runtime_error("Failed to initialize allocate grant\n"); } - return {int(ref), page}; + return gntref(int(ref), page); } grant_head *kernel_gntalloc::alloc_ref(unsigned nr_ents, bool alloc) { diff --git a/core/xen/gntalloc.hh b/core/xen/gntalloc.hh index 398642c257..409bbdbc67 100644 --- a/core/xen/gntalloc.hh +++ b/core/xen/gntalloc.hh @@ -3,9 +3,15 @@ #include "core/posix.hh" -struct gntref { +class gntref { +public: int xen_id; void* page; + bool operator==(const gntref &a) { return (xen_id == a.xen_id) && (page == a.page); } + gntref& operator=(const gntref &a) { xen_id = a.xen_id; page = a.page; return *this; } + gntref(int id, void *page) : xen_id(id), page(page) {} + gntref() : xen_id(-1), page(nullptr) {} + operator bool() const { return xen_id != -1 && page != nullptr; } }; class gntalloc; @@ -35,4 +41,6 @@ public: virtual grant_head *alloc_ref(unsigned nr_ents, bool alloc) = 0; friend class grant_head; }; + +extern gntref invalid_ref; #endif From ae1122bfc8b15632d576f61e647c8e4cf326583b Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Wed, 5 Nov 2014 22:34:04 +0100 Subject: [PATCH 4/9] xen: manage index list Aside from managing the grant references, we also need to manage the positional indexes in the array. We need to keep track of which indexes are free, and which are used. Because we need the actual position number to fill xen's data structures, I figured we could use a queue and then fill it up with all the integers in our range. The queue is already futurized, so that's easy. Signed-off-by: Glauber Costa --- net/xenfront.cc | 13 ++++++++----- net/xenfront.hh | 13 ++++++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/net/xenfront.cc b/net/xenfront.cc index c2f0563876..f2a3bea12f 100644 --- a/net/xenfront.cc +++ b/net/xenfront.cc @@ -111,7 +111,7 @@ xenfront_net_device::send(packet _p) { // FIXME: negotiate and use scatter/gather _p.linearize(); - return _tx_ring.free_idx().then([this, p = std::move(_p), frag] (uint32_t idx) mutable { + return _tx_ring.entries.get_index().then([this, p = std::move(_p), frag] (unsigned idx) mutable { auto req_prod = _tx_ring._sring->req_prod; @@ -152,11 +152,14 @@ xenfront_net_device::send(packet _p) { #define rmb() asm volatile("lfence":::"memory"); #define wmb() asm volatile("":::"memory"); -// FIXME: This is totally wrong, just coded so we can gt started with sending template -future front_ring::free_idx() { - static uint32_t idx = 0; - return make_ready_future(idx++); +future front_ring::entries::get_index() { + return _ids.pop_eventually(); +} + +template +future<> front_ring::entries::free_index(unsigned id) { + return _ids.push_eventually(std::move(id)); } future<> xenfront_net_device::queue_rx_packet() { diff --git a/net/xenfront.hh b/net/xenfront.hh index f43a944f95..04ebb1681c 100644 --- a/net/xenfront.hh +++ b/net/xenfront.hh @@ -9,6 +9,7 @@ #include "net.hh" #include "core/sstring.hh" #include "core/xen/gntalloc.hh" +#include "core/queue.hh" std::unique_ptr create_xenfront_net_device(boost::program_options::variables_map opts, bool userspace); boost::program_options::options_description get_xenfront_net_options_description(); @@ -71,13 +72,21 @@ template class front_ring { public: class entries { + protected: + queue _ids; private: std::array::nr_ents> _entries; front_ring *_ring; public: - entries(front_ring *ring) : _ring(ring) {} + entries(front_ring *ring) : _ids(front_ring::nr_ents), _ring(ring) { + for (unsigned i = 0; i < front_ring::nr_ents; ++i) { + _ids.push(std::move(i)); + } + } gntref& operator[](std::size_t i) { return _entries[_ring->idx(i)]; } friend front_ring; + future get_index(); + future<> free_index(unsigned index); }; protected: uint32_t idx(int i) { return i & (nr_ents - 1); } @@ -92,8 +101,6 @@ public: , _sring(new (r.page) sring()) { } - - future free_idx(); entries entries; sring *_sring; From 01c861fba48577b60071d143d1758324c4d46cef Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Wed, 5 Nov 2014 23:06:22 +0100 Subject: [PATCH 5/9] xen: don't increment producer index in receive path Right now, we allocate the whole index, and notify the backend that we have produced nr_ents indexes. If we do that, we cannot increment the producer index when we receive a new package. This would make the index overflow, and basically, it is the responsible for the biggest part of the slowdown we are seeing. Before this patch, we're seeing 2s RTT for pings. After the patch: 64 bytes from 192.168.100.79: icmp_seq=1 ttl=64 time=0.437 ms 64 bytes from 192.168.100.79: icmp_seq=2 ttl=64 time=0.431 ms 64 bytes from 192.168.100.79: icmp_seq=3 ttl=64 time=0.475 ms Signed-off-by: Glauber Costa --- net/xenfront.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/net/xenfront.cc b/net/xenfront.cc index f2a3bea12f..b9f4257ed1 100644 --- a/net/xenfront.cc +++ b/net/xenfront.cc @@ -190,7 +190,6 @@ future<> xenfront_net_device::queue_rx_packet() { } _rx_ring._sring->rsp_event = rsp_cons + 1; - _rx_ring._sring->req_prod = rsp_cons + 1; rsp_prod = _rx_ring._sring->rsp_prod; // FIXME: END GRANT. FIXME: ALLOCATE MORE MEMORY From 722926d5459d98fa6feb322ac6d6b2c98d622ce0 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Wed, 5 Nov 2014 23:13:13 +0100 Subject: [PATCH 6/9] xen: factor out allocation of a single rx entry I'll need this code later to refill the buffer, so factor this out Signed-off-by: Glauber Costa --- net/xenfront.cc | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/net/xenfront.cc b/net/xenfront.cc index b9f4257ed1..d691d4ea36 100644 --- a/net/xenfront.cc +++ b/net/xenfront.cc @@ -65,6 +65,7 @@ private: future<> alloc_rx_references(unsigned refs); future<> queue_rx_packet(); + void alloc_one_rx_reference(unsigned id); std::string path(std::string s) { return _device_str + "/" + s; } public: @@ -199,17 +200,22 @@ future<> xenfront_net_device::queue_rx_packet() { return make_ready_future<>(); } +void xenfront_net_device::alloc_one_rx_reference(unsigned index) { + + _rx_ring.entries[index] = _rx_refs->new_ref(); + + // This is how the backend knows where to put data. + auto req = &_rx_ring._sring->_ring[index].req; + req->id = index; + req->gref = _rx_ring.entries[index].xen_id; +} + future<> xenfront_net_device::alloc_rx_references(unsigned refs) { auto req_prod = _rx_ring.req_prod_pvt; rmb(); for (auto i = req_prod; (i < _rx_ring.nr_ents) && (i < refs); ++i) { - _rx_ring.entries[i] = _rx_refs->new_ref(); - - // This is how the backend knows where to put data. - auto req = &_rx_ring._sring->_ring[i].req; - req->id = i; - req->gref = _rx_ring.entries[i].xen_id; + alloc_one_rx_reference(i); ++req_prod; } From 0a1f5f9e731367be5e19727de7f2460d8be47194 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Wed, 5 Nov 2014 23:26:29 +0100 Subject: [PATCH 7/9] xen: defer grant table operations Instead of returning a reference to a grant that is already present in an array, defer the initialization. This is how the OSv driver handles it, and I honestly am not sure if this is really needed: it seems to me we should be able to just reuse the old grants. I need to check in the backend code if we can be any smarter than this. However, right now we need to do something to recycle the buffers, and just re-doing the refs would lead to inconsistencies. So the best by now is to close and reopen the grants, and then later on rework this in a way that works for both the initial setup and the recycle. Signed-off-by: Glauber Costa --- core/xen/gntalloc.cc | 61 +++++++++++++++++++++----------------------- core/xen/gntalloc.hh | 8 +++--- net/xenfront.cc | 4 +-- 3 files changed, 34 insertions(+), 39 deletions(-) diff --git a/core/xen/gntalloc.cc b/core/xen/gntalloc.cc index 72bedaaa74..29f96609a9 100644 --- a/core/xen/gntalloc.cc +++ b/core/xen/gntalloc.cc @@ -15,10 +15,11 @@ gntref invalid_ref; class userspace_grant_head : public grant_head { std::atomic _ref_head = { 0 }; + std::vector _refs; public: - userspace_grant_head(std::vector v) : grant_head(v) {} - virtual gntref& new_ref() override; - virtual gntref& new_ref(void *addr, size_t size) override; + userspace_grant_head(std::vector v) : _refs(v) {} + virtual gntref new_ref() override; + virtual gntref new_ref(void *addr, size_t size) override; }; class userspace_gntalloc : public gntalloc { @@ -29,7 +30,7 @@ public: explicit userspace_gntalloc(unsigned otherend); ~userspace_gntalloc(); virtual gntref alloc_ref() override; - virtual grant_head *alloc_ref(unsigned refs, bool alloc) override; + virtual grant_head *alloc_ref(unsigned refs) override; }; userspace_gntalloc::userspace_gntalloc(unsigned otherend) @@ -52,7 +53,7 @@ userspace_gntalloc::get_gref(unsigned nr_ents) return gref; } -grant_head *userspace_gntalloc::alloc_ref(unsigned nr_ents, bool alloc) { +grant_head *userspace_gntalloc::alloc_ref(unsigned nr_ents) { auto gref = get_gref(nr_ents); @@ -79,11 +80,11 @@ gntref userspace_gntalloc::alloc_ref() { return p; } -gntref& userspace_grant_head::new_ref() { +gntref userspace_grant_head::new_ref() { return _refs[_id++ % _refs.size()]; } -gntref& userspace_grant_head::new_ref(void *addr, size_t size) { +gntref userspace_grant_head::new_ref(void *addr, size_t size) { gntref& ref = _refs[_id % _refs.size()]; memcpy(ref.page, addr, size); return ref; @@ -93,10 +94,11 @@ gntref& userspace_grant_head::new_ref(void *addr, size_t size) { class kernel_gntalloc; class kernel_grant_head : public grant_head { + uint32_t _head; public: - kernel_grant_head(std::vector r) : grant_head(r) {} - virtual gntref& new_ref() override; - virtual gntref& new_ref(void *addr, size_t size) override; + kernel_grant_head(uint32_t head) : _head(head) {} + virtual gntref new_ref() override; + virtual gntref new_ref(void *addr, size_t size) override; }; class kernel_gntalloc : public gntalloc { @@ -110,7 +112,7 @@ public: kernel_gntalloc(unsigned otherend) : gntalloc(otherend) {} virtual gntref alloc_ref() override; - virtual grant_head *alloc_ref(unsigned refs, bool alloc) override; + virtual grant_head *alloc_ref(unsigned refs) override; friend class kernel_grant_head; }; @@ -121,25 +123,28 @@ virt_to_mfn(void *virt) { return virt_to_phys(virt) >> 12; } -gntref& kernel_grant_head::new_ref() { - return _refs[_id++ % _refs.size()]; +gntref kernel_grant_head::new_ref() { + auto gnt = dynamic_cast(gntalloc::instance()); + + auto ref = gnttab_claim_grant_reference(&_head); + auto page = gnt->new_frame(); + gnttab_grant_foreign_access_ref(ref, gnt->_otherend, virt_to_mfn(page), 0); + return gntref(ref, page); } -gntref& kernel_grant_head::new_ref(void *addr, size_t size) { - - gntref& ref = _refs[_id % _refs.size()]; +gntref kernel_grant_head::new_ref(void *addr, size_t size) { auto gnt = dynamic_cast(gntalloc::instance()); // FIXME: if we can guarantee that the packet allocation came from malloc, not // mmap, we can grant it directly, without copying. We would also have to propagate // the offset information, but that is easier - ref.page = gnt->new_frame(); - memcpy(ref.page, addr, size); + auto page = gnt->new_frame(); + memcpy(page, addr, size); - gnttab_grant_foreign_access_ref(ref.xen_id, gnt->_otherend, virt_to_mfn(ref.page), 0); - - return _refs[_id++ % _refs.size()]; + auto ref = gnttab_claim_grant_reference(&_head); + gnttab_grant_foreign_access_ref(ref, gnt->_otherend, virt_to_mfn(page), 0); + return gntref(ref, page); } void *kernel_gntalloc::new_frame() { @@ -158,7 +163,7 @@ gntref kernel_gntalloc::alloc_ref() { return gntref(int(ref), page); } -grant_head *kernel_gntalloc::alloc_ref(unsigned nr_ents, bool alloc) { +grant_head *kernel_gntalloc::alloc_ref(unsigned nr_ents) { std::vector v; uint32_t head; @@ -166,18 +171,10 @@ grant_head *kernel_gntalloc::alloc_ref(unsigned nr_ents, bool alloc) { if (gnttab_alloc_grant_references(nr_ents, &head)) { throw std::runtime_error("Failed to initialize allocate grant\n"); } - for (unsigned i = 0; i < nr_ents; ++i) { - auto ref = gnttab_claim_grant_reference(&head); - void *page = nullptr; - if (alloc) { - page = new_frame(); - gnttab_grant_foreign_access_ref(ref, _otherend, virt_to_mfn(page), 0); - } - v.push_back({ref, page}); - } - return new kernel_grant_head(v); + return new kernel_grant_head(head); } + #endif gntalloc *gntalloc::_instance = nullptr; diff --git a/core/xen/gntalloc.hh b/core/xen/gntalloc.hh index 409bbdbc67..29d277bee5 100644 --- a/core/xen/gntalloc.hh +++ b/core/xen/gntalloc.hh @@ -19,11 +19,9 @@ class gntalloc; class grant_head { protected: unsigned _id = 0; - std::vector _refs; public: - grant_head(std::vector r) : _refs(r) {} - virtual gntref& new_ref() = 0; - virtual gntref& new_ref(void *addr, size_t size) = 0; + virtual gntref new_ref() = 0; + virtual gntref new_ref(void *addr, size_t size) = 0; }; class gntalloc { @@ -38,7 +36,7 @@ public: // The kernel interface can defer allocation, userspace allocation // cannot. The boolean "alloc" tell us whether or not we should allocate // now or try to defer. - virtual grant_head *alloc_ref(unsigned nr_ents, bool alloc) = 0; + virtual grant_head *alloc_ref(unsigned nr_ents) = 0; friend class grant_head; }; diff --git a/net/xenfront.cc b/net/xenfront.cc index d691d4ea36..0541ea384a 100644 --- a/net/xenfront.cc +++ b/net/xenfront.cc @@ -260,8 +260,8 @@ xenfront_net_device::xenfront_net_device(boost::program_options::variables_map o , _rx_evtchn(bind_rx_evtchn()) , _tx_ring(_gntalloc->alloc_ref()) , _rx_ring(_gntalloc->alloc_ref()) - , _tx_refs(_gntalloc->alloc_ref(front_ring::nr_ents, false)) - , _rx_refs(_gntalloc->alloc_ref(front_ring::nr_ents, true)) + , _tx_refs(_gntalloc->alloc_ref(front_ring::nr_ents)) + , _rx_refs(_gntalloc->alloc_ref(front_ring::nr_ents)) , _hw_address(net::parse_ethernet_address(_xenstore->read(path("mac")))) { _rx_stream.started(); From 3d0f2de8bb51d6c23e5a11c166c12a9c93f70eae Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Wed, 5 Nov 2014 23:41:33 +0100 Subject: [PATCH 8/9] xen: method to end a grant operation Signed-off-by: Glauber Costa --- core/xen/gntalloc.cc | 12 ++++++++++++ core/xen/gntalloc.hh | 1 + core/xen/osv_xen.hh | 4 ++++ 3 files changed, 17 insertions(+) diff --git a/core/xen/gntalloc.cc b/core/xen/gntalloc.cc index 29f96609a9..d835b4577c 100644 --- a/core/xen/gntalloc.cc +++ b/core/xen/gntalloc.cc @@ -20,6 +20,7 @@ public: userspace_grant_head(std::vector v) : _refs(v) {} virtual gntref new_ref() override; virtual gntref new_ref(void *addr, size_t size) override; + virtual void free_ref(gntref& ref); }; class userspace_gntalloc : public gntalloc { @@ -90,6 +91,10 @@ gntref userspace_grant_head::new_ref(void *addr, size_t size) { return ref; } +void userspace_grant_head::free_ref(gntref& ref) { + abort(); +} + #ifdef HAVE_OSV class kernel_gntalloc; @@ -99,6 +104,7 @@ public: kernel_grant_head(uint32_t head) : _head(head) {} virtual gntref new_ref() override; virtual gntref new_ref(void *addr, size_t size) override; + virtual void free_ref(gntref& ref); }; class kernel_gntalloc : public gntalloc { @@ -175,6 +181,12 @@ grant_head *kernel_gntalloc::alloc_ref(unsigned nr_ents) { return new kernel_grant_head(head); } +void kernel_grant_head::free_ref(gntref& ref) { + gnttab_end_foreign_access_ref(ref.xen_id); + gnttab_release_grant_reference(&_head, ref.xen_id); + free(ref.page); + ref = invalid_ref; +} #endif gntalloc *gntalloc::_instance = nullptr; diff --git a/core/xen/gntalloc.hh b/core/xen/gntalloc.hh index 29d277bee5..fde8462aad 100644 --- a/core/xen/gntalloc.hh +++ b/core/xen/gntalloc.hh @@ -22,6 +22,7 @@ protected: public: virtual gntref new_ref() = 0; virtual gntref new_ref(void *addr, size_t size) = 0; + virtual void free_ref(gntref& ref) = 0; }; class gntalloc { diff --git a/core/xen/osv_xen.hh b/core/xen/osv_xen.hh index 49630e76a8..08159d9512 100644 --- a/core/xen/osv_xen.hh +++ b/core/xen/osv_xen.hh @@ -24,6 +24,10 @@ gnttab_claim_grant_reference(uint32_t *private_head); extern int gnttab_grant_foreign_access(uint16_t domid, unsigned long frame, int readonly, uint32_t *result); extern void gnttab_grant_foreign_access_ref(uint32_t ref, uint16_t domid, unsigned long frame, int readonly); +extern void +gnttab_release_grant_reference(uint32_t *private_head, unsigned ref); +extern int +gnttab_end_foreign_access_ref(unsigned ref); extern "C" uint64_t virt_to_phys(void *virt); From 6c0aaa126c9d693c04d7acb2609a10a1b201f7bf Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Wed, 5 Nov 2014 23:45:19 +0100 Subject: [PATCH 9/9] xen: grant recycle handle buffer recycles. Right now it is very simple: allocate a new receive buffer after a succesful receival, and mark the tx spot free when we get the tx event notification. Signed-off-by: Glauber Costa --- net/xenfront.cc | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/net/xenfront.cc b/net/xenfront.cc index 0541ea384a..a74cede9e3 100644 --- a/net/xenfront.cc +++ b/net/xenfront.cc @@ -63,6 +63,7 @@ private: int bind_rx_evtchn(); future<> alloc_rx_references(unsigned refs); + future<> handle_tx_completions(); future<> queue_rx_packet(); void alloc_one_rx_reference(unsigned id); @@ -120,6 +121,8 @@ xenfront_net_device::send(packet _p) { auto ref = _tx_refs->new_ref(f.base, f.size); + assert(!_tx_ring.entries[idx]); + _tx_ring.entries[idx] = ref; auto req = &_tx_ring._sring->_ring[idx].req; @@ -193,7 +196,11 @@ future<> xenfront_net_device::queue_rx_packet() { _rx_ring._sring->rsp_event = rsp_cons + 1; rsp_prod = _rx_ring._sring->rsp_prod; - // FIXME: END GRANT. FIXME: ALLOCATE MORE MEMORY + + _rx_refs->free_ref(entry); + _rx_ring.entries.free_index(rsp.id).then([this, id = rsp.id]() { + alloc_one_rx_reference(id); + }); } // FIXME: Queue_rx maybe should not be a future then @@ -227,6 +234,30 @@ future<> xenfront_net_device::alloc_rx_references(unsigned refs) { return make_ready_future(); } +future<> xenfront_net_device::handle_tx_completions() { + auto prod = _tx_ring._sring->rsp_prod; + rmb(); + + for (unsigned i = _tx_ring.rsp_cons; i != prod; i++) { + auto rsp = _tx_ring[i].rsp; + + if (rsp.status == 1) { + continue; + } + + if (rsp.status != 0) { + printf("Packet error: Handle it\n"); + continue; + } + + auto entry = _tx_ring.entries[rsp.id]; + _tx_refs->free_ref(entry); + _tx_ring.entries.free_index(rsp.id).then([this]() {}); + } + _tx_ring.rsp_cons = prod; + return make_ready_future<>(); +} + ethernet_address xenfront_net_device::hw_address() { return _hw_address; } @@ -298,6 +329,11 @@ xenfront_net_device::xenfront_net_device(boost::program_options::variables_map o } alloc_rx_references(_rx_ring.nr_ents); + keep_doing([this] () { + return _evtchn->pending(_tx_evtchn).then([this] { + handle_tx_completions(); + }); + }); } xenfront_net_device::~xenfront_net_device() {