From: Tao Zhou <tao.zh...@amd.com>

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.zh...@amd.com>
Reviewed-by: Hawking Zhang <hawking.zh...@amd.com>
---
 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

Reply via email to