summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c154
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.c11
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c171
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h5
7 files changed, 354 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index b68aea97c166..4c3e933ff6d5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -309,6 +309,7 @@ extern uint amdgpu_hdmi_hpd_debounce_delay_ms;
#define AMDGPU_RESET_TYPE_SOFT_RECOVERY (1 << 1) /* soft recovery, eg. kill shaders */
#define AMDGPU_RESET_TYPE_PER_QUEUE (1 << 2) /* per queue */
#define AMDGPU_RESET_TYPE_PER_PIPE (1 << 3) /* per pipe */
+#define AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET (1 << 4) /* soft-resets an IP block */
/* max cursor sizes (in pixels) */
#define CIK_CURSOR_WIDTH 128
@@ -1104,6 +1105,7 @@ struct amdgpu_device {
bool debug_disable_ce_logs;
bool debug_enable_ce_cs;
bool debug_hibernation_thaw_resume_gpu;
+ bool debug_disable_ip_block_soft_reset;
/* Protection for the following isolation structure */
struct mutex enforce_isolation_mutex;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 157c0f260cc0..f5e8e4f455ee 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -147,6 +147,7 @@ enum AMDGPU_DEBUG_MASK {
AMDGPU_DEBUG_DISABLE_RAS_CE_LOG = BIT(9),
AMDGPU_DEBUG_ENABLE_CE_CS = BIT(10),
AMDGPU_DEBUG_HIBERNATION_THAW_RESUME_GPU = BIT(11),
+ AMDGPU_DEBUG_DISABLE_IP_BLOCK_SOFT_RESET = BIT(12),
};
unsigned int amdgpu_vram_limit = UINT_MAX;
@@ -2296,6 +2297,11 @@ static void amdgpu_init_debug_options(struct amdgpu_device *adev)
pr_info("debug: resume gpu in thaw() of hibernation\n");
adev->debug_hibernation_thaw_resume_gpu = true;
}
+
+ if (amdgpu_debug_mask & AMDGPU_DEBUG_DISABLE_IP_BLOCK_SOFT_RESET) {
+ pr_info("debug: IP block soft reset disabled\n");
+ adev->debug_disable_ip_block_soft_reset = true;
+ }
}
static unsigned long amdgpu_fix_asic_type(struct pci_dev *pdev, unsigned long flags)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c
index 6aa54156bbc9..65505bc50399 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c
@@ -409,3 +409,157 @@ bool amdgpu_device_ip_is_valid(struct amdgpu_device *adev,
return false;
}
+
+/**
+ * amdgpu_ip_from_ring() - Find IP block type corresponding to ring type.
+ *
+ * @ring_type: The ring type whose IP block you are looking for.
+ */
+static enum amd_ip_block_type amdgpu_ip_from_ring(const enum amdgpu_ring_type ring_type)
+{
+ switch (ring_type) {
+ case AMDGPU_RING_TYPE_GFX:
+ case AMDGPU_RING_TYPE_COMPUTE:
+ return AMD_IP_BLOCK_TYPE_GFX;
+
+ case AMDGPU_RING_TYPE_SDMA:
+ return AMD_IP_BLOCK_TYPE_SDMA;
+
+ case AMDGPU_RING_TYPE_UVD:
+ case AMDGPU_RING_TYPE_UVD_ENC:
+ return AMD_IP_BLOCK_TYPE_UVD;
+
+ case AMDGPU_RING_TYPE_VCE:
+ return AMD_IP_BLOCK_TYPE_VCE;
+
+ case AMDGPU_RING_TYPE_VCN_DEC:
+ case AMDGPU_RING_TYPE_VCN_ENC:
+ return AMD_IP_BLOCK_TYPE_VCN;
+
+ case AMDGPU_RING_TYPE_VCN_JPEG:
+ return AMD_IP_BLOCK_TYPE_JPEG;
+
+ case AMDGPU_RING_TYPE_VPE:
+ return AMD_IP_BLOCK_TYPE_VPE;
+
+ default:
+ return AMD_IP_BLOCK_TYPE_NUM;
+ }
+}
+
+/**
+ * amdgpu_ring_mask_from_ip() - Find mask of ring types corresponding to an IP block type.
+ *
+ * @ip_type: The IP block type whose rings you are looking for.
+ */
+static u32 amdgpu_ring_mask_from_ip(const enum amd_ip_block_type ip_type)
+{
+ switch (ip_type) {
+ case AMD_IP_BLOCK_TYPE_GFX:
+ return BIT(AMDGPU_RING_TYPE_GFX) | BIT(AMDGPU_RING_TYPE_COMPUTE);
+
+ case AMD_IP_BLOCK_TYPE_SDMA:
+ return BIT(AMDGPU_RING_TYPE_SDMA);
+
+ case AMD_IP_BLOCK_TYPE_UVD:
+ return BIT(AMDGPU_RING_TYPE_UVD) | BIT(AMDGPU_RING_TYPE_UVD_ENC);
+
+ case AMD_IP_BLOCK_TYPE_VCE:
+ return BIT(AMD_IP_BLOCK_TYPE_VCE);
+
+ case AMD_IP_BLOCK_TYPE_VCN:
+ return BIT(AMDGPU_RING_TYPE_VCN_DEC) | BIT(AMDGPU_RING_TYPE_VCN_ENC);
+
+ case AMD_IP_BLOCK_TYPE_JPEG:
+ return BIT(AMDGPU_RING_TYPE_VCN_JPEG);
+
+ case AMD_IP_BLOCK_TYPE_VPE:
+ return BIT(AMDGPU_RING_TYPE_VPE);
+
+ default:
+ return 0;
+ }
+}
+
+/**
+ * amdgpu_filter_rings() - Filter rings according to a mask.
+ *
+ * @adev: amdgpu_device pointer
+ * @ring_type_mask: Mask of ring types you are looking for
+ * @out_rings: Array of rings which is going to be filled
+ * @out_num_rings: Number of rings which were filtered
+ */
+static void amdgpu_filter_rings(struct amdgpu_device *adev, const u32 ring_type_mask,
+ struct amdgpu_ring **out_rings, u32 *out_num_rings)
+{
+ u32 num_rings = 0;
+ int i;
+
+ for (i = 0; i < adev->num_rings; ++i) {
+ if (BIT(adev->rings[i]->funcs->type) & ring_type_mask)
+ out_rings[num_rings++] = adev->rings[i];
+ }
+
+ *out_num_rings = num_rings;
+}
+
+/**
+ * amdgpu_device_ip_soft_reset() - Perform a graceful soft reset on an IP block.
+ *
+ * @guilty_ring: The ring which is guilty of causing a reset.
+ * @guilty_fence: The fence which didn't signal.
+ *
+ * IP block soft reset is used when attempting to recover
+ * from a GPU hang in a situation where a more fine grained
+ * reset type isn't available or didn't work. This effectively
+ * resets all rings that belong to the same device IP block
+ * and re-initializes the device IP block.
+ *
+ * The reset is handled gracefully, meaning that we try to
+ * minimize collateral damage (ie. avoid rejecting non-guilty jobs)
+ * as well as back up and restore the contents of all rings
+ * so that the system can move on from the hang.
+ */
+int amdgpu_device_ip_soft_reset(struct amdgpu_ring *guilty_ring,
+ struct amdgpu_fence *guilty_fence)
+{
+ struct amdgpu_device *adev = guilty_ring->adev;
+ struct amdgpu_ring *rings[AMDGPU_MAX_RINGS];
+ struct amdgpu_ip_block *ip_block;
+ enum amd_ip_block_type ip_type;
+ u32 num_rings, ring_type_mask;
+ int r;
+
+ ip_type = amdgpu_ip_from_ring(guilty_ring->funcs->type);
+ ip_block = amdgpu_device_ip_get_ip_block(adev, ip_type);
+
+ if (!ip_block || !ip_block->version->funcs->soft_reset) {
+ dev_warn(adev->dev, "IP block soft reset not supported on %s\n",
+ ip_block->version->funcs->name);
+ return -EOPNOTSUPP;
+ }
+
+ dev_err(adev->dev, "Starting %s IP block soft reset\n",
+ ip_block->version->funcs->name);
+
+ ring_type_mask = amdgpu_ring_mask_from_ip(ip_type);
+ amdgpu_filter_rings(adev, ring_type_mask, rings, &num_rings);
+
+ amdgpu_device_lock_reset_domain(adev->reset_domain);
+ amdgpu_multi_ring_reset_helper_begin(rings, num_rings, guilty_ring, guilty_fence);
+
+ r = ip_block->version->funcs->soft_reset(ip_block);
+
+ r = amdgpu_multi_ring_reset_helper_end(rings, num_rings, guilty_ring, r);
+ amdgpu_device_unlock_reset_domain(adev->reset_domain);
+
+ if (r) {
+ dev_err(adev->dev, "Failed %s IP block soft reset: %d\n",
+ ip_block->version->funcs->name, r);
+ return r;
+ }
+
+ dev_err(adev->dev, "Successful %s IP block soft reset\n",
+ ip_block->version->funcs->name);
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h
index 590ad82f115e..18fd8631a092 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h
@@ -85,6 +85,9 @@ enum amd_hw_ip_block_type {
#define IP_VERSION_SUBREV(ver) ((ver) & 0xF)
#define IP_VERSION_MAJ_MIN_REV(ver) ((ver) >> 8)
+struct amdgpu_ring;
+struct amdgpu_fence;
+
struct amdgpu_ip_map_info {
/* Map of logical to actual dev instances/mask */
uint32_t dev_inst[MAX_HWIP][HWIP_MAX_INSTANCE];
@@ -151,5 +154,7 @@ bool amdgpu_device_ip_is_hw(struct amdgpu_device *adev,
enum amd_ip_block_type block_type);
bool amdgpu_device_ip_is_valid(struct amdgpu_device *adev,
enum amd_ip_block_type block_type);
+int amdgpu_device_ip_soft_reset(struct amdgpu_ring *guilty_ring,
+ struct amdgpu_fence *guilty_fence);
#endif /* __AMDGPU_IP_H__ */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
index 8c40eb8cec51..cff73f1b5a72 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
@@ -151,6 +151,17 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct drm_sched_job *s_job)
dev_err(adev->dev, "Ring %s reset failed\n", ring->sched.name);
}
+ /* Attempt an IP block soft reset, if supported. */
+ if (amdgpu_gpu_recovery &&
+ amdgpu_ring_is_reset_type_supported(ring, AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET)) {
+ r = amdgpu_device_ip_soft_reset(ring, job->hw_fence);
+ if (!r) {
+ atomic_inc(&ring->adev->gpu_reset_counter);
+ drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, info);
+ goto exit;
+ }
+ }
+
if (dma_fence_get_status(&s_job->s_fence->finished) == 0)
dma_fence_set_error(&s_job->s_fence->finished, -ETIME);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
index b97fa35bac23..3f78aa6ed82f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
@@ -935,6 +935,177 @@ int amdgpu_ring_reset_helper_end(struct amdgpu_ring *ring,
return 0;
}
+/**
+ * amdgpu_multi_ring_reset_helper_begin() - Prepare multiple rings for a reset.
+ *
+ * @rings: Pointer to an array of amdgpu rings that are affected.
+ * @num_rings: Number of rings in the array.
+ * @guilty_ring: The ring which is guilty of causing a reset.
+ * @guilty_fence: The fence which didn't signal on the guilty ring.
+ *
+ * Useful when performing a GPU reset method that affects
+ * multiple rings at the same time, such as an IP block soft
+ * reset. For example, a GFX IP block soft reset will affect
+ * every graphics and compute queue.
+ *
+ * This function should be called before such a reset.
+ *
+ * Prepare the affected rings before the reset, make sure to
+ * minimize collateral damage, and backup the contents of
+ * the rings. Then the caller can call the actual HW specific
+ * reset function.
+ *
+ * After the reset is complete, the caller should then call
+ * amdgpu_multi_ring_reset_helper_end() to restore the rings.
+ */
+void amdgpu_multi_ring_reset_helper_begin(struct amdgpu_ring **rings, u32 num_rings,
+ struct amdgpu_ring *guilty_ring,
+ struct amdgpu_fence *guilty_fence)
+{
+ struct amdgpu_device *adev = guilty_ring->adev;
+ struct amdgpu_fence *ring_guilty_fence;
+ struct amdgpu_ring *ring;
+ bool rings_busy;
+ int i;
+ u32 t;
+
+ for (i = 0; i < num_rings; ++i) {
+ ring = rings[i];
+
+ /* Don't accept new submissions on the ring. */
+ if (amdgpu_ring_sched_ready(ring) && !drm_sched_is_stopped(&ring->sched))
+ drm_sched_wqueue_stop(&ring->sched);
+
+ /*
+ * Clear the preempt condition to stop the ring
+ * from starting its next submission. This ensures
+ * that only the currently executing submission
+ * can be rejected because of the reset and helps
+ * minimize collateral damage.
+ */
+ if (ring->funcs->init_cond_exec)
+ amdgpu_ring_set_preempt_cond_exec(ring, false);
+ }
+
+ /* Flush HDP cache so the GPU can see the updated COND_EXEC values */
+ amdgpu_device_flush_hdp(adev, NULL);
+
+ /*
+ * Give some time for non-guilty rings to finish their
+ * current submission, to try to minimize collateral damage.
+ *
+ * Note that this just a best effort, but really there
+ * is no way to really know which ring is actually responsible
+ * because different rings may share resources, eg. a compute
+ * ring may hog shader engines, causing a graphics ring to hang.
+ */
+ for (t = 0; t < adev->usec_timeout; t += 10000) {
+ rings_busy = false;
+
+ /* Check if any of the non-guilty rings are busy */
+ for (i = 0; i < num_rings; ++i) {
+ ring = rings[i];
+
+ if (ring == guilty_ring)
+ continue;
+
+ rings_busy |=
+ atomic_read(&ring->fence_drv.last_seq) !=
+ READ_ONCE(ring->fence_drv.sync_seq);
+ }
+
+ if (!rings_busy)
+ break;
+
+ mdelay(10);
+ }
+
+ for (i = 0; i < num_rings; ++i) {
+ ring = rings[i];
+
+ /*
+ * Find guilty fences, ie. the fences that didn't signal
+ * on each ring. At this point there is no way to know
+ * which one is really responsible for the hang, and no
+ * way to save any of them, so we treat all of them as guilty.
+ */
+ ring_guilty_fence =
+ ring == guilty_ring ? guilty_fence :
+ amdgpu_ring_find_guilty_fence(ring);
+
+ /*
+ * Backup current contents of the ring.
+ * The helper takes care to only reemit unsignalled fences
+ * so we don't have to worry about that here.
+ */
+ amdgpu_ring_reset_helper_begin(ring, ring_guilty_fence);
+ }
+}
+
+/**
+ * amdgpu_multi_ring_reset_helper_end() - Prepare multiple rings for a reset.
+ *
+ * @rings: Pointer to an array of amdgpu rings that are affected.
+ * @num_rings: Number of rings in the array.
+ * @guilty_ring: The ring which is guilty of causing a reset.
+ * @ret: Return code from the reset function.
+ *
+ * After calling amdgpu_multi_ring_reset_helper_end()
+ * and executing the actual reset method, call this
+ * function to restore normal operation.
+ *
+ * In case the reset failed, this function should still
+ * be called to restore some state, but it won't attempt to
+ * fully restore the ring contents.
+ */
+int amdgpu_multi_ring_reset_helper_end(struct amdgpu_ring **rings, u32 num_rings,
+ struct amdgpu_ring *guilty_ring, int ret)
+{
+ struct amdgpu_device *adev = guilty_ring->adev;
+ struct amdgpu_ring *ring;
+ int i, r;
+
+ /* Set preempt condition, rings are now allowed to execute submissions */
+ for (i = 0; i < num_rings; ++i) {
+ ring = rings[i];
+
+ if (ring->funcs->init_cond_exec)
+ amdgpu_ring_set_preempt_cond_exec(ring, true);
+ }
+
+ /* Flush HDP cache so the GPU can see the updated COND_EXEC values */
+ amdgpu_device_flush_hdp(adev, NULL);
+
+ /* If the reset was unsuccessful, return without restoring anything. */
+ if (ret)
+ return ret;
+
+ /* Restore contents of all rings */
+ for (i = 0; i < num_rings; ++i) {
+ ring = rings[i];
+
+ r = amdgpu_ring_reset_helper_end(ring, ring->guilty_fence);
+ if (r) {
+ dev_err(adev->dev,
+ "Failed to recover ring %s after soft reset\n",
+ ring->name);
+ return r;
+ }
+ }
+
+ /* Accept submissions on all rings again */
+ for (i = 0; i < num_rings; ++i) {
+ ring = rings[i];
+
+ if (!amdgpu_ring_sched_ready(ring))
+ continue;
+
+ drm_sched_wqueue_start(&ring->sched);
+ }
+
+ return 0;
+}
+
bool amdgpu_ring_is_reset_type_supported(struct amdgpu_ring *ring,
u32 reset_type)
{
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
index 71cd9bb12f75..c272e0b028ad 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
@@ -595,6 +595,11 @@ void amdgpu_ring_reset_helper_begin(struct amdgpu_ring *ring,
struct amdgpu_fence *guilty_fence);
int amdgpu_ring_reset_helper_end(struct amdgpu_ring *ring,
struct amdgpu_fence *guilty_fence);
+void amdgpu_multi_ring_reset_helper_begin(struct amdgpu_ring **rings, u32 num_rings,
+ struct amdgpu_ring *guilty_ring,
+ struct amdgpu_fence *guilty_fence);
+int amdgpu_multi_ring_reset_helper_end(struct amdgpu_ring **rings, u32 num_rings,
+ struct amdgpu_ring *guilty_ring, int ret);
bool amdgpu_ring_is_reset_type_supported(struct amdgpu_ring *ring,
u32 reset_type);
#endif