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.

Reply via email to