123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883 |
- /* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/export.h>
- #include <linux/time.h>
- #include <linux/sysfs.h>
- #include <linux/utsname.h>
- #include <linux/sched.h>
- #include <linux/idr.h>
- #include "kgsl.h"
- #include "kgsl_log.h"
- #include "kgsl_device.h"
- #include "kgsl_sharedmem.h"
- #include "kgsl_snapshot.h"
- /* Placeholder for the list of memory objects frozen after a hang */
- struct kgsl_snapshot_object {
- unsigned int gpuaddr;
- phys_addr_t ptbase;
- unsigned int size;
- unsigned int offset;
- int type;
- struct kgsl_mem_entry *entry;
- struct list_head node;
- };
- struct snapshot_obj_itr {
- void *buf; /* Buffer pointer to write to */
- int pos; /* Current position in the sequence */
- loff_t offset; /* file offset to start writing from */
- size_t remain; /* Bytes remaining in buffer */
- size_t write; /* Bytes written so far */
- };
- static void obj_itr_init(struct snapshot_obj_itr *itr, void *buf,
- loff_t offset, size_t remain)
- {
- itr->buf = buf;
- itr->offset = offset;
- itr->remain = remain;
- itr->pos = 0;
- itr->write = 0;
- }
- static int obj_itr_out(struct snapshot_obj_itr *itr, void *src, int size)
- {
- if (itr->remain == 0)
- return 0;
- if ((itr->pos + size) <= itr->offset)
- goto done;
- /* Handle the case that offset is in the middle of the buffer */
- if (itr->offset > itr->pos) {
- src += (itr->offset - itr->pos);
- size -= (itr->offset - itr->pos);
- /* Advance pos to the offset start */
- itr->pos = itr->offset;
- }
- if (size > itr->remain)
- size = itr->remain;
- memcpy(itr->buf, src, size);
- itr->buf += size;
- itr->write += size;
- itr->remain -= size;
- done:
- itr->pos += size;
- return size;
- }
- /* idr_for_each function to count the number of contexts */
- static int snapshot_context_count(int id, void *ptr, void *data)
- {
- int *count = data;
- *count = *count + 1;
- return 0;
- }
- /*
- * To simplify the iterator loop use a global pointer instead of trying
- * to pass around double star references to the snapshot data
- */
- static void *_ctxtptr;
- static int snapshot_context_info(int id, void *ptr, void *data)
- {
- struct kgsl_snapshot_linux_context *header = _ctxtptr;
- struct kgsl_context *context = ptr;
- struct kgsl_device *device;
- if (context)
- device = context->device;
- else
- device = (struct kgsl_device *)data;
- header->id = id;
- /* Future-proof for per-context timestamps - for now, just
- * return the global timestamp for all contexts
- */
- header->timestamp_queued = kgsl_readtimestamp(device, context,
- KGSL_TIMESTAMP_QUEUED);
- header->timestamp_retired = kgsl_readtimestamp(device, context,
- KGSL_TIMESTAMP_RETIRED);
- _ctxtptr += sizeof(struct kgsl_snapshot_linux_context);
- return 0;
- }
- /* Snapshot the Linux specific information */
- static int snapshot_os(struct kgsl_device *device,
- void *snapshot, int remain, void *priv)
- {
- struct kgsl_snapshot_linux *header = snapshot;
- struct kgsl_pwrctrl *pwr = &device->pwrctrl;
- struct task_struct *task;
- pid_t pid;
- int hang = (int) priv;
- int ctxtcount = 0;
- int size = sizeof(*header);
- phys_addr_t temp_ptbase;
- /* Figure out how many active contexts there are - these will
- * be appended on the end of the structure */
- read_lock(&device->context_lock);
- idr_for_each(&device->context_idr, snapshot_context_count, &ctxtcount);
- read_unlock(&device->context_lock);
- /* Increment ctxcount for the global memstore */
- ctxtcount++;
- size += ctxtcount * sizeof(struct kgsl_snapshot_linux_context);
- /* Make sure there is enough room for the data */
- if (remain < size) {
- SNAPSHOT_ERR_NOMEM(device, "OS");
- return 0;
- }
- memset(header, 0, sizeof(*header));
- header->osid = KGSL_SNAPSHOT_OS_LINUX;
- header->state = hang ? SNAPSHOT_STATE_HUNG : SNAPSHOT_STATE_RUNNING;
- /* Get the kernel build information */
- strlcpy(header->release, utsname()->release, sizeof(header->release));
- strlcpy(header->version, utsname()->version, sizeof(header->version));
- /* Get the Unix time for the timestamp */
- header->seconds = get_seconds();
- /* Remember the power information */
- header->power_flags = pwr->power_flags;
- header->power_level = pwr->active_pwrlevel;
- header->power_interval_timeout = pwr->interval_timeout;
- header->grpclk = kgsl_get_clkrate(pwr->grp_clks[0]);
- header->busclk = kgsl_get_clkrate(pwr->ebi1_clk);
- /* Save the last active context */
- kgsl_sharedmem_readl(&device->memstore, &header->current_context,
- KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context));
- /* Get the current PT base */
- temp_ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
- /* Truncate to 32 bits in case LPAE is used */
- header->ptbase = (__u32)temp_ptbase;
- /* And the PID for the task leader */
- pid = header->pid = kgsl_mmu_get_ptname_from_ptbase(&device->mmu,
- temp_ptbase);
- task = find_task_by_vpid(pid);
- if (task)
- get_task_comm(header->comm, task);
- header->ctxtcount = ctxtcount;
- _ctxtptr = snapshot + sizeof(*header);
- /* append information for the global context */
- snapshot_context_info(KGSL_MEMSTORE_GLOBAL, NULL, device);
- /* append information for each context */
- read_lock(&device->context_lock);
- idr_for_each(&device->context_idr, snapshot_context_info, NULL);
- read_unlock(&device->context_lock);
- /* Return the size of the data segment */
- return size;
- }
- /*
- * kgsl_snapshot_dump_indexed_regs - helper function to dump indexed registers
- * @device - the device to dump registers from
- * @snapshot - pointer to the start of the region of memory for the snapshot
- * @remain - a pointer to the number of bytes remaining in the snapshot
- * @priv - A pointer to the kgsl_snapshot_indexed_registers data
- *
- * Given a indexed register cmd/data pair and a count, dump each indexed
- * register
- */
- static int kgsl_snapshot_dump_indexed_regs(struct kgsl_device *device,
- void *snapshot, int remain, void *priv)
- {
- struct kgsl_snapshot_indexed_registers *iregs = priv;
- struct kgsl_snapshot_indexed_regs *header = snapshot;
- unsigned int *data = snapshot + sizeof(*header);
- int i;
- if (remain < (iregs->count * 4) + sizeof(*header)) {
- SNAPSHOT_ERR_NOMEM(device, "INDEXED REGS");
- return 0;
- }
- header->index_reg = iregs->index;
- header->data_reg = iregs->data;
- header->count = iregs->count;
- header->start = iregs->start;
- for (i = 0; i < iregs->count; i++) {
- kgsl_regwrite(device, iregs->index, iregs->start + i);
- kgsl_regread(device, iregs->data, &data[i]);
- }
- return (iregs->count * 4) + sizeof(*header);
- }
- #define GPU_OBJ_HEADER_SZ \
- (sizeof(struct kgsl_snapshot_section_header) + \
- sizeof(struct kgsl_snapshot_gpu_object))
- static int kgsl_snapshot_dump_object(struct kgsl_device *device,
- struct kgsl_snapshot_object *obj, struct snapshot_obj_itr *itr)
- {
- struct kgsl_snapshot_section_header sect;
- struct kgsl_snapshot_gpu_object header;
- int ret;
- sect.magic = SNAPSHOT_SECTION_MAGIC;
- sect.id = KGSL_SNAPSHOT_SECTION_GPU_OBJECT;
- /*
- * Header size is in dwords, object size is in bytes -
- * round up if the object size isn't dword aligned
- */
- sect.size = GPU_OBJ_HEADER_SZ + ALIGN(obj->size, 4);
- ret = obj_itr_out(itr, §, sizeof(sect));
- if (ret == 0)
- return 0;
- header.size = ALIGN(obj->size, 4) >> 2;
- header.gpuaddr = obj->gpuaddr;
- header.ptbase = (__u32)obj->ptbase;
- header.type = obj->type;
- ret = obj_itr_out(itr, &header, sizeof(header));
- if (ret == 0)
- return 0;
- ret = obj_itr_out(itr, obj->entry->memdesc.hostptr + obj->offset,
- obj->size);
- if (ret == 0)
- return 0;
- /* Pad the end to a dword boundary if we need to */
- if (obj->size % 4) {
- unsigned int dummy = 0;
- ret = obj_itr_out(itr, &dummy, obj->size % 4);
- }
- return ret;
- }
- static void kgsl_snapshot_put_object(struct kgsl_device *device,
- struct kgsl_snapshot_object *obj)
- {
- list_del(&obj->node);
- obj->entry->memdesc.priv &= ~KGSL_MEMDESC_FROZEN;
- kgsl_memdesc_unmap(&obj->entry->memdesc);
- kgsl_mem_entry_put(obj->entry);
- kfree(obj);
- }
- /* ksgl_snapshot_have_object - Return 1 if the object has been processed
- *@device - the device that is being snapshotted
- * @ptbase - the pagetable base of the object to freeze
- * @gpuaddr - The gpu address of the object to freeze
- * @size - the size of the object (may not always be the size of the region)
- *
- * Return 1 if the object is already in the list - this can save us from
- * having to parse the sme thing over again.
- */
- int kgsl_snapshot_have_object(struct kgsl_device *device, phys_addr_t ptbase,
- unsigned int gpuaddr, unsigned int size)
- {
- struct kgsl_snapshot_object *obj;
- list_for_each_entry(obj, &device->snapshot_obj_list, node) {
- if (obj->ptbase != ptbase)
- continue;
- if ((gpuaddr >= obj->gpuaddr) &&
- ((gpuaddr + size) <= (obj->gpuaddr + obj->size)))
- return 1;
- }
- return 0;
- }
- EXPORT_SYMBOL(kgsl_snapshot_have_object);
- /* kgsl_snapshot_get_object - Mark a GPU buffer to be frozen
- * @device - the device that is being snapshotted
- * @ptbase - the pagetable base of the object to freeze
- * @gpuaddr - The gpu address of the object to freeze
- * @size - the size of the object (may not always be the size of the region)
- * @type - the type of object being saved (shader, vbo, etc)
- *
- * Mark and freeze a GPU buffer object. This will prevent it from being
- * freed until it can be copied out as part of the snapshot dump. Returns the
- * size of the object being frozen
- */
- int kgsl_snapshot_get_object(struct kgsl_device *device, phys_addr_t ptbase,
- unsigned int gpuaddr, unsigned int size, unsigned int type)
- {
- struct kgsl_mem_entry *entry;
- struct kgsl_snapshot_object *obj;
- int offset;
- int ret = -EINVAL;
- if (!gpuaddr)
- return 0;
- entry = kgsl_get_mem_entry(device, ptbase, gpuaddr, size);
- if (entry == NULL) {
- KGSL_DRV_ERR(device, "Unable to find GPU buffer %8.8X\n",
- gpuaddr);
- return -EINVAL;
- }
- /* We can't freeze external memory, because we don't own it */
- if (entry->memtype != KGSL_MEM_ENTRY_KERNEL) {
- KGSL_DRV_ERR(device,
- "Only internal GPU buffers can be frozen\n");
- goto err_put;
- }
- /*
- * size indicates the number of bytes in the region to save. This might
- * not always be the entire size of the region because some buffers are
- * sub-allocated from a larger region. However, if size 0 was passed
- * thats a flag that the caller wants to capture the entire buffer
- */
- if (size == 0) {
- size = entry->memdesc.size;
- offset = 0;
- /* Adjust the gpuaddr to the start of the object */
- gpuaddr = entry->memdesc.gpuaddr;
- } else {
- offset = gpuaddr - entry->memdesc.gpuaddr;
- }
- if (size + offset > entry->memdesc.size) {
- KGSL_DRV_ERR(device, "Invalid size for GPU buffer %8.8X\n",
- gpuaddr);
- goto err_put;
- }
- /* If the buffer is already on the list, skip it */
- list_for_each_entry(obj, &device->snapshot_obj_list, node) {
- if (obj->gpuaddr == gpuaddr && obj->ptbase == ptbase) {
- /* If the size is different, use the bigger size */
- if (obj->size < size)
- obj->size = size;
- ret = 0;
- goto err_put;
- }
- }
- if (kgsl_memdesc_map(&entry->memdesc) == NULL) {
- KGSL_DRV_ERR(device, "Unable to map GPU buffer %X\n",
- gpuaddr);
- goto err_put;
- }
- obj = kzalloc(sizeof(*obj), GFP_KERNEL);
- if (obj == NULL) {
- KGSL_DRV_ERR(device, "Unable to allocate memory\n");
- goto err_unmap;
- }
- obj->type = type;
- obj->entry = entry;
- obj->gpuaddr = gpuaddr;
- obj->ptbase = ptbase;
- obj->size = size;
- obj->offset = offset;
- list_add(&obj->node, &device->snapshot_obj_list);
- /*
- * Return the size of the entire mem entry that was frozen - this gets
- * used for tracking how much memory is frozen for a hang. Also, mark
- * the memory entry as frozen. If the entry was already marked as
- * frozen, then another buffer already got to it. In that case, return
- * 0 so it doesn't get counted twice
- */
- ret = (entry->memdesc.priv & KGSL_MEMDESC_FROZEN) ? 0
- : entry->memdesc.size;
- entry->memdesc.priv |= KGSL_MEMDESC_FROZEN;
- return ret;
- err_unmap:
- kgsl_memdesc_unmap(&entry->memdesc);
- err_put:
- kgsl_mem_entry_put(entry);
- return ret;
- }
- EXPORT_SYMBOL(kgsl_snapshot_get_object);
- /*
- * kgsl_snapshot_dump_regs - helper function to dump device registers
- * @device - the device to dump registers from
- * @snapshot - pointer to the start of the region of memory for the snapshot
- * @remain - a pointer to the number of bytes remaining in the snapshot
- * @priv - A pointer to the kgsl_snapshot_registers data
- *
- * Given an array of register ranges pairs (start,end [inclusive]), dump the
- * registers into a snapshot register section. The snapshot region stores a
- * part of dwords for each register - the word address of the register, and
- * the value.
- */
- int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot,
- int remain, void *priv)
- {
- struct kgsl_snapshot_registers_list *list = priv;
- struct kgsl_snapshot_regs *header = snapshot;
- struct kgsl_snapshot_registers *regs;
- unsigned int *data = snapshot + sizeof(*header);
- int count = 0, i, j, k;
- /* Figure out how many registers we are going to dump */
- for (i = 0; i < list->count; i++) {
- regs = &(list->registers[i]);
- for (j = 0; j < regs->count; j++) {
- int start = regs->regs[j * 2];
- int end = regs->regs[j * 2 + 1];
- count += (end - start + 1);
- }
- }
- if (remain < (count * 8) + sizeof(*header)) {
- SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
- return 0;
- }
- for (i = 0; i < list->count; i++) {
- regs = &(list->registers[i]);
- for (j = 0; j < regs->count; j++) {
- unsigned int start = regs->regs[j * 2];
- unsigned int end = regs->regs[j * 2 + 1];
- for (k = start; k <= end; k++) {
- unsigned int val;
- kgsl_regread(device, k, &val);
- *data++ = k;
- *data++ = val;
- }
- }
- }
- header->count = count;
- /* Return the size of the section */
- return (count * 8) + sizeof(*header);
- }
- EXPORT_SYMBOL(kgsl_snapshot_dump_regs);
- void *kgsl_snapshot_indexed_registers(struct kgsl_device *device,
- void *snapshot, int *remain,
- unsigned int index, unsigned int data, unsigned int start,
- unsigned int count)
- {
- struct kgsl_snapshot_indexed_registers iregs;
- iregs.index = index;
- iregs.data = data;
- iregs.start = start;
- iregs.count = count;
- return kgsl_snapshot_add_section(device,
- KGSL_SNAPSHOT_SECTION_INDEXED_REGS, snapshot,
- remain, kgsl_snapshot_dump_indexed_regs, &iregs);
- }
- EXPORT_SYMBOL(kgsl_snapshot_indexed_registers);
- /*
- * kgsl_snapshot - construct a device snapshot
- * @device - device to snapshot
- * @hang - set to 1 if the snapshot was triggered following a hnag
- * Given a device, construct a binary snapshot dump of the current device state
- * and store it in the device snapshot memory.
- */
- int kgsl_device_snapshot(struct kgsl_device *device, int hang)
- {
- struct kgsl_snapshot_header *header = device->snapshot;
- int remain = device->snapshot_maxsize - sizeof(*header);
- void *snapshot;
- struct timespec boot;
- int ret = 0;
- /*
- * Bail if failed to get active count for GPU,
- * try again
- */
- if (kgsl_active_count_get(device)) {
- KGSL_DRV_ERR(device, "Failed to get GPU active count");
- return -EINVAL;
- }
- /* increment the hang count (on hang) for good book keeping */
- if (hang)
- device->snapshot_faultcount++;
- /*
- * The first hang is always the one we are interested in. To
- * avoid a subsequent hang blowing away the first, the snapshot
- * is frozen until it is dumped via sysfs.
- *
- * Note that triggered snapshots are always taken regardless
- * of the state and never frozen.
- */
- if (hang && device->snapshot_frozen == 1) {
- ret = 0;
- goto done;
- }
- if (device->snapshot == NULL) {
- KGSL_DRV_ERR(device,
- "snapshot: No snapshot memory available\n");
- ret = -ENOMEM;
- goto done;
- }
- if (remain < sizeof(*header)) {
- KGSL_DRV_ERR(device,
- "snapshot: Not enough memory for the header\n");
- ret = -ENOMEM;
- goto done;
- }
- header->magic = SNAPSHOT_MAGIC;
- header->gpuid = kgsl_gpuid(device, &header->chipid);
- /* Get a pointer to the first section (right after the header) */
- snapshot = ((void *) device->snapshot) + sizeof(*header);
- /* Build the Linux specific header */
- snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
- snapshot, &remain, snapshot_os, (void *) hang);
- /* Get the device specific sections */
- if (device->ftbl->snapshot)
- snapshot = device->ftbl->snapshot(device, snapshot, &remain,
- hang);
- /*
- * The timestamp is the seconds since boot so it is easier to match to
- * the kernel log
- */
- getboottime(&boot);
- device->snapshot_timestamp = get_seconds() - boot.tv_sec;
- device->snapshot_size = (int) (snapshot - device->snapshot);
- /* Freeze the snapshot on a hang until it gets read */
- device->snapshot_frozen = (hang) ? 1 : 0;
- /* log buffer info to aid in ramdump fault tolerance */
- KGSL_DRV_ERR(device, "snapshot created at pa %lx size %d\n",
- __pa(device->snapshot), device->snapshot_size);
- if (hang)
- sysfs_notify(&device->snapshot_kobj, NULL, "timestamp");
- done:
- kgsl_active_count_put(device);
- return ret;
- }
- EXPORT_SYMBOL(kgsl_device_snapshot);
- /* An attribute for showing snapshot details */
- struct kgsl_snapshot_attribute {
- struct attribute attr;
- ssize_t (*show)(struct kgsl_device *device, char *buf);
- ssize_t (*store)(struct kgsl_device *device, const char *buf,
- size_t count);
- };
- #define to_snapshot_attr(a) \
- container_of(a, struct kgsl_snapshot_attribute, attr)
- #define kobj_to_device(a) \
- container_of(a, struct kgsl_device, snapshot_kobj)
- /* Dump the sysfs binary data to the user */
- static ssize_t snapshot_show(struct file *filep, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off,
- size_t count)
- {
- struct kgsl_device *device = kobj_to_device(kobj);
- struct kgsl_snapshot_object *obj, *tmp;
- struct kgsl_snapshot_section_header head;
- struct snapshot_obj_itr itr;
- int ret;
- if (device == NULL)
- return 0;
- /* Return nothing if we haven't taken a snapshot yet */
- if (device->snapshot_timestamp == 0)
- return 0;
- /* Get the mutex to keep things from changing while we are dumping */
- kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
- obj_itr_init(&itr, buf, off, count);
- ret = obj_itr_out(&itr, device->snapshot, device->snapshot_size);
- if (ret == 0)
- goto done;
- list_for_each_entry(obj, &device->snapshot_obj_list, node)
- kgsl_snapshot_dump_object(device, obj, &itr);
- {
- head.magic = SNAPSHOT_SECTION_MAGIC;
- head.id = KGSL_SNAPSHOT_SECTION_END;
- head.size = sizeof(head);
- obj_itr_out(&itr, &head, sizeof(head));
- }
- /*
- * Make sure everything has been written out before destroying things.
- * The best way to confirm this is to go all the way through without
- * writing any bytes - so only release if we get this far and
- * itr->write is 0
- */
- if (itr.write == 0) {
- list_for_each_entry_safe(obj, tmp, &device->snapshot_obj_list,
- node)
- kgsl_snapshot_put_object(device, obj);
- if (device->snapshot_frozen)
- KGSL_DRV_ERR(device, "Snapshot objects released\n");
- device->snapshot_frozen = 0;
- }
- done:
- kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
- return itr.write;
- }
- /* Show the total number of hangs since device boot */
- static ssize_t faultcount_show(struct kgsl_device *device, char *buf)
- {
- return snprintf(buf, PAGE_SIZE, "%d\n", device->snapshot_faultcount);
- }
- /* Reset the total number of hangs since device boot */
- static ssize_t faultcount_store(struct kgsl_device *device, const char *buf,
- size_t count)
- {
- if (device && count > 0)
- device->snapshot_faultcount = 0;
- return count;
- }
- /* Show the timestamp of the last collected snapshot */
- static ssize_t timestamp_show(struct kgsl_device *device, char *buf)
- {
- return snprintf(buf, PAGE_SIZE, "%d\n", device->snapshot_timestamp);
- }
- /* manually trigger a new snapshot to be collected */
- static ssize_t trigger_store(struct kgsl_device *device, const char *buf,
- size_t count)
- {
- if (device && count > 0) {
- kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
- if (!kgsl_active_count_get(device)) {
- kgsl_device_snapshot(device, 0);
- kgsl_active_count_put(device);
- }
- kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
- }
- return count;
- }
- static struct bin_attribute snapshot_attr = {
- .attr.name = "dump",
- .attr.mode = 0444,
- .size = 0,
- .read = snapshot_show
- };
- #define SNAPSHOT_ATTR(_name, _mode, _show, _store) \
- struct kgsl_snapshot_attribute attr_##_name = { \
- .attr = { .name = __stringify(_name), .mode = _mode }, \
- .show = _show, \
- .store = _store, \
- }
- SNAPSHOT_ATTR(trigger, 0600, NULL, trigger_store);
- SNAPSHOT_ATTR(timestamp, 0444, timestamp_show, NULL);
- SNAPSHOT_ATTR(faultcount, 0644, faultcount_show, faultcount_store);
- static void snapshot_sysfs_release(struct kobject *kobj)
- {
- }
- static ssize_t snapshot_sysfs_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
- {
- struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
- struct kgsl_device *device = kobj_to_device(kobj);
- ssize_t ret;
- if (device && pattr->show)
- ret = pattr->show(device, buf);
- else
- ret = -EIO;
- return ret;
- }
- static ssize_t snapshot_sysfs_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
- {
- struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
- struct kgsl_device *device = kobj_to_device(kobj);
- ssize_t ret;
- if (device && pattr->store)
- ret = pattr->store(device, buf, count);
- else
- ret = -EIO;
- return ret;
- }
- static const struct sysfs_ops snapshot_sysfs_ops = {
- .show = snapshot_sysfs_show,
- .store = snapshot_sysfs_store,
- };
- static struct kobj_type ktype_snapshot = {
- .sysfs_ops = &snapshot_sysfs_ops,
- .default_attrs = NULL,
- .release = snapshot_sysfs_release,
- };
- /* kgsl_device_snapshot_init - Add resources for the device GPU snapshot
- * @device - The device to initalize
- *
- * Allocate memory for a GPU snapshot for the specified device,
- * and create the sysfs files to manage it
- */
- int kgsl_device_snapshot_init(struct kgsl_device *device)
- {
- int ret;
- if (device->snapshot == NULL)
- device->snapshot = kzalloc(KGSL_SNAPSHOT_MEMSIZE, GFP_KERNEL);
- if (device->snapshot == NULL)
- return -ENOMEM;
- device->snapshot_maxsize = KGSL_SNAPSHOT_MEMSIZE;
- device->snapshot_timestamp = 0;
- device->snapshot_faultcount = 0;
- INIT_LIST_HEAD(&device->snapshot_obj_list);
- ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot,
- &device->dev->kobj, "snapshot");
- if (ret)
- goto done;
- ret = sysfs_create_bin_file(&device->snapshot_kobj, &snapshot_attr);
- if (ret)
- goto done;
- ret = sysfs_create_file(&device->snapshot_kobj, &attr_trigger.attr);
- if (ret)
- goto done;
- ret = sysfs_create_file(&device->snapshot_kobj, &attr_timestamp.attr);
- if (ret)
- goto done;
- ret = sysfs_create_file(&device->snapshot_kobj, &attr_faultcount.attr);
- done:
- return ret;
- }
- EXPORT_SYMBOL(kgsl_device_snapshot_init);
- /* kgsl_device_snapshot_close - Take down snapshot memory for a device
- * @device - Pointer to the kgsl_device
- *
- * Remove the sysfs files and free the memory allocated for the GPU
- * snapshot
- */
- void kgsl_device_snapshot_close(struct kgsl_device *device)
- {
- sysfs_remove_bin_file(&device->snapshot_kobj, &snapshot_attr);
- sysfs_remove_file(&device->snapshot_kobj, &attr_trigger.attr);
- sysfs_remove_file(&device->snapshot_kobj, &attr_timestamp.attr);
- kobject_put(&device->snapshot_kobj);
- kfree(device->snapshot);
- device->snapshot = NULL;
- device->snapshot_maxsize = 0;
- device->snapshot_timestamp = 0;
- device->snapshot_faultcount = 0;
- }
- EXPORT_SYMBOL(kgsl_device_snapshot_close);
|