From: Jan Kiszka <jan.kis...@siemens.com> Simplify the shutdown logic by requiring the root cell driver to explicitly destroy all non-root cells first. In particular, it helps to resolve the tricky handover of non-root cell CPUs in unplugged state to Linux.
At this chance, properly document potential races around the shutdown and other management hypercalls. They are non-obvious but harmless for the integrity of any non-root cell. Signed-off-by: Jan Kiszka <jan.kis...@siemens.com> --- Documentation/hypervisor-interfaces.txt | 11 ++++--- hypervisor/control.c | 51 +++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/Documentation/hypervisor-interfaces.txt b/Documentation/hypervisor-interfaces.txt index e9f0ae0..50e6fac 100644 --- a/Documentation/hypervisor-interfaces.txt +++ b/Documentation/hypervisor-interfaces.txt @@ -76,8 +76,10 @@ Return code: EAX Hypercall "Disable" (code 0) - - - - - - - - - - - - - - - -Tries to destroy all non-Linux cells and then shuts down the hypervisor, -returning full control over the hardware back to Linux. +Tries to shuts down the hypervisor, returning full control over the hardware +back to Linux. All non-root cells must have been destroyed prior to issuing +this hypercall. The hypercall has to be invoked on all CPUs of the root cell to +achieve a consistent shutdown. This hypercall can only be issued on CPUs belonging to the Linux cell. @@ -86,8 +88,9 @@ Arguments: None Return code: 0 on success, negative error code otherwise Possible errors are: - -EPERM (-1) - hypercall was issued over a non-Linux cell or an active - cell rejected the shutdown request + -EPERM (-1) - hypercall was issued over a non-Linux cell or an active + cell rejected the shutdown request + -EBUSY (-16) - one or more non-root cells still exist Hypercall "Cell Create" (code 1) diff --git a/hypervisor/control.c b/hypervisor/control.c index 39d293e..a266341 100644 --- a/hypervisor/control.c +++ b/hypervisor/control.c @@ -669,7 +669,6 @@ static int cell_get_state(struct per_cpu *cpu_data, unsigned long id) static int shutdown(struct per_cpu *cpu_data) { unsigned int this_cpu = cpu_data->cpu_id; - struct cell *cell; unsigned int cpu; int state, ret; @@ -677,32 +676,40 @@ static int shutdown(struct per_cpu *cpu_data) if (cpu_data->cell != &root_cell) return -EPERM; + /* + * This may race against another root cell CPU invoking a different + * management hypercall (cell create, set loadable, start, destroy). + * Those suspend the root cell to protect against concurrent requests. + * We can't do this here because all root cell CPUs will invoke this + * function, and cell_suspend doesn't support such a scenario. We are + * safe nevertheless because we only need to see a consistent num_cells + * that is not increasing anymore once the shutdown was started: + * + * If another CPU in a management hypercall already called cell_suspend, + * it is now waiting for this CPU to react. In this case, we see + * num_cells prior to any change, can start the shutdown if it is 1, and + * will prevent the other CPU from changing it anymore. This is because + * we are taking one CPU away from the hypervisor when leaving shutdown. + * This will lock up the root cell (which is violating the hypercall + * protocol), but only if it was the last cell. + * + * If the other CPU already returned from cell_suspend, we cannot be + * running in parallel before that CPU releases the root cell again via + * cell_resume. In that case, we will see the result of the change. + * + * shutdown_lock is here just to coordinate between the root cell CPUs + * who is evaluating num_cells and should start the shutdown depending + * on its state. + */ spin_lock(&shutdown_lock); if (cpu_data->shutdown_state == SHUTDOWN_NONE) { - state = SHUTDOWN_STARTED; - for_each_non_root_cell(cell) - if (!cell_shutdown_ok(cell)) - state = -EPERM; - - if (state == SHUTDOWN_STARTED) { + if (num_cells == 1) { printk("Shutting down hypervisor\n"); - - for_each_non_root_cell(cell) { - cell_suspend(cell, cpu_data); - - printk("Closing cell \"%s\"\n", - cell->config->name); - - for_each_cpu(cpu, cell->cpu_set) { - printk(" Releasing CPU %d\n", cpu); - arch_shutdown_cpu(cpu); - } - } - - printk("Closing root cell \"%s\"\n", - root_cell.config->name); arch_shutdown(); + state = SHUTDOWN_STARTED; + } else { + state = -EBUSY; } for_each_cpu(cpu, root_cell.cpu_set) -- 2.1.4 -- You received this message because you are subscribed to the Google Groups "Jailhouse" group. To unsubscribe from this group and stop receiving emails from it, send an email to jailhouse-dev+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.