root/drivers/gpu/drm/xe/xe_eu_stall.c
// SPDX-License-Identifier: MIT
/*
 * Copyright © 2025 Intel Corporation
 */

#include <linux/anon_inodes.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/types.h>

#include <drm/drm_drv.h>
#include <generated/xe_wa_oob.h>
#include <uapi/drm/xe_drm.h>

#include "xe_bo.h"
#include "xe_device.h"
#include "xe_eu_stall.h"
#include "xe_force_wake.h"
#include "xe_gt_mcr.h"
#include "xe_gt_printk.h"
#include "xe_gt_topology.h"
#include "xe_macros.h"
#include "xe_observation.h"
#include "xe_pm.h"
#include "xe_trace.h"
#include "xe_wa.h"

#include "regs/xe_eu_stall_regs.h"
#include "regs/xe_gt_regs.h"

#define POLL_PERIOD_MS  5

static size_t per_xecore_buf_size = SZ_512K;

struct per_xecore_buf {
        /* Buffer vaddr */
        u8 *vaddr;
        /* Write pointer */
        u32 write;
        /* Read pointer */
        u32 read;
};

struct xe_eu_stall_data_stream {
        bool pollin;
        bool enabled;
        int wait_num_reports;
        int sampling_rate_mult;
        wait_queue_head_t poll_wq;
        size_t data_record_size;
        size_t per_xecore_buf_size;
        unsigned int fw_ref;

        struct xe_gt *gt;
        struct xe_bo *bo;
        /* Lock to protect data buffer pointers */
        struct mutex xecore_buf_lock;
        struct per_xecore_buf *xecore_buf;
        struct {
                bool reported_to_user;
                xe_dss_mask_t mask;
        } data_drop;
        struct delayed_work buf_poll_work;
};

struct xe_eu_stall_gt {
        /* Lock to protect stream */
        struct mutex stream_lock;
        /* EU stall data stream */
        struct xe_eu_stall_data_stream *stream;
        /* Workqueue to schedule buffer pointers polling work */
        struct workqueue_struct *buf_ptr_poll_wq;
};

/**
 * struct eu_stall_open_properties - EU stall sampling properties received
 *                                   from user space at open.
 * @sampling_rate_mult: EU stall sampling rate multiplier.
 *                      HW will sample every (sampling_rate_mult x 251) cycles.
 * @wait_num_reports: Minimum number of EU stall data reports to unblock poll().
 * @gt: GT on which EU stall data will be captured.
 */
struct eu_stall_open_properties {
        int sampling_rate_mult;
        int wait_num_reports;
        struct xe_gt *gt;
};

/*
 * EU stall data format for PVC
 */
struct xe_eu_stall_data_pvc {
        __u64 ip_addr:29;         /* Bits 0  to 28  */
        __u64 active_count:8;     /* Bits 29 to 36  */
        __u64 other_count:8;      /* Bits 37 to 44  */
        __u64 control_count:8;    /* Bits 45 to 52  */
        __u64 pipestall_count:8;  /* Bits 53 to 60  */
        __u64 send_count:8;       /* Bits 61 to 68  */
        __u64 dist_acc_count:8;   /* Bits 69 to 76  */
        __u64 sbid_count:8;       /* Bits 77 to 84  */
        __u64 sync_count:8;       /* Bits 85 to 92  */
        __u64 inst_fetch_count:8; /* Bits 93 to 100 */
        __u64 unused_bits:27;
        __u64 unused[6];
} __packed;

/*
 * EU stall data format for Xe2 arch GPUs (LNL, BMG).
 */
struct xe_eu_stall_data_xe2 {
        __u64 ip_addr:29;         /* Bits 0  to 28  */
        __u64 tdr_count:8;        /* Bits 29 to 36  */
        __u64 other_count:8;      /* Bits 37 to 44  */
        __u64 control_count:8;    /* Bits 45 to 52  */
        __u64 pipestall_count:8;  /* Bits 53 to 60  */
        __u64 send_count:8;       /* Bits 61 to 68  */
        __u64 dist_acc_count:8;   /* Bits 69 to 76  */
        __u64 sbid_count:8;       /* Bits 77 to 84  */
        __u64 sync_count:8;       /* Bits 85 to 92  */
        __u64 inst_fetch_count:8; /* Bits 93 to 100 */
        __u64 active_count:8;     /* Bits 101 to 108 */
        __u64 ex_id:3;            /* Bits 109 to 111 */
        __u64 end_flag:1;         /* Bit  112 */
        __u64 unused_bits:15;
        __u64 unused[6];
} __packed;

/*
 * EU stall data format for Xe3p arch GPUs.
 */
struct xe_eu_stall_data_xe3p {
        __u64 ip_addr:61;         /* Bits 0  to 60  */
        __u64 tdr_count:8;        /* Bits 61 to 68  */
        __u64 other_count:8;      /* Bits 69 to 76  */
        __u64 control_count:8;    /* Bits 77 to 84  */
        __u64 pipestall_count:8;  /* Bits 85 to 92  */
        __u64 send_count:8;       /* Bits 93 to 100 */
        __u64 dist_acc_count:8;   /* Bits 101 to 108 */
        __u64 sbid_count:8;       /* Bits 109 to 116 */
        __u64 sync_count:8;       /* Bits 117 to 124 */
        __u64 inst_fetch_count:8; /* Bits 125 to 132 */
        __u64 active_count:8;     /* Bits 133 to 140 */
        __u64 ex_id:3;            /* Bits 141 to 143 */
        __u64 end_flag:1;         /* Bit  144 */
        __u64 unused_bits:47;
        __u64 unused[5];
} __packed;

const u64 eu_stall_sampling_rates[] = {251, 251 * 2, 251 * 3, 251 * 4, 251 * 5, 251 * 6, 251 * 7};

/**
 * xe_eu_stall_get_sampling_rates - get EU stall sampling rates information.
 *
 * @num_rates: Pointer to a u32 to return the number of sampling rates.
 * @rates: double u64 pointer to point to an array of sampling rates.
 *
 * Stores the number of sampling rates and pointer to the array of
 * sampling rates in the input pointers.
 *
 * Returns: Size of the EU stall sampling rates array.
 */
size_t xe_eu_stall_get_sampling_rates(u32 *num_rates, const u64 **rates)
{
        *num_rates = ARRAY_SIZE(eu_stall_sampling_rates);
        *rates = eu_stall_sampling_rates;

        return sizeof(eu_stall_sampling_rates);
}

/**
 * xe_eu_stall_get_per_xecore_buf_size - get per XeCore buffer size.
 *
 * Returns: The per XeCore buffer size used to allocate the per GT
 *          EU stall data buffer.
 */
size_t xe_eu_stall_get_per_xecore_buf_size(void)
{
        return per_xecore_buf_size;
}

/**
 * xe_eu_stall_data_record_size - get EU stall data record size.
 *
 * @xe: Pointer to a Xe device.
 *
 * Returns: EU stall data record size.
 */
size_t xe_eu_stall_data_record_size(struct xe_device *xe)
{
        size_t record_size = 0;

        if (GRAPHICS_VER(xe) >= 35)
                record_size = sizeof(struct xe_eu_stall_data_xe3p);
        else if (GRAPHICS_VER(xe) >= 20)
                record_size = sizeof(struct xe_eu_stall_data_xe2);
        else if (xe->info.platform == XE_PVC)
                record_size = sizeof(struct xe_eu_stall_data_pvc);


        xe_assert(xe, is_power_of_2(record_size));

        return record_size;
}

/**
 * num_data_rows - Return the number of EU stall data rows of 64B each
 *                 for a given data size.
 *
 * @data_size: EU stall data size
 */
static u32 num_data_rows(u32 data_size)
{
        return data_size >> 6;
}

static void xe_eu_stall_fini(void *arg)
{
        struct xe_gt *gt = arg;

        destroy_workqueue(gt->eu_stall->buf_ptr_poll_wq);
        mutex_destroy(&gt->eu_stall->stream_lock);
        kfree(gt->eu_stall);
}

/**
 * xe_eu_stall_init() - Allocate and initialize GT level EU stall data
 *                      structure xe_eu_stall_gt within struct xe_gt.
 *
 * @gt: GT being initialized.
 *
 * Returns: zero on success or a negative error code.
 */
int xe_eu_stall_init(struct xe_gt *gt)
{
        struct xe_device *xe = gt_to_xe(gt);
        int ret;

        if (!xe_eu_stall_supported_on_platform(xe))
                return 0;

        gt->eu_stall = kzalloc_obj(*gt->eu_stall);
        if (!gt->eu_stall) {
                ret = -ENOMEM;
                goto exit;
        }

        mutex_init(&gt->eu_stall->stream_lock);

        gt->eu_stall->buf_ptr_poll_wq = alloc_ordered_workqueue("xe_eu_stall", 0);
        if (!gt->eu_stall->buf_ptr_poll_wq) {
                ret = -ENOMEM;
                goto exit_free;
        }

        return devm_add_action_or_reset(xe->drm.dev, xe_eu_stall_fini, gt);
exit_free:
        mutex_destroy(&gt->eu_stall->stream_lock);
        kfree(gt->eu_stall);
exit:
        return ret;
}

static int set_prop_eu_stall_sampling_rate(struct xe_device *xe, u64 value,
                                           struct eu_stall_open_properties *props)
{
        value = div_u64(value, 251);
        if (value == 0 || value > 7) {
                drm_dbg(&xe->drm, "Invalid EU stall sampling rate %llu\n", value);
                return -EINVAL;
        }
        props->sampling_rate_mult = value;
        return 0;
}

static int set_prop_eu_stall_wait_num_reports(struct xe_device *xe, u64 value,
                                              struct eu_stall_open_properties *props)
{
        props->wait_num_reports = value;

        return 0;
}

static int set_prop_eu_stall_gt_id(struct xe_device *xe, u64 value,
                                   struct eu_stall_open_properties *props)
{
        struct xe_gt *gt = xe_device_get_gt(xe, value);

        if (!gt) {
                drm_dbg(&xe->drm, "Invalid GT ID %llu for EU stall sampling\n", value);
                return -EINVAL;
        }
        props->gt = gt;
        return 0;
}

typedef int (*set_eu_stall_property_fn)(struct xe_device *xe, u64 value,
                                        struct eu_stall_open_properties *props);

static const set_eu_stall_property_fn xe_set_eu_stall_property_funcs[] = {
        [DRM_XE_EU_STALL_PROP_SAMPLE_RATE] = set_prop_eu_stall_sampling_rate,
        [DRM_XE_EU_STALL_PROP_WAIT_NUM_REPORTS] = set_prop_eu_stall_wait_num_reports,
        [DRM_XE_EU_STALL_PROP_GT_ID] = set_prop_eu_stall_gt_id,
};

static int xe_eu_stall_user_ext_set_property(struct xe_device *xe, u64 extension,
                                             struct eu_stall_open_properties *props)
{
        u64 __user *address = u64_to_user_ptr(extension);
        struct drm_xe_ext_set_property ext;
        int err;
        u32 idx;

        err = copy_from_user(&ext, address, sizeof(ext));
        if (XE_IOCTL_DBG(xe, err))
                return -EFAULT;

        if (XE_IOCTL_DBG(xe, ext.property >= ARRAY_SIZE(xe_set_eu_stall_property_funcs)) ||
            XE_IOCTL_DBG(xe, !ext.property) || XE_IOCTL_DBG(xe, ext.pad))
                return -EINVAL;

        idx = array_index_nospec(ext.property, ARRAY_SIZE(xe_set_eu_stall_property_funcs));
        return xe_set_eu_stall_property_funcs[idx](xe, ext.value, props);
}

typedef int (*xe_eu_stall_user_extension_fn)(struct xe_device *xe, u64 extension,
                                             struct eu_stall_open_properties *props);
static const xe_eu_stall_user_extension_fn xe_eu_stall_user_extension_funcs[] = {
        [DRM_XE_EU_STALL_EXTENSION_SET_PROPERTY] = xe_eu_stall_user_ext_set_property,
};

#define MAX_USER_EXTENSIONS     5
static int xe_eu_stall_user_extensions(struct xe_device *xe, u64 extension,
                                       int ext_number, struct eu_stall_open_properties *props)
{
        u64 __user *address = u64_to_user_ptr(extension);
        struct drm_xe_user_extension ext;
        int err;
        u32 idx;

        if (XE_IOCTL_DBG(xe, ext_number >= MAX_USER_EXTENSIONS))
                return -E2BIG;

        err = copy_from_user(&ext, address, sizeof(ext));
        if (XE_IOCTL_DBG(xe, err))
                return -EFAULT;

        if (XE_IOCTL_DBG(xe, ext.pad) ||
            XE_IOCTL_DBG(xe, ext.name >= ARRAY_SIZE(xe_eu_stall_user_extension_funcs)))
                return -EINVAL;

        idx = array_index_nospec(ext.name, ARRAY_SIZE(xe_eu_stall_user_extension_funcs));
        err = xe_eu_stall_user_extension_funcs[idx](xe, extension, props);
        if (XE_IOCTL_DBG(xe, err))
                return err;

        if (ext.next_extension)
                return xe_eu_stall_user_extensions(xe, ext.next_extension, ++ext_number, props);

        return 0;
}

/**
 * buf_data_size - Calculate the number of bytes in a circular buffer
 *                 given the read and write pointers and the size of
 *                 the buffer.
 *
 * @buf_size: Size of the circular buffer
 * @read_ptr: Read pointer with an additional overflow bit
 * @write_ptr: Write pointer with an additional overflow bit
 *
 * Since the read and write pointers have an additional overflow bit,
 * this function calculates the offsets from the pointers and use the
 * offsets to calculate the data size in the buffer.
 *
 * Returns: number of bytes of data in the buffer
 */
static u32 buf_data_size(size_t buf_size, u32 read_ptr, u32 write_ptr)
{
        u32 read_offset, write_offset, size = 0;

        if (read_ptr == write_ptr)
                goto exit;

        read_offset = read_ptr & (buf_size - 1);
        write_offset = write_ptr & (buf_size - 1);

        if (write_offset > read_offset)
                size = write_offset - read_offset;
        else
                size = buf_size - read_offset + write_offset;
exit:
        return size;
}

/**
 * eu_stall_data_buf_poll - Poll for EU stall data in the buffer.
 *
 * @stream: xe EU stall data stream instance
 *
 * Returns: true if the EU stall buffer contains minimum stall data as
 *          specified by the event report count, else false.
 */
static bool eu_stall_data_buf_poll(struct xe_eu_stall_data_stream *stream)
{
        u32 read_ptr, write_ptr_reg, write_ptr, total_data = 0;
        u32 buf_size = stream->per_xecore_buf_size;
        struct per_xecore_buf *xecore_buf;
        struct xe_gt *gt = stream->gt;
        bool min_data_present = false;
        u16 group, instance;
        unsigned int xecore;

        mutex_lock(&stream->xecore_buf_lock);
        for_each_dss_steering(xecore, gt, group, instance) {
                xecore_buf = &stream->xecore_buf[xecore];
                read_ptr = xecore_buf->read;
                write_ptr_reg = xe_gt_mcr_unicast_read(gt, XEHPC_EUSTALL_REPORT,
                                                       group, instance);
                write_ptr = REG_FIELD_GET(XEHPC_EUSTALL_REPORT_WRITE_PTR_MASK, write_ptr_reg);
                write_ptr <<= 6;
                write_ptr &= ((buf_size << 1) - 1);
                if (!min_data_present) {
                        total_data += buf_data_size(buf_size, read_ptr, write_ptr);
                        if (num_data_rows(total_data) >= stream->wait_num_reports)
                                min_data_present = true;
                }
                if (write_ptr_reg & XEHPC_EUSTALL_REPORT_OVERFLOW_DROP)
                        set_bit(xecore, stream->data_drop.mask);
                xecore_buf->write = write_ptr;
        }
        mutex_unlock(&stream->xecore_buf_lock);

        return min_data_present;
}

static void clear_dropped_eviction_line_bit(struct xe_gt *gt, u16 group, u16 instance)
{
        struct xe_device *xe = gt_to_xe(gt);
        u32 write_ptr_reg;

        /* On PVC, the overflow bit has to be cleared by writing 1 to it.
         * On Xe2 and later GPUs, the bit has to be cleared by writing 0 to it.
         */
        if (GRAPHICS_VER(xe) >= 20)
                write_ptr_reg = _MASKED_BIT_DISABLE(XEHPC_EUSTALL_REPORT_OVERFLOW_DROP);
        else
                write_ptr_reg = _MASKED_BIT_ENABLE(XEHPC_EUSTALL_REPORT_OVERFLOW_DROP);

        xe_gt_mcr_unicast_write(gt, XEHPC_EUSTALL_REPORT, write_ptr_reg, group, instance);
}

static int xe_eu_stall_data_buf_read(struct xe_eu_stall_data_stream *stream,
                                     char __user *buf, size_t count,
                                     size_t *total_data_size, struct xe_gt *gt,
                                     u16 group, u16 instance, unsigned int xecore)
{
        size_t read_data_size, copy_size, buf_size;
        u32 read_ptr_reg, read_ptr, write_ptr;
        u8 *xecore_start_vaddr, *read_vaddr;
        struct per_xecore_buf *xecore_buf;
        u32 read_offset, write_offset;

        /* Hardware increments the read and write pointers such that they can
         * overflow into one additional bit. For example, a 256KB size buffer
         * offset pointer needs 18 bits. But HW uses 19 bits for the read and
         * write pointers. This technique avoids wasting a slot in the buffer.
         * Read and write offsets are calculated from the pointers in order to
         * check if the write pointer has wrapped around the array.
         */
        xecore_buf = &stream->xecore_buf[xecore];
        xecore_start_vaddr = xecore_buf->vaddr;
        read_ptr = xecore_buf->read;
        write_ptr = xecore_buf->write;
        buf_size = stream->per_xecore_buf_size;

        read_data_size = buf_data_size(buf_size, read_ptr, write_ptr);
        /* Read only the data that the user space buffer can accommodate */
        read_data_size = min_t(size_t, count - *total_data_size, read_data_size);
        if (read_data_size == 0)
                goto exit_drop;

        read_offset = read_ptr & (buf_size - 1);
        write_offset = write_ptr & (buf_size - 1);
        read_vaddr = xecore_start_vaddr + read_offset;

        if (write_offset > read_offset) {
                if (copy_to_user(buf + *total_data_size, read_vaddr, read_data_size))
                        return -EFAULT;
        } else {
                if (read_data_size >= buf_size - read_offset)
                        copy_size = buf_size - read_offset;
                else
                        copy_size = read_data_size;
                if (copy_to_user(buf + *total_data_size, read_vaddr, copy_size))
                        return -EFAULT;
                if (copy_to_user(buf + *total_data_size + copy_size,
                                 xecore_start_vaddr, read_data_size - copy_size))
                        return -EFAULT;
        }

        *total_data_size += read_data_size;
        read_ptr += read_data_size;

        /* Read pointer can overflow into one additional bit */
        read_ptr &= (buf_size << 1) - 1;
        read_ptr_reg = REG_FIELD_PREP(XEHPC_EUSTALL_REPORT1_READ_PTR_MASK, (read_ptr >> 6));
        read_ptr_reg = _MASKED_FIELD(XEHPC_EUSTALL_REPORT1_READ_PTR_MASK, read_ptr_reg);
        xe_gt_mcr_unicast_write(gt, XEHPC_EUSTALL_REPORT1, read_ptr_reg, group, instance);
        xecore_buf->read = read_ptr;
        trace_xe_eu_stall_data_read(group, instance, read_ptr, write_ptr,
                                    read_data_size, *total_data_size);
exit_drop:
        /* Clear drop bit (if set) after any data was read or if the buffer was empty.
         * Drop bit can be set even if the buffer is empty as the buffer may have been emptied
         * in the previous read() and the data drop bit was set during the previous read().
         */
        if (test_bit(xecore, stream->data_drop.mask)) {
                clear_dropped_eviction_line_bit(gt, group, instance);
                clear_bit(xecore, stream->data_drop.mask);
        }
        return 0;
}

/**
 * xe_eu_stall_stream_read_locked - copy EU stall counters data from the
 *                                  per xecore buffers to the userspace buffer
 * @stream: A stream opened for EU stall count metrics
 * @file: An xe EU stall data stream file
 * @buf: destination buffer given by userspace
 * @count: the number of bytes userspace wants to read
 *
 * Returns: Number of bytes copied or a negative error code
 * If we've successfully copied any data then reporting that takes
 * precedence over any internal error status, so the data isn't lost.
 */
static ssize_t xe_eu_stall_stream_read_locked(struct xe_eu_stall_data_stream *stream,
                                              struct file *file, char __user *buf,
                                              size_t count)
{
        struct xe_gt *gt = stream->gt;
        size_t total_size = 0;
        u16 group, instance;
        unsigned int xecore;
        int ret = 0;

        mutex_lock(&stream->xecore_buf_lock);
        if (bitmap_weight(stream->data_drop.mask, XE_MAX_DSS_FUSE_BITS)) {
                if (!stream->data_drop.reported_to_user) {
                        stream->data_drop.reported_to_user = true;
                        xe_gt_dbg(gt, "EU stall data dropped in XeCores: %*pb\n",
                                  XE_MAX_DSS_FUSE_BITS, stream->data_drop.mask);
                        mutex_unlock(&stream->xecore_buf_lock);
                        return -EIO;
                }
                stream->data_drop.reported_to_user = false;
        }

        for_each_dss_steering(xecore, gt, group, instance) {
                ret = xe_eu_stall_data_buf_read(stream, buf, count, &total_size,
                                                gt, group, instance, xecore);
                if (ret || count == total_size)
                        break;
        }
        mutex_unlock(&stream->xecore_buf_lock);
        return total_size ?: (ret ?: -EAGAIN);
}

/*
 * Userspace must enable the EU stall stream with DRM_XE_OBSERVATION_IOCTL_ENABLE
 * before calling read().
 *
 * Returns: The number of bytes copied or a negative error code on failure.
 *          -EIO if HW drops any EU stall data when the buffer is full.
 */
static ssize_t xe_eu_stall_stream_read(struct file *file, char __user *buf,
                                       size_t count, loff_t *ppos)
{
        struct xe_eu_stall_data_stream *stream = file->private_data;
        struct xe_gt *gt = stream->gt;
        ssize_t ret, aligned_count;

        aligned_count = ALIGN_DOWN(count, stream->data_record_size);
        if (aligned_count == 0)
                return -EINVAL;

        if (!stream->enabled) {
                xe_gt_dbg(gt, "EU stall data stream not enabled to read\n");
                return -EINVAL;
        }

        if (!(file->f_flags & O_NONBLOCK)) {
                do {
                        ret = wait_event_interruptible(stream->poll_wq, stream->pollin);
                        if (ret)
                                return -EINTR;

                        mutex_lock(&gt->eu_stall->stream_lock);
                        ret = xe_eu_stall_stream_read_locked(stream, file, buf, aligned_count);
                        mutex_unlock(&gt->eu_stall->stream_lock);
                } while (ret == -EAGAIN);
        } else {
                mutex_lock(&gt->eu_stall->stream_lock);
                ret = xe_eu_stall_stream_read_locked(stream, file, buf, aligned_count);
                mutex_unlock(&gt->eu_stall->stream_lock);
        }

        /*
         * This may not work correctly if the user buffer is very small.
         * We don't want to block the next read() when there is data in the buffer
         * now, but couldn't be accommodated in the small user buffer.
         */
        stream->pollin = false;

        return ret;
}

static void xe_eu_stall_stream_free(struct xe_eu_stall_data_stream *stream)
{
        struct xe_gt *gt = stream->gt;

        mutex_destroy(&stream->xecore_buf_lock);
        gt->eu_stall->stream = NULL;
        kfree(stream);
}

static void xe_eu_stall_data_buf_destroy(struct xe_eu_stall_data_stream *stream)
{
        xe_bo_unpin_map_no_vm(stream->bo);
        kfree(stream->xecore_buf);
}

static int xe_eu_stall_data_buf_alloc(struct xe_eu_stall_data_stream *stream,
                                      u16 last_xecore)
{
        struct xe_tile *tile = stream->gt->tile;
        struct xe_bo *bo;
        u32 size;

        stream->xecore_buf = kzalloc_objs(*stream->xecore_buf, last_xecore);
        if (!stream->xecore_buf)
                return -ENOMEM;

        size = stream->per_xecore_buf_size * last_xecore;

        bo = xe_bo_create_pin_map_at_novm(tile->xe, tile, size, ~0ull, ttm_bo_type_kernel,
                                          XE_BO_FLAG_SYSTEM | XE_BO_FLAG_GGTT, SZ_64, false);
        if (IS_ERR(bo)) {
                kfree(stream->xecore_buf);
                return PTR_ERR(bo);
        }

        XE_WARN_ON(!IS_ALIGNED(xe_bo_ggtt_addr(bo), SZ_64));
        stream->bo = bo;

        return 0;
}

static int xe_eu_stall_stream_enable(struct xe_eu_stall_data_stream *stream)
{
        u32 write_ptr_reg, write_ptr, read_ptr_reg, reg_value;
        struct per_xecore_buf *xecore_buf;
        struct xe_gt *gt = stream->gt;
        u16 group, instance;
        int xecore;

        /* Take runtime pm ref and forcewake to disable RC6 */
        xe_pm_runtime_get(gt_to_xe(gt));
        stream->fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_RENDER);
        if (!xe_force_wake_ref_has_domain(stream->fw_ref, XE_FW_RENDER)) {
                xe_gt_err(gt, "Failed to get RENDER forcewake\n");
                xe_pm_runtime_put(gt_to_xe(gt));
                return -ETIMEDOUT;
        }

        if (XE_GT_WA(gt, 22016596838))
                xe_gt_mcr_multicast_write(gt, ROW_CHICKEN2,
                                          _MASKED_BIT_ENABLE(DISABLE_DOP_GATING));

        for_each_dss_steering(xecore, gt, group, instance) {
                write_ptr_reg = xe_gt_mcr_unicast_read(gt, XEHPC_EUSTALL_REPORT, group, instance);
                /* Clear any drop bits set and not cleared in the previous session. */
                if (write_ptr_reg & XEHPC_EUSTALL_REPORT_OVERFLOW_DROP)
                        clear_dropped_eviction_line_bit(gt, group, instance);
                write_ptr = REG_FIELD_GET(XEHPC_EUSTALL_REPORT_WRITE_PTR_MASK, write_ptr_reg);
                read_ptr_reg = REG_FIELD_PREP(XEHPC_EUSTALL_REPORT1_READ_PTR_MASK, write_ptr);
                read_ptr_reg = _MASKED_FIELD(XEHPC_EUSTALL_REPORT1_READ_PTR_MASK, read_ptr_reg);
                /* Initialize the read pointer to the write pointer */
                xe_gt_mcr_unicast_write(gt, XEHPC_EUSTALL_REPORT1, read_ptr_reg, group, instance);
                write_ptr <<= 6;
                write_ptr &= (stream->per_xecore_buf_size << 1) - 1;
                xecore_buf = &stream->xecore_buf[xecore];
                xecore_buf->write = write_ptr;
                xecore_buf->read = write_ptr;
        }
        stream->data_drop.reported_to_user = false;
        bitmap_zero(stream->data_drop.mask, XE_MAX_DSS_FUSE_BITS);

        reg_value = _MASKED_FIELD(EUSTALL_MOCS | EUSTALL_SAMPLE_RATE,
                                  REG_FIELD_PREP(EUSTALL_MOCS, gt->mocs.uc_index << 1) |
                                  REG_FIELD_PREP(EUSTALL_SAMPLE_RATE,
                                                 stream->sampling_rate_mult));
        xe_gt_mcr_multicast_write(gt, XEHPC_EUSTALL_CTRL, reg_value);
        /* GGTT addresses can never be > 32 bits */
        xe_gt_mcr_multicast_write(gt, XEHPC_EUSTALL_BASE_UPPER, 0);
        reg_value = xe_bo_ggtt_addr(stream->bo);
        reg_value |= REG_FIELD_PREP(XEHPC_EUSTALL_BASE_XECORE_BUF_SZ,
                                    stream->per_xecore_buf_size / SZ_256K);
        reg_value |= XEHPC_EUSTALL_BASE_ENABLE_SAMPLING;
        xe_gt_mcr_multicast_write(gt, XEHPC_EUSTALL_BASE, reg_value);

        return 0;
}

static void eu_stall_data_buf_poll_work_fn(struct work_struct *work)
{
        struct xe_eu_stall_data_stream *stream =
                container_of(work, typeof(*stream), buf_poll_work.work);
        struct xe_gt *gt = stream->gt;

        if (eu_stall_data_buf_poll(stream)) {
                stream->pollin = true;
                wake_up(&stream->poll_wq);
        }
        queue_delayed_work(gt->eu_stall->buf_ptr_poll_wq,
                           &stream->buf_poll_work,
                           msecs_to_jiffies(POLL_PERIOD_MS));
}

static int xe_eu_stall_stream_init(struct xe_eu_stall_data_stream *stream,
                                   struct eu_stall_open_properties *props)
{
        unsigned int max_wait_num_reports, xecore, last_xecore, num_xecores;
        struct per_xecore_buf *xecore_buf;
        struct xe_gt *gt = stream->gt;
        xe_dss_mask_t all_xecores;
        u16 group, instance;
        u32 vaddr_offset;
        int ret;

        bitmap_or(all_xecores, gt->fuse_topo.g_dss_mask, gt->fuse_topo.c_dss_mask,
                  XE_MAX_DSS_FUSE_BITS);
        num_xecores = bitmap_weight(all_xecores, XE_MAX_DSS_FUSE_BITS);
        last_xecore = xe_gt_topology_mask_last_dss(all_xecores) + 1;

        max_wait_num_reports = num_data_rows(per_xecore_buf_size * num_xecores);
        if (props->wait_num_reports == 0 || props->wait_num_reports > max_wait_num_reports) {
                xe_gt_dbg(gt, "Invalid EU stall event report count %u\n",
                          props->wait_num_reports);
                xe_gt_dbg(gt, "Minimum event report count is 1, maximum is %u\n",
                          max_wait_num_reports);
                return -EINVAL;
        }

        init_waitqueue_head(&stream->poll_wq);
        mutex_init(&stream->xecore_buf_lock);
        INIT_DELAYED_WORK(&stream->buf_poll_work, eu_stall_data_buf_poll_work_fn);
        stream->per_xecore_buf_size = per_xecore_buf_size;
        stream->sampling_rate_mult = props->sampling_rate_mult;
        stream->wait_num_reports = props->wait_num_reports;
        stream->data_record_size = xe_eu_stall_data_record_size(gt_to_xe(gt));

        ret = xe_eu_stall_data_buf_alloc(stream, last_xecore);
        if (ret)
                return ret;

        for_each_dss_steering(xecore, gt, group, instance) {
                xecore_buf = &stream->xecore_buf[xecore];
                vaddr_offset = xecore * stream->per_xecore_buf_size;
                xecore_buf->vaddr = stream->bo->vmap.vaddr + vaddr_offset;
        }
        return 0;
}

static __poll_t xe_eu_stall_stream_poll_locked(struct xe_eu_stall_data_stream *stream,
                                               struct file *file, poll_table *wait)
{
        __poll_t events = 0;

        poll_wait(file, &stream->poll_wq, wait);

        if (stream->pollin)
                events |= EPOLLIN;

        return events;
}

static __poll_t xe_eu_stall_stream_poll(struct file *file, poll_table *wait)
{
        struct xe_eu_stall_data_stream *stream = file->private_data;
        struct xe_gt *gt = stream->gt;
        __poll_t ret;

        mutex_lock(&gt->eu_stall->stream_lock);
        ret = xe_eu_stall_stream_poll_locked(stream, file, wait);
        mutex_unlock(&gt->eu_stall->stream_lock);

        return ret;
}

static int xe_eu_stall_enable_locked(struct xe_eu_stall_data_stream *stream)
{
        struct xe_gt *gt = stream->gt;
        int ret = 0;

        if (stream->enabled)
                return ret;

        stream->enabled = true;

        ret = xe_eu_stall_stream_enable(stream);

        queue_delayed_work(gt->eu_stall->buf_ptr_poll_wq,
                           &stream->buf_poll_work,
                           msecs_to_jiffies(POLL_PERIOD_MS));
        return ret;
}

static int xe_eu_stall_disable_locked(struct xe_eu_stall_data_stream *stream)
{
        struct xe_gt *gt = stream->gt;

        if (!stream->enabled)
                return 0;

        stream->enabled = false;

        xe_gt_mcr_multicast_write(gt, XEHPC_EUSTALL_BASE, 0);

        cancel_delayed_work_sync(&stream->buf_poll_work);

        if (XE_GT_WA(gt, 22016596838))
                xe_gt_mcr_multicast_write(gt, ROW_CHICKEN2,
                                          _MASKED_BIT_DISABLE(DISABLE_DOP_GATING));

        xe_force_wake_put(gt_to_fw(gt), stream->fw_ref);
        xe_pm_runtime_put(gt_to_xe(gt));

        return 0;
}

static long xe_eu_stall_stream_ioctl_locked(struct xe_eu_stall_data_stream *stream,
                                            unsigned int cmd, unsigned long arg)
{
        switch (cmd) {
        case DRM_XE_OBSERVATION_IOCTL_ENABLE:
                return xe_eu_stall_enable_locked(stream);
        case DRM_XE_OBSERVATION_IOCTL_DISABLE:
                return xe_eu_stall_disable_locked(stream);
        }

        return -EINVAL;
}

static long xe_eu_stall_stream_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
        struct xe_eu_stall_data_stream *stream = file->private_data;
        struct xe_gt *gt = stream->gt;
        long ret;

        mutex_lock(&gt->eu_stall->stream_lock);
        ret = xe_eu_stall_stream_ioctl_locked(stream, cmd, arg);
        mutex_unlock(&gt->eu_stall->stream_lock);

        return ret;
}

static int xe_eu_stall_stream_close(struct inode *inode, struct file *file)
{
        struct xe_eu_stall_data_stream *stream = file->private_data;
        struct xe_gt *gt = stream->gt;

        drm_dev_put(&gt->tile->xe->drm);

        mutex_lock(&gt->eu_stall->stream_lock);
        xe_eu_stall_disable_locked(stream);
        xe_eu_stall_data_buf_destroy(stream);
        xe_eu_stall_stream_free(stream);
        mutex_unlock(&gt->eu_stall->stream_lock);

        return 0;
}

static const struct file_operations fops_eu_stall = {
        .owner          = THIS_MODULE,
        .llseek         = noop_llseek,
        .release        = xe_eu_stall_stream_close,
        .poll           = xe_eu_stall_stream_poll,
        .read           = xe_eu_stall_stream_read,
        .unlocked_ioctl = xe_eu_stall_stream_ioctl,
        .compat_ioctl   = xe_eu_stall_stream_ioctl,
};

static int xe_eu_stall_stream_open_locked(struct drm_device *dev,
                                          struct eu_stall_open_properties *props,
                                          struct drm_file *file)
{
        struct xe_eu_stall_data_stream *stream;
        struct xe_gt *gt = props->gt;
        unsigned long f_flags = 0;
        int ret, stream_fd;

        /* Only one session can be active at any time */
        if (gt->eu_stall->stream) {
                xe_gt_dbg(gt, "EU stall sampling session already active\n");
                return -EBUSY;
        }

        stream = kzalloc_obj(*stream);
        if (!stream)
                return -ENOMEM;

        gt->eu_stall->stream = stream;
        stream->gt = gt;

        ret = xe_eu_stall_stream_init(stream, props);
        if (ret) {
                xe_gt_dbg(gt, "EU stall stream init failed : %d\n", ret);
                goto err_free;
        }

        stream_fd = anon_inode_getfd("[xe_eu_stall]", &fops_eu_stall, stream, f_flags);
        if (stream_fd < 0) {
                ret = stream_fd;
                xe_gt_dbg(gt, "EU stall inode get fd failed : %d\n", ret);
                goto err_destroy;
        }

        /* Take a reference on the driver that will be kept with stream_fd
         * until its release.
         */
        drm_dev_get(&gt->tile->xe->drm);

        return stream_fd;

err_destroy:
        xe_eu_stall_data_buf_destroy(stream);
err_free:
        xe_eu_stall_stream_free(stream);
        return ret;
}

/**
 * xe_eu_stall_stream_open - Open a xe EU stall data stream fd
 *
 * @dev: DRM device pointer
 * @data: pointer to first struct @drm_xe_ext_set_property in
 *        the chain of input properties from the user space.
 * @file: DRM file pointer
 *
 * This function opens a EU stall data stream with input properties from
 * the user space.
 *
 * Returns: EU stall data stream fd on success or a negative error code.
 */
int xe_eu_stall_stream_open(struct drm_device *dev, u64 data, struct drm_file *file)
{
        struct xe_device *xe = to_xe_device(dev);
        struct eu_stall_open_properties props = {};
        int ret;

        if (!xe_eu_stall_supported_on_platform(xe)) {
                drm_dbg(&xe->drm, "EU stall monitoring is not supported on this platform\n");
                return -ENODEV;
        }

        if (xe_observation_paranoid && !perfmon_capable()) {
                drm_dbg(&xe->drm,  "Insufficient privileges for EU stall monitoring\n");
                return -EACCES;
        }

        /* Initialize and set default values */
        props.wait_num_reports = 1;
        props.sampling_rate_mult = 4;

        ret = xe_eu_stall_user_extensions(xe, data, 0, &props);
        if (ret)
                return ret;

        if (!props.gt) {
                drm_dbg(&xe->drm, "GT ID not provided for EU stall sampling\n");
                return -EINVAL;
        }

        mutex_lock(&props.gt->eu_stall->stream_lock);
        ret = xe_eu_stall_stream_open_locked(dev, &props, file);
        mutex_unlock(&props.gt->eu_stall->stream_lock);

        return ret;
}