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