/** * Copyright (C) ARM Limited 2010-2015. 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 #include #include #include #include #include #include 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); }