Skip to content
Snippets Groups Projects
Commit b4e8d47d authored by Vlad Zolotarov's avatar Vlad Zolotarov Committed by Pekka Enberg
Browse files

msix: thread affinity


Instead of binding all msix interrupts to cpu 0, have them chase the
interrupt service routine thread and pin themselves to the same cpu.

This patch is based on the patch from Avi Kivity <avi@cloudius-systems.com>
and used some ideas of Nadav Har'El <nyh@cloudius-systems.com>.

It improves the performance of the single thread Rx netperf test by 16%:
before - 25694 Mbps
after  - 29875 Mbps

New in V2:
 - Dropped the functor class - use lambda instead.
 - Fixed the race in a waking flow.
 - Added some comments.
 - Added the performance numbers to the patch description.

Signed-off-by: default avatarVlad Zolotarov <vladz@cloudius-systems.com>
Signed-off-by: default avatarPekka Enberg <penberg@cloudius-systems.com>
parent ce177a50
No related branches found
No related tags found
No related merge requests found
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <osv/trace.hh> #include <osv/trace.hh>
TRACEPOINT(trace_msix_interrupt, "vector=0x%02x", unsigned); TRACEPOINT(trace_msix_interrupt, "vector=0x%02x", unsigned);
TRACEPOINT(trace_msix_migrate, "vector=0x%02x apic_id=0x%x",
unsigned, unsigned);
using namespace pci; using namespace pci;
...@@ -71,6 +73,14 @@ void msix_vector::interrupt(void) ...@@ -71,6 +73,14 @@ void msix_vector::interrupt(void)
_handler(); _handler();
} }
void msix_vector::set_affinity(unsigned apic_id)
{
msi_message msix_msg = apic->compose_msix(_vector, apic_id);
for (auto entry_id : _entryids) {
_dev->msix_write_entry(entry_id, msix_msg._addr, msix_msg._data);
}
}
interrupt_manager::interrupt_manager(pci::function* dev) interrupt_manager::interrupt_manager(pci::function* dev)
: _dev(dev) : _dev(dev)
{ {
...@@ -81,6 +91,42 @@ interrupt_manager::~interrupt_manager() ...@@ -81,6 +91,42 @@ interrupt_manager::~interrupt_manager()
} }
/**
* Changes the affinity of the MSI-X vector to the same CPU where its service
* routine thread is bound and then wakes that thread.
*
* @param current The CPU to which the MSI-X vector is currently bound
* @param v MSI-X vector handle
* @param t interrupt service routine thread
*/
static inline void set_affinity_and_wake(
sched::cpu*& current, msix_vector* v, sched::thread* t)
{
auto cpu = t->get_cpu();;
if (cpu != current) {
//
// According to PCI spec chapter 6.8.3.5 the MSI-X table entry may be
// updated only if the entry is masked and the new values are promissed
// to be read only when the entry is unmasked.
//
v->msix_mask_entries();
std::atomic_thread_fence(std::memory_order_seq_cst);
current = cpu;
trace_msix_migrate(v->get_vector(), cpu->arch.apic_id);
v->set_affinity(cpu->arch.apic_id);
std::atomic_thread_fence(std::memory_order_seq_cst);
v->msix_unmask_entries();
}
t->wake();
}
bool interrupt_manager::easy_register(std::initializer_list<msix_binding> bindings) bool interrupt_manager::easy_register(std::initializer_list<msix_binding> bindings)
{ {
unsigned n = bindings.size(); unsigned n = bindings.size();
...@@ -103,7 +149,21 @@ bool interrupt_manager::easy_register(std::initializer_list<msix_binding> bindin ...@@ -103,7 +149,21 @@ bool interrupt_manager::easy_register(std::initializer_list<msix_binding> bindin
auto isr = binding.isr; auto isr = binding.isr;
auto t = binding.t; auto t = binding.t;
bool assign_ok = assign_isr(vec, [=] { if (isr) isr(); if (t) t->wake(); }); bool assign_ok;
if (t) {
sched::cpu* current = nullptr;
assign_ok =
assign_isr(vec,
[=]() mutable {
if (isr)
isr();
set_affinity_and_wake(current, vec, t);
});
} else {
assign_ok = assign_isr(vec, [=]() { if (isr) isr(); });
}
if (!assign_ok) { if (!assign_ok) {
free_vectors(assigned); free_vectors(assigned);
return false; return false;
......
...@@ -35,6 +35,7 @@ public: ...@@ -35,6 +35,7 @@ public:
void add_entryid(unsigned entry_id); void add_entryid(unsigned entry_id);
void interrupt(void); void interrupt(void);
void set_handler(std::function<void ()> handler); void set_handler(std::function<void ()> handler);
void set_affinity(unsigned apic_id);
private: private:
// Handler to invoke... // Handler to invoke...
......
...@@ -382,6 +382,9 @@ public: ...@@ -382,6 +382,9 @@ public:
static void wait_for(mutex& mtx, waitable&&... waitables); static void wait_for(mutex& mtx, waitable&&... waitables);
void wake(); void wake();
cpu* get_cpu() const {
return _detached_state.get()->_cpu;
}
// wake up after acquiring mtx // wake up after acquiring mtx
// //
// mtx must be locked, and wr must be a free wait_record that will // mtx must be locked, and wr must be a free wait_record that will
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment