1 | /* |
2 | * Copyright © 2008 Intel Corporation |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice (including the next |
12 | * paragraph) shall be included in all copies or substantial portions of the |
13 | * Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
21 | * IN THE SOFTWARE. |
22 | * |
23 | * Authors: |
24 | * Eric Anholt <eric@anholt.net> |
25 | * Keith Packard <keithp@keithp.com> |
26 | * |
27 | */ |
28 | |
29 | #include <linux/sched/mm.h> |
30 | #include <linux/sort.h> |
31 | #include <linux/string_helpers.h> |
32 | |
33 | #include <drm/drm_debugfs.h> |
34 | |
35 | #include "gem/i915_gem_context.h" |
36 | #include "gt/intel_gt.h" |
37 | #include "gt/intel_gt_buffer_pool.h" |
38 | #include "gt/intel_gt_clock_utils.h" |
39 | #include "gt/intel_gt_debugfs.h" |
40 | #include "gt/intel_gt_pm.h" |
41 | #include "gt/intel_gt_pm_debugfs.h" |
42 | #include "gt/intel_gt_regs.h" |
43 | #include "gt/intel_gt_requests.h" |
44 | #include "gt/intel_rc6.h" |
45 | #include "gt/intel_reset.h" |
46 | #include "gt/intel_rps.h" |
47 | #include "gt/intel_sseu_debugfs.h" |
48 | |
49 | #include "i915_debugfs.h" |
50 | #include "i915_debugfs_params.h" |
51 | #include "i915_driver.h" |
52 | #include "i915_irq.h" |
53 | #include "i915_reg.h" |
54 | #include "i915_scheduler.h" |
55 | #include "intel_mchbar_regs.h" |
56 | |
57 | static inline struct drm_i915_private *node_to_i915(struct drm_info_node *node) |
58 | { |
59 | return to_i915(dev: node->minor->dev); |
60 | } |
61 | |
62 | static int i915_capabilities(struct seq_file *m, void *data) |
63 | { |
64 | struct drm_i915_private *i915 = node_to_i915(node: m->private); |
65 | struct drm_printer p = drm_seq_file_printer(f: m); |
66 | |
67 | seq_printf(m, fmt: "pch: %d\n" , INTEL_PCH_TYPE(i915)); |
68 | |
69 | intel_device_info_print(INTEL_INFO(i915), RUNTIME_INFO(i915), p: &p); |
70 | intel_display_device_info_print(DISPLAY_INFO(i915), DISPLAY_RUNTIME_INFO(i915), p: &p); |
71 | i915_print_iommu_status(i915, p: &p); |
72 | intel_gt_info_print(info: &to_gt(i915)->info, p: &p); |
73 | intel_driver_caps_print(caps: &i915->caps, p: &p); |
74 | |
75 | kernel_param_lock(THIS_MODULE); |
76 | i915_params_dump(params: &i915->params, p: &p); |
77 | kernel_param_unlock(THIS_MODULE); |
78 | |
79 | return 0; |
80 | } |
81 | |
82 | static char get_tiling_flag(struct drm_i915_gem_object *obj) |
83 | { |
84 | switch (i915_gem_object_get_tiling(obj)) { |
85 | default: |
86 | case I915_TILING_NONE: return ' '; |
87 | case I915_TILING_X: return 'X'; |
88 | case I915_TILING_Y: return 'Y'; |
89 | } |
90 | } |
91 | |
92 | static char get_global_flag(struct drm_i915_gem_object *obj) |
93 | { |
94 | return READ_ONCE(obj->userfault_count) ? 'g' : ' '; |
95 | } |
96 | |
97 | static char get_pin_mapped_flag(struct drm_i915_gem_object *obj) |
98 | { |
99 | return obj->mm.mapping ? 'M' : ' '; |
100 | } |
101 | |
102 | static const char * |
103 | stringify_page_sizes(unsigned int page_sizes, char *buf, size_t len) |
104 | { |
105 | size_t x = 0; |
106 | |
107 | switch (page_sizes) { |
108 | case 0: |
109 | return "" ; |
110 | case I915_GTT_PAGE_SIZE_4K: |
111 | return "4K" ; |
112 | case I915_GTT_PAGE_SIZE_64K: |
113 | return "64K" ; |
114 | case I915_GTT_PAGE_SIZE_2M: |
115 | return "2M" ; |
116 | default: |
117 | if (!buf) |
118 | return "M" ; |
119 | |
120 | if (page_sizes & I915_GTT_PAGE_SIZE_2M) |
121 | x += snprintf(buf: buf + x, size: len - x, fmt: "2M, " ); |
122 | if (page_sizes & I915_GTT_PAGE_SIZE_64K) |
123 | x += snprintf(buf: buf + x, size: len - x, fmt: "64K, " ); |
124 | if (page_sizes & I915_GTT_PAGE_SIZE_4K) |
125 | x += snprintf(buf: buf + x, size: len - x, fmt: "4K, " ); |
126 | buf[x-2] = '\0'; |
127 | |
128 | return buf; |
129 | } |
130 | } |
131 | |
132 | static const char *stringify_vma_type(const struct i915_vma *vma) |
133 | { |
134 | if (i915_vma_is_ggtt(vma)) |
135 | return "ggtt" ; |
136 | |
137 | if (i915_vma_is_dpt(vma)) |
138 | return "dpt" ; |
139 | |
140 | return "ppgtt" ; |
141 | } |
142 | |
143 | static const char *i915_cache_level_str(struct drm_i915_gem_object *obj) |
144 | { |
145 | struct drm_i915_private *i915 = obj_to_i915(obj); |
146 | |
147 | if (IS_GFX_GT_IP_RANGE(to_gt(i915), IP_VER(12, 70), IP_VER(12, 71))) { |
148 | switch (obj->pat_index) { |
149 | case 0: return " WB" ; |
150 | case 1: return " WT" ; |
151 | case 2: return " UC" ; |
152 | case 3: return " WB (1-Way Coh)" ; |
153 | case 4: return " WB (2-Way Coh)" ; |
154 | default: return " not defined" ; |
155 | } |
156 | } else if (IS_PONTEVECCHIO(i915)) { |
157 | switch (obj->pat_index) { |
158 | case 0: return " UC" ; |
159 | case 1: return " WC" ; |
160 | case 2: return " WT" ; |
161 | case 3: return " WB" ; |
162 | case 4: return " WT (CLOS1)" ; |
163 | case 5: return " WB (CLOS1)" ; |
164 | case 6: return " WT (CLOS2)" ; |
165 | case 7: return " WT (CLOS2)" ; |
166 | default: return " not defined" ; |
167 | } |
168 | } else if (GRAPHICS_VER(i915) >= 12) { |
169 | switch (obj->pat_index) { |
170 | case 0: return " WB" ; |
171 | case 1: return " WC" ; |
172 | case 2: return " WT" ; |
173 | case 3: return " UC" ; |
174 | default: return " not defined" ; |
175 | } |
176 | } else { |
177 | switch (obj->pat_index) { |
178 | case 0: return " UC" ; |
179 | case 1: return HAS_LLC(i915) ? |
180 | " LLC" : " snooped" ; |
181 | case 2: return " L3+LLC" ; |
182 | case 3: return " WT" ; |
183 | default: return " not defined" ; |
184 | } |
185 | } |
186 | } |
187 | |
188 | void |
189 | i915_debugfs_describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) |
190 | { |
191 | struct i915_vma *vma; |
192 | int pin_count = 0; |
193 | |
194 | seq_printf(m, fmt: "%pK: %c%c%c %8zdKiB %02x %02x %s%s%s" , |
195 | &obj->base, |
196 | get_tiling_flag(obj), |
197 | get_global_flag(obj), |
198 | get_pin_mapped_flag(obj), |
199 | obj->base.size / 1024, |
200 | obj->read_domains, |
201 | obj->write_domain, |
202 | i915_cache_level_str(obj), |
203 | obj->mm.dirty ? " dirty" : "" , |
204 | obj->mm.madv == I915_MADV_DONTNEED ? " purgeable" : "" ); |
205 | if (obj->base.name) |
206 | seq_printf(m, fmt: " (name: %d)" , obj->base.name); |
207 | |
208 | spin_lock(lock: &obj->vma.lock); |
209 | list_for_each_entry(vma, &obj->vma.list, obj_link) { |
210 | if (!drm_mm_node_allocated(node: &vma->node)) |
211 | continue; |
212 | |
213 | spin_unlock(lock: &obj->vma.lock); |
214 | |
215 | if (i915_vma_is_pinned(vma)) |
216 | pin_count++; |
217 | |
218 | seq_printf(m, fmt: " (%s offset: %08llx, size: %08llx, pages: %s" , |
219 | stringify_vma_type(vma), |
220 | i915_vma_offset(vma), i915_vma_size(vma), |
221 | stringify_page_sizes(page_sizes: vma->resource->page_sizes_gtt, |
222 | NULL, len: 0)); |
223 | if (i915_vma_is_ggtt(vma) || i915_vma_is_dpt(vma)) { |
224 | switch (vma->gtt_view.type) { |
225 | case I915_GTT_VIEW_NORMAL: |
226 | seq_puts(m, s: ", normal" ); |
227 | break; |
228 | |
229 | case I915_GTT_VIEW_PARTIAL: |
230 | seq_printf(m, fmt: ", partial [%08llx+%x]" , |
231 | vma->gtt_view.partial.offset << PAGE_SHIFT, |
232 | vma->gtt_view.partial.size << PAGE_SHIFT); |
233 | break; |
234 | |
235 | case I915_GTT_VIEW_ROTATED: |
236 | seq_printf(m, fmt: ", rotated [(%ux%u, src_stride=%u, dst_stride=%u, offset=%u), (%ux%u, src_stride=%u, dst_stride=%u, offset=%u)]" , |
237 | vma->gtt_view.rotated.plane[0].width, |
238 | vma->gtt_view.rotated.plane[0].height, |
239 | vma->gtt_view.rotated.plane[0].src_stride, |
240 | vma->gtt_view.rotated.plane[0].dst_stride, |
241 | vma->gtt_view.rotated.plane[0].offset, |
242 | vma->gtt_view.rotated.plane[1].width, |
243 | vma->gtt_view.rotated.plane[1].height, |
244 | vma->gtt_view.rotated.plane[1].src_stride, |
245 | vma->gtt_view.rotated.plane[1].dst_stride, |
246 | vma->gtt_view.rotated.plane[1].offset); |
247 | break; |
248 | |
249 | case I915_GTT_VIEW_REMAPPED: |
250 | seq_printf(m, fmt: ", remapped [(%ux%u, src_stride=%u, dst_stride=%u, offset=%u), (%ux%u, src_stride=%u, dst_stride=%u, offset=%u)]" , |
251 | vma->gtt_view.remapped.plane[0].width, |
252 | vma->gtt_view.remapped.plane[0].height, |
253 | vma->gtt_view.remapped.plane[0].src_stride, |
254 | vma->gtt_view.remapped.plane[0].dst_stride, |
255 | vma->gtt_view.remapped.plane[0].offset, |
256 | vma->gtt_view.remapped.plane[1].width, |
257 | vma->gtt_view.remapped.plane[1].height, |
258 | vma->gtt_view.remapped.plane[1].src_stride, |
259 | vma->gtt_view.remapped.plane[1].dst_stride, |
260 | vma->gtt_view.remapped.plane[1].offset); |
261 | break; |
262 | |
263 | default: |
264 | MISSING_CASE(vma->gtt_view.type); |
265 | break; |
266 | } |
267 | } |
268 | if (vma->fence) |
269 | seq_printf(m, fmt: " , fence: %d" , vma->fence->id); |
270 | seq_puts(m, s: ")" ); |
271 | |
272 | spin_lock(lock: &obj->vma.lock); |
273 | } |
274 | spin_unlock(lock: &obj->vma.lock); |
275 | |
276 | seq_printf(m, fmt: " (pinned x %d)" , pin_count); |
277 | if (i915_gem_object_is_stolen(obj)) |
278 | seq_printf(m, fmt: " (stolen: %08llx)" , obj->stolen->start); |
279 | if (i915_gem_object_is_framebuffer(obj)) |
280 | seq_printf(m, fmt: " (fb)" ); |
281 | } |
282 | |
283 | static int i915_gem_object_info(struct seq_file *m, void *data) |
284 | { |
285 | struct drm_i915_private *i915 = node_to_i915(node: m->private); |
286 | struct drm_printer p = drm_seq_file_printer(f: m); |
287 | struct intel_memory_region *mr; |
288 | enum intel_region_id id; |
289 | |
290 | seq_printf(m, fmt: "%u shrinkable [%u free] objects, %llu bytes\n" , |
291 | i915->mm.shrink_count, |
292 | atomic_read(v: &i915->mm.free_count), |
293 | i915->mm.shrink_memory); |
294 | for_each_memory_region(mr, i915, id) |
295 | intel_memory_region_debug(mr, printer: &p); |
296 | |
297 | return 0; |
298 | } |
299 | |
300 | #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR) |
301 | static ssize_t gpu_state_read(struct file *file, char __user *ubuf, |
302 | size_t count, loff_t *pos) |
303 | { |
304 | struct i915_gpu_coredump *error; |
305 | ssize_t ret; |
306 | void *buf; |
307 | |
308 | error = file->private_data; |
309 | if (!error) |
310 | return 0; |
311 | |
312 | /* Bounce buffer required because of kernfs __user API convenience. */ |
313 | buf = kmalloc(size: count, GFP_KERNEL); |
314 | if (!buf) |
315 | return -ENOMEM; |
316 | |
317 | ret = i915_gpu_coredump_copy_to_buffer(error, buf, offset: *pos, count); |
318 | if (ret <= 0) |
319 | goto out; |
320 | |
321 | if (!copy_to_user(to: ubuf, from: buf, n: ret)) |
322 | *pos += ret; |
323 | else |
324 | ret = -EFAULT; |
325 | |
326 | out: |
327 | kfree(objp: buf); |
328 | return ret; |
329 | } |
330 | |
331 | static int gpu_state_release(struct inode *inode, struct file *file) |
332 | { |
333 | i915_gpu_coredump_put(gpu: file->private_data); |
334 | return 0; |
335 | } |
336 | |
337 | static int i915_gpu_info_open(struct inode *inode, struct file *file) |
338 | { |
339 | struct drm_i915_private *i915 = inode->i_private; |
340 | struct i915_gpu_coredump *gpu; |
341 | intel_wakeref_t wakeref; |
342 | |
343 | gpu = NULL; |
344 | with_intel_runtime_pm(&i915->runtime_pm, wakeref) |
345 | gpu = i915_gpu_coredump(gt: to_gt(i915), ALL_ENGINES, CORE_DUMP_FLAG_NONE); |
346 | |
347 | if (IS_ERR(ptr: gpu)) |
348 | return PTR_ERR(ptr: gpu); |
349 | |
350 | file->private_data = gpu; |
351 | return 0; |
352 | } |
353 | |
354 | static const struct file_operations i915_gpu_info_fops = { |
355 | .owner = THIS_MODULE, |
356 | .open = i915_gpu_info_open, |
357 | .read = gpu_state_read, |
358 | .llseek = default_llseek, |
359 | .release = gpu_state_release, |
360 | }; |
361 | |
362 | static ssize_t |
363 | i915_error_state_write(struct file *filp, |
364 | const char __user *ubuf, |
365 | size_t cnt, |
366 | loff_t *ppos) |
367 | { |
368 | struct i915_gpu_coredump *error = filp->private_data; |
369 | |
370 | if (!error) |
371 | return 0; |
372 | |
373 | drm_dbg(&error->i915->drm, "Resetting error state\n" ); |
374 | i915_reset_error_state(i915: error->i915); |
375 | |
376 | return cnt; |
377 | } |
378 | |
379 | static int i915_error_state_open(struct inode *inode, struct file *file) |
380 | { |
381 | struct i915_gpu_coredump *error; |
382 | |
383 | error = i915_first_error_state(i915: inode->i_private); |
384 | if (IS_ERR(ptr: error)) |
385 | return PTR_ERR(ptr: error); |
386 | |
387 | file->private_data = error; |
388 | return 0; |
389 | } |
390 | |
391 | static const struct file_operations i915_error_state_fops = { |
392 | .owner = THIS_MODULE, |
393 | .open = i915_error_state_open, |
394 | .read = gpu_state_read, |
395 | .write = i915_error_state_write, |
396 | .llseek = default_llseek, |
397 | .release = gpu_state_release, |
398 | }; |
399 | #endif |
400 | |
401 | static int i915_frequency_info(struct seq_file *m, void *unused) |
402 | { |
403 | struct drm_i915_private *i915 = node_to_i915(node: m->private); |
404 | struct intel_gt *gt = to_gt(i915); |
405 | struct drm_printer p = drm_seq_file_printer(f: m); |
406 | |
407 | intel_gt_pm_frequency_dump(gt, m: &p); |
408 | |
409 | return 0; |
410 | } |
411 | |
412 | static const char *swizzle_string(unsigned swizzle) |
413 | { |
414 | switch (swizzle) { |
415 | case I915_BIT_6_SWIZZLE_NONE: |
416 | return "none" ; |
417 | case I915_BIT_6_SWIZZLE_9: |
418 | return "bit9" ; |
419 | case I915_BIT_6_SWIZZLE_9_10: |
420 | return "bit9/bit10" ; |
421 | case I915_BIT_6_SWIZZLE_9_11: |
422 | return "bit9/bit11" ; |
423 | case I915_BIT_6_SWIZZLE_9_10_11: |
424 | return "bit9/bit10/bit11" ; |
425 | case I915_BIT_6_SWIZZLE_9_17: |
426 | return "bit9/bit17" ; |
427 | case I915_BIT_6_SWIZZLE_9_10_17: |
428 | return "bit9/bit10/bit17" ; |
429 | case I915_BIT_6_SWIZZLE_UNKNOWN: |
430 | return "unknown" ; |
431 | } |
432 | |
433 | return "bug" ; |
434 | } |
435 | |
436 | static int i915_swizzle_info(struct seq_file *m, void *data) |
437 | { |
438 | struct drm_i915_private *dev_priv = node_to_i915(node: m->private); |
439 | struct intel_uncore *uncore = &dev_priv->uncore; |
440 | intel_wakeref_t wakeref; |
441 | |
442 | seq_printf(m, fmt: "bit6 swizzle for X-tiling = %s\n" , |
443 | swizzle_string(swizzle: to_gt(i915: dev_priv)->ggtt->bit_6_swizzle_x)); |
444 | seq_printf(m, fmt: "bit6 swizzle for Y-tiling = %s\n" , |
445 | swizzle_string(swizzle: to_gt(i915: dev_priv)->ggtt->bit_6_swizzle_y)); |
446 | |
447 | if (dev_priv->gem_quirks & GEM_QUIRK_PIN_SWIZZLED_PAGES) |
448 | seq_puts(m, s: "L-shaped memory detected\n" ); |
449 | |
450 | /* On BDW+, swizzling is not used. See detect_bit_6_swizzle() */ |
451 | if (GRAPHICS_VER(dev_priv) >= 8 || IS_VALLEYVIEW(dev_priv)) |
452 | return 0; |
453 | |
454 | wakeref = intel_runtime_pm_get(rpm: &dev_priv->runtime_pm); |
455 | |
456 | if (IS_GRAPHICS_VER(dev_priv, 3, 4)) { |
457 | seq_printf(m, fmt: "DDC = 0x%08x\n" , |
458 | intel_uncore_read(uncore, DCC)); |
459 | seq_printf(m, fmt: "DDC2 = 0x%08x\n" , |
460 | intel_uncore_read(uncore, DCC2)); |
461 | seq_printf(m, fmt: "C0DRB3 = 0x%04x\n" , |
462 | intel_uncore_read16(uncore, C0DRB3_BW)); |
463 | seq_printf(m, fmt: "C1DRB3 = 0x%04x\n" , |
464 | intel_uncore_read16(uncore, C1DRB3_BW)); |
465 | } else if (GRAPHICS_VER(dev_priv) >= 6) { |
466 | seq_printf(m, fmt: "MAD_DIMM_C0 = 0x%08x\n" , |
467 | intel_uncore_read(uncore, MAD_DIMM_C0)); |
468 | seq_printf(m, fmt: "MAD_DIMM_C1 = 0x%08x\n" , |
469 | intel_uncore_read(uncore, MAD_DIMM_C1)); |
470 | seq_printf(m, fmt: "MAD_DIMM_C2 = 0x%08x\n" , |
471 | intel_uncore_read(uncore, MAD_DIMM_C2)); |
472 | seq_printf(m, fmt: "TILECTL = 0x%08x\n" , |
473 | intel_uncore_read(uncore, TILECTL)); |
474 | if (GRAPHICS_VER(dev_priv) >= 8) |
475 | seq_printf(m, fmt: "GAMTARBMODE = 0x%08x\n" , |
476 | intel_uncore_read(uncore, GAMTARBMODE)); |
477 | else |
478 | seq_printf(m, fmt: "ARB_MODE = 0x%08x\n" , |
479 | intel_uncore_read(uncore, ARB_MODE)); |
480 | seq_printf(m, fmt: "DISP_ARB_CTL = 0x%08x\n" , |
481 | intel_uncore_read(uncore, DISP_ARB_CTL)); |
482 | } |
483 | |
484 | intel_runtime_pm_put(rpm: &dev_priv->runtime_pm, wref: wakeref); |
485 | |
486 | return 0; |
487 | } |
488 | |
489 | static int i915_rps_boost_info(struct seq_file *m, void *data) |
490 | { |
491 | struct drm_i915_private *dev_priv = node_to_i915(node: m->private); |
492 | struct intel_rps *rps = &to_gt(i915: dev_priv)->rps; |
493 | |
494 | seq_printf(m, fmt: "RPS enabled? %s\n" , |
495 | str_yes_no(v: intel_rps_is_enabled(rps))); |
496 | seq_printf(m, fmt: "RPS active? %s\n" , |
497 | str_yes_no(v: intel_rps_is_active(rps))); |
498 | seq_printf(m, fmt: "GPU busy? %s\n" , str_yes_no(v: to_gt(i915: dev_priv)->awake)); |
499 | seq_printf(m, fmt: "Boosts outstanding? %d\n" , |
500 | atomic_read(v: &rps->num_waiters)); |
501 | seq_printf(m, fmt: "Interactive? %d\n" , READ_ONCE(rps->power.interactive)); |
502 | seq_printf(m, fmt: "Frequency requested %d, actual %d\n" , |
503 | intel_gpu_freq(rps, val: rps->cur_freq), |
504 | intel_rps_read_actual_frequency(rps)); |
505 | seq_printf(m, fmt: " min hard:%d, soft:%d; max soft:%d, hard:%d\n" , |
506 | intel_gpu_freq(rps, val: rps->min_freq), |
507 | intel_gpu_freq(rps, val: rps->min_freq_softlimit), |
508 | intel_gpu_freq(rps, val: rps->max_freq_softlimit), |
509 | intel_gpu_freq(rps, val: rps->max_freq)); |
510 | seq_printf(m, fmt: " idle:%d, efficient:%d, boost:%d\n" , |
511 | intel_gpu_freq(rps, val: rps->idle_freq), |
512 | intel_gpu_freq(rps, val: rps->efficient_freq), |
513 | intel_gpu_freq(rps, val: rps->boost_freq)); |
514 | |
515 | seq_printf(m, fmt: "Wait boosts: %d\n" , READ_ONCE(rps->boosts)); |
516 | |
517 | return 0; |
518 | } |
519 | |
520 | static int i915_runtime_pm_status(struct seq_file *m, void *unused) |
521 | { |
522 | struct drm_i915_private *dev_priv = node_to_i915(node: m->private); |
523 | struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); |
524 | |
525 | if (!HAS_RUNTIME_PM(dev_priv)) |
526 | seq_puts(m, s: "Runtime power management not supported\n" ); |
527 | |
528 | seq_printf(m, fmt: "Runtime power status: %s\n" , |
529 | str_enabled_disabled(v: !dev_priv->display.power.domains.init_wakeref)); |
530 | |
531 | seq_printf(m, fmt: "GPU idle: %s\n" , str_yes_no(v: !to_gt(i915: dev_priv)->awake)); |
532 | seq_printf(m, fmt: "IRQs disabled: %s\n" , |
533 | str_yes_no(v: !intel_irqs_enabled(dev_priv))); |
534 | #ifdef CONFIG_PM |
535 | seq_printf(m, fmt: "Usage count: %d\n" , |
536 | atomic_read(v: &dev_priv->drm.dev->power.usage_count)); |
537 | #else |
538 | seq_printf(m, "Device Power Management (CONFIG_PM) disabled\n" ); |
539 | #endif |
540 | seq_printf(m, fmt: "PCI device power state: %s [%d]\n" , |
541 | pci_power_name(state: pdev->current_state), |
542 | pdev->current_state); |
543 | |
544 | if (IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM)) { |
545 | struct drm_printer p = drm_seq_file_printer(f: m); |
546 | |
547 | print_intel_runtime_pm_wakeref(rpm: &dev_priv->runtime_pm, p: &p); |
548 | } |
549 | |
550 | return 0; |
551 | } |
552 | |
553 | static int i915_engine_info(struct seq_file *m, void *unused) |
554 | { |
555 | struct drm_i915_private *i915 = node_to_i915(node: m->private); |
556 | struct intel_engine_cs *engine; |
557 | intel_wakeref_t wakeref; |
558 | struct drm_printer p; |
559 | |
560 | wakeref = intel_runtime_pm_get(rpm: &i915->runtime_pm); |
561 | |
562 | seq_printf(m, fmt: "GT awake? %s [%d], %llums\n" , |
563 | str_yes_no(v: to_gt(i915)->awake), |
564 | atomic_read(v: &to_gt(i915)->wakeref.count), |
565 | ktime_to_ms(kt: intel_gt_get_awake_time(gt: to_gt(i915)))); |
566 | seq_printf(m, fmt: "CS timestamp frequency: %u Hz, %d ns\n" , |
567 | to_gt(i915)->clock_frequency, |
568 | to_gt(i915)->clock_period_ns); |
569 | |
570 | p = drm_seq_file_printer(f: m); |
571 | for_each_uabi_engine(engine, i915) |
572 | intel_engine_dump(engine, m: &p, header: "%s\n" , engine->name); |
573 | |
574 | intel_gt_show_timelines(gt: to_gt(i915), m: &p, show_request: i915_request_show_with_schedule); |
575 | |
576 | intel_runtime_pm_put(rpm: &i915->runtime_pm, wref: wakeref); |
577 | |
578 | return 0; |
579 | } |
580 | |
581 | static int i915_wa_registers(struct seq_file *m, void *unused) |
582 | { |
583 | struct drm_i915_private *i915 = node_to_i915(node: m->private); |
584 | struct intel_engine_cs *engine; |
585 | |
586 | for_each_uabi_engine(engine, i915) { |
587 | const struct i915_wa_list *wal = &engine->ctx_wa_list; |
588 | const struct i915_wa *wa; |
589 | unsigned int count; |
590 | |
591 | count = wal->count; |
592 | if (!count) |
593 | continue; |
594 | |
595 | seq_printf(m, fmt: "%s: Workarounds applied: %u\n" , |
596 | engine->name, count); |
597 | |
598 | for (wa = wal->list; count--; wa++) |
599 | seq_printf(m, fmt: "0x%X: 0x%08X, mask: 0x%08X\n" , |
600 | i915_mmio_reg_offset(wa->reg), |
601 | wa->set, wa->clr); |
602 | |
603 | seq_printf(m, fmt: "\n" ); |
604 | } |
605 | |
606 | return 0; |
607 | } |
608 | |
609 | static int i915_wedged_get(void *data, u64 *val) |
610 | { |
611 | struct drm_i915_private *i915 = data; |
612 | struct intel_gt *gt; |
613 | unsigned int i; |
614 | |
615 | *val = 0; |
616 | |
617 | for_each_gt(gt, i915, i) { |
618 | int ret; |
619 | |
620 | ret = intel_gt_debugfs_reset_show(gt, val); |
621 | if (ret) |
622 | return ret; |
623 | |
624 | /* at least one tile should be wedged */ |
625 | if (*val) |
626 | break; |
627 | } |
628 | |
629 | return 0; |
630 | } |
631 | |
632 | static int i915_wedged_set(void *data, u64 val) |
633 | { |
634 | struct drm_i915_private *i915 = data; |
635 | struct intel_gt *gt; |
636 | unsigned int i; |
637 | |
638 | for_each_gt(gt, i915, i) |
639 | intel_gt_debugfs_reset_store(gt, val); |
640 | |
641 | return 0; |
642 | } |
643 | |
644 | DEFINE_SIMPLE_ATTRIBUTE(i915_wedged_fops, |
645 | i915_wedged_get, i915_wedged_set, |
646 | "%llu\n" ); |
647 | |
648 | static int |
649 | i915_perf_noa_delay_set(void *data, u64 val) |
650 | { |
651 | struct drm_i915_private *i915 = data; |
652 | |
653 | /* |
654 | * This would lead to infinite waits as we're doing timestamp |
655 | * difference on the CS with only 32bits. |
656 | */ |
657 | if (intel_gt_ns_to_clock_interval(gt: to_gt(i915), ns: val) > U32_MAX) |
658 | return -EINVAL; |
659 | |
660 | atomic64_set(v: &i915->perf.noa_programming_delay, i: val); |
661 | return 0; |
662 | } |
663 | |
664 | static int |
665 | i915_perf_noa_delay_get(void *data, u64 *val) |
666 | { |
667 | struct drm_i915_private *i915 = data; |
668 | |
669 | *val = atomic64_read(v: &i915->perf.noa_programming_delay); |
670 | return 0; |
671 | } |
672 | |
673 | DEFINE_SIMPLE_ATTRIBUTE(i915_perf_noa_delay_fops, |
674 | i915_perf_noa_delay_get, |
675 | i915_perf_noa_delay_set, |
676 | "%llu\n" ); |
677 | |
678 | #define DROP_UNBOUND BIT(0) |
679 | #define DROP_BOUND BIT(1) |
680 | #define DROP_RETIRE BIT(2) |
681 | #define DROP_ACTIVE BIT(3) |
682 | #define DROP_FREED BIT(4) |
683 | #define DROP_SHRINK_ALL BIT(5) |
684 | #define DROP_IDLE BIT(6) |
685 | #define DROP_RESET_ACTIVE BIT(7) |
686 | #define DROP_RESET_SEQNO BIT(8) |
687 | #define DROP_RCU BIT(9) |
688 | #define DROP_ALL (DROP_UNBOUND | \ |
689 | DROP_BOUND | \ |
690 | DROP_RETIRE | \ |
691 | DROP_ACTIVE | \ |
692 | DROP_FREED | \ |
693 | DROP_SHRINK_ALL |\ |
694 | DROP_IDLE | \ |
695 | DROP_RESET_ACTIVE | \ |
696 | DROP_RESET_SEQNO | \ |
697 | DROP_RCU) |
698 | static int |
699 | i915_drop_caches_get(void *data, u64 *val) |
700 | { |
701 | *val = DROP_ALL; |
702 | |
703 | return 0; |
704 | } |
705 | |
706 | static int |
707 | gt_drop_caches(struct intel_gt *gt, u64 val) |
708 | { |
709 | int ret; |
710 | |
711 | if (val & DROP_RESET_ACTIVE && |
712 | wait_for(intel_engines_are_idle(gt), 200)) |
713 | intel_gt_set_wedged(gt); |
714 | |
715 | if (val & DROP_RETIRE) |
716 | intel_gt_retire_requests(gt); |
717 | |
718 | if (val & (DROP_IDLE | DROP_ACTIVE)) { |
719 | ret = intel_gt_wait_for_idle(gt, MAX_SCHEDULE_TIMEOUT); |
720 | if (ret) |
721 | return ret; |
722 | } |
723 | |
724 | if (val & DROP_IDLE) { |
725 | ret = intel_gt_pm_wait_for_idle(gt); |
726 | if (ret) |
727 | return ret; |
728 | } |
729 | |
730 | if (val & DROP_RESET_ACTIVE && intel_gt_terminally_wedged(gt)) |
731 | intel_gt_handle_error(gt, ALL_ENGINES, flags: 0, NULL); |
732 | |
733 | if (val & DROP_FREED) |
734 | intel_gt_flush_buffer_pool(gt); |
735 | |
736 | return 0; |
737 | } |
738 | |
739 | static int |
740 | i915_drop_caches_set(void *data, u64 val) |
741 | { |
742 | struct drm_i915_private *i915 = data; |
743 | struct intel_gt *gt; |
744 | unsigned int flags; |
745 | unsigned int i; |
746 | int ret; |
747 | |
748 | drm_dbg(&i915->drm, "Dropping caches: 0x%08llx [0x%08llx]\n" , |
749 | val, val & DROP_ALL); |
750 | |
751 | for_each_gt(gt, i915, i) { |
752 | ret = gt_drop_caches(gt, val); |
753 | if (ret) |
754 | return ret; |
755 | } |
756 | |
757 | fs_reclaim_acquire(GFP_KERNEL); |
758 | flags = memalloc_noreclaim_save(); |
759 | if (val & DROP_BOUND) |
760 | i915_gem_shrink(NULL, i915, LONG_MAX, NULL, I915_SHRINK_BOUND); |
761 | |
762 | if (val & DROP_UNBOUND) |
763 | i915_gem_shrink(NULL, i915, LONG_MAX, NULL, I915_SHRINK_UNBOUND); |
764 | |
765 | if (val & DROP_SHRINK_ALL) |
766 | i915_gem_shrink_all(i915); |
767 | memalloc_noreclaim_restore(flags); |
768 | fs_reclaim_release(GFP_KERNEL); |
769 | |
770 | if (val & DROP_RCU) |
771 | rcu_barrier(); |
772 | |
773 | if (val & DROP_FREED) |
774 | i915_gem_drain_freed_objects(i915); |
775 | |
776 | return 0; |
777 | } |
778 | |
779 | DEFINE_SIMPLE_ATTRIBUTE(i915_drop_caches_fops, |
780 | i915_drop_caches_get, i915_drop_caches_set, |
781 | "0x%08llx\n" ); |
782 | |
783 | static int i915_sseu_status(struct seq_file *m, void *unused) |
784 | { |
785 | struct drm_i915_private *i915 = node_to_i915(node: m->private); |
786 | struct intel_gt *gt = to_gt(i915); |
787 | |
788 | return intel_sseu_status(m, gt); |
789 | } |
790 | |
791 | static int i915_forcewake_open(struct inode *inode, struct file *file) |
792 | { |
793 | struct drm_i915_private *i915 = inode->i_private; |
794 | struct intel_gt *gt; |
795 | unsigned int i; |
796 | |
797 | for_each_gt(gt, i915, i) |
798 | intel_gt_pm_debugfs_forcewake_user_open(gt); |
799 | |
800 | return 0; |
801 | } |
802 | |
803 | static int i915_forcewake_release(struct inode *inode, struct file *file) |
804 | { |
805 | struct drm_i915_private *i915 = inode->i_private; |
806 | struct intel_gt *gt; |
807 | unsigned int i; |
808 | |
809 | for_each_gt(gt, i915, i) |
810 | intel_gt_pm_debugfs_forcewake_user_release(gt); |
811 | |
812 | return 0; |
813 | } |
814 | |
815 | static const struct file_operations i915_forcewake_fops = { |
816 | .owner = THIS_MODULE, |
817 | .open = i915_forcewake_open, |
818 | .release = i915_forcewake_release, |
819 | }; |
820 | |
821 | static const struct drm_info_list i915_debugfs_list[] = { |
822 | {"i915_capabilities" , i915_capabilities, 0}, |
823 | {"i915_gem_objects" , i915_gem_object_info, 0}, |
824 | {"i915_frequency_info" , i915_frequency_info, 0}, |
825 | {"i915_swizzle_info" , i915_swizzle_info, 0}, |
826 | {"i915_runtime_pm_status" , i915_runtime_pm_status, 0}, |
827 | {"i915_engine_info" , i915_engine_info, 0}, |
828 | {"i915_wa_registers" , i915_wa_registers, 0}, |
829 | {"i915_sseu_status" , i915_sseu_status, 0}, |
830 | {"i915_rps_boost_info" , i915_rps_boost_info, 0}, |
831 | }; |
832 | |
833 | static const struct i915_debugfs_files { |
834 | const char *name; |
835 | const struct file_operations *fops; |
836 | } i915_debugfs_files[] = { |
837 | {"i915_perf_noa_delay" , &i915_perf_noa_delay_fops}, |
838 | {"i915_wedged" , &i915_wedged_fops}, |
839 | {"i915_gem_drop_caches" , &i915_drop_caches_fops}, |
840 | #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR) |
841 | {"i915_error_state" , &i915_error_state_fops}, |
842 | {"i915_gpu_info" , &i915_gpu_info_fops}, |
843 | #endif |
844 | }; |
845 | |
846 | void i915_debugfs_register(struct drm_i915_private *dev_priv) |
847 | { |
848 | struct drm_minor *minor = dev_priv->drm.primary; |
849 | int i; |
850 | |
851 | i915_debugfs_params(i915: dev_priv); |
852 | |
853 | debugfs_create_file(name: "i915_forcewake_user" , S_IRUSR, parent: minor->debugfs_root, |
854 | data: to_i915(dev: minor->dev), fops: &i915_forcewake_fops); |
855 | for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) { |
856 | debugfs_create_file(name: i915_debugfs_files[i].name, |
857 | S_IRUGO | S_IWUSR, |
858 | parent: minor->debugfs_root, |
859 | data: to_i915(dev: minor->dev), |
860 | fops: i915_debugfs_files[i].fops); |
861 | } |
862 | |
863 | drm_debugfs_create_files(files: i915_debugfs_list, |
864 | ARRAY_SIZE(i915_debugfs_list), |
865 | root: minor->debugfs_root, minor); |
866 | } |
867 | |