On Thu, Jan 15, 2015 at 02:00:06PM +0000, Dave Gordon wrote: > On 12/01/15 03:35, Ben Widawsky wrote: > > WARNING: very minimally tested > > > > In general you should not need this tool. It's 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) > > > > Signed-off-by: Ben Widawsky <ben@xxxxxxxxxxxx> > > Reviewed-by: Jordan Justen <jordan.l.justen@xxxxxxxxx> > > Cc: Kenneth Graunke <kenneth@xxxxxxxxxxxxx> > > I think the syntax of some of these commands is rather strange; It all uses standard POSIX commands to parse them. If you think the names are bad, we can discuss that. > also, it would be nicer to have consistent naming of inputs & outputs - > specifically, the input name "eff" is related to the output tag "RP1". Please > pick one of the other. Yes, this was laziness feel free to fix it. > > And are all the examples below actually correct? > > > Here are some sample usages: > > $ sudo intel_frequency --get=cur,min,max,eff > > cur: 200 MHz > > min: 200 MHz > > RP1: 200 MHz > > max: 1200 MHz > > > > $ sudo intel_frequency -g > > cur: 200 MHz > > min: 200 MHz > > RP1: 200 MHz > > max: 1200 MHz > > > > $ sudo intel_frequency -geff > > RP1: 200 MHz > > I wouldn't expect this syntax to work, because generally single-letter > options can be combined, so that "-geff" would be equivalent to "-g -e > -f -f". I'd expect to have to write "-g eff" or "-g=eff". This is a limitation of getsubopt() which is in the process of being removed anyway, so I wouldn't worry about it. > > > $ sudo intel_frequency --set min=300 > > $ sudo intel_frequency --get min > > cur: 300 MHz > > min: 300 MHz > > RP1: 200 MHz > > max: 1200 MHz > > Surely if you specify "--get min" it should only print the "min" value, > not all the others; and the same with "--get max" below. (However, I > think you actually do need to show all values in these two examples to > clarify the effects of the parameter-setting commands, so I'd change > them to get rid of the parameter name). > > Here, it isn't really clear what the difference between --custom and > --set is supposed to be. Partly this is just down to the numbers in > these specific examples, but also I think the examples don't agree with > the code below. > > > $ sudo intel_frequency --custom max=900 > > $ sudo intel_frequency --get max > > cur: 300 MHz > > min: 300 MHz > > RP1: 200 MHz > > max: 900 MHz > > So from the code, I deduce that "--custom" lets you change either 'min' > or 'max', but doesn't /necessarily/ change the current frequency, only > if it becomes out-of-bounds. > > OTOH "--set" is supposed to set both min *and* max, thus locking the > frequency to a specific value. But the --set example above doesn't show > that. Also, the "min=300" syntax would be illogical - you just need a > number, and doesn't appear to be what the code actually expects. > Looks like I messed up the comment, but it's going away shortly anyway. There is a man page and help that describe the usage in more detail. I changed a bunch of this midflight, and probably forgot to update the comments. Hasn't anyone ever told you not to read comments before? If not, let me be the first :P > Finally, I think the choice of command names (especially --set vs > --custom) is confusing. I'd like to propose the following: > > # Reading: > --get # get all > --get namelist # get specified > -g [namelist] # synonym for -g --get is going to always get all. Initially I thought scripts could want to run this app, but it's probably better if they just read the sysfs directly, if the're scripting. Tim Gore already sent a patch which I've reviewed. > > # Set one parameter: > --set name=freq # set the specified parameter (min or max) > # may or may not affect 'cur' > -s name=freq # synonym > > # Reset (all) parameters: > --defaults # Reset all r/w parameters to h/w defaults > -d # synonym > > # Lock to specific frequency: > --max # Lock to (h/w defined) max > -m # synonym > --min # Lock to (h/w defined) min > -i # synonym > --eff # Lock to (h/w defined) RPe > -e # synonym > --lock freq # Lock to custom frequency > -l freq # synonym > > [Aside] > If the locking commands are implemented by setting both 'min' and 'max' > to the same value, as appears to be the case in the code below, then > defining "--max" and "--min to mean "lock to current value of max/min > parameter (as I was originally going to) would result in the sequence of > commands: > > $ sudo intel_frequency --lock 900 > $ sudo intel_frequency --max > > not having the expected effect - the second command would have no > effect, because max would have been changed to 900 by the first. So it's > better to for --min and --max to lock the frequency to the h/w default > values. > [/Aside] > > Only one command may be given per invocation. (It could be meaningful to > combine some permutations, but it would be complicated and/or confusing > and involve extra (arbitrary) decisions, such as whether options are > processed in command-line order or in fixed order, and hence whether or > not "-d -g -m" is equivalent to "-m -g -d", etc. So KISS). > > .Dave. > I honestly don't care too much as long as we don't roll our own parsing. It made sense to me to do it this way when I wrote it - note I didn't initially have --custom at all, it was added by request from Jordan. Feel free to submit a patch to change it to what you want. Opinions are like a$$holes, and everyone has one. Do realize again that the comments may not match the reality, and see Tim Gore's patch before you change anything. > > $ sudo intel_frequency --max > > $ sudo intel_frequency -g > > cur: 1200 MHz > > min: 1200 MHz > > RP1: 200 MHz > > max: 1200 MHz > > > > $ sudo intel_frequency -e > > $ sudo intel_frequency -g > > cur: 200 MHz > > min: 200 MHz > > RP1: 200 MHz > > max: 200 MHz > > > > $ sudo intel_frequency --max > > $ sudo intel_frequency -g > > cur: 1200 MHz > > min: 1200 MHz > > RP1: 200 MHz > > max: 1200 MHz > > > > $ sudo intel_frequency --min > > $ sudo intel_frequency -g > > cur: 200 MHz > > min: 200 MHz > > RP1: 200 MHz > > max: 200 MHz > > --- > > tools/Makefile.sources | 1 + > > tools/intel_frequency.c | 363 ++++++++++++++++++++++++++++++++++++++++++++++++ > > 2 files changed, 364 insertions(+) > > create mode 100644 tools/intel_frequency.c > > > > diff --git a/tools/Makefile.sources b/tools/Makefile.sources > > index b85a6b8..2bea389 100644 > > --- a/tools/Makefile.sources > > +++ b/tools/Makefile.sources > > @@ -14,6 +14,7 @@ bin_PROGRAMS = \ > > intel_dump_decode \ > > intel_error_decode \ > > intel_forcewaked \ > > + intel_frequency \ > > intel_framebuffer_dump \ > > intel_gpu_time \ > > intel_gpu_top \ > > diff --git a/tools/intel_frequency.c b/tools/intel_frequency.c > > new file mode 100644 > > index 0000000..59f3814 > > --- /dev/null > > +++ b/tools/intel_frequency.c > > @@ -0,0 +1,363 @@ > > +/* > > + * 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_frequency --get=cur,min,max,eff > > + * > > + * Same as above: > > + * intel_frequency -g > > + * > > + * Get the efficient frequency: > > + * intel_frequency -geff > > + * > > + * Lock the GPU frequency to 300MHz: > > + * intel_frequency --set min=300 > > + * > > + * Set the maximum frequency to 900MHz: > > + * intel_frequency --custom max=900 > > + * > > + * Lock the GPU frequency to its maximum frequency: > > + * intel_frequency --max > > + * > > + * Lock the GPU frequency to its most efficient frequency: > > + * intel_frequency -e > > + * > > + * Lock The GPU frequency to its minimum frequency: > > + * intel_frequency --min > > + * > > + * Reset the GPU to hardware defaults > > + * intel_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" > > + > > +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("Usage: %s [-e] [--min | --max] [-g (min|max|efficient)] [-s frequency_mhz]\n\n", prog); > > + printf("%s A program to manipulate Intel GPU frequencies.\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("Examples:\n"); > > + printf("\tintel_frequency -gmin,cur Get the current and minimum frequency\n"); > > + printf("\tintel_frequency -s 400 Lock frequency to 400Mhz\n"); > > + printf("\tintel_frequency -c max=750 Set the max frequency to 750MHz\n"); > > + exit(EXIT_FAILURE); > > +} > > + > > +/* 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' }, > > + { 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 '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; > > +} > > > _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx