1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2023-2024 Intel Corporation 4 */ 5 6 #include <linux/debugfs.h> 7 8 #include <drm/drm_print.h> 9 #include <drm/drm_debugfs.h> 10 11 #include "xe_bo.h" 12 #include "xe_debugfs.h" 13 #include "xe_device.h" 14 #include "xe_gt.h" 15 #include "xe_gt_debugfs.h" 16 #include "xe_gt_sriov_pf_config.h" 17 #include "xe_gt_sriov_pf_control.h" 18 #include "xe_gt_sriov_pf_debugfs.h" 19 #include "xe_gt_sriov_pf_helpers.h" 20 #include "xe_gt_sriov_pf_migration.h" 21 #include "xe_gt_sriov_pf_monitor.h" 22 #include "xe_gt_sriov_pf_policy.h" 23 #include "xe_gt_sriov_pf_service.h" 24 #include "xe_pm.h" 25 26 /* 27 * /sys/kernel/debug/dri/0/ 28 * ├── gt0 # d_inode->i_private = gt 29 * │ ├── pf # d_inode->i_private = gt 30 * │ ├── vf1 # d_inode->i_private = VFID(1) 31 * : : 32 * │ ├── vfN # d_inode->i_private = VFID(N) 33 */ 34 35 static void *extract_priv(struct dentry *d) 36 { 37 return d->d_inode->i_private; 38 } 39 40 static struct xe_gt *extract_gt(struct dentry *d) 41 { 42 return extract_priv(d->d_parent); 43 } 44 45 static unsigned int extract_vfid(struct dentry *d) 46 { 47 return extract_priv(d) == extract_gt(d) ? PFID : (uintptr_t)extract_priv(d); 48 } 49 50 /* 51 * /sys/kernel/debug/dri/0/ 52 * ├── gt0 53 * │ ├── pf 54 * │ │ ├── contexts_provisioned 55 * │ │ ├── doorbells_provisioned 56 * │ │ ├── runtime_registers 57 * │ │ ├── negotiated_versions 58 * │ │ ├── adverse_events 59 * ├── gt1 60 * │ ├── pf 61 * │ │ ├── ... 62 */ 63 64 static const struct drm_info_list pf_info[] = { 65 { 66 "contexts_provisioned", 67 .show = xe_gt_debugfs_simple_show, 68 .data = xe_gt_sriov_pf_config_print_ctxs, 69 }, 70 { 71 "doorbells_provisioned", 72 .show = xe_gt_debugfs_simple_show, 73 .data = xe_gt_sriov_pf_config_print_dbs, 74 }, 75 { 76 "runtime_registers", 77 .show = xe_gt_debugfs_simple_show, 78 .data = xe_gt_sriov_pf_service_print_runtime, 79 }, 80 { 81 "adverse_events", 82 .show = xe_gt_debugfs_simple_show, 83 .data = xe_gt_sriov_pf_monitor_print_events, 84 }, 85 }; 86 87 /* 88 * /sys/kernel/debug/dri/0/ 89 * ├── gt0 90 * │ ├── pf 91 * │ │ ├── ggtt_available 92 * │ │ ├── ggtt_provisioned 93 */ 94 95 static const struct drm_info_list pf_ggtt_info[] = { 96 { 97 "ggtt_available", 98 .show = xe_gt_debugfs_simple_show, 99 .data = xe_gt_sriov_pf_config_print_available_ggtt, 100 }, 101 { 102 "ggtt_provisioned", 103 .show = xe_gt_debugfs_simple_show, 104 .data = xe_gt_sriov_pf_config_print_ggtt, 105 }, 106 }; 107 108 /* 109 * /sys/kernel/debug/dri/0/ 110 * ├── gt0 111 * │ ├── pf 112 * │ │ ├── lmem_provisioned 113 */ 114 115 static const struct drm_info_list pf_lmem_info[] = { 116 { 117 "lmem_provisioned", 118 .show = xe_gt_debugfs_simple_show, 119 .data = xe_gt_sriov_pf_config_print_lmem, 120 }, 121 }; 122 123 /* 124 * /sys/kernel/debug/dri/0/ 125 * ├── gt0 126 * │ ├── pf 127 * │ │ ├── reset_engine 128 * │ │ ├── sample_period 129 * │ │ ├── sched_if_idle 130 */ 131 132 #define DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(POLICY, TYPE, FORMAT) \ 133 \ 134 static int POLICY##_set(void *data, u64 val) \ 135 { \ 136 struct xe_gt *gt = extract_gt(data); \ 137 struct xe_device *xe = gt_to_xe(gt); \ 138 int err; \ 139 \ 140 if (val > (TYPE)~0ull) \ 141 return -EOVERFLOW; \ 142 \ 143 xe_pm_runtime_get(xe); \ 144 err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val); \ 145 xe_pm_runtime_put(xe); \ 146 \ 147 return err; \ 148 } \ 149 \ 150 static int POLICY##_get(void *data, u64 *val) \ 151 { \ 152 struct xe_gt *gt = extract_gt(data); \ 153 \ 154 *val = xe_gt_sriov_pf_policy_get_##POLICY(gt); \ 155 return 0; \ 156 } \ 157 \ 158 DEFINE_DEBUGFS_ATTRIBUTE(POLICY##_fops, POLICY##_get, POLICY##_set, FORMAT) 159 160 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(reset_engine, bool, "%llu\n"); 161 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sched_if_idle, bool, "%llu\n"); 162 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sample_period, u32, "%llu\n"); 163 164 static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent) 165 { 166 xe_gt_assert(gt, gt == extract_gt(parent)); 167 xe_gt_assert(gt, PFID == extract_vfid(parent)); 168 169 debugfs_create_file_unsafe("reset_engine", 0644, parent, parent, &reset_engine_fops); 170 debugfs_create_file_unsafe("sched_if_idle", 0644, parent, parent, &sched_if_idle_fops); 171 debugfs_create_file_unsafe("sample_period_ms", 0644, parent, parent, &sample_period_fops); 172 } 173 174 /* 175 * /sys/kernel/debug/dri/0/ 176 * ├── gt0 177 * │ ├── pf 178 * │ │ ├── ggtt_spare 179 * │ │ ├── lmem_spare 180 * │ │ ├── doorbells_spare 181 * │ │ ├── contexts_spare 182 * │ │ ├── exec_quantum_ms 183 * │ │ ├── preempt_timeout_us 184 * │ │ ├── sched_priority 185 * │ ├── vf1 186 * │ │ ├── ggtt_quota 187 * │ │ ├── lmem_quota 188 * │ │ ├── doorbells_quota 189 * │ │ ├── contexts_quota 190 * │ │ ├── exec_quantum_ms 191 * │ │ ├── preempt_timeout_us 192 * │ │ ├── sched_priority 193 */ 194 195 #define DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(CONFIG, TYPE, FORMAT) \ 196 \ 197 static int CONFIG##_set(void *data, u64 val) \ 198 { \ 199 struct xe_gt *gt = extract_gt(data); \ 200 unsigned int vfid = extract_vfid(data); \ 201 struct xe_device *xe = gt_to_xe(gt); \ 202 int err; \ 203 \ 204 if (val > (TYPE)~0ull) \ 205 return -EOVERFLOW; \ 206 \ 207 xe_pm_runtime_get(xe); \ 208 err = xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \ 209 xe_pm_runtime_put(xe); \ 210 \ 211 return err; \ 212 } \ 213 \ 214 static int CONFIG##_get(void *data, u64 *val) \ 215 { \ 216 struct xe_gt *gt = extract_gt(data); \ 217 unsigned int vfid = extract_vfid(data); \ 218 \ 219 *val = xe_gt_sriov_pf_config_get_##CONFIG(gt, vfid); \ 220 return 0; \ 221 } \ 222 \ 223 DEFINE_DEBUGFS_ATTRIBUTE(CONFIG##_fops, CONFIG##_get, CONFIG##_set, FORMAT) 224 225 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ggtt, u64, "%llu\n"); 226 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(lmem, u64, "%llu\n"); 227 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ctxs, u32, "%llu\n"); 228 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(dbs, u32, "%llu\n"); 229 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(exec_quantum, u32, "%llu\n"); 230 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(preempt_timeout, u32, "%llu\n"); 231 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(sched_priority, u32, "%llu\n"); 232 233 /* 234 * /sys/kernel/debug/dri/0/ 235 * ├── gt0 236 * │ ├── pf 237 * │ │ ├── threshold_cat_error_count 238 * │ │ ├── threshold_doorbell_time_us 239 * │ │ ├── threshold_engine_reset_count 240 * │ │ ├── threshold_guc_time_us 241 * │ │ ├── threshold_irq_time_us 242 * │ │ ├── threshold_page_fault_count 243 * │ ├── vf1 244 * │ │ ├── threshold_cat_error_count 245 * │ │ ├── threshold_doorbell_time_us 246 * │ │ ├── threshold_engine_reset_count 247 * │ │ ├── threshold_guc_time_us 248 * │ │ ├── threshold_irq_time_us 249 * │ │ ├── threshold_page_fault_count 250 */ 251 252 static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index index) 253 { 254 struct xe_gt *gt = extract_gt(data); 255 unsigned int vfid = extract_vfid(data); 256 struct xe_device *xe = gt_to_xe(gt); 257 int err; 258 259 if (val > (u32)~0ull) 260 return -EOVERFLOW; 261 262 xe_pm_runtime_get(xe); 263 err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val); 264 xe_pm_runtime_put(xe); 265 266 return err; 267 } 268 269 static int get_threshold(void *data, u64 *val, enum xe_guc_klv_threshold_index index) 270 { 271 struct xe_gt *gt = extract_gt(data); 272 unsigned int vfid = extract_vfid(data); 273 274 *val = xe_gt_sriov_pf_config_get_threshold(gt, vfid, index); 275 return 0; 276 } 277 278 #define DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(THRESHOLD, INDEX) \ 279 \ 280 static int THRESHOLD##_set(void *data, u64 val) \ 281 { \ 282 return set_threshold(data, val, INDEX); \ 283 } \ 284 \ 285 static int THRESHOLD##_get(void *data, u64 *val) \ 286 { \ 287 return get_threshold(data, val, INDEX); \ 288 } \ 289 \ 290 DEFINE_DEBUGFS_ATTRIBUTE(THRESHOLD##_fops, THRESHOLD##_get, THRESHOLD##_set, "%llu\n") 291 292 /* generate all threshold attributes */ 293 #define define_threshold_attribute(TAG, NAME, ...) \ 294 DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(NAME, MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)); 295 MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_attribute) 296 #undef define_threshold_attribute 297 298 static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigned int vfid) 299 { 300 xe_gt_assert(gt, gt == extract_gt(parent)); 301 xe_gt_assert(gt, vfid == extract_vfid(parent)); 302 303 if (xe_gt_is_main_type(gt)) { 304 debugfs_create_file_unsafe(vfid ? "ggtt_quota" : "ggtt_spare", 305 0644, parent, parent, &ggtt_fops); 306 if (xe_device_has_lmtt(gt_to_xe(gt))) 307 debugfs_create_file_unsafe(vfid ? "lmem_quota" : "lmem_spare", 308 0644, parent, parent, &lmem_fops); 309 } 310 debugfs_create_file_unsafe(vfid ? "doorbells_quota" : "doorbells_spare", 311 0644, parent, parent, &dbs_fops); 312 debugfs_create_file_unsafe(vfid ? "contexts_quota" : "contexts_spare", 313 0644, parent, parent, &ctxs_fops); 314 debugfs_create_file_unsafe("exec_quantum_ms", 0644, parent, parent, 315 &exec_quantum_fops); 316 debugfs_create_file_unsafe("preempt_timeout_us", 0644, parent, parent, 317 &preempt_timeout_fops); 318 debugfs_create_file_unsafe("sched_priority", 0644, parent, parent, 319 &sched_priority_fops); 320 321 /* register all threshold attributes */ 322 #define register_threshold_attribute(TAG, NAME, ...) \ 323 debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \ 324 &NAME##_fops); 325 MAKE_XE_GUC_KLV_THRESHOLDS_SET(register_threshold_attribute) 326 #undef register_threshold_attribute 327 } 328 329 /* 330 * /sys/kernel/debug/dri/0/ 331 * ├── gt0 332 * │ ├── vf1 333 * │ │ ├── control { stop, pause, resume } 334 */ 335 336 static const struct { 337 const char *cmd; 338 int (*fn)(struct xe_gt *gt, unsigned int vfid); 339 } control_cmds[] = { 340 { "stop", xe_gt_sriov_pf_control_stop_vf }, 341 { "pause", xe_gt_sriov_pf_control_pause_vf }, 342 { "resume", xe_gt_sriov_pf_control_resume_vf }, 343 #ifdef CONFIG_DRM_XE_DEBUG_SRIOV 344 { "restore!", xe_gt_sriov_pf_migration_restore_guc_state }, 345 #endif 346 }; 347 348 static ssize_t control_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) 349 { 350 struct dentry *dent = file_dentry(file); 351 struct dentry *parent = dent->d_parent; 352 struct xe_gt *gt = extract_gt(parent); 353 struct xe_device *xe = gt_to_xe(gt); 354 unsigned int vfid = extract_vfid(parent); 355 int ret = -EINVAL; 356 char cmd[32]; 357 size_t n; 358 359 xe_gt_assert(gt, vfid); 360 xe_gt_sriov_pf_assert_vfid(gt, vfid); 361 362 if (*pos) 363 return -ESPIPE; 364 365 if (count > sizeof(cmd) - 1) 366 return -EINVAL; 367 368 ret = simple_write_to_buffer(cmd, sizeof(cmd) - 1, pos, buf, count); 369 if (ret < 0) 370 return ret; 371 cmd[ret] = '\0'; 372 373 for (n = 0; n < ARRAY_SIZE(control_cmds); n++) { 374 xe_gt_assert(gt, sizeof(cmd) > strlen(control_cmds[n].cmd)); 375 376 if (sysfs_streq(cmd, control_cmds[n].cmd)) { 377 xe_pm_runtime_get(xe); 378 ret = control_cmds[n].fn ? (*control_cmds[n].fn)(gt, vfid) : 0; 379 xe_pm_runtime_put(xe); 380 break; 381 } 382 } 383 384 return (ret < 0) ? ret : count; 385 } 386 387 static ssize_t control_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 388 { 389 char help[128]; 390 size_t n; 391 392 help[0] = '\0'; 393 for (n = 0; n < ARRAY_SIZE(control_cmds); n++) { 394 strlcat(help, control_cmds[n].cmd, sizeof(help)); 395 strlcat(help, "\n", sizeof(help)); 396 } 397 398 return simple_read_from_buffer(buf, count, ppos, help, strlen(help)); 399 } 400 401 static const struct file_operations control_ops = { 402 .owner = THIS_MODULE, 403 .open = simple_open, 404 .write = control_write, 405 .read = control_read, 406 .llseek = default_llseek, 407 }; 408 409 /* 410 * /sys/kernel/debug/dri/0/ 411 * ├── gt0 412 * │ ├── vf1 413 * │ │ ├── guc_state 414 */ 415 static ssize_t guc_state_read(struct file *file, char __user *buf, 416 size_t count, loff_t *pos) 417 { 418 struct dentry *dent = file_dentry(file); 419 struct dentry *parent = dent->d_parent; 420 struct xe_gt *gt = extract_gt(parent); 421 unsigned int vfid = extract_vfid(parent); 422 423 return xe_gt_sriov_pf_migration_read_guc_state(gt, vfid, buf, count, pos); 424 } 425 426 static ssize_t guc_state_write(struct file *file, const char __user *buf, 427 size_t count, loff_t *pos) 428 { 429 struct dentry *dent = file_dentry(file); 430 struct dentry *parent = dent->d_parent; 431 struct xe_gt *gt = extract_gt(parent); 432 unsigned int vfid = extract_vfid(parent); 433 434 if (*pos) 435 return -EINVAL; 436 437 return xe_gt_sriov_pf_migration_write_guc_state(gt, vfid, buf, count); 438 } 439 440 static const struct file_operations guc_state_ops = { 441 .owner = THIS_MODULE, 442 .read = guc_state_read, 443 .write = guc_state_write, 444 .llseek = default_llseek, 445 }; 446 447 /* 448 * /sys/kernel/debug/dri/0/ 449 * ├── gt0 450 * │ ├── vf1 451 * │ │ ├── config_blob 452 */ 453 static ssize_t config_blob_read(struct file *file, char __user *buf, 454 size_t count, loff_t *pos) 455 { 456 struct dentry *dent = file_dentry(file); 457 struct dentry *parent = dent->d_parent; 458 struct xe_gt *gt = extract_gt(parent); 459 unsigned int vfid = extract_vfid(parent); 460 ssize_t ret; 461 void *tmp; 462 463 ret = xe_gt_sriov_pf_config_save(gt, vfid, NULL, 0); 464 if (!ret) 465 return -ENODATA; 466 if (ret < 0) 467 return ret; 468 469 tmp = kzalloc(ret, GFP_KERNEL); 470 if (!tmp) 471 return -ENOMEM; 472 473 ret = xe_gt_sriov_pf_config_save(gt, vfid, tmp, ret); 474 if (ret > 0) 475 ret = simple_read_from_buffer(buf, count, pos, tmp, ret); 476 477 kfree(tmp); 478 return ret; 479 } 480 481 static ssize_t config_blob_write(struct file *file, const char __user *buf, 482 size_t count, loff_t *pos) 483 { 484 struct dentry *dent = file_dentry(file); 485 struct dentry *parent = dent->d_parent; 486 struct xe_gt *gt = extract_gt(parent); 487 unsigned int vfid = extract_vfid(parent); 488 ssize_t ret; 489 void *tmp; 490 491 if (*pos) 492 return -EINVAL; 493 494 if (!count) 495 return -ENODATA; 496 497 if (count > SZ_4K) 498 return -EINVAL; 499 500 tmp = kzalloc(count, GFP_KERNEL); 501 if (!tmp) 502 return -ENOMEM; 503 504 if (copy_from_user(tmp, buf, count)) { 505 ret = -EFAULT; 506 } else { 507 ret = xe_gt_sriov_pf_config_restore(gt, vfid, tmp, count); 508 if (!ret) 509 ret = count; 510 } 511 kfree(tmp); 512 return ret; 513 } 514 515 static const struct file_operations config_blob_ops = { 516 .owner = THIS_MODULE, 517 .read = config_blob_read, 518 .write = config_blob_write, 519 .llseek = default_llseek, 520 }; 521 522 /** 523 * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs. 524 * @gt: the &xe_gt to register 525 * @root: the &dentry that represents the GT directory 526 * 527 * Register SR-IOV PF entries that are GT related and must be shown under GT debugfs. 528 */ 529 void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root) 530 { 531 struct xe_device *xe = gt_to_xe(gt); 532 struct drm_minor *minor = xe->drm.primary; 533 int n, totalvfs = xe_sriov_pf_get_totalvfs(xe); 534 struct dentry *pfdentry; 535 struct dentry *vfdentry; 536 char buf[14]; /* should be enough up to "vf%u\0" for 2^32 - 1 */ 537 538 xe_gt_assert(gt, IS_SRIOV_PF(xe)); 539 xe_gt_assert(gt, root->d_inode->i_private == gt); 540 541 /* 542 * /sys/kernel/debug/dri/0/ 543 * ├── gt0 544 * │ ├── pf 545 */ 546 pfdentry = debugfs_create_dir("pf", root); 547 if (IS_ERR(pfdentry)) 548 return; 549 pfdentry->d_inode->i_private = gt; 550 551 drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), pfdentry, minor); 552 if (xe_gt_is_main_type(gt)) { 553 drm_debugfs_create_files(pf_ggtt_info, 554 ARRAY_SIZE(pf_ggtt_info), 555 pfdentry, minor); 556 if (xe_device_has_lmtt(gt_to_xe(gt))) 557 drm_debugfs_create_files(pf_lmem_info, 558 ARRAY_SIZE(pf_lmem_info), 559 pfdentry, minor); 560 } 561 562 pf_add_policy_attrs(gt, pfdentry); 563 pf_add_config_attrs(gt, pfdentry, PFID); 564 565 for (n = 1; n <= totalvfs; n++) { 566 /* 567 * /sys/kernel/debug/dri/0/ 568 * ├── gt0 569 * │ ├── vf1 570 * │ ├── vf2 571 */ 572 snprintf(buf, sizeof(buf), "vf%u", n); 573 vfdentry = debugfs_create_dir(buf, root); 574 if (IS_ERR(vfdentry)) 575 break; 576 vfdentry->d_inode->i_private = (void *)(uintptr_t)n; 577 578 pf_add_config_attrs(gt, vfdentry, VFID(n)); 579 debugfs_create_file("control", 0600, vfdentry, NULL, &control_ops); 580 581 /* for testing/debugging purposes only! */ 582 if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) { 583 debugfs_create_file("guc_state", 584 IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400, 585 vfdentry, NULL, &guc_state_ops); 586 debugfs_create_file("config_blob", 587 IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400, 588 vfdentry, NULL, &config_blob_ops); 589 } 590 } 591 } 592