aboutsummaryrefslogtreecommitdiff
path: root/drivers/gator/gator_buffer.c
blob: eba22dfe3bf27e2de154745ae50e5456e918725e (plain)
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
/**
 * 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.
 *
 */

static void marshal_frame(int cpu, int buftype)
{
	int frame;

	if (!per_cpu(gator_buffer, cpu)[buftype]) {
		return;
	}

	switch (buftype) {
	case SUMMARY_BUF:
		frame = FRAME_SUMMARY;
		break;
	case BACKTRACE_BUF:
		frame = FRAME_BACKTRACE;
		break;
	case NAME_BUF:
		frame = FRAME_NAME;
		break;
	case COUNTER_BUF:
		frame = FRAME_COUNTER;
		break;
	case BLOCK_COUNTER_BUF:
		frame = FRAME_BLOCK_COUNTER;
		break;
	case ANNOTATE_BUF:
		frame = FRAME_ANNOTATE;
		break;
	case SCHED_TRACE_BUF:
		frame = FRAME_SCHED_TRACE;
		break;
	case GPU_TRACE_BUF:
		frame = FRAME_GPU_TRACE;
		break;
	case IDLE_BUF:
		frame = FRAME_IDLE;
		break;
	default:
		frame = -1;
		break;
	}

	// add response type
	if (gator_response_type > 0) {
		gator_buffer_write_packed_int(cpu, buftype, gator_response_type);
	}

	// leave space for 4-byte unpacked length
	per_cpu(gator_buffer_write, cpu)[buftype] = (per_cpu(gator_buffer_write, cpu)[buftype] + sizeof(s32)) & gator_buffer_mask[buftype];

	// add frame type and core number
	gator_buffer_write_packed_int(cpu, buftype, frame);
	gator_buffer_write_packed_int(cpu, buftype, cpu);
}

static int buffer_bytes_available(int cpu, int buftype)
{
	int remaining, filled;

	filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_read, cpu)[buftype];
	if (filled < 0) {
		filled += gator_buffer_size[buftype];
	}

	remaining = gator_buffer_size[buftype] - filled;

	if (per_cpu(buffer_space_available, cpu)[buftype]) {
		// Give some extra room; also allows space to insert the overflow error packet
		remaining -= 200;
	} else {
		// Hysteresis, prevents multiple overflow messages
		remaining -= 2000;
	}

	return remaining;
}

static bool buffer_check_space(int cpu, int buftype, int bytes)
{
	int remaining = buffer_bytes_available(cpu, buftype);

	if (remaining < bytes) {
		per_cpu(buffer_space_available, cpu)[buftype] = false;
	} else {
		per_cpu(buffer_space_available, cpu)[buftype] = true;
	}

	return per_cpu(buffer_space_available, cpu)[buftype];
}

static int contiguous_space_available(int cpu, int buftype)
{
	int remaining = buffer_bytes_available(cpu, buftype);
	int contiguous = gator_buffer_size[buftype] - per_cpu(gator_buffer_write, cpu)[buftype];
	if (remaining < contiguous)
		return remaining;
	else
		return contiguous;
}

static void gator_commit_buffer(int cpu, int buftype, u64 time)
{
	int type_length, commit, length, byte;
	unsigned long flags;

	if (!per_cpu(gator_buffer, cpu)[buftype])
		return;

	// post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
	local_irq_save(flags);
	type_length = gator_response_type ? 1 : 0;
	commit = per_cpu(gator_buffer_commit, cpu)[buftype];
	length = per_cpu(gator_buffer_write, cpu)[buftype] - commit;
	if (length < 0) {
		length += gator_buffer_size[buftype];
	}
	length = length - type_length - sizeof(s32);

	if (length <= FRAME_HEADER_SIZE) {
		// Nothing to write, only the frame header is present
		local_irq_restore(flags);
		return;
	}

	for (byte = 0; byte < sizeof(s32); byte++) {
		per_cpu(gator_buffer, cpu)[buftype][(commit + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF;
	}

	per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype];

	if (gator_live_rate > 0) {
		while (time > per_cpu(gator_buffer_commit_time, cpu)) {
			per_cpu(gator_buffer_commit_time, cpu) += gator_live_rate;
		}
	}

	marshal_frame(cpu, buftype);
	local_irq_restore(flags);

	// had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater
	if (per_cpu(in_scheduler_context, cpu)) {
#ifndef CONFIG_PREEMPT_RT_FULL
		// mod_timer can not be used in interrupt context in RT-Preempt full
		mod_timer(&gator_buffer_wake_up_timer, jiffies + 1);
#endif
	} else {
		up(&gator_buffer_wake_sem);
	}
}

static void buffer_check(int cpu, int buftype, u64 time)
{
	int filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_commit, cpu)[buftype];
	if (filled < 0) {
		filled += gator_buffer_size[buftype];
	}
	if (filled >= ((gator_buffer_size[buftype] * 3) / 4)) {
		gator_commit_buffer(cpu, buftype, time);
	}
}