[PATCH v1] KVM: arm64: vgic-its: Add debugfs interface to expose ITS tables

Marc Zyngier maz at kernel.org
Wed Feb 19 13:51:30 PST 2025


On Fri, 14 Feb 2025 02:02:58 +0000,
Jing Zhang <jingzhangos at google.com> wrote:
> 
> This commit introduces a debugfs interface to display the contents of the
> VGIC Interrupt Translation Service (ITS) tables.
> 
> The ITS tables map Device/Event IDs to Interrupt IDs and target processors.
> Exposing this information through debugfs allows for easier inspection and
> debugging of the interrupt routing configuration.
> 
> The debugfs interface presents the ITS table data in a tabular format:
> 
>   Device ID: 0, Event ID Range: [0 - 65535]
>   EVENT_ID    INTID  HWINTID   TARGET   COL_ID HW
>   -----------------------------------------------
>          0       32        0        0        0  0
>          1       33        0        0        0  0
>          2       34        0        0        0  0

Is this an actual output? INTID 32 as the LPI number is pretty creative.

>        ...      ...      ...      ...      ... ..
> 
> The output is generated using the seq_file interface, allowing for efficient
> handling of potentially large ITS tables.
> 
> This interface is read-only and does not allow modification of the ITS
> tables. It is intended for debugging and informational purposes only.
> 
> Signed-off-by: Jing Zhang <jingzhangos at google.com>
> ---
> rfc -> v1:
>  - Rebase on to v6.14-rc2.
>  - Retained only the debugfs interface patch from the original RFC [1].
> 
> [1] https://lore.kernel.org/all/20250113193128.1533449-1-jingzhangos@google.com
> ---
>  arch/arm64/kvm/Makefile              |   2 +-
>  arch/arm64/kvm/vgic/vgic-its-debug.c | 234 +++++++++++++++++++++++++++
>  arch/arm64/kvm/vgic/vgic-its.c       |  39 +----
>  arch/arm64/kvm/vgic/vgic.h           |  33 ++++
>  4 files changed, 276 insertions(+), 32 deletions(-)
>  create mode 100644 arch/arm64/kvm/vgic/vgic-its-debug.c
> 
> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> index 3cf7adb2b5038..b1e7b9c29589c 100644
> --- a/arch/arm64/kvm/Makefile
> +++ b/arch/arm64/kvm/Makefile
> @@ -23,7 +23,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
>  	 vgic/vgic-v3.o vgic/vgic-v4.o \
>  	 vgic/vgic-mmio.o vgic/vgic-mmio-v2.o \
>  	 vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \
> -	 vgic/vgic-its.o vgic/vgic-debug.o
> +	 vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-its-debug.o

I'd rather you keep the vgic debugging stuff in vgic-debug.c. This is
positively tiny, and we don't need to spread the debugging around.

>  
>  kvm-$(CONFIG_HW_PERF_EVENTS)  += pmu-emul.o pmu.o
>  kvm-$(CONFIG_ARM64_PTR_AUTH)  += pauth.o
> diff --git a/arch/arm64/kvm/vgic/vgic-its-debug.c b/arch/arm64/kvm/vgic/vgic-its-debug.c
> new file mode 100644
> index 0000000000000..1ff0dd0137939
> --- /dev/null
> +++ b/arch/arm64/kvm/vgic/vgic-its-debug.c
> @@ -0,0 +1,234 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * vgic-its-debug.c - Debugfs interface for exposing VGIC ITS tables.
> + *
> + * Copyright (C) 2025 Google
> + *
> + * This file provides a debugfs interface to display the contents of the
> + * VGIC Interrupt Translation Service (ITS) tables. This allows for
> + * inspection of the mapping between Event IDs, Interrupt IDs, and target
> + * processors. The information is presented in a tabular format through a
> + * seq_file interface.
> + */
> +
> +#include <linux/debugfs.h>
> +#include <linux/kvm_host.h>
> +#include <linux/seq_file.h>
> +#include <kvm/arm_vgic.h>
> +#include "vgic.h"
> +
> +/**
> + * struct vgic_its_iter - Iterator for traversing VGIC ITS tables.
> + * @dev: Pointer to the current its_device being processed.
> + * @ite: Pointer to the current its_ite within the device being processed.
> + *
> + * This structure is used to maintain the current position during iteration
> + * over the ITS tables. It holds pointers to both the current device and the
> + * current ITE within that device.

Which tables? You only seem to consider the device table. If that's
all you need to consider, then call it out in the comment.

> + */
> +struct vgic_its_iter {
> +	struct its_device *dev;
> +	struct its_ite *ite;
> +};
> +
> +/**
> + * end_of_iter - Checks if the iterator has reached the end of the ITS tables.
> + * @iter: The iterator to check.
> + *
> + * Return: True if the iterator is at the end, false otherwise.

The end of what?

> + */
> +static inline bool end_of_iter(struct vgic_its_iter *iter)
> +{
> +	return !iter->dev && !iter->ite;
> +}
> +
> +/**
> + * iter_next - Advances the iterator to the next entry in the ITS tables.
> + * @its: The VGIC ITS structure.
> + * @iter: The iterator to advance.
> + *
> + * This function moves the iterator to the next ITE within the current device,
> + * or to the first ITE of the next device if the current ITE is the last in
> + * the device. If the current device is the last device, the iterator is set
> + * to indicate the end of iteration.
> + */
> +static void iter_next(struct vgic_its *its, struct vgic_its_iter *iter)
> +{
> +	struct its_device *dev = iter->dev;
> +	struct its_ite *ite = iter->ite;
> +
> +	if (!ite || list_is_last(&ite->ite_list, &dev->itt_head)) {
> +		if (list_is_last(&dev->dev_list, &its->device_list)) {
> +			dev = NULL;
> +			ite = NULL;
> +		} else {
> +			dev = list_next_entry(dev, dev_list);
> +			ite = list_first_entry_or_null(&dev->itt_head,
> +						       struct its_ite,
> +						       ite_list);
> +		}
> +	} else {
> +		ite = list_next_entry(ite, ite_list);
> +	}
> +
> +	iter->dev = dev;
> +	iter->ite = ite;
> +}
> +
> +/**
> + * vgic_its_debug_start - Start function for the seq_file interface.
> + * @s: The seq_file structure.
> + * @pos: The starting position (offset).
> + *
> + * This function initializes the iterator to the beginning of the ITS tables
> + * and advances it to the specified position. It acquires the its_lock mutex
> + * to protect shared data.
> + *
> + * Return: An iterator pointer on success, NULL if no devices are found or
> + *         the end of the list is reached, or ERR_PTR(-ENOMEM) on memory
> + *         allocation failure.
> + */
> +static void *vgic_its_debug_start(struct seq_file *s, loff_t *pos)
> +{
> +	struct vgic_its *its = s->private;
> +	struct vgic_its_iter *iter;
> +	struct its_device *dev;
> +	loff_t offset = *pos;
> +
> +	mutex_lock(&its->its_lock);
> +
> +	dev = list_first_entry_or_null(&its->device_list,
> +				       struct its_device, dev_list);
> +	if (!dev)
> +		return NULL;
> +
> +	iter = kmalloc(sizeof(*iter), GFP_KERNEL);
> +	if (!iter)
> +		return ERR_PTR(-ENOMEM);
> +
> +	iter->dev = dev;
> +	iter->ite = list_first_entry_or_null(&dev->itt_head,
> +					     struct its_ite, ite_list);
> +
> +	while (!end_of_iter(iter) && offset--)
> +		iter_next(its, iter);
> +
> +	if (end_of_iter(iter)) {
> +		kfree(iter);
> +		return NULL;
> +	}
> +
> +	return iter;
> +}
> +
> +/**
> + * vgic_its_debug_next - Next function for the seq_file interface.
> + * @s: The seq_file structure.
> + * @v: The current iterator.
> + * @pos: The current position (offset).
> + *
> + * This function advances the iterator to the next entry and increments the
> + * position.
> + *
> + * Return: An iterator pointer on success, or NULL if the end of the list is
> + *         reached.
> + */
> +static void *vgic_its_debug_next(struct seq_file *s, void *v, loff_t *pos)
> +{
> +	struct vgic_its *its = s->private;
> +	struct vgic_its_iter *iter = v;
> +
> +	++*pos;
> +	iter_next(its, iter);
> +
> +	if (end_of_iter(iter)) {
> +		kfree(iter);
> +		return NULL;
> +	}
> +	return iter;
> +}
> +
> +/**
> + * vgic_its_debug_stop - Stop function for the seq_file interface.
> + * @s: The seq_file structure.
> + * @v: The current iterator.
> + *
> + * This function frees the iterator and releases the its_lock mutex.
> + */
> +static void vgic_its_debug_stop(struct seq_file *s, void *v)
> +{
> +	struct vgic_its *its = s->private;
> +	struct vgic_its_iter *iter = v;
> +
> +	if (!IS_ERR_OR_NULL(iter))
> +		kfree(iter);
> +	mutex_unlock(&its->its_lock);
> +}
> +
> +/**
> + * vgic_its_debug_show - Show function for the seq_file interface.
> + * @s: The seq_file structure.
> + * @v: The current iterator.
> + *
> + * This function formats and prints the ITS table entry information to the
> + * seq_file output.
> + *
> + * Return: 0 on success.
> + */
> +static int vgic_its_debug_show(struct seq_file *s, void *v)
> +{
> +	struct vgic_its_iter *iter = v;
> +	struct its_device *dev = iter->dev;
> +	struct its_ite *ite = iter->ite;
> +
> +	if (list_is_first(&ite->ite_list, &dev->itt_head)) {
> +		seq_printf(s, "\n");
> +		seq_printf(s, "Device ID: %u, Event ID Range: [0 - %llu]\n",
> +			   dev->device_id, BIT_ULL(dev->num_eventid_bits) - 1);

nit: DevIDs are usually expressed in hex, because they tend to encode
the bus topology.

> +		seq_printf(s, "EVENT_ID    INTID  HWINTID   TARGET   COL_ID HW\n");
> +		seq_printf(s, "-----------------------------------------------\n");
> +	}
> +
> +	if (ite && ite->irq && ite->collection) {
> +		seq_printf(s, "%8u %8u %8u %8u %8u %2d\n",
> +			   ite->event_id, ite->irq->intid, ite->irq->hwintid,
> +			   ite->collection->target_addr,
> +			   ite->collection->collection_id, ite->irq->hw);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct seq_operations vgic_its_debug_sops = {
> +	.start = vgic_its_debug_start,
> +	.next  = vgic_its_debug_next,
> +	.stop  = vgic_its_debug_stop,
> +	.show  = vgic_its_debug_show
> +};
> +
> +DEFINE_SEQ_ATTRIBUTE(vgic_its_debug);
> +
> +/**
> + * vgic_its_debug_init - Initializes the debugfs interface for VGIC ITS.
> + * @dev: The KVM device structure.
> + *
> + * This function creates a debugfs file named "vgic-its-state@%its_base"
> + * to expose the ITS table information.
> + *
> + * Return: 0 on success.
> + */
> +int vgic_its_debug_init(struct kvm_device *dev)
> +{
> +	struct vgic_its *its = dev->private;
> +	char name[32];
> +
> +	snprintf(name, sizeof(name), "vgic-its-state@%llx", (u64)its->vgic_its_base);

This is very fragile. How about using kasprintf() instead, even if it
is a short lived allocation?

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.



More information about the linux-arm-kernel mailing list