|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v2] xen: rework error handling in vcpu_create
On 01/08/2025 9:24 pm, Stewart Hildebrand wrote:
> In vcpu_create after scheduler data is allocated, if
> vmtrace_alloc_buffer fails, it will jump to the wrong cleanup label
> resulting in a memory leak.
>
> Move sched_destroy_vcpu and destroy_waitqueue_vcpu to vcpu_teardown.
> Make vcpu_teardown idempotent: deal with NULL unit.
>
> Fix vcpu_runstate_get (called during XEN_SYSCTL_getdomaininfolist post
> vcpu_teardown) when v->sched_unit is NULL.
This is unfortunate. It feels wrong to be updating stats on a domain
that's in the process of being destroyed, especially as a side effect of
a get call.
But, I wonder if we've uncovered an object lifecycle problem here.
Previously any vCPU which was addressable in the system (i.e. domid was
addressable, a d->vcpu[x] was not NULL) had a unit.
>
> Fixes: 217dd79ee292 ("xen/domain: Add vmtrace_size domain creation parameter")
> Signed-off-by: Stewart Hildebrand <stewart.hildebrand@xxxxxxx>
> ---
> v1->v2:
> * move cleanup functions to vcpu_teardown
> * renamed, was ("xen: fix memory leak on error in vcpu_create")
> ---
> xen/common/domain.c | 14 ++++++--------
> xen/common/sched/core.c | 5 ++++-
> 2 files changed, 10 insertions(+), 9 deletions(-)
>
> diff --git a/xen/common/domain.c b/xen/common/domain.c
> index 5241a1629eeb..9c65c2974ea3 100644
> --- a/xen/common/domain.c
> +++ b/xen/common/domain.c
> @@ -388,6 +388,8 @@ static int vmtrace_alloc_buffer(struct vcpu *v)
> static int vcpu_teardown(struct vcpu *v)
> {
> vmtrace_free_buffer(v);
> + sched_destroy_vcpu(v);
> + destroy_waitqueue_vcpu(v);
>
> return 0;
> }
Along with this, you want a matching:
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 5241a1629eeb..3fd75a6d6784 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -1412,8 +1412,6 @@ static void cf_check complete_domain_destroy(struct
rcu_head *head)
continue;
tasklet_kill(&v->continue_hypercall_tasklet);
arch_vcpu_destroy(v);
- sched_destroy_vcpu(v);
- destroy_waitqueue_vcpu(v);
}
grant_table_destroy(d);
> @@ -448,13 +450,13 @@ struct vcpu *vcpu_create(struct domain *d, unsigned int
> vcpu_id)
> }
>
> if ( sched_init_vcpu(v) != 0 )
> - goto fail_wq;
> + goto fail;
>
> if ( vmtrace_alloc_buffer(v) != 0 )
> - goto fail_wq;
> + goto fail;
>
> if ( arch_vcpu_create(v) != 0 )
> - goto fail_sched;
> + goto fail;
>
> d->vcpu[vcpu_id] = v;
> if ( vcpu_id != 0 )
> @@ -472,11 +474,7 @@ struct vcpu *vcpu_create(struct domain *d, unsigned int
> vcpu_id)
>
> return v;
>
> - fail_sched:
> - sched_destroy_vcpu(v);
> - fail_wq:
> - destroy_waitqueue_vcpu(v);
> -
> + fail:
> /* Must not hit a continuation in this context. */
> if ( vcpu_teardown(v) )
> ASSERT_UNREACHABLE();
> diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c
> index 2ab4313517c3..fb7c99b05360 100644
> --- a/xen/common/sched/core.c
> +++ b/xen/common/sched/core.c
> @@ -321,7 +321,7 @@ void vcpu_runstate_get(const struct vcpu *v,
> */
> unit = is_idle_vcpu(v) ? get_sched_res(v->processor)->sched_unit_idle
> : v->sched_unit;
> - lock = likely(v == current) ? NULL : unit_schedule_lock_irq(unit);
> + lock = likely(v == current || !unit) ? NULL :
> unit_schedule_lock_irq(unit);
This is obfuscation for obfuscations sake. The normal way of writing it
would be:
if ( v != current && unit )
lock = unit_schedule_lock_irq(unit);
and that is precisely what the compiler emits.
Moreover it also matches the pattern for how the lock is released, later
in the function.
~Andrew
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |