#include <linux/ascii85.h>
#include <linux/debugfs.h>
#include <linux/highmem.h>
#include <linux/nmi.h>
#include <linux/pagevec.h>
#include <linux/scatterlist.h>
#include <linux/string_helpers.h>
#include <linux/utsname.h>
#include <linux/zlib.h>
#include <drm/drm_cache.h>
#include <drm/drm_print.h>
#include "display/intel_dmc.h"
#include "display/intel_overlay.h"
#include "gem/i915_gem_context.h"
#include "gem/i915_gem_lmem.h"
#include "gt/intel_engine_regs.h"
#include "gt/intel_gt.h"
#include "gt/intel_gt_mcr.h"
#include "gt/intel_gt_pm.h"
#include "gt/intel_gt_regs.h"
#include "gt/uc/intel_guc_capture.h"
#include "i915_driver.h"
#include "i915_drv.h"
#include "i915_gpu_error.h"
#include "i915_memcpy.h"
#include "i915_reg.h"
#include "i915_scatterlist.h"
#include "i915_sysfs.h"
#include "i915_utils.h"
#define ALLOW_FAIL …
#define ATOMIC_MAYFAIL …
static void __sg_set_buf(struct scatterlist *sg,
void *addr, unsigned int len, loff_t it)
{ … }
static bool __i915_error_grow(struct drm_i915_error_state_buf *e, size_t len)
{ … }
__printf(2, 0)
static void i915_error_vprintf(struct drm_i915_error_state_buf *e,
const char *fmt, va_list args)
{ … }
static void i915_error_puts(struct drm_i915_error_state_buf *e, const char *str)
{ … }
#define err_printf(e, ...) …
#define err_puts(e, s) …
static void __i915_printfn_error(struct drm_printer *p, struct va_format *vaf)
{ … }
static inline struct drm_printer
i915_error_printer(struct drm_i915_error_state_buf *e)
{ … }
static void pool_fini(struct folio_batch *fbatch)
{ … }
static int pool_refill(struct folio_batch *fbatch, gfp_t gfp)
{ … }
static int pool_init(struct folio_batch *fbatch, gfp_t gfp)
{ … }
static void *pool_alloc(struct folio_batch *fbatch, gfp_t gfp)
{ … }
static void pool_free(struct folio_batch *fbatch, void *addr)
{ … }
#ifdef CONFIG_DRM_I915_COMPRESS_ERROR
struct i915_vma_compress { … };
static bool compress_init(struct i915_vma_compress *c)
{ … }
static bool compress_start(struct i915_vma_compress *c)
{ … }
static void *compress_next_page(struct i915_vma_compress *c,
struct i915_vma_coredump *dst)
{ … }
static int compress_page(struct i915_vma_compress *c,
void *src,
struct i915_vma_coredump *dst,
bool wc)
{ … }
static int compress_flush(struct i915_vma_compress *c,
struct i915_vma_coredump *dst)
{ … }
static void compress_finish(struct i915_vma_compress *c)
{ … }
static void compress_fini(struct i915_vma_compress *c)
{ … }
static void err_compression_marker(struct drm_i915_error_state_buf *m)
{ … }
#else
struct i915_vma_compress {
struct folio_batch pool;
};
static bool compress_init(struct i915_vma_compress *c)
{
return pool_init(&c->pool, ALLOW_FAIL) == 0;
}
static bool compress_start(struct i915_vma_compress *c)
{
return true;
}
static int compress_page(struct i915_vma_compress *c,
void *src,
struct i915_vma_coredump *dst,
bool wc)
{
void *ptr;
ptr = pool_alloc(&c->pool, ALLOW_FAIL);
if (!ptr)
return -ENOMEM;
if (!(wc && i915_memcpy_from_wc(ptr, src, PAGE_SIZE)))
memcpy(ptr, src, PAGE_SIZE);
list_add_tail(&virt_to_page(ptr)->lru, &dst->page_list);
cond_resched();
return 0;
}
static int compress_flush(struct i915_vma_compress *c,
struct i915_vma_coredump *dst)
{
return 0;
}
static void compress_finish(struct i915_vma_compress *c)
{
}
static void compress_fini(struct i915_vma_compress *c)
{
pool_fini(&c->pool);
}
static void err_compression_marker(struct drm_i915_error_state_buf *m)
{
err_puts(m, "~");
}
#endif
static void error_print_instdone(struct drm_i915_error_state_buf *m,
const struct intel_engine_coredump *ee)
{ … }
static void error_print_request(struct drm_i915_error_state_buf *m,
const char *prefix,
const struct i915_request_coredump *erq)
{ … }
static void error_print_context(struct drm_i915_error_state_buf *m,
const char *header,
const struct i915_gem_context_coredump *ctx)
{ … }
static struct i915_vma_coredump *
__find_vma(struct i915_vma_coredump *vma, const char *name)
{ … }
static struct i915_vma_coredump *
intel_gpu_error_find_batch(const struct intel_engine_coredump *ee)
{ … }
static void error_print_engine(struct drm_i915_error_state_buf *m,
const struct intel_engine_coredump *ee)
{ … }
void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...)
{ … }
static void intel_gpu_error_print_vma(struct drm_i915_error_state_buf *m,
const struct intel_engine_cs *engine,
const struct i915_vma_coredump *vma)
{ … }
static void err_print_capabilities(struct drm_i915_error_state_buf *m,
struct i915_gpu_coredump *error)
{ … }
static void err_print_params(struct drm_i915_error_state_buf *m,
const struct i915_params *params)
{ … }
static void err_print_pciid(struct drm_i915_error_state_buf *m,
struct drm_i915_private *i915)
{ … }
static void err_print_guc_ctb(struct drm_i915_error_state_buf *m,
const char *name,
const struct intel_ctb_coredump *ctb)
{ … }
static void err_print_uc(struct drm_i915_error_state_buf *m,
const struct intel_uc_coredump *error_uc)
{ … }
static void err_free_sgl(struct scatterlist *sgl)
{ … }
static void err_print_gt_info(struct drm_i915_error_state_buf *m,
struct intel_gt_coredump *gt)
{ … }
static void err_print_gt_display(struct drm_i915_error_state_buf *m,
struct intel_gt_coredump *gt)
{ … }
static void err_print_gt_global_nonguc(struct drm_i915_error_state_buf *m,
struct intel_gt_coredump *gt)
{ … }
static void err_print_gt_global(struct drm_i915_error_state_buf *m,
struct intel_gt_coredump *gt)
{ … }
static void err_print_gt_fences(struct drm_i915_error_state_buf *m,
struct intel_gt_coredump *gt)
{ … }
static void err_print_gt_engines(struct drm_i915_error_state_buf *m,
struct intel_gt_coredump *gt)
{ … }
static void __err_print_to_sgl(struct drm_i915_error_state_buf *m,
struct i915_gpu_coredump *error)
{ … }
static int err_print_to_sgl(struct i915_gpu_coredump *error)
{ … }
ssize_t i915_gpu_coredump_copy_to_buffer(struct i915_gpu_coredump *error,
char *buf, loff_t off, size_t rem)
{ … }
static void i915_vma_coredump_free(struct i915_vma_coredump *vma)
{ … }
static void cleanup_params(struct i915_gpu_coredump *error)
{ … }
static void cleanup_uc(struct intel_uc_coredump *uc)
{ … }
static void cleanup_gt(struct intel_gt_coredump *gt)
{ … }
void __i915_gpu_coredump_free(struct kref *error_ref)
{ … }
static struct i915_vma_coredump *
i915_vma_coredump_create(const struct intel_gt *gt,
const struct i915_vma_resource *vma_res,
struct i915_vma_compress *compress,
const char *name)
{ … }
static void gt_record_fences(struct intel_gt_coredump *gt)
{ … }
static void engine_record_registers(struct intel_engine_coredump *ee)
{ … }
static void record_request(const struct i915_request *request,
struct i915_request_coredump *erq)
{ … }
static void engine_record_execlists(struct intel_engine_coredump *ee)
{ … }
static bool record_context(struct i915_gem_context_coredump *e,
struct intel_context *ce)
{ … }
struct intel_engine_capture_vma { … };
static struct intel_engine_capture_vma *
capture_vma_snapshot(struct intel_engine_capture_vma *next,
struct i915_vma_resource *vma_res,
gfp_t gfp, const char *name)
{ … }
static struct intel_engine_capture_vma *
capture_vma(struct intel_engine_capture_vma *next,
struct i915_vma *vma,
const char *name,
gfp_t gfp)
{ … }
static struct intel_engine_capture_vma *
capture_user(struct intel_engine_capture_vma *capture,
const struct i915_request *rq,
gfp_t gfp)
{ … }
static void add_vma(struct intel_engine_coredump *ee,
struct i915_vma_coredump *vma)
{ … }
static struct i915_vma_coredump *
create_vma_coredump(const struct intel_gt *gt, struct i915_vma *vma,
const char *name, struct i915_vma_compress *compress)
{ … }
static void add_vma_coredump(struct intel_engine_coredump *ee,
const struct intel_gt *gt,
struct i915_vma *vma,
const char *name,
struct i915_vma_compress *compress)
{ … }
struct intel_engine_coredump *
intel_engine_coredump_alloc(struct intel_engine_cs *engine, gfp_t gfp, u32 dump_flags)
{ … }
static struct intel_engine_capture_vma *
engine_coredump_add_context(struct intel_engine_coredump *ee,
struct intel_context *ce,
gfp_t gfp)
{ … }
struct intel_engine_capture_vma *
intel_engine_coredump_add_request(struct intel_engine_coredump *ee,
struct i915_request *rq,
gfp_t gfp)
{ … }
void
intel_engine_coredump_add_vma(struct intel_engine_coredump *ee,
struct intel_engine_capture_vma *capture,
struct i915_vma_compress *compress)
{ … }
static struct intel_engine_coredump *
capture_engine(struct intel_engine_cs *engine,
struct i915_vma_compress *compress,
u32 dump_flags)
{ … }
static void
gt_record_engines(struct intel_gt_coredump *gt,
intel_engine_mask_t engine_mask,
struct i915_vma_compress *compress,
u32 dump_flags)
{ … }
static void gt_record_guc_ctb(struct intel_ctb_coredump *saved,
const struct intel_guc_ct_buffer *ctb,
const void *blob_ptr, struct intel_guc *guc)
{ … }
static struct intel_uc_coredump *
gt_record_uc(struct intel_gt_coredump *gt,
struct i915_vma_compress *compress)
{ … }
static void gt_record_display_regs(struct intel_gt_coredump *gt)
{ … }
static void gt_record_global_nonguc_regs(struct intel_gt_coredump *gt)
{ … }
static void gt_record_global_regs(struct intel_gt_coredump *gt)
{ … }
static void gt_record_info(struct intel_gt_coredump *gt)
{ … }
static u32 generate_ecode(const struct intel_engine_coredump *ee)
{ … }
static const char *error_msg(struct i915_gpu_coredump *error)
{ … }
static void capture_gen(struct i915_gpu_coredump *error)
{ … }
struct i915_gpu_coredump *
i915_gpu_coredump_alloc(struct drm_i915_private *i915, gfp_t gfp)
{ … }
#define DAY_AS_SECONDS(x) …
struct intel_gt_coredump *
intel_gt_coredump_alloc(struct intel_gt *gt, gfp_t gfp, u32 dump_flags)
{ … }
struct i915_vma_compress *
i915_vma_capture_prepare(struct intel_gt_coredump *gt)
{ … }
void i915_vma_capture_finish(struct intel_gt_coredump *gt,
struct i915_vma_compress *compress)
{ … }
static struct i915_gpu_coredump *
__i915_gpu_coredump(struct intel_gt *gt, intel_engine_mask_t engine_mask, u32 dump_flags)
{ … }
static struct i915_gpu_coredump *
i915_gpu_coredump(struct intel_gt *gt, intel_engine_mask_t engine_mask, u32 dump_flags)
{ … }
void i915_error_state_store(struct i915_gpu_coredump *error)
{ … }
void i915_capture_error_state(struct intel_gt *gt,
intel_engine_mask_t engine_mask, u32 dump_flags)
{ … }
static struct i915_gpu_coredump *
i915_first_error_state(struct drm_i915_private *i915)
{ … }
void i915_reset_error_state(struct drm_i915_private *i915)
{ … }
void i915_disable_error_state(struct drm_i915_private *i915, int err)
{ … }
#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
void intel_klog_error_capture(struct intel_gt *gt,
intel_engine_mask_t engine_mask)
{
static int g_count;
struct drm_i915_private *i915 = gt->i915;
struct i915_gpu_coredump *error;
intel_wakeref_t wakeref;
size_t buf_size = PAGE_SIZE * 128;
size_t pos_err;
char *buf, *ptr, *next;
int l_count = g_count++;
int line = 0;
if (test_bit(I915_RESET_BACKOFF, >->reset.flags)) {
drm_err(>->i915->drm, "[Capture/%d.%d] Inside GT reset, skipping error capture :(\n",
l_count, line++);
return;
}
error = READ_ONCE(i915->gpu_error.first_error);
if (error) {
drm_err(&i915->drm, "[Capture/%d.%d] Clearing existing error capture first...\n",
l_count, line++);
i915_reset_error_state(i915);
}
with_intel_runtime_pm(&i915->runtime_pm, wakeref)
error = i915_gpu_coredump(gt, engine_mask, CORE_DUMP_FLAG_NONE);
if (IS_ERR(error)) {
drm_err(&i915->drm, "[Capture/%d.%d] Failed to capture error capture: %ld!\n",
l_count, line++, PTR_ERR(error));
return;
}
buf = kvmalloc(buf_size, GFP_KERNEL);
if (!buf) {
drm_err(&i915->drm, "[Capture/%d.%d] Failed to allocate buffer for error capture!\n",
l_count, line++);
i915_gpu_coredump_put(error);
return;
}
drm_info(&i915->drm, "[Capture/%d.%d] Dumping i915 error capture for %ps...\n",
l_count, line++, __builtin_return_address(0));
#define MAX_CHUNK …
pos_err = 0;
while (1) {
ssize_t got = i915_gpu_coredump_copy_to_buffer(error, buf, pos_err, buf_size - 1);
if (got <= 0)
break;
buf[got] = 0;
pos_err += got;
ptr = buf;
while (got > 0) {
size_t count;
char tag[2];
next = strnchr(ptr, got, '\n');
if (next) {
count = next - ptr;
*next = 0;
tag[0] = '>';
tag[1] = '<';
} else {
count = got;
tag[0] = '}';
tag[1] = '{';
}
if (count > MAX_CHUNK) {
size_t pos;
char *ptr2 = ptr;
for (pos = MAX_CHUNK; pos < count; pos += MAX_CHUNK) {
char chr = ptr[pos];
ptr[pos] = 0;
drm_info(&i915->drm, "[Capture/%d.%d] }%s{\n",
l_count, line++, ptr2);
ptr[pos] = chr;
ptr2 = ptr + pos;
cond_resched();
}
if (ptr2 < (ptr + count))
drm_info(&i915->drm, "[Capture/%d.%d] %c%s%c\n",
l_count, line++, tag[0], ptr2, tag[1]);
else if (tag[0] == '>')
drm_info(&i915->drm, "[Capture/%d.%d] ><\n",
l_count, line++);
} else {
drm_info(&i915->drm, "[Capture/%d.%d] %c%s%c\n",
l_count, line++, tag[0], ptr, tag[1]);
}
ptr = next;
got -= count;
if (next) {
ptr++;
got--;
}
cond_resched();
}
if (got)
drm_info(&i915->drm, "[Capture/%d.%d] Got %zd bytes remaining!\n",
l_count, line++, got);
}
kvfree(buf);
drm_info(&i915->drm, "[Capture/%d.%d] Dumped %zd bytes\n", l_count, line++, pos_err);
}
#endif
static ssize_t gpu_state_read(struct file *file, char __user *ubuf,
size_t count, loff_t *pos)
{ … }
static int gpu_state_release(struct inode *inode, struct file *file)
{ … }
static int i915_gpu_info_open(struct inode *inode, struct file *file)
{ … }
static const struct file_operations i915_gpu_info_fops = …;
static ssize_t
i915_error_state_write(struct file *filp,
const char __user *ubuf,
size_t cnt,
loff_t *ppos)
{ … }
static int i915_error_state_open(struct inode *inode, struct file *file)
{ … }
static const struct file_operations i915_error_state_fops = …;
void i915_gpu_error_debugfs_register(struct drm_i915_private *i915)
{ … }
static ssize_t error_state_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{ … }
static ssize_t error_state_write(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{ … }
static const struct bin_attribute error_state_attr = …;
void i915_gpu_error_sysfs_setup(struct drm_i915_private *i915)
{ … }
void i915_gpu_error_sysfs_teardown(struct drm_i915_private *i915)
{ … }