Skip to content

Commit

Permalink
improve interrupt queue in vcpu_t
Browse files Browse the repository at this point in the history
  • Loading branch information
wbenny committed Aug 1, 2019
1 parent 4b67630 commit b41d9f9
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 100 deletions.
14 changes: 7 additions & 7 deletions src/hvpp/hvpp/interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ class interrupt_t final
//
// Constructors.
//
constexpr
interrupt_t() noexcept // TODO: make default-constructor private!
: info_{}
, error_code_{}
, rip_adjust_{}
{ }

constexpr
interrupt_t(
vmx::interrupt_type interrupt_type,
Expand Down Expand Up @@ -60,13 +67,6 @@ class interrupt_t final
private:
friend class vcpu_t;

constexpr
interrupt_t() noexcept
: info_{}
, error_code_{}
, rip_adjust_{}
{ }

constexpr
interrupt_t(
vmx::interrupt_type interrupt_type,
Expand Down
6 changes: 0 additions & 6 deletions src/hvpp/hvpp/vcpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,6 @@ vcpu_t::vcpu_t(vmexit_handler& handler) noexcept
, ept_count_{}
, ept_index_{}

//
// Initialize pending-interrupt FIFO queue.
//
, pending_interrupt_first_{}
, pending_interrupt_count_{}

//
// Well, this is also not necessary.
// This member is reset to "false" on each VM-exit in entry_host() method.
Expand Down
24 changes: 16 additions & 8 deletions src/hvpp/hvpp/vcpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "ia32/arch.h"

#include "lib/deque.h"
#include "lib/error.h"

#include "lib/mm/memory_mapper.h"
Expand Down Expand Up @@ -61,18 +62,27 @@ class vcpu_t final

public:
//
// Make storage for up-to 16 pending interrupts.
// Pending interrupt queue (FIFO).
// Make storage for up-to 64 pending interrupts.
// In practice I haven't seen more than 2 pending interrupts.
//
static constexpr auto pending_interrupt_queue_size = 16;
using interrupt_queue_t = fixed_dequeue<interrupt_t, 64>;

enum interrupt_queue_type
{
interrupt_queue_external, // vmx::interrupt_type::external (0)
interrupt_queue_nmi, // vmx::interrupt_type::nmi (2)

interrupt_queue_max
};

auto interrupt_info() const noexcept -> interrupt_t;
auto idt_vectoring_info() const noexcept -> interrupt_t;

bool interrupt_inject(interrupt_t interrupt, bool first = false) noexcept;
bool interrupt_inject(interrupt_t interrupt, bool front = false) noexcept;
void interrupt_inject_force(interrupt_t interrupt) noexcept;
void interrupt_inject_pending() noexcept;
bool interrupt_is_pending() const noexcept;
void interrupt_inject_pending(interrupt_queue_type queue_type) noexcept;
bool interrupt_is_pending(interrupt_queue_type queue_type) const noexcept;

auto exit_instruction_info_guest_va() const noexcept -> void*;

Expand Down Expand Up @@ -410,9 +420,7 @@ class vcpu_t final
//
// Pending interrupt queue (FIFO).
//
interrupt_t pending_interrupt_[pending_interrupt_queue_size];
uint8_t pending_interrupt_first_;
uint8_t pending_interrupt_count_;
interrupt_queue_t pending_interrupt_queue_[interrupt_queue_max];

bool suppress_rip_adjust_;
};
Expand Down
144 changes: 68 additions & 76 deletions src/hvpp/hvpp/vcpu.inl
Original file line number Diff line number Diff line change
Expand Up @@ -36,77 +36,82 @@ auto vcpu_t::idt_vectoring_info() const noexcept -> interrupt_t
return result;
}

bool vcpu_t::interrupt_inject(interrupt_t interrupt, bool first /*= false */) noexcept
#define interrupt_type_to_queue_type(type) ( \
type == vmx::interrupt_type::external ? interrupt_queue_external \
: type == vmx::interrupt_type::nmi ? interrupt_queue_nmi \
: vcpu_t::interrupt_queue_type(-1) /* error */ \
)

bool vcpu_t::interrupt_inject(interrupt_t interrupt, bool front /*= false */) noexcept
{
//
// External interrupts cannot be injected into the
// guest if guest isn't interruptible (e.g.: guest
// is blocked by "mov ss", or EFLAGS.IF == 0).
//
bool interruptible = true;

if (interrupt.type() == vmx::interrupt_type::external)
{
bool interruptible = context().rflags.interrupt_enable_flag &&
guest_interruptibility_state().flags;
//
// External interrupts cannot be injected into the
// guest if guest isn't interruptible (e.g.: guest
// is blocked by "mov ss", or EFLAGS.IF == 0).
//
interruptible = context().rflags.interrupt_enable_flag &&
guest_interruptibility_state().flags;
}
else if (interrupt.type() == vmx::interrupt_type::nmi)
{
interruptible = !guest_interruptibility_state().blocking_by_nmi;
}

if (!interruptible)
{
//
// Make sure there aren't too much pending interrupts.
// We don't want the queue to overflow.
//
hvpp_assert(pending_interrupt_count_ < pending_interrupt_queue_size);
if (interruptible)
{
//
// Inject interrupt immediately.
//
interrupt_inject_force(interrupt);

if (first)
{
//
// Enqueue pending interrupt ("push_front").
//
pending_interrupt_first_ = !pending_interrupt_first_
? pending_interrupt_queue_size - 1
: pending_interrupt_first_ - 1;

pending_interrupt_[pending_interrupt_first_] = interrupt;
pending_interrupt_count_ += 1;
}
else
{
//
// Enqueue pending interrupt ("push_back").
//
auto index = (pending_interrupt_first_ + pending_interrupt_count_) % pending_interrupt_queue_size;
//
// Signalize immediately injected interrupt.
//
return true;
}

pending_interrupt_[index] = interrupt;
pending_interrupt_count_ += 1;
}
auto queue_type = interrupt_type_to_queue_type(interrupt.type());
auto& queue = pending_interrupt_queue_[queue_type];

//
// Enable Interrupt-window exiting.
//
auto procbased_ctls = processor_based_controls();
procbased_ctls.interrupt_window_exiting = true;
processor_based_controls(procbased_ctls);

//
// "false" signalizes that the interrupt hasn't been
// immediately injected.
//
return false;
}
}
//
// Enqueue pending interrupt.
//
front
? queue.push_front(std::move(interrupt))
: queue.push_back(std::move(interrupt));

//
// Inject interrupt immediately.
// Enable Interrupt/NMI-window exiting.
//
interrupt_inject_force(interrupt);
auto procbased_ctls = processor_based_controls();

if (interrupt.type() == vmx::interrupt_type::external)
{
procbased_ctls.interrupt_window_exiting = true;
}
else if (interrupt.type() == vmx::interrupt_type::nmi)
{
procbased_ctls.nmi_window_exiting = true;
}

processor_based_controls(procbased_ctls);

//
// Signalize immediately injected interrupt.
// "false" signalizes that the interrupt hasn't been
// immediately injected.
//
return true;
return false;
}

void vcpu_t::interrupt_inject_force(interrupt_t interrupt) noexcept
{
//
// Set ctrl_vmentry_interruption_info.
//
entry_interruption_info(interrupt.info_);

if (interrupt.valid())
Expand Down Expand Up @@ -200,35 +205,22 @@ void vcpu_t::interrupt_inject_force(interrupt_t interrupt) noexcept
}
}

void vcpu_t::interrupt_inject_pending() noexcept
void vcpu_t::interrupt_inject_pending(interrupt_queue_type queue_type) noexcept
{
//
// Make sure there is at least 1 pending interrupt.
//
hvpp_assert(
interrupt_is_pending() &&
pending_interrupt_count_ <= pending_interrupt_queue_size
);

//
// Dequeue pending interrupt ("pop_front").
// Dequeue and inject pending interrupt.
//
auto interrupt = pending_interrupt_[pending_interrupt_first_];
auto& queue = pending_interrupt_queue_[queue_type];

pending_interrupt_first_ += 1;
pending_interrupt_count_ -= 1;

if (!pending_interrupt_count_ || pending_interrupt_first_ == pending_interrupt_queue_size)
{
pending_interrupt_first_ = 0;
}

interrupt_inject_force(interrupt);
interrupt_inject_force(queue.front());
queue.pop_front();
}

bool vcpu_t::interrupt_is_pending() const noexcept
bool vcpu_t::interrupt_is_pending(interrupt_queue_type queue_type) const noexcept
{
return pending_interrupt_count_ > 0;
auto& queue = pending_interrupt_queue_[queue_type];

return queue.size() > 0;
}

auto vcpu_t::exit_instruction_info_guest_va() const noexcept -> void*
Expand Down
6 changes: 3 additions & 3 deletions src/hvpp/hvpp/vmexit/vmexit_passthrough.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,19 +99,19 @@ void vmexit_passthrough_handler::handle_interrupt_window(vcpu_t& vp) noexcept
//
// Make sure there is an interrupt pending.
//
hvpp_assert(vp.interrupt_is_pending());
hvpp_assert(vp.interrupt_is_pending(vcpu_t::interrupt_queue_external));

//
// Guest is in the interruptible state.
// Dequeue one pending interrupt from the queue
// and inject it.
//
vp.interrupt_inject_pending();
vp.interrupt_inject_pending(vcpu_t::interrupt_queue_external);

//
// If queue is empty, disable Interrupt-window exiting.
//
if (!vp.interrupt_is_pending())
if (!vp.interrupt_is_pending(vcpu_t::interrupt_queue_external))
{
auto procbased_ctls = vp.processor_based_controls();
procbased_ctls.interrupt_window_exiting = false;
Expand Down

0 comments on commit b41d9f9

Please sign in to comment.