summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Widawsky <benjamin.widawsky@intel.com>2015-01-10 14:36:20 -0800
committerBen Widawsky <benjamin.widawsky@intel.com>2015-01-12 15:58:21 -0800
commit5fb26d1469f95ea93df2f047f57c0cdcf27da5d7 (patch)
treef667be0c2d8617102c2cfa8eb13af4973f812c9b
parentcdbc514b4144b614b6ca296c0817828b6a20aa26 (diff)
intel_gpu_frequency: A tool to manipulate Intel GPU frequency
WARNING: very minimally tested In general you should not need this tool. Its primary purpose is for benchmarking, and for debugging performance issues. For many kernel releases now sysfs has supported reading and writing the GPU frequency. Therefore, this tool provides no new functionality. What it does provide is an easy to package (for distros) tool that handles the most common scenarios. v2: Get rid of -f from the usage message (Jordan) Add space before [-s (Jordan) Add a -c/--custom example (Jordan) Add a setting for resetting to hardware default (Ken) Replicate examples in commit message in the source code. (me) v3: Its not It's (me) Add --help/-h to usage Add Version + man page Rename tool to intel_gpu_frequency, from intel_frequency Remove "sudo" from the examples Signed-off-by: Ben Widawsky <ben@bwidawsk.net> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com> Cc: Kenneth Graunke <kenneth@whitecape.org> Here are some sample usages: $ intel_gpu_frequency --get=cur,min,max,eff cur: 200 MHz min: 200 MHz RP1: 200 MHz max: 1200 MHz $ intel_gpu_frequency -g cur: 200 MHz min: 200 MHz RP1: 200 MHz max: 1200 MHz $ intel_gpu_frequency -geff RP1: 200 MHz $ intel_gpu_frequency --set min=300 $ intel_gpu_frequency --get min cur: 300 MHz min: 300 MHz RP1: 200 MHz max: 1200 MHz $ intel_gpu_frequency --custom max=900 $ intel_gpu_frequency --get max cur: 300 MHz min: 300 MHz RP1: 200 MHz max: 900 MHz $ intel_gpu_frequency --max $ intel_gpu_frequency -g cur: 1200 MHz min: 1200 MHz RP1: 200 MHz max: 1200 MHz $ intel_gpu_frequency -e $ intel_gpu_frequency -g cur: 200 MHz min: 200 MHz RP1: 200 MHz max: 200 MHz $ intel_gpu_frequency --max $ intel_gpu_frequency -g cur: 1200 MHz min: 1200 MHz RP1: 200 MHz max: 1200 MHz $ intel_gpu_frequency --min $ intel_gpu_frequency -g cur: 200 MHz min: 200 MHz RP1: 200 MHz max: 200 MHz
-rw-r--r--NEWS2
-rw-r--r--man/Makefile.am1
-rw-r--r--man/intel_gpu_frequency.man58
-rw-r--r--tools/Makefile.sources1
-rw-r--r--tools/intel_gpu_frequency.c381
5 files changed, 443 insertions, 0 deletions
diff --git a/NEWS b/NEWS
index 143c361a..67146a3c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
Release 1.10 (XXXX-XX-XX)
-------------------------
+- New frequency manipulation tool (intel_gpu_frequency)
+
Release 1.9 (2014-12-12)
------------------------
diff --git a/man/Makefile.am b/man/Makefile.am
index 0d04f934..dcd79528 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -4,6 +4,7 @@ appman_PRE = \
intel_bios_dumper.man \
intel_bios_reader.man \
intel_error_decode.man \
+ intel_gpu_frequency.man \
intel_gpu_top.man \
intel_gtt.man \
intel_infoframes.man \
diff --git a/man/intel_gpu_frequency.man b/man/intel_gpu_frequency.man
new file mode 100644
index 00000000..60e4e0c8
--- /dev/null
+++ b/man/intel_gpu_frequency.man
@@ -0,0 +1,58 @@
+.TH INTEL_FREQUENCY: "1" "January 2015" "intel_gpu_frequency" "User Commands"
+.SH NAME
+intel_gpu_frequency: \- manual page for intel_gpu_frequency
+.SH SYNOPSIS
+.B intel_gpu_frequency
+[\fI\,-e\/\fR] [\fI\,--min | --max\/\fR] [\fI\,-g (min|max|efficient)\/\fR] [\fI\,-s frequency_mhz\/\fR]
+.SH DESCRIPTION
+\&A program to manipulate Intel GPU frequencies. Intel GPUs
+will automatically throttle the frequencies based on system demands, up when
+needed, down when not. This tool should only be used for debugging performance
+problems, or trying to get a stable frequency while benchmarking.
+
+Intel GPUs only accept specific frequencies. The tool may, or may not attempt to
+adjust requests to the proper frequency if they aren't correct. This may lead to
+non-obvious failures when setting frequency. Multiples of 50MHz is usually a
+safe bet.
+.SH OPTIONS
+.TP
+\fB\-e\fR
+Lock frequency to the most efficient frequency
+.TP
+\fB\-g\fR, \fB\-\-get=\fR
+Get the frequency comma separated list of ("cur"|"min"|"max"|"eff")
+.TP
+\fB\-s\fR, \fB\-\-set\fR
+Lock frequency to an absolute value (MHz)
+.TP
+\fB\-c\fR, \fB\-\-custom\fR
+Set a min, or max frequency "min=X | max=Y"
+.TP
+\fB\-m\fR \fB\-\-max\fR
+Lock frequency to max frequency
+.TP
+\fB\-i\fR \fB\-\-min\fR
+Lock frequency to min (never a good idea, DEBUG ONLY)
+.TP
+\fB\-d\fR \fB\-\-defaults\fR
+Return the system to hardware defaults
+.TP
+\fB\-h\fR \fB\-\-help\fR
+Returns this
+.HP
+\fB\-v\fR \fB\-\-version\fR Version
+.SH EXAMPLES
+.TP
+\fbintel_gpu_frequency \-gmin,cur\fR
+Get the current and minimum frequency
+.TP
+\fbintel_gpu_frequency \-s 400\fR
+Lock frequency to 400Mhz
+.TP
+\fbintel_gpu_frequency \-c max=750\fR
+Set the max frequency to 750MHz
+.PP
+.SH "REPORTING BUGS"
+Report bugs to https://bugs.freedesktop.org
+.SH COPYRIGHT
+Copyright (C) 2015 Intel Corporation
diff --git a/tools/Makefile.sources b/tools/Makefile.sources
index b85a6b88..b16af8a6 100644
--- a/tools/Makefile.sources
+++ b/tools/Makefile.sources
@@ -14,6 +14,7 @@ bin_PROGRAMS = \
intel_dump_decode \
intel_error_decode \
intel_forcewaked \
+ intel_gpu_frequency \
intel_framebuffer_dump \
intel_gpu_time \
intel_gpu_top \
diff --git a/tools/intel_gpu_frequency.c b/tools/intel_gpu_frequency.c
new file mode 100644
index 00000000..d9e7d228
--- /dev/null
+++ b/tools/intel_gpu_frequency.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Example:
+ * Get all frequencies:
+ * intel_gpu_frequency --get=cur,min,max,eff
+ *
+ * Same as above:
+ * intel_gpu_frequency -g
+ *
+ * Get the efficient frequency:
+ * intel_gpu_frequency -geff
+ *
+ * Lock the GPU frequency to 300MHz:
+ * intel_gpu_frequency --set min=300
+ *
+ * Set the maximum frequency to 900MHz:
+ * intel_gpu_frequency --custom max=900
+ *
+ * Lock the GPU frequency to its maximum frequency:
+ * intel_gpu_frequency --max
+ *
+ * Lock the GPU frequency to its most efficient frequency:
+ * intel_gpu_frequency -e
+ *
+ * Lock The GPU frequency to its minimum frequency:
+ * intel_gpu_frequency --min
+ *
+ * Reset the GPU to hardware defaults
+ * intel_gpu_frequency -d
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "drmtest.h"
+#include "intel_chipset.h"
+
+#define VERSION "1.0"
+
+static int device, devid;
+
+enum {
+ CUR=0,
+ MIN,
+ EFF,
+ MAX,
+ RP0,
+ RPn
+};
+
+struct freq_info {
+ const char *name;
+ const char *mode;
+ FILE *filp;
+ char *path;
+};
+
+static struct freq_info info[] = {
+ { "cur", "r" },
+ { "min", "rb+" },
+ { "RP1", "r" },
+ { "max", "rb+" },
+ { "RP0", "r" },
+ { "RPn", "r" }
+};
+
+static char *
+get_sysfs_path(const char *which)
+{
+ static const char fmt[] = "/sys/class/drm/card%1d/gt_%3s_freq_mhz";
+ char *path;
+ int ret;
+
+#define STATIC_STRLEN(string) (sizeof(string) / sizeof(string [0]))
+ ret = asprintf(&path, fmt, device, which);
+ assert(ret == (STATIC_STRLEN(fmt) - 3));
+#undef STATIC_STRLEN
+
+ return path;
+}
+
+static void
+initialize_freq_info(struct freq_info *freq_info)
+{
+ if (freq_info->filp)
+ return;
+
+ freq_info->path = get_sysfs_path(freq_info->name);
+ assert(freq_info->path);
+ freq_info->filp = fopen(freq_info->path, freq_info->mode);
+ assert(freq_info->filp);
+}
+
+static void wait_freq_settle(void)
+{
+ struct timespec ts;
+
+ /* FIXME: Lazy sleep without check. */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 20000;
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
+}
+
+static void set_frequency(struct freq_info *freq_info, int val)
+{
+ initialize_freq_info(freq_info);
+ rewind(freq_info->filp);
+ assert(fprintf(freq_info->filp, "%d", val) > 0);
+
+ wait_freq_settle();
+}
+
+static int get_frequency(struct freq_info *freq_info)
+{
+ int val;
+
+ initialize_freq_info(freq_info);
+ rewind(freq_info->filp);
+ assert(fscanf(freq_info->filp, "%d", &val)==1);
+
+ return val;
+}
+
+static void
+usage(const char *prog)
+{
+ printf("%s A program to manipulate Intel GPU frequencies.\n\n", prog);
+ printf("Usage: %s [-e] [--min | --max] [-g (min|max|efficient)] [-s frequency_mhz]\n\n", prog);
+ printf("Options: \n");
+ printf(" -e Lock frequency to the most efficient frequency\n");
+ printf(" -g, --get= Get the frequency (optional arg: \"cur\"|\"min\"|\"max\"|\"eff\")\n");
+ printf(" -s, --set Lock frequency to an absolute value (MHz)\n");
+ printf(" -c, --custom Set a min, or max frequency \"min=X | max=Y\"\n");
+ printf(" -m --max Lock frequency to max frequency\n");
+ printf(" -i --min Lock frequency to min (never a good idea, DEBUG ONLY)\n");
+ printf(" -d --defaults Return the system to hardware defaults\n");
+ printf(" -h --help Returns this\n");
+ printf(" -v --version Version\n");
+ printf("\n");
+ printf("Examples:\n");
+ printf(" intel_gpu_frequency -gmin,cur\tGet the current and minimum frequency\n");
+ printf(" intel_gpu_frequency -s 400\tLock frequency to 400Mhz\n");
+ printf(" intel_gpu_frequency -c max=750\tSet the max frequency to 750MHz\n");
+ printf("\n");
+ printf("Report bugs to <bugs.freedesktop.org>\n");
+ exit(EXIT_FAILURE);
+}
+
+static void
+version(const char *prog)
+{
+ printf("%s: %s\n", prog, VERSION);
+ printf("Copyright © 2015 Intel Corporation\n");
+}
+
+/* Returns read or write operation */
+static bool
+parse(int argc, char *argv[], bool *act_upon, int *new_freq)
+{
+ int c, tmp;
+ bool write = false;
+
+ char *token[] = {
+ (char *)info[CUR].name,
+ (char *)info[MIN].name,
+ (char *)"eff",
+ (char *)info[MAX].name
+ };
+
+ /* No args means -g" */
+ if (argc == 1) {
+ for (c = 0; c < ARRAY_SIZE(act_upon); c++)
+ act_upon[c] = true;
+ goto done;
+ }
+ while (1) {
+ int option_index = 0;
+ static struct option long_options[] = {
+ { "get", optional_argument, NULL, 'g' },
+ { "set", required_argument, NULL, 's' },
+ { "custom", required_argument, NULL, 'c'},
+ { "min", no_argument, NULL, 'i' },
+ { "max", no_argument, NULL, 'm' },
+ { "defaults", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0}
+ };
+
+ c = getopt_long(argc, argv, "eg::s:c:midh", long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'g':
+ if (write == true)
+ fprintf(stderr, "Read and write operations not support simultaneously.\n");
+
+ if (optarg) {
+ char *value, *subopts = optarg;
+ int x;
+ while (*subopts != '\0') {
+ x = getsubopt(&subopts, token, &value);
+ if (x == -1) {
+ fprintf(stderr, "Unrecognized option (%s)\n", value);
+ break;
+ } else
+ act_upon[x] = true;
+ }
+ } else {
+ int i;
+ for (i = 0; i < ARRAY_SIZE(act_upon); i++)
+ act_upon[i] = true;
+ }
+ break;
+ case 's':
+ if (!optarg)
+ usage(argv[0]);
+
+ if (write == true) {
+ fprintf(stderr, "Only one write may be specified at a time\n");
+ exit(EXIT_FAILURE);
+ }
+
+ write = true;
+ act_upon[MIN] = true;
+ act_upon[MAX] = true;
+ sscanf(optarg, "%d", &new_freq[MAX]);
+ new_freq[MIN] = new_freq[MAX];
+ break;
+ case 'c':
+ if (!optarg)
+ usage(argv[0]);
+
+ if (write == true) {
+ fprintf(stderr, "Only one write may be specified at a time\n");
+ exit(EXIT_FAILURE);
+ }
+
+ write = true;
+
+ if (!strncmp("min=", optarg, 4)) {
+ act_upon[MIN] = true;
+ sscanf(optarg+4, "%d", &new_freq[MIN]);
+ } else if (!strncmp("max=", optarg, 4)) {
+ act_upon[MAX] = true;
+ sscanf(optarg+4, "%d", &new_freq[MAX]);
+ } else {
+ fprintf(stderr, "Selected unmodifiable frequency\n");
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'e': /* efficient */
+ if (IS_VALLEYVIEW(devid) || IS_CHERRYVIEW(devid)) {
+ /* the LP parts have special efficient frequencies */
+ fprintf(stderr,
+ "FIXME: Warning efficient frequency information is incorrect.\n");
+ exit(EXIT_FAILURE);
+ }
+ tmp = get_frequency(&info[EFF]);
+ new_freq[MIN] = tmp;
+ new_freq[MAX] = tmp;
+ act_upon[MIN] = true;
+ act_upon[MAX] = true;
+ write = true;
+ break;
+ case 'i': /* mIn */
+ tmp = get_frequency(&info[RPn]);
+ new_freq[MIN] = tmp;
+ new_freq[MAX] = tmp;
+ act_upon[MIN] = true;
+ act_upon[MAX] = true;
+ write = true;
+ break;
+ case 'm': /* max */
+ tmp = get_frequency(&info[RP0]);
+ new_freq[MIN] = tmp;
+ new_freq[MAX] = tmp;
+ act_upon[MIN] = true;
+ act_upon[MAX] = true;
+ write = true;
+ break;
+ case 'd': /* defaults */
+ new_freq[MIN] = get_frequency(&info[RPn]);
+ new_freq[MAX] = get_frequency(&info[RP0]);
+ act_upon[MIN] = true;
+ act_upon[MAX] = true;
+ write = true;
+ break;
+ case 'v':
+ version(argv[0]);
+ exit(0);
+ case 'h':
+ default:
+ usage(argv[0]);
+ }
+ }
+
+done:
+ return write;
+}
+
+int main(int argc, char *argv[])
+{
+
+ bool write, fail, targets[MAX+1] = {false};
+ int i, try = 1, set_freq[MAX+1] = {0};
+
+ devid = intel_get_drm_devid(drm_open_any());
+ device = drm_get_card();
+
+ write = parse(argc, argv, targets, set_freq);
+ fail = write;
+
+ /* If we've previously locked the frequency, we need to make sure to set things
+ * in the correct order, or else the operation will fail (ie. min = max = 200,
+ * and we set min to 300, we fail because it would try to set min >
+ * max). This can be accomplished be going either forward or reverse
+ * through the loop. MIN is always before MAX.
+ *
+ * XXX: Since only min and max are at play, the super lazy way is to do this
+ * 3 times and if we still fail after 3, it's for real.
+ */
+again:
+ if (try > 2) {
+ fprintf(stderr, "Did not achieve desired freq.\n");
+ exit(EXIT_FAILURE);
+ }
+ for (i = 0; i < ARRAY_SIZE(targets); i++) {
+ if (targets[i] == false)
+ continue;
+
+ if (write) {
+ set_frequency(&info[i], set_freq[i]);
+ if (get_frequency(&info[i]) != set_freq[i])
+ fail = true;
+ else
+ fail = false;
+ } else {
+ printf("%s: %d MHz\n", info[i].name, get_frequency(&info[i]));
+ }
+ }
+
+ if (fail) {
+ try++;
+ goto again;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(targets); i++) {
+ if (info[i].filp) {
+ fclose(info[i].filp);
+ free(info[i].path);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}