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