gator_annotate.c 5.31 KB
Newer Older
Abhijith PA's avatar
Abhijith PA committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
/**
 * Copyright (C) ARM Limited 2010-2014. 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);
}