Re: [RFC 03/15] drm/xe: Stub out new access_counter layer

Matthew Brost
2026-04-02T21:46:16Z

On Wed, Mar 18, 2026 at 01:14:44PM +0530, Himal Prasad Ghimiray wrote:
> Add access counter infrastructure with type definitions, header files,
> and stub implementation. This follows a two-layer producer-consumer
> architecture similar to the pagefault layer.
>
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
> ---
> drivers/gpu/drm/xe/Makefile | 3 +-
> drivers/gpu/drm/xe/xe_access_counter.c | 55 +++++++++
> drivers/gpu/drm/xe/xe_access_counter.h | 17 +++
> drivers/gpu/drm/xe/xe_access_counter_types.h | 123 +++++++++++++++++++
> 4 files changed, 197 insertions(+), 1 deletion(-)
> create mode 100644 drivers/gpu/drm/xe/xe_access_counter.c
> create mode 100644 drivers/gpu/drm/xe/xe_access_counter.h
> create mode 100644 drivers/gpu/drm/xe/xe_access_counter_types.h
>
> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
> index 3a3f9f22d42a..92d8d6e4a447 100644
> --- a/drivers/gpu/drm/xe/Makefile
> +++ b/drivers/gpu/drm/xe/Makefile
> @@ -32,7 +32,8 @@ $(obj)/generated/%_device_wa_oob.c $(obj)/generated/%_device_wa_oob.h: $(obj)/xe
>
> # core driver code
>
> -xe-y += xe_bb.o \
> +xe-y += xe_access_counter.o \
> + xe_bb.o \
> xe_bo.o \
> xe_bo_evict.o \
> xe_dep_scheduler.o \
> diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
> new file mode 100644
> index 000000000000..f3a8a93b5135
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_access_counter.c
> @@ -0,0 +1,55 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2026 Intel Corporation
> + */
> +
> +#include <linux/circ_buf.h>
> +
> +#include <drm/drm_exec.h>
> +#include <drm/drm_managed.h>
> +
> +#include "xe_access_counter.h"
> +#include "xe_access_counter_types.h"
> +#include "xe_device.h"
> +
> +/**
> + * DOC: Xe access counters
> + *
> + * Xe access counters are handled in two layers with one-way communication.
> + * The producer layer interacts with hardware or firmware to receive and parse
> + * access counter notifications into struct xe_access_counter, then forwards them
> + * to the consumer. The consumer layer services the notifications (e.g., memory
> + * migration hints, binding decisions). No acknowledgment is sent back to the
> + * producer. The consumer uses an access counter queue sized to absorb all potential
> + * notifications and a multi-threaded worker to process them. Multiple producers
> + * are supported, with a single shared consumer.
> + *
> + * xe_access_counter.c implements the consumer layer.
> + */
> +
> +/**
> + * xe_access_counter_init - Initialize access counter consumer layer
> + * @xe: xe device
> + *
> + * Return: 0 on success, negative error code on error
> + */
> +int xe_access_counter_init(struct xe_device *xe)
> +{
> + /* Stub implementation - to be filled in */
> + return 0;
> +}
> +
> +/**
> + * xe_access_counter_handler - Handle an access counter notification
> + * @xe: xe device
> + * @ac: access counter notification
> + *
> + * Process an access counter notification from the producer layer.
> + *
> + * Return: 0 on success, negative error code on error
> + */
> +int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac)
> +{
> + /* Stub implementation - to be filled in */
> + return 0;
> +}
> diff --git a/drivers/gpu/drm/xe/xe_access_counter.h b/drivers/gpu/drm/xe/xe_access_counter.h
> new file mode 100644
> index 000000000000..b3a331687f13
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_access_counter.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2026 Intel Corporation
> + */
> +
> +#ifndef _XE_ACCESS_COUNTER_H_
> +#define _XE_ACCESS_COUNTER_H_
> +
> +struct xe_device;
> +struct xe_gt;
> +struct xe_access_counter;
> +
> +int xe_access_counter_init(struct xe_device *xe);
> +
> +int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac);
> +
> +#endif
> diff --git a/drivers/gpu/drm/xe/xe_access_counter_types.h b/drivers/gpu/drm/xe/xe_access_counter_types.h
> new file mode 100644
> index 000000000000..74b903b9461b
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_access_counter_types.h
> @@ -0,0 +1,123 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2026 Intel Corporation
> + */
> +
> +#ifndef _XE_ACCESS_COUNTER_TYPES_H_
> +#define _XE_ACCESS_COUNTER_TYPES_H_
> +
> +#include <linux/sizes.h>
> +#include <linux/types.h>
> +
> +struct xe_gt;
> +struct xe_access_counter;
> +
> +/** enum xe_access_counter_type - Xe access counter type */
> +enum xe_access_counter_type {
> + /** @XE_ACCESS_COUNTER_TYPE_TRIGGER */
> + XE_ACCESS_COUNTER_TYPE_TRIGGER = 0,
> + /** @XE_ACCESS_COUNTER_TYPE_NOTIFY*/
> + XE_ACCESS_COUNTER_TYPE_NOTIFY = 1,
> +};

What’s the difference between the types? Patch 8 handles both of them in
exactly the same way, which may be correct or fine, but without kernel
documentation explaining the distinction between the types here, it’s
hard to reason about it.

> +
> +/** enum xe_access_counter_granularity - Xe access counter granularity */
> +enum xe_access_counter_granularity {
> + /** @XE_ACCESS_COUNTER_GRANULARITY_128K: 128K granularity */
> + XE_ACCESS_COUNTER_GRANULARITY_128K = 0,
> + /** @XE_ACCESS_COUNTER_GRANULARITY_2M: 2M granularity */
> + XE_ACCESS_COUNTER_GRANULARITY_2M = 1,
> + /** @XE_ACCESS_COUNTER_GRANULARITY_16M: 16M granularity */
> + XE_ACCESS_COUNTER_GRANULARITY_16M = 2,
> + /** @XE_ACCESS_COUNTER_GRANULARITY_64M: 64M granularity */
> + XE_ACCESS_COUNTER_GRANULARITY_64M = 3,
> +};
> +
> +static inline int xe_access_counter_granularity_in_byte(int val)
> +{
> + switch (val) {
> + case XE_ACCESS_COUNTER_GRANULARITY_128K:
> + return SZ_128K;
> + case XE_ACCESS_COUNTER_GRANULARITY_2M:
> + return SZ_2M;
> + case XE_ACCESS_COUNTER_GRANULARITY_16M:
> + return SZ_16M;
> + case XE_ACCESS_COUNTER_GRANULARITY_64M:
> + return SZ_64M;
> + default:
> + return 0;
> + }
> +}

As discussed in [1], I think the granularity handling can be moved into
the GuC layer, so there’s no need to include public definitions here.

[1] https://patchwork.freedesktop.org/patch/712600/?series=163429&rev=1#comment_1318525

> +
> +/**
> + * struct xe_access_counter - Xe access counter
> + *
> + * Generic access counter structure for communication between producer and consumer.
> + * Carefully sized to be 64 bytes. Upon a device access counter notification, the
> + * producer populates this structure, and the consumer copies it into the access
> + * counter queue for deferred handling.
> + */
> +struct xe_access_counter {
> + /**
> + * @gt: GT of access counter
> + */
> + struct xe_gt *gt;
> + /**
> + * @consumer: State for the software handling the access counter.
> + * Populated by the producer and may be modified by the consumer to
> + * communicate information back to the producer upon acknowledgment.
> + */
> + struct {
> + /** @consumer.va_range_base: base virtual address of range */
> + u64 va_range_base;
> + /** @consumer.sub_granularity: sub-granularity */
> + u32 sub_granularity;
> + /**
> + * @consumer.counter_type: counter type, u8 rather than enum to
> + * keep size compact
> + */
> + u8 counter_type;
> + /**
> + * @consumer.granularity: access granularity, u8 rather than enum
> + * to keep size compact
> + */
> + u8 granularity;
> + /** @consumer.reserved: reserved bits for alignment */
> + u8 reserved[2];
> + /** @consumer: Platform-specific fields */
> + union {
> + /** @consumer.xe3: Xe3-specific fields */
> + struct {
> + /** @consumer.xe3.asid: address space ID */
> + u32 asid;
> + /** @consumer.xe3.engine_class: engine class */
> + u8 engine_class;
> + /** @consumer.xe3.engine_instance: engine instance */
> + u8 engine_instance;
> + /** @consumer.xe3.vfid: VFID */
> + u8 vfid;
> + /** @consumer.xe3.reserved: reserved bits */
> + u8 reserved;
> + } xe3;
> + };

Can we drop the xe3 prefix here to keep this interface generic across
platforms?

Also, a question: xe3 implies these are only valid on xe3+, so do access
counters not work on prior platforms? For example, without a valid ASID,
the common layer can’t find a VMA.

> + } consumer;
> + /**
> + * @producer: State for the producer (i.e., HW/FW interface). Populated
> + * by the producer and should not be modified—or even inspected—by the
> + * consumer, except for calling operations.
> + */
> + struct {
> + /** @producer.private: private pointer */
> + void *private;
> +#define XE_ACCESS_COUNTER_PRODUCER_MSG_LEN_DW 4
> + /**
> + * @producer.msg: access counter message, used by producer in
> + * acknowledgment to formulate response to HW/FW interface.
> + * Included in the access counter message because the producer
> + * typically receives the notification in a context where memory
> + * cannot be allocated (e.g., atomic context or the reclaim path).
> + */
> + u32 msg[XE_ACCESS_COUNTER_PRODUCER_MSG_LEN_DW];
> + } producer;

As discussed in [1], unless we have plans to ACK access counters in the
near future, I would just drop this.

Matt

> +};
> +
> +#endif
> --
> 2.34.1
>