--- /dev/null
+/*#############################################################################
+# #
+# collecty - A system statistics collection daemon for IPFire #
+# Copyright (C) 2025 IPFire Development Team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#include <errno.h>
+#include <linux/perf_event.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "../ctx.h"
+#include "../module.h"
+#include "contextswitches.h"
+
+static int perf_event_open(struct perf_event_attr* hw_event,
+ pid_t pid, int cpu, int group_fd, unsigned long flags) {
+ return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
+}
+
+static unsigned int num_cpus = 0;
+static int* perf_eventfds = NULL;
+
+static int contextswitches_init(collecty_ctx* ctx) {
+ struct perf_event_attr event = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_CONTEXT_SWITCHES,
+ .size = sizeof(event),
+ .disabled = 0,
+ .exclude_kernel = 0,
+ .exclude_hv = 0,
+ };
+ int fd = -EBADF;
+
+ // Detect the total number of processors
+ if (!num_cpus)
+ num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+
+ // Allocate an array for the file descriptors
+ perf_eventfds = calloc(num_cpus, sizeof(*perf_eventfds));
+ if (!perf_eventfds)
+ return -errno;
+
+ // Initialize the array
+ for (unsigned int i = 0; i < num_cpus; i++)
+ perf_eventfds[i] = -EBADF;
+
+ // Keep a perf file descriptor open for each CPU core
+ for (unsigned int i = 0; i < num_cpus; i++) {
+ fd = perf_event_open(&event, -1, i, -1, 0);
+ if (fd < 0) {
+ ERROR(ctx, "Failed run perf_event_open() for CPU %d: %m\n", i);
+ return -errno;
+ }
+
+ // Store the file descriptor
+ perf_eventfds[i] = fd;
+ }
+
+ return 0;
+}
+
+static int contextswitches_free(collecty_ctx* ctx) {
+ // Close any open file descriptors
+ if (perf_eventfds) {
+ for (unsigned int i = 0; i < num_cpus; i++) {
+ if (perf_eventfds[i] >= 0)
+ close(perf_eventfds[i]);
+ }
+ free(perf_eventfds);
+ }
+
+ return 0;
+}
+
+static int contextswitches_collect(collecty_ctx* ctx, collecty_module* module) {
+ long long total = 0;
+ uint64_t count = 0;
+ int fd = -EBADF;
+ int r;
+
+ // Read the number of context switches per CPU and add them up
+ for (unsigned int i = 0; i < num_cpus; i++) {
+ fd = perf_eventfds[i];
+
+ // Abort if we are missing some file descriptors
+ if (fd < 0)
+ return -EBADFD;
+
+ // Read the integer
+ r = read(fd, &count, sizeof(count));
+ if (r < 0) {
+ ERROR(ctx, "Failed to read the number of context switches: %m\n");
+ return -errno;
+ }
+
+ // Sum up all values
+ total += count;
+ }
+
+ // Submit the values
+ return collecty_module_submit(module, NULL, "%lld", total);
+}
+
+const collecty_module_methods contextswitches_module = {
+ .name = "contextswitches",
+
+ // RRD Data Sources
+ .rrd_dss = {
+ { "ctxt", "DERIVE", 0, -1, },
+ { NULL },
+ },
+
+ // Methods
+ .init = contextswitches_init,
+ .free = contextswitches_free,
+ .collect = contextswitches_collect,
+};
--- /dev/null
+/*#############################################################################
+# #
+# collecty - A system statistics collection daemon for IPFire #
+# Copyright (C) 2025 IPFire Development Team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#ifndef COLLECTY_MODULE_CONTEXTSWITCHES_H
+#define COLLECTY_MODULE_CONTEXTSWITCHES_H
+
+#include "../module.h"
+
+extern const collecty_module_methods contextswitches_module;
+
+#endif /* COLLECTY_MODULE_CONTEXTSWITCHES_H */