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