Return SPCI_BUSY when trying to run a vCPU which is still stopping.
Bug: 144477730
Change-Id: I7d71fc0a13bcb60924ed82c9b9cd95d29799563e
diff --git a/driver/linux b/driver/linux
index 39fdefd..9abce27 160000
--- a/driver/linux
+++ b/driver/linux
@@ -1 +1 @@
-Subproject commit 39fdefde0565df7811cf4bb98e6185e624a43603
+Subproject commit 9abce277b3963472481d1068b98d502fe42d792c
diff --git a/src/api.c b/src/api.c
index 60d308f..bbec545 100644
--- a/src/api.c
+++ b/src/api.c
@@ -385,49 +385,45 @@
bool ret;
/*
- * Wait until the registers become available. All locks must be released
- * between iterations of this loop to avoid potential deadlocks if, on
- * any path, a lock needs to be taken after taking the decision to
- * switch context but before the registers have been saved.
+ * Check that the registers are available so that the vCPU can be run.
*
* The VM lock is not needed in the common case so it must only be taken
* when it is going to be needed. This ensures there are no inter-vCPU
* dependencies in the common run case meaning the sensitive context
* switch performance is consistent.
*/
- for (;;) {
- sl_lock(&vcpu->lock);
+ sl_lock(&vcpu->lock);
- /* The VM needs to be locked to deliver mailbox messages. */
- need_vm_lock = vcpu->state == VCPU_STATE_BLOCKED_MAILBOX;
- if (need_vm_lock) {
- sl_unlock(&vcpu->lock);
- sl_lock(&vcpu->vm->lock);
- sl_lock(&vcpu->lock);
- }
-
- if (vcpu->regs_available) {
- break;
- }
-
- if (vcpu->state == VCPU_STATE_RUNNING) {
- /*
- * vCPU is running on another pCPU.
- *
- * It's okay not to return the sleep duration here
- * because the other physical CPU that is currently
- * running this vCPU will return the sleep duration if
- * needed.
- */
- *run_ret = spci_error(SPCI_BUSY);
- ret = false;
- goto out;
- }
-
+ /* The VM needs to be locked to deliver mailbox messages. */
+ need_vm_lock = vcpu->state == VCPU_STATE_BLOCKED_MAILBOX;
+ if (need_vm_lock) {
sl_unlock(&vcpu->lock);
- if (need_vm_lock) {
- sl_unlock(&vcpu->vm->lock);
- }
+ sl_lock(&vcpu->vm->lock);
+ sl_lock(&vcpu->lock);
+ }
+
+ /*
+ * If the vCPU is already running somewhere then we can't run it here
+ * simultaneously. While it is actually running then the state should be
+ * `VCPU_STATE_RUNNING` and `regs_available` should be false. Once it
+ * stops running but while Hafnium is in the process of switching back
+ * to the primary there will be a brief period while the state has been
+ * updated but `regs_available` is still false (until
+ * `api_regs_state_saved` is called). We can't start running it again
+ * until this has finished, so count this state as still running for the
+ * purposes of this check.
+ */
+ if (vcpu->state == VCPU_STATE_RUNNING || !vcpu->regs_available) {
+ /*
+ * vCPU is running on another pCPU.
+ *
+ * It's okay not to return the sleep duration here because the
+ * other physical CPU that is currently running this vCPU will
+ * return the sleep duration if needed.
+ */
+ *run_ret = spci_error(SPCI_BUSY);
+ ret = false;
+ goto out;
}
if (atomic_load_explicit(&vcpu->vm->aborting, memory_order_relaxed)) {