msm: kgsl: Limit buffer dumping in the snapshot to the current context
In the vast majority of cases the only interesting indirect buffers (IBs)
to dump in a shapshot are the ones that are in the current context
because they are the ones that probably contributed to the hang. Limit
the IBs that are dumped to the range between the start of context and
the RB read pointer. If the start of context can't be found, then dump
from the start of the ringbuffer dump to read pointer. Also, since
buffers can be reused (though typically not in the same context), use the
most recent parsed size when dumping the IB to make sure we get the right
buffer contents for the most recent usage of said buffer.
Change-Id: Ic0dedbad090156d69b964e229806aa45242de6fd
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index cc3f3e7..fd05ad3 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -45,11 +45,19 @@
int index;
void *ptr;
- /* Go through the list and see that object has already been seen */
+ /*
+ * Sometimes IBs can be reused in the same dump. Because we parse from
+ * oldest to newest, if we come across an IB that has already been used,
+ * assume that it has been reused and update the list with the newest
+ * size.
+ */
+
for (index = 0; index < objbufptr; index++) {
if (objbuf[index].gpuaddr == gpuaddr &&
- objbuf[index].ptbase == ptbase)
- return;
+ objbuf[index].ptbase == ptbase) {
+ objbuf[index].dwords = dwords;
+ return;
+ }
}
if (objbufptr == SNAPSHOT_OBJ_BUFSIZE) {
@@ -77,6 +85,25 @@
objbuf[objbufptr++].ptr = ptr;
}
+/*
+ * Return a 1 if the specified object is already on the list of buffers
+ * to be dumped
+ */
+
+static int find_object(int type, unsigned int gpuaddr, unsigned int ptbase)
+{
+ int index;
+
+ for (index = 0; index < objbufptr; index++) {
+ if (objbuf[index].gpuaddr == gpuaddr &&
+ objbuf[index].ptbase == ptbase &&
+ objbuf[index].type == type)
+ return 1;
+ }
+
+ return 0;
+}
+
/* Snapshot the istore memory */
static int snapshot_istore(struct kgsl_device *device, void *snapshot,
int remain, void *priv)
@@ -113,6 +140,7 @@
unsigned int rbbase, ptbase, rptr, *rbptr;
int start, stop, index;
int numitems, size;
+ int parse_ibs = 0, ib_parse_start;
/* Get the GPU address of the ringbuffer */
kgsl_regread(device, REG_CP_RB_BASE, &rbbase);
@@ -158,9 +186,53 @@
header->rbsize = rb->sizedwords;
header->count = numitems;
- index = start;
+ /*
+ * We can only reliably dump IBs from the beginning of the context,
+ * and it turns out that for the vast majority of the time we really
+ * only care about the current context when it comes to diagnosing
+ * a hang. So, with an eye to limiting the buffer dumping to what is
+ * really useful find the beginning of the context and only dump
+ * IBs from that point
+ */
+
+ index = rptr;
+ ib_parse_start = start;
rbptr = rb->buffer_desc.hostptr;
+ while (index != start) {
+ index--;
+
+ if (index < 0) {
+ /*
+ * The marker we are looking for is 2 dwords long, so
+ * when wrapping, go back 2 from the end so we don't
+ * access out of range in the if statement below
+ */
+ index = rb->sizedwords - 2;
+
+ /*
+ * Account for the possibility that start might be at
+ * rb->sizedwords - 1
+ */
+
+ if (start == rb->sizedwords - 1)
+ break;
+ }
+
+ /*
+ * Look for a NOP packet with the context switch identifier in
+ * the second dword
+ */
+
+ if (rbptr[index] == cp_nop_packet(1) &&
+ rbptr[index + 1] == KGSL_CONTEXT_TO_MEM_IDENTIFIER) {
+ ib_parse_start = index;
+ break;
+ }
+ }
+
+ index = start;
+
/*
* Loop through the RB, copying the data and looking for indirect
* buffers and MMU pagetable changes
@@ -169,15 +241,20 @@
while (index != rb->wptr) {
*data = rbptr[index];
- if (rbptr[index] == cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2))
+ /* Only parse IBs between the context start and the rptr */
+
+ if (index == ib_parse_start)
+ parse_ibs = 1;
+
+ if (index == rptr)
+ parse_ibs = 0;
+
+ if (parse_ibs &&
+ rbptr[index] ==
+ cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2))
push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
rbptr[index + 1], rbptr[index + 2]);
- /*
- * FIXME: Handle upcoming MMU pagetable changes, but only
- * between the rptr and the wptr
- */
-
index = index + 1;
if (index == rb->sizedwords)
@@ -288,22 +365,45 @@
snapshot, remain, snapshot_rb, NULL);
/*
- * Make sure that the IBs described in the CP registers are on the
- * list of objects
+ * Make sure that the last IB1 that was being executed is dumped.
+ * Since this was the last IB1 that was processed, we should have
+ * already added it to the list during the ringbuffer parse but we
+ * want to be double plus sure.
*/
+
kgsl_regread(device, REG_CP_IB1_BASE, &ibbase);
kgsl_regread(device, REG_CP_IB1_BUFSZ, &ibsize);
- if (ibsize)
+ /*
+ * The problem is that IB size from the register is the unprocessed size
+ * of the buffer not the original size, so if we didn't catch this
+ * buffer being directly used in the RB, then we might not be able to
+ * dump the whle thing. Print a warning message so we can try to
+ * figure how often this really happens.
+ */
+
+ if (!find_object(SNAPSHOT_OBJ_TYPE_IB, ibbase, ptbase) && ibsize) {
push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
ibbase, ibsize);
+ KGSL_DRV_ERR(device, "CP_IB1_BASE not found in the ringbuffer. "
+ "Dumping %x dwords of the buffer.\n", ibsize);
+ }
kgsl_regread(device, REG_CP_IB2_BASE, &ibbase);
kgsl_regread(device, REG_CP_IB2_BUFSZ, &ibsize);
- if (ibsize)
+ /*
+ * Add the last parsed IB2 to the list. The IB2 should be found as we
+ * parse the objects below, but we try to add it to the list first, so
+ * it too can be parsed. Don't print an error message in this case - if
+ * the IB2 is found during parsing, the list will be updated with the
+ * correct size.
+ */
+
+ if (!find_object(SNAPSHOT_OBJ_TYPE_IB, ibbase, ptbase) && ibsize) {
push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
ibbase, ibsize);
+ }
/*
* Go through the list of found objects and dump each one. As the IBs