blob: cfcb2ea77f043b17aa925ae76335d5108583c816 [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
Jordan Crouse156cfbc2012-01-24 09:32:04 -070013#include <linux/time.h>
14#include <linux/sysfs.h>
15#include <linux/utsname.h>
16#include <linux/sched.h>
17#include <linux/idr.h>
18
19#include "kgsl.h"
20#include "kgsl_log.h"
21#include "kgsl_device.h"
22#include "kgsl_sharedmem.h"
23#include "kgsl_snapshot.h"
24
Jordan Crouse9610b6b2012-03-16 14:53:42 -060025/* Placeholder for the list of memory objects frozen after a hang */
26
27struct kgsl_snapshot_object {
28 unsigned int gpuaddr;
29 unsigned int ptbase;
30 unsigned int size;
31 int type;
32 struct kgsl_mem_entry *entry;
33 struct list_head node;
34};
35
Jordan Crouse156cfbc2012-01-24 09:32:04 -070036/* idr_for_each function to count the number of contexts */
37
38static int snapshot_context_count(int id, void *ptr, void *data)
39{
40 int *count = data;
41 *count = *count + 1;
42
43 return 0;
44}
45
46/*
47 * To simplify the iterator loop use a global pointer instead of trying
48 * to pass around double star references to the snapshot data
49 */
50
51static void *_ctxtptr;
52
53static int snapshot_context_info(int id, void *ptr, void *data)
54{
55 struct kgsl_snapshot_linux_context *header = _ctxtptr;
56 struct kgsl_context *context = ptr;
57 struct kgsl_device *device = context->dev_priv->device;
58
59 header->id = id;
60
61 /* Future-proof for per-context timestamps - for now, just
62 * return the global timestamp for all contexts
63 */
64
65 header->timestamp_queued = -1;
66 header->timestamp_retired = device->ftbl->readtimestamp(device,
Wei Zouc8c01632012-03-24 17:27:26 -070067 KGSL_TIMESTAMP_RETIRED);
Jordan Crouse156cfbc2012-01-24 09:32:04 -070068
69 _ctxtptr += sizeof(struct kgsl_snapshot_linux_context);
70
71 return 0;
72}
73
74/* Snapshot the Linux specific information */
75static int snapshot_os(struct kgsl_device *device,
76 void *snapshot, int remain, void *priv)
77{
78 struct kgsl_snapshot_linux *header = snapshot;
79 struct kgsl_pwrctrl *pwr = &device->pwrctrl;
80 struct task_struct *task;
81 pid_t pid;
82 int hang = (int) priv;
83 int ctxtcount = 0;
84 int size = sizeof(*header);
85
86 /* Figure out how many active contexts there are - these will
87 * be appended on the end of the structure */
88
89 idr_for_each(&device->context_idr, snapshot_context_count, &ctxtcount);
90
91 size += ctxtcount * sizeof(struct kgsl_snapshot_linux_context);
92
93 /* Make sure there is enough room for the data */
94 if (remain < size) {
95 SNAPSHOT_ERR_NOMEM(device, "OS");
96 return 0;
97 }
98
99 memset(header, 0, sizeof(*header));
100
101 header->osid = KGSL_SNAPSHOT_OS_LINUX;
102
103 header->state = hang ? SNAPSHOT_STATE_HUNG : SNAPSHOT_STATE_RUNNING;
104
105 /* Get the kernel build information */
106 strlcpy(header->release, utsname()->release, sizeof(header->release));
107 strlcpy(header->version, utsname()->version, sizeof(header->version));
108
109 /* Get the Unix time for the timestamp */
110 header->seconds = get_seconds();
111
112 /* Remember the power information */
113 header->power_flags = pwr->power_flags;
114 header->power_level = pwr->active_pwrlevel;
115 header->power_interval_timeout = pwr->interval_timeout;
116 header->grpclk = kgsl_get_clkrate(pwr->grp_clks[0]);
117 header->busclk = kgsl_get_clkrate(pwr->ebi1_clk);
118
119 /* Future proof for per-context timestamps */
120 header->current_context = -1;
121
122 /* Get the current PT base */
123 header->ptbase = kgsl_mmu_get_current_ptbase(device);
124 /* And the PID for the task leader */
125 pid = header->pid = kgsl_mmu_get_ptname_from_ptbase(header->ptbase);
126
127 task = find_task_by_vpid(pid);
128
129 if (task)
130 get_task_comm(header->comm, task);
131
132 header->ctxtcount = ctxtcount;
133
134 /* append information for each context */
135 _ctxtptr = snapshot + sizeof(*header);
136 idr_for_each(&device->context_idr, snapshot_context_info, NULL);
137
138 /* Return the size of the data segment */
139 return size;
140}
141/*
142 * kgsl_snapshot_dump_indexed_regs - helper function to dump indexed registers
143 * @device - the device to dump registers from
144 * @snapshot - pointer to the start of the region of memory for the snapshot
145 * @remain - a pointer to the number of bytes remaining in the snapshot
146 * @priv - A pointer to the kgsl_snapshot_indexed_registers data
147 *
148 * Given a indexed register cmd/data pair and a count, dump each indexed
149 * register
150 */
151
Jordan Crouse0c2761a2012-02-01 22:11:12 -0700152static int kgsl_snapshot_dump_indexed_regs(struct kgsl_device *device,
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700153 void *snapshot, int remain, void *priv)
154{
155 struct kgsl_snapshot_indexed_registers *iregs = priv;
156 struct kgsl_snapshot_indexed_regs *header = snapshot;
157 unsigned int *data = snapshot + sizeof(*header);
158 int i;
159
160 if (remain < (iregs->count * 4) + sizeof(*header)) {
161 SNAPSHOT_ERR_NOMEM(device, "INDEXED REGS");
162 return 0;
163 }
164
165 header->index_reg = iregs->index;
166 header->data_reg = iregs->data;
167 header->count = iregs->count;
168 header->start = iregs->start;
169
170 for (i = 0; i < iregs->count; i++) {
171 kgsl_regwrite(device, iregs->index, iregs->start + i);
172 kgsl_regread(device, iregs->data, &data[i]);
173 }
174
175 return (iregs->count * 4) + sizeof(*header);
176}
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700177
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600178#define GPU_OBJ_HEADER_SZ \
179 (sizeof(struct kgsl_snapshot_section_header) + \
180 sizeof(struct kgsl_snapshot_gpu_object))
181
182#define GPU_OBJ_SECTION_SIZE(_o) \
183 (GPU_OBJ_HEADER_SZ + ((_o)->size))
184
185static int kgsl_snapshot_dump_object(struct kgsl_device *device,
186 struct kgsl_snapshot_object *obj, void *buf,
187 unsigned int off, unsigned int count)
188{
189 unsigned char headers[GPU_OBJ_HEADER_SZ];
190 struct kgsl_snapshot_section_header *sect =
191 (struct kgsl_snapshot_section_header *) headers;
192 struct kgsl_snapshot_gpu_object *header =
193 (struct kgsl_snapshot_gpu_object *) (headers + sizeof(*sect));
194 int ret = 0;
195
196 /* Construct a local copy of the headers */
197
198 sect->magic = SNAPSHOT_SECTION_MAGIC;
199 sect->id = KGSL_SNAPSHOT_SECTION_GPU_OBJECT;
200 sect->size = GPU_OBJ_SECTION_SIZE(obj);
201
202 header->type = obj->type;
203
204 /* Header size is in dwords, object size is in bytes */
205 header->size = obj->size >> 2;
206 header->gpuaddr = obj->gpuaddr;
207 header->ptbase = obj->ptbase;
208
209 /* Copy out any part of the header block that is needed */
210
211 if (off < GPU_OBJ_HEADER_SZ) {
212 int size = count < GPU_OBJ_HEADER_SZ - off ?
213 count : GPU_OBJ_HEADER_SZ - off;
214
215 memcpy(buf, headers + off, size);
216
217 count -= size;
218 ret += size;
219 }
220
221 /* Now copy whatever part of the data is needed */
222
223 if (off < (GPU_OBJ_HEADER_SZ + obj->size)) {
224 int offset;
225 int size = count < obj->size ? count : obj->size;
226
227 /*
228 * If the desired gpuaddr isn't at the beginning of the region,
229 * then offset the source pointer
230 */
231
232 offset = obj->gpuaddr - obj->entry->memdesc.gpuaddr;
233
234 /*
235 * Then adjust it to account for the offset for the output
236 * buffer.
237 */
238
239 if (off > GPU_OBJ_HEADER_SZ) {
240 int loff = (off - GPU_OBJ_HEADER_SZ);
241
242 /* Adjust the size so we don't walk off the end */
243
244 if ((loff + size) > obj->size)
245 size = obj->size - loff;
246
247 offset += loff;
248 }
249
250 memcpy(buf + ret, obj->entry->memdesc.hostptr + offset, size);
251 ret += size;
252 }
253
254 return ret;
255}
256
257static void kgsl_snapshot_put_object(struct kgsl_device *device,
258 struct kgsl_snapshot_object *obj)
259{
260 list_del(&obj->node);
261
262 obj->entry->flags &= ~KGSL_MEM_ENTRY_FROZEN;
263 kgsl_mem_entry_put(obj->entry);
264
265 kfree(obj);
266}
267
268/* kgsl_snapshot_get_object - Mark a GPU buffer to be frozen
269 * @device - the device that is being snapshotted
270 * @ptbase - the pagetable base of the object to freeze
271 * @gpuaddr - The gpu address of the object to freeze
272 * @size - the size of the object (may not always be the size of the region)
273 * @type - the type of object being saved (shader, vbo, etc)
274 *
275 * Mark and freeze a GPU buffer object. This will prevent it from being
276 * freed until it can be copied out as part of the snapshot dump. Returns the
277 * size of the object being frozen
278 */
279
280int kgsl_snapshot_get_object(struct kgsl_device *device, unsigned int ptbase,
281 unsigned int gpuaddr, unsigned int size, unsigned int type)
282{
283 struct kgsl_mem_entry *entry;
284 struct kgsl_snapshot_object *obj;
285 int offset;
286
287 entry = kgsl_get_mem_entry(ptbase, gpuaddr, size);
288
289 if (entry == NULL) {
290 KGSL_DRV_ERR(device, "Unable to find GPU buffer %8.8X\n",
291 gpuaddr);
292 return 0;
293 }
294
295 /* We can't freeze external memory, because we don't own it */
296 if (entry->memtype != KGSL_MEM_ENTRY_KERNEL) {
297 KGSL_DRV_ERR(device,
298 "Only internal GPU buffers can be frozen\n");
299 return 0;
300 }
301
302 /*
303 * size indicates the number of bytes in the region to save. This might
304 * not always be the entire size of the region because some buffers are
305 * sub-allocated from a larger region. However, if size 0 was passed
306 * thats a flag that the caller wants to capture the entire buffer
307 */
308
309 if (size == 0) {
310 size = entry->memdesc.size;
311 offset = 0;
312
313 /* Adjust the gpuaddr to the start of the object */
314 gpuaddr = entry->memdesc.gpuaddr;
315 } else {
316 offset = gpuaddr - entry->memdesc.gpuaddr;
317 }
318
319 if (size + offset > entry->memdesc.size) {
320 KGSL_DRV_ERR(device, "Invalid size for GPU buffer %8.8X\n",
321 gpuaddr);
322 return 0;
323 }
324
325 /* If the buffer is already on the list, skip it */
326 list_for_each_entry(obj, &device->snapshot_obj_list, node) {
327 if (obj->gpuaddr == gpuaddr && obj->ptbase == ptbase) {
328 /* If the size is different, use the new size */
329 if (obj->size != size)
330 obj->size = size;
331
332 return 0;
333 }
334 }
335
336 obj = kzalloc(sizeof(*obj), GFP_KERNEL);
337
338 if (obj == NULL) {
339 KGSL_DRV_ERR(device, "Unable to allocate memory\n");
340 return 0;
341 }
342
343 /* Ref count the mem entry */
344 kgsl_mem_entry_get(entry);
345
346 obj->type = type;
347 obj->entry = entry;
348 obj->gpuaddr = gpuaddr;
349 obj->ptbase = ptbase;
350 obj->size = size;
351
352 list_add(&obj->node, &device->snapshot_obj_list);
353
354 /*
355 * Return the size of the entire mem entry that was frozen - this gets
356 * used for tracking how much memory is frozen for a hang. Also, mark
357 * the memory entry as frozen. If the entry was already marked as
358 * frozen, then another buffer already got to it. In that case, return
359 * 0 so it doesn't get counted twice
360 */
361
362 if (entry->flags & KGSL_MEM_ENTRY_FROZEN)
363 return 0;
364
365 entry->flags |= KGSL_MEM_ENTRY_FROZEN;
366
367 return entry->memdesc.size;
368}
369EXPORT_SYMBOL(kgsl_snapshot_get_object);
370
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700371/*
372 * kgsl_snapshot_dump_regs - helper function to dump device registers
373 * @device - the device to dump registers from
374 * @snapshot - pointer to the start of the region of memory for the snapshot
375 * @remain - a pointer to the number of bytes remaining in the snapshot
376 * @priv - A pointer to the kgsl_snapshot_registers data
377 *
378 * Given an array of register ranges pairs (start,end [inclusive]), dump the
379 * registers into a snapshot register section. The snapshot region stores a
380 * part of dwords for each register - the word address of the register, and
381 * the value.
382 */
383int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot,
384 int remain, void *priv)
385{
386 struct kgsl_snapshot_regs *header = snapshot;
387 struct kgsl_snapshot_registers *regs = priv;
388 unsigned int *data = snapshot + sizeof(*header);
389 int count = 0, i, j;
390
391 /* Figure out how many registers we are going to dump */
392
393 for (i = 0; i < regs->count; i++) {
394 int start = regs->regs[i * 2];
395 int end = regs->regs[i * 2 + 1];
396
397 count += (end - start + 1);
398 }
399
400 if (remain < (count * 8) + sizeof(*header)) {
401 SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
402 return 0;
403 }
404
405 for (i = 0; i < regs->count; i++) {
406 unsigned int start = regs->regs[i * 2];
407 unsigned int end = regs->regs[i * 2 + 1];
408
409 for (j = start; j <= end; j++) {
410 unsigned int val;
411
412 kgsl_regread(device, j, &val);
413 *data++ = j;
414 *data++ = val;
415 }
416 }
417
418 header->count = count;
419
420 /* Return the size of the section */
421 return (count * 8) + sizeof(*header);
422}
423EXPORT_SYMBOL(kgsl_snapshot_dump_regs);
424
Jordan Crouse0c2761a2012-02-01 22:11:12 -0700425void *kgsl_snapshot_indexed_registers(struct kgsl_device *device,
426 void *snapshot, int *remain,
427 unsigned int index, unsigned int data, unsigned int start,
428 unsigned int count)
429{
430 struct kgsl_snapshot_indexed_registers iregs;
431 iregs.index = index;
432 iregs.data = data;
433 iregs.start = start;
434 iregs.count = count;
435
436 return kgsl_snapshot_add_section(device,
437 KGSL_SNAPSHOT_SECTION_INDEXED_REGS, snapshot,
438 remain, kgsl_snapshot_dump_indexed_regs, &iregs);
439}
440EXPORT_SYMBOL(kgsl_snapshot_indexed_registers);
441
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700442/*
443 * kgsl_snapshot - construct a device snapshot
444 * @device - device to snapshot
445 * @hang - set to 1 if the snapshot was triggered following a hnag
446 * Given a device, construct a binary snapshot dump of the current device state
447 * and store it in the device snapshot memory.
448 */
449int kgsl_device_snapshot(struct kgsl_device *device, int hang)
450{
451 struct kgsl_snapshot_header *header = device->snapshot;
452 int remain = device->snapshot_maxsize - sizeof(*header);
453 void *snapshot;
454
455 /*
456 * The first hang is always the one we are interested in. To
457 * avoid a subsequent hang blowing away the first, the snapshot
458 * is frozen until it is dumped via sysfs.
459 *
460 * Note that triggered snapshots are always taken regardless
461 * of the state and never frozen.
462 */
463
464 if (hang && device->snapshot_frozen == 1)
465 return 0;
466
467 if (device->snapshot == NULL) {
468 KGSL_DRV_ERR(device,
469 "snapshot: No snapshot memory available\n");
470 return -ENOMEM;
471 }
472
473 if (remain < sizeof(*header)) {
474 KGSL_DRV_ERR(device,
475 "snapshot: Not enough memory for the header\n");
476 return -ENOMEM;
477 }
478
479 header->magic = SNAPSHOT_MAGIC;
480
481 header->gpuid = kgsl_gpuid(device);
482
483 /* Get a pointer to the first section (right after the header) */
484 snapshot = ((void *) device->snapshot) + sizeof(*header);
485
486 /* Build the Linux specific header */
487 snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
488 snapshot, &remain, snapshot_os, (void *) hang);
489
490 /* Get the device specific sections */
491 if (device->ftbl->snapshot)
492 snapshot = device->ftbl->snapshot(device, snapshot, &remain,
493 hang);
494
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700495 device->snapshot_timestamp = get_seconds();
496 device->snapshot_size = (int) (snapshot - device->snapshot);
497
498 /* Freeze the snapshot on a hang until it gets read */
499 device->snapshot_frozen = (hang) ? 1 : 0;
500
Jeremy Gebben9d15ae42012-02-29 16:50:27 -0700501 /* log buffer info to aid in ramdump recovery */
502 KGSL_DRV_ERR(device, "snapshot created at va %p pa %lx size %d\n",
503 device->snapshot, __pa(device->snapshot),
504 device->snapshot_size);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700505 return 0;
506}
507EXPORT_SYMBOL(kgsl_device_snapshot);
508
509/* An attribute for showing snapshot details */
510struct kgsl_snapshot_attribute {
511 struct attribute attr;
512 ssize_t (*show)(struct kgsl_device *device, char *buf);
513 ssize_t (*store)(struct kgsl_device *device, const char *buf,
514 size_t count);
515};
516
517#define to_snapshot_attr(a) \
518container_of(a, struct kgsl_snapshot_attribute, attr)
519
520#define kobj_to_device(a) \
521container_of(a, struct kgsl_device, snapshot_kobj)
522
523/* Dump the sysfs binary data to the user */
524static ssize_t snapshot_show(struct file *filep, struct kobject *kobj,
525 struct bin_attribute *attr, char *buf, loff_t off,
526 size_t count)
527{
528 struct kgsl_device *device = kobj_to_device(kobj);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600529 struct kgsl_snapshot_object *obj, *tmp;
530 unsigned int size, src, dst = 0;
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700531
532 if (device == NULL)
533 return 0;
534
535 /* Return nothing if we haven't taken a snapshot yet */
536 if (device->snapshot_timestamp == 0)
537 return 0;
538
539 /* Get the mutex to keep things from changing while we are dumping */
540 mutex_lock(&device->mutex);
541
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600542 if (off < device->snapshot_size) {
543 size = count < (device->snapshot_size - off) ?
544 count : device->snapshot_size - off;
545
546 memcpy(buf, device->snapshot + off, size);
547
548 count -= size;
549 dst += size;
550 }
551
552 if (count == 0)
553 goto done;
554
555 src = device->snapshot_size;
556
557 list_for_each_entry(obj, &device->snapshot_obj_list, node) {
558
559 int objsize = GPU_OBJ_SECTION_SIZE(obj);
560 int offset;
561
562 /* If the offset is beyond this object, then move on */
563
564 if (off >= (src + objsize)) {
565 src += objsize;
566 continue;
567 }
568
569 /* Adjust the offset to be relative to the object */
570 offset = (off >= src) ? (off - src) : 0;
571
572 size = kgsl_snapshot_dump_object(device, obj, buf + dst,
573 offset, count);
574
575 count -= size;
576 dst += size;
577
578 if (count == 0)
579 goto done;
580
581 /* Move on to the next object - update src accordingly */
582 src += objsize;
583 }
584
585 /* Add the end section */
586
587 if (off < (src + sizeof(struct kgsl_snapshot_section_header))) {
588 if (count >= sizeof(struct kgsl_snapshot_section_header)) {
589 struct kgsl_snapshot_section_header *head =
590 (void *) (buf + dst);
591
592 head->magic = SNAPSHOT_SECTION_MAGIC;
593 head->id = KGSL_SNAPSHOT_SECTION_END;
594 head->size = sizeof(*head);
595
596 dst += sizeof(*head);
597 } else {
598 goto done;
599 }
600 }
601
602 /* Release the buffers and unfreeze the snapshot */
603
604 list_for_each_entry_safe(obj, tmp, &device->snapshot_obj_list, node)
605 kgsl_snapshot_put_object(device, obj);
606
607 if (device->snapshot_frozen)
608 KGSL_DRV_ERR(device, "Snapshot objects released\n");
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700609
610 device->snapshot_frozen = 0;
611
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600612done:
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700613 mutex_unlock(&device->mutex);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600614
615 return dst;
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700616}
617
618/* Show the timestamp of the last collected snapshot */
619static ssize_t timestamp_show(struct kgsl_device *device, char *buf)
620{
621 return snprintf(buf, PAGE_SIZE, "%x\n", device->snapshot_timestamp);
622}
623
624/* manually trigger a new snapshot to be collected */
625static ssize_t trigger_store(struct kgsl_device *device, const char *buf,
626 size_t count)
627{
628 if (device && count > 0) {
629 mutex_lock(&device->mutex);
630 kgsl_device_snapshot(device, 0);
631 mutex_unlock(&device->mutex);
632 }
633
634 return count;
635}
636
637static struct bin_attribute snapshot_attr = {
638 .attr.name = "dump",
639 .attr.mode = 0444,
640 .size = 0,
641 .read = snapshot_show
642};
643
644#define SNAPSHOT_ATTR(_name, _mode, _show, _store) \
645struct kgsl_snapshot_attribute attr_##_name = { \
646 .attr = { .name = __stringify(_name), .mode = _mode }, \
647 .show = _show, \
648 .store = _store, \
649}
650
651SNAPSHOT_ATTR(trigger, 0600, NULL, trigger_store);
652SNAPSHOT_ATTR(timestamp, 0444, timestamp_show, NULL);
653
654static void snapshot_sysfs_release(struct kobject *kobj)
655{
656}
657
658static ssize_t snapshot_sysfs_show(struct kobject *kobj,
659 struct attribute *attr, char *buf)
660{
661 struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
662 struct kgsl_device *device = kobj_to_device(kobj);
663 ssize_t ret;
664
665 if (device && pattr->show)
666 ret = pattr->show(device, buf);
667 else
668 ret = -EIO;
669
670 return ret;
671}
672
673static ssize_t snapshot_sysfs_store(struct kobject *kobj,
674 struct attribute *attr, const char *buf, size_t count)
675{
676 struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
677 struct kgsl_device *device = kobj_to_device(kobj);
678 ssize_t ret;
679
680 if (device && pattr->store)
681 ret = pattr->store(device, buf, count);
682 else
683 ret = -EIO;
684
685 return ret;
686}
687
688static const struct sysfs_ops snapshot_sysfs_ops = {
689 .show = snapshot_sysfs_show,
690 .store = snapshot_sysfs_store,
691};
692
693static struct kobj_type ktype_snapshot = {
694 .sysfs_ops = &snapshot_sysfs_ops,
695 .default_attrs = NULL,
696 .release = snapshot_sysfs_release,
697};
698
699/* kgsl_device_snapshot_init - Add resources for the device GPU snapshot
700 * @device - The device to initalize
701 *
702 * Allocate memory for a GPU snapshot for the specified device,
703 * and create the sysfs files to manage it
704 */
705
706int kgsl_device_snapshot_init(struct kgsl_device *device)
707{
708 int ret;
709
710 if (device->snapshot == NULL)
Jeremy Gebben9d15ae42012-02-29 16:50:27 -0700711 device->snapshot = kzalloc(KGSL_SNAPSHOT_MEMSIZE, GFP_KERNEL);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700712
713 if (device->snapshot == NULL)
714 return -ENOMEM;
715
716 device->snapshot_maxsize = KGSL_SNAPSHOT_MEMSIZE;
717 device->snapshot_timestamp = 0;
718
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600719 INIT_LIST_HEAD(&device->snapshot_obj_list);
720
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700721 ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot,
722 &device->dev->kobj, "snapshot");
723 if (ret)
724 goto done;
725
726 ret = sysfs_create_bin_file(&device->snapshot_kobj, &snapshot_attr);
727 if (ret)
728 goto done;
729
730 ret = sysfs_create_file(&device->snapshot_kobj, &attr_trigger.attr);
731 if (ret)
732 goto done;
733
734 ret = sysfs_create_file(&device->snapshot_kobj, &attr_timestamp.attr);
735
736done:
737 return ret;
738}
739EXPORT_SYMBOL(kgsl_device_snapshot_init);
740
741/* kgsl_device_snapshot_close - Take down snapshot memory for a device
742 * @device - Pointer to the kgsl_device
743 *
744 * Remove the sysfs files and free the memory allocated for the GPU
745 * snapshot
746 */
747
748void kgsl_device_snapshot_close(struct kgsl_device *device)
749{
750 sysfs_remove_bin_file(&device->snapshot_kobj, &snapshot_attr);
751 sysfs_remove_file(&device->snapshot_kobj, &attr_trigger.attr);
752 sysfs_remove_file(&device->snapshot_kobj, &attr_timestamp.attr);
753
754 kobject_put(&device->snapshot_kobj);
755
Jeremy Gebben9d15ae42012-02-29 16:50:27 -0700756 kfree(device->snapshot);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700757
758 device->snapshot = NULL;
759 device->snapshot_maxsize = 0;
760 device->snapshot_timestamp = 0;
761}
762EXPORT_SYMBOL(kgsl_device_snapshot_close);