From: Tao Zhou <tao.zhou1@xxxxxxx> Old CPER data will be overwritten if ring buffer is full, and read pointer always points to CPER header. Signed-off-by: Tao Zhou <tao.zhou1@xxxxxxx> Reviewed-by: Hawking Zhang <Hawking.Zhang@xxxxxxx> --- drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c | 93 ++++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h | 2 + 2 files changed, 95 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c index cef7c1ec0d7c..64624b8b0cbc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c @@ -365,6 +365,99 @@ int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev, return 0; } +static bool amdgpu_cper_is_hdr(struct amdgpu_ring *ring, u64 pos) +{ + struct cper_hdr *chdr; + + chdr = (struct cper_hdr *)&(ring->ring[pos]); + return strcmp(chdr->signature, "CPER") ? false : true; +} + +static u32 amdgpu_cper_ring_get_ent_sz(struct amdgpu_ring *ring, u64 pos) +{ + struct cper_hdr *chdr; + u64 p; + u32 chunk, rec_len = 0; + + chdr = (struct cper_hdr *)&(ring->ring[pos]); + chunk = ring->ring_size - (pos << 2); + + if (!strcmp(chdr->signature, "CPER")) { + rec_len = chdr->record_length; + goto calc; + } + + /* ring buffer is not full, no cper data after ring->wptr */ + if (ring->count_dw) + goto calc; + + for (p = pos + 1; p <= ring->buf_mask; p++) { + chdr = (struct cper_hdr *)&(ring->ring[p]); + if (!strcmp(chdr->signature, "CPER")) { + rec_len = (p - pos) << 2; + goto calc; + } + } + +calc: + if (!rec_len) + return chunk; + else + return min(rec_len, chunk); +} + +void amdgpu_cper_ring_write(struct amdgpu_ring *ring, + void *src, int count) +{ + u64 pos, wptr_old, rptr = *ring->rptr_cpu_addr & ring->ptr_mask; + u32 chunk, ent_sz; + u8 *s = (u8 *)src; + + if (count >= ring->ring_size - 4) { + dev_err(ring->adev->dev, + "CPER data size(%d) is larger than ring size(%d)\n", + count, ring->ring_size - 4); + + return; + } + + wptr_old = ring->wptr; + + while (count) { + ent_sz = amdgpu_cper_ring_get_ent_sz(ring, ring->wptr); + chunk = min(ent_sz, count); + + memcpy(&ring->ring[ring->wptr], s, chunk); + + ring->wptr += (chunk >> 2); + ring->wptr &= ring->ptr_mask; + count -= chunk; + s += chunk; + } + + /* the buffer is overflow, adjust rptr */ + if (((wptr_old < rptr) && (rptr <= ring->wptr)) || + ((ring->wptr < wptr_old) && (wptr_old < rptr)) || + ((rptr <= ring->wptr) && (ring->wptr < wptr_old))) { + pos = (ring->wptr + 1) & ring->ptr_mask; + + do { + ent_sz = amdgpu_cper_ring_get_ent_sz(ring, pos); + + rptr += (ent_sz >> 2); + rptr &= ring->ptr_mask; + *ring->rptr_cpu_addr = rptr; + + pos = rptr; + } while (!amdgpu_cper_is_hdr(ring, rptr)); + } + + if (ring->count_dw >= (count >> 2)) + ring->count_dw - (count >> 2); + else + ring->count_dw = 0; +} + static u64 amdgpu_cper_ring_get_rptr(struct amdgpu_ring *ring) { return *(ring->rptr_cpu_addr); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h index 80c8571cff9d..1fa41858f22e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h @@ -93,6 +93,8 @@ int amdgpu_cper_generate_ue_record(struct amdgpu_device *adev, int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev, struct aca_banks *banks, uint16_t bank_count); +void amdgpu_cper_ring_write(struct amdgpu_ring *ring, + void *src, int count); int amdgpu_cper_init(struct amdgpu_device *adev); int amdgpu_cper_fini(struct amdgpu_device *adev); -- 2.34.1