virtio: fix overzealous vring completion

The complete() function loops around, waiting for notifications and
cleaning up the ring when they happen.  However, we also call it
opportunistically from produce(), and each time this happens, it starts
another loop in the background, leaking memory.

Fix by splitting complete() into do_complete(), which doesn't loop, and
complete(), which does, and only call do_complete() from the produce() loop.
This commit is contained in:
Avi Kivity
2014-09-11 13:03:59 +03:00
parent b39ab60a4f
commit 7c7eebbab4

View File

@@ -174,6 +174,8 @@ public:
void disable_interrupts();
void enable_interrupts();
private:
void produce();
void do_complete();
size_t mask() { return size() - 1; }
size_t masked(size_t idx) { return idx & mask(); }
size_t available();
@@ -226,6 +228,11 @@ void vring::setup() {
}
void vring::run() {
produce();
complete();
}
void vring::produce() {
_producer(_available_descriptors).then([this] (std::vector<buffer_chain> vbc) {
for (auto&& bc: vbc) {
bool has_prev = false;
@@ -248,12 +255,12 @@ void vring::run() {
}
_avail._shared->_idx.store(_avail._head, std::memory_order_release);
_kick.signal(1);
complete();
run();
do_complete();
produce();
});
}
void vring::complete() {
void vring::do_complete() {
auto used_head = _used._shared->_idx.load(std::memory_order_acquire);
while (used_head != _used._tail) {
auto ue = _used._shared->_used_elements[masked(_used._tail++)];
@@ -268,6 +275,10 @@ void vring::complete() {
id = next;
}
}
}
void vring::complete() {
do_complete();
_notified.wait().then([this] (size_t ignore) {
complete();
});