-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drm/msm: add support to take dpu snapshot
Add the msm_disp_snapshot module which adds supports to dump dpu registers and capture the drm atomic state which can be used in case of error conditions. changes in v5: - start storing disp_state in msm_kms instead of dpu_kms - get rid of MSM_DISP_SNAPSHOT_IN_* enum by simplifying the functions - move snprintf inside the snapshot core by using varargs - get rid of some stale code comments - allow snapshot module for non-DPU targets Reported-by: kernel test robot <lkp@intel.com> Signed-off-by: Abhinav Kumar <abhinavk@codeaurora.org> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> Link: https://lore.kernel.org/r/1618606645-19695-3-git-send-email-abhinavk@codeaurora.org Signed-off-by: Rob Clark <robdclark@chromium.org>
- Loading branch information
Abhinav Kumar
authored and
Rob Clark
committed
Jun 23, 2021
1 parent
a4324a7
commit 9865948
Showing
11 changed files
with
529 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
/* | ||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. | ||
*/ | ||
|
||
#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ | ||
|
||
#include "msm_disp_snapshot.h" | ||
|
||
#ifdef CONFIG_DEV_COREDUMP | ||
static ssize_t disp_devcoredump_read(char *buffer, loff_t offset, | ||
size_t count, void *data, size_t datalen) | ||
{ | ||
struct drm_print_iterator iter; | ||
struct drm_printer p; | ||
struct msm_disp_state *disp_state; | ||
|
||
disp_state = data; | ||
|
||
iter.data = buffer; | ||
iter.offset = 0; | ||
iter.start = offset; | ||
iter.remain = count; | ||
|
||
p = drm_coredump_printer(&iter); | ||
|
||
msm_disp_state_print(disp_state, &p); | ||
|
||
return count - iter.remain; | ||
} | ||
|
||
static void disp_devcoredump_free(void *data) | ||
{ | ||
struct msm_disp_state *disp_state; | ||
|
||
disp_state = data; | ||
|
||
msm_disp_state_free(disp_state); | ||
|
||
disp_state->coredump_pending = false; | ||
} | ||
#endif /* CONFIG_DEV_COREDUMP */ | ||
|
||
static void _msm_disp_snapshot_work(struct kthread_work *work) | ||
{ | ||
struct msm_disp_state *disp_state = container_of(work, struct msm_disp_state, dump_work); | ||
struct drm_printer p; | ||
|
||
mutex_lock(&disp_state->mutex); | ||
|
||
msm_disp_snapshot_capture_state(disp_state); | ||
|
||
if (MSM_DISP_SNAPSHOT_DUMP_IN_CONSOLE) { | ||
p = drm_info_printer(disp_state->drm_dev->dev); | ||
msm_disp_state_print(disp_state, &p); | ||
} | ||
|
||
/* | ||
* if devcoredump is not defined free the state immediately | ||
* otherwise it will be freed in the free handler. | ||
*/ | ||
#ifdef CONFIG_DEV_COREDUMP | ||
dev_coredumpm(disp_state->dev, THIS_MODULE, disp_state, 0, GFP_KERNEL, | ||
disp_devcoredump_read, disp_devcoredump_free); | ||
disp_state->coredump_pending = true; | ||
#else | ||
msm_disp_state_free(disp_state); | ||
#endif | ||
|
||
mutex_unlock(&disp_state->mutex); | ||
} | ||
|
||
void msm_disp_snapshot_state(struct drm_device *drm_dev) | ||
{ | ||
struct msm_drm_private *priv; | ||
struct msm_kms *kms; | ||
struct msm_disp_state *disp_state; | ||
|
||
if (!drm_dev) { | ||
DRM_ERROR("invalid params\n"); | ||
return; | ||
} | ||
|
||
priv = drm_dev->dev_private; | ||
kms = priv->kms; | ||
disp_state = kms->disp_state; | ||
|
||
if (!disp_state) { | ||
DRM_ERROR("invalid params\n"); | ||
return; | ||
} | ||
|
||
/* | ||
* if there is a coredump pending return immediately till dump | ||
* if read by userspace or timeout happens | ||
*/ | ||
if (disp_state->coredump_pending) { | ||
DRM_DEBUG("coredump is pending read\n"); | ||
return; | ||
} | ||
|
||
kthread_queue_work(disp_state->dump_worker, | ||
&disp_state->dump_work); | ||
} | ||
|
||
int msm_disp_snapshot_init(struct drm_device *drm_dev) | ||
{ | ||
struct msm_drm_private *priv; | ||
struct msm_disp_state *disp_state; | ||
struct msm_kms *kms; | ||
|
||
if (!drm_dev) { | ||
DRM_ERROR("invalid params\n"); | ||
return -EINVAL; | ||
} | ||
|
||
priv = drm_dev->dev_private; | ||
kms = priv->kms; | ||
|
||
disp_state = devm_kzalloc(drm_dev->dev, sizeof(struct msm_disp_state), GFP_KERNEL); | ||
|
||
mutex_init(&disp_state->mutex); | ||
|
||
disp_state->dev = drm_dev->dev; | ||
disp_state->drm_dev = drm_dev; | ||
|
||
INIT_LIST_HEAD(&disp_state->blocks); | ||
|
||
disp_state->dump_worker = kthread_create_worker(0, "%s", "disp_snapshot"); | ||
if (IS_ERR(disp_state->dump_worker)) | ||
DRM_ERROR("failed to create disp state task\n"); | ||
|
||
kthread_init_work(&disp_state->dump_work, _msm_disp_snapshot_work); | ||
|
||
kms->disp_state = disp_state; | ||
|
||
return 0; | ||
} | ||
|
||
void msm_disp_snapshot_destroy(struct drm_device *drm_dev) | ||
{ | ||
struct msm_kms *kms; | ||
struct msm_drm_private *priv; | ||
struct msm_disp_state *disp_state; | ||
|
||
if (!drm_dev) { | ||
DRM_ERROR("invalid params\n"); | ||
return; | ||
} | ||
|
||
priv = drm_dev->dev_private; | ||
kms = priv->kms; | ||
disp_state = kms->disp_state; | ||
|
||
if (disp_state->dump_worker) | ||
kthread_destroy_worker(disp_state->dump_worker); | ||
|
||
list_del(&disp_state->blocks); | ||
|
||
mutex_destroy(&disp_state->mutex); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-only */ | ||
/* | ||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. | ||
*/ | ||
|
||
#ifndef MSM_DISP_SNAPSHOT_H_ | ||
#define MSM_DISP_SNAPSHOT_H_ | ||
|
||
#include <drm/drm_atomic_helper.h> | ||
#include <drm/drm_device.h> | ||
#include "../../../drm_crtc_internal.h" | ||
#include <drm/drm_print.h> | ||
#include <drm/drm_atomic.h> | ||
#include <linux/debugfs.h> | ||
#include <linux/list.h> | ||
#include <linux/delay.h> | ||
#include <linux/spinlock.h> | ||
#include <linux/ktime.h> | ||
#include <linux/debugfs.h> | ||
#include <linux/uaccess.h> | ||
#include <linux/dma-buf.h> | ||
#include <linux/slab.h> | ||
#include <linux/list_sort.h> | ||
#include <linux/pm.h> | ||
#include <linux/pm_runtime.h> | ||
#include <linux/kthread.h> | ||
#include <linux/devcoredump.h> | ||
#include <stdarg.h> | ||
#include "msm_kms.h" | ||
|
||
#define MSM_DISP_SNAPSHOT_MAX_BLKS 10 | ||
|
||
/* debug option to print the registers in logs */ | ||
#define MSM_DISP_SNAPSHOT_DUMP_IN_CONSOLE 0 | ||
|
||
/* print debug ranges in groups of 4 u32s */ | ||
#define REG_DUMP_ALIGN 16 | ||
|
||
/** | ||
* struct msm_disp_state - structure to store current dpu state | ||
* @dev: device pointer | ||
* @drm_dev: drm device pointer | ||
* @mutex: mutex to serialize access to serialze dumps, debugfs access | ||
* @coredump_pending: coredump is pending read from userspace | ||
* @atomic_state: atomic state duplicated at the time of the error | ||
* @dump_worker: kworker thread which runs the dump work | ||
* @dump_work: kwork which dumps the registers and drm state | ||
* @timestamp: timestamp at which the coredump was captured | ||
*/ | ||
struct msm_disp_state { | ||
struct device *dev; | ||
struct drm_device *drm_dev; | ||
struct mutex mutex; | ||
|
||
bool coredump_pending; | ||
|
||
struct list_head blocks; | ||
|
||
struct drm_atomic_state *atomic_state; | ||
|
||
struct kthread_worker *dump_worker; | ||
struct kthread_work dump_work; | ||
ktime_t timestamp; | ||
}; | ||
|
||
/** | ||
* struct msm_disp_state_block - structure to store each hardware block state | ||
* @name: name of the block | ||
* @drm_dev: handle to the linked list head | ||
* @size: size of the register space of this hardware block | ||
* @state: array holding the register dump of this hardware block | ||
* @base_addr: starting address of this hardware block's register space | ||
*/ | ||
struct msm_disp_state_block { | ||
char name[SZ_128]; | ||
struct list_head node; | ||
unsigned int size; | ||
u32 *state; | ||
void __iomem *base_addr; | ||
}; | ||
|
||
/** | ||
* msm_disp_snapshot_init - initialize display snapshot | ||
* @drm_dev: drm device handle | ||
* | ||
* Returns: 0 or -ERROR | ||
*/ | ||
int msm_disp_snapshot_init(struct drm_device *drm_dev); | ||
|
||
/** | ||
* msm_disp_snapshot_destroy - destroy the display snapshot | ||
* @drm_dev: drm device handle | ||
* | ||
* Returns: none | ||
*/ | ||
void msm_disp_snapshot_destroy(struct drm_device *drm_dev); | ||
|
||
/** | ||
* msm_disp_snapshot_state - trigger to dump the display snapshot | ||
* @drm_dev: handle to drm device | ||
* Returns: none | ||
*/ | ||
void msm_disp_snapshot_state(struct drm_device *drm_dev); | ||
|
||
/** | ||
* msm_disp_state_get - get the handle to msm_disp_state struct from the drm device | ||
* @drm: handle to drm device | ||
* Returns: handle to the msm_disp_state struct | ||
*/ | ||
struct msm_disp_state *msm_disp_state_get(struct drm_device *drm); | ||
|
||
/** | ||
* msm_disp_state_print - print out the current dpu state | ||
* @disp_state: handle to drm device | ||
* @p: handle to drm printer | ||
* | ||
* Returns: none | ||
*/ | ||
void msm_disp_state_print(struct msm_disp_state *disp_state, struct drm_printer *p); | ||
|
||
/** | ||
* msm_disp_snapshot_capture_state - utility to capture atomic state and hw registers | ||
* @disp_state: handle to msm_disp_state struct | ||
* Returns: none | ||
*/ | ||
void msm_disp_snapshot_capture_state(struct msm_disp_state *disp_state); | ||
|
||
/** | ||
* msm_disp_state_free - free the memory after the coredump has been read | ||
* @disp_state: handle to struct msm_disp_state | ||
* Returns: none | ||
*/ | ||
void msm_disp_state_free(struct msm_disp_state *disp_state); | ||
|
||
/** | ||
* msm_disp_snapshot_add_block - add a hardware block with its register dump | ||
* @disp_state: handle to struct msm_disp_state | ||
* @name: name of the hardware block | ||
* @len: size of the register space of the hardware block | ||
* @base_addr: starting address of the register space of the hardware block | ||
* @fmt: format in which the block names need to be printed | ||
* | ||
* Returns: none | ||
*/ | ||
__printf(4, 5) | ||
void msm_disp_snapshot_add_block(struct msm_disp_state *disp_state, u32 len, | ||
void __iomem *base_addr, const char *fmt, ...); | ||
|
||
#endif /* MSM_DISP_SNAPSHOT_H_ */ |
Oops, something went wrong.