189 lines
5.3 KiB
C
189 lines
5.3 KiB
C
/**
|
|
* 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 <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);
|
|
}
|