| 1 | |
| 2 | |
| 3 | * Copyright © 2022 Intel Corporation
|
| 4 |
|
| 5 |
|
| 6 | #include "xe_debugfs.h"
|
| 7 |
|
| 8 | #include <linux/bits.h>
|
| 9 | #include <linux/debugfs.h>
|
| 10 | #include <linux/fault-inject.h>
|
| 11 | #include <linux/string_helpers.h>
|
| 12 |
|
| 13 | #include <drm/drm_debugfs.h>
|
| 14 |
|
| 15 | #include "regs/xe_pmt.h"
|
| 16 | #include "xe_bo.h"
|
| 17 | #include "xe_device.h"
|
| 18 | #include "xe_force_wake.h"
|
| 19 | #include "xe_gt.h"
|
| 20 | #include "xe_gt_debugfs.h"
|
| 21 | #include "xe_gt_printk.h"
|
| 22 | #include "xe_guc_ads.h"
|
| 23 | #include "xe_hw_engine.h"
|
| 24 | #include "xe_mmio.h"
|
| 25 | #include "xe_pcode.h"
|
| 26 | #include "xe_pm.h"
|
| 27 | #include "xe_psmi.h"
|
| 28 | #include "xe_pxp_debugfs.h"
|
| 29 | #include "xe_sriov.h"
|
| 30 | #include "xe_sriov_pf_debugfs.h"
|
| 31 | #include "xe_sriov_vf.h"
|
| 32 | #include "xe_step.h"
|
| 33 | #include "xe_tile_debugfs.h"
|
| 34 | #include "xe_vsec.h"
|
| 35 | #include "xe_wa.h"
|
| 36 |
|
| 37 | #ifdef CONFIG_DRM_XE_DEBUG
|
| 38 | #include "xe_bo_evict.h"
|
| 39 | #include "xe_migrate.h"
|
| 40 | #include "xe_vm.h"
|
| 41 | #endif
|
| 42 |
|
| 43 | DECLARE_FAULT_ATTR(gt_reset_failure);
|
| 44 | DECLARE_FAULT_ATTR(inject_csc_hw_error);
|
| 45 |
|
| 46 | static bool csc_hw_error_available(struct xe_device *xe)
|
| 47 | {
|
| 48 | return !IS_SRIOV_VF(xe) && xe->info.platform == XE_BATTLEMAGE;
|
| 49 | }
|
| 50 |
|
| 51 | |
| 52 | * Fault injection table. Each entry registers a debugfs attribute; add a
|
| 53 | * matching FAULT_ACTION() below for every entry added here.
|
| 54 |
|
| 55 | static struct {
|
| 56 | const char *name;
|
| 57 | struct fault_attr *attr;
|
| 58 | bool (*is_visible)(struct xe_device *xe);
|
| 59 | } xe_fault_inject_entry[] = {
|
| 60 | { .name = "fail_gt_reset",
|
| 61 | .attr = >_reset_failure },
|
| 62 | { .name = "inject_csc_hw_error",
|
| 63 | .attr = &inject_csc_hw_error,
|
| 64 | .is_visible = csc_hw_error_available },
|
| 65 | };
|
| 66 |
|
| 67 | |
| 68 | * FAULT_ACTION(name, fault_attr) - generate xe_fault_<name>() accessor.
|
| 69 | * Add one entry per row in xe_fault_inject_entry[].
|
| 70 |
|
| 71 | #define FAULT_ACTION(name, fault_attr) \
|
| 72 | bool xe_fault_##name(void) \
|
| 73 | { \
|
| 74 | return should_fail(&(fault_attr), 1); \
|
| 75 | }
|
| 76 |
|
| 77 | FAULT_ACTION(gt_reset, gt_reset_failure)
|
| 78 | FAULT_ACTION(csc_hw_error, inject_csc_hw_error)
|
| 79 |
|
| 80 | static void xe_fault_inject_debugfs_register(struct xe_device *xe,
|
| 81 | struct dentry *root)
|
| 82 | {
|
| 83 | int i;
|
| 84 |
|
| 85 | for (i = 0; i < ARRAY_SIZE(xe_fault_inject_entry); i++) {
|
| 86 | if (xe_fault_inject_entry[i].is_visible &&
|
| 87 | !xe_fault_inject_entry[i].is_visible(xe))
|
| 88 | continue;
|
| 89 |
|
| 90 | fault_create_debugfs_attr(xe_fault_inject_entry[i].name, root,
|
| 91 | xe_fault_inject_entry[i].attr);
|
| 92 | }
|
| 93 | }
|
| 94 |
|
| 95 | static void read_residency_counter(struct xe_device *xe, struct xe_mmio *mmio,
|
| 96 | u32 offset, const char *name, struct drm_printer *p)
|
| 97 | {
|
| 98 | u64 residency = 0;
|
| 99 | int ret;
|
| 100 |
|
| 101 | ret = xe_pmt_telem_read(xe->drm.dev,
|
| 102 | xe_mmio_read32(mmio, PUNIT_TELEMETRY_GUID),
|
| 103 | &residency, offset, sizeof(residency));
|
| 104 | if (ret != sizeof(residency)) {
|
| 105 | drm_warn(&xe->drm, "%s counter failed to read, ret %d\n", name, ret);
|
| 106 | return;
|
| 107 | }
|
| 108 |
|
| 109 | drm_printf(p, "%s : %llu\n", name, residency);
|
| 110 | }
|
| 111 |
|
| 112 | static struct xe_device *node_to_xe(struct drm_info_node *node)
|
| 113 | {
|
| 114 | return to_xe_device(node->minor->dev);
|
| 115 | }
|
| 116 |
|
| 117 | static void print_engine_class_mask(struct drm_printer *p, u16 mask)
|
| 118 | {
|
| 119 | if (!mask) {
|
| 120 | drm_printf(p, " none\n");
|
| 121 | return;
|
| 122 | }
|
| 123 |
|
| 124 | for (enum xe_engine_class ec = 0; ec < XE_ENGINE_CLASS_MAX; ec++) {
|
| 125 | if (mask & BIT(ec))
|
| 126 | drm_printf(p, " %s", xe_hw_engine_class_to_str(ec));
|
| 127 | }
|
| 128 | drm_printf(p, "\n");
|
| 129 | }
|
| 130 |
|
| 131 | static void print_engine_mask(struct drm_printer *p, struct xe_gt *gt, u64 mask)
|
| 132 | {
|
| 133 | struct xe_hw_engine *hwe;
|
| 134 | enum xe_hw_engine_id id;
|
| 135 |
|
| 136 | if (!mask) {
|
| 137 | drm_printf(p, " none\n");
|
| 138 | return;
|
| 139 | }
|
| 140 |
|
| 141 | for_each_hw_engine(hwe, gt, id) {
|
| 142 | if (mask & BIT_ULL(id))
|
| 143 | drm_printf(p, " %s", hwe->name);
|
| 144 | }
|
| 145 | drm_printf(p, "\n");
|
| 146 | }
|
| 147 |
|
| 148 | static int info(struct seq_file *m, void *data)
|
| 149 | {
|
| 150 | struct xe_device *xe = node_to_xe(m->private);
|
| 151 | struct drm_printer p = drm_seq_file_printer(m);
|
| 152 | struct xe_gt *gt;
|
| 153 | u8 id;
|
| 154 |
|
| 155 | guard(xe_pm_runtime)(xe);
|
| 156 |
|
| 157 | drm_printf(&p, "graphics_verx100 %d\n", xe->info.graphics_verx100);
|
| 158 | drm_printf(&p, "media_verx100 %d\n", xe->info.media_verx100);
|
| 159 | drm_printf(&p, "stepping G:%s M:%s B:%s\n",
|
| 160 | xe_step_name(xe->info.step.graphics),
|
| 161 | xe_step_name(xe->info.step.media),
|
| 162 | xe_step_name(xe->info.step.basedie));
|
| 163 | drm_printf(&p, "is_dgfx %s\n", str_yes_no(xe->info.is_dgfx));
|
| 164 | drm_printf(&p, "platform %d\n", xe->info.platform);
|
| 165 | drm_printf(&p, "subplatform %d\n",
|
| 166 | xe->info.subplatform > XE_SUBPLATFORM_NONE ? xe->info.subplatform : 0);
|
| 167 | drm_printf(&p, "devid 0x%x\n", xe->info.devid);
|
| 168 | drm_printf(&p, "revid %d\n", xe->info.revid);
|
| 169 | drm_printf(&p, "tile_count %d\n", xe->info.tile_count);
|
| 170 | drm_printf(&p, "vm_max_level %d\n", xe->info.vm_max_level);
|
| 171 | drm_printf(&p, "has_flat_ccs %s\n", str_yes_no(xe->info.has_flat_ccs));
|
| 172 | drm_printf(&p, "has_usm %s\n", str_yes_no(xe->info.has_usm));
|
| 173 | drm_printf(&p, "skip_guc_pc %s\n", str_yes_no(xe->info.skip_guc_pc));
|
| 174 | drm_printf(&p, "multi_lrc_engine_classes");
|
| 175 | print_engine_class_mask(&p, xe->info.multi_lrc_mask);
|
| 176 | for_each_gt(gt, xe, id) {
|
| 177 | drm_printf(&p, "gt%d force wake %d\n", id,
|
| 178 | xe_force_wake_ref(gt_to_fw(gt), XE_FW_GT));
|
| 179 | drm_printf(&p, "gt%d engines", id);
|
| 180 | print_engine_mask(&p, gt, gt->info.engine_mask);
|
| 181 | drm_printf(&p, "gt%d multi_queue_engine_classes", id);
|
| 182 | print_engine_class_mask(&p, gt->info.multi_queue_engine_class_mask);
|
| 183 | }
|
| 184 |
|
| 185 | return 0;
|
| 186 | }
|
| 187 |
|
| 188 | static int sriov_info(struct seq_file *m, void *data)
|
| 189 | {
|
| 190 | struct xe_device *xe = node_to_xe(m->private);
|
| 191 | struct drm_printer p = drm_seq_file_printer(m);
|
| 192 |
|
| 193 | xe_sriov_print_info(xe, &p);
|
| 194 | return 0;
|
| 195 | }
|
| 196 |
|
| 197 | static int workarounds(struct xe_device *xe, struct drm_printer *p)
|
| 198 | {
|
| 199 | guard(xe_pm_runtime)(xe);
|
| 200 | xe_wa_device_dump(xe, p);
|
| 201 |
|
| 202 | return 0;
|
| 203 | }
|
| 204 |
|
| 205 | static int workaround_info(struct seq_file *m, void *data)
|
| 206 | {
|
| 207 | struct xe_device *xe = node_to_xe(m->private);
|
| 208 | struct drm_printer p = drm_seq_file_printer(m);
|
| 209 |
|
| 210 | workarounds(xe, &p);
|
| 211 | return 0;
|
| 212 | }
|
| 213 |
|
| 214 | static int pcode_info(struct seq_file *m, void *data)
|
| 215 | {
|
| 216 | struct xe_device *xe = node_to_xe(m->private);
|
| 217 | struct drm_printer p = drm_seq_file_printer(m);
|
| 218 | struct xe_pcode_version version;
|
| 219 | int ret = 0;
|
| 220 |
|
| 221 | ret = xe_get_pcode_version(xe, &version);
|
| 222 | if (ret)
|
| 223 | return ret;
|
| 224 |
|
| 225 | drm_printf(&p, "pcode version: %u.%u.%u\n", version.major,
|
| 226 | version.minor, version.engg);
|
| 227 | return 0;
|
| 228 | }
|
| 229 |
|
| 230 | static int dgfx_pkg_residencies_show(struct seq_file *m, void *data)
|
| 231 | {
|
| 232 | struct xe_device *xe;
|
| 233 | struct xe_mmio *mmio;
|
| 234 | struct drm_printer p;
|
| 235 |
|
| 236 | xe = node_to_xe(m->private);
|
| 237 | p = drm_seq_file_printer(m);
|
| 238 | guard(xe_pm_runtime)(xe);
|
| 239 | mmio = xe_root_tile_mmio(xe);
|
| 240 | static const struct {
|
| 241 | u32 offset;
|
| 242 | const char *name;
|
| 243 | } residencies[] = {
|
| 244 | {BMG_G2_RESIDENCY_OFFSET, "Package G2"},
|
| 245 | {BMG_G6_RESIDENCY_OFFSET, "Package G6"},
|
| 246 | {BMG_G7_RESIDENCY_OFFSET, "Package G7"},
|
| 247 | {BMG_G8_RESIDENCY_OFFSET, "Package G8"},
|
| 248 | {BMG_G10_RESIDENCY_OFFSET, "Package G10"},
|
| 249 | {BMG_MODS_RESIDENCY_OFFSET, "Package ModS"}
|
| 250 | };
|
| 251 |
|
| 252 | for (int i = 0; i < ARRAY_SIZE(residencies); i++)
|
| 253 | read_residency_counter(xe, mmio, residencies[i].offset, residencies[i].name, &p);
|
| 254 |
|
| 255 | return 0;
|
| 256 | }
|
| 257 |
|
| 258 | static int dgfx_pcie_link_residencies_show(struct seq_file *m, void *data)
|
| 259 | {
|
| 260 | struct xe_device *xe;
|
| 261 | struct xe_mmio *mmio;
|
| 262 | struct drm_printer p;
|
| 263 |
|
| 264 | xe = node_to_xe(m->private);
|
| 265 | p = drm_seq_file_printer(m);
|
| 266 | guard(xe_pm_runtime)(xe);
|
| 267 | mmio = xe_root_tile_mmio(xe);
|
| 268 |
|
| 269 | static const struct {
|
| 270 | u32 offset;
|
| 271 | const char *name;
|
| 272 | } residencies[] = {
|
| 273 | {BMG_PCIE_LINK_L0_RESIDENCY_OFFSET, "PCIE LINK L0 RESIDENCY"},
|
| 274 | {BMG_PCIE_LINK_L1_RESIDENCY_OFFSET, "PCIE LINK L1 RESIDENCY"},
|
| 275 | {BMG_PCIE_LINK_L1_2_RESIDENCY_OFFSET, "PCIE LINK L1.2 RESIDENCY"}
|
| 276 | };
|
| 277 |
|
| 278 | for (int i = 0; i < ARRAY_SIZE(residencies); i++)
|
| 279 | read_residency_counter(xe, mmio, residencies[i].offset, residencies[i].name, &p);
|
| 280 |
|
| 281 | return 0;
|
| 282 | }
|
| 283 |
|
| 284 | static const struct drm_info_list debugfs_list[] = {
|
| 285 | {"info", info, 0},
|
| 286 | { .name = "sriov_info", .show = sriov_info, },
|
| 287 | { .name = "workarounds", .show = workaround_info, },
|
| 288 | };
|
| 289 |
|
| 290 | static const struct drm_info_list pcode_info_debugfs[] = {
|
| 291 | { .name = "pcode_info", .show = pcode_info, },
|
| 292 | };
|
| 293 |
|
| 294 | static const struct drm_info_list debugfs_residencies[] = {
|
| 295 | { .name = "dgfx_pkg_residencies", .show = dgfx_pkg_residencies_show, },
|
| 296 | { .name = "dgfx_pcie_link_residencies", .show = dgfx_pcie_link_residencies_show, },
|
| 297 | };
|
| 298 |
|
| 299 | static int forcewake_open(struct inode *inode, struct file *file)
|
| 300 | {
|
| 301 | struct xe_device *xe = inode->i_private;
|
| 302 | struct xe_gt *gt;
|
| 303 | u8 id, last_gt;
|
| 304 | unsigned int fw_ref;
|
| 305 |
|
| 306 | xe_pm_runtime_get(xe);
|
| 307 | for_each_gt(gt, xe, id) {
|
| 308 | last_gt = id;
|
| 309 |
|
| 310 | fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
| 311 | if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL))
|
| 312 | goto err_fw_get;
|
| 313 | }
|
| 314 |
|
| 315 | return 0;
|
| 316 |
|
| 317 | err_fw_get:
|
| 318 | for_each_gt(gt, xe, id) {
|
| 319 | if (id < last_gt)
|
| 320 | xe_force_wake_put(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
| 321 | else if (id == last_gt)
|
| 322 | xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
| 323 | else
|
| 324 | break;
|
| 325 | }
|
| 326 |
|
| 327 | xe_pm_runtime_put(xe);
|
| 328 | return -ETIMEDOUT;
|
| 329 | }
|
| 330 |
|
| 331 | static int forcewake_release(struct inode *inode, struct file *file)
|
| 332 | {
|
| 333 | struct xe_device *xe = inode->i_private;
|
| 334 | struct xe_gt *gt;
|
| 335 | u8 id;
|
| 336 |
|
| 337 | for_each_gt(gt, xe, id)
|
| 338 | xe_force_wake_put(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
| 339 | xe_pm_runtime_put(xe);
|
| 340 |
|
| 341 | return 0;
|
| 342 | }
|
| 343 |
|
| 344 | static const struct file_operations forcewake_all_fops = {
|
| 345 | .owner = THIS_MODULE,
|
| 346 | .open = forcewake_open,
|
| 347 | .release = forcewake_release,
|
| 348 | };
|
| 349 |
|
| 350 | static ssize_t wedged_mode_show(struct file *f, char __user *ubuf,
|
| 351 | size_t size, loff_t *pos)
|
| 352 | {
|
| 353 | struct xe_device *xe = file_inode(f)->i_private;
|
| 354 | char buf[32];
|
| 355 | int len = 0;
|
| 356 |
|
| 357 | len = scnprintf(buf, sizeof(buf), "%d\n", xe->wedged.mode);
|
| 358 |
|
| 359 | return simple_read_from_buffer(ubuf, size, pos, buf, len);
|
| 360 | }
|
| 361 |
|
| 362 | static int __wedged_mode_set_reset_policy(struct xe_gt *gt, enum xe_wedged_mode mode)
|
| 363 | {
|
| 364 | bool enable_engine_reset;
|
| 365 | int ret;
|
| 366 |
|
| 367 | enable_engine_reset = (mode != XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET);
|
| 368 | ret = xe_guc_ads_scheduler_policy_toggle_reset(>->uc.guc.ads,
|
| 369 | enable_engine_reset);
|
| 370 | if (ret)
|
| 371 | xe_gt_err(gt, "Failed to update GuC ADS scheduler policy (%pe)\n", ERR_PTR(ret));
|
| 372 |
|
| 373 | return ret;
|
| 374 | }
|
| 375 |
|
| 376 | static int wedged_mode_set_reset_policy(struct xe_device *xe, enum xe_wedged_mode mode)
|
| 377 | {
|
| 378 | struct xe_gt *gt;
|
| 379 | int ret;
|
| 380 | u8 id;
|
| 381 |
|
| 382 | guard(xe_pm_runtime)(xe);
|
| 383 | for_each_gt(gt, xe, id) {
|
| 384 | ret = __wedged_mode_set_reset_policy(gt, mode);
|
| 385 | if (ret) {
|
| 386 | if (id > 0) {
|
| 387 | xe->wedged.inconsistent_reset = true;
|
| 388 | drm_err(&xe->drm, "Inconsistent reset policy state between GTs\n");
|
| 389 | }
|
| 390 | return ret;
|
| 391 | }
|
| 392 | }
|
| 393 |
|
| 394 | xe->wedged.inconsistent_reset = false;
|
| 395 |
|
| 396 | return 0;
|
| 397 | }
|
| 398 |
|
| 399 | static bool wedged_mode_needs_policy_update(struct xe_device *xe, enum xe_wedged_mode mode)
|
| 400 | {
|
| 401 | if (xe->wedged.inconsistent_reset)
|
| 402 | return true;
|
| 403 |
|
| 404 | if (xe->wedged.mode == mode)
|
| 405 | return false;
|
| 406 |
|
| 407 | if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET ||
|
| 408 | mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)
|
| 409 | return true;
|
| 410 |
|
| 411 | return false;
|
| 412 | }
|
| 413 |
|
| 414 | static ssize_t wedged_mode_set(struct file *f, const char __user *ubuf,
|
| 415 | size_t size, loff_t *pos)
|
| 416 | {
|
| 417 | struct xe_device *xe = file_inode(f)->i_private;
|
| 418 | u32 wedged_mode;
|
| 419 | ssize_t ret;
|
| 420 |
|
| 421 | ret = kstrtouint_from_user(ubuf, size, 0, &wedged_mode);
|
| 422 | if (ret)
|
| 423 | return ret;
|
| 424 |
|
| 425 | ret = xe_device_validate_wedged_mode(xe, wedged_mode);
|
| 426 | if (ret)
|
| 427 | return ret;
|
| 428 |
|
| 429 | if (wedged_mode_needs_policy_update(xe, wedged_mode)) {
|
| 430 | ret = wedged_mode_set_reset_policy(xe, wedged_mode);
|
| 431 | if (ret)
|
| 432 | return ret;
|
| 433 | }
|
| 434 |
|
| 435 | xe->wedged.mode = wedged_mode;
|
| 436 |
|
| 437 | return size;
|
| 438 | }
|
| 439 |
|
| 440 | static const struct file_operations wedged_mode_fops = {
|
| 441 | .owner = THIS_MODULE,
|
| 442 | .read = wedged_mode_show,
|
| 443 | .write = wedged_mode_set,
|
| 444 | };
|
| 445 |
|
| 446 | static ssize_t page_reclaim_hw_assist_show(struct file *f, char __user *ubuf,
|
| 447 | size_t size, loff_t *pos)
|
| 448 | {
|
| 449 | struct xe_device *xe = file_inode(f)->i_private;
|
| 450 | char buf[8];
|
| 451 | int len;
|
| 452 |
|
| 453 | len = scnprintf(buf, sizeof(buf), "%d\n", xe->info.has_page_reclaim_hw_assist);
|
| 454 | return simple_read_from_buffer(ubuf, size, pos, buf, len);
|
| 455 | }
|
| 456 |
|
| 457 | static ssize_t page_reclaim_hw_assist_set(struct file *f, const char __user *ubuf,
|
| 458 | size_t size, loff_t *pos)
|
| 459 | {
|
| 460 | struct xe_device *xe = file_inode(f)->i_private;
|
| 461 | bool val;
|
| 462 | ssize_t ret;
|
| 463 |
|
| 464 | ret = kstrtobool_from_user(ubuf, size, &val);
|
| 465 | if (ret)
|
| 466 | return ret;
|
| 467 |
|
| 468 | xe->info.has_page_reclaim_hw_assist = val;
|
| 469 |
|
| 470 | return size;
|
| 471 | }
|
| 472 |
|
| 473 | static const struct file_operations page_reclaim_hw_assist_fops = {
|
| 474 | .owner = THIS_MODULE,
|
| 475 | .read = page_reclaim_hw_assist_show,
|
| 476 | .write = page_reclaim_hw_assist_set,
|
| 477 | };
|
| 478 |
|
| 479 | static ssize_t atomic_svm_timeslice_ms_show(struct file *f, char __user *ubuf,
|
| 480 | size_t size, loff_t *pos)
|
| 481 | {
|
| 482 | struct xe_device *xe = file_inode(f)->i_private;
|
| 483 | char buf[32];
|
| 484 | int len = 0;
|
| 485 |
|
| 486 | len = scnprintf(buf, sizeof(buf), "%d\n", xe->atomic_svm_timeslice_ms);
|
| 487 |
|
| 488 | return simple_read_from_buffer(ubuf, size, pos, buf, len);
|
| 489 | }
|
| 490 |
|
| 491 | static ssize_t atomic_svm_timeslice_ms_set(struct file *f,
|
| 492 | const char __user *ubuf,
|
| 493 | size_t size, loff_t *pos)
|
| 494 | {
|
| 495 | struct xe_device *xe = file_inode(f)->i_private;
|
| 496 | u32 atomic_svm_timeslice_ms;
|
| 497 | ssize_t ret;
|
| 498 |
|
| 499 | ret = kstrtouint_from_user(ubuf, size, 0, &atomic_svm_timeslice_ms);
|
| 500 | if (ret)
|
| 501 | return ret;
|
| 502 |
|
| 503 | xe->atomic_svm_timeslice_ms = atomic_svm_timeslice_ms;
|
| 504 |
|
| 505 | return size;
|
| 506 | }
|
| 507 |
|
| 508 | static const struct file_operations atomic_svm_timeslice_ms_fops = {
|
| 509 | .owner = THIS_MODULE,
|
| 510 | .read = atomic_svm_timeslice_ms_show,
|
| 511 | .write = atomic_svm_timeslice_ms_set,
|
| 512 | };
|
| 513 |
|
| 514 | static ssize_t min_run_period_lr_ms_show(struct file *f, char __user *ubuf,
|
| 515 | size_t size, loff_t *pos)
|
| 516 | {
|
| 517 | struct xe_device *xe = file_inode(f)->i_private;
|
| 518 | char buf[32];
|
| 519 | int len = 0;
|
| 520 |
|
| 521 | len = scnprintf(buf, sizeof(buf), "%d\n", xe->min_run_period_lr_ms);
|
| 522 |
|
| 523 | return simple_read_from_buffer(ubuf, size, pos, buf, len);
|
| 524 | }
|
| 525 |
|
| 526 | static ssize_t min_run_period_lr_ms_set(struct file *f, const char __user *ubuf,
|
| 527 | size_t size, loff_t *pos)
|
| 528 | {
|
| 529 | struct xe_device *xe = file_inode(f)->i_private;
|
| 530 | u32 min_run_period_lr_ms;
|
| 531 | ssize_t ret;
|
| 532 |
|
| 533 | ret = kstrtouint_from_user(ubuf, size, 0, &min_run_period_lr_ms);
|
| 534 | if (ret)
|
| 535 | return ret;
|
| 536 |
|
| 537 | xe->min_run_period_lr_ms = min_run_period_lr_ms;
|
| 538 |
|
| 539 | return size;
|
| 540 | }
|
| 541 |
|
| 542 | static const struct file_operations min_run_period_lr_ms_fops = {
|
| 543 | .owner = THIS_MODULE,
|
| 544 | .read = min_run_period_lr_ms_show,
|
| 545 | .write = min_run_period_lr_ms_set,
|
| 546 | };
|
| 547 |
|
| 548 | static ssize_t min_run_period_pf_ms_show(struct file *f, char __user *ubuf,
|
| 549 | size_t size, loff_t *pos)
|
| 550 | {
|
| 551 | struct xe_device *xe = file_inode(f)->i_private;
|
| 552 | char buf[32];
|
| 553 | int len = 0;
|
| 554 |
|
| 555 | len = scnprintf(buf, sizeof(buf), "%d\n", xe->min_run_period_pf_ms);
|
| 556 |
|
| 557 | return simple_read_from_buffer(ubuf, size, pos, buf, len);
|
| 558 | }
|
| 559 |
|
| 560 | static ssize_t min_run_period_pf_ms_set(struct file *f, const char __user *ubuf,
|
| 561 | size_t size, loff_t *pos)
|
| 562 | {
|
| 563 | struct xe_device *xe = file_inode(f)->i_private;
|
| 564 | u32 min_run_period_pf_ms;
|
| 565 | ssize_t ret;
|
| 566 |
|
| 567 | ret = kstrtouint_from_user(ubuf, size, 0, &min_run_period_pf_ms);
|
| 568 | if (ret)
|
| 569 | return ret;
|
| 570 |
|
| 571 | xe->min_run_period_pf_ms = min_run_period_pf_ms;
|
| 572 |
|
| 573 | return size;
|
| 574 | }
|
| 575 |
|
| 576 | static const struct file_operations min_run_period_pf_ms_fops = {
|
| 577 | .owner = THIS_MODULE,
|
| 578 | .read = min_run_period_pf_ms_show,
|
| 579 | .write = min_run_period_pf_ms_set,
|
| 580 | };
|
| 581 |
|
| 582 | static ssize_t disable_late_binding_show(struct file *f, char __user *ubuf,
|
| 583 | size_t size, loff_t *pos)
|
| 584 | {
|
| 585 | struct xe_device *xe = file_inode(f)->i_private;
|
| 586 | struct xe_late_bind *late_bind = &xe->late_bind;
|
| 587 | char buf[32];
|
| 588 | int len;
|
| 589 |
|
| 590 | len = scnprintf(buf, sizeof(buf), "%d\n", late_bind->disable);
|
| 591 |
|
| 592 | return simple_read_from_buffer(ubuf, size, pos, buf, len);
|
| 593 | }
|
| 594 |
|
| 595 | static ssize_t disable_late_binding_set(struct file *f, const char __user *ubuf,
|
| 596 | size_t size, loff_t *pos)
|
| 597 | {
|
| 598 | struct xe_device *xe = file_inode(f)->i_private;
|
| 599 | struct xe_late_bind *late_bind = &xe->late_bind;
|
| 600 | bool val;
|
| 601 | int ret;
|
| 602 |
|
| 603 | ret = kstrtobool_from_user(ubuf, size, &val);
|
| 604 | if (ret)
|
| 605 | return ret;
|
| 606 |
|
| 607 | late_bind->disable = val;
|
| 608 | return size;
|
| 609 | }
|
| 610 |
|
| 611 | static const struct file_operations disable_late_binding_fops = {
|
| 612 | .owner = THIS_MODULE,
|
| 613 | .read = disable_late_binding_show,
|
| 614 | .write = disable_late_binding_set,
|
| 615 | };
|
| 616 |
|
| 617 | #ifdef CONFIG_DRM_XE_DEBUG_PAGE_SIZE
|
| 618 | static const char * const page_size_alloc_mode_names[] = {
|
| 619 | [XE_PAGE_SIZE_ALLOC_CTRL_MODE_NONE] = "none",
|
| 620 | [XE_PAGE_SIZE_ALLOC_CTRL_MODE_ONLY_2M] = "only_2m",
|
| 621 | [XE_PAGE_SIZE_ALLOC_CTRL_MODE_ONLY_1G] = "only_1g",
|
| 622 | [XE_PAGE_SIZE_ALLOC_CTRL_MODE_MIXED] = "mixed",
|
| 623 | };
|
| 624 |
|
| 625 | static ssize_t page_size_alloc_mode_show(struct file *f, char __user *ubuf,
|
| 626 | size_t size, loff_t *pos)
|
| 627 | {
|
| 628 | struct xe_device *xe = file_inode(f)->i_private;
|
| 629 | char buf[32];
|
| 630 | int len;
|
| 631 | enum xe_page_size_alloc_ctrl_mode mode;
|
| 632 |
|
| 633 | mode = READ_ONCE(xe->page_size_alloc_ctrl.mode);
|
| 634 | if (mode >= ARRAY_SIZE(page_size_alloc_mode_names) ||
|
| 635 | !page_size_alloc_mode_names[mode])
|
| 636 | len = scnprintf(buf, sizeof(buf), "unknown\n");
|
| 637 | else
|
| 638 | len = scnprintf(buf, sizeof(buf), "%s\n",
|
| 639 | page_size_alloc_mode_names[mode]);
|
| 640 | return simple_read_from_buffer(ubuf, size, pos, buf, len);
|
| 641 | }
|
| 642 |
|
| 643 | static ssize_t page_size_alloc_mode_set(struct file *f, const char __user *ubuf,
|
| 644 | size_t size, loff_t *pos)
|
| 645 | {
|
| 646 | struct xe_device *xe = file_inode(f)->i_private;
|
| 647 | int ret;
|
| 648 | char buf[32];
|
| 649 | int mode;
|
| 650 |
|
| 651 | if (*pos)
|
| 652 | return -ESPIPE;
|
| 653 |
|
| 654 | if (size > sizeof(buf) - 1)
|
| 655 | return -EINVAL;
|
| 656 |
|
| 657 | ret = simple_write_to_buffer(buf, sizeof(buf) - 1, pos, ubuf, size);
|
| 658 | if (ret < 0)
|
| 659 | return ret;
|
| 660 | buf[ret] = '\0';
|
| 661 |
|
| 662 | mode = sysfs_match_string(page_size_alloc_mode_names, buf);
|
| 663 | if (mode < 0)
|
| 664 | return mode;
|
| 665 |
|
| 666 | mutex_lock(&xe->page_size_alloc_ctrl.lock);
|
| 667 | if (mode == XE_PAGE_SIZE_ALLOC_CTRL_MODE_MIXED)
|
| 668 | xe->page_size_alloc_ctrl.cur_index = 0;
|
| 669 | WRITE_ONCE(xe->page_size_alloc_ctrl.mode,
|
| 670 | (enum xe_page_size_alloc_ctrl_mode)mode);
|
| 671 | mutex_unlock(&xe->page_size_alloc_ctrl.lock);
|
| 672 |
|
| 673 | return size;
|
| 674 | }
|
| 675 |
|
| 676 | static const struct file_operations page_size_alloc_mode_fops = {
|
| 677 | .owner = THIS_MODULE,
|
| 678 | .read = page_size_alloc_mode_show,
|
| 679 | .write = page_size_alloc_mode_set,
|
| 680 | };
|
| 681 | #endif
|
| 682 |
|
| 683 | void xe_debugfs_register(struct xe_device *xe)
|
| 684 | {
|
| 685 | struct ttm_device *bdev = &xe->ttm;
|
| 686 | struct drm_minor *minor = xe->drm.primary;
|
| 687 | struct dentry *root = minor->debugfs_root;
|
| 688 | struct ttm_resource_manager *man;
|
| 689 | struct xe_tile *tile;
|
| 690 | struct xe_gt *gt;
|
| 691 | u8 tile_id;
|
| 692 | u8 id;
|
| 693 |
|
| 694 | drm_debugfs_create_files(debugfs_list,
|
| 695 | ARRAY_SIZE(debugfs_list),
|
| 696 | root, minor);
|
| 697 |
|
| 698 | if (xe->info.platform == XE_BATTLEMAGE && !IS_SRIOV_VF(xe)) {
|
| 699 | drm_debugfs_create_files(debugfs_residencies,
|
| 700 | ARRAY_SIZE(debugfs_residencies),
|
| 701 | root, minor);
|
| 702 | }
|
| 703 |
|
| 704 | |
| 705 | * Pcode version read from PMT is currently only supported on CRI and BMG platforms in PF
|
| 706 | * mode, as both platforms support the necessary telemetry read mechanism and have a fixed
|
| 707 | * PUNIT_VERSION_OFFSET.
|
| 708 | * Attempting this access on other platforms must be verified before enabling support.
|
| 709 |
|
| 710 | if (!IS_SRIOV_VF(xe) &&
|
| 711 | (xe->info.platform == XE_CRESCENTISLAND || xe->info.platform == XE_BATTLEMAGE))
|
| 712 | drm_debugfs_create_files(pcode_info_debugfs,
|
| 713 | ARRAY_SIZE(pcode_info_debugfs),
|
| 714 | root, minor);
|
| 715 |
|
| 716 | debugfs_create_file("forcewake_all", 0400, root, xe,
|
| 717 | &forcewake_all_fops);
|
| 718 |
|
| 719 | debugfs_create_file("wedged_mode", 0600, root, xe,
|
| 720 | &wedged_mode_fops);
|
| 721 |
|
| 722 | debugfs_create_file("atomic_svm_timeslice_ms", 0600, root, xe,
|
| 723 | &atomic_svm_timeslice_ms_fops);
|
| 724 |
|
| 725 | debugfs_create_file("min_run_period_lr_ms", 0600, root, xe,
|
| 726 | &min_run_period_lr_ms_fops);
|
| 727 |
|
| 728 | debugfs_create_file("min_run_period_pf_ms", 0600, root, xe,
|
| 729 | &min_run_period_pf_ms_fops);
|
| 730 |
|
| 731 | debugfs_create_file("disable_late_binding", 0600, root, xe,
|
| 732 | &disable_late_binding_fops);
|
| 733 |
|
| 734 | #ifdef CONFIG_DRM_XE_DEBUG_PAGE_SIZE
|
| 735 | |
| 736 | * Expose a debugfs knob to control user BO page-size allocation:
|
| 737 | * "none" - default behavior
|
| 738 | * "only_2m" - force 2M page allocations
|
| 739 | * "only_1g" - force 1G page allocations
|
| 740 | * "mixed" - select 4K, 64K, 2M, and 1G in round-robin order
|
| 741 |
|
| 742 | if (xe_debug_page_size_supported(xe))
|
| 743 | debugfs_create_file("page_size_alloc_mode", 0600, root, xe,
|
| 744 | &page_size_alloc_mode_fops);
|
| 745 | #endif
|
| 746 | |
| 747 | * Don't expose page reclaim configuration file if not supported by the
|
| 748 | * hardware initially.
|
| 749 |
|
| 750 | if (xe->info.has_page_reclaim_hw_assist)
|
| 751 | debugfs_create_file("page_reclaim_hw_assist", 0600, root, xe,
|
| 752 | &page_reclaim_hw_assist_fops);
|
| 753 |
|
| 754 | man = ttm_manager_type(bdev, XE_PL_TT);
|
| 755 | ttm_resource_manager_create_debugfs(man, root, "gtt_mm");
|
| 756 |
|
| 757 | man = ttm_manager_type(bdev, XE_PL_STOLEN);
|
| 758 | if (man)
|
| 759 | ttm_resource_manager_create_debugfs(man, root, "stolen_mm");
|
| 760 |
|
| 761 | for_each_tile(tile, xe, tile_id)
|
| 762 | xe_tile_debugfs_register(tile);
|
| 763 |
|
| 764 | for_each_gt(gt, xe, id)
|
| 765 | xe_gt_debugfs_register(gt);
|
| 766 |
|
| 767 | xe_pxp_debugfs_register(xe->pxp);
|
| 768 |
|
| 769 | xe_psmi_debugfs_register(xe);
|
| 770 |
|
| 771 | xe_fault_inject_debugfs_register(xe, root);
|
| 772 |
|
| 773 | if (IS_SRIOV_PF(xe))
|
| 774 | xe_sriov_pf_debugfs_register(xe, root);
|
| 775 | else if (IS_SRIOV_VF(xe))
|
| 776 | xe_sriov_vf_debugfs_register(xe, root);
|
| 777 | }
|