msm: kgsl: Overwrite a GPU snapshot with a GMU snapshot
If we get a GMU fault, overwrite any existing GPU snapshot unless it
is a GMU snapshot.
Change-Id: Id2de38770ae400c138bea18d567b984eeb7c4ca9
Signed-off-by: Lynus Vaz <lvaz@codeaurora.org>
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index d16e42d..780cefe 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -2464,7 +2464,7 @@
dev_err(device->dev, " hwfault=%8.8X\n", hwfault);
- kgsl_device_snapshot(device, NULL);
+ kgsl_device_snapshot(device, NULL, adreno_gmu_gpu_fault(adreno_dev));
}
/**
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 36bd656..49dc5ed 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -1329,6 +1329,10 @@
smp_wmb();
}
+static inline bool adreno_gmu_gpu_fault(struct adreno_device *adreno_dev)
+{
+ return adreno_gpu_fault(adreno_dev) & ADRENO_GMU_FAULT;
+}
/**
* adreno_clear_gpu_fault() - Clear the GPU fault register
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 5d7fb21..356d7c2 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -685,7 +685,7 @@
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
dev_err(device->dev, "CP initialization failed to idle\n");
- kgsl_device_snapshot(device, NULL);
+ kgsl_device_snapshot(device, NULL, false);
}
return ret;
@@ -1823,7 +1823,7 @@
if (ret) {
KGSL_DRV_ERR(device, "microcode bootstrap failed to idle\n");
- kgsl_device_snapshot(device, NULL);
+ kgsl_device_snapshot(device, NULL, false);
}
/* Clear the chicken bit for speed up on A430 and its derivatives */
diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c
index c807b67..0cf909e 100644
--- a/drivers/gpu/msm/adreno_a4xx.c
+++ b/drivers/gpu/msm/adreno_a4xx.c
@@ -1535,7 +1535,7 @@
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
dev_err(device->dev, "CP initialization failed to idle\n");
- kgsl_device_snapshot(device, NULL);
+ kgsl_device_snapshot(device, NULL, false);
}
return ret;
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 25aeaef..21ecaa1 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -2046,7 +2046,7 @@
kfree(replay);
}
-static void do_header_and_snapshot(struct kgsl_device *device,
+static void do_header_and_snapshot(struct kgsl_device *device, int fault,
struct adreno_ringbuffer *rb, struct kgsl_drawobj_cmd *cmdobj)
{
struct kgsl_drawobj *drawobj = DRAWOBJ(cmdobj);
@@ -2054,7 +2054,7 @@
/* Always dump the snapshot on a non-drawobj failure */
if (cmdobj == NULL) {
adreno_fault_header(device, rb, NULL);
- kgsl_device_snapshot(device, NULL);
+ kgsl_device_snapshot(device, NULL, fault & ADRENO_GMU_FAULT);
return;
}
@@ -2066,7 +2066,7 @@
adreno_fault_header(device, rb, cmdobj);
if (!(drawobj->context->flags & KGSL_CONTEXT_NO_SNAPSHOT))
- kgsl_device_snapshot(device, drawobj->context);
+ kgsl_device_snapshot(device, NULL, fault & ADRENO_GMU_FAULT);
}
static int dispatcher_do_fault(struct adreno_device *adreno_dev)
@@ -2192,7 +2192,7 @@
adreno_readreg64(adreno_dev, ADRENO_REG_CP_IB1_BASE,
ADRENO_REG_CP_IB1_BASE_HI, &base);
- do_header_and_snapshot(device, hung_rb, cmdobj);
+ do_header_and_snapshot(device, fault, hung_rb, cmdobj);
/* Turn off the KEEPALIVE vote from the ISR for hard fault */
if (gpudev->gpu_keepalive && fault & ADRENO_HARD_FAULT)
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 0882447..2615d44 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -533,7 +533,8 @@
drawctxt->internal_timestamp,
drawctxt->type, ret);
device->force_panic = 1;
- kgsl_device_snapshot(device, context);
+ kgsl_device_snapshot(device, context,
+ adreno_gmu_gpu_fault(adreno_dev));
}
kgsl_sharedmem_writel(device, &device->memstore,
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 2cc1869..f8d5f5b 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -493,7 +493,9 @@
* @work: worker to dump the frozen memory
* @dump_gate: completion gate signaled by worker when it is finished.
* @process: the process that caused the hang, if known.
- * @sysfs_read: An atomic for concurrent snapshot reads via syfs.
+ * @sysfs_read: Count of current reads via sysfs
+ * @first_read: True until the snapshot read is started
+ * @gmu_fault: Snapshot collected when GMU fault happened
*/
struct kgsl_snapshot {
uint64_t ib1base;
@@ -514,7 +516,9 @@
struct work_struct work;
struct completion dump_gate;
struct kgsl_process_private *process;
- atomic_t sysfs_read;
+ unsigned int sysfs_read;
+ bool first_read;
+ bool gmu_fault;
};
/**
@@ -700,7 +704,7 @@
int kgsl_device_snapshot_init(struct kgsl_device *device);
void kgsl_device_snapshot(struct kgsl_device *device,
- struct kgsl_context *context);
+ struct kgsl_context *context, bool gmu_fault);
void kgsl_device_snapshot_close(struct kgsl_device *device);
void kgsl_snapshot_save_frozen_objs(struct work_struct *work);
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 9b04543..3414bf0 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -1364,7 +1364,7 @@
/* Wait for the NMI to be handled */
wmb();
udelay(100);
- kgsl_device_snapshot(device, ERR_PTR(-EINVAL));
+ kgsl_device_snapshot(device, ERR_PTR(-EINVAL), true);
adreno_write_gmureg(adreno_dev,
ADRENO_REG_GMU_GMU2HOST_INTR_CLR, 0xFFFFFFFF);
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index e704db7..f9494a4 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -573,16 +573,34 @@
snapshot->size += header->size;
}
+static void kgsl_free_snapshot(struct kgsl_snapshot *snapshot)
+{
+ struct kgsl_snapshot_object *obj, *tmp;
+
+ wait_for_completion(&snapshot->dump_gate);
+
+ list_for_each_entry_safe(obj, tmp,
+ &snapshot->obj_list, node)
+ kgsl_snapshot_put_object(obj);
+
+ if (snapshot->mempool)
+ vfree(snapshot->mempool);
+
+ kfree(snapshot);
+ KGSL_CORE_ERR("snapshot: objects released\n");
+}
+
/**
* kgsl_snapshot() - construct a device snapshot
* @device: device to snapshot
* @context: the context that is hung, might be NULL if unknown.
+ * @gmu_fault: whether this snapshot is triggered by a GMU fault.
*
* Given a device, construct a binary snapshot dump of the current device state
* and store it in the device snapshot memory.
*/
void kgsl_device_snapshot(struct kgsl_device *device,
- struct kgsl_context *context)
+ struct kgsl_context *context, bool gmu_fault)
{
struct kgsl_snapshot_header *header = device->snapshot_memory.ptr;
struct kgsl_snapshot *snapshot;
@@ -603,11 +621,20 @@
device->snapshot_faultcount++;
/*
- * The first hang is always the one we are interested in. Don't capture
- * a new snapshot instance if the old one hasn't been grabbed yet
+ * Overwrite a non-GMU fault snapshot if a GMU fault occurs.
*/
- if (device->snapshot != NULL)
- return;
+ if (device->snapshot != NULL) {
+ if (!gmu_fault || device->snapshot->gmu_fault)
+ return;
+
+ /*
+ * If another thread is currently reading it, that thread
+ * will free it, otherwise free it now.
+ */
+ if (!device->snapshot->sysfs_read)
+ kgsl_free_snapshot(device->snapshot);
+ device->snapshot = NULL;
+ }
/* Allocate memory for the snapshot instance */
snapshot = kzalloc(sizeof(*snapshot), GFP_KERNEL);
@@ -622,7 +649,9 @@
snapshot->start = device->snapshot_memory.ptr;
snapshot->ptr = device->snapshot_memory.ptr;
snapshot->remain = device->snapshot_memory.size;
- atomic_set(&snapshot->sysfs_read, 0);
+ snapshot->gmu_fault = gmu_fault;
+ snapshot->first_read = true;
+ snapshot->sysfs_read = 0;
header = (struct kgsl_snapshot_header *) snapshot->ptr;
@@ -711,6 +740,30 @@
#define kobj_to_device(a) \
container_of(a, struct kgsl_device, snapshot_kobj)
+static int snapshot_release(struct kgsl_device *device,
+ struct kgsl_snapshot *snapshot)
+{
+ bool snapshot_free = false;
+ int ret = 0;
+
+ mutex_lock(&device->mutex);
+ snapshot->sysfs_read--;
+
+ /*
+ * If someone's replaced the snapshot, return an error and free
+ * the snapshot if this is the last thread to read it.
+ */
+ if (device->snapshot != snapshot) {
+ ret = -EIO;
+ if (!snapshot->sysfs_read)
+ snapshot_free = true;
+ }
+ mutex_unlock(&device->mutex);
+ if (snapshot_free)
+ kgsl_free_snapshot(snapshot);
+ return ret;
+}
+
/* Dump the sysfs binary data to the user */
static ssize_t snapshot_show(struct file *filep, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off,
@@ -718,20 +771,35 @@
{
struct kgsl_device *device = kobj_to_device(kobj);
struct kgsl_snapshot *snapshot;
- struct kgsl_snapshot_object *obj, *tmp;
struct kgsl_snapshot_section_header head;
struct snapshot_obj_itr itr;
- int ret;
+ int ret = 0;
if (device == NULL)
return 0;
mutex_lock(&device->mutex);
snapshot = device->snapshot;
- if (snapshot != NULL)
- atomic_inc(&snapshot->sysfs_read);
+ if (snapshot != NULL) {
+ /*
+ * If we're reading at a non-zero offset from a new snapshot,
+ * that means we want to read from the previous snapshot (which
+ * was overwritten), so return an error
+ */
+ if (snapshot->first_read) {
+ if (off)
+ ret = -EIO;
+ else
+ snapshot->first_read = false;
+ }
+ if (!ret)
+ snapshot->sysfs_read++;
+ }
mutex_unlock(&device->mutex);
+ if (ret)
+ return ret;
+
/* Return nothing if we haven't taken a snapshot yet */
if (snapshot == NULL)
return 0;
@@ -742,7 +810,7 @@
*/
ret = wait_for_completion_interruptible(&snapshot->dump_gate);
if (ret) {
- atomic_dec(&snapshot->sysfs_read);
+ snapshot_release(device, snapshot);
return ret;
}
@@ -779,29 +847,21 @@
bool snapshot_free = false;
mutex_lock(&device->mutex);
- if (atomic_dec_and_test(&snapshot->sysfs_read)) {
- device->snapshot = NULL;
+ if (--snapshot->sysfs_read == 0) {
+ if (device->snapshot == snapshot)
+ device->snapshot = NULL;
snapshot_free = true;
}
mutex_unlock(&device->mutex);
- if (snapshot_free) {
- list_for_each_entry_safe(obj, tmp,
- &snapshot->obj_list, node)
- kgsl_snapshot_put_object(obj);
-
- if (snapshot->mempool)
- vfree(snapshot->mempool);
-
- kfree(snapshot);
- KGSL_CORE_ERR("snapshot: objects released\n");
- }
+ if (snapshot_free)
+ kgsl_free_snapshot(snapshot);
return 0;
}
done:
- atomic_dec(&snapshot->sysfs_read);
- return itr.write;
+ ret = snapshot_release(device, snapshot);
+ return (ret < 0) ? ret : itr.write;
}
/* Show the total number of hangs since device boot */
@@ -872,9 +932,11 @@
/* Show the timestamp of the last collected snapshot */
static ssize_t timestamp_show(struct kgsl_device *device, char *buf)
{
- unsigned long timestamp =
- device->snapshot ? device->snapshot->timestamp : 0;
+ unsigned long timestamp;
+ mutex_lock(&device->mutex);
+ timestamp = device->snapshot ? device->snapshot->timestamp : 0;
+ mutex_unlock(&device->mutex);
return snprintf(buf, PAGE_SIZE, "%lu\n", timestamp);
}