[PATCH kvmtool v2 7/7] arm64: Improve KVM_ARM_VCPU_PMU_V3_CTRL diagnostics

Alexandru Elisei alexandru.elisei at arm.com
Mon Jun 22 02:04:32 PDT 2026


Hi Oliver,

On Thu, Jun 18, 2026 at 12:06:12PM -0700, Oliver Upton wrote:
> On Thu, Jun 18, 2026 at 04:50:01PM +0100, Alexandru Elisei wrote:
> > kvmtool issues several ioctls when configuring the PMU, and each of them
> > can fail for different reasons. Be more specific about the ioctl that
> > failed when that happens.
> > 
> > Signed-off-by: Alexandru Elisei <alexandru.elisei at arm.com>
> > ---
> >  arm64/pmu.c | 31 ++++++++++++++++++++++++-------
> >  1 file changed, 24 insertions(+), 7 deletions(-)
> > 
> > diff --git a/arm64/pmu.c b/arm64/pmu.c
> > index 92cacd62479e..78c15f153fad 100644
> > --- a/arm64/pmu.c
> > +++ b/arm64/pmu.c
> > @@ -12,6 +12,24 @@
> >  
> >  #include "asm/pmu.h"
> >  
> > +static const char *pmu_attr_names[] = {
> > +	[KVM_ARM_VCPU_PMU_V3_IRQ]	= "KVM_ARM_VCPU_PMU_V3_IRQ",
> > +	[KVM_ARM_VCPU_PMU_V3_INIT]	= "KVM_ARM_VCPU_PMU_V3_INIT",
> > +	[KVM_ARM_VCPU_PMU_V3_FILTER]	= "KVM_ARM_VCPU_PMU_V3_FILTER",
> > +	[KVM_ARM_VCPU_PMU_V3_SET_PMU]	= "KVM_ARM_VCPU_PMU_V3_SET_PMU",
> > +	[KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS] = "KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTER",
> > +};
> > +
> > +static const char *pmu_get_attr_name(u64 attr)
> > +{
> > +	switch (attr) {
> > +	case KVM_ARM_VCPU_PMU_V3_IRQ ... KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS:
> > +		return pmu_attr_names[attr];
> > +	default:
> > +		return "UNKNOWN";
> > +	}
> > +}
> > +
> >  static bool pmu_has_attr(struct kvm_cpu *vcpu, u64 attr)
> >  {
> >  	struct kvm_device_attr pmu_attr = {
> > @@ -32,13 +50,12 @@ static void set_pmu_attr(struct kvm_cpu *vcpu, void *addr, u64 attr)
> >  	};
> >  	int ret;
> >  
> > -	if (pmu_has_attr(vcpu, attr)) {
> > -		ret = ioctl(vcpu->vcpu_fd, KVM_SET_DEVICE_ATTR, &pmu_attr);
> > -		if (ret)
> > -			die_perror("PMU KVM_SET_DEVICE_ATTR");
> > -	} else {
> > -		die_perror("PMU KVM_HAS_DEVICE_ATTR");
> > -	}
> > +	if (!pmu_has_attr(vcpu, attr))
> > +		die_perror("KVM_HAS_DEVICE_ATTR(%s)", pmu_get_attr_name(attr));
> > +
> > +	ret = ioctl(vcpu->vcpu_fd, KVM_SET_DEVICE_ATTR, &pmu_attr);
> > +	if (ret)
> > +		die_perror("KVM_SET_DEVICE_ATTR(%s)", pmu_get_attr_name(attr));
> >  }
> 
> The whole if (ret) die_perror(...) thing is a bit repetetive IMO. A
> treewide cleanup replacing this with macros would be nice, then you could
> stringize the ioctl under the hood.

Thank you for having a look, that's a great idea, it will avoid out of bounds
array access if KVM gets a new PMU ioctl and the name array is not updated at
the same time - that's quite possible since ioctl numbers are pulled by running
update_headers.sh, and the dependency is not obvious.

I think the compilation errors are a bit higher priority than this, and a
treewide change would more involved, possibly involving a change in behaviour
(the gic seems to propagate the error instead of calling die_perror(), for
example), would you mind if for this series I'll only introduce the macro for
the pmu and convert the rest of the code in a separate series?

Thanks,
Alex

> 
> diff --git a/arm64/pmu.c b/arm64/pmu.c
> index 5f31d6b..0d9f3df 100644
> --- a/arm64/pmu.c
> +++ b/arm64/pmu.c
> @@ -23,23 +23,19 @@ static bool pmu_has_attr(struct kvm_cpu *vcpu, u64 attr)
>  	return ret == 0;
>  }
>  
> -static void set_pmu_attr(struct kvm_cpu *vcpu, void *addr, u64 attr)
> -{
> -	struct kvm_device_attr pmu_attr = {
> -		.group	= KVM_ARM_VCPU_PMU_V3_CTRL,
> -		.addr	= (u64)addr,
> -		.attr	= attr,
> -	};
> -	int ret;
> -
> -	if (pmu_has_attr(vcpu, attr)) {
> -		ret = ioctl(vcpu->vcpu_fd, KVM_SET_DEVICE_ATTR, &pmu_attr);
> -		if (ret)
> -			die_perror("PMU KVM_SET_DEVICE_ATTR");
> -	} else {
> -		die_perror("PMU KVM_HAS_DEVICE_ATTR");
> -	}
> -}
> +#define kvm_set_device_attr(fd, _group, _attr, _addr)					\
> +do {											\
> +	struct kvm_device_attr __attr = {						\
> +		.group	= (_group),							\
> +		.attr	= (_attr),							\
> +		.addr	= (u64)(_addr),							\
> +	};										\
> +	int r;										\
> +											\
> +	r = ioctl((fd), KVM_SET_DEVICE_ATTR, &__attr);					\
> +	if (r)										\
> +		die_perror("KVM_SET_DEVICE_ATTR(group:"#_group", attr:"#_attr")");	\
> +} while (0)
>  
>  #define SYS_EVENT_SOURCE	"/sys/bus/event_source/devices/"
>  /*
> @@ -218,14 +214,18 @@ void pmu__generate_fdt_nodes(void *fdt, struct kvm *kvm)
>  
>  	for (i = 0; i < kvm->nrcpus; i++) {
>  		vcpu = kvm->cpus[i];
> -		set_pmu_attr(vcpu, &irq, KVM_ARM_VCPU_PMU_V3_IRQ);
> +		kvm_set_device_attr(vcpu->vcpu_fd, KVM_ARM_VCPU_PMU_V3_CTRL,
> +				    KVM_ARM_VCPU_PMU_V3_IRQ, &irq);
>  		/*
>  		 * PMU IDs 0-5 are reserved; a positive value means a PMU was
>  		 * found.
>  		 */
>  		if (pmu_id > 0)
> -			set_pmu_attr(vcpu, &pmu_id, KVM_ARM_VCPU_PMU_V3_SET_PMU);
> -		set_pmu_attr(vcpu, NULL, KVM_ARM_VCPU_PMU_V3_INIT);
> +			kvm_set_device_attr(vcpu->vcpu_fd, KVM_ARM_VCPU_PMU_V3_CTRL,
> +					    KVM_ARM_VCPU_PMU_V3_SET_PMU, &pmu_id);
> +
> +		kvm_set_device_attr(vcpu->vcpu_fd, KVM_ARM_VCPU_PMU_V3_CTRL,
> +				    KVM_ARM_VCPU_PMU_V3_INIT, NULL);
>  	}
>  
>  	_FDT(fdt_begin_node(fdt, "pmu"));



More information about the linux-arm-kernel mailing list