xen: make sure stop_machine_run() is always called in a tasklet

With core scheduling active it is mandatory for stop_machine_run() to
be called in idle context only (so either during boot or in a tasklet),
as otherwise a scheduling deadlock would occur: stop_machine_run()
does a cpu rendezvous by activating a tasklet on all other cpus. In
case stop_machine_run() was not called in an idle vcpu it would block
scheduling the idle vcpu on its siblings with core scheduling being
active, resulting in a hang.

Put a BUG_ON() into stop_machine_run() to test for being called in an
idle vcpu only and adapt the missing call site (ucode loading) to use a
tasklet for calling stop_machine_run().

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
master commit: 188f479de4
master date: 2020-03-02 13:08:04 +0000
This commit is contained in:
Juergen Gross authored and Jan Beulich committed 2020-04-09 09:03:21 +02:00
1 parent 431ddeb205
commit 07ac8a9790
3 files changed
+46 -20

No files matched your search

+35 -20
View File
@@ -584,30 +584,18 @@ static int do_microcode_update(void *patch)
return ret;
}
int microcode_update(XEN_GUEST_HANDLE_PARAM(const_void) buf, unsigned long len)
struct ucode_buf {
unsigned int len;
char buffer[];
};
static long microcode_update_helper(void *data)
{
int ret;
void *buffer;
struct ucode_buf *buffer = data;
unsigned int cpu, updated;
struct microcode_patch *patch;
if ( len != (uint32_t)len )
return -E2BIG;
if ( microcode_ops == NULL )
return -EINVAL;
buffer = xmalloc_bytes(len);
if ( !buffer )
return -ENOMEM;
ret = copy_from_guest(buffer, buf, len);
if ( ret )
{
xfree(buffer);
return -EFAULT;
}
/* cpu_online_map must not change during update */
if ( !get_cpu_maps() )
{
@@ -629,7 +617,7 @@ int microcode_update(XEN_GUEST_HANDLE_PARAM(const_void) buf, unsigned long len)
return -EPERM;
}
patch = parse_blob(buffer, len);
patch = parse_blob(buffer->buffer, buffer->len);
xfree(buffer);
if ( IS_ERR(patch) )
{
@@ -722,6 +710,33 @@ int microcode_update(XEN_GUEST_HANDLE_PARAM(const_void) buf, unsigned long len)
return ret;
}
int microcode_update(XEN_GUEST_HANDLE_PARAM(const_void) buf, unsigned long len)
{
int ret;
struct ucode_buf *buffer;
if ( len != (uint32_t)len )
return -E2BIG;
if ( microcode_ops == NULL )
return -EINVAL;
buffer = xmalloc_flex_struct(struct ucode_buf, buffer, len);
if ( !buffer )
return -ENOMEM;
ret = copy_from_guest(buffer->buffer, buf, len);
if ( ret )
{
xfree(buffer);
return -EFAULT;
}
buffer->len = len;
return continue_hypercall_on_cpu(smp_processor_id(),
microcode_update_helper, buffer);
}
static int __init microcode_init(void)
{
/*
+4
View File
@@ -177,6 +177,10 @@ static int rcu_barrier_action(void *_cpu_count)
return 0;
}
/*
* As rcu_barrier() is using stop_machine_run() it is allowed to be used in
* idle context only (see comment for stop_machine_run()).
*/
int rcu_barrier(void)
{
atomic_t cpu_count = ATOMIC_INIT(0);
+7
View File
@@ -67,6 +67,12 @@ static void stopmachine_wait_state(void)
cpu_relax();
}
/*
* Sync all processors and call a function on one or all of them.
* As stop_machine_run() is using a tasklet for syncing the processors it is
* mandatory to be called only on an idle vcpu, as otherwise active core
* scheduling might hang.
*/
int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu)
{
unsigned int i, nr_cpus;
@@ -74,6 +80,7 @@ int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu)
int ret;
BUG_ON(!local_irq_is_enabled());
BUG_ON(!is_idle_vcpu(current));
/* cpu_online_map must not change. */
if ( !get_cpu_maps() )
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.