diff options
Diffstat (limited to 'system_cmds/cpuctl.tproj')
-rw-r--r-- | system_cmds/cpuctl.tproj/cpuctl.8 | 67 | ||||
-rw-r--r-- | system_cmds/cpuctl.tproj/cpuctl.c | 145 |
2 files changed, 212 insertions, 0 deletions
diff --git a/system_cmds/cpuctl.tproj/cpuctl.8 b/system_cmds/cpuctl.tproj/cpuctl.8 new file mode 100644 index 0000000..f52f514 --- /dev/null +++ b/system_cmds/cpuctl.tproj/cpuctl.8 @@ -0,0 +1,67 @@ +.\" Darwin cpuctl man page adapted from NetBSD (credits below): +.\" +.\" $NetBSD: cpuctl.8,v 1.18 2018/01/14 00:45:54 mrg Exp $ +.\" +.\" Copyright (c) 2007, 2008, 2012, 2015 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Andrew Doran. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd March 18, 2019 +.Dt CPUCTL 8 +.Os Darwin +.Sh NAME +.Nm cpuctl +.Nd program to control CPUs +.Sh SYNOPSIS +.Nm cpuctl +.Ar command +.Op Ar arguments +.Sh DESCRIPTION +The +.Nm +command can be used to control and inspect the state of CPUs in the system. +.Pp +The first argument, +.Ar command , +specifies the action to take. +Valid commands are: +.Bl -tag -width offline +.It list +For each CPU in the system, display the current state and time of the last +state change. +.It offline Ar cpu Op Ar cpu ... +Set the specified CPUs off line. +.Pp +At least one CPU in the system must remain on line. +.It online Ar cpu Op Ar cpu ... +Set the specified CPUs on line. +.El +.Sh EXAMPLES +Run +.Dl cpuctl offline 2 +and then +.Dl cpuctl list +The output should reflect the fact that CPU#2 was taken offline. diff --git a/system_cmds/cpuctl.tproj/cpuctl.c b/system_cmds/cpuctl.tproj/cpuctl.c new file mode 100644 index 0000000..4821878 --- /dev/null +++ b/system_cmds/cpuctl.tproj/cpuctl.c @@ -0,0 +1,145 @@ +// +// cpuctl.c +// system_cmds +// +// Copyright (c) 2019 Apple Inc. All rights reserved. +// + +#include <err.h> +#include <getopt.h> +#include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include <mach/mach.h> + +static void usage() +{ + printf("usage: cpuctl [ list ]\n"); + printf(" cpuctl { offline | online } <cpu> [ <cpu>... ]\n"); + exit(EX_USAGE); +} + +static void fetch_cpu_info(host_t *priv_port, + processor_port_array_t proc_ports, + mach_msg_type_number_t proc_count, + processor_basic_info_data_t *cpus) +{ + for (int i = 0; i < proc_count; i++) { + mach_msg_type_number_t info_count = PROCESSOR_BASIC_INFO_COUNT; + + if (processor_info(proc_ports[i], PROCESSOR_BASIC_INFO, priv_port, + (processor_info_t)&cpus[i], &info_count) != KERN_SUCCESS) { + errx(EX_OSERR, "processor_info(%d) failed", i); + } + } +} + +static int do_cmd_list(mach_msg_type_number_t proc_count, processor_basic_info_data_t *cpus) +{ + int prev_lowest = -1; + for (int i = 0; i < proc_count; i++) { + int lowest_slot = INT_MAX; + int lowest_idx = -1; + for (int j = 0; j < proc_count; j++) { + int slot = cpus[j].slot_num; + if (slot > prev_lowest && slot < lowest_slot) { + lowest_slot = slot; + lowest_idx = j; + } + } + if (lowest_idx == -1) + errx(EX_OSERR, "slot numbers are out of range"); + + processor_basic_info_data_t *cpu = &cpus[lowest_idx]; + printf("CPU%d: %-7s type=%x,%x master=%d\n", + cpu->slot_num, + cpu->running ? "online" : "offline", + cpu->cpu_type, + cpu->cpu_subtype, + cpu->is_master); + + prev_lowest = lowest_slot; + } + return 0; +} + +static int find_cpu_by_slot(mach_msg_type_number_t proc_count, + processor_basic_info_data_t *cpus, + int slot) +{ + for (int i = 0; i < proc_count; i++) { + if (cpus[i].slot_num == slot) + return i; + } + return -1; +} + +int main(int argc, char **argv) +{ + int opt; + + while ((opt = getopt(argc, argv, "h")) != -1) { + switch (opt) { + case 'h': + usage(); + } + } + + host_t priv_port; + if (host_get_host_priv_port(mach_host_self(), &priv_port) != KERN_SUCCESS) + errx(EX_OSERR, "host_get_host_priv_port() failed"); + + processor_port_array_t proc_ports; + mach_msg_type_number_t proc_count; + if (host_processors(priv_port, &proc_ports, &proc_count) != KERN_SUCCESS) + errx(EX_OSERR, "host_processors() failed"); + + processor_basic_info_data_t *cpus = calloc(proc_count, sizeof(*cpus)); + if (!cpus) + errx(EX_OSERR, "calloc() failed"); + fetch_cpu_info(&priv_port, proc_ports, proc_count, cpus); + + if (optind == argc) + return do_cmd_list(proc_count, cpus); + + const char *cmd = argv[optind]; + optind++; + + if (!strcmp(cmd, "list")) + return do_cmd_list(proc_count, cpus); + + bool up = true; + if (!strncmp(cmd, "off", 3)) + up = false; + else if (strncmp(cmd, "on", 2)) + usage(); + + if (optind == argc) + usage(); + + int ret = 0; + for (; optind < argc; optind++) { + char *endp = NULL; + int slot = (int)strtoul(argv[optind], &endp, 0); + if (*endp != 0) + usage(); + + int cpu = find_cpu_by_slot(proc_count, cpus, slot); + if (cpu == -1) + errx(EX_USAGE, "Invalid CPU ID %d", slot); + + if (up) { + if (processor_start(proc_ports[cpu]) != KERN_SUCCESS) + errx(EX_OSERR, "processor_start(%u) failed", cpu); + } else { + if (processor_exit(proc_ports[cpu]) != KERN_SUCCESS) + errx(EX_OSERR, "processor_exit(%u) failed", cpu); + } + } + + return ret; +} |