| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2013 Red Hat |
|---|
| 3 | 4 | * Author: Rob Clark <robdclark@gmail.com> |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 6 | | - * under the terms of the GNU General Public License version 2 as published by |
|---|
| 7 | | - * the Free Software Foundation. |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is distributed in the hope that it will be useful, but WITHOUT |
|---|
| 10 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|---|
| 11 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|---|
| 12 | | - * more details. |
|---|
| 13 | | - * |
|---|
| 14 | | - * You should have received a copy of the GNU General Public License along with |
|---|
| 15 | | - * this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 16 | 5 | */ |
|---|
| 17 | 6 | |
|---|
| 18 | 7 | /* For debugging crashes, userspace can: |
|---|
| .. | .. |
|---|
| 40 | 29 | * or shader programs (if not emitted inline in cmdstream). |
|---|
| 41 | 30 | */ |
|---|
| 42 | 31 | |
|---|
| 43 | | -#ifdef CONFIG_DEBUG_FS |
|---|
| 44 | | - |
|---|
| 45 | | -#include <linux/kfifo.h> |
|---|
| 46 | | -#include <linux/debugfs.h> |
|---|
| 47 | 32 | #include <linux/circ_buf.h> |
|---|
| 33 | +#include <linux/debugfs.h> |
|---|
| 34 | +#include <linux/kfifo.h> |
|---|
| 35 | +#include <linux/uaccess.h> |
|---|
| 48 | 36 | #include <linux/wait.h> |
|---|
| 37 | + |
|---|
| 38 | +#include <drm/drm_file.h> |
|---|
| 49 | 39 | |
|---|
| 50 | 40 | #include "msm_drv.h" |
|---|
| 51 | 41 | #include "msm_gpu.h" |
|---|
| 52 | 42 | #include "msm_gem.h" |
|---|
| 53 | 43 | |
|---|
| 54 | | -static bool rd_full = false; |
|---|
| 44 | +bool rd_full = false; |
|---|
| 55 | 45 | MODULE_PARM_DESC(rd_full, "If true, $debugfs/.../rd will snapshot all buffer contents"); |
|---|
| 56 | 46 | module_param_named(rd_full, rd_full, bool, 0600); |
|---|
| 47 | + |
|---|
| 48 | +#ifdef CONFIG_DEBUG_FS |
|---|
| 57 | 49 | |
|---|
| 58 | 50 | enum rd_sect_type { |
|---|
| 59 | 51 | RD_NONE, |
|---|
| .. | .. |
|---|
| 199 | 191 | file->private_data = rd; |
|---|
| 200 | 192 | rd->open = true; |
|---|
| 201 | 193 | |
|---|
| 194 | + /* Reset fifo to clear any previously unread data: */ |
|---|
| 195 | + rd->fifo.head = rd->fifo.tail = 0; |
|---|
| 196 | + |
|---|
| 202 | 197 | /* the parsing tools need to know gpu-id to know which |
|---|
| 203 | 198 | * register database to load. |
|---|
| 204 | 199 | */ |
|---|
| .. | .. |
|---|
| 244 | 239 | static struct msm_rd_state *rd_init(struct drm_minor *minor, const char *name) |
|---|
| 245 | 240 | { |
|---|
| 246 | 241 | struct msm_rd_state *rd; |
|---|
| 247 | | - struct dentry *ent; |
|---|
| 248 | | - int ret = 0; |
|---|
| 249 | 242 | |
|---|
| 250 | 243 | rd = kzalloc(sizeof(*rd), GFP_KERNEL); |
|---|
| 251 | 244 | if (!rd) |
|---|
| .. | .. |
|---|
| 258 | 251 | |
|---|
| 259 | 252 | init_waitqueue_head(&rd->fifo_event); |
|---|
| 260 | 253 | |
|---|
| 261 | | - ent = debugfs_create_file(name, S_IFREG | S_IRUGO, |
|---|
| 262 | | - minor->debugfs_root, rd, &rd_debugfs_fops); |
|---|
| 263 | | - if (!ent) { |
|---|
| 264 | | - DRM_ERROR("Cannot create /sys/kernel/debug/dri/%pd/%s\n", |
|---|
| 265 | | - minor->debugfs_root, name); |
|---|
| 266 | | - ret = -ENOMEM; |
|---|
| 267 | | - goto fail; |
|---|
| 268 | | - } |
|---|
| 254 | + debugfs_create_file(name, S_IFREG | S_IRUGO, minor->debugfs_root, rd, |
|---|
| 255 | + &rd_debugfs_fops); |
|---|
| 269 | 256 | |
|---|
| 270 | 257 | return rd; |
|---|
| 271 | | - |
|---|
| 272 | | -fail: |
|---|
| 273 | | - rd_cleanup(rd); |
|---|
| 274 | | - return ERR_PTR(ret); |
|---|
| 275 | 258 | } |
|---|
| 276 | 259 | |
|---|
| 277 | 260 | int msm_rd_debugfs_init(struct drm_minor *minor) |
|---|
| .. | .. |
|---|
| 318 | 301 | |
|---|
| 319 | 302 | static void snapshot_buf(struct msm_rd_state *rd, |
|---|
| 320 | 303 | struct msm_gem_submit *submit, int idx, |
|---|
| 321 | | - uint64_t iova, uint32_t size) |
|---|
| 304 | + uint64_t iova, uint32_t size, bool full) |
|---|
| 322 | 305 | { |
|---|
| 323 | 306 | struct msm_gem_object *obj = submit->bos[idx].obj; |
|---|
| 324 | 307 | unsigned offset = 0; |
|---|
| .. | .. |
|---|
| 337 | 320 | */ |
|---|
| 338 | 321 | rd_write_section(rd, RD_GPUADDR, |
|---|
| 339 | 322 | (uint32_t[3]){ iova, size, iova >> 32 }, 12); |
|---|
| 323 | + |
|---|
| 324 | + if (!full) |
|---|
| 325 | + return; |
|---|
| 340 | 326 | |
|---|
| 341 | 327 | /* But only dump the contents of buffers marked READ */ |
|---|
| 342 | 328 | if (!(submit->bos[idx].flags & MSM_SUBMIT_BO_READ)) |
|---|
| .. | .. |
|---|
| 374 | 360 | va_list args; |
|---|
| 375 | 361 | |
|---|
| 376 | 362 | va_start(args, fmt); |
|---|
| 377 | | - n = vsnprintf(msg, sizeof(msg), fmt, args); |
|---|
| 363 | + n = vscnprintf(msg, sizeof(msg), fmt, args); |
|---|
| 378 | 364 | va_end(args); |
|---|
| 379 | 365 | |
|---|
| 380 | 366 | rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4)); |
|---|
| .. | .. |
|---|
| 383 | 369 | rcu_read_lock(); |
|---|
| 384 | 370 | task = pid_task(submit->pid, PIDTYPE_PID); |
|---|
| 385 | 371 | if (task) { |
|---|
| 386 | | - n = snprintf(msg, sizeof(msg), "%.*s/%d: fence=%u", |
|---|
| 372 | + n = scnprintf(msg, sizeof(msg), "%.*s/%d: fence=%u", |
|---|
| 387 | 373 | TASK_COMM_LEN, task->comm, |
|---|
| 388 | 374 | pid_nr(submit->pid), submit->seqno); |
|---|
| 389 | 375 | } else { |
|---|
| 390 | | - n = snprintf(msg, sizeof(msg), "???/%d: fence=%u", |
|---|
| 376 | + n = scnprintf(msg, sizeof(msg), "???/%d: fence=%u", |
|---|
| 391 | 377 | pid_nr(submit->pid), submit->seqno); |
|---|
| 392 | 378 | } |
|---|
| 393 | 379 | rcu_read_unlock(); |
|---|
| 394 | 380 | |
|---|
| 395 | 381 | rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4)); |
|---|
| 396 | 382 | |
|---|
| 397 | | - for (i = 0; rd_full && i < submit->nr_bos; i++) |
|---|
| 398 | | - snapshot_buf(rd, submit, i, 0, 0); |
|---|
| 383 | + for (i = 0; i < submit->nr_bos; i++) |
|---|
| 384 | + snapshot_buf(rd, submit, i, 0, 0, should_dump(submit, i)); |
|---|
| 385 | + |
|---|
| 386 | + for (i = 0; i < submit->nr_cmds; i++) { |
|---|
| 387 | + uint32_t szd = submit->cmd[i].size; /* in dwords */ |
|---|
| 388 | + |
|---|
| 389 | + /* snapshot cmdstream bo's (if we haven't already): */ |
|---|
| 390 | + if (!should_dump(submit, i)) { |
|---|
| 391 | + snapshot_buf(rd, submit, submit->cmd[i].idx, |
|---|
| 392 | + submit->cmd[i].iova, szd * 4, true); |
|---|
| 393 | + } |
|---|
| 394 | + } |
|---|
| 399 | 395 | |
|---|
| 400 | 396 | for (i = 0; i < submit->nr_cmds; i++) { |
|---|
| 401 | 397 | uint64_t iova = submit->cmd[i].iova; |
|---|
| 402 | 398 | uint32_t szd = submit->cmd[i].size; /* in dwords */ |
|---|
| 403 | | - |
|---|
| 404 | | - /* snapshot cmdstream bo's (if we haven't already): */ |
|---|
| 405 | | - if (!rd_full) { |
|---|
| 406 | | - snapshot_buf(rd, submit, submit->cmd[i].idx, |
|---|
| 407 | | - submit->cmd[i].iova, szd * 4); |
|---|
| 408 | | - } |
|---|
| 409 | 399 | |
|---|
| 410 | 400 | switch (submit->cmd[i].type) { |
|---|
| 411 | 401 | case MSM_SUBMIT_CMD_IB_TARGET_BUF: |
|---|