| /** |
| * Copyright (C) Arm Limited 2010-2016. 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 as |
| * published by the Free Software Foundation. |
| * |
| */ |
| |
| #include <linux/slab.h> |
| #include <linux/fs.h> |
| #include <linux/mm.h> |
| #include <linux/sched.h> |
| #include <linux/uaccess.h> |
| #include <asm/current.h> |
| #include <linux/spinlock.h> |
| |
| static DEFINE_SPINLOCK(annotate_lock); |
| static bool collect_annotations; |
| |
| static int annotate_copy(struct file *file, char const __user *buf, size_t count) |
| { |
| int cpu = 0; |
| int write = per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF]; |
| |
| if (file == NULL) { |
| /* copy from kernel */ |
| memcpy(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count); |
| } else { |
| /* copy from user space */ |
| if (copy_from_user(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count) != 0) |
| return -1; |
| } |
| per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF] = (write + count) & gator_buffer_mask[ANNOTATE_BUF]; |
| |
| return 0; |
| } |
| |
| static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count_orig, loff_t *offset) |
| { |
| int pid, cpu, header_size, available, contiguous, length1, length2, size, count = count_orig & 0x7fffffff; |
| bool interrupt_context; |
| |
| if (*offset) |
| return -EINVAL; |
| |
| interrupt_context = in_interrupt(); |
| /* Annotations are not supported in interrupt context, but may work |
| * if you comment out the the next four lines of code. By doing so, |
| * annotations in interrupt context can result in deadlocks and lost |
| * data. |
| */ |
| if (interrupt_context) { |
| pr_warning("gator: Annotations are not supported in interrupt context. Edit gator_annotate.c in the gator driver to enable annotations in interrupt context.\n"); |
| return -EINVAL; |
| } |
| |
| retry: |
| /* synchronize between cores and with collect_annotations */ |
| spin_lock(&annotate_lock); |
| |
| if (!collect_annotations) { |
| /* Not collecting annotations, tell the caller everything was written */ |
| size = count_orig; |
| goto annotate_write_out; |
| } |
| |
| /* Annotation only uses a single per-cpu buffer as the data must be in order to the engine */ |
| cpu = 0; |
| |
| if (current == NULL) |
| pid = 0; |
| else |
| pid = current->pid; |
| |
| /* determine total size of the payload */ |
| header_size = MAXSIZE_PACK32 * 3 + MAXSIZE_PACK64; |
| available = buffer_bytes_available(cpu, ANNOTATE_BUF) - header_size; |
| size = count < available ? count : available; |
| |
| if (size <= 0) { |
| /* Buffer is full, wait until space is available */ |
| spin_unlock(&annotate_lock); |
| |
| /* Drop the annotation as blocking is not allowed in interrupt context */ |
| if (interrupt_context) |
| return -EINVAL; |
| |
| wait_event_interruptible(gator_annotate_wait, buffer_bytes_available(cpu, ANNOTATE_BUF) > header_size || !collect_annotations); |
| |
| /* Check to see if a signal is pending */ |
| if (signal_pending(current)) |
| return -EINTR; |
| |
| goto retry; |
| } |
| |
| /* synchronize shared variables annotateBuf and annotatePos */ |
| if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF]) { |
| u64 time = gator_get_time(); |
| |
| gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, get_physical_cpu()); |
| gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, pid); |
| gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, time); |
| gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, size); |
| |
| /* determine the sizes to capture, length1 + length2 will equal size */ |
| contiguous = contiguous_space_available(cpu, ANNOTATE_BUF); |
| if (size < contiguous) { |
| length1 = size; |
| length2 = 0; |
| } else { |
| length1 = contiguous; |
| length2 = size - contiguous; |
| } |
| |
| if (annotate_copy(file, buf, length1) != 0) { |
| size = -EINVAL; |
| goto annotate_write_out; |
| } |
| |
| if (length2 > 0 && annotate_copy(file, &buf[length1], length2) != 0) { |
| size = -EINVAL; |
| goto annotate_write_out; |
| } |
| |
| /* Check and commit; commit is set to occur once buffer is 3/4 full */ |
| buffer_check(cpu, ANNOTATE_BUF, time); |
| } |
| |
| annotate_write_out: |
| spin_unlock(&annotate_lock); |
| |
| /* return the number of bytes written */ |
| return size; |
| } |
| |
| #include "gator_annotate_kernel.c" |
| |
| static int annotate_release(struct inode *inode, struct file *file) |
| { |
| int cpu = 0; |
| |
| /* synchronize between cores */ |
| spin_lock(&annotate_lock); |
| |
| if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF] && buffer_check_space(cpu, ANNOTATE_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) { |
| uint32_t pid = current->pid; |
| |
| gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, get_physical_cpu()); |
| gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, pid); |
| /* time */ |
| gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, 0); |
| /* size */ |
| gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, 0); |
| } |
| |
| /* Check and commit; commit is set to occur once buffer is 3/4 full */ |
| buffer_check(cpu, ANNOTATE_BUF, gator_get_time()); |
| |
| spin_unlock(&annotate_lock); |
| |
| return 0; |
| } |
| |
| static const struct file_operations annotate_fops = { |
| .write = annotate_write, |
| .release = annotate_release |
| }; |
| |
| static int gator_annotate_create_files(struct super_block *sb, struct dentry *root) |
| { |
| return gatorfs_create_file_perm(sb, root, "annotate", &annotate_fops, 0666); |
| } |
| |
| static int gator_annotate_start(void) |
| { |
| collect_annotations = true; |
| return 0; |
| } |
| |
| static void gator_annotate_stop(void) |
| { |
| /* the spinlock here will ensure that when this function exits, we are not in the middle of an annotation */ |
| spin_lock(&annotate_lock); |
| collect_annotations = false; |
| wake_up(&gator_annotate_wait); |
| spin_unlock(&annotate_lock); |
| } |