11537ec85SHarish Chegondi // SPDX-License-Identifier: MIT
21537ec85SHarish Chegondi /*
31537ec85SHarish Chegondi * Copyright © 2025 Intel Corporation
41537ec85SHarish Chegondi */
51537ec85SHarish Chegondi
61537ec85SHarish Chegondi #include <linux/anon_inodes.h>
71537ec85SHarish Chegondi #include <linux/fs.h>
81537ec85SHarish Chegondi #include <linux/poll.h>
91537ec85SHarish Chegondi #include <linux/types.h>
101537ec85SHarish Chegondi
119a0b11d4SHarish Chegondi #include <drm/drm_drv.h>
12e67a35bcSHarish Chegondi #include <generated/xe_wa_oob.h>
131537ec85SHarish Chegondi #include <uapi/drm/xe_drm.h>
141537ec85SHarish Chegondi
159a0b11d4SHarish Chegondi #include "xe_bo.h"
161537ec85SHarish Chegondi #include "xe_device.h"
171537ec85SHarish Chegondi #include "xe_eu_stall.h"
189a0b11d4SHarish Chegondi #include "xe_force_wake.h"
199a0b11d4SHarish Chegondi #include "xe_gt_mcr.h"
201537ec85SHarish Chegondi #include "xe_gt_printk.h"
211537ec85SHarish Chegondi #include "xe_gt_topology.h"
221537ec85SHarish Chegondi #include "xe_macros.h"
231537ec85SHarish Chegondi #include "xe_observation.h"
249a0b11d4SHarish Chegondi #include "xe_pm.h"
25760edec9SHarish Chegondi #include "xe_trace.h"
26e67a35bcSHarish Chegondi #include "xe_wa.h"
279a0b11d4SHarish Chegondi
289a0b11d4SHarish Chegondi #include "regs/xe_eu_stall_regs.h"
299a0b11d4SHarish Chegondi #include "regs/xe_gt_regs.h"
309a0b11d4SHarish Chegondi
31760edec9SHarish Chegondi #define POLL_PERIOD_MS 5
32760edec9SHarish Chegondi
339a0b11d4SHarish Chegondi static size_t per_xecore_buf_size = SZ_512K;
349a0b11d4SHarish Chegondi
359a0b11d4SHarish Chegondi struct per_xecore_buf {
369a0b11d4SHarish Chegondi /* Buffer vaddr */
379a0b11d4SHarish Chegondi u8 *vaddr;
389a0b11d4SHarish Chegondi /* Write pointer */
399a0b11d4SHarish Chegondi u32 write;
409a0b11d4SHarish Chegondi /* Read pointer */
419a0b11d4SHarish Chegondi u32 read;
429a0b11d4SHarish Chegondi };
439a0b11d4SHarish Chegondi
449a0b11d4SHarish Chegondi struct xe_eu_stall_data_stream {
45760edec9SHarish Chegondi bool pollin;
469a0b11d4SHarish Chegondi bool enabled;
479a0b11d4SHarish Chegondi int wait_num_reports;
489a0b11d4SHarish Chegondi int sampling_rate_mult;
49760edec9SHarish Chegondi wait_queue_head_t poll_wq;
509a0b11d4SHarish Chegondi size_t data_record_size;
519a0b11d4SHarish Chegondi size_t per_xecore_buf_size;
529a0b11d4SHarish Chegondi
539a0b11d4SHarish Chegondi struct xe_gt *gt;
549a0b11d4SHarish Chegondi struct xe_bo *bo;
555a295badSHarish Chegondi /* Lock to protect data buffer pointers */
565a295badSHarish Chegondi struct mutex xecore_buf_lock;
579a0b11d4SHarish Chegondi struct per_xecore_buf *xecore_buf;
589e0590eeSHarish Chegondi struct {
599e0590eeSHarish Chegondi bool reported_to_user;
609e0590eeSHarish Chegondi xe_dss_mask_t mask;
619e0590eeSHarish Chegondi } data_drop;
62760edec9SHarish Chegondi struct delayed_work buf_poll_work;
639a0b11d4SHarish Chegondi };
649a0b11d4SHarish Chegondi
659a0b11d4SHarish Chegondi struct xe_eu_stall_gt {
669a0b11d4SHarish Chegondi /* Lock to protect stream */
679a0b11d4SHarish Chegondi struct mutex stream_lock;
689a0b11d4SHarish Chegondi /* EU stall data stream */
699a0b11d4SHarish Chegondi struct xe_eu_stall_data_stream *stream;
70760edec9SHarish Chegondi /* Workqueue to schedule buffer pointers polling work */
71760edec9SHarish Chegondi struct workqueue_struct *buf_ptr_poll_wq;
729a0b11d4SHarish Chegondi };
731537ec85SHarish Chegondi
741537ec85SHarish Chegondi /**
751537ec85SHarish Chegondi * struct eu_stall_open_properties - EU stall sampling properties received
761537ec85SHarish Chegondi * from user space at open.
771537ec85SHarish Chegondi * @sampling_rate_mult: EU stall sampling rate multiplier.
781537ec85SHarish Chegondi * HW will sample every (sampling_rate_mult x 251) cycles.
791537ec85SHarish Chegondi * @wait_num_reports: Minimum number of EU stall data reports to unblock poll().
801537ec85SHarish Chegondi * @gt: GT on which EU stall data will be captured.
811537ec85SHarish Chegondi */
821537ec85SHarish Chegondi struct eu_stall_open_properties {
831537ec85SHarish Chegondi int sampling_rate_mult;
841537ec85SHarish Chegondi int wait_num_reports;
851537ec85SHarish Chegondi struct xe_gt *gt;
861537ec85SHarish Chegondi };
871537ec85SHarish Chegondi
889a0b11d4SHarish Chegondi /*
899a0b11d4SHarish Chegondi * EU stall data format for PVC
909a0b11d4SHarish Chegondi */
919a0b11d4SHarish Chegondi struct xe_eu_stall_data_pvc {
929a0b11d4SHarish Chegondi __u64 ip_addr:29; /* Bits 0 to 28 */
939a0b11d4SHarish Chegondi __u64 active_count:8; /* Bits 29 to 36 */
949a0b11d4SHarish Chegondi __u64 other_count:8; /* Bits 37 to 44 */
959a0b11d4SHarish Chegondi __u64 control_count:8; /* Bits 45 to 52 */
969a0b11d4SHarish Chegondi __u64 pipestall_count:8; /* Bits 53 to 60 */
979a0b11d4SHarish Chegondi __u64 send_count:8; /* Bits 61 to 68 */
989a0b11d4SHarish Chegondi __u64 dist_acc_count:8; /* Bits 69 to 76 */
999a0b11d4SHarish Chegondi __u64 sbid_count:8; /* Bits 77 to 84 */
1009a0b11d4SHarish Chegondi __u64 sync_count:8; /* Bits 85 to 92 */
1019a0b11d4SHarish Chegondi __u64 inst_fetch_count:8; /* Bits 93 to 100 */
1029a0b11d4SHarish Chegondi __u64 unused_bits:27;
1039a0b11d4SHarish Chegondi __u64 unused[6];
1049a0b11d4SHarish Chegondi } __packed;
1059a0b11d4SHarish Chegondi
106e827cf32SHarish Chegondi /*
107e827cf32SHarish Chegondi * EU stall data format for Xe2 arch GPUs (LNL, BMG).
108e827cf32SHarish Chegondi */
109e827cf32SHarish Chegondi struct xe_eu_stall_data_xe2 {
110e827cf32SHarish Chegondi __u64 ip_addr:29; /* Bits 0 to 28 */
111e827cf32SHarish Chegondi __u64 tdr_count:8; /* Bits 29 to 36 */
112e827cf32SHarish Chegondi __u64 other_count:8; /* Bits 37 to 44 */
113e827cf32SHarish Chegondi __u64 control_count:8; /* Bits 45 to 52 */
114e827cf32SHarish Chegondi __u64 pipestall_count:8; /* Bits 53 to 60 */
115e827cf32SHarish Chegondi __u64 send_count:8; /* Bits 61 to 68 */
116e827cf32SHarish Chegondi __u64 dist_acc_count:8; /* Bits 69 to 76 */
117e827cf32SHarish Chegondi __u64 sbid_count:8; /* Bits 77 to 84 */
118e827cf32SHarish Chegondi __u64 sync_count:8; /* Bits 85 to 92 */
119e827cf32SHarish Chegondi __u64 inst_fetch_count:8; /* Bits 93 to 100 */
120e827cf32SHarish Chegondi __u64 active_count:8; /* Bits 101 to 108 */
121e827cf32SHarish Chegondi __u64 ex_id:3; /* Bits 109 to 111 */
122e827cf32SHarish Chegondi __u64 end_flag:1; /* Bit 112 */
123e827cf32SHarish Chegondi __u64 unused_bits:15;
124e827cf32SHarish Chegondi __u64 unused[6];
125e827cf32SHarish Chegondi } __packed;
126e827cf32SHarish Chegondi
127cd5bbb25SHarish Chegondi const u64 eu_stall_sampling_rates[] = {251, 251 * 2, 251 * 3, 251 * 4, 251 * 5, 251 * 6, 251 * 7};
128cd5bbb25SHarish Chegondi
129cd5bbb25SHarish Chegondi /**
130cd5bbb25SHarish Chegondi * xe_eu_stall_get_sampling_rates - get EU stall sampling rates information.
131cd5bbb25SHarish Chegondi *
132cd5bbb25SHarish Chegondi * @num_rates: Pointer to a u32 to return the number of sampling rates.
133cd5bbb25SHarish Chegondi * @rates: double u64 pointer to point to an array of sampling rates.
134cd5bbb25SHarish Chegondi *
135cd5bbb25SHarish Chegondi * Stores the number of sampling rates and pointer to the array of
136cd5bbb25SHarish Chegondi * sampling rates in the input pointers.
137cd5bbb25SHarish Chegondi *
138cd5bbb25SHarish Chegondi * Returns: Size of the EU stall sampling rates array.
139cd5bbb25SHarish Chegondi */
xe_eu_stall_get_sampling_rates(u32 * num_rates,const u64 ** rates)140cd5bbb25SHarish Chegondi size_t xe_eu_stall_get_sampling_rates(u32 *num_rates, const u64 **rates)
141cd5bbb25SHarish Chegondi {
142cd5bbb25SHarish Chegondi *num_rates = ARRAY_SIZE(eu_stall_sampling_rates);
143cd5bbb25SHarish Chegondi *rates = eu_stall_sampling_rates;
144cd5bbb25SHarish Chegondi
145cd5bbb25SHarish Chegondi return sizeof(eu_stall_sampling_rates);
146cd5bbb25SHarish Chegondi }
147cd5bbb25SHarish Chegondi
148cd5bbb25SHarish Chegondi /**
149cd5bbb25SHarish Chegondi * xe_eu_stall_get_per_xecore_buf_size - get per XeCore buffer size.
150cd5bbb25SHarish Chegondi *
151cd5bbb25SHarish Chegondi * Returns: The per XeCore buffer size used to allocate the per GT
152cd5bbb25SHarish Chegondi * EU stall data buffer.
153cd5bbb25SHarish Chegondi */
xe_eu_stall_get_per_xecore_buf_size(void)154cd5bbb25SHarish Chegondi size_t xe_eu_stall_get_per_xecore_buf_size(void)
155cd5bbb25SHarish Chegondi {
156cd5bbb25SHarish Chegondi return per_xecore_buf_size;
157cd5bbb25SHarish Chegondi }
158cd5bbb25SHarish Chegondi
159cd5bbb25SHarish Chegondi /**
160cd5bbb25SHarish Chegondi * xe_eu_stall_data_record_size - get EU stall data record size.
161cd5bbb25SHarish Chegondi *
162cd5bbb25SHarish Chegondi * @xe: Pointer to a Xe device.
163cd5bbb25SHarish Chegondi *
164cd5bbb25SHarish Chegondi * Returns: EU stall data record size.
165cd5bbb25SHarish Chegondi */
xe_eu_stall_data_record_size(struct xe_device * xe)166cd5bbb25SHarish Chegondi size_t xe_eu_stall_data_record_size(struct xe_device *xe)
1679a0b11d4SHarish Chegondi {
1689a0b11d4SHarish Chegondi size_t record_size = 0;
1699a0b11d4SHarish Chegondi
1709a0b11d4SHarish Chegondi if (xe->info.platform == XE_PVC)
1719a0b11d4SHarish Chegondi record_size = sizeof(struct xe_eu_stall_data_pvc);
172e827cf32SHarish Chegondi else if (GRAPHICS_VER(xe) >= 20)
173e827cf32SHarish Chegondi record_size = sizeof(struct xe_eu_stall_data_xe2);
1749a0b11d4SHarish Chegondi
1759a0b11d4SHarish Chegondi xe_assert(xe, is_power_of_2(record_size));
1769a0b11d4SHarish Chegondi
1779a0b11d4SHarish Chegondi return record_size;
1789a0b11d4SHarish Chegondi }
1799a0b11d4SHarish Chegondi
1809a0b11d4SHarish Chegondi /**
1819a0b11d4SHarish Chegondi * num_data_rows - Return the number of EU stall data rows of 64B each
1829a0b11d4SHarish Chegondi * for a given data size.
1839a0b11d4SHarish Chegondi *
1849a0b11d4SHarish Chegondi * @data_size: EU stall data size
1859a0b11d4SHarish Chegondi */
num_data_rows(u32 data_size)1869a0b11d4SHarish Chegondi static u32 num_data_rows(u32 data_size)
1879a0b11d4SHarish Chegondi {
1889a0b11d4SHarish Chegondi return data_size >> 6;
1899a0b11d4SHarish Chegondi }
1909a0b11d4SHarish Chegondi
xe_eu_stall_fini(void * arg)1919a0b11d4SHarish Chegondi static void xe_eu_stall_fini(void *arg)
1929a0b11d4SHarish Chegondi {
1939a0b11d4SHarish Chegondi struct xe_gt *gt = arg;
1949a0b11d4SHarish Chegondi
195760edec9SHarish Chegondi destroy_workqueue(gt->eu_stall->buf_ptr_poll_wq);
1969a0b11d4SHarish Chegondi mutex_destroy(>->eu_stall->stream_lock);
1979a0b11d4SHarish Chegondi kfree(gt->eu_stall);
1989a0b11d4SHarish Chegondi }
1999a0b11d4SHarish Chegondi
2009a0b11d4SHarish Chegondi /**
2019a0b11d4SHarish Chegondi * xe_eu_stall_init() - Allocate and initialize GT level EU stall data
2029a0b11d4SHarish Chegondi * structure xe_eu_stall_gt within struct xe_gt.
2039a0b11d4SHarish Chegondi *
2049a0b11d4SHarish Chegondi * @gt: GT being initialized.
2059a0b11d4SHarish Chegondi *
2069a0b11d4SHarish Chegondi * Returns: zero on success or a negative error code.
2079a0b11d4SHarish Chegondi */
xe_eu_stall_init(struct xe_gt * gt)2089a0b11d4SHarish Chegondi int xe_eu_stall_init(struct xe_gt *gt)
2099a0b11d4SHarish Chegondi {
2109a0b11d4SHarish Chegondi struct xe_device *xe = gt_to_xe(gt);
2119a0b11d4SHarish Chegondi int ret;
2129a0b11d4SHarish Chegondi
213*1d622a4fSHarish Chegondi if (!xe_eu_stall_supported_on_platform(xe))
214*1d622a4fSHarish Chegondi return 0;
215*1d622a4fSHarish Chegondi
2169a0b11d4SHarish Chegondi gt->eu_stall = kzalloc(sizeof(*gt->eu_stall), GFP_KERNEL);
2179a0b11d4SHarish Chegondi if (!gt->eu_stall) {
2189a0b11d4SHarish Chegondi ret = -ENOMEM;
2199a0b11d4SHarish Chegondi goto exit;
2209a0b11d4SHarish Chegondi }
2219a0b11d4SHarish Chegondi
2229a0b11d4SHarish Chegondi mutex_init(>->eu_stall->stream_lock);
2239a0b11d4SHarish Chegondi
224760edec9SHarish Chegondi gt->eu_stall->buf_ptr_poll_wq = alloc_ordered_workqueue("xe_eu_stall", 0);
225760edec9SHarish Chegondi if (!gt->eu_stall->buf_ptr_poll_wq) {
226760edec9SHarish Chegondi ret = -ENOMEM;
227760edec9SHarish Chegondi goto exit_free;
228760edec9SHarish Chegondi }
229760edec9SHarish Chegondi
230488975c2SHarish Chegondi return devm_add_action_or_reset(xe->drm.dev, xe_eu_stall_fini, gt);
2319a0b11d4SHarish Chegondi exit_free:
2329a0b11d4SHarish Chegondi mutex_destroy(>->eu_stall->stream_lock);
2339a0b11d4SHarish Chegondi kfree(gt->eu_stall);
2349a0b11d4SHarish Chegondi exit:
2359a0b11d4SHarish Chegondi return ret;
2369a0b11d4SHarish Chegondi }
2379a0b11d4SHarish Chegondi
set_prop_eu_stall_sampling_rate(struct xe_device * xe,u64 value,struct eu_stall_open_properties * props)2381537ec85SHarish Chegondi static int set_prop_eu_stall_sampling_rate(struct xe_device *xe, u64 value,
2391537ec85SHarish Chegondi struct eu_stall_open_properties *props)
2401537ec85SHarish Chegondi {
2411537ec85SHarish Chegondi value = div_u64(value, 251);
2421537ec85SHarish Chegondi if (value == 0 || value > 7) {
2431537ec85SHarish Chegondi drm_dbg(&xe->drm, "Invalid EU stall sampling rate %llu\n", value);
2441537ec85SHarish Chegondi return -EINVAL;
2451537ec85SHarish Chegondi }
2461537ec85SHarish Chegondi props->sampling_rate_mult = value;
2471537ec85SHarish Chegondi return 0;
2481537ec85SHarish Chegondi }
2491537ec85SHarish Chegondi
set_prop_eu_stall_wait_num_reports(struct xe_device * xe,u64 value,struct eu_stall_open_properties * props)2501537ec85SHarish Chegondi static int set_prop_eu_stall_wait_num_reports(struct xe_device *xe, u64 value,
2511537ec85SHarish Chegondi struct eu_stall_open_properties *props)
2521537ec85SHarish Chegondi {
2531537ec85SHarish Chegondi props->wait_num_reports = value;
2541537ec85SHarish Chegondi
2551537ec85SHarish Chegondi return 0;
2561537ec85SHarish Chegondi }
2571537ec85SHarish Chegondi
set_prop_eu_stall_gt_id(struct xe_device * xe,u64 value,struct eu_stall_open_properties * props)2581537ec85SHarish Chegondi static int set_prop_eu_stall_gt_id(struct xe_device *xe, u64 value,
2591537ec85SHarish Chegondi struct eu_stall_open_properties *props)
2601537ec85SHarish Chegondi {
2611537ec85SHarish Chegondi if (value >= xe->info.gt_count) {
2621537ec85SHarish Chegondi drm_dbg(&xe->drm, "Invalid GT ID %llu for EU stall sampling\n", value);
2631537ec85SHarish Chegondi return -EINVAL;
2641537ec85SHarish Chegondi }
2651537ec85SHarish Chegondi props->gt = xe_device_get_gt(xe, value);
2661537ec85SHarish Chegondi return 0;
2671537ec85SHarish Chegondi }
2681537ec85SHarish Chegondi
2691537ec85SHarish Chegondi typedef int (*set_eu_stall_property_fn)(struct xe_device *xe, u64 value,
2701537ec85SHarish Chegondi struct eu_stall_open_properties *props);
2711537ec85SHarish Chegondi
2721537ec85SHarish Chegondi static const set_eu_stall_property_fn xe_set_eu_stall_property_funcs[] = {
2731537ec85SHarish Chegondi [DRM_XE_EU_STALL_PROP_SAMPLE_RATE] = set_prop_eu_stall_sampling_rate,
2741537ec85SHarish Chegondi [DRM_XE_EU_STALL_PROP_WAIT_NUM_REPORTS] = set_prop_eu_stall_wait_num_reports,
2751537ec85SHarish Chegondi [DRM_XE_EU_STALL_PROP_GT_ID] = set_prop_eu_stall_gt_id,
2761537ec85SHarish Chegondi };
2771537ec85SHarish Chegondi
xe_eu_stall_user_ext_set_property(struct xe_device * xe,u64 extension,struct eu_stall_open_properties * props)2781537ec85SHarish Chegondi static int xe_eu_stall_user_ext_set_property(struct xe_device *xe, u64 extension,
2791537ec85SHarish Chegondi struct eu_stall_open_properties *props)
2801537ec85SHarish Chegondi {
2811537ec85SHarish Chegondi u64 __user *address = u64_to_user_ptr(extension);
2821537ec85SHarish Chegondi struct drm_xe_ext_set_property ext;
2831537ec85SHarish Chegondi int err;
2841537ec85SHarish Chegondi u32 idx;
2851537ec85SHarish Chegondi
2861537ec85SHarish Chegondi err = copy_from_user(&ext, address, sizeof(ext));
2871537ec85SHarish Chegondi if (XE_IOCTL_DBG(xe, err))
2881537ec85SHarish Chegondi return -EFAULT;
2891537ec85SHarish Chegondi
2901537ec85SHarish Chegondi if (XE_IOCTL_DBG(xe, ext.property >= ARRAY_SIZE(xe_set_eu_stall_property_funcs)) ||
2911537ec85SHarish Chegondi XE_IOCTL_DBG(xe, ext.pad))
2921537ec85SHarish Chegondi return -EINVAL;
2931537ec85SHarish Chegondi
2941537ec85SHarish Chegondi idx = array_index_nospec(ext.property, ARRAY_SIZE(xe_set_eu_stall_property_funcs));
2951537ec85SHarish Chegondi return xe_set_eu_stall_property_funcs[idx](xe, ext.value, props);
2961537ec85SHarish Chegondi }
2971537ec85SHarish Chegondi
2981537ec85SHarish Chegondi typedef int (*xe_eu_stall_user_extension_fn)(struct xe_device *xe, u64 extension,
2991537ec85SHarish Chegondi struct eu_stall_open_properties *props);
3001537ec85SHarish Chegondi static const xe_eu_stall_user_extension_fn xe_eu_stall_user_extension_funcs[] = {
3011537ec85SHarish Chegondi [DRM_XE_EU_STALL_EXTENSION_SET_PROPERTY] = xe_eu_stall_user_ext_set_property,
3021537ec85SHarish Chegondi };
3031537ec85SHarish Chegondi
3041537ec85SHarish Chegondi #define MAX_USER_EXTENSIONS 5
xe_eu_stall_user_extensions(struct xe_device * xe,u64 extension,int ext_number,struct eu_stall_open_properties * props)3051537ec85SHarish Chegondi static int xe_eu_stall_user_extensions(struct xe_device *xe, u64 extension,
3061537ec85SHarish Chegondi int ext_number, struct eu_stall_open_properties *props)
3071537ec85SHarish Chegondi {
3081537ec85SHarish Chegondi u64 __user *address = u64_to_user_ptr(extension);
3091537ec85SHarish Chegondi struct drm_xe_user_extension ext;
3101537ec85SHarish Chegondi int err;
3111537ec85SHarish Chegondi u32 idx;
3121537ec85SHarish Chegondi
3131537ec85SHarish Chegondi if (XE_IOCTL_DBG(xe, ext_number >= MAX_USER_EXTENSIONS))
3141537ec85SHarish Chegondi return -E2BIG;
3151537ec85SHarish Chegondi
3161537ec85SHarish Chegondi err = copy_from_user(&ext, address, sizeof(ext));
3171537ec85SHarish Chegondi if (XE_IOCTL_DBG(xe, err))
3181537ec85SHarish Chegondi return -EFAULT;
3191537ec85SHarish Chegondi
3201537ec85SHarish Chegondi if (XE_IOCTL_DBG(xe, ext.pad) ||
3211537ec85SHarish Chegondi XE_IOCTL_DBG(xe, ext.name >= ARRAY_SIZE(xe_eu_stall_user_extension_funcs)))
3221537ec85SHarish Chegondi return -EINVAL;
3231537ec85SHarish Chegondi
3241537ec85SHarish Chegondi idx = array_index_nospec(ext.name, ARRAY_SIZE(xe_eu_stall_user_extension_funcs));
3251537ec85SHarish Chegondi err = xe_eu_stall_user_extension_funcs[idx](xe, extension, props);
3261537ec85SHarish Chegondi if (XE_IOCTL_DBG(xe, err))
3271537ec85SHarish Chegondi return err;
3281537ec85SHarish Chegondi
3291537ec85SHarish Chegondi if (ext.next_extension)
3301537ec85SHarish Chegondi return xe_eu_stall_user_extensions(xe, ext.next_extension, ++ext_number, props);
3311537ec85SHarish Chegondi
3321537ec85SHarish Chegondi return 0;
3331537ec85SHarish Chegondi }
3341537ec85SHarish Chegondi
335760edec9SHarish Chegondi /**
336760edec9SHarish Chegondi * buf_data_size - Calculate the number of bytes in a circular buffer
337760edec9SHarish Chegondi * given the read and write pointers and the size of
338760edec9SHarish Chegondi * the buffer.
339760edec9SHarish Chegondi *
340760edec9SHarish Chegondi * @buf_size: Size of the circular buffer
341760edec9SHarish Chegondi * @read_ptr: Read pointer with an additional overflow bit
342760edec9SHarish Chegondi * @write_ptr: Write pointer with an additional overflow bit
343760edec9SHarish Chegondi *
344760edec9SHarish Chegondi * Since the read and write pointers have an additional overflow bit,
345760edec9SHarish Chegondi * this function calculates the offsets from the pointers and use the
346760edec9SHarish Chegondi * offsets to calculate the data size in the buffer.
347760edec9SHarish Chegondi *
348760edec9SHarish Chegondi * Returns: number of bytes of data in the buffer
349760edec9SHarish Chegondi */
buf_data_size(size_t buf_size,u32 read_ptr,u32 write_ptr)350760edec9SHarish Chegondi static u32 buf_data_size(size_t buf_size, u32 read_ptr, u32 write_ptr)
351760edec9SHarish Chegondi {
352760edec9SHarish Chegondi u32 read_offset, write_offset, size = 0;
353760edec9SHarish Chegondi
354760edec9SHarish Chegondi if (read_ptr == write_ptr)
355760edec9SHarish Chegondi goto exit;
356760edec9SHarish Chegondi
357760edec9SHarish Chegondi read_offset = read_ptr & (buf_size - 1);
358760edec9SHarish Chegondi write_offset = write_ptr & (buf_size - 1);
359760edec9SHarish Chegondi
360760edec9SHarish Chegondi if (write_offset > read_offset)
361760edec9SHarish Chegondi size = write_offset - read_offset;
362760edec9SHarish Chegondi else
363760edec9SHarish Chegondi size = buf_size - read_offset + write_offset;
364760edec9SHarish Chegondi exit:
365760edec9SHarish Chegondi return size;
366760edec9SHarish Chegondi }
367760edec9SHarish Chegondi
368760edec9SHarish Chegondi /**
369760edec9SHarish Chegondi * eu_stall_data_buf_poll - Poll for EU stall data in the buffer.
370760edec9SHarish Chegondi *
371760edec9SHarish Chegondi * @stream: xe EU stall data stream instance
372760edec9SHarish Chegondi *
373760edec9SHarish Chegondi * Returns: true if the EU stall buffer contains minimum stall data as
374760edec9SHarish Chegondi * specified by the event report count, else false.
375760edec9SHarish Chegondi */
eu_stall_data_buf_poll(struct xe_eu_stall_data_stream * stream)376760edec9SHarish Chegondi static bool eu_stall_data_buf_poll(struct xe_eu_stall_data_stream *stream)
377760edec9SHarish Chegondi {
378760edec9SHarish Chegondi u32 read_ptr, write_ptr_reg, write_ptr, total_data = 0;
379760edec9SHarish Chegondi u32 buf_size = stream->per_xecore_buf_size;
380760edec9SHarish Chegondi struct per_xecore_buf *xecore_buf;
381760edec9SHarish Chegondi struct xe_gt *gt = stream->gt;
382760edec9SHarish Chegondi bool min_data_present = false;
383760edec9SHarish Chegondi u16 group, instance;
384760edec9SHarish Chegondi unsigned int xecore;
385760edec9SHarish Chegondi
3865a295badSHarish Chegondi mutex_lock(&stream->xecore_buf_lock);
387760edec9SHarish Chegondi for_each_dss_steering(xecore, gt, group, instance) {
388760edec9SHarish Chegondi xecore_buf = &stream->xecore_buf[xecore];
389760edec9SHarish Chegondi read_ptr = xecore_buf->read;
390760edec9SHarish Chegondi write_ptr_reg = xe_gt_mcr_unicast_read(gt, XEHPC_EUSTALL_REPORT,
391760edec9SHarish Chegondi group, instance);
392760edec9SHarish Chegondi write_ptr = REG_FIELD_GET(XEHPC_EUSTALL_REPORT_WRITE_PTR_MASK, write_ptr_reg);
393760edec9SHarish Chegondi write_ptr <<= 6;
394760edec9SHarish Chegondi write_ptr &= ((buf_size << 1) - 1);
395760edec9SHarish Chegondi if (!min_data_present) {
396760edec9SHarish Chegondi total_data += buf_data_size(buf_size, read_ptr, write_ptr);
397760edec9SHarish Chegondi if (num_data_rows(total_data) >= stream->wait_num_reports)
398760edec9SHarish Chegondi min_data_present = true;
399760edec9SHarish Chegondi }
4009e0590eeSHarish Chegondi if (write_ptr_reg & XEHPC_EUSTALL_REPORT_OVERFLOW_DROP)
4019e0590eeSHarish Chegondi set_bit(xecore, stream->data_drop.mask);
402760edec9SHarish Chegondi xecore_buf->write = write_ptr;
403760edec9SHarish Chegondi }
4045a295badSHarish Chegondi mutex_unlock(&stream->xecore_buf_lock);
405760edec9SHarish Chegondi
406760edec9SHarish Chegondi return min_data_present;
407760edec9SHarish Chegondi }
408760edec9SHarish Chegondi
clear_dropped_eviction_line_bit(struct xe_gt * gt,u16 group,u16 instance)4099e0590eeSHarish Chegondi static void clear_dropped_eviction_line_bit(struct xe_gt *gt, u16 group, u16 instance)
4109e0590eeSHarish Chegondi {
411e827cf32SHarish Chegondi struct xe_device *xe = gt_to_xe(gt);
4129e0590eeSHarish Chegondi u32 write_ptr_reg;
4139e0590eeSHarish Chegondi
414e827cf32SHarish Chegondi /* On PVC, the overflow bit has to be cleared by writing 1 to it.
415e827cf32SHarish Chegondi * On Xe2 and later GPUs, the bit has to be cleared by writing 0 to it.
416e827cf32SHarish Chegondi */
417e827cf32SHarish Chegondi if (GRAPHICS_VER(xe) >= 20)
418e827cf32SHarish Chegondi write_ptr_reg = _MASKED_BIT_DISABLE(XEHPC_EUSTALL_REPORT_OVERFLOW_DROP);
419e827cf32SHarish Chegondi else
4209e0590eeSHarish Chegondi write_ptr_reg = _MASKED_BIT_ENABLE(XEHPC_EUSTALL_REPORT_OVERFLOW_DROP);
4219e0590eeSHarish Chegondi
4229e0590eeSHarish Chegondi xe_gt_mcr_unicast_write(gt, XEHPC_EUSTALL_REPORT, write_ptr_reg, group, instance);
4239e0590eeSHarish Chegondi }
4249e0590eeSHarish Chegondi
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)425760edec9SHarish Chegondi static int xe_eu_stall_data_buf_read(struct xe_eu_stall_data_stream *stream,
426760edec9SHarish Chegondi char __user *buf, size_t count,
427760edec9SHarish Chegondi size_t *total_data_size, struct xe_gt *gt,
428760edec9SHarish Chegondi u16 group, u16 instance, unsigned int xecore)
429760edec9SHarish Chegondi {
430760edec9SHarish Chegondi size_t read_data_size, copy_size, buf_size;
431760edec9SHarish Chegondi u32 read_ptr_reg, read_ptr, write_ptr;
432760edec9SHarish Chegondi u8 *xecore_start_vaddr, *read_vaddr;
433760edec9SHarish Chegondi struct per_xecore_buf *xecore_buf;
434760edec9SHarish Chegondi u32 read_offset, write_offset;
435760edec9SHarish Chegondi
436760edec9SHarish Chegondi /* Hardware increments the read and write pointers such that they can
437760edec9SHarish Chegondi * overflow into one additional bit. For example, a 256KB size buffer
438760edec9SHarish Chegondi * offset pointer needs 18 bits. But HW uses 19 bits for the read and
439760edec9SHarish Chegondi * write pointers. This technique avoids wasting a slot in the buffer.
440760edec9SHarish Chegondi * Read and write offsets are calculated from the pointers in order to
441760edec9SHarish Chegondi * check if the write pointer has wrapped around the array.
442760edec9SHarish Chegondi */
443760edec9SHarish Chegondi xecore_buf = &stream->xecore_buf[xecore];
444760edec9SHarish Chegondi xecore_start_vaddr = xecore_buf->vaddr;
445760edec9SHarish Chegondi read_ptr = xecore_buf->read;
446760edec9SHarish Chegondi write_ptr = xecore_buf->write;
447760edec9SHarish Chegondi buf_size = stream->per_xecore_buf_size;
448760edec9SHarish Chegondi
449760edec9SHarish Chegondi read_data_size = buf_data_size(buf_size, read_ptr, write_ptr);
450760edec9SHarish Chegondi /* Read only the data that the user space buffer can accommodate */
451760edec9SHarish Chegondi read_data_size = min_t(size_t, count - *total_data_size, read_data_size);
452760edec9SHarish Chegondi if (read_data_size == 0)
4539e0590eeSHarish Chegondi goto exit_drop;
454760edec9SHarish Chegondi
455760edec9SHarish Chegondi read_offset = read_ptr & (buf_size - 1);
456760edec9SHarish Chegondi write_offset = write_ptr & (buf_size - 1);
457760edec9SHarish Chegondi read_vaddr = xecore_start_vaddr + read_offset;
458760edec9SHarish Chegondi
459760edec9SHarish Chegondi if (write_offset > read_offset) {
460760edec9SHarish Chegondi if (copy_to_user(buf + *total_data_size, read_vaddr, read_data_size))
461760edec9SHarish Chegondi return -EFAULT;
462760edec9SHarish Chegondi } else {
463760edec9SHarish Chegondi if (read_data_size >= buf_size - read_offset)
464760edec9SHarish Chegondi copy_size = buf_size - read_offset;
465760edec9SHarish Chegondi else
466760edec9SHarish Chegondi copy_size = read_data_size;
467760edec9SHarish Chegondi if (copy_to_user(buf + *total_data_size, read_vaddr, copy_size))
468760edec9SHarish Chegondi return -EFAULT;
469760edec9SHarish Chegondi if (copy_to_user(buf + *total_data_size + copy_size,
470760edec9SHarish Chegondi xecore_start_vaddr, read_data_size - copy_size))
471760edec9SHarish Chegondi return -EFAULT;
472760edec9SHarish Chegondi }
473760edec9SHarish Chegondi
474760edec9SHarish Chegondi *total_data_size += read_data_size;
475760edec9SHarish Chegondi read_ptr += read_data_size;
476760edec9SHarish Chegondi
477760edec9SHarish Chegondi /* Read pointer can overflow into one additional bit */
478760edec9SHarish Chegondi read_ptr &= (buf_size << 1) - 1;
479760edec9SHarish Chegondi read_ptr_reg = REG_FIELD_PREP(XEHPC_EUSTALL_REPORT1_READ_PTR_MASK, (read_ptr >> 6));
480760edec9SHarish Chegondi read_ptr_reg = _MASKED_FIELD(XEHPC_EUSTALL_REPORT1_READ_PTR_MASK, read_ptr_reg);
481760edec9SHarish Chegondi xe_gt_mcr_unicast_write(gt, XEHPC_EUSTALL_REPORT1, read_ptr_reg, group, instance);
482760edec9SHarish Chegondi xecore_buf->read = read_ptr;
483760edec9SHarish Chegondi trace_xe_eu_stall_data_read(group, instance, read_ptr, write_ptr,
484760edec9SHarish Chegondi read_data_size, *total_data_size);
4859e0590eeSHarish Chegondi exit_drop:
4869e0590eeSHarish Chegondi /* Clear drop bit (if set) after any data was read or if the buffer was empty.
4879e0590eeSHarish Chegondi * Drop bit can be set even if the buffer is empty as the buffer may have been emptied
4889e0590eeSHarish Chegondi * in the previous read() and the data drop bit was set during the previous read().
4899e0590eeSHarish Chegondi */
4909e0590eeSHarish Chegondi if (test_bit(xecore, stream->data_drop.mask)) {
4919e0590eeSHarish Chegondi clear_dropped_eviction_line_bit(gt, group, instance);
4929e0590eeSHarish Chegondi clear_bit(xecore, stream->data_drop.mask);
4939e0590eeSHarish Chegondi }
494760edec9SHarish Chegondi return 0;
495760edec9SHarish Chegondi }
496760edec9SHarish Chegondi
497760edec9SHarish Chegondi /**
498760edec9SHarish Chegondi * xe_eu_stall_stream_read_locked - copy EU stall counters data from the
499760edec9SHarish Chegondi * per xecore buffers to the userspace buffer
500760edec9SHarish Chegondi * @stream: A stream opened for EU stall count metrics
501760edec9SHarish Chegondi * @file: An xe EU stall data stream file
502760edec9SHarish Chegondi * @buf: destination buffer given by userspace
503760edec9SHarish Chegondi * @count: the number of bytes userspace wants to read
504760edec9SHarish Chegondi *
505760edec9SHarish Chegondi * Returns: Number of bytes copied or a negative error code
506760edec9SHarish Chegondi * If we've successfully copied any data then reporting that takes
507760edec9SHarish Chegondi * precedence over any internal error status, so the data isn't lost.
508760edec9SHarish Chegondi */
xe_eu_stall_stream_read_locked(struct xe_eu_stall_data_stream * stream,struct file * file,char __user * buf,size_t count)509760edec9SHarish Chegondi static ssize_t xe_eu_stall_stream_read_locked(struct xe_eu_stall_data_stream *stream,
510760edec9SHarish Chegondi struct file *file, char __user *buf,
511760edec9SHarish Chegondi size_t count)
512760edec9SHarish Chegondi {
513760edec9SHarish Chegondi struct xe_gt *gt = stream->gt;
514760edec9SHarish Chegondi size_t total_size = 0;
515760edec9SHarish Chegondi u16 group, instance;
516760edec9SHarish Chegondi unsigned int xecore;
517760edec9SHarish Chegondi int ret = 0;
518760edec9SHarish Chegondi
5195a295badSHarish Chegondi mutex_lock(&stream->xecore_buf_lock);
5209e0590eeSHarish Chegondi if (bitmap_weight(stream->data_drop.mask, XE_MAX_DSS_FUSE_BITS)) {
5219e0590eeSHarish Chegondi if (!stream->data_drop.reported_to_user) {
5229e0590eeSHarish Chegondi stream->data_drop.reported_to_user = true;
5239e0590eeSHarish Chegondi xe_gt_dbg(gt, "EU stall data dropped in XeCores: %*pb\n",
5249e0590eeSHarish Chegondi XE_MAX_DSS_FUSE_BITS, stream->data_drop.mask);
5255a295badSHarish Chegondi mutex_unlock(&stream->xecore_buf_lock);
5269e0590eeSHarish Chegondi return -EIO;
5279e0590eeSHarish Chegondi }
5289e0590eeSHarish Chegondi stream->data_drop.reported_to_user = false;
5299e0590eeSHarish Chegondi }
5309e0590eeSHarish Chegondi
531760edec9SHarish Chegondi for_each_dss_steering(xecore, gt, group, instance) {
532760edec9SHarish Chegondi ret = xe_eu_stall_data_buf_read(stream, buf, count, &total_size,
533760edec9SHarish Chegondi gt, group, instance, xecore);
534760edec9SHarish Chegondi if (ret || count == total_size)
535760edec9SHarish Chegondi break;
536760edec9SHarish Chegondi }
5375a295badSHarish Chegondi mutex_unlock(&stream->xecore_buf_lock);
538760edec9SHarish Chegondi return total_size ?: (ret ?: -EAGAIN);
539760edec9SHarish Chegondi }
540760edec9SHarish Chegondi
5411537ec85SHarish Chegondi /*
5421537ec85SHarish Chegondi * Userspace must enable the EU stall stream with DRM_XE_OBSERVATION_IOCTL_ENABLE
5431537ec85SHarish Chegondi * before calling read().
5449e0590eeSHarish Chegondi *
5459e0590eeSHarish Chegondi * Returns: The number of bytes copied or a negative error code on failure.
5469e0590eeSHarish Chegondi * -EIO if HW drops any EU stall data when the buffer is full.
5471537ec85SHarish Chegondi */
xe_eu_stall_stream_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)5481537ec85SHarish Chegondi static ssize_t xe_eu_stall_stream_read(struct file *file, char __user *buf,
5491537ec85SHarish Chegondi size_t count, loff_t *ppos)
5501537ec85SHarish Chegondi {
551760edec9SHarish Chegondi struct xe_eu_stall_data_stream *stream = file->private_data;
552760edec9SHarish Chegondi struct xe_gt *gt = stream->gt;
553760edec9SHarish Chegondi ssize_t ret, aligned_count;
554760edec9SHarish Chegondi
555760edec9SHarish Chegondi aligned_count = ALIGN_DOWN(count, stream->data_record_size);
556760edec9SHarish Chegondi if (aligned_count == 0)
557760edec9SHarish Chegondi return -EINVAL;
558760edec9SHarish Chegondi
559760edec9SHarish Chegondi if (!stream->enabled) {
560760edec9SHarish Chegondi xe_gt_dbg(gt, "EU stall data stream not enabled to read\n");
561760edec9SHarish Chegondi return -EINVAL;
562760edec9SHarish Chegondi }
563760edec9SHarish Chegondi
564760edec9SHarish Chegondi if (!(file->f_flags & O_NONBLOCK)) {
565760edec9SHarish Chegondi do {
566760edec9SHarish Chegondi ret = wait_event_interruptible(stream->poll_wq, stream->pollin);
567760edec9SHarish Chegondi if (ret)
568760edec9SHarish Chegondi return -EINTR;
569760edec9SHarish Chegondi
570760edec9SHarish Chegondi mutex_lock(>->eu_stall->stream_lock);
571760edec9SHarish Chegondi ret = xe_eu_stall_stream_read_locked(stream, file, buf, aligned_count);
572760edec9SHarish Chegondi mutex_unlock(>->eu_stall->stream_lock);
573760edec9SHarish Chegondi } while (ret == -EAGAIN);
574760edec9SHarish Chegondi } else {
575760edec9SHarish Chegondi mutex_lock(>->eu_stall->stream_lock);
576760edec9SHarish Chegondi ret = xe_eu_stall_stream_read_locked(stream, file, buf, aligned_count);
577760edec9SHarish Chegondi mutex_unlock(>->eu_stall->stream_lock);
578760edec9SHarish Chegondi }
579760edec9SHarish Chegondi
580760edec9SHarish Chegondi /*
581760edec9SHarish Chegondi * This may not work correctly if the user buffer is very small.
582760edec9SHarish Chegondi * We don't want to block the next read() when there is data in the buffer
583760edec9SHarish Chegondi * now, but couldn't be accommodated in the small user buffer.
584760edec9SHarish Chegondi */
585760edec9SHarish Chegondi stream->pollin = false;
5861537ec85SHarish Chegondi
5871537ec85SHarish Chegondi return ret;
5881537ec85SHarish Chegondi }
5891537ec85SHarish Chegondi
xe_eu_stall_stream_free(struct xe_eu_stall_data_stream * stream)5909a0b11d4SHarish Chegondi static void xe_eu_stall_stream_free(struct xe_eu_stall_data_stream *stream)
5919a0b11d4SHarish Chegondi {
5929a0b11d4SHarish Chegondi struct xe_gt *gt = stream->gt;
5939a0b11d4SHarish Chegondi
5945a295badSHarish Chegondi mutex_destroy(&stream->xecore_buf_lock);
5959a0b11d4SHarish Chegondi gt->eu_stall->stream = NULL;
5969a0b11d4SHarish Chegondi kfree(stream);
5979a0b11d4SHarish Chegondi }
5989a0b11d4SHarish Chegondi
xe_eu_stall_data_buf_destroy(struct xe_eu_stall_data_stream * stream)5999a0b11d4SHarish Chegondi static void xe_eu_stall_data_buf_destroy(struct xe_eu_stall_data_stream *stream)
6009a0b11d4SHarish Chegondi {
6019a0b11d4SHarish Chegondi xe_bo_unpin_map_no_vm(stream->bo);
6029a0b11d4SHarish Chegondi kfree(stream->xecore_buf);
6039a0b11d4SHarish Chegondi }
6049a0b11d4SHarish Chegondi
xe_eu_stall_data_buf_alloc(struct xe_eu_stall_data_stream * stream,u16 last_xecore)6059a0b11d4SHarish Chegondi static int xe_eu_stall_data_buf_alloc(struct xe_eu_stall_data_stream *stream,
6069a0b11d4SHarish Chegondi u16 last_xecore)
6079a0b11d4SHarish Chegondi {
6089a0b11d4SHarish Chegondi struct xe_tile *tile = stream->gt->tile;
6099a0b11d4SHarish Chegondi struct xe_bo *bo;
6109a0b11d4SHarish Chegondi u32 size;
6119a0b11d4SHarish Chegondi
6129a0b11d4SHarish Chegondi stream->xecore_buf = kcalloc(last_xecore, sizeof(*stream->xecore_buf), GFP_KERNEL);
6139a0b11d4SHarish Chegondi if (!stream->xecore_buf)
6149a0b11d4SHarish Chegondi return -ENOMEM;
6159a0b11d4SHarish Chegondi
6169a0b11d4SHarish Chegondi size = stream->per_xecore_buf_size * last_xecore;
6179a0b11d4SHarish Chegondi
6189a0b11d4SHarish Chegondi bo = xe_bo_create_pin_map_at_aligned(tile->xe, tile, NULL,
6199a0b11d4SHarish Chegondi size, ~0ull, ttm_bo_type_kernel,
6209a0b11d4SHarish Chegondi XE_BO_FLAG_SYSTEM | XE_BO_FLAG_GGTT, SZ_64);
6219a0b11d4SHarish Chegondi if (IS_ERR(bo)) {
6229a0b11d4SHarish Chegondi kfree(stream->xecore_buf);
6239a0b11d4SHarish Chegondi return PTR_ERR(bo);
6249a0b11d4SHarish Chegondi }
6259a0b11d4SHarish Chegondi
6269a0b11d4SHarish Chegondi XE_WARN_ON(!IS_ALIGNED(xe_bo_ggtt_addr(bo), SZ_64));
6279a0b11d4SHarish Chegondi stream->bo = bo;
6289a0b11d4SHarish Chegondi
6299a0b11d4SHarish Chegondi return 0;
6309a0b11d4SHarish Chegondi }
6319a0b11d4SHarish Chegondi
xe_eu_stall_stream_enable(struct xe_eu_stall_data_stream * stream)6329a0b11d4SHarish Chegondi static int xe_eu_stall_stream_enable(struct xe_eu_stall_data_stream *stream)
6339a0b11d4SHarish Chegondi {
6349a0b11d4SHarish Chegondi u32 write_ptr_reg, write_ptr, read_ptr_reg, reg_value;
6359a0b11d4SHarish Chegondi struct per_xecore_buf *xecore_buf;
6369a0b11d4SHarish Chegondi struct xe_gt *gt = stream->gt;
6379a0b11d4SHarish Chegondi u16 group, instance;
6389a0b11d4SHarish Chegondi unsigned int fw_ref;
6399a0b11d4SHarish Chegondi int xecore;
6409a0b11d4SHarish Chegondi
6419a0b11d4SHarish Chegondi /* Take runtime pm ref and forcewake to disable RC6 */
6429a0b11d4SHarish Chegondi xe_pm_runtime_get(gt_to_xe(gt));
6439a0b11d4SHarish Chegondi fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_RENDER);
6449a0b11d4SHarish Chegondi if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_RENDER)) {
6459a0b11d4SHarish Chegondi xe_gt_err(gt, "Failed to get RENDER forcewake\n");
6469a0b11d4SHarish Chegondi xe_pm_runtime_put(gt_to_xe(gt));
6479a0b11d4SHarish Chegondi return -ETIMEDOUT;
6489a0b11d4SHarish Chegondi }
6499a0b11d4SHarish Chegondi
650e67a35bcSHarish Chegondi if (XE_WA(gt, 22016596838))
651e67a35bcSHarish Chegondi xe_gt_mcr_multicast_write(gt, ROW_CHICKEN2,
652e67a35bcSHarish Chegondi _MASKED_BIT_ENABLE(DISABLE_DOP_GATING));
653e67a35bcSHarish Chegondi
6549a0b11d4SHarish Chegondi for_each_dss_steering(xecore, gt, group, instance) {
6559a0b11d4SHarish Chegondi write_ptr_reg = xe_gt_mcr_unicast_read(gt, XEHPC_EUSTALL_REPORT, group, instance);
6569e0590eeSHarish Chegondi /* Clear any drop bits set and not cleared in the previous session. */
6579e0590eeSHarish Chegondi if (write_ptr_reg & XEHPC_EUSTALL_REPORT_OVERFLOW_DROP)
6589e0590eeSHarish Chegondi clear_dropped_eviction_line_bit(gt, group, instance);
6599a0b11d4SHarish Chegondi write_ptr = REG_FIELD_GET(XEHPC_EUSTALL_REPORT_WRITE_PTR_MASK, write_ptr_reg);
6609a0b11d4SHarish Chegondi read_ptr_reg = REG_FIELD_PREP(XEHPC_EUSTALL_REPORT1_READ_PTR_MASK, write_ptr);
6619a0b11d4SHarish Chegondi read_ptr_reg = _MASKED_FIELD(XEHPC_EUSTALL_REPORT1_READ_PTR_MASK, read_ptr_reg);
6629a0b11d4SHarish Chegondi /* Initialize the read pointer to the write pointer */
6639a0b11d4SHarish Chegondi xe_gt_mcr_unicast_write(gt, XEHPC_EUSTALL_REPORT1, read_ptr_reg, group, instance);
6649a0b11d4SHarish Chegondi write_ptr <<= 6;
6659a0b11d4SHarish Chegondi write_ptr &= (stream->per_xecore_buf_size << 1) - 1;
6669a0b11d4SHarish Chegondi xecore_buf = &stream->xecore_buf[xecore];
6679a0b11d4SHarish Chegondi xecore_buf->write = write_ptr;
6689a0b11d4SHarish Chegondi xecore_buf->read = write_ptr;
6699a0b11d4SHarish Chegondi }
6709e0590eeSHarish Chegondi stream->data_drop.reported_to_user = false;
6719e0590eeSHarish Chegondi bitmap_zero(stream->data_drop.mask, XE_MAX_DSS_FUSE_BITS);
6729e0590eeSHarish Chegondi
6739a0b11d4SHarish Chegondi reg_value = _MASKED_FIELD(EUSTALL_MOCS | EUSTALL_SAMPLE_RATE,
6749a0b11d4SHarish Chegondi REG_FIELD_PREP(EUSTALL_MOCS, gt->mocs.uc_index << 1) |
6759a0b11d4SHarish Chegondi REG_FIELD_PREP(EUSTALL_SAMPLE_RATE,
6769a0b11d4SHarish Chegondi stream->sampling_rate_mult));
6779a0b11d4SHarish Chegondi xe_gt_mcr_multicast_write(gt, XEHPC_EUSTALL_CTRL, reg_value);
6789a0b11d4SHarish Chegondi /* GGTT addresses can never be > 32 bits */
6799a0b11d4SHarish Chegondi xe_gt_mcr_multicast_write(gt, XEHPC_EUSTALL_BASE_UPPER, 0);
6809a0b11d4SHarish Chegondi reg_value = xe_bo_ggtt_addr(stream->bo);
6819a0b11d4SHarish Chegondi reg_value |= REG_FIELD_PREP(XEHPC_EUSTALL_BASE_XECORE_BUF_SZ,
6829a0b11d4SHarish Chegondi stream->per_xecore_buf_size / SZ_256K);
6839a0b11d4SHarish Chegondi reg_value |= XEHPC_EUSTALL_BASE_ENABLE_SAMPLING;
6849a0b11d4SHarish Chegondi xe_gt_mcr_multicast_write(gt, XEHPC_EUSTALL_BASE, reg_value);
6859a0b11d4SHarish Chegondi
6869a0b11d4SHarish Chegondi return 0;
6879a0b11d4SHarish Chegondi }
6889a0b11d4SHarish Chegondi
eu_stall_data_buf_poll_work_fn(struct work_struct * work)689760edec9SHarish Chegondi static void eu_stall_data_buf_poll_work_fn(struct work_struct *work)
690760edec9SHarish Chegondi {
691760edec9SHarish Chegondi struct xe_eu_stall_data_stream *stream =
692760edec9SHarish Chegondi container_of(work, typeof(*stream), buf_poll_work.work);
693760edec9SHarish Chegondi struct xe_gt *gt = stream->gt;
694760edec9SHarish Chegondi
695760edec9SHarish Chegondi if (eu_stall_data_buf_poll(stream)) {
696760edec9SHarish Chegondi stream->pollin = true;
697760edec9SHarish Chegondi wake_up(&stream->poll_wq);
698760edec9SHarish Chegondi }
699760edec9SHarish Chegondi queue_delayed_work(gt->eu_stall->buf_ptr_poll_wq,
700760edec9SHarish Chegondi &stream->buf_poll_work,
701760edec9SHarish Chegondi msecs_to_jiffies(POLL_PERIOD_MS));
702760edec9SHarish Chegondi }
703760edec9SHarish Chegondi
xe_eu_stall_stream_init(struct xe_eu_stall_data_stream * stream,struct eu_stall_open_properties * props)7049a0b11d4SHarish Chegondi static int xe_eu_stall_stream_init(struct xe_eu_stall_data_stream *stream,
7059a0b11d4SHarish Chegondi struct eu_stall_open_properties *props)
7069a0b11d4SHarish Chegondi {
7079a0b11d4SHarish Chegondi unsigned int max_wait_num_reports, xecore, last_xecore, num_xecores;
7089a0b11d4SHarish Chegondi struct per_xecore_buf *xecore_buf;
7099a0b11d4SHarish Chegondi struct xe_gt *gt = stream->gt;
7109a0b11d4SHarish Chegondi xe_dss_mask_t all_xecores;
7119a0b11d4SHarish Chegondi u16 group, instance;
7129a0b11d4SHarish Chegondi u32 vaddr_offset;
7139a0b11d4SHarish Chegondi int ret;
7149a0b11d4SHarish Chegondi
7159a0b11d4SHarish Chegondi bitmap_or(all_xecores, gt->fuse_topo.g_dss_mask, gt->fuse_topo.c_dss_mask,
7169a0b11d4SHarish Chegondi XE_MAX_DSS_FUSE_BITS);
7179a0b11d4SHarish Chegondi num_xecores = bitmap_weight(all_xecores, XE_MAX_DSS_FUSE_BITS);
7189a0b11d4SHarish Chegondi last_xecore = xe_gt_topology_mask_last_dss(all_xecores) + 1;
7199a0b11d4SHarish Chegondi
7209a0b11d4SHarish Chegondi max_wait_num_reports = num_data_rows(per_xecore_buf_size * num_xecores);
7219a0b11d4SHarish Chegondi if (props->wait_num_reports == 0 || props->wait_num_reports > max_wait_num_reports) {
7229a0b11d4SHarish Chegondi xe_gt_dbg(gt, "Invalid EU stall event report count %u\n",
7239a0b11d4SHarish Chegondi props->wait_num_reports);
7249a0b11d4SHarish Chegondi xe_gt_dbg(gt, "Minimum event report count is 1, maximum is %u\n",
7259a0b11d4SHarish Chegondi max_wait_num_reports);
7269a0b11d4SHarish Chegondi return -EINVAL;
7279a0b11d4SHarish Chegondi }
728760edec9SHarish Chegondi
729760edec9SHarish Chegondi init_waitqueue_head(&stream->poll_wq);
7305a295badSHarish Chegondi mutex_init(&stream->xecore_buf_lock);
731760edec9SHarish Chegondi INIT_DELAYED_WORK(&stream->buf_poll_work, eu_stall_data_buf_poll_work_fn);
7329a0b11d4SHarish Chegondi stream->per_xecore_buf_size = per_xecore_buf_size;
7339a0b11d4SHarish Chegondi stream->sampling_rate_mult = props->sampling_rate_mult;
7349a0b11d4SHarish Chegondi stream->wait_num_reports = props->wait_num_reports;
7359a0b11d4SHarish Chegondi stream->data_record_size = xe_eu_stall_data_record_size(gt_to_xe(gt));
7369a0b11d4SHarish Chegondi
7379a0b11d4SHarish Chegondi ret = xe_eu_stall_data_buf_alloc(stream, last_xecore);
7389a0b11d4SHarish Chegondi if (ret)
7399a0b11d4SHarish Chegondi return ret;
7409a0b11d4SHarish Chegondi
7419a0b11d4SHarish Chegondi for_each_dss_steering(xecore, gt, group, instance) {
7429a0b11d4SHarish Chegondi xecore_buf = &stream->xecore_buf[xecore];
7439a0b11d4SHarish Chegondi vaddr_offset = xecore * stream->per_xecore_buf_size;
7449a0b11d4SHarish Chegondi xecore_buf->vaddr = stream->bo->vmap.vaddr + vaddr_offset;
7459a0b11d4SHarish Chegondi }
7469a0b11d4SHarish Chegondi return 0;
7479a0b11d4SHarish Chegondi }
7489a0b11d4SHarish Chegondi
xe_eu_stall_stream_poll_locked(struct xe_eu_stall_data_stream * stream,struct file * file,poll_table * wait)749760edec9SHarish Chegondi static __poll_t xe_eu_stall_stream_poll_locked(struct xe_eu_stall_data_stream *stream,
750760edec9SHarish Chegondi struct file *file, poll_table *wait)
751760edec9SHarish Chegondi {
752760edec9SHarish Chegondi __poll_t events = 0;
753760edec9SHarish Chegondi
754760edec9SHarish Chegondi poll_wait(file, &stream->poll_wq, wait);
755760edec9SHarish Chegondi
756760edec9SHarish Chegondi if (stream->pollin)
757760edec9SHarish Chegondi events |= EPOLLIN;
758760edec9SHarish Chegondi
759760edec9SHarish Chegondi return events;
760760edec9SHarish Chegondi }
761760edec9SHarish Chegondi
xe_eu_stall_stream_poll(struct file * file,poll_table * wait)7621537ec85SHarish Chegondi static __poll_t xe_eu_stall_stream_poll(struct file *file, poll_table *wait)
7631537ec85SHarish Chegondi {
764760edec9SHarish Chegondi struct xe_eu_stall_data_stream *stream = file->private_data;
765760edec9SHarish Chegondi struct xe_gt *gt = stream->gt;
766760edec9SHarish Chegondi __poll_t ret;
767760edec9SHarish Chegondi
768760edec9SHarish Chegondi mutex_lock(>->eu_stall->stream_lock);
769760edec9SHarish Chegondi ret = xe_eu_stall_stream_poll_locked(stream, file, wait);
770760edec9SHarish Chegondi mutex_unlock(>->eu_stall->stream_lock);
7711537ec85SHarish Chegondi
7721537ec85SHarish Chegondi return ret;
7731537ec85SHarish Chegondi }
7741537ec85SHarish Chegondi
xe_eu_stall_enable_locked(struct xe_eu_stall_data_stream * stream)7759a0b11d4SHarish Chegondi static int xe_eu_stall_enable_locked(struct xe_eu_stall_data_stream *stream)
7769a0b11d4SHarish Chegondi {
777760edec9SHarish Chegondi struct xe_gt *gt = stream->gt;
7789a0b11d4SHarish Chegondi int ret = 0;
7799a0b11d4SHarish Chegondi
7809a0b11d4SHarish Chegondi if (stream->enabled)
7819a0b11d4SHarish Chegondi return ret;
7829a0b11d4SHarish Chegondi
7839a0b11d4SHarish Chegondi stream->enabled = true;
7849a0b11d4SHarish Chegondi
7859a0b11d4SHarish Chegondi ret = xe_eu_stall_stream_enable(stream);
786760edec9SHarish Chegondi
787760edec9SHarish Chegondi queue_delayed_work(gt->eu_stall->buf_ptr_poll_wq,
788760edec9SHarish Chegondi &stream->buf_poll_work,
789760edec9SHarish Chegondi msecs_to_jiffies(POLL_PERIOD_MS));
7909a0b11d4SHarish Chegondi return ret;
7919a0b11d4SHarish Chegondi }
7929a0b11d4SHarish Chegondi
xe_eu_stall_disable_locked(struct xe_eu_stall_data_stream * stream)7939a0b11d4SHarish Chegondi static int xe_eu_stall_disable_locked(struct xe_eu_stall_data_stream *stream)
7949a0b11d4SHarish Chegondi {
7959a0b11d4SHarish Chegondi struct xe_gt *gt = stream->gt;
7969a0b11d4SHarish Chegondi
7979a0b11d4SHarish Chegondi if (!stream->enabled)
7989a0b11d4SHarish Chegondi return 0;
7999a0b11d4SHarish Chegondi
8009a0b11d4SHarish Chegondi stream->enabled = false;
8019a0b11d4SHarish Chegondi
8029a0b11d4SHarish Chegondi xe_gt_mcr_multicast_write(gt, XEHPC_EUSTALL_BASE, 0);
8039a0b11d4SHarish Chegondi
804760edec9SHarish Chegondi cancel_delayed_work_sync(&stream->buf_poll_work);
805760edec9SHarish Chegondi
806e67a35bcSHarish Chegondi if (XE_WA(gt, 22016596838))
807e67a35bcSHarish Chegondi xe_gt_mcr_multicast_write(gt, ROW_CHICKEN2,
808e67a35bcSHarish Chegondi _MASKED_BIT_DISABLE(DISABLE_DOP_GATING));
809e67a35bcSHarish Chegondi
8109a0b11d4SHarish Chegondi xe_force_wake_put(gt_to_fw(gt), XE_FW_RENDER);
8119a0b11d4SHarish Chegondi xe_pm_runtime_put(gt_to_xe(gt));
8129a0b11d4SHarish Chegondi
8139a0b11d4SHarish Chegondi return 0;
8149a0b11d4SHarish Chegondi }
8159a0b11d4SHarish Chegondi
xe_eu_stall_stream_ioctl_locked(struct xe_eu_stall_data_stream * stream,unsigned int cmd,unsigned long arg)8169a0b11d4SHarish Chegondi static long xe_eu_stall_stream_ioctl_locked(struct xe_eu_stall_data_stream *stream,
8179a0b11d4SHarish Chegondi unsigned int cmd, unsigned long arg)
8189a0b11d4SHarish Chegondi {
8199a0b11d4SHarish Chegondi switch (cmd) {
8209a0b11d4SHarish Chegondi case DRM_XE_OBSERVATION_IOCTL_ENABLE:
8219a0b11d4SHarish Chegondi return xe_eu_stall_enable_locked(stream);
8229a0b11d4SHarish Chegondi case DRM_XE_OBSERVATION_IOCTL_DISABLE:
8239a0b11d4SHarish Chegondi return xe_eu_stall_disable_locked(stream);
8249a0b11d4SHarish Chegondi }
8259a0b11d4SHarish Chegondi
8269a0b11d4SHarish Chegondi return -EINVAL;
8279a0b11d4SHarish Chegondi }
8289a0b11d4SHarish Chegondi
xe_eu_stall_stream_ioctl(struct file * file,unsigned int cmd,unsigned long arg)8291537ec85SHarish Chegondi static long xe_eu_stall_stream_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
8301537ec85SHarish Chegondi {
8319a0b11d4SHarish Chegondi struct xe_eu_stall_data_stream *stream = file->private_data;
8329a0b11d4SHarish Chegondi struct xe_gt *gt = stream->gt;
8339a0b11d4SHarish Chegondi long ret;
8349a0b11d4SHarish Chegondi
8359a0b11d4SHarish Chegondi mutex_lock(>->eu_stall->stream_lock);
8369a0b11d4SHarish Chegondi ret = xe_eu_stall_stream_ioctl_locked(stream, cmd, arg);
8379a0b11d4SHarish Chegondi mutex_unlock(>->eu_stall->stream_lock);
8389a0b11d4SHarish Chegondi
8399a0b11d4SHarish Chegondi return ret;
8401537ec85SHarish Chegondi }
8411537ec85SHarish Chegondi
xe_eu_stall_stream_close(struct inode * inode,struct file * file)8421537ec85SHarish Chegondi static int xe_eu_stall_stream_close(struct inode *inode, struct file *file)
8431537ec85SHarish Chegondi {
8449a0b11d4SHarish Chegondi struct xe_eu_stall_data_stream *stream = file->private_data;
8459a0b11d4SHarish Chegondi struct xe_gt *gt = stream->gt;
8469a0b11d4SHarish Chegondi
8479a0b11d4SHarish Chegondi drm_dev_put(>->tile->xe->drm);
8489a0b11d4SHarish Chegondi
8499a0b11d4SHarish Chegondi mutex_lock(>->eu_stall->stream_lock);
8509a0b11d4SHarish Chegondi xe_eu_stall_disable_locked(stream);
8519a0b11d4SHarish Chegondi xe_eu_stall_data_buf_destroy(stream);
8529a0b11d4SHarish Chegondi xe_eu_stall_stream_free(stream);
8539a0b11d4SHarish Chegondi mutex_unlock(>->eu_stall->stream_lock);
8549a0b11d4SHarish Chegondi
8551537ec85SHarish Chegondi return 0;
8561537ec85SHarish Chegondi }
8571537ec85SHarish Chegondi
8581537ec85SHarish Chegondi static const struct file_operations fops_eu_stall = {
8591537ec85SHarish Chegondi .owner = THIS_MODULE,
8601537ec85SHarish Chegondi .llseek = noop_llseek,
8611537ec85SHarish Chegondi .release = xe_eu_stall_stream_close,
8621537ec85SHarish Chegondi .poll = xe_eu_stall_stream_poll,
8631537ec85SHarish Chegondi .read = xe_eu_stall_stream_read,
8641537ec85SHarish Chegondi .unlocked_ioctl = xe_eu_stall_stream_ioctl,
8651537ec85SHarish Chegondi .compat_ioctl = xe_eu_stall_stream_ioctl,
8661537ec85SHarish Chegondi };
8671537ec85SHarish Chegondi
xe_eu_stall_stream_open_locked(struct drm_device * dev,struct eu_stall_open_properties * props,struct drm_file * file)8689a0b11d4SHarish Chegondi static int xe_eu_stall_stream_open_locked(struct drm_device *dev,
8699a0b11d4SHarish Chegondi struct eu_stall_open_properties *props,
8709a0b11d4SHarish Chegondi struct drm_file *file)
8719a0b11d4SHarish Chegondi {
8729a0b11d4SHarish Chegondi struct xe_eu_stall_data_stream *stream;
8739a0b11d4SHarish Chegondi struct xe_gt *gt = props->gt;
8749a0b11d4SHarish Chegondi unsigned long f_flags = 0;
8759a0b11d4SHarish Chegondi int ret, stream_fd;
8769a0b11d4SHarish Chegondi
8779a0b11d4SHarish Chegondi /* Only one session can be active at any time */
8789a0b11d4SHarish Chegondi if (gt->eu_stall->stream) {
8799a0b11d4SHarish Chegondi xe_gt_dbg(gt, "EU stall sampling session already active\n");
8809a0b11d4SHarish Chegondi return -EBUSY;
8819a0b11d4SHarish Chegondi }
8829a0b11d4SHarish Chegondi
8839a0b11d4SHarish Chegondi stream = kzalloc(sizeof(*stream), GFP_KERNEL);
8849a0b11d4SHarish Chegondi if (!stream)
8859a0b11d4SHarish Chegondi return -ENOMEM;
8869a0b11d4SHarish Chegondi
8879a0b11d4SHarish Chegondi gt->eu_stall->stream = stream;
8889a0b11d4SHarish Chegondi stream->gt = gt;
8899a0b11d4SHarish Chegondi
8909a0b11d4SHarish Chegondi ret = xe_eu_stall_stream_init(stream, props);
8919a0b11d4SHarish Chegondi if (ret) {
8929a0b11d4SHarish Chegondi xe_gt_dbg(gt, "EU stall stream init failed : %d\n", ret);
8939a0b11d4SHarish Chegondi goto err_free;
8949a0b11d4SHarish Chegondi }
8959a0b11d4SHarish Chegondi
8969a0b11d4SHarish Chegondi stream_fd = anon_inode_getfd("[xe_eu_stall]", &fops_eu_stall, stream, f_flags);
8979a0b11d4SHarish Chegondi if (stream_fd < 0) {
8989a0b11d4SHarish Chegondi ret = stream_fd;
8999a0b11d4SHarish Chegondi xe_gt_dbg(gt, "EU stall inode get fd failed : %d\n", ret);
9009a0b11d4SHarish Chegondi goto err_destroy;
9019a0b11d4SHarish Chegondi }
9029a0b11d4SHarish Chegondi
9039a0b11d4SHarish Chegondi /* Take a reference on the driver that will be kept with stream_fd
9049a0b11d4SHarish Chegondi * until its release.
9059a0b11d4SHarish Chegondi */
9069a0b11d4SHarish Chegondi drm_dev_get(>->tile->xe->drm);
9079a0b11d4SHarish Chegondi
9089a0b11d4SHarish Chegondi return stream_fd;
9099a0b11d4SHarish Chegondi
9109a0b11d4SHarish Chegondi err_destroy:
9119a0b11d4SHarish Chegondi xe_eu_stall_data_buf_destroy(stream);
9129a0b11d4SHarish Chegondi err_free:
9139a0b11d4SHarish Chegondi xe_eu_stall_stream_free(stream);
9149a0b11d4SHarish Chegondi return ret;
9151537ec85SHarish Chegondi }
9161537ec85SHarish Chegondi
9171537ec85SHarish Chegondi /**
9181537ec85SHarish Chegondi * xe_eu_stall_stream_open - Open a xe EU stall data stream fd
9191537ec85SHarish Chegondi *
9201537ec85SHarish Chegondi * @dev: DRM device pointer
9211537ec85SHarish Chegondi * @data: pointer to first struct @drm_xe_ext_set_property in
9221537ec85SHarish Chegondi * the chain of input properties from the user space.
9231537ec85SHarish Chegondi * @file: DRM file pointer
9241537ec85SHarish Chegondi *
9251537ec85SHarish Chegondi * This function opens a EU stall data stream with input properties from
9261537ec85SHarish Chegondi * the user space.
9271537ec85SHarish Chegondi *
9281537ec85SHarish Chegondi * Returns: EU stall data stream fd on success or a negative error code.
9291537ec85SHarish Chegondi */
xe_eu_stall_stream_open(struct drm_device * dev,u64 data,struct drm_file * file)9301537ec85SHarish Chegondi int xe_eu_stall_stream_open(struct drm_device *dev, u64 data, struct drm_file *file)
9311537ec85SHarish Chegondi {
9321537ec85SHarish Chegondi struct xe_device *xe = to_xe_device(dev);
9331537ec85SHarish Chegondi struct eu_stall_open_properties props = {};
9349a0b11d4SHarish Chegondi int ret;
9351537ec85SHarish Chegondi
936cd5bbb25SHarish Chegondi if (!xe_eu_stall_supported_on_platform(xe)) {
9371537ec85SHarish Chegondi drm_dbg(&xe->drm, "EU stall monitoring is not supported on this platform\n");
9381537ec85SHarish Chegondi return -ENODEV;
9391537ec85SHarish Chegondi }
9401537ec85SHarish Chegondi
9411537ec85SHarish Chegondi if (xe_observation_paranoid && !perfmon_capable()) {
9421537ec85SHarish Chegondi drm_dbg(&xe->drm, "Insufficient privileges for EU stall monitoring\n");
9431537ec85SHarish Chegondi return -EACCES;
9441537ec85SHarish Chegondi }
9451537ec85SHarish Chegondi
9469a0b11d4SHarish Chegondi /* Initialize and set default values */
9479a0b11d4SHarish Chegondi props.wait_num_reports = 1;
9489a0b11d4SHarish Chegondi props.sampling_rate_mult = 4;
9499a0b11d4SHarish Chegondi
9501537ec85SHarish Chegondi ret = xe_eu_stall_user_extensions(xe, data, 0, &props);
9511537ec85SHarish Chegondi if (ret)
9521537ec85SHarish Chegondi return ret;
9531537ec85SHarish Chegondi
9541537ec85SHarish Chegondi if (!props.gt) {
9551537ec85SHarish Chegondi drm_dbg(&xe->drm, "GT ID not provided for EU stall sampling\n");
9561537ec85SHarish Chegondi return -EINVAL;
9571537ec85SHarish Chegondi }
9581537ec85SHarish Chegondi
9599a0b11d4SHarish Chegondi mutex_lock(&props.gt->eu_stall->stream_lock);
9609a0b11d4SHarish Chegondi ret = xe_eu_stall_stream_open_locked(dev, &props, file);
9619a0b11d4SHarish Chegondi mutex_unlock(&props.gt->eu_stall->stream_lock);
9621537ec85SHarish Chegondi
9639a0b11d4SHarish Chegondi return ret;
9641537ec85SHarish Chegondi }
965