summaryrefslogtreecommitdiff
path: root/drivers/gator/gator_events_sched.c
blob: 114f86be189057037fcf1bea03fe8cc8424eb639 (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
/**
 * Copyright (C) ARM Limited 2010-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 "gator.h"
#include <trace/events/sched.h>

#define SCHED_SWITCH	0
#define SCHED_TOTAL		(SCHED_SWITCH+1)

static ulong sched_switch_enabled[GATOR_CLUSTER_COUNT];
static bool sched_switch_enabled_any;
static ulong sched_switch_key[GATOR_CLUSTER_COUNT];
static DEFINE_PER_CPU(int[SCHED_TOTAL], schedCnt);
static DEFINE_PER_CPU(int[SCHED_TOTAL * 2], schedGet);

#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)
GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
#else
GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next))
#endif
{
	unsigned long flags;

	/* disable interrupts to synchronize with gator_events_sched_read()
	 * spinlocks not needed since percpu buffers are used
	 */
	local_irq_save(flags);
	per_cpu(schedCnt, get_physical_cpu())[SCHED_SWITCH]++;
	local_irq_restore(flags);
}

static int gator_events_sched_create_files(struct super_block *sb, struct dentry *root)
{
	struct dentry *dir;
	int i;
	char buf[40];

	/* switch */
	for (i = 0; i < gator_cluster_count; i++) {
		snprintf(buf, sizeof(buf), "%s_switch", gator_clusters[i]->pmnc_name);
		dir = gatorfs_mkdir(sb, root, buf);
		if (!dir)
			return -1;
		gatorfs_create_ulong(sb, dir, "enabled", &sched_switch_enabled[i]);
		gatorfs_create_ro_ulong(sb, dir, "key", &sched_switch_key[i]);
	}

	return 0;
}

static int gator_events_sched_start(void)
{
	int i;

	sched_switch_enabled_any = false;
	for (i = 0; i < gator_cluster_count; i++) {
		if (sched_switch_enabled[i]) {
			sched_switch_enabled_any = true;
			break;
		}
	}

	/* register tracepoints */
	if (sched_switch_enabled_any)
		if (GATOR_REGISTER_TRACE(sched_switch))
			goto sched_switch_exit;
	pr_debug("gator: registered scheduler event tracepoints\n");

	return 0;

	/* unregister tracepoints on error */
sched_switch_exit:
	pr_err("gator: scheduler event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");

	return -1;
}

static void gator_events_sched_stop(void)
{
	if (sched_switch_enabled_any)
		GATOR_UNREGISTER_TRACE(sched_switch);
	pr_debug("gator: unregistered scheduler event tracepoints\n");

	sched_switch_enabled_any = false;
	memset(sched_switch_enabled, 0, sizeof(sched_switch_enabled));
}

static int gator_events_sched_read(int **buffer, bool sched_switch)
{
	unsigned long flags;
	int len, value;
	int cpu = get_physical_cpu();
	int cluster = gator_clusterids[cpu];

	len = 0;
	if (sched_switch_enabled[cluster]) {
		local_irq_save(flags);
		value = per_cpu(schedCnt, cpu)[SCHED_SWITCH];
		per_cpu(schedCnt, cpu)[SCHED_SWITCH] = 0;
		local_irq_restore(flags);
		per_cpu(schedGet, cpu)[len++] = sched_switch_key[cluster];
		per_cpu(schedGet, cpu)[len++] = value;
	}

	if (buffer)
		*buffer = per_cpu(schedGet, cpu);

	return len;
}

static struct gator_interface gator_events_sched_interface = {
	.name = "sched",
	.create_files = gator_events_sched_create_files,
	.start = gator_events_sched_start,
	.stop = gator_events_sched_stop,
	.read = gator_events_sched_read,
};

int gator_events_sched_init(void)
{
	int i;

	for (i = 0; i < gator_cluster_count; i++) {
		sched_switch_enabled[i] = 0;
		sched_switch_key[i] = gator_events_get_key();
	}

	return gator_events_install(&gator_events_sched_interface);
}