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