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

Ghimiray, Himal Prasad
2026-04-06T05:28:30Z

On 03-04-2026 03:16, Matthew Brost wrote:
> 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.
>

Will add kernel doc in next rev.

>> +
>> +/** 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

Makes sense, will move it to guc_layer, probably xe_access_counter_type
can also be moved to guc layer.

>
>> +
>> +/**
>> + * 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?
Yes we can drop xe3 prefix, but we need differentiatior since interface
will be HW dependent.

>
> 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.
Will explore this.
>
>> + } 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.

The ACK is not available rightnow, I left it for future usecases, will
remove it.

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