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_debugfs.h" 12 #include "xe_device.h" 13 #include "xe_gt.h" 14 #include "xe_gt_debugfs.h" 15 #include "xe_gt_sriov_pf_config.h" 16 #include "xe_gt_sriov_pf_control.h" 17 #include "xe_gt_sriov_pf_debugfs.h" 18 #include "xe_gt_sriov_pf_helpers.h" 19 #include "xe_gt_sriov_pf_migration.h" 20 #include "xe_gt_sriov_pf_monitor.h" 21 #include "xe_gt_sriov_pf_policy.h" 22 #include "xe_gt_sriov_pf_service.h" 23 #include "xe_guc.h" 24 #include "xe_pm.h" 25 #include "xe_sriov_pf.h" 26 #include "xe_sriov_pf_provision.h" 27 28 /* 29 * /sys/kernel/debug/dri/BDF/ 30 * ├── sriov # d_inode->i_private = (xe_device*) 31 * │ ├── pf # d_inode->i_private = (xe_device*) 32 * │ │ ├── tile0 # d_inode->i_private = (xe_tile*) 33 * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*) 34 * │ │ │ ├── gt1 # d_inode->i_private = (xe_gt*) 35 * │ │ ├── tile1 36 * │ │ │ : 37 * │ ├── vf1 # d_inode->i_private = VFID(1) 38 * │ │ ├── tile0 # d_inode->i_private = (xe_tile*) 39 * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*) 40 * │ │ │ ├── gt1 # d_inode->i_private = (xe_gt*) 41 * │ │ ├── tile1 42 * │ │ │ : 43 * : : 44 * │ ├── vfN # d_inode->i_private = VFID(N) 45 */ 46 47 static void *extract_priv(struct dentry *d) 48 { 49 return d->d_inode->i_private; 50 } 51 52 static struct xe_gt *extract_gt(struct dentry *d) 53 { 54 return extract_priv(d); 55 } 56 57 static struct xe_device *extract_xe(struct dentry *d) 58 { 59 return extract_priv(d->d_parent->d_parent->d_parent); 60 } 61 62 static unsigned int extract_vfid(struct dentry *d) 63 { 64 void *priv = extract_priv(d->d_parent->d_parent); 65 66 return priv == extract_xe(d) ? PFID : (uintptr_t)priv; 67 } 68 69 /* 70 * /sys/kernel/debug/dri/BDF/ 71 * ├── sriov 72 * : ├── pf 73 * : ├── tile0 74 * : ├── gt0 75 * : ├── contexts_provisioned 76 * ├── doorbells_provisioned 77 * ├── runtime_registers 78 * ├── adverse_events 79 */ 80 81 static const struct drm_info_list pf_info[] = { 82 { 83 "contexts_provisioned", 84 .show = xe_gt_debugfs_simple_show, 85 .data = xe_gt_sriov_pf_config_print_ctxs, 86 }, 87 { 88 "doorbells_provisioned", 89 .show = xe_gt_debugfs_simple_show, 90 .data = xe_gt_sriov_pf_config_print_dbs, 91 }, 92 { 93 "runtime_registers", 94 .show = xe_gt_debugfs_simple_show, 95 .data = xe_gt_sriov_pf_service_print_runtime, 96 }, 97 { 98 "adverse_events", 99 .show = xe_gt_debugfs_simple_show, 100 .data = xe_gt_sriov_pf_monitor_print_events, 101 }, 102 }; 103 104 /* 105 * /sys/kernel/debug/dri/BDF/ 106 * ├── sriov 107 * : ├── pf 108 * : ├── tile0 109 * : ├── gt0 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 guard(xe_pm_runtime)(xe); \ 127 err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val); \ 128 if (!err) \ 129 xe_sriov_pf_provision_set_custom_mode(xe); \ 130 \ 131 return err; \ 132 } \ 133 \ 134 static int POLICY##_get(void *data, u64 *val) \ 135 { \ 136 struct xe_gt *gt = extract_gt(data); \ 137 \ 138 *val = xe_gt_sriov_pf_policy_get_##POLICY(gt); \ 139 return 0; \ 140 } \ 141 \ 142 DEFINE_DEBUGFS_ATTRIBUTE(POLICY##_fops, POLICY##_get, POLICY##_set, FORMAT) 143 144 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(reset_engine, bool, "%llu\n"); 145 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sched_if_idle, bool, "%llu\n"); 146 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sample_period, u32, "%llu\n"); 147 148 static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent) 149 { 150 xe_gt_assert(gt, gt == extract_gt(parent)); 151 xe_gt_assert(gt, PFID == extract_vfid(parent)); 152 153 debugfs_create_file_unsafe("reset_engine", 0644, parent, parent, &reset_engine_fops); 154 debugfs_create_file_unsafe("sched_if_idle", 0644, parent, parent, &sched_if_idle_fops); 155 debugfs_create_file_unsafe("sample_period_ms", 0644, parent, parent, &sample_period_fops); 156 } 157 158 /* 159 * /sys/kernel/debug/dri/BDF/ 160 * ├── sriov 161 * : ├── pf 162 * : ├── tile0 163 * : ├── gt0 164 * : ├── sched_groups_mode 165 * ├── sched_groups_exec_quantums_ms 166 * ├── sched_groups_preempt_timeout_us 167 * ├── sched_groups 168 * : ├── group0 169 * : 170 * : └── groupN 171 * ├── vf1 172 * : ├── tile0 173 * : ├── gt0 174 * : ├── sched_groups_exec_quantums_ms 175 * ├── sched_groups_preempt_timeout_us 176 * : 177 */ 178 179 static const char *sched_group_mode_to_string(enum xe_sriov_sched_group_modes mode) 180 { 181 switch (mode) { 182 case XE_SRIOV_SCHED_GROUPS_DISABLED: 183 return "disabled"; 184 case XE_SRIOV_SCHED_GROUPS_MEDIA_SLICES: 185 return "media_slices"; 186 case XE_SRIOV_SCHED_GROUPS_MODES_COUNT: 187 /* dummy mode to make the compiler happy */ 188 break; 189 } 190 191 return "unknown"; 192 } 193 194 static int sched_groups_info(struct seq_file *m, void *data) 195 { 196 struct drm_printer p = drm_seq_file_printer(m); 197 struct xe_gt *gt = extract_gt(m->private); 198 enum xe_sriov_sched_group_modes current_mode = 199 gt->sriov.pf.policy.guc.sched_groups.current_mode; 200 enum xe_sriov_sched_group_modes mode; 201 202 for (mode = XE_SRIOV_SCHED_GROUPS_DISABLED; 203 mode < XE_SRIOV_SCHED_GROUPS_MODES_COUNT; 204 mode++) { 205 if (!xe_sriov_gt_pf_policy_has_sched_group_mode(gt, mode)) 206 continue; 207 208 drm_printf(&p, "%s%s%s%s", 209 mode == XE_SRIOV_SCHED_GROUPS_DISABLED ? "" : " ", 210 mode == current_mode ? "[" : "", 211 sched_group_mode_to_string(mode), 212 mode == current_mode ? "]" : ""); 213 } 214 215 drm_puts(&p, "\n"); 216 217 return 0; 218 } 219 220 static int sched_groups_open(struct inode *inode, struct file *file) 221 { 222 return single_open(file, sched_groups_info, inode->i_private); 223 } 224 225 static ssize_t sched_groups_write(struct file *file, const char __user *ubuf, 226 size_t size, loff_t *pos) 227 { 228 struct xe_gt *gt = extract_gt(file_inode(file)->i_private); 229 enum xe_sriov_sched_group_modes mode; 230 char name[32]; 231 int ret; 232 233 if (*pos) 234 return -ESPIPE; 235 236 if (!size) 237 return -ENODATA; 238 239 if (size > sizeof(name) - 1) 240 return -EINVAL; 241 242 ret = simple_write_to_buffer(name, sizeof(name) - 1, pos, ubuf, size); 243 if (ret < 0) 244 return ret; 245 name[ret] = '\0'; 246 247 for (mode = XE_SRIOV_SCHED_GROUPS_DISABLED; 248 mode < XE_SRIOV_SCHED_GROUPS_MODES_COUNT; 249 mode++) 250 if (sysfs_streq(name, sched_group_mode_to_string(mode))) 251 break; 252 253 if (mode == XE_SRIOV_SCHED_GROUPS_MODES_COUNT) 254 return -EINVAL; 255 256 guard(xe_pm_runtime)(gt_to_xe(gt)); 257 ret = xe_gt_sriov_pf_policy_set_sched_groups_mode(gt, mode); 258 259 return ret < 0 ? ret : size; 260 } 261 262 static const struct file_operations sched_groups_fops = { 263 .owner = THIS_MODULE, 264 .open = sched_groups_open, 265 .read = seq_read, 266 .write = sched_groups_write, 267 .llseek = seq_lseek, 268 .release = single_release, 269 }; 270 271 static int sched_groups_config_show(struct seq_file *m, void *data, 272 void (*get)(struct xe_gt *, unsigned int, u32 *, u32)) 273 { 274 struct drm_printer p = drm_seq_file_printer(m); 275 unsigned int vfid = extract_vfid(m->private); 276 struct xe_gt *gt = extract_gt(m->private); 277 u32 values[GUC_MAX_SCHED_GROUPS]; 278 bool first = true; 279 u8 group; 280 281 get(gt, vfid, values, ARRAY_SIZE(values)); 282 283 for (group = 0; group < ARRAY_SIZE(values); group++) { 284 drm_printf(&p, "%s%u", first ? "" : ",", values[group]); 285 286 first = false; 287 } 288 289 drm_puts(&p, "\n"); 290 291 return 0; 292 } 293 294 static ssize_t sched_groups_config_write(struct file *file, const char __user *ubuf, 295 size_t size, loff_t *pos, 296 int (*set)(struct xe_gt *, unsigned int, u32 *, u32)) 297 { 298 struct dentry *parent = file_inode(file)->i_private; 299 unsigned int vfid = extract_vfid(parent); 300 struct xe_gt *gt = extract_gt(parent); 301 u32 values[GUC_MAX_SCHED_GROUPS]; 302 int *input __free(kfree) = NULL; 303 u32 count; 304 int ret; 305 int i; 306 307 if (*pos) 308 return -ESPIPE; 309 310 if (!size) 311 return -ENODATA; 312 313 ret = parse_int_array_user(ubuf, min(size, GUC_MAX_SCHED_GROUPS * sizeof(u32)), &input); 314 if (ret) 315 return ret; 316 317 count = input[0]; 318 if (count > GUC_MAX_SCHED_GROUPS) 319 return -E2BIG; 320 321 for (i = 0; i < count; i++) { 322 if (input[i + 1] < 0 || input[i + 1] > S32_MAX) 323 return -EINVAL; 324 325 values[i] = input[i + 1]; 326 } 327 328 guard(xe_pm_runtime)(gt_to_xe(gt)); 329 ret = set(gt, vfid, values, count); 330 331 return ret < 0 ? ret : size; 332 } 333 334 #define DEFINE_SRIOV_GT_GRP_CFG_DEBUGFS_ATTRIBUTE(CONFIG) \ 335 static int sched_groups_##CONFIG##_show(struct seq_file *m, void *data) \ 336 { \ 337 return sched_groups_config_show(m, data, \ 338 xe_gt_sriov_pf_config_get_groups_##CONFIG); \ 339 } \ 340 \ 341 static int sched_groups_##CONFIG##_open(struct inode *inode, struct file *file) \ 342 { \ 343 return single_open(file, sched_groups_##CONFIG##_show, \ 344 inode->i_private); \ 345 } \ 346 \ 347 static ssize_t sched_groups_##CONFIG##_write(struct file *file, \ 348 const char __user *ubuf, \ 349 size_t size, loff_t *pos) \ 350 { \ 351 return sched_groups_config_write(file, ubuf, size, pos, \ 352 xe_gt_sriov_pf_config_set_groups_##CONFIG); \ 353 } \ 354 \ 355 static const struct file_operations sched_groups_##CONFIG##_fops = { \ 356 .owner = THIS_MODULE, \ 357 .open = sched_groups_##CONFIG##_open, \ 358 .read = seq_read, \ 359 .llseek = seq_lseek, \ 360 .write = sched_groups_##CONFIG##_write, \ 361 .release = single_release, \ 362 } 363 364 DEFINE_SRIOV_GT_GRP_CFG_DEBUGFS_ATTRIBUTE(exec_quantums); 365 DEFINE_SRIOV_GT_GRP_CFG_DEBUGFS_ATTRIBUTE(preempt_timeouts); 366 367 static ssize_t sched_group_engines_read(struct file *file, char __user *buf, 368 size_t count, loff_t *ppos) 369 { 370 struct dentry *dent = file_dentry(file); 371 struct xe_gt *gt = extract_gt(dent->d_parent->d_parent); 372 struct xe_gt_sriov_scheduler_groups *info = >->sriov.pf.policy.guc.sched_groups; 373 struct guc_sched_group *groups = info->modes[info->current_mode].groups; 374 u32 num_groups = info->modes[info->current_mode].num_groups; 375 unsigned int group = (uintptr_t)extract_priv(dent); 376 struct xe_hw_engine *hwe; 377 enum xe_hw_engine_id id; 378 char engines[128]; 379 380 engines[0] = '\0'; 381 382 if (group < num_groups) { 383 for_each_hw_engine(hwe, gt, id) { 384 u8 guc_class = xe_hwe_to_guc_class(hwe); 385 u16 guc_logical_instance = xe_hwe_guc_logical_instance(hwe); 386 u32 mask = groups[group].engines[guc_class]; 387 388 if (mask & BIT(guc_logical_instance)) { 389 strlcat(engines, hwe->name, sizeof(engines)); 390 strlcat(engines, " ", sizeof(engines)); 391 } 392 } 393 strlcat(engines, "\n", sizeof(engines)); 394 } 395 396 return simple_read_from_buffer(buf, count, ppos, engines, strlen(engines)); 397 } 398 399 static const struct file_operations sched_group_engines_fops = { 400 .owner = THIS_MODULE, 401 .open = simple_open, 402 .read = sched_group_engines_read, 403 .llseek = default_llseek, 404 }; 405 406 static void pf_add_sched_groups(struct xe_gt *gt, struct dentry *parent, unsigned int vfid) 407 { 408 struct dentry *groups; 409 u8 group; 410 411 xe_gt_assert(gt, gt == extract_gt(parent)); 412 xe_gt_assert(gt, vfid == extract_vfid(parent)); 413 414 /* 415 * TODO: we currently call this function before we initialize scheduler 416 * groups, so at this point in time we don't know if there are any 417 * valid groups on the GT and we can't selectively register the debugfs 418 * only if there are any. Therefore, we always register the debugfs 419 * files if we're on a platform that has support for groups. 420 * We should rework the flow so that debugfs is registered after the 421 * policy init, so that we check if there are valid groups before 422 * adding the debugfs files. 423 * Similarly, instead of using GUC_MAX_SCHED_GROUPS we could use 424 * gt->sriov.pf.policy.guc.sched_groups.max_number_of_groups. 425 */ 426 if (!xe_sriov_gt_pf_policy_has_sched_groups_support(gt)) 427 return; 428 429 debugfs_create_file("sched_groups_exec_quantums_ms", 0644, parent, parent, 430 &sched_groups_exec_quantums_fops); 431 debugfs_create_file("sched_groups_preempt_timeouts_us", 0644, parent, parent, 432 &sched_groups_preempt_timeouts_fops); 433 434 if (vfid != PFID) 435 return; 436 437 debugfs_create_file("sched_groups_mode", 0644, parent, parent, &sched_groups_fops); 438 439 groups = debugfs_create_dir("sched_groups", parent); 440 if (IS_ERR(groups)) 441 return; 442 443 for (group = 0; group < GUC_MAX_SCHED_GROUPS; group++) { 444 char name[10]; 445 446 snprintf(name, sizeof(name), "group%u", group); 447 debugfs_create_file(name, 0644, groups, (void *)(uintptr_t)group, 448 &sched_group_engines_fops); 449 } 450 } 451 452 /* 453 * /sys/kernel/debug/dri/BDF/ 454 * ├── sriov 455 * : ├── pf 456 * │ ├── tile0 457 * │ : ├── gt0 458 * │ : ├── doorbells_spare 459 * │ ├── contexts_spare 460 * │ ├── exec_quantum_ms 461 * │ ├── preempt_timeout_us 462 * │ ├── sched_priority 463 * ├── vf1 464 * : ├── tile0 465 * : ├── gt0 466 * : ├── doorbells_quota 467 * ├── contexts_quota 468 * ├── exec_quantum_ms 469 * ├── preempt_timeout_us 470 * ├── sched_priority 471 */ 472 473 #define DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(CONFIG, TYPE, FORMAT) \ 474 \ 475 static int CONFIG##_set(void *data, u64 val) \ 476 { \ 477 struct xe_gt *gt = extract_gt(data); \ 478 unsigned int vfid = extract_vfid(data); \ 479 struct xe_device *xe = gt_to_xe(gt); \ 480 int err; \ 481 \ 482 if (val > (TYPE)~0ull) \ 483 return -EOVERFLOW; \ 484 \ 485 guard(xe_pm_runtime)(xe); \ 486 err = xe_sriov_pf_wait_ready(xe) ?: \ 487 xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \ 488 if (!err) \ 489 xe_sriov_pf_provision_set_custom_mode(xe); \ 490 \ 491 return err; \ 492 } \ 493 \ 494 static int CONFIG##_get(void *data, u64 *val) \ 495 { \ 496 struct xe_gt *gt = extract_gt(data); \ 497 unsigned int vfid = extract_vfid(data); \ 498 \ 499 *val = xe_gt_sriov_pf_config_get_##CONFIG(gt, vfid); \ 500 return 0; \ 501 } \ 502 \ 503 DEFINE_DEBUGFS_ATTRIBUTE(CONFIG##_fops, CONFIG##_get, CONFIG##_set, FORMAT) 504 505 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ctxs, u32, "%llu\n"); 506 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(dbs, u32, "%llu\n"); 507 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(exec_quantum, u32, "%llu\n"); 508 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(preempt_timeout, u32, "%llu\n"); 509 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(sched_priority, u32, "%llu\n"); 510 511 /* 512 * /sys/kernel/debug/dri/BDF/ 513 * ├── sriov 514 * : ├── pf 515 * │ ├── tile0 516 * │ : ├── gt0 517 * │ : ├── threshold_cat_error_count 518 * │ ├── threshold_doorbell_time_us 519 * │ ├── threshold_engine_reset_count 520 * │ ├── threshold_guc_time_us 521 * │ ├── threshold_irq_time_us 522 * │ ├── threshold_page_fault_count 523 * ├── vf1 524 * : ├── tile0 525 * : ├── gt0 526 * : ├── threshold_cat_error_count 527 * ├── threshold_doorbell_time_us 528 * ├── threshold_engine_reset_count 529 * ├── threshold_guc_time_us 530 * ├── threshold_irq_time_us 531 * ├── threshold_page_fault_count 532 */ 533 534 static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index index) 535 { 536 struct xe_gt *gt = extract_gt(data); 537 unsigned int vfid = extract_vfid(data); 538 struct xe_device *xe = gt_to_xe(gt); 539 int err; 540 541 if (val > (u32)~0ull) 542 return -EOVERFLOW; 543 544 guard(xe_pm_runtime)(xe); 545 err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val); 546 if (!err) 547 xe_sriov_pf_provision_set_custom_mode(xe); 548 549 return err; 550 } 551 552 static int get_threshold(void *data, u64 *val, enum xe_guc_klv_threshold_index index) 553 { 554 struct xe_gt *gt = extract_gt(data); 555 unsigned int vfid = extract_vfid(data); 556 557 *val = xe_gt_sriov_pf_config_get_threshold(gt, vfid, index); 558 return 0; 559 } 560 561 #define DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(THRESHOLD, INDEX) \ 562 \ 563 static int THRESHOLD##_set(void *data, u64 val) \ 564 { \ 565 return set_threshold(data, val, INDEX); \ 566 } \ 567 \ 568 static int THRESHOLD##_get(void *data, u64 *val) \ 569 { \ 570 return get_threshold(data, val, INDEX); \ 571 } \ 572 \ 573 DEFINE_DEBUGFS_ATTRIBUTE(THRESHOLD##_fops, THRESHOLD##_get, THRESHOLD##_set, "%llu\n") 574 575 /* generate all threshold attributes */ 576 #define define_threshold_attribute(TAG, NAME, ...) \ 577 DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(NAME, MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)); 578 MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_attribute) 579 #undef define_threshold_attribute 580 581 static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigned int vfid) 582 { 583 xe_gt_assert(gt, gt == extract_gt(parent)); 584 xe_gt_assert(gt, vfid == extract_vfid(parent)); 585 586 debugfs_create_file_unsafe(vfid ? "doorbells_quota" : "doorbells_spare", 587 0644, parent, parent, &dbs_fops); 588 debugfs_create_file_unsafe(vfid ? "contexts_quota" : "contexts_spare", 589 0644, parent, parent, &ctxs_fops); 590 debugfs_create_file_unsafe("exec_quantum_ms", 0644, parent, parent, 591 &exec_quantum_fops); 592 debugfs_create_file_unsafe("preempt_timeout_us", 0644, parent, parent, 593 &preempt_timeout_fops); 594 debugfs_create_file_unsafe("sched_priority", 0644, parent, parent, 595 &sched_priority_fops); 596 597 /* register all threshold attributes */ 598 #define register_threshold_attribute(TAG, NAME, VER...) ({ \ 599 if (IF_ARGS(GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, VER), true, VER)) \ 600 debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \ 601 &NAME##_fops); \ 602 }); 603 MAKE_XE_GUC_KLV_THRESHOLDS_SET(register_threshold_attribute) 604 #undef register_threshold_attribute 605 } 606 607 /* 608 * /sys/kernel/debug/dri/BDF/ 609 * ├── sriov 610 * : ├── vf1 611 * : ├── tile0 612 * : ├── gt0 613 * : ├── control { stop, pause, resume } 614 */ 615 616 static const struct { 617 const char *cmd; 618 int (*fn)(struct xe_gt *gt, unsigned int vfid); 619 } control_cmds[] = { 620 { "stop", xe_gt_sriov_pf_control_stop_vf }, 621 { "pause", xe_gt_sriov_pf_control_pause_vf }, 622 { "resume", xe_gt_sriov_pf_control_resume_vf }, 623 }; 624 625 static ssize_t control_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) 626 { 627 struct dentry *dent = file_dentry(file); 628 struct dentry *parent = dent->d_parent; 629 struct xe_gt *gt = extract_gt(parent); 630 struct xe_device *xe = gt_to_xe(gt); 631 unsigned int vfid = extract_vfid(parent); 632 int ret = -EINVAL; 633 char cmd[32]; 634 size_t n; 635 636 xe_gt_assert(gt, vfid); 637 xe_gt_sriov_pf_assert_vfid(gt, vfid); 638 639 if (*pos) 640 return -ESPIPE; 641 642 if (count > sizeof(cmd) - 1) 643 return -EINVAL; 644 645 ret = simple_write_to_buffer(cmd, sizeof(cmd) - 1, pos, buf, count); 646 if (ret < 0) 647 return ret; 648 cmd[ret] = '\0'; 649 650 for (n = 0; n < ARRAY_SIZE(control_cmds); n++) { 651 xe_gt_assert(gt, sizeof(cmd) > strlen(control_cmds[n].cmd)); 652 653 if (sysfs_streq(cmd, control_cmds[n].cmd)) { 654 guard(xe_pm_runtime)(xe); 655 ret = control_cmds[n].fn ? (*control_cmds[n].fn)(gt, vfid) : 0; 656 break; 657 } 658 } 659 660 return (ret < 0) ? ret : count; 661 } 662 663 static ssize_t control_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 664 { 665 char help[128]; 666 size_t n; 667 668 help[0] = '\0'; 669 for (n = 0; n < ARRAY_SIZE(control_cmds); n++) { 670 strlcat(help, control_cmds[n].cmd, sizeof(help)); 671 strlcat(help, "\n", sizeof(help)); 672 } 673 674 return simple_read_from_buffer(buf, count, ppos, help, strlen(help)); 675 } 676 677 static const struct file_operations control_ops = { 678 .owner = THIS_MODULE, 679 .open = simple_open, 680 .write = control_write, 681 .read = control_read, 682 .llseek = default_llseek, 683 }; 684 685 /* 686 * /sys/kernel/debug/dri/BDF/ 687 * ├── sriov 688 * : ├── vf1 689 * : ├── tile0 690 * : ├── gt0 691 * : ├── config_blob 692 */ 693 694 struct config_blob_data { 695 size_t size; 696 u8 blob[]; 697 }; 698 699 static int config_blob_open(struct inode *inode, struct file *file) 700 { 701 struct dentry *dent = file_dentry(file); 702 struct dentry *parent = dent->d_parent; 703 struct xe_gt *gt = extract_gt(parent); 704 unsigned int vfid = extract_vfid(parent); 705 struct config_blob_data *cbd; 706 ssize_t ret; 707 708 ret = xe_gt_sriov_pf_config_save(gt, vfid, NULL, 0); 709 if (!ret) 710 return -ENODATA; 711 if (ret < 0) 712 return ret; 713 714 cbd = kzalloc_flex(*cbd, blob, ret); 715 if (!cbd) 716 return -ENOMEM; 717 718 ret = xe_gt_sriov_pf_config_save(gt, vfid, cbd->blob, ret); 719 if (ret < 0) { 720 kfree(cbd); 721 return ret; 722 } 723 724 cbd->size = ret; 725 file->private_data = cbd; 726 return nonseekable_open(inode, file); 727 } 728 729 static ssize_t config_blob_read(struct file *file, char __user *buf, 730 size_t count, loff_t *pos) 731 { 732 struct config_blob_data *cbd = file->private_data; 733 734 return simple_read_from_buffer(buf, count, pos, cbd->blob, cbd->size); 735 } 736 737 static ssize_t config_blob_write(struct file *file, const char __user *buf, 738 size_t count, loff_t *pos) 739 { 740 struct dentry *dent = file_dentry(file); 741 struct dentry *parent = dent->d_parent; 742 struct xe_gt *gt = extract_gt(parent); 743 unsigned int vfid = extract_vfid(parent); 744 ssize_t ret; 745 void *tmp; 746 747 if (*pos) 748 return -EINVAL; 749 750 if (!count) 751 return -ENODATA; 752 753 if (count > SZ_4K) 754 return -EINVAL; 755 756 tmp = kzalloc(count, GFP_KERNEL); 757 if (!tmp) 758 return -ENOMEM; 759 760 if (copy_from_user(tmp, buf, count)) { 761 ret = -EFAULT; 762 } else { 763 ret = xe_gt_sriov_pf_config_restore(gt, vfid, tmp, count); 764 if (!ret) 765 ret = count; 766 } 767 kfree(tmp); 768 return ret; 769 } 770 771 static int config_blob_release(struct inode *inode, struct file *file) 772 { 773 kfree(file->private_data); 774 return 0; 775 } 776 777 static const struct file_operations config_blob_ops = { 778 .owner = THIS_MODULE, 779 .open = config_blob_open, 780 .read = config_blob_read, 781 .write = config_blob_write, 782 .release = config_blob_release, 783 }; 784 785 static void pf_add_compat_attrs(struct xe_gt *gt, struct dentry *dent, unsigned int vfid) 786 { 787 struct xe_device *xe = gt_to_xe(gt); 788 789 if (!xe_gt_is_main_type(gt)) 790 return; 791 792 if (vfid) { 793 debugfs_create_symlink("ggtt_quota", dent, "../ggtt_quota"); 794 if (xe_device_has_lmtt(xe)) 795 debugfs_create_symlink("lmem_quota", dent, "../vram_quota"); 796 } else { 797 debugfs_create_symlink("ggtt_spare", dent, "../ggtt_spare"); 798 debugfs_create_symlink("ggtt_available", dent, "../ggtt_available"); 799 debugfs_create_symlink("ggtt_provisioned", dent, "../ggtt_provisioned"); 800 if (xe_device_has_lmtt(xe)) { 801 debugfs_create_symlink("lmem_spare", dent, "../vram_spare"); 802 debugfs_create_symlink("lmem_provisioned", dent, "../vram_provisioned"); 803 } 804 } 805 } 806 807 static void pf_populate_gt(struct xe_gt *gt, struct dentry *dent, unsigned int vfid) 808 { 809 struct xe_device *xe = gt_to_xe(gt); 810 struct drm_minor *minor = xe->drm.primary; 811 812 if (vfid) { 813 pf_add_config_attrs(gt, dent, vfid); 814 pf_add_sched_groups(gt, dent, vfid); 815 816 debugfs_create_file("control", 0600, dent, NULL, &control_ops); 817 818 /* for testing/debugging purposes only! */ 819 if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) { 820 debugfs_create_file("config_blob", 821 IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400, 822 dent, NULL, &config_blob_ops); 823 } 824 825 } else { 826 pf_add_config_attrs(gt, dent, PFID); 827 pf_add_policy_attrs(gt, dent); 828 pf_add_sched_groups(gt, dent, PFID); 829 830 drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), dent, minor); 831 } 832 833 /* for backward compatibility only */ 834 pf_add_compat_attrs(gt, dent, vfid); 835 } 836 837 /** 838 * xe_gt_sriov_pf_debugfs_populate() - Create SR-IOV GT-level debugfs directories and files. 839 * @gt: the &xe_gt to register 840 * @parent: the parent &dentry that represents a &xe_tile 841 * @vfid: the VF identifier 842 * 843 * Add to the @parent directory new debugfs directory that will represent a @gt and 844 * populate it with GT files that are related to the SR-IOV @vfid function. 845 * 846 * This function can only be called on PF. 847 */ 848 void xe_gt_sriov_pf_debugfs_populate(struct xe_gt *gt, struct dentry *parent, unsigned int vfid) 849 { 850 struct dentry *dent; 851 char name[8]; /* should be enough up to "gt%u\0" for 2^8 - 1 */ 852 853 xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt))); 854 xe_gt_assert(gt, extract_priv(parent) == gt->tile); 855 xe_gt_assert(gt, extract_priv(parent->d_parent) == gt_to_xe(gt) || 856 (uintptr_t)extract_priv(parent->d_parent) == vfid); 857 858 /* 859 * /sys/kernel/debug/dri/BDF/ 860 * ├── sriov 861 * │ ├── pf 862 * │ │ ├── tile0 # parent 863 * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*) 864 * │ │ │ ├── gt1 865 * │ │ : : 866 * │ ├── vf1 867 * │ │ ├── tile0 # parent 868 * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*) 869 * │ │ │ ├── gt1 870 * │ : : : 871 */ 872 snprintf(name, sizeof(name), "gt%u", gt->info.id); 873 dent = debugfs_create_dir(name, parent); 874 if (IS_ERR(dent)) 875 return; 876 dent->d_inode->i_private = gt; 877 878 xe_gt_assert(gt, extract_gt(dent) == gt); 879 xe_gt_assert(gt, extract_vfid(dent) == vfid); 880 881 pf_populate_gt(gt, dent, vfid); 882 } 883 884 static void pf_add_links(struct xe_gt *gt, struct dentry *dent) 885 { 886 unsigned int totalvfs = xe_gt_sriov_pf_get_totalvfs(gt); 887 unsigned int vfid; 888 char name[16]; /* should be more than enough for "vf%u\0" and VFID(UINT_MAX) */ 889 char symlink[64]; /* should be more enough for "../../sriov/vf%u/tile%u/gt%u\0" */ 890 891 for (vfid = 0; vfid <= totalvfs; vfid++) { 892 if (vfid) 893 snprintf(name, sizeof(name), "vf%u", vfid); 894 else 895 snprintf(name, sizeof(name), "pf"); 896 snprintf(symlink, sizeof(symlink), "../../sriov/%s/tile%u/gt%u", 897 name, gt->tile->id, gt->info.id); 898 debugfs_create_symlink(name, dent, symlink); 899 } 900 } 901 902 /** 903 * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs. 904 * @gt: the &xe_gt to register 905 * @dent: the &dentry that represents the GT directory 906 * 907 * Instead of actual files, create symlinks for PF and each VF to their GT specific 908 * attributes that should be already exposed in the dedicated debugfs SR-IOV tree. 909 */ 910 void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *dent) 911 { 912 xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt))); 913 xe_gt_assert(gt, dent->d_inode->i_private == gt); 914 915 pf_add_links(gt, dent); 916 } 917