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_monitor.h" 21 #include "xe_gt_sriov_pf_policy.h" 22 #include "xe_gt_sriov_pf_service.h" 23 #include "xe_pm.h" 24 25 /* 26 * /sys/kernel/debug/dri/0/ 27 * ├── gt0 # d_inode->i_private = gt 28 * │ ├── pf # d_inode->i_private = gt 29 * │ ├── vf1 # d_inode->i_private = VFID(1) 30 * : : 31 * │ ├── vfN # d_inode->i_private = VFID(N) 32 */ 33 34 static void *extract_priv(struct dentry *d) 35 { 36 return d->d_inode->i_private; 37 } 38 39 static struct xe_gt *extract_gt(struct dentry *d) 40 { 41 return extract_priv(d->d_parent); 42 } 43 44 static unsigned int extract_vfid(struct dentry *d) 45 { 46 return extract_priv(d) == extract_gt(d) ? PFID : (uintptr_t)extract_priv(d); 47 } 48 49 /* 50 * /sys/kernel/debug/dri/0/ 51 * ├── gt0 52 * │ ├── pf 53 * │ │ ├── ggtt_available 54 * │ │ ├── ggtt_provisioned 55 * │ │ ├── contexts_provisioned 56 * │ │ ├── doorbells_provisioned 57 * │ │ ├── runtime_registers 58 * │ │ ├── negotiated_versions 59 * │ │ ├── adverse_events 60 */ 61 62 static const struct drm_info_list pf_info[] = { 63 { 64 "ggtt_available", 65 .show = xe_gt_debugfs_simple_show, 66 .data = xe_gt_sriov_pf_config_print_available_ggtt, 67 }, 68 { 69 "ggtt_provisioned", 70 .show = xe_gt_debugfs_simple_show, 71 .data = xe_gt_sriov_pf_config_print_ggtt, 72 }, 73 { 74 "contexts_provisioned", 75 .show = xe_gt_debugfs_simple_show, 76 .data = xe_gt_sriov_pf_config_print_ctxs, 77 }, 78 { 79 "doorbells_provisioned", 80 .show = xe_gt_debugfs_simple_show, 81 .data = xe_gt_sriov_pf_config_print_dbs, 82 }, 83 { 84 "runtime_registers", 85 .show = xe_gt_debugfs_simple_show, 86 .data = xe_gt_sriov_pf_service_print_runtime, 87 }, 88 { 89 "negotiated_versions", 90 .show = xe_gt_debugfs_simple_show, 91 .data = xe_gt_sriov_pf_service_print_version, 92 }, 93 { 94 "adverse_events", 95 .show = xe_gt_debugfs_simple_show, 96 .data = xe_gt_sriov_pf_monitor_print_events, 97 }, 98 }; 99 100 /* 101 * /sys/kernel/debug/dri/0/ 102 * ├── gt0 103 * │ ├── pf 104 * │ │ ├── reset_engine 105 * │ │ ├── sample_period 106 * │ │ ├── sched_if_idle 107 */ 108 109 #define DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(POLICY, TYPE, FORMAT) \ 110 \ 111 static int POLICY##_set(void *data, u64 val) \ 112 { \ 113 struct xe_gt *gt = extract_gt(data); \ 114 struct xe_device *xe = gt_to_xe(gt); \ 115 int err; \ 116 \ 117 if (val > (TYPE)~0ull) \ 118 return -EOVERFLOW; \ 119 \ 120 xe_pm_runtime_get(xe); \ 121 err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val); \ 122 xe_pm_runtime_put(xe); \ 123 \ 124 return err; \ 125 } \ 126 \ 127 static int POLICY##_get(void *data, u64 *val) \ 128 { \ 129 struct xe_gt *gt = extract_gt(data); \ 130 \ 131 *val = xe_gt_sriov_pf_policy_get_##POLICY(gt); \ 132 return 0; \ 133 } \ 134 \ 135 DEFINE_DEBUGFS_ATTRIBUTE(POLICY##_fops, POLICY##_get, POLICY##_set, FORMAT) 136 137 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(reset_engine, bool, "%llu\n"); 138 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sched_if_idle, bool, "%llu\n"); 139 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sample_period, u32, "%llu\n"); 140 141 static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent) 142 { 143 xe_gt_assert(gt, gt == extract_gt(parent)); 144 xe_gt_assert(gt, PFID == extract_vfid(parent)); 145 146 debugfs_create_file_unsafe("reset_engine", 0644, parent, parent, &reset_engine_fops); 147 debugfs_create_file_unsafe("sched_if_idle", 0644, parent, parent, &sched_if_idle_fops); 148 debugfs_create_file_unsafe("sample_period_ms", 0644, parent, parent, &sample_period_fops); 149 } 150 151 /* 152 * /sys/kernel/debug/dri/0/ 153 * ├── gt0 154 * │ ├── pf 155 * │ │ ├── ggtt_spare 156 * │ │ ├── lmem_spare 157 * │ │ ├── doorbells_spare 158 * │ │ ├── contexts_spare 159 * │ │ ├── exec_quantum_ms 160 * │ │ ├── preempt_timeout_us 161 * │ ├── vf1 162 * │ │ ├── ggtt_quota 163 * │ │ ├── lmem_quota 164 * │ │ ├── doorbells_quota 165 * │ │ ├── contexts_quota 166 * │ │ ├── exec_quantum_ms 167 * │ │ ├── preempt_timeout_us 168 */ 169 170 #define DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(CONFIG, TYPE, FORMAT) \ 171 \ 172 static int CONFIG##_set(void *data, u64 val) \ 173 { \ 174 struct xe_gt *gt = extract_gt(data); \ 175 unsigned int vfid = extract_vfid(data); \ 176 struct xe_device *xe = gt_to_xe(gt); \ 177 int err; \ 178 \ 179 if (val > (TYPE)~0ull) \ 180 return -EOVERFLOW; \ 181 \ 182 xe_pm_runtime_get(xe); \ 183 err = xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \ 184 xe_pm_runtime_put(xe); \ 185 \ 186 return err; \ 187 } \ 188 \ 189 static int CONFIG##_get(void *data, u64 *val) \ 190 { \ 191 struct xe_gt *gt = extract_gt(data); \ 192 unsigned int vfid = extract_vfid(data); \ 193 \ 194 *val = xe_gt_sriov_pf_config_get_##CONFIG(gt, vfid); \ 195 return 0; \ 196 } \ 197 \ 198 DEFINE_DEBUGFS_ATTRIBUTE(CONFIG##_fops, CONFIG##_get, CONFIG##_set, FORMAT) 199 200 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ggtt, u64, "%llu\n"); 201 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(lmem, u64, "%llu\n"); 202 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ctxs, u32, "%llu\n"); 203 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(dbs, u32, "%llu\n"); 204 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(exec_quantum, u32, "%llu\n"); 205 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(preempt_timeout, u32, "%llu\n"); 206 207 /* 208 * /sys/kernel/debug/dri/0/ 209 * ├── gt0 210 * │ ├── pf 211 * │ │ ├── threshold_cat_error_count 212 * │ │ ├── threshold_doorbell_time_us 213 * │ │ ├── threshold_engine_reset_count 214 * │ │ ├── threshold_guc_time_us 215 * │ │ ├── threshold_irq_time_us 216 * │ │ ├── threshold_page_fault_count 217 * │ ├── vf1 218 * │ │ ├── threshold_cat_error_count 219 * │ │ ├── threshold_doorbell_time_us 220 * │ │ ├── threshold_engine_reset_count 221 * │ │ ├── threshold_guc_time_us 222 * │ │ ├── threshold_irq_time_us 223 * │ │ ├── threshold_page_fault_count 224 */ 225 226 static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index index) 227 { 228 struct xe_gt *gt = extract_gt(data); 229 unsigned int vfid = extract_vfid(data); 230 struct xe_device *xe = gt_to_xe(gt); 231 int err; 232 233 if (val > (u32)~0ull) 234 return -EOVERFLOW; 235 236 xe_pm_runtime_get(xe); 237 err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val); 238 xe_pm_runtime_put(xe); 239 240 return err; 241 } 242 243 static int get_threshold(void *data, u64 *val, enum xe_guc_klv_threshold_index index) 244 { 245 struct xe_gt *gt = extract_gt(data); 246 unsigned int vfid = extract_vfid(data); 247 248 *val = xe_gt_sriov_pf_config_get_threshold(gt, vfid, index); 249 return 0; 250 } 251 252 #define DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(THRESHOLD, INDEX) \ 253 \ 254 static int THRESHOLD##_set(void *data, u64 val) \ 255 { \ 256 return set_threshold(data, val, INDEX); \ 257 } \ 258 \ 259 static int THRESHOLD##_get(void *data, u64 *val) \ 260 { \ 261 return get_threshold(data, val, INDEX); \ 262 } \ 263 \ 264 DEFINE_DEBUGFS_ATTRIBUTE(THRESHOLD##_fops, THRESHOLD##_get, THRESHOLD##_set, "%llu\n") 265 266 /* generate all threshold attributes */ 267 #define define_threshold_attribute(TAG, NAME, ...) \ 268 DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(NAME, MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)); 269 MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_attribute) 270 #undef define_threshold_attribute 271 272 static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigned int vfid) 273 { 274 xe_gt_assert(gt, gt == extract_gt(parent)); 275 xe_gt_assert(gt, vfid == extract_vfid(parent)); 276 277 if (!xe_gt_is_media_type(gt)) { 278 debugfs_create_file_unsafe(vfid ? "ggtt_quota" : "ggtt_spare", 279 0644, parent, parent, &ggtt_fops); 280 if (IS_DGFX(gt_to_xe(gt))) 281 debugfs_create_file_unsafe(vfid ? "lmem_quota" : "lmem_spare", 282 0644, parent, parent, &lmem_fops); 283 } 284 debugfs_create_file_unsafe(vfid ? "doorbells_quota" : "doorbells_spare", 285 0644, parent, parent, &dbs_fops); 286 debugfs_create_file_unsafe(vfid ? "contexts_quota" : "contexts_spare", 287 0644, parent, parent, &ctxs_fops); 288 debugfs_create_file_unsafe("exec_quantum_ms", 0644, parent, parent, 289 &exec_quantum_fops); 290 debugfs_create_file_unsafe("preempt_timeout_us", 0644, parent, parent, 291 &preempt_timeout_fops); 292 293 /* register all threshold attributes */ 294 #define register_threshold_attribute(TAG, NAME, ...) \ 295 debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \ 296 &NAME##_fops); 297 MAKE_XE_GUC_KLV_THRESHOLDS_SET(register_threshold_attribute) 298 #undef register_threshold_attribute 299 } 300 301 /* 302 * /sys/kernel/debug/dri/0/ 303 * ├── gt0 304 * │ ├── vf1 305 * │ │ ├── control { stop, pause, resume } 306 */ 307 308 static const struct { 309 const char *cmd; 310 int (*fn)(struct xe_gt *gt, unsigned int vfid); 311 } control_cmds[] = { 312 { "stop", xe_gt_sriov_pf_control_stop_vf }, 313 { "pause", xe_gt_sriov_pf_control_pause_vf }, 314 { "resume", xe_gt_sriov_pf_control_resume_vf }, 315 }; 316 317 static ssize_t control_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) 318 { 319 struct dentry *dent = file_dentry(file); 320 struct dentry *parent = dent->d_parent; 321 struct xe_gt *gt = extract_gt(parent); 322 struct xe_device *xe = gt_to_xe(gt); 323 unsigned int vfid = extract_vfid(parent); 324 int ret = -EINVAL; 325 char cmd[32]; 326 size_t n; 327 328 xe_gt_assert(gt, vfid); 329 xe_gt_sriov_pf_assert_vfid(gt, vfid); 330 331 if (*pos) 332 return -ESPIPE; 333 334 if (count > sizeof(cmd) - 1) 335 return -EINVAL; 336 337 ret = simple_write_to_buffer(cmd, sizeof(cmd) - 1, pos, buf, count); 338 if (ret < 0) 339 return ret; 340 cmd[ret] = '\0'; 341 342 for (n = 0; n < ARRAY_SIZE(control_cmds); n++) { 343 xe_gt_assert(gt, sizeof(cmd) > strlen(control_cmds[n].cmd)); 344 345 if (sysfs_streq(cmd, control_cmds[n].cmd)) { 346 xe_pm_runtime_get(xe); 347 ret = control_cmds[n].fn ? (*control_cmds[n].fn)(gt, vfid) : 0; 348 xe_pm_runtime_put(xe); 349 break; 350 } 351 } 352 353 return (ret < 0) ? ret : count; 354 } 355 356 static ssize_t control_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 357 { 358 char help[128]; 359 size_t n; 360 361 help[0] = '\0'; 362 for (n = 0; n < ARRAY_SIZE(control_cmds); n++) { 363 strlcat(help, control_cmds[n].cmd, sizeof(help)); 364 strlcat(help, "\n", sizeof(help)); 365 } 366 367 return simple_read_from_buffer(buf, count, ppos, help, strlen(help)); 368 } 369 370 static const struct file_operations control_ops = { 371 .owner = THIS_MODULE, 372 .open = simple_open, 373 .write = control_write, 374 .read = control_read, 375 .llseek = default_llseek, 376 }; 377 378 /** 379 * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs. 380 * @gt: the &xe_gt to register 381 * @root: the &dentry that represents the GT directory 382 * 383 * Register SR-IOV PF entries that are GT related and must be shown under GT debugfs. 384 */ 385 void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root) 386 { 387 struct xe_device *xe = gt_to_xe(gt); 388 struct drm_minor *minor = xe->drm.primary; 389 int n, totalvfs = xe_sriov_pf_get_totalvfs(xe); 390 struct dentry *pfdentry; 391 struct dentry *vfdentry; 392 char buf[14]; /* should be enough up to "vf%u\0" for 2^32 - 1 */ 393 394 xe_gt_assert(gt, IS_SRIOV_PF(xe)); 395 xe_gt_assert(gt, root->d_inode->i_private == gt); 396 397 /* 398 * /sys/kernel/debug/dri/0/ 399 * ├── gt0 400 * │ ├── pf 401 */ 402 pfdentry = debugfs_create_dir("pf", root); 403 if (IS_ERR(pfdentry)) 404 return; 405 pfdentry->d_inode->i_private = gt; 406 407 drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), pfdentry, minor); 408 pf_add_policy_attrs(gt, pfdentry); 409 pf_add_config_attrs(gt, pfdentry, PFID); 410 411 for (n = 1; n <= totalvfs; n++) { 412 /* 413 * /sys/kernel/debug/dri/0/ 414 * ├── gt0 415 * │ ├── vf1 416 * │ ├── vf2 417 */ 418 snprintf(buf, sizeof(buf), "vf%u", n); 419 vfdentry = debugfs_create_dir(buf, root); 420 if (IS_ERR(vfdentry)) 421 break; 422 vfdentry->d_inode->i_private = (void *)(uintptr_t)n; 423 424 pf_add_config_attrs(gt, vfdentry, VFID(n)); 425 debugfs_create_file("control", 0600, vfdentry, NULL, &control_ops); 426 } 427 } 428