blob: abaa8ce2eb20ffd8df71b1bdaceac2a68dc5784a [file] [log] [blame]
Jordan Crouse156cfbc2012-01-24 09:32:04 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
Steve Mucklef132c6c2012-06-06 18:30:57 -070013#include <linux/export.h>
Jordan Crouse156cfbc2012-01-24 09:32:04 -070014#include <linux/time.h>
15#include <linux/sysfs.h>
16#include <linux/utsname.h>
17#include <linux/sched.h>
18#include <linux/idr.h>
19
20#include "kgsl.h"
21#include "kgsl_log.h"
22#include "kgsl_device.h"
23#include "kgsl_sharedmem.h"
24#include "kgsl_snapshot.h"
25
Jordan Crouse9610b6b2012-03-16 14:53:42 -060026/* Placeholder for the list of memory objects frozen after a hang */
27
28struct kgsl_snapshot_object {
29 unsigned int gpuaddr;
30 unsigned int ptbase;
31 unsigned int size;
Jordan Crouse233b2092012-04-18 09:31:09 -060032 unsigned int offset;
Jordan Crouse9610b6b2012-03-16 14:53:42 -060033 int type;
34 struct kgsl_mem_entry *entry;
35 struct list_head node;
36};
37
Jordan Crouse7c325702012-06-20 08:22:16 -060038struct snapshot_obj_itr {
39 void *buf; /* Buffer pointer to write to */
40 int pos; /* Current position in the sequence */
41 loff_t offset; /* file offset to start writing from */
42 size_t remain; /* Bytes remaining in buffer */
43 size_t write; /* Bytes written so far */
44};
45
46static void obj_itr_init(struct snapshot_obj_itr *itr, void *buf,
47 loff_t offset, size_t remain)
48{
49 itr->buf = buf;
50 itr->offset = offset;
51 itr->remain = remain;
52 itr->pos = 0;
53 itr->write = 0;
54}
55
56static int obj_itr_out(struct snapshot_obj_itr *itr, void *src, int size)
57{
58 if (itr->remain == 0)
59 return 0;
60
61 if ((itr->pos + size) <= itr->offset)
62 goto done;
63
64 /* Handle the case that offset is in the middle of the buffer */
65
66 if (itr->offset > itr->pos) {
67 src += (itr->offset - itr->pos);
68 size -= (itr->offset - itr->pos);
69
70 /* Advance pos to the offset start */
71 itr->pos = itr->offset;
72 }
73
74 if (size > itr->remain)
75 size = itr->remain;
76
77 memcpy(itr->buf, src, size);
78
79 itr->buf += size;
80 itr->write += size;
81 itr->remain -= size;
82
83done:
84 itr->pos += size;
85 return size;
86}
87
Jordan Crouse156cfbc2012-01-24 09:32:04 -070088/* idr_for_each function to count the number of contexts */
89
90static int snapshot_context_count(int id, void *ptr, void *data)
91{
92 int *count = data;
93 *count = *count + 1;
94
95 return 0;
96}
97
98/*
99 * To simplify the iterator loop use a global pointer instead of trying
100 * to pass around double star references to the snapshot data
101 */
102
103static void *_ctxtptr;
104
105static int snapshot_context_info(int id, void *ptr, void *data)
106{
107 struct kgsl_snapshot_linux_context *header = _ctxtptr;
108 struct kgsl_context *context = ptr;
109 struct kgsl_device *device = context->dev_priv->device;
110
111 header->id = id;
112
113 /* Future-proof for per-context timestamps - for now, just
114 * return the global timestamp for all contexts
115 */
116
Jeremy Gebben731dac52012-05-10 11:13:42 -0600117 header->timestamp_queued = kgsl_readtimestamp(device, context,
118 KGSL_TIMESTAMP_QUEUED);
119 header->timestamp_retired = kgsl_readtimestamp(device, context,
120 KGSL_TIMESTAMP_RETIRED);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700121
122 _ctxtptr += sizeof(struct kgsl_snapshot_linux_context);
123
124 return 0;
125}
126
127/* Snapshot the Linux specific information */
128static int snapshot_os(struct kgsl_device *device,
129 void *snapshot, int remain, void *priv)
130{
131 struct kgsl_snapshot_linux *header = snapshot;
132 struct kgsl_pwrctrl *pwr = &device->pwrctrl;
133 struct task_struct *task;
134 pid_t pid;
135 int hang = (int) priv;
136 int ctxtcount = 0;
137 int size = sizeof(*header);
138
139 /* Figure out how many active contexts there are - these will
140 * be appended on the end of the structure */
141
142 idr_for_each(&device->context_idr, snapshot_context_count, &ctxtcount);
143
144 size += ctxtcount * sizeof(struct kgsl_snapshot_linux_context);
145
146 /* Make sure there is enough room for the data */
147 if (remain < size) {
148 SNAPSHOT_ERR_NOMEM(device, "OS");
149 return 0;
150 }
151
152 memset(header, 0, sizeof(*header));
153
154 header->osid = KGSL_SNAPSHOT_OS_LINUX;
155
156 header->state = hang ? SNAPSHOT_STATE_HUNG : SNAPSHOT_STATE_RUNNING;
157
158 /* Get the kernel build information */
159 strlcpy(header->release, utsname()->release, sizeof(header->release));
160 strlcpy(header->version, utsname()->version, sizeof(header->version));
161
162 /* Get the Unix time for the timestamp */
163 header->seconds = get_seconds();
164
165 /* Remember the power information */
166 header->power_flags = pwr->power_flags;
167 header->power_level = pwr->active_pwrlevel;
168 header->power_interval_timeout = pwr->interval_timeout;
169 header->grpclk = kgsl_get_clkrate(pwr->grp_clks[0]);
170 header->busclk = kgsl_get_clkrate(pwr->ebi1_clk);
171
172 /* Future proof for per-context timestamps */
173 header->current_context = -1;
174
175 /* Get the current PT base */
Shubhraprakash Das79447952012-04-26 18:12:23 -0600176 header->ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700177 /* And the PID for the task leader */
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700178 pid = header->pid = kgsl_mmu_get_ptname_from_ptbase(&device->mmu,
179 header->ptbase);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700180
181 task = find_task_by_vpid(pid);
182
183 if (task)
184 get_task_comm(header->comm, task);
185
186 header->ctxtcount = ctxtcount;
187
188 /* append information for each context */
189 _ctxtptr = snapshot + sizeof(*header);
190 idr_for_each(&device->context_idr, snapshot_context_info, NULL);
191
192 /* Return the size of the data segment */
193 return size;
194}
195/*
196 * kgsl_snapshot_dump_indexed_regs - helper function to dump indexed registers
197 * @device - the device to dump registers from
198 * @snapshot - pointer to the start of the region of memory for the snapshot
199 * @remain - a pointer to the number of bytes remaining in the snapshot
200 * @priv - A pointer to the kgsl_snapshot_indexed_registers data
201 *
202 * Given a indexed register cmd/data pair and a count, dump each indexed
203 * register
204 */
205
Jordan Crouse0c2761a2012-02-01 22:11:12 -0700206static int kgsl_snapshot_dump_indexed_regs(struct kgsl_device *device,
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700207 void *snapshot, int remain, void *priv)
208{
209 struct kgsl_snapshot_indexed_registers *iregs = priv;
210 struct kgsl_snapshot_indexed_regs *header = snapshot;
211 unsigned int *data = snapshot + sizeof(*header);
212 int i;
213
214 if (remain < (iregs->count * 4) + sizeof(*header)) {
215 SNAPSHOT_ERR_NOMEM(device, "INDEXED REGS");
216 return 0;
217 }
218
219 header->index_reg = iregs->index;
220 header->data_reg = iregs->data;
221 header->count = iregs->count;
222 header->start = iregs->start;
223
224 for (i = 0; i < iregs->count; i++) {
225 kgsl_regwrite(device, iregs->index, iregs->start + i);
226 kgsl_regread(device, iregs->data, &data[i]);
227 }
228
229 return (iregs->count * 4) + sizeof(*header);
230}
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700231
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600232#define GPU_OBJ_HEADER_SZ \
233 (sizeof(struct kgsl_snapshot_section_header) + \
234 sizeof(struct kgsl_snapshot_gpu_object))
235
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600236static int kgsl_snapshot_dump_object(struct kgsl_device *device,
Jordan Crouse7c325702012-06-20 08:22:16 -0600237 struct kgsl_snapshot_object *obj, struct snapshot_obj_itr *itr)
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600238{
Jordan Crouse7c325702012-06-20 08:22:16 -0600239 struct kgsl_snapshot_section_header sect;
240 struct kgsl_snapshot_gpu_object header;
241 int ret;
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600242
Jordan Crouse7c325702012-06-20 08:22:16 -0600243 sect.magic = SNAPSHOT_SECTION_MAGIC;
244 sect.id = KGSL_SNAPSHOT_SECTION_GPU_OBJECT;
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600245
Jordan Crouse7c325702012-06-20 08:22:16 -0600246 /*
247 * Header size is in dwords, object size is in bytes -
248 * round up if the object size isn't dword aligned
249 */
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600250
Jordan Crouse7c325702012-06-20 08:22:16 -0600251 sect.size = GPU_OBJ_HEADER_SZ + ALIGN(obj->size, 4);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600252
Jordan Crouse7c325702012-06-20 08:22:16 -0600253 ret = obj_itr_out(itr, &sect, sizeof(sect));
254 if (ret == 0)
255 return 0;
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600256
Jordan Crouse7c325702012-06-20 08:22:16 -0600257 header.size = ALIGN(obj->size, 4) >> 2;
258 header.gpuaddr = obj->gpuaddr;
259 header.ptbase = obj->ptbase;
260 header.type = obj->type;
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600261
Jordan Crouse7c325702012-06-20 08:22:16 -0600262 ret = obj_itr_out(itr, &header, sizeof(header));
263 if (ret == 0)
264 return 0;
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600265
Jordan Crouse7c325702012-06-20 08:22:16 -0600266 ret = obj_itr_out(itr, obj->entry->memdesc.hostptr + obj->offset,
267 obj->size);
268 if (ret == 0)
269 return 0;
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600270
Jordan Crouse7c325702012-06-20 08:22:16 -0600271 /* Pad the end to a dword boundary if we need to */
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600272
Jordan Crouse7c325702012-06-20 08:22:16 -0600273 if (obj->size % 4) {
274 unsigned int dummy = 0;
275 ret = obj_itr_out(itr, &dummy, obj->size % 4);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600276 }
277
278 return ret;
279}
280
281static void kgsl_snapshot_put_object(struct kgsl_device *device,
282 struct kgsl_snapshot_object *obj)
283{
284 list_del(&obj->node);
285
286 obj->entry->flags &= ~KGSL_MEM_ENTRY_FROZEN;
287 kgsl_mem_entry_put(obj->entry);
288
289 kfree(obj);
290}
291
292/* kgsl_snapshot_get_object - Mark a GPU buffer to be frozen
293 * @device - the device that is being snapshotted
294 * @ptbase - the pagetable base of the object to freeze
295 * @gpuaddr - The gpu address of the object to freeze
296 * @size - the size of the object (may not always be the size of the region)
297 * @type - the type of object being saved (shader, vbo, etc)
298 *
299 * Mark and freeze a GPU buffer object. This will prevent it from being
300 * freed until it can be copied out as part of the snapshot dump. Returns the
301 * size of the object being frozen
302 */
303
304int kgsl_snapshot_get_object(struct kgsl_device *device, unsigned int ptbase,
305 unsigned int gpuaddr, unsigned int size, unsigned int type)
306{
307 struct kgsl_mem_entry *entry;
308 struct kgsl_snapshot_object *obj;
309 int offset;
310
Shubhraprakash Das3cf33be2012-08-16 22:42:55 -0700311 entry = kgsl_get_mem_entry(device, ptbase, gpuaddr, size);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600312
313 if (entry == NULL) {
314 KGSL_DRV_ERR(device, "Unable to find GPU buffer %8.8X\n",
315 gpuaddr);
316 return 0;
317 }
318
319 /* We can't freeze external memory, because we don't own it */
320 if (entry->memtype != KGSL_MEM_ENTRY_KERNEL) {
321 KGSL_DRV_ERR(device,
322 "Only internal GPU buffers can be frozen\n");
323 return 0;
324 }
325
326 /*
327 * size indicates the number of bytes in the region to save. This might
328 * not always be the entire size of the region because some buffers are
329 * sub-allocated from a larger region. However, if size 0 was passed
330 * thats a flag that the caller wants to capture the entire buffer
331 */
332
333 if (size == 0) {
334 size = entry->memdesc.size;
335 offset = 0;
336
337 /* Adjust the gpuaddr to the start of the object */
338 gpuaddr = entry->memdesc.gpuaddr;
339 } else {
340 offset = gpuaddr - entry->memdesc.gpuaddr;
341 }
342
343 if (size + offset > entry->memdesc.size) {
344 KGSL_DRV_ERR(device, "Invalid size for GPU buffer %8.8X\n",
345 gpuaddr);
346 return 0;
347 }
348
349 /* If the buffer is already on the list, skip it */
350 list_for_each_entry(obj, &device->snapshot_obj_list, node) {
351 if (obj->gpuaddr == gpuaddr && obj->ptbase == ptbase) {
352 /* If the size is different, use the new size */
353 if (obj->size != size)
354 obj->size = size;
355
356 return 0;
357 }
358 }
359
Jordan Crouse17d6d8b2012-04-18 09:31:09 -0600360 if (kgsl_memdesc_map(&entry->memdesc) == NULL) {
361 KGSL_DRV_ERR(device, "Unable to map GPU buffer %X\n",
362 gpuaddr);
363 return 0;
364 }
365
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600366 obj = kzalloc(sizeof(*obj), GFP_KERNEL);
367
368 if (obj == NULL) {
369 KGSL_DRV_ERR(device, "Unable to allocate memory\n");
370 return 0;
371 }
372
373 /* Ref count the mem entry */
374 kgsl_mem_entry_get(entry);
375
376 obj->type = type;
377 obj->entry = entry;
378 obj->gpuaddr = gpuaddr;
379 obj->ptbase = ptbase;
380 obj->size = size;
Jordan Crouse233b2092012-04-18 09:31:09 -0600381 obj->offset = offset;
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600382
383 list_add(&obj->node, &device->snapshot_obj_list);
384
385 /*
386 * Return the size of the entire mem entry that was frozen - this gets
387 * used for tracking how much memory is frozen for a hang. Also, mark
388 * the memory entry as frozen. If the entry was already marked as
389 * frozen, then another buffer already got to it. In that case, return
390 * 0 so it doesn't get counted twice
391 */
392
393 if (entry->flags & KGSL_MEM_ENTRY_FROZEN)
394 return 0;
395
396 entry->flags |= KGSL_MEM_ENTRY_FROZEN;
397
398 return entry->memdesc.size;
399}
400EXPORT_SYMBOL(kgsl_snapshot_get_object);
401
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700402/*
403 * kgsl_snapshot_dump_regs - helper function to dump device registers
404 * @device - the device to dump registers from
405 * @snapshot - pointer to the start of the region of memory for the snapshot
406 * @remain - a pointer to the number of bytes remaining in the snapshot
407 * @priv - A pointer to the kgsl_snapshot_registers data
408 *
409 * Given an array of register ranges pairs (start,end [inclusive]), dump the
410 * registers into a snapshot register section. The snapshot region stores a
411 * part of dwords for each register - the word address of the register, and
412 * the value.
413 */
414int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot,
415 int remain, void *priv)
416{
417 struct kgsl_snapshot_regs *header = snapshot;
418 struct kgsl_snapshot_registers *regs = priv;
419 unsigned int *data = snapshot + sizeof(*header);
420 int count = 0, i, j;
421
422 /* Figure out how many registers we are going to dump */
423
424 for (i = 0; i < regs->count; i++) {
425 int start = regs->regs[i * 2];
426 int end = regs->regs[i * 2 + 1];
427
428 count += (end - start + 1);
429 }
430
431 if (remain < (count * 8) + sizeof(*header)) {
432 SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
433 return 0;
434 }
435
436 for (i = 0; i < regs->count; i++) {
437 unsigned int start = regs->regs[i * 2];
438 unsigned int end = regs->regs[i * 2 + 1];
439
440 for (j = start; j <= end; j++) {
441 unsigned int val;
442
443 kgsl_regread(device, j, &val);
444 *data++ = j;
445 *data++ = val;
446 }
447 }
448
449 header->count = count;
450
451 /* Return the size of the section */
452 return (count * 8) + sizeof(*header);
453}
454EXPORT_SYMBOL(kgsl_snapshot_dump_regs);
455
Jordan Crouse0c2761a2012-02-01 22:11:12 -0700456void *kgsl_snapshot_indexed_registers(struct kgsl_device *device,
457 void *snapshot, int *remain,
458 unsigned int index, unsigned int data, unsigned int start,
459 unsigned int count)
460{
461 struct kgsl_snapshot_indexed_registers iregs;
462 iregs.index = index;
463 iregs.data = data;
464 iregs.start = start;
465 iregs.count = count;
466
467 return kgsl_snapshot_add_section(device,
468 KGSL_SNAPSHOT_SECTION_INDEXED_REGS, snapshot,
469 remain, kgsl_snapshot_dump_indexed_regs, &iregs);
470}
471EXPORT_SYMBOL(kgsl_snapshot_indexed_registers);
472
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700473/*
474 * kgsl_snapshot - construct a device snapshot
475 * @device - device to snapshot
476 * @hang - set to 1 if the snapshot was triggered following a hnag
477 * Given a device, construct a binary snapshot dump of the current device state
478 * and store it in the device snapshot memory.
479 */
480int kgsl_device_snapshot(struct kgsl_device *device, int hang)
481{
482 struct kgsl_snapshot_header *header = device->snapshot;
483 int remain = device->snapshot_maxsize - sizeof(*header);
484 void *snapshot;
485
486 /*
487 * The first hang is always the one we are interested in. To
488 * avoid a subsequent hang blowing away the first, the snapshot
489 * is frozen until it is dumped via sysfs.
490 *
491 * Note that triggered snapshots are always taken regardless
492 * of the state and never frozen.
493 */
494
495 if (hang && device->snapshot_frozen == 1)
496 return 0;
497
498 if (device->snapshot == NULL) {
499 KGSL_DRV_ERR(device,
500 "snapshot: No snapshot memory available\n");
501 return -ENOMEM;
502 }
503
504 if (remain < sizeof(*header)) {
505 KGSL_DRV_ERR(device,
506 "snapshot: Not enough memory for the header\n");
507 return -ENOMEM;
508 }
509
510 header->magic = SNAPSHOT_MAGIC;
511
Jordan Crouse70829d32012-06-20 08:22:17 -0600512 header->gpuid = kgsl_gpuid(device, &header->chipid);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700513
514 /* Get a pointer to the first section (right after the header) */
515 snapshot = ((void *) device->snapshot) + sizeof(*header);
516
517 /* Build the Linux specific header */
518 snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
519 snapshot, &remain, snapshot_os, (void *) hang);
520
521 /* Get the device specific sections */
522 if (device->ftbl->snapshot)
523 snapshot = device->ftbl->snapshot(device, snapshot, &remain,
524 hang);
525
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700526 device->snapshot_timestamp = get_seconds();
527 device->snapshot_size = (int) (snapshot - device->snapshot);
528
529 /* Freeze the snapshot on a hang until it gets read */
530 device->snapshot_frozen = (hang) ? 1 : 0;
531
Jeremy Gebben9d15ae42012-02-29 16:50:27 -0700532 /* log buffer info to aid in ramdump recovery */
533 KGSL_DRV_ERR(device, "snapshot created at va %p pa %lx size %d\n",
534 device->snapshot, __pa(device->snapshot),
535 device->snapshot_size);
Jeremy Gebben90812c82012-03-08 12:46:01 -0700536 if (hang)
537 sysfs_notify(&device->snapshot_kobj, NULL, "timestamp");
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700538 return 0;
539}
540EXPORT_SYMBOL(kgsl_device_snapshot);
541
542/* An attribute for showing snapshot details */
543struct kgsl_snapshot_attribute {
544 struct attribute attr;
545 ssize_t (*show)(struct kgsl_device *device, char *buf);
546 ssize_t (*store)(struct kgsl_device *device, const char *buf,
547 size_t count);
548};
549
550#define to_snapshot_attr(a) \
551container_of(a, struct kgsl_snapshot_attribute, attr)
552
553#define kobj_to_device(a) \
554container_of(a, struct kgsl_device, snapshot_kobj)
555
556/* Dump the sysfs binary data to the user */
557static ssize_t snapshot_show(struct file *filep, struct kobject *kobj,
558 struct bin_attribute *attr, char *buf, loff_t off,
559 size_t count)
560{
561 struct kgsl_device *device = kobj_to_device(kobj);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600562 struct kgsl_snapshot_object *obj, *tmp;
Jordan Crouse7c325702012-06-20 08:22:16 -0600563 struct kgsl_snapshot_section_header head;
564 struct snapshot_obj_itr itr;
565 int ret;
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700566
567 if (device == NULL)
568 return 0;
569
570 /* Return nothing if we haven't taken a snapshot yet */
571 if (device->snapshot_timestamp == 0)
572 return 0;
573
574 /* Get the mutex to keep things from changing while we are dumping */
575 mutex_lock(&device->mutex);
576
Jordan Crouse7c325702012-06-20 08:22:16 -0600577 obj_itr_init(&itr, buf, off, count);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600578
Jordan Crouse7c325702012-06-20 08:22:16 -0600579 ret = obj_itr_out(&itr, device->snapshot, device->snapshot_size);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600580
Jordan Crouse7c325702012-06-20 08:22:16 -0600581 if (ret == 0)
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600582 goto done;
583
Jordan Crouse7c325702012-06-20 08:22:16 -0600584 list_for_each_entry(obj, &device->snapshot_obj_list, node)
585 kgsl_snapshot_dump_object(device, obj, &itr);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600586
Jordan Crouse7c325702012-06-20 08:22:16 -0600587 {
588 head.magic = SNAPSHOT_SECTION_MAGIC;
589 head.id = KGSL_SNAPSHOT_SECTION_END;
590 head.size = sizeof(head);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600591
Jordan Crouse7c325702012-06-20 08:22:16 -0600592 obj_itr_out(&itr, &head, sizeof(head));
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600593 }
594
Jordan Crouse7c325702012-06-20 08:22:16 -0600595 /*
596 * Make sure everything has been written out before destroying things.
597 * The best way to confirm this is to go all the way through without
598 * writing any bytes - so only release if we get this far and
599 * itr->write is 0
600 */
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600601
Jordan Crouse7c325702012-06-20 08:22:16 -0600602 if (itr.write == 0) {
603 list_for_each_entry_safe(obj, tmp, &device->snapshot_obj_list,
604 node)
605 kgsl_snapshot_put_object(device, obj);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600606
Jordan Crouse7c325702012-06-20 08:22:16 -0600607 if (device->snapshot_frozen)
608 KGSL_DRV_ERR(device, "Snapshot objects released\n");
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600609
Jordan Crouse7c325702012-06-20 08:22:16 -0600610 device->snapshot_frozen = 0;
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600611 }
612
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600613done:
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700614 mutex_unlock(&device->mutex);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600615
Jordan Crouse7c325702012-06-20 08:22:16 -0600616 return itr.write;
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700617}
618
619/* Show the timestamp of the last collected snapshot */
620static ssize_t timestamp_show(struct kgsl_device *device, char *buf)
621{
622 return snprintf(buf, PAGE_SIZE, "%x\n", device->snapshot_timestamp);
623}
624
625/* manually trigger a new snapshot to be collected */
626static ssize_t trigger_store(struct kgsl_device *device, const char *buf,
627 size_t count)
628{
629 if (device && count > 0) {
630 mutex_lock(&device->mutex);
631 kgsl_device_snapshot(device, 0);
632 mutex_unlock(&device->mutex);
633 }
634
635 return count;
636}
637
638static struct bin_attribute snapshot_attr = {
639 .attr.name = "dump",
640 .attr.mode = 0444,
641 .size = 0,
642 .read = snapshot_show
643};
644
645#define SNAPSHOT_ATTR(_name, _mode, _show, _store) \
646struct kgsl_snapshot_attribute attr_##_name = { \
647 .attr = { .name = __stringify(_name), .mode = _mode }, \
648 .show = _show, \
649 .store = _store, \
650}
651
652SNAPSHOT_ATTR(trigger, 0600, NULL, trigger_store);
653SNAPSHOT_ATTR(timestamp, 0444, timestamp_show, NULL);
654
655static void snapshot_sysfs_release(struct kobject *kobj)
656{
657}
658
659static ssize_t snapshot_sysfs_show(struct kobject *kobj,
660 struct attribute *attr, char *buf)
661{
662 struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
663 struct kgsl_device *device = kobj_to_device(kobj);
664 ssize_t ret;
665
666 if (device && pattr->show)
667 ret = pattr->show(device, buf);
668 else
669 ret = -EIO;
670
671 return ret;
672}
673
674static ssize_t snapshot_sysfs_store(struct kobject *kobj,
675 struct attribute *attr, const char *buf, size_t count)
676{
677 struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
678 struct kgsl_device *device = kobj_to_device(kobj);
679 ssize_t ret;
680
681 if (device && pattr->store)
682 ret = pattr->store(device, buf, count);
683 else
684 ret = -EIO;
685
686 return ret;
687}
688
689static const struct sysfs_ops snapshot_sysfs_ops = {
690 .show = snapshot_sysfs_show,
691 .store = snapshot_sysfs_store,
692};
693
694static struct kobj_type ktype_snapshot = {
695 .sysfs_ops = &snapshot_sysfs_ops,
696 .default_attrs = NULL,
697 .release = snapshot_sysfs_release,
698};
699
700/* kgsl_device_snapshot_init - Add resources for the device GPU snapshot
701 * @device - The device to initalize
702 *
703 * Allocate memory for a GPU snapshot for the specified device,
704 * and create the sysfs files to manage it
705 */
706
707int kgsl_device_snapshot_init(struct kgsl_device *device)
708{
709 int ret;
710
711 if (device->snapshot == NULL)
Jeremy Gebben9d15ae42012-02-29 16:50:27 -0700712 device->snapshot = kzalloc(KGSL_SNAPSHOT_MEMSIZE, GFP_KERNEL);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700713
714 if (device->snapshot == NULL)
715 return -ENOMEM;
716
717 device->snapshot_maxsize = KGSL_SNAPSHOT_MEMSIZE;
718 device->snapshot_timestamp = 0;
719
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600720 INIT_LIST_HEAD(&device->snapshot_obj_list);
721
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700722 ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot,
723 &device->dev->kobj, "snapshot");
724 if (ret)
725 goto done;
726
727 ret = sysfs_create_bin_file(&device->snapshot_kobj, &snapshot_attr);
728 if (ret)
729 goto done;
730
731 ret = sysfs_create_file(&device->snapshot_kobj, &attr_trigger.attr);
732 if (ret)
733 goto done;
734
735 ret = sysfs_create_file(&device->snapshot_kobj, &attr_timestamp.attr);
736
737done:
738 return ret;
739}
740EXPORT_SYMBOL(kgsl_device_snapshot_init);
741
742/* kgsl_device_snapshot_close - Take down snapshot memory for a device
743 * @device - Pointer to the kgsl_device
744 *
745 * Remove the sysfs files and free the memory allocated for the GPU
746 * snapshot
747 */
748
749void kgsl_device_snapshot_close(struct kgsl_device *device)
750{
751 sysfs_remove_bin_file(&device->snapshot_kobj, &snapshot_attr);
752 sysfs_remove_file(&device->snapshot_kobj, &attr_trigger.attr);
753 sysfs_remove_file(&device->snapshot_kobj, &attr_timestamp.attr);
754
755 kobject_put(&device->snapshot_kobj);
756
Jeremy Gebben9d15ae42012-02-29 16:50:27 -0700757 kfree(device->snapshot);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700758
759 device->snapshot = NULL;
760 device->snapshot_maxsize = 0;
761 device->snapshot_timestamp = 0;
762}
763EXPORT_SYMBOL(kgsl_device_snapshot_close);