summaryrefslogtreecommitdiff
path: root/tools/gator/daemon/CCNDriver.cpp
blob: c041d6e439891dab86dad6e53a0d5feabe52dab4 (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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
/**
 * Copyright (C) ARM Limited 2014-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 "CCNDriver.h"

#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "k/perf_event.h"

#include "Config.h"
#include "DriverSource.h"
#include "Logging.h"
#include "SessionData.h"

static const char TAG_CATEGORY[] = "category";
static const char TAG_COUNTER_SET[] = "counter_set";
static const char TAG_EVENT[] = "event";
static const char TAG_OPTION[] = "option";
static const char TAG_OPTION_SET[] = "option_set";

static const char ATTR_AVERAGE_SELECTION[] = "average_selection";
static const char ATTR_COUNTER[] = "counter";
static const char ATTR_COUNT[] = "count";
static const char ATTR_DESCRIPTION[] = "description";
static const char ATTR_DISPLAY[] = "display";
static const char ATTR_EVENT[] = "event";
static const char ATTR_EVENT_DELTA[] = "event_delta";
static const char ATTR_NAME[] = "name";
static const char ATTR_OPTION_SET[] = "option_set";
static const char ATTR_TITLE[] = "title";
static const char ATTR_UNITS[] = "units";

static const char XP_REGION[] = "XP_Region";
static const char HNF_REGION[] = "HN-F_Region";
static const char RNI_REGION[] = "RN-I_Region";
static const char SBAS_REGION[] = "SBAS_Region";
static const char CCN_5XX[] = "CCN-5xx";
#define ARM_CCN_5XX "ARM_CCN_5XX_"
#define CCN_COUNT 8
static const char ARM_CCN_5XX_CNT[] = ARM_CCN_5XX "cnt";

static const char *const VC_TYPES[] = { "REQ", "RSP", "SNP", "DAT" };
static const char *const XP_EVENT_NAMES[] = { NULL, "H-bit", "S-bit", "P-Cnt", "TknV" };
static const char *const XP_EVENT_DESCRIPTIONS[] = { NULL, "Set H-bit, signaled when this XP sets the H-bit.", "Set S-bit, signaled when this XP sets the S-bit.", "Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC.", "No TknV, signaled when this XP transmits a valid packet." };
static const char *const HNF_EVENT_NAMES[] = { NULL, "Cache Miss", "L3 SF Cache Access", "Cache Fill", "POCQ Retry", "POCQ Reqs Recvd", "SF Hit", "SF Evictions", "Snoops Sent", "Snoops Broadcast", "L3 Eviction", "L3 Fill Invalid Way", "MC Retries", "MC Reqs", "QOS HH Retry" };
static const char *const HNF_EVENT_DESCRIPTIONS[] = { NULL, "Counts the total cache misses. This is the first time lookup result, and is high priority.", "Counts the number of cache accesses. This is the first time access, and is high priority.", "Counts the total allocations in the HN L3 cache, and all cache line allocations to the L3 cache.", "Counts the number of requests that have been retried.", "Counts the number of requests received by HN.", "Counts the number of snoop filter hits.", "Counts the number of snoop filter evictions. Cache invalidations are initiated.", "Counts the number of snoops sent. Does not differentiate between broadcast or directed snoops.", "Counts the number of snoop broadcasts sent.", "Counts the number of L3 evictions.", "Counts the number of L3 fills to an invalid way.", "Counts the number of transactions retried by the memory controller.", "Counts the number of requests to the memory controller.", "Counts the number of times a highest-priority QoS class was retried at the HN-F." };
static const char *const RNI_EVENT_NAMES[] = { NULL, "S0 RDataBeats", "S1 RDataBeats", "S2 RDataBeats", "RXDAT Flits received", "TXDAT Flits sent", "Total TXREQ Flits sent", "Retried TXREQ Flits sent", "RRT full", "WRT full", "Replayed TXREQ Flits" };
static const char *const RNI_EVENT_DESCRIPTIONS[] = { NULL, "S0 RDataBeats.", "S1 RDataBeats.", "S2 RDataBeats.", "RXDAT Flits received.", "TXDAT Flits sent.", "Total TXREQ Flits sent.", "Retried TXREQ Flits sent.", "RRT full.", "WRT full.", "Replayed TXREQ Flits." };
static const char *const SBAS_EVENT_NAMES[] = { NULL, "S0 RDataBeats", NULL, NULL, "RXDAT Flits received", "TXDAT Flits sent", "Total TXREQ Flits sent", "Retried TXREQ Flits sent", "RRT full", "WRT full", "Replayed TXREQ Flits" };
static const char *const SBAS_EVENT_DESCRIPTIONS[] = { NULL, "S0 RDataBeats.", NULL, NULL, "RXDAT Flits received.", "TXDAT Flits sent.", "Total TXREQ Flits sent.", "Retried TXREQ Flits sent.", "RRT full.", "WRT full.", "Replayed TXREQ Flits." };

// This class is used only to poll for CCN-5xx configuration and emit events XML for it. All other operations are handled by PerfDriver

static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t pid, const int cpu, const int group_fd, const unsigned long flags) {
	return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
}

static unsigned int getConfig(unsigned int node, unsigned int type, unsigned int event, unsigned int port, unsigned int vc) {
  return
    ((node  & 0xff) <<  0) |
    ((type  & 0xff) <<  8) |
    ((event & 0xff) << 16) |
    ((port  & 0x03) << 24) |
    ((vc    & 0x07) << 26) |
    0;
}

static bool perfPoll(struct perf_event_attr *const pea) {
	int fd = sys_perf_event_open(pea, -1, 0, -1, 0);
	if (fd < 0) {
		return false;
	}
	close(fd);
	return true;
}

CCNDriver::CCNDriver() : mNodeTypes(NULL), mXpCount(0) {
}

CCNDriver::~CCNDriver() {
	delete mNodeTypes;
}

bool CCNDriver::claimCounter(const Counter &) const {
	// Handled by PerfDriver
	return false;
}

void CCNDriver::resetCounters() {
	// Handled by PerfDriver
}

void CCNDriver::setupCounter(Counter &) {
	// Handled by PerfDriver
}

void CCNDriver::readEvents(mxml_node_t *const) {
	struct stat st;
	if (stat("/sys/bus/event_source/devices/ccn", &st) != 0) {
		// Not found
		return;
	}

	int type;
	if (DriverSource::readIntDriver("/sys/bus/event_source/devices/ccn/type", &type) != 0) {
		logg.logError("Unable to read CCN-5xx type");
		handleException();
	}

	// Detect number of xps
	struct perf_event_attr pea;
	memset(&pea, 0, sizeof(pea));
	pea.type = type;
	pea.size = sizeof(pea);

	mXpCount = 1;
	while (true) {
		pea.config = getConfig(0, 0x08, 1, 0, 1) | mXpCount;
		if (!perfPoll(&pea)) {
			break;
		}
		mXpCount *= 2;
	};
	{
		int lower = mXpCount/2 + 1;
		while (lower < mXpCount) {
			int mid = (lower + mXpCount)/2;
			pea.config = getConfig(0, 0x08, 1, 0, 1) | mid;
			if (perfPoll(&pea)) {
				lower = mid + 1;
			} else {
				mXpCount = mid;
			}
		}
	}

	mNodeTypes = new NodeType[2*mXpCount];

	// Detect node types
	for (int i = 0; i < 2*mXpCount; ++i) {
		pea.config = getConfig(0, 0x04, 1, 0, 0) | i;
		if (perfPoll(&pea)) {
			mNodeTypes[i] = NT_HNF;
			continue;
		}

		pea.config = getConfig(0, 0x16, 1, 0, 0) | i;
		if (perfPoll(&pea)) {
			mNodeTypes[i] = NT_RNI;
			continue;
		}

		pea.config = getConfig(0, 0x10, 1, 0, 0) | i;
		if (perfPoll(&pea)) {
			mNodeTypes[i] = NT_SBAS;
			continue;
		}

		mNodeTypes[i] = NT_UNKNOWN;
	}
}

int CCNDriver::writeCounters(mxml_node_t *const) const {
	// Handled by PerfDriver
	return 0;
}

void CCNDriver::writeEvents(mxml_node_t *const root) const {
	mxml_node_t *const counter_set = mxmlNewElement(root, TAG_COUNTER_SET);
	mxmlElementSetAttr(counter_set, ATTR_NAME, ARM_CCN_5XX_CNT);
	mxmlElementSetAttr(counter_set, ATTR_COUNT, STRIFY(CCN_COUNT));

	mxml_node_t *const category = mxmlNewElement(root, TAG_CATEGORY);
	mxmlElementSetAttr(category, ATTR_NAME, CCN_5XX);
	mxmlElementSetAttr(category, TAG_COUNTER_SET, ARM_CCN_5XX_CNT);

	mxml_node_t *const clock_event = mxmlNewElement(category, TAG_EVENT);
	mxmlElementSetAttr(clock_event, ATTR_COUNTER, ARM_CCN_5XX "ccnt");
	mxmlElementSetAttr(clock_event, ATTR_EVENT, "0xff00");
	mxmlElementSetAttr(clock_event, ATTR_TITLE, "CCN-5xx Clock");
	mxmlElementSetAttr(clock_event, ATTR_NAME, "Cycles");
	mxmlElementSetAttr(clock_event, ATTR_DISPLAY, "hertz");
	mxmlElementSetAttr(clock_event, ATTR_UNITS, "Hz");
	mxmlElementSetAttr(clock_event, ATTR_AVERAGE_SELECTION, "yes");
	mxmlElementSetAttr(clock_event, ATTR_DESCRIPTION, "The number of core clock cycles");

	mxml_node_t *const xp_option_set = mxmlNewElement(category, TAG_OPTION_SET);
	mxmlElementSetAttr(xp_option_set, ATTR_NAME, XP_REGION);

	for (int i = 0; i < mXpCount; ++i) {
		mxml_node_t *const option = mxmlNewElement(xp_option_set, TAG_OPTION);
		mxmlElementSetAttrf(option, ATTR_EVENT_DELTA, "0x%x", getConfig(i, 0, 0, 0, 0));
		mxmlElementSetAttrf(option, ATTR_NAME, "XP %i", i);
		mxmlElementSetAttrf(option, ATTR_DESCRIPTION, "Crosspoint %i", i);
	}

	for (int vc = 0; vc < ARRAY_LENGTH(VC_TYPES); ++vc) {
		if (VC_TYPES[vc] == NULL) {
			continue;
		}
		for (int bus = 0; bus < 2; ++bus) {
			for (int eventId = 0; eventId < ARRAY_LENGTH(XP_EVENT_NAMES); ++eventId) {
				if (XP_EVENT_NAMES[eventId] == NULL) {
					continue;
				}
				mxml_node_t *const event = mxmlNewElement(category, TAG_EVENT);
				mxmlElementSetAttrf(event, ATTR_EVENT, "0x%x", getConfig(0, 0x08, eventId, bus, vc));
				mxmlElementSetAttr(event, ATTR_OPTION_SET, XP_REGION);
				mxmlElementSetAttr(event, ATTR_TITLE, CCN_5XX);
				mxmlElementSetAttrf(event, ATTR_NAME, "Bus %i: %s: %s", bus, VC_TYPES[vc], XP_EVENT_NAMES[eventId]);
				mxmlElementSetAttrf(event, ATTR_DESCRIPTION, "Bus %i: %s: %s", bus, VC_TYPES[vc], XP_EVENT_DESCRIPTIONS[eventId]);
			}
		}
	}

	mxml_node_t *const hnf_option_set = mxmlNewElement(category, TAG_OPTION_SET);
	mxmlElementSetAttr(hnf_option_set, ATTR_NAME, HNF_REGION);

	for (int eventId = 0; eventId < ARRAY_LENGTH(HNF_EVENT_NAMES); ++eventId) {
		if (HNF_EVENT_NAMES[eventId] == NULL) {
			continue;
		}
		mxml_node_t *const event = mxmlNewElement(category, TAG_EVENT);
		mxmlElementSetAttrf(event, ATTR_EVENT, "0x%x", getConfig(0, 0x04, eventId, 0, 0));
		mxmlElementSetAttr(event, ATTR_OPTION_SET, HNF_REGION);
		mxmlElementSetAttr(event, ATTR_TITLE, CCN_5XX);
		mxmlElementSetAttr(event, ATTR_NAME, HNF_EVENT_NAMES[eventId]);
		mxmlElementSetAttr(event, ATTR_DESCRIPTION, HNF_EVENT_DESCRIPTIONS[eventId]);
	}

	mxml_node_t *const rni_option_set = mxmlNewElement(category, TAG_OPTION_SET);
	mxmlElementSetAttr(rni_option_set, ATTR_NAME, RNI_REGION);

	for (int eventId = 0; eventId < ARRAY_LENGTH(RNI_EVENT_NAMES); ++eventId) {
		if (RNI_EVENT_NAMES[eventId] == NULL) {
			continue;
		}
		mxml_node_t *const event = mxmlNewElement(category, TAG_EVENT);
		mxmlElementSetAttrf(event, ATTR_EVENT, "0x%x", getConfig(0, 0x16, eventId, 0, 0));
		mxmlElementSetAttr(event, ATTR_OPTION_SET, RNI_REGION);
		mxmlElementSetAttr(event, ATTR_TITLE, CCN_5XX);
		mxmlElementSetAttr(event, ATTR_NAME, RNI_EVENT_NAMES[eventId]);
		mxmlElementSetAttr(event, ATTR_DESCRIPTION, RNI_EVENT_DESCRIPTIONS[eventId]);
	}

	mxml_node_t *const sbas_option_set = mxmlNewElement(category, TAG_OPTION_SET);
	mxmlElementSetAttr(sbas_option_set, ATTR_NAME, SBAS_REGION);

	for (int eventId = 0; eventId < ARRAY_LENGTH(SBAS_EVENT_NAMES); ++eventId) {
		if (SBAS_EVENT_NAMES[eventId] == NULL) {
			continue;
		}
		mxml_node_t *const event = mxmlNewElement(category, TAG_EVENT);
		mxmlElementSetAttrf(event, ATTR_EVENT, "0x%x", getConfig(0, 0x10, eventId, 0, 0));
		mxmlElementSetAttr(event, ATTR_OPTION_SET, SBAS_REGION);
		mxmlElementSetAttr(event, ATTR_TITLE, CCN_5XX);
		mxmlElementSetAttr(event, ATTR_NAME, SBAS_EVENT_NAMES[eventId]);
		mxmlElementSetAttr(event, ATTR_DESCRIPTION, SBAS_EVENT_DESCRIPTIONS[eventId]);
	}

	for (int i = 0; i < 2*mXpCount; ++i) {
		switch (mNodeTypes[i]) {
		case NT_HNF: {
			mxml_node_t *const option = mxmlNewElement(hnf_option_set, TAG_OPTION);
			mxmlElementSetAttrf(option, ATTR_EVENT_DELTA, "0x%x", getConfig(i, 0, 0, 0, 0));
			mxmlElementSetAttrf(option, ATTR_NAME, "HN-F %i", i);
			mxmlElementSetAttrf(option, ATTR_DESCRIPTION, "Fully-coherent Home Node %i", i);
			break;
		}
		case NT_RNI: {
			mxml_node_t *const option = mxmlNewElement(rni_option_set, TAG_OPTION);
			mxmlElementSetAttrf(option, ATTR_EVENT_DELTA, "0x%x", getConfig(i, 0, 0, 0, 0));
			mxmlElementSetAttrf(option, ATTR_NAME, "RN-I %i", i);
			mxmlElementSetAttrf(option, ATTR_DESCRIPTION, "I/O-coherent Requesting Node %i", i);
			break;
		}
		case NT_SBAS: {
			mxml_node_t *const option = mxmlNewElement(sbas_option_set, TAG_OPTION);
			mxmlElementSetAttrf(option, ATTR_EVENT_DELTA, "0x%x", getConfig(i, 0, 0, 0, 0));
			mxmlElementSetAttrf(option, ATTR_NAME, "SBAS %i", i);
			mxmlElementSetAttrf(option, ATTR_DESCRIPTION, "ACE master to CHI protocol bridge %i", i);
			break;
		}
		default:
			continue;
		}
	}
}

void CCNDriver::validateCounters() const {
	int counts[CCN_COUNT][2] = { { 0 } };
	const unsigned int mask = getConfig(0xff, 0xff, 0, 0, 0);

	for (int i = 0; i < ARRAY_LENGTH(gSessionData.mCounters); ++i) {
		const Counter *const counter = &gSessionData.mCounters[i];

		if (!counter->isEnabled()) {
			continue;
		}

		if (strncmp(counter->getType(), ARM_CCN_5XX_CNT, sizeof(ARM_CCN_5XX_CNT) - 1) == 0) {
			const int node = counter->getEvent() & mask;

			for (int j = 0; j < ARRAY_LENGTH(counts); ++j) {
				if (counts[j][0] == 0) {
					counts[j][0] = node;
				}
				if (counts[j][0] == node) {
					++counts[j][1];
					if (counts[j][1] > 4) {
						if (asprintf(&gSessionData.mCountersError, "More than 4 events are assigned to the same CCN node") <= 0) {
							logg.logError("asprintf failed");
							handleException();
						}
					}
				}
			}
		}
	}
}