diff options
Diffstat (limited to 'mali-midgard-16.0/mali_kbase_debug_job_fault.c')
-rw-r--r-- | mali-midgard-16.0/mali_kbase_debug_job_fault.c | 499 |
1 files changed, 0 insertions, 499 deletions
diff --git a/mali-midgard-16.0/mali_kbase_debug_job_fault.c b/mali-midgard-16.0/mali_kbase_debug_job_fault.c deleted file mode 100644 index f29430d..0000000 --- a/mali-midgard-16.0/mali_kbase_debug_job_fault.c +++ /dev/null @@ -1,499 +0,0 @@ -/* - * - * (C) COPYRIGHT 2012-2016 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the - * GNU General Public License version 2 as published by the Free Software - * Foundation, and any use by you of this program is subject to the terms - * of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained - * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - - - -#include <mali_kbase.h> -#include <linux/spinlock.h> -#include <mali_kbase_hwaccess_jm.h> - -#ifdef CONFIG_DEBUG_FS - -static bool kbase_is_job_fault_event_pending(struct kbase_device *kbdev) -{ - struct list_head *event_list = &kbdev->job_fault_event_list; - unsigned long flags; - bool ret; - - spin_lock_irqsave(&kbdev->job_fault_event_lock, flags); - ret = !list_empty(event_list); - spin_unlock_irqrestore(&kbdev->job_fault_event_lock, flags); - - return ret; -} - -static bool kbase_ctx_has_no_event_pending(struct kbase_context *kctx) -{ - struct kbase_device *kbdev = kctx->kbdev; - struct list_head *event_list = &kctx->kbdev->job_fault_event_list; - struct base_job_fault_event *event; - unsigned long flags; - - spin_lock_irqsave(&kbdev->job_fault_event_lock, flags); - if (list_empty(event_list)) { - spin_unlock_irqrestore(&kbdev->job_fault_event_lock, flags); - return true; - } - list_for_each_entry(event, event_list, head) { - if (event->katom->kctx == kctx) { - spin_unlock_irqrestore(&kbdev->job_fault_event_lock, - flags); - return false; - } - } - spin_unlock_irqrestore(&kbdev->job_fault_event_lock, flags); - return true; -} - -/* wait until the fault happen and copy the event */ -static int kbase_job_fault_event_wait(struct kbase_device *kbdev, - struct base_job_fault_event *event) -{ - struct list_head *event_list = &kbdev->job_fault_event_list; - struct base_job_fault_event *event_in; - unsigned long flags; - - spin_lock_irqsave(&kbdev->job_fault_event_lock, flags); - if (list_empty(event_list)) { - spin_unlock_irqrestore(&kbdev->job_fault_event_lock, flags); - if (wait_event_interruptible(kbdev->job_fault_wq, - kbase_is_job_fault_event_pending(kbdev))) - return -ERESTARTSYS; - spin_lock_irqsave(&kbdev->job_fault_event_lock, flags); - } - - event_in = list_entry(event_list->next, - struct base_job_fault_event, head); - event->event_code = event_in->event_code; - event->katom = event_in->katom; - - spin_unlock_irqrestore(&kbdev->job_fault_event_lock, flags); - - return 0; - -} - -/* remove the event from the queue */ -static struct base_job_fault_event *kbase_job_fault_event_dequeue( - struct kbase_device *kbdev, struct list_head *event_list) -{ - struct base_job_fault_event *event; - - event = list_entry(event_list->next, - struct base_job_fault_event, head); - list_del(event_list->next); - - return event; - -} - -/* Remove all the following atoms after the failed atom in the same context - * Call the postponed bottom half of job done. - * Then, this context could be rescheduled. - */ -static void kbase_job_fault_resume_event_cleanup(struct kbase_context *kctx) -{ - struct list_head *event_list = &kctx->job_fault_resume_event_list; - - while (!list_empty(event_list)) { - struct base_job_fault_event *event; - - event = kbase_job_fault_event_dequeue(kctx->kbdev, - &kctx->job_fault_resume_event_list); - kbase_jd_done_worker(&event->katom->work); - } - -} - -/* Remove all the failed atoms that belong to different contexts - * Resume all the contexts that were suspend due to failed job - */ -static void kbase_job_fault_event_cleanup(struct kbase_device *kbdev) -{ - struct list_head *event_list = &kbdev->job_fault_event_list; - unsigned long flags; - - spin_lock_irqsave(&kbdev->job_fault_event_lock, flags); - while (!list_empty(event_list)) { - kbase_job_fault_event_dequeue(kbdev, event_list); - spin_unlock_irqrestore(&kbdev->job_fault_event_lock, flags); - wake_up(&kbdev->job_fault_resume_wq); - spin_lock_irqsave(&kbdev->job_fault_event_lock, flags); - } - spin_unlock_irqrestore(&kbdev->job_fault_event_lock, flags); -} - -static void kbase_job_fault_resume_worker(struct work_struct *data) -{ - struct base_job_fault_event *event = container_of(data, - struct base_job_fault_event, job_fault_work); - struct kbase_context *kctx; - struct kbase_jd_atom *katom; - - katom = event->katom; - kctx = katom->kctx; - - dev_info(kctx->kbdev->dev, "Job dumping wait\n"); - - /* When it was waked up, it need to check if queue is empty or the - * failed atom belongs to different context. If yes, wake up. Both - * of them mean the failed job has been dumped. Please note, it - * should never happen that the job_fault_event_list has the two - * atoms belong to the same context. - */ - wait_event(kctx->kbdev->job_fault_resume_wq, - kbase_ctx_has_no_event_pending(kctx)); - - atomic_set(&kctx->job_fault_count, 0); - kbase_jd_done_worker(&katom->work); - - /* In case the following atoms were scheduled during failed job dump - * the job_done_worker was held. We need to rerun it after the dump - * was finished - */ - kbase_job_fault_resume_event_cleanup(kctx); - - dev_info(kctx->kbdev->dev, "Job dumping finish, resume scheduler\n"); -} - -static struct base_job_fault_event *kbase_job_fault_event_queue( - struct list_head *event_list, - struct kbase_jd_atom *atom, - u32 completion_code) -{ - struct base_job_fault_event *event; - - event = &atom->fault_event; - - event->katom = atom; - event->event_code = completion_code; - - list_add_tail(&event->head, event_list); - - return event; - -} - -static void kbase_job_fault_event_post(struct kbase_device *kbdev, - struct kbase_jd_atom *katom, u32 completion_code) -{ - struct base_job_fault_event *event; - unsigned long flags; - - spin_lock_irqsave(&kbdev->job_fault_event_lock, flags); - event = kbase_job_fault_event_queue(&kbdev->job_fault_event_list, - katom, completion_code); - spin_unlock_irqrestore(&kbdev->job_fault_event_lock, flags); - - wake_up_interruptible(&kbdev->job_fault_wq); - - INIT_WORK(&event->job_fault_work, kbase_job_fault_resume_worker); - queue_work(kbdev->job_fault_resume_workq, &event->job_fault_work); - - dev_info(katom->kctx->kbdev->dev, "Job fault happen, start dump: %d_%d", - katom->kctx->tgid, katom->kctx->id); - -} - -/* - * This function will process the job fault - * Get the register copy - * Send the failed job dump event - * Create a Wait queue to wait until the job dump finish - */ - -bool kbase_debug_job_fault_process(struct kbase_jd_atom *katom, - u32 completion_code) -{ - struct kbase_context *kctx = katom->kctx; - - /* Check if dumping is in the process - * only one atom of each context can be dumped at the same time - * If the atom belongs to different context, it can be dumped - */ - if (atomic_read(&kctx->job_fault_count) > 0) { - kbase_job_fault_event_queue( - &kctx->job_fault_resume_event_list, - katom, completion_code); - dev_info(kctx->kbdev->dev, "queue:%d\n", - kbase_jd_atom_id(kctx, katom)); - return true; - } - - if (kctx->kbdev->job_fault_debug == true) { - - if (completion_code != BASE_JD_EVENT_DONE) { - - if (kbase_job_fault_get_reg_snapshot(kctx) == false) { - dev_warn(kctx->kbdev->dev, "get reg dump failed\n"); - return false; - } - - kbase_job_fault_event_post(kctx->kbdev, katom, - completion_code); - atomic_inc(&kctx->job_fault_count); - dev_info(kctx->kbdev->dev, "post:%d\n", - kbase_jd_atom_id(kctx, katom)); - return true; - - } - } - return false; - -} - -static int debug_job_fault_show(struct seq_file *m, void *v) -{ - struct kbase_device *kbdev = m->private; - struct base_job_fault_event *event = (struct base_job_fault_event *)v; - struct kbase_context *kctx = event->katom->kctx; - int i; - - dev_info(kbdev->dev, "debug job fault seq show:%d_%d, %d", - kctx->tgid, kctx->id, event->reg_offset); - - if (kctx->reg_dump == NULL) { - dev_warn(kbdev->dev, "reg dump is NULL"); - return -1; - } - - if (kctx->reg_dump[event->reg_offset] == - REGISTER_DUMP_TERMINATION_FLAG) { - /* Return the error here to stop the read. And the - * following next() will not be called. The stop can - * get the real event resource and release it - */ - return -1; - } - - if (event->reg_offset == 0) - seq_printf(m, "%d_%d\n", kctx->tgid, kctx->id); - - for (i = 0; i < 50; i++) { - if (kctx->reg_dump[event->reg_offset] == - REGISTER_DUMP_TERMINATION_FLAG) { - break; - } - seq_printf(m, "%08x: %08x\n", - kctx->reg_dump[event->reg_offset], - kctx->reg_dump[1+event->reg_offset]); - event->reg_offset += 2; - - } - - - return 0; -} -static void *debug_job_fault_next(struct seq_file *m, void *v, loff_t *pos) -{ - struct kbase_device *kbdev = m->private; - struct base_job_fault_event *event = (struct base_job_fault_event *)v; - - dev_info(kbdev->dev, "debug job fault seq next:%d, %d", - event->reg_offset, (int)*pos); - - return event; -} - -static void *debug_job_fault_start(struct seq_file *m, loff_t *pos) -{ - struct kbase_device *kbdev = m->private; - struct base_job_fault_event *event; - - dev_info(kbdev->dev, "fault job seq start:%d", (int)*pos); - - /* The condition is trick here. It needs make sure the - * fault hasn't happened and the dumping hasn't been started, - * or the dumping has finished - */ - if (*pos == 0) { - event = kmalloc(sizeof(*event), GFP_KERNEL); - if (!event) - return NULL; - event->reg_offset = 0; - if (kbase_job_fault_event_wait(kbdev, event)) { - kfree(event); - return NULL; - } - - /* The cache flush workaround is called in bottom half of - * job done but we delayed it. Now we should clean cache - * earlier. Then the GPU memory dump should be correct. - */ - kbase_backend_cacheclean(kbdev, event->katom); - } else - return NULL; - - return event; -} - -static void debug_job_fault_stop(struct seq_file *m, void *v) -{ - struct kbase_device *kbdev = m->private; - - /* here we wake up the kbase_jd_done_worker after stop, it needs - * get the memory dump before the register dump in debug daemon, - * otherwise, the memory dump may be incorrect. - */ - - if (v != NULL) { - kfree(v); - dev_info(kbdev->dev, "debug job fault seq stop stage 1"); - - } else { - unsigned long flags; - - spin_lock_irqsave(&kbdev->job_fault_event_lock, flags); - if (!list_empty(&kbdev->job_fault_event_list)) { - kbase_job_fault_event_dequeue(kbdev, - &kbdev->job_fault_event_list); - wake_up(&kbdev->job_fault_resume_wq); - } - spin_unlock_irqrestore(&kbdev->job_fault_event_lock, flags); - dev_info(kbdev->dev, "debug job fault seq stop stage 2"); - } - -} - -static const struct seq_operations ops = { - .start = debug_job_fault_start, - .next = debug_job_fault_next, - .stop = debug_job_fault_stop, - .show = debug_job_fault_show, -}; - -static int debug_job_fault_open(struct inode *in, struct file *file) -{ - struct kbase_device *kbdev = in->i_private; - - seq_open(file, &ops); - - ((struct seq_file *)file->private_data)->private = kbdev; - dev_info(kbdev->dev, "debug job fault seq open"); - - kbdev->job_fault_debug = true; - - return 0; - -} - -static int debug_job_fault_release(struct inode *in, struct file *file) -{ - struct kbase_device *kbdev = in->i_private; - - seq_release(in, file); - - kbdev->job_fault_debug = false; - - /* Clean the unprocessed job fault. After that, all the suspended - * contexts could be rescheduled. - */ - kbase_job_fault_event_cleanup(kbdev); - - dev_info(kbdev->dev, "debug job fault seq close"); - - return 0; -} - -static const struct file_operations kbasep_debug_job_fault_fops = { - .open = debug_job_fault_open, - .read = seq_read, - .llseek = seq_lseek, - .release = debug_job_fault_release, -}; - -/* - * Initialize debugfs entry for job fault dump - */ -void kbase_debug_job_fault_debugfs_init(struct kbase_device *kbdev) -{ - debugfs_create_file("job_fault", S_IRUGO, - kbdev->mali_debugfs_directory, kbdev, - &kbasep_debug_job_fault_fops); -} - - -int kbase_debug_job_fault_dev_init(struct kbase_device *kbdev) -{ - - INIT_LIST_HEAD(&kbdev->job_fault_event_list); - - init_waitqueue_head(&(kbdev->job_fault_wq)); - init_waitqueue_head(&(kbdev->job_fault_resume_wq)); - spin_lock_init(&kbdev->job_fault_event_lock); - - kbdev->job_fault_resume_workq = alloc_workqueue( - "kbase_job_fault_resume_work_queue", WQ_MEM_RECLAIM, 1); - if (!kbdev->job_fault_resume_workq) - return -ENOMEM; - - kbdev->job_fault_debug = false; - - return 0; -} - -/* - * Release the relevant resource per device - */ -void kbase_debug_job_fault_dev_term(struct kbase_device *kbdev) -{ - destroy_workqueue(kbdev->job_fault_resume_workq); -} - - -/* - * Initialize the relevant data structure per context - */ -void kbase_debug_job_fault_context_init(struct kbase_context *kctx) -{ - - /* We need allocate double size register range - * Because this memory will keep the register address and value - */ - kctx->reg_dump = vmalloc(0x4000 * 2); - if (kctx->reg_dump == NULL) - return; - - if (kbase_debug_job_fault_reg_snapshot_init(kctx, 0x4000) == false) { - vfree(kctx->reg_dump); - kctx->reg_dump = NULL; - } - INIT_LIST_HEAD(&kctx->job_fault_resume_event_list); - atomic_set(&kctx->job_fault_count, 0); - -} - -/* - * release the relevant resource per context - */ -void kbase_debug_job_fault_context_term(struct kbase_context *kctx) -{ - vfree(kctx->reg_dump); -} - -#else /* CONFIG_DEBUG_FS */ - -int kbase_debug_job_fault_dev_init(struct kbase_device *kbdev) -{ - kbdev->job_fault_debug = false; - - return 0; -} - -void kbase_debug_job_fault_dev_term(struct kbase_device *kbdev) -{ -} - -#endif /* CONFIG_DEBUG_FS */ |