blob: 553dc603fc5085756c4c07b9ae1059838044c196 [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
337 obj = kzalloc(sizeof(*obj), GFP_KERNEL);
338
339 if (obj == NULL) {
340 KGSL_DRV_ERR(device, "Unable to allocate memory\n");
341 return 0;
342 }
343
344 /* Ref count the mem entry */
345 kgsl_mem_entry_get(entry);
346
347 obj->type = type;
348 obj->entry = entry;
349 obj->gpuaddr = gpuaddr;
350 obj->ptbase = ptbase;
351 obj->size = size;
Jordan Crouse233b2092012-04-18 09:31:09 -0600352 obj->offset = offset;
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600353
354 list_add(&obj->node, &device->snapshot_obj_list);
355
356 /*
357 * Return the size of the entire mem entry that was frozen - this gets
358 * used for tracking how much memory is frozen for a hang. Also, mark
359 * the memory entry as frozen. If the entry was already marked as
360 * frozen, then another buffer already got to it. In that case, return
361 * 0 so it doesn't get counted twice
362 */
363
364 if (entry->flags & KGSL_MEM_ENTRY_FROZEN)
365 return 0;
366
367 entry->flags |= KGSL_MEM_ENTRY_FROZEN;
368
369 return entry->memdesc.size;
370}
371EXPORT_SYMBOL(kgsl_snapshot_get_object);
372
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700373/*
374 * kgsl_snapshot_dump_regs - helper function to dump device registers
375 * @device - the device to dump registers from
376 * @snapshot - pointer to the start of the region of memory for the snapshot
377 * @remain - a pointer to the number of bytes remaining in the snapshot
378 * @priv - A pointer to the kgsl_snapshot_registers data
379 *
380 * Given an array of register ranges pairs (start,end [inclusive]), dump the
381 * registers into a snapshot register section. The snapshot region stores a
382 * part of dwords for each register - the word address of the register, and
383 * the value.
384 */
385int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot,
386 int remain, void *priv)
387{
388 struct kgsl_snapshot_regs *header = snapshot;
389 struct kgsl_snapshot_registers *regs = priv;
390 unsigned int *data = snapshot + sizeof(*header);
391 int count = 0, i, j;
392
393 /* Figure out how many registers we are going to dump */
394
395 for (i = 0; i < regs->count; i++) {
396 int start = regs->regs[i * 2];
397 int end = regs->regs[i * 2 + 1];
398
399 count += (end - start + 1);
400 }
401
402 if (remain < (count * 8) + sizeof(*header)) {
403 SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
404 return 0;
405 }
406
407 for (i = 0; i < regs->count; i++) {
408 unsigned int start = regs->regs[i * 2];
409 unsigned int end = regs->regs[i * 2 + 1];
410
411 for (j = start; j <= end; j++) {
412 unsigned int val;
413
414 kgsl_regread(device, j, &val);
415 *data++ = j;
416 *data++ = val;
417 }
418 }
419
420 header->count = count;
421
422 /* Return the size of the section */
423 return (count * 8) + sizeof(*header);
424}
425EXPORT_SYMBOL(kgsl_snapshot_dump_regs);
426
Jordan Crouse0c2761a2012-02-01 22:11:12 -0700427void *kgsl_snapshot_indexed_registers(struct kgsl_device *device,
428 void *snapshot, int *remain,
429 unsigned int index, unsigned int data, unsigned int start,
430 unsigned int count)
431{
432 struct kgsl_snapshot_indexed_registers iregs;
433 iregs.index = index;
434 iregs.data = data;
435 iregs.start = start;
436 iregs.count = count;
437
438 return kgsl_snapshot_add_section(device,
439 KGSL_SNAPSHOT_SECTION_INDEXED_REGS, snapshot,
440 remain, kgsl_snapshot_dump_indexed_regs, &iregs);
441}
442EXPORT_SYMBOL(kgsl_snapshot_indexed_registers);
443
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700444/*
445 * kgsl_snapshot - construct a device snapshot
446 * @device - device to snapshot
447 * @hang - set to 1 if the snapshot was triggered following a hnag
448 * Given a device, construct a binary snapshot dump of the current device state
449 * and store it in the device snapshot memory.
450 */
451int kgsl_device_snapshot(struct kgsl_device *device, int hang)
452{
453 struct kgsl_snapshot_header *header = device->snapshot;
454 int remain = device->snapshot_maxsize - sizeof(*header);
455 void *snapshot;
456
457 /*
458 * The first hang is always the one we are interested in. To
459 * avoid a subsequent hang blowing away the first, the snapshot
460 * is frozen until it is dumped via sysfs.
461 *
462 * Note that triggered snapshots are always taken regardless
463 * of the state and never frozen.
464 */
465
466 if (hang && device->snapshot_frozen == 1)
467 return 0;
468
469 if (device->snapshot == NULL) {
470 KGSL_DRV_ERR(device,
471 "snapshot: No snapshot memory available\n");
472 return -ENOMEM;
473 }
474
475 if (remain < sizeof(*header)) {
476 KGSL_DRV_ERR(device,
477 "snapshot: Not enough memory for the header\n");
478 return -ENOMEM;
479 }
480
481 header->magic = SNAPSHOT_MAGIC;
482
483 header->gpuid = kgsl_gpuid(device);
484
485 /* Get a pointer to the first section (right after the header) */
486 snapshot = ((void *) device->snapshot) + sizeof(*header);
487
488 /* Build the Linux specific header */
489 snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
490 snapshot, &remain, snapshot_os, (void *) hang);
491
492 /* Get the device specific sections */
493 if (device->ftbl->snapshot)
494 snapshot = device->ftbl->snapshot(device, snapshot, &remain,
495 hang);
496
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700497 device->snapshot_timestamp = get_seconds();
498 device->snapshot_size = (int) (snapshot - device->snapshot);
499
500 /* Freeze the snapshot on a hang until it gets read */
501 device->snapshot_frozen = (hang) ? 1 : 0;
502
Jeremy Gebben9d15ae42012-02-29 16:50:27 -0700503 /* log buffer info to aid in ramdump recovery */
504 KGSL_DRV_ERR(device, "snapshot created at va %p pa %lx size %d\n",
505 device->snapshot, __pa(device->snapshot),
506 device->snapshot_size);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700507 return 0;
508}
509EXPORT_SYMBOL(kgsl_device_snapshot);
510
511/* An attribute for showing snapshot details */
512struct kgsl_snapshot_attribute {
513 struct attribute attr;
514 ssize_t (*show)(struct kgsl_device *device, char *buf);
515 ssize_t (*store)(struct kgsl_device *device, const char *buf,
516 size_t count);
517};
518
519#define to_snapshot_attr(a) \
520container_of(a, struct kgsl_snapshot_attribute, attr)
521
522#define kobj_to_device(a) \
523container_of(a, struct kgsl_device, snapshot_kobj)
524
525/* Dump the sysfs binary data to the user */
526static ssize_t snapshot_show(struct file *filep, struct kobject *kobj,
527 struct bin_attribute *attr, char *buf, loff_t off,
528 size_t count)
529{
530 struct kgsl_device *device = kobj_to_device(kobj);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600531 struct kgsl_snapshot_object *obj, *tmp;
532 unsigned int size, src, dst = 0;
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700533
534 if (device == NULL)
535 return 0;
536
537 /* Return nothing if we haven't taken a snapshot yet */
538 if (device->snapshot_timestamp == 0)
539 return 0;
540
541 /* Get the mutex to keep things from changing while we are dumping */
542 mutex_lock(&device->mutex);
543
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600544 if (off < device->snapshot_size) {
545 size = count < (device->snapshot_size - off) ?
546 count : device->snapshot_size - off;
547
548 memcpy(buf, device->snapshot + off, size);
549
550 count -= size;
551 dst += size;
552 }
553
554 if (count == 0)
555 goto done;
556
557 src = device->snapshot_size;
558
559 list_for_each_entry(obj, &device->snapshot_obj_list, node) {
560
561 int objsize = GPU_OBJ_SECTION_SIZE(obj);
562 int offset;
563
564 /* If the offset is beyond this object, then move on */
565
566 if (off >= (src + objsize)) {
567 src += objsize;
568 continue;
569 }
570
571 /* Adjust the offset to be relative to the object */
572 offset = (off >= src) ? (off - src) : 0;
573
574 size = kgsl_snapshot_dump_object(device, obj, buf + dst,
575 offset, count);
576
577 count -= size;
578 dst += size;
579
580 if (count == 0)
581 goto done;
582
583 /* Move on to the next object - update src accordingly */
584 src += objsize;
585 }
586
587 /* Add the end section */
588
589 if (off < (src + sizeof(struct kgsl_snapshot_section_header))) {
590 if (count >= sizeof(struct kgsl_snapshot_section_header)) {
591 struct kgsl_snapshot_section_header *head =
592 (void *) (buf + dst);
593
594 head->magic = SNAPSHOT_SECTION_MAGIC;
595 head->id = KGSL_SNAPSHOT_SECTION_END;
596 head->size = sizeof(*head);
597
598 dst += sizeof(*head);
599 } else {
600 goto done;
601 }
602 }
603
604 /* Release the buffers and unfreeze the snapshot */
605
606 list_for_each_entry_safe(obj, tmp, &device->snapshot_obj_list, node)
607 kgsl_snapshot_put_object(device, obj);
608
609 if (device->snapshot_frozen)
610 KGSL_DRV_ERR(device, "Snapshot objects released\n");
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700611
612 device->snapshot_frozen = 0;
613
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600614done:
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700615 mutex_unlock(&device->mutex);
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600616
617 return dst;
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700618}
619
620/* Show the timestamp of the last collected snapshot */
621static ssize_t timestamp_show(struct kgsl_device *device, char *buf)
622{
623 return snprintf(buf, PAGE_SIZE, "%x\n", device->snapshot_timestamp);
624}
625
626/* manually trigger a new snapshot to be collected */
627static ssize_t trigger_store(struct kgsl_device *device, const char *buf,
628 size_t count)
629{
630 if (device && count > 0) {
631 mutex_lock(&device->mutex);
632 kgsl_device_snapshot(device, 0);
633 mutex_unlock(&device->mutex);
634 }
635
636 return count;
637}
638
639static struct bin_attribute snapshot_attr = {
640 .attr.name = "dump",
641 .attr.mode = 0444,
642 .size = 0,
643 .read = snapshot_show
644};
645
646#define SNAPSHOT_ATTR(_name, _mode, _show, _store) \
647struct kgsl_snapshot_attribute attr_##_name = { \
648 .attr = { .name = __stringify(_name), .mode = _mode }, \
649 .show = _show, \
650 .store = _store, \
651}
652
653SNAPSHOT_ATTR(trigger, 0600, NULL, trigger_store);
654SNAPSHOT_ATTR(timestamp, 0444, timestamp_show, NULL);
655
656static void snapshot_sysfs_release(struct kobject *kobj)
657{
658}
659
660static ssize_t snapshot_sysfs_show(struct kobject *kobj,
661 struct attribute *attr, char *buf)
662{
663 struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
664 struct kgsl_device *device = kobj_to_device(kobj);
665 ssize_t ret;
666
667 if (device && pattr->show)
668 ret = pattr->show(device, buf);
669 else
670 ret = -EIO;
671
672 return ret;
673}
674
675static ssize_t snapshot_sysfs_store(struct kobject *kobj,
676 struct attribute *attr, const char *buf, size_t count)
677{
678 struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
679 struct kgsl_device *device = kobj_to_device(kobj);
680 ssize_t ret;
681
682 if (device && pattr->store)
683 ret = pattr->store(device, buf, count);
684 else
685 ret = -EIO;
686
687 return ret;
688}
689
690static const struct sysfs_ops snapshot_sysfs_ops = {
691 .show = snapshot_sysfs_show,
692 .store = snapshot_sysfs_store,
693};
694
695static struct kobj_type ktype_snapshot = {
696 .sysfs_ops = &snapshot_sysfs_ops,
697 .default_attrs = NULL,
698 .release = snapshot_sysfs_release,
699};
700
701/* kgsl_device_snapshot_init - Add resources for the device GPU snapshot
702 * @device - The device to initalize
703 *
704 * Allocate memory for a GPU snapshot for the specified device,
705 * and create the sysfs files to manage it
706 */
707
708int kgsl_device_snapshot_init(struct kgsl_device *device)
709{
710 int ret;
711
712 if (device->snapshot == NULL)
Jeremy Gebben9d15ae42012-02-29 16:50:27 -0700713 device->snapshot = kzalloc(KGSL_SNAPSHOT_MEMSIZE, GFP_KERNEL);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700714
715 if (device->snapshot == NULL)
716 return -ENOMEM;
717
718 device->snapshot_maxsize = KGSL_SNAPSHOT_MEMSIZE;
719 device->snapshot_timestamp = 0;
720
Jordan Crouse9610b6b2012-03-16 14:53:42 -0600721 INIT_LIST_HEAD(&device->snapshot_obj_list);
722
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700723 ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot,
724 &device->dev->kobj, "snapshot");
725 if (ret)
726 goto done;
727
728 ret = sysfs_create_bin_file(&device->snapshot_kobj, &snapshot_attr);
729 if (ret)
730 goto done;
731
732 ret = sysfs_create_file(&device->snapshot_kobj, &attr_trigger.attr);
733 if (ret)
734 goto done;
735
736 ret = sysfs_create_file(&device->snapshot_kobj, &attr_timestamp.attr);
737
738done:
739 return ret;
740}
741EXPORT_SYMBOL(kgsl_device_snapshot_init);
742
743/* kgsl_device_snapshot_close - Take down snapshot memory for a device
744 * @device - Pointer to the kgsl_device
745 *
746 * Remove the sysfs files and free the memory allocated for the GPU
747 * snapshot
748 */
749
750void kgsl_device_snapshot_close(struct kgsl_device *device)
751{
752 sysfs_remove_bin_file(&device->snapshot_kobj, &snapshot_attr);
753 sysfs_remove_file(&device->snapshot_kobj, &attr_trigger.attr);
754 sysfs_remove_file(&device->snapshot_kobj, &attr_timestamp.attr);
755
756 kobject_put(&device->snapshot_kobj);
757
Jeremy Gebben9d15ae42012-02-29 16:50:27 -0700758 kfree(device->snapshot);
Jordan Crouse156cfbc2012-01-24 09:32:04 -0700759
760 device->snapshot = NULL;
761 device->snapshot_maxsize = 0;
762 device->snapshot_timestamp = 0;
763}
764EXPORT_SYMBOL(kgsl_device_snapshot_close);