From 2545eb6198e7e1ec50daa0cfc64a4cdfecf24ec9 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 15:01:32 -0400 Subject: Initial start of ktest.pl Originally named autotest.pl, but renamed to ktest.pl now because the autotest name is used by other projects. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl new file mode 100644 index 0000000..81fb2e4 --- /dev/null +++ b/tools/testing/ktest/ktest.pl @@ -0,0 +1,334 @@ +#!/usr/bin/perl -w + +use strict; +use IPC::Open2; +use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); +use FileHandle; + +$#ARGV >= 0 || die "usage: autotest.pl config-file\n"; + +$| = 1; + +my %opt; + +#default opts +$opt{"NUM_BUILDS"} = 5; +$opt{"DEFAULT_BUILD_TYPE"} = "randconfig"; +$opt{"MAKE_CMD"} = "make"; +$opt{"TIMEOUT"} = 50; +$opt{"TMP_DIR"} = "/tmp/autotest"; +$opt{"SLEEP_TIME"} = 60; # sleep time between tests + +my $version; +my $install_mods; +my $grub_number; +my $target; +my $make; + +sub read_config { + my ($config) = @_; + + open(IN, $config) || die "can't read file $config"; + + while () { + + # ignore blank lines and comments + next if (/^\s*$/ || /\s*\#/); + + if (/^\s*(\S+)\s*=\s*(.*?)\s*$/) { + my $lvalue = $1; + my $rvalue = $2; + + $opt{$lvalue} = $rvalue; + } + } + + close(IN); +} + +sub doprint { + print @_; + + if (defined($opt{"LOG_FILE"})) { + open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; + print OUT @_; + close(OUT); + } +} + +sub run_command { + my ($command) = @_; + my $redirect = ""; + + if (defined($opt{"LOG_FILE"})) { + $redirect = " >> $opt{LOG_FILE} 2>&1"; + } + + doprint "$command ... "; + `$command $redirect`; + + my $failed = $?; + + if ($failed) { + doprint "FAILED!\n"; + } else { + doprint "SUCCESS\n"; + } + + return $failed; +} + +my $timeout = $opt{"TIMEOUT"}; + +sub wait_for_input +{ + my ($fp, $time) = @_; + my $rin; + my $ready; + my $line; + my $ch; + + if (!defined($time)) { + $time = $timeout; + } + + $rin = ''; + vec($rin, fileno($fp), 1) = 1; + $ready = select($rin, undef, undef, $time); + + $line = ""; + + # try to read one char at a time + while (sysread $fp, $ch, 1) { + $line .= $ch; + last if ($ch eq "\n"); + } + + if (!length($line)) { + return undef; + } + + return $line; +} + +sub reboot { + run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; +} + +sub monitor { + my $flags; + my $booted = 0; + my $bug = 0; + my $pid; + my $doopen2 = 0; + + if ($doopen2) { + $pid = open2(\*IN, \*OUT, $opt{CONSOLE}); + if ($pid < 0) { + die "Failed to connect to the console"; + } + } else { + $pid = open(IN, "$opt{CONSOLE} |"); + } + + $flags = fcntl(IN, F_GETFL, 0) or + die "Can't get flags for the socket: $!\n"; + + $flags = fcntl(IN, F_SETFL, $flags | O_NONBLOCK) or + die "Can't set flags for the socket: $!\n"; + + my $line; + my $full_line = ""; + + doprint "Wait for monitor to settle down.\n"; + # read the monitor and wait for the system to calm down + do { + $line = wait_for_input(\*IN, 5); + } while (defined($line)); + + reboot; + + for (;;) { + + $line = wait_for_input(\*IN); + + last if (!defined($line)); + + doprint $line; + + # we are not guaranteed to get a full line + $full_line .= $line; + + if ($full_line =~ /login:/) { + $booted = 1; + } + + if ($full_line =~ /call trace:/i) { + $bug = 1; + } + + if ($line =~ /\n/) { + $full_line = ""; + } + } + + doprint "kill child process $pid\n"; + kill 2, $pid; + + print "closing!\n"; + close(IN); + + if (!$booted) { + die "failed - never got a boot prompt.\n"; + } + + if ($bug) { + die "failed - got a bug report\n"; + } +} + +sub install { + + if (run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}") { + die "failed to copy image"; + } + + if ($install_mods) { + my $modlib = "/lib/modules/$version"; + + if (run_command "ssh $target rm -rf $modlib") { + die "failed to remove old mods: $modlib"; + } + + if (run_command "scp -r $opt{TMP_DIR}/lib $target:/lib/modules/$version") { + die "failed to copy modules"; + } + } + +} + +sub build { + my ($type) = @_; + + unlink "$opt{OUTPUT_DIR}/.config"; + + run_command "$make mrproper"; + + # add something to distinguish this build + open(OUT, "> $opt{OUTPUT_DIR}/localversion") or die("Can't make localversion file"); + print OUT "$opt{LOCALVERSION}\n"; + close(OUT); + + if (run_command "$make $opt{$type}") { + die "failed make config"; + } + + if (defined($opt{"MIN_CONFIG"})) { + run_command "cat $opt{MIN_CONFIG} >> $opt{OUTPUT_DIR}/.config"; + run_command "yes '' | $make oldconfig"; + } + + if (run_command "$make $opt{BUILD_OPTIONS}") { + die "failed build"; + } +} + +read_config $ARGV[0]; + +# mandatory configs +die "MACHINE not defined\n" if (!defined($opt{"MACHINE"})); +die "SSH_USER not defined\n" if (!defined($opt{"SSH_USER"})); +die "BUILD_DIR not defined\n" if (!defined($opt{"BUILD_DIR"})); +die "OUTPUT_DIR not defined\n" if (!defined($opt{"OUTPUT_DIR"})); +die "BUILD_TARGET not defined\n" if (!defined($opt{"BUILD_TARGET"})); +die "POWER_CYCLE not defined\n" if (!defined($opt{"POWER_CYCLE"})); +die "CONSOLE not defined\n" if (!defined($opt{"CONSOLE"})); +die "LOCALVERSION not defined\n" if (!defined($opt{"LOCALVERSION"})); +die "GRUB_MENU not defined\n" if (!defined($opt{"GRUB_MENU"})); + +chdir $opt{"BUILD_DIR"} || die "can't change directory to $opt{BUILD_DIR}"; + +$target = "$opt{SSH_USER}\@$opt{MACHINE}"; + +doprint "\n\nSTARTING AUTOMATED TESTS\n"; + +doprint "Find grub menu ... "; +$grub_number = -1; +open(IN, "ssh $target cat /boot/grub/menu.lst |") + or die "unable to get menu.lst"; +while () { + if (/^\s*title\s+$opt{GRUB_MENU}\s*$/) { + $grub_number++; + last; + } elsif (/^\s*title\s/) { + $grub_number++; + } +} +close(IN); +die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}" + if ($grub_number < 0); +doprint "$grub_number\n"; + +$make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; + +# First we need to do is the builds +for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { + my $type = "BUILD_TYPE[$i]"; + + if (!defined($opt{$type})) { + $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; + } + + doprint "\n\n"; + doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; + + if ($opt{$type} ne "nobuild") { + build $type; + } + + # get the release name + doprint "$make kernelrelease ... "; + $version = `$make kernelrelease | tail -1`; + chomp($version); + doprint "$version\n"; + + # should we process modules? + $install_mods = 0; + open(IN, "$opt{OUTPUT_DIR}/.config") or die("Can't read config file"); + while () { + if (/CONFIG_MODULES(=y)?/) { + $install_mods = 1 if (defined($1)); + last; + } + } + close(IN); + + if ($install_mods) { + if (run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install") { + die "Failed to install modules"; + } + } else { + doprint "No modules needed\n"; + } + + install; + + monitor; + + doprint "\n\n*******************************************\n"; + doprint "*******************************************\n"; + doprint "** SUCCESS!!!! **\n"; + doprint "*******************************************\n"; + doprint "*******************************************\n"; + + # try to reboot normally + + if (run_command "ssh $target reboot") { + # nope? power cycle it. + run_command "$opt{POWER_CYCLE}"; + } + + sleep "$opt{SLEEP_TIME}"; +} + +exit 0; -- cgit v0.10.2 From 5c42fc5b975869e73bb8b6c279dd2da81eab5607 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:01 -0400 Subject: ktest: New features: noclean, dodie, poweroff on error and success Added dodie function to have a bit more control over die calls. BUILD_NOCLEAN to not run make mrproper or remove .config. POWEROFF_ON_{SUCCESS,ERROR} to turn off the power after tests. Skip backtrace calls that were done by the backtrace tests. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 81fb2e4..1acb0e1 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -18,12 +18,16 @@ $opt{"MAKE_CMD"} = "make"; $opt{"TIMEOUT"} = 50; $opt{"TMP_DIR"} = "/tmp/autotest"; $opt{"SLEEP_TIME"} = 60; # sleep time between tests +$opt{"BUILD_NOCLEAN"} = 0; +$opt{"POWEROFF_ON_ERROR"} = 0; +$opt{"POWEROFF_ON_SUCCESS"} = 0; my $version; my $install_mods; my $grub_number; my $target; my $make; +my $noclean; sub read_config { my ($config) = @_; @@ -56,6 +60,16 @@ sub doprint { } } +sub dodie { + doprint "CRITICAL FAILURE... ", @_; + + if ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) { + doprint "POWERING OFF\n"; + `$opt{"POWER_OFF"}`; + } + die @_; +} + sub run_command { my ($command) = @_; my $redirect = ""; @@ -121,21 +135,22 @@ sub monitor { my $bug = 0; my $pid; my $doopen2 = 0; + my $skip_call_trace = 0; if ($doopen2) { $pid = open2(\*IN, \*OUT, $opt{CONSOLE}); if ($pid < 0) { - die "Failed to connect to the console"; + dodie "Failed to connect to the console"; } } else { $pid = open(IN, "$opt{CONSOLE} |"); } $flags = fcntl(IN, F_GETFL, 0) or - die "Can't get flags for the socket: $!\n"; + dodie "Can't get flags for the socket: $!\n"; $flags = fcntl(IN, F_SETFL, $flags | O_NONBLOCK) or - die "Can't set flags for the socket: $!\n"; + dodie "Can't set flags for the socket: $!\n"; my $line; my $full_line = ""; @@ -163,7 +178,19 @@ sub monitor { $booted = 1; } + if ($full_line =~ /\[ backtrace testing \]/) { + $skip_call_trace = 1; + } + if ($full_line =~ /call trace:/i) { + $bug = 1 if (!$skip_call_trace); + } + + if ($full_line =~ /\[ end of backtrace testing \]/) { + $skip_call_trace = 0; + } + + if ($full_line =~ /Kernel panic -/) { $bug = 1; } @@ -179,57 +206,90 @@ sub monitor { close(IN); if (!$booted) { - die "failed - never got a boot prompt.\n"; + dodie "failed - never got a boot prompt.\n"; } if ($bug) { - die "failed - got a bug report\n"; + dodie "failed - got a bug report\n"; } } sub install { if (run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}") { - die "failed to copy image"; + dodie "failed to copy image"; } if ($install_mods) { my $modlib = "/lib/modules/$version"; + my $modtar = "autotest-mods.tar.bz2"; if (run_command "ssh $target rm -rf $modlib") { - die "failed to remove old mods: $modlib"; + dodie "failed to remove old mods: $modlib"; + } + + # would be nice if scp -r did not follow symbolic links + if (run_command "cd $opt{TMP_DIR}; tar -cjf $modtar lib/modules/$version") { + dodie "making tarball"; } - if (run_command "scp -r $opt{TMP_DIR}/lib $target:/lib/modules/$version") { - die "failed to copy modules"; + if (run_command "scp $opt{TMP_DIR}/$modtar $target:/tmp") { + dodie "failed to copy modules"; + } + + unlink "$opt{TMP_DIR}/$modtar"; + + if (run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'") { + dodie "failed to tar modules"; } + + run_command "ssh $target rm -f /tmp/$modtar"; } } sub build { my ($type) = @_; + my $defconfig = ""; + my $append = ""; + + # old config can ask questions + if ($type eq "oldconfig") { + $append = "yes ''|"; + if (run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp") { + dodie "moving .config"; + } - unlink "$opt{OUTPUT_DIR}/.config"; + if (!$noclean && run_command "$make mrproper") { + dodie "make mrproper"; + } - run_command "$make mrproper"; + if (run_command "mv $opt{OUTPUT_DIR}/config_temp $opt{OUTPUT_DIR}/.config") { + dodie "moving config_temp"; + } + + } elsif (!$noclean) { + unlink "$opt{OUTPUT_DIR}/.config"; + if (run_command "$make mrproper") { + dodie "make mrproper"; + } + } # add something to distinguish this build - open(OUT, "> $opt{OUTPUT_DIR}/localversion") or die("Can't make localversion file"); + open(OUT, "> $opt{OUTPUT_DIR}/localversion") or dodie("Can't make localversion file"); print OUT "$opt{LOCALVERSION}\n"; close(OUT); - if (run_command "$make $opt{$type}") { - die "failed make config"; + if (defined($opt{"MIN_CONFIG"})) { + $defconfig = "KCONFIG_ALLCONFIG=$opt{MIN_CONFIG}"; } - if (defined($opt{"MIN_CONFIG"})) { - run_command "cat $opt{MIN_CONFIG} >> $opt{OUTPUT_DIR}/.config"; - run_command "yes '' | $make oldconfig"; + if (run_command "$defconfig $append $make $type") { + dodie "failed make config"; } if (run_command "$make $opt{BUILD_OPTIONS}") { - die "failed build"; + dodie "failed build"; } } @@ -275,6 +335,13 @@ $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { my $type = "BUILD_TYPE[$i]"; + if (defined($opt{"BUILD_NOCLEAN[$i]"}) && + $opt{"BUILD_NOCLEAN[$i]"} != 0) { + $noclean = 1; + } else { + $noclean = $opt{"BUILD_NOCLEAN"}; + } + if (!defined($opt{$type})) { $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; } @@ -283,7 +350,7 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; if ($opt{$type} ne "nobuild") { - build $type; + build $opt{$type}; } # get the release name @@ -294,7 +361,7 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { # should we process modules? $install_mods = 0; - open(IN, "$opt{OUTPUT_DIR}/.config") or die("Can't read config file"); + open(IN, "$opt{OUTPUT_DIR}/.config") or dodie("Can't read config file"); while () { if (/CONFIG_MODULES(=y)?/) { $install_mods = 1 if (defined($1)); @@ -305,7 +372,7 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { if ($install_mods) { if (run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install") { - die "Failed to install modules"; + dodie "Failed to install modules"; } } else { doprint "No modules needed\n"; @@ -331,4 +398,10 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { sleep "$opt{SLEEP_TIME}"; } +if ($opt{"POWEROFF_ON_SUCCESS"}) { + if (run_command "ssh $target halt" && defined($opt{"POWER_OFF"})) { + # nope? the zap it! + run_command "$opt{POWER_OFF}"; + } +} exit 0; -- cgit v0.10.2 From 75c3fda79e97b1e2c390e3623f19476e1e78ca0c Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:21 -0400 Subject: ktest: New features reboot on error, make options REBOOT_ON_ERROR to reboot the box on error BUILD_OPTIONS to add options to the make build (like -j40) Added "useconfig:". Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 1acb0e1..79da57f 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -19,8 +19,10 @@ $opt{"TIMEOUT"} = 50; $opt{"TMP_DIR"} = "/tmp/autotest"; $opt{"SLEEP_TIME"} = 60; # sleep time between tests $opt{"BUILD_NOCLEAN"} = 0; +$opt{"REBOOT_ON_ERROR"} = 0; $opt{"POWEROFF_ON_ERROR"} = 0; $opt{"POWEROFF_ON_SUCCESS"} = 0; +$opt{"BUILD_OPTIONS"} = ""; my $version; my $install_mods; @@ -63,10 +65,15 @@ sub doprint { sub dodie { doprint "CRITICAL FAILURE... ", @_; - if ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) { + if ($opt{"REBOOT_ON_ERROR"}) { + doprint "REBOOTING\n"; + `$opt{"POWER_CYCLE"}`; + + } elsif ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) { doprint "POWERING OFF\n"; `$opt{"POWER_OFF"}`; } + die @_; } @@ -125,7 +132,7 @@ sub wait_for_input return $line; } -sub reboot { +sub reboot_to { run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; } @@ -138,7 +145,7 @@ sub monitor { my $skip_call_trace = 0; if ($doopen2) { - $pid = open2(\*IN, \*OUT, $opt{CONSOLE}); + $pid = open2(\*IN, \*OUT, $opt{"CONSOLE"}); if ($pid < 0) { dodie "Failed to connect to the console"; } @@ -161,7 +168,7 @@ sub monitor { $line = wait_for_input(\*IN, 5); } while (defined($line)); - reboot; + reboot_to; for (;;) { @@ -229,7 +236,7 @@ sub install { } # would be nice if scp -r did not follow symbolic links - if (run_command "cd $opt{TMP_DIR}; tar -cjf $modtar lib/modules/$version") { + if (run_command "cd $opt{TMP_DIR} && tar -cjf $modtar lib/modules/$version") { dodie "making tarball"; } @@ -253,9 +260,20 @@ sub build { my $defconfig = ""; my $append = ""; + if ($type =~ /^useconfig:(.*)/) { + if (run_command "cp $1 $opt{OUTPUT_DIR}/.config") { + dodie "could not copy $1 to .config"; + } + $type = "oldconfig"; + } + # old config can ask questions if ($type eq "oldconfig") { $append = "yes ''|"; + + # allow for empty configs + run_command "touch $opt{OUTPUT_DIR}/.config"; + if (run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp") { dodie "moving .config"; } @@ -293,6 +311,21 @@ sub build { } } +sub reboot { + # try to reboot normally + if (run_command "ssh $target reboot") { + # nope? power cycle it. + run_command "$opt{POWER_CYCLE}"; + } +} + +sub halt { + if ((run_command "ssh $target halt") or defined($opt{"POWER_OFF"})) { + # nope? the zap it! + run_command "$opt{POWER_OFF}"; + } +} + read_config $ARGV[0]; # mandatory configs @@ -301,6 +334,7 @@ die "SSH_USER not defined\n" if (!defined($opt{"SSH_USER"})); die "BUILD_DIR not defined\n" if (!defined($opt{"BUILD_DIR"})); die "OUTPUT_DIR not defined\n" if (!defined($opt{"OUTPUT_DIR"})); die "BUILD_TARGET not defined\n" if (!defined($opt{"BUILD_TARGET"})); +die "TARGET_IMAGE not defined\n" if (!defined($opt{"TARGET_IMAGE"})); die "POWER_CYCLE not defined\n" if (!defined($opt{"POWER_CYCLE"})); die "CONSOLE not defined\n" if (!defined($opt{"CONSOLE"})); die "LOCALVERSION not defined\n" if (!defined($opt{"LOCALVERSION"})); @@ -388,20 +422,16 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { doprint "*******************************************\n"; doprint "*******************************************\n"; - # try to reboot normally - - if (run_command "ssh $target reboot") { - # nope? power cycle it. - run_command "$opt{POWER_CYCLE}"; + if ($i != $opt{"NUM_BUILDS"}) { + reboot; + sleep "$opt{SLEEP_TIME}"; } - - sleep "$opt{SLEEP_TIME}"; } if ($opt{"POWEROFF_ON_SUCCESS"}) { - if (run_command "ssh $target halt" && defined($opt{"POWER_OFF"})) { - # nope? the zap it! - run_command "$opt{POWER_OFF}"; - } + halt; +} else { + reboot; } + exit 0; -- cgit v0.10.2 From 5f9b6ced04a4e9c3ee6b4d4042ac5935ef5a8dbd Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:33 -0400 Subject: ktest: Bisecting, install modules, add logging Added bisecting, modules, logging of the output. Banners that show success. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 79da57f..a34f6f4 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -25,11 +25,13 @@ $opt{"POWEROFF_ON_SUCCESS"} = 0; $opt{"BUILD_OPTIONS"} = ""; my $version; -my $install_mods; my $grub_number; my $target; my $make; my $noclean; +my $minconfig; +my $in_bisect = 0; +my $bisect_bad = ""; sub read_config { my ($config) = @_; @@ -52,9 +54,7 @@ sub read_config { close(IN); } -sub doprint { - print @_; - +sub logit { if (defined($opt{"LOG_FILE"})) { open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; print OUT @_; @@ -62,6 +62,11 @@ sub doprint { } } +sub doprint { + print @_; + logit @_; +} + sub dodie { doprint "CRITICAL FAILURE... ", @_; @@ -96,7 +101,30 @@ sub run_command { doprint "SUCCESS\n"; } - return $failed; + return !$failed; +} + +sub get_grub_index { + + return if ($grub_number >= 0); + + doprint "Find grub menu ... "; + $grub_number = -1; + open(IN, "ssh $target cat /boot/grub/menu.lst |") + or die "unable to get menu.lst"; + while () { + if (/^\s*title\s+$opt{GRUB_MENU}\s*$/) { + $grub_number++; + last; + } elsif (/^\s*title\s/) { + $grub_number++; + } + } + close(IN); + + die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}" + if ($grub_number < 0); + doprint "$grub_number\n"; } my $timeout = $opt{"TIMEOUT"}; @@ -213,46 +241,63 @@ sub monitor { close(IN); if (!$booted) { + return 1 if (!$in_bisect); dodie "failed - never got a boot prompt.\n"; } if ($bug) { + return 1 if (!$in_bisect); dodie "failed - got a bug report\n"; } + + return 0; } sub install { - if (run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}") { + run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}" or dodie "failed to copy image"; - } - if ($install_mods) { - my $modlib = "/lib/modules/$version"; - my $modtar = "autotest-mods.tar.bz2"; + my $install_mods = 0; - if (run_command "ssh $target rm -rf $modlib") { - dodie "failed to remove old mods: $modlib"; + # should we process modules? + $install_mods = 0; + open(IN, "$opt{OUTPUT_DIR}/.config") or dodie("Can't read config file"); + while () { + if (/CONFIG_MODULES(=y)?/) { + $install_mods = 1 if (defined($1)); + last; } + } + close(IN); - # would be nice if scp -r did not follow symbolic links - if (run_command "cd $opt{TMP_DIR} && tar -cjf $modtar lib/modules/$version") { - dodie "making tarball"; - } + if (!$install_mods) { + doprint "No modules needed\n"; + return; + } - if (run_command "scp $opt{TMP_DIR}/$modtar $target:/tmp") { - dodie "failed to copy modules"; - } + run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install" or + dodie "Failed to install modules"; - unlink "$opt{TMP_DIR}/$modtar"; + my $modlib = "/lib/modules/$version"; + my $modtar = "autotest-mods.tar.bz2"; - if (run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'") { - dodie "failed to tar modules"; - } + run_command "ssh $target rm -rf $modlib" or + dodie "failed to remove old mods: $modlib"; - run_command "ssh $target rm -f /tmp/$modtar"; - } + # would be nice if scp -r did not follow symbolic links + run_command "cd $opt{TMP_DIR} && tar -cjf $modtar lib/modules/$version" or + dodie "making tarball"; + + run_command "scp $opt{TMP_DIR}/$modtar $target:/tmp" or + dodie "failed to copy modules"; + + unlink "$opt{TMP_DIR}/$modtar"; + + run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'" or + dodie "failed to tar modules"; + run_command "ssh $target rm -f /tmp/$modtar"; } sub build { @@ -261,9 +306,9 @@ sub build { my $append = ""; if ($type =~ /^useconfig:(.*)/) { - if (run_command "cp $1 $opt{OUTPUT_DIR}/.config") { + run_command "cp $1 $opt{OUTPUT_DIR}/.config" or dodie "could not copy $1 to .config"; - } + $type = "oldconfig"; } @@ -274,23 +319,20 @@ sub build { # allow for empty configs run_command "touch $opt{OUTPUT_DIR}/.config"; - if (run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp") { + run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp" or dodie "moving .config"; - } - if (!$noclean && run_command "$make mrproper") { + if (!$noclean && !run_command "$make mrproper") { dodie "make mrproper"; } - if (run_command "mv $opt{OUTPUT_DIR}/config_temp $opt{OUTPUT_DIR}/.config") { + run_command "mv $opt{OUTPUT_DIR}/config_temp $opt{OUTPUT_DIR}/.config" or dodie "moving config_temp"; - } } elsif (!$noclean) { unlink "$opt{OUTPUT_DIR}/.config"; - if (run_command "$make mrproper") { + run_command "$make mrproper" or dodie "make mrproper"; - } } # add something to distinguish this build @@ -298,34 +340,162 @@ sub build { print OUT "$opt{LOCALVERSION}\n"; close(OUT); - if (defined($opt{"MIN_CONFIG"})) { - $defconfig = "KCONFIG_ALLCONFIG=$opt{MIN_CONFIG}"; + if (defined($minconfig)) { + $defconfig = "KCONFIG_ALLCONFIG=$minconfig"; } - if (run_command "$defconfig $append $make $type") { + run_command "$defconfig $append $make $type" or dodie "failed make config"; - } - if (run_command "$make $opt{BUILD_OPTIONS}") { + if (!run_command "$make $opt{BUILD_OPTIONS}") { + # bisect may need this to pass + return 1 if ($in_bisect); dodie "failed build"; } + + return 0; } sub reboot { # try to reboot normally - if (run_command "ssh $target reboot") { + if (!run_command "ssh $target reboot") { # nope? power cycle it. run_command "$opt{POWER_CYCLE}"; } } sub halt { - if ((run_command "ssh $target halt") or defined($opt{"POWER_OFF"})) { + if (!run_command "ssh $target halt" or defined($opt{"POWER_OFF"})) { # nope? the zap it! run_command "$opt{POWER_OFF}"; } } +sub success { + my ($i) = @_; + + doprint "\n\n*******************************************\n"; + doprint "*******************************************\n"; + doprint "** SUCCESS!!!! **\n"; + doprint "*******************************************\n"; + doprint "*******************************************\n"; + + if ($i != $opt{"NUM_BUILDS"}) { + reboot; + doprint "Sleeping $opt{SLEEP_TIME} seconds\n"; + sleep "$opt{SLEEP_TIME}"; + } +} + +sub get_version { + # get the release name + doprint "$make kernelrelease ... "; + $version = `$make kernelrelease | tail -1`; + chomp($version); + doprint "$version\n"; +} + +sub run_bisect { + my ($type) = @_; + + my $failed; + my $result; + my $output; + my $ret; + + + if (defined($minconfig)) { + $failed = build "useconfig:$minconfig"; + } else { + # ?? no config to use? + $failed = build "oldconfig"; + } + + if ($type ne "build") { + dodie "Failed on build" if $failed; + + # Now boot the box + get_grub_index; + get_version; + install; + $failed = monitor; + + if ($type ne "boot") { + dodie "Failed on boot" if $failed; + } + } + + if ($failed) { + $result = "bad"; + } else { + $result = "good"; + } + + doprint "git bisect $result ... "; + $output = `git bisect $result 2>&1`; + $ret = $?; + + logit $output; + + if ($ret) { + doprint "FAILED\n"; + dodie "Failed to git bisect"; + } + + doprint "SUCCESS\n"; + if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\)) \[([[:xdigit:]]+)\]/) { + doprint "$1 [$2]\n"; + } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) { + $bisect_bad = $1; + doprint "Found bad commit... $1\n"; + return 0; + } + + + return 1; +} + +sub bisect { + my ($i) = @_; + + my $result; + + die "BISECT_GOOD[$i] not defined\n" if (!defined($opt{"BISECT_GOOD[$i]"})); + die "BISECT_BAD[$i] not defined\n" if (!defined($opt{"BISECT_BAD[$i]"})); + die "BISECT_TYPE[$i] not defined\n" if (!defined($opt{"BISECT_TYPE[$i]"})); + + my $good = $opt{"BISECT_GOOD[$i]"}; + my $bad = $opt{"BISECT_BAD[$i]"}; + my $type = $opt{"BISECT_TYPE[$i]"}; + + $in_bisect = 1; + + run_command "git bisect start" or + dodie "could not start bisect"; + + run_command "git bisect good $good" or + dodie "could not set bisect good to $good"; + + run_command "git bisect bad $bad" or + dodie "could not set bisect good to $bad"; + + do { + $result = run_bisect $type; + } while ($result); + + run_command "git bisect log" or + dodie "could not capture git bisect log"; + + run_command "git bisect reset" or + dodie "could not reset git bisect"; + + doprint "Bad commit was [$bisect_bad]\n"; + + $in_bisect = 0; + + success $i; +} + read_config $ARGV[0]; # mandatory configs @@ -346,22 +516,6 @@ $target = "$opt{SSH_USER}\@$opt{MACHINE}"; doprint "\n\nSTARTING AUTOMATED TESTS\n"; -doprint "Find grub menu ... "; -$grub_number = -1; -open(IN, "ssh $target cat /boot/grub/menu.lst |") - or die "unable to get menu.lst"; -while () { - if (/^\s*title\s+$opt{GRUB_MENU}\s*$/) { - $grub_number++; - last; - } elsif (/^\s*title\s/) { - $grub_number++; - } -} -close(IN); -die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}" - if ($grub_number < 0); -doprint "$grub_number\n"; $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; @@ -376,6 +530,14 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { $noclean = $opt{"BUILD_NOCLEAN"}; } + if (defined($opt{"MIN_CONFIG[$i]"})) { + $minconfig = $opt{"MIN_CONFIG[$i]"}; + } elsif (defined($opt{"MIN_CONFIG"})) { + $minconfig = $opt{"MIN_CONFIG"}; + } else { + undef $minconfig; + } + if (!defined($opt{$type})) { $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; } @@ -383,49 +545,20 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { doprint "\n\n"; doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; - if ($opt{$type} ne "nobuild") { - build $opt{$type}; - } - - # get the release name - doprint "$make kernelrelease ... "; - $version = `$make kernelrelease | tail -1`; - chomp($version); - doprint "$version\n"; - - # should we process modules? - $install_mods = 0; - open(IN, "$opt{OUTPUT_DIR}/.config") or dodie("Can't read config file"); - while () { - if (/CONFIG_MODULES(=y)?/) { - $install_mods = 1 if (defined($1)); - last; - } + if ($opt{$type} eq "bisect") { + bisect $i; + next; } - close(IN); - if ($install_mods) { - if (run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install") { - dodie "Failed to install modules"; - } - } else { - doprint "No modules needed\n"; + if ($opt{$type} ne "nobuild") { + build $opt{$type}; } + get_grub_index; + get_version; install; - monitor; - - doprint "\n\n*******************************************\n"; - doprint "*******************************************\n"; - doprint "** SUCCESS!!!! **\n"; - doprint "*******************************************\n"; - doprint "*******************************************\n"; - - if ($i != $opt{"NUM_BUILDS"}) { - reboot; - sleep "$opt{SLEEP_TIME}"; - } + success $i; } if ($opt{"POWEROFF_ON_SUCCESS"}) { -- cgit v0.10.2 From 5a391fbff8755592eb080784ef32ff818d2daa44 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:43 -0400 Subject: ktest: Added better console, add test build Better reading of the console. Added running a script to do testing after build succeeds. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index a34f6f4..9eaf8d0 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -23,6 +23,7 @@ $opt{"REBOOT_ON_ERROR"} = 0; $opt{"POWEROFF_ON_ERROR"} = 0; $opt{"POWEROFF_ON_SUCCESS"} = 0; $opt{"BUILD_OPTIONS"} = ""; +$opt{"BISECT_SLEEP_TIME"} = 10; # sleep time between bisects my $version; my $grub_number; @@ -32,6 +33,7 @@ my $noclean; my $minconfig; my $in_bisect = 0; my $bisect_bad = ""; +my $run_test; sub read_config { my ($config) = @_; @@ -68,7 +70,7 @@ sub doprint { } sub dodie { - doprint "CRITICAL FAILURE... ", @_; + doprint "CRITICAL FAILURE... ", @_, "\n"; if ($opt{"REBOOT_ON_ERROR"}) { doprint "REBOOTING\n"; @@ -84,14 +86,14 @@ sub dodie { sub run_command { my ($command) = @_; - my $redirect = ""; + my $redirect_log = ""; if (defined($opt{"LOG_FILE"})) { - $redirect = " >> $opt{LOG_FILE} 2>&1"; + $redirect_log = " >> $opt{LOG_FILE} 2>&1"; } doprint "$command ... "; - `$command $redirect`; + `$command $redirect_log`; my $failed = $?; @@ -106,7 +108,7 @@ sub run_command { sub get_grub_index { - return if ($grub_number >= 0); + return if (defined($grub_number)); doprint "Find grub menu ... "; $grub_number = -1; @@ -164,28 +166,40 @@ sub reboot_to { run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; } -sub monitor { +sub open_console { + my ($fp) = @_; + my $flags; + + my $pid = open($fp, "$opt{CONSOLE}|") or + dodie "Can't open console $opt{CONSOLE}"; + + $flags = fcntl($fp, F_GETFL, 0) or + dodie "Can't get flags for the socket: $!\n"; + $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or + dodie "Can't set flags for the socket: $!\n"; + + return $pid; +} + +sub close_console { + my ($fp, $pid) = @_; + + doprint "kill child process $pid\n"; + kill 2, $pid; + + print "closing!\n"; + close($fp); +} + +sub monitor { my $booted = 0; my $bug = 0; my $pid; - my $doopen2 = 0; my $skip_call_trace = 0; + my $fp = \*IN; - if ($doopen2) { - $pid = open2(\*IN, \*OUT, $opt{"CONSOLE"}); - if ($pid < 0) { - dodie "Failed to connect to the console"; - } - } else { - $pid = open(IN, "$opt{CONSOLE} |"); - } - - $flags = fcntl(IN, F_GETFL, 0) or - dodie "Can't get flags for the socket: $!\n"; - - $flags = fcntl(IN, F_SETFL, $flags | O_NONBLOCK) or - dodie "Can't set flags for the socket: $!\n"; + $pid = open_console($fp); my $line; my $full_line = ""; @@ -193,14 +207,14 @@ sub monitor { doprint "Wait for monitor to settle down.\n"; # read the monitor and wait for the system to calm down do { - $line = wait_for_input(\*IN, 5); + $line = wait_for_input($fp, 5); } while (defined($line)); reboot_to; for (;;) { - $line = wait_for_input(\*IN); + $line = wait_for_input($fp); last if (!defined($line)); @@ -234,19 +248,15 @@ sub monitor { } } - doprint "kill child process $pid\n"; - kill 2, $pid; - - print "closing!\n"; - close(IN); + close_console($fp, $pid); if (!$booted) { - return 1 if (!$in_bisect); + return 1 if ($in_bisect); dodie "failed - never got a boot prompt.\n"; } if ($bug) { - return 1 if (!$in_bisect); + return 1 if ($in_bisect); dodie "failed - got a bug report\n"; } @@ -395,6 +405,84 @@ sub get_version { doprint "$version\n"; } +sub child_run_test { + my $failed; + + $failed = !run_command $run_test; + exit $failed; +} + +my $child_done; + +sub child_finished { + $child_done = 1; +} + +sub do_run_test { + my $child_pid; + my $child_exit; + my $pid; + my $line; + my $full_line; + my $bug = 0; + my $fp = \*IN; + + $pid = open_console($fp); + + # read the monitor and wait for the system to calm down + do { + $line = wait_for_input($fp, 1); + } while (defined($line)); + + $child_done = 0; + + $SIG{CHLD} = qw(child_finished); + + $child_pid = fork; + + child_run_test if (!$child_pid); + + $full_line = ""; + + do { + $line = wait_for_input($fp, 1); + if (defined($line)) { + + # we are not guaranteed to get a full line + $full_line .= $line; + + if ($full_line =~ /call trace:/i) { + $bug = 1; + } + + if ($full_line =~ /Kernel panic -/) { + $bug = 1; + } + + if ($line =~ /\n/) { + $full_line = ""; + } + } + } while (!$child_done && !$bug); + + if ($bug) { + doprint "Detected kernel crash!\n"; + # kill the child with extreme prejudice + kill 9, $child_pid; + } + + waitpid $child_pid, 0; + $child_exit = $?; + + close_console($fp, $pid); + + if ($bug || $child_exit) { + return 1 if $in_bisect; + dodie "test failed"; + } + return 0; +} + sub run_bisect { my ($type) = @_; @@ -422,11 +510,20 @@ sub run_bisect { if ($type ne "boot") { dodie "Failed on boot" if $failed; + + $failed = do_run_test; } } if ($failed) { $result = "bad"; + + # reboot the box to a good kernel + if ($type eq "boot") { + reboot; + doprint "sleep a little for reboot\n"; + sleep $opt{"BISECT_SLEEP_TIME"}; + } } else { $result = "good"; } @@ -443,12 +540,15 @@ sub run_bisect { } doprint "SUCCESS\n"; - if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\)) \[([[:xdigit:]]+)\]/) { + if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\))\s+\[([[:xdigit:]]+)\]/) { doprint "$1 [$2]\n"; } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) { $bisect_bad = $1; doprint "Found bad commit... $1\n"; return 0; + } else { + # we already logged it, just print it now. + print $output; } @@ -479,6 +579,11 @@ sub bisect { run_command "git bisect bad $bad" or dodie "could not set bisect good to $bad"; + # Can't have a test without having a test to run + if ($type eq "test" && !defined($run_test)) { + $type = "boot"; + } + do { $result = run_bisect $type; } while ($result); @@ -519,29 +624,30 @@ doprint "\n\nSTARTING AUTOMATED TESTS\n"; $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; -# First we need to do is the builds -for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { - my $type = "BUILD_TYPE[$i]"; +sub set_build_option { + my ($name, $i) = @_; - if (defined($opt{"BUILD_NOCLEAN[$i]"}) && - $opt{"BUILD_NOCLEAN[$i]"} != 0) { - $noclean = 1; - } else { - $noclean = $opt{"BUILD_NOCLEAN"}; - } + my $option = "$name\[$i\]"; - if (defined($opt{"MIN_CONFIG[$i]"})) { - $minconfig = $opt{"MIN_CONFIG[$i]"}; - } elsif (defined($opt{"MIN_CONFIG"})) { - $minconfig = $opt{"MIN_CONFIG"}; - } else { - undef $minconfig; + if (defined($opt{$option})) { + return $opt{$option}; } - if (!defined($opt{$type})) { - $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; + if (defined($opt{$name})) { + return $opt{$name}; } + return undef; +} + +# First we need to do is the builds +for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { + my $type = "BUILD_TYPE[$i]"; + + $noclean = set_build_option("BUILD_NOCLEAN", $i); + $minconfig = set_build_option("MIN_CONFIG", $i); + $run_test = set_build_option("TEST", $i); + doprint "\n\n"; doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; @@ -558,6 +664,11 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { get_version; install; monitor; + + if (defined($run_test)) { + do_run_test; + } + success $i; } -- cgit v0.10.2 From 1a5cfce344711a541aa63bdff81a58db35e20564 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:51 -0400 Subject: ktest: Added reboot on success Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 9eaf8d0..f344fd0 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -21,6 +21,7 @@ $opt{"SLEEP_TIME"} = 60; # sleep time between tests $opt{"BUILD_NOCLEAN"} = 0; $opt{"REBOOT_ON_ERROR"} = 0; $opt{"POWEROFF_ON_ERROR"} = 0; +$opt{"REBOOT_ON_SUCCESS"} = 1; $opt{"POWEROFF_ON_SUCCESS"} = 0; $opt{"BUILD_OPTIONS"} = ""; $opt{"BISECT_SLEEP_TIME"} = 10; # sleep time between bisects @@ -644,6 +645,10 @@ sub set_build_option { for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { my $type = "BUILD_TYPE[$i]"; + if (!defined($opt{$type})) { + $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; + } + $noclean = set_build_option("BUILD_NOCLEAN", $i); $minconfig = set_build_option("MIN_CONFIG", $i); $run_test = set_build_option("TEST", $i); @@ -674,7 +679,7 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { if ($opt{"POWEROFF_ON_SUCCESS"}) { halt; -} else { +} elsif ($opt{"REBOOT_ON_SUCCESS"}) { reboot; } -- cgit v0.10.2 From 6c5ee0be02f73ebd70eb50b84013e8830f08a6da Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:58 -0400 Subject: ktest: Added patchcheck Added patchcheck functionality. It will checkout a given SHA1 and test that commit and all commits to another given SHA1. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index f344fd0..e0ded14 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -34,7 +34,9 @@ my $noclean; my $minconfig; my $in_bisect = 0; my $bisect_bad = ""; +my $in_patchcheck = 0; my $run_test; +my $redirect; sub read_config { my ($config) = @_; @@ -88,13 +90,18 @@ sub dodie { sub run_command { my ($command) = @_; my $redirect_log = ""; + my $redirect_tee = ""; if (defined($opt{"LOG_FILE"})) { - $redirect_log = " >> $opt{LOG_FILE} 2>&1"; + $redirect_log = "| tee -a $opt{LOG_FILE}"; + } + + if (defined($redirect)) { + $redirect_tee = "| tee $redirect" } doprint "$command ... "; - `$command $redirect_log`; + `$command 2>&1 $redirect_tee $redirect_log > /dev/null`; my $failed = $?; @@ -311,6 +318,37 @@ sub install { run_command "ssh $target rm -f /tmp/$modtar"; } +sub check_buildlog { + my ($patch) = @_; + + my $buildlog = "$opt{TMP_DIR}/buildlog"; + my @files = `git show $patch | diffstat -l`; + + open(IN, "git show $patch |") or + dodie "failed to show $patch"; + while () { + if (m,^--- a/(.*),) { + chomp $1; + $files[$#files] = $1; + } + } + close(IN); + + open(IN, $buildlog) or dodie "Can't open $buildlog"; + while () { + if (/^\s*(.*?):.*(warning|error)/) { + my $err = $1; + foreach my $file (@files) { + my $fullpath = "$opt{BUILD_DIR}/$file"; + if ($file eq $err || $fullpath eq $err) { + dodie "$file built with warnings"; + } + } + } + } + close(IN); +} + sub build { my ($type) = @_; my $defconfig = ""; @@ -358,11 +396,18 @@ sub build { run_command "$defconfig $append $make $type" or dodie "failed make config"; + # patch check will examine the log + if ($in_patchcheck) { + $redirect = "$opt{TMP_DIR}/buildlog"; + } + if (!run_command "$make $opt{BUILD_OPTIONS}") { + undef $redirect; # bisect may need this to pass return 1 if ($in_bisect); dodie "failed build"; } + undef $redirect; return 0; } @@ -602,6 +647,90 @@ sub bisect { success $i; } +sub patchcheck { + my ($i) = @_; + + die "PATCHCHECK_START[$i] not defined\n" + if (!defined($opt{"PATCHCHECK_START[$i]"})); + die "PATCHCHECK_TYPE[$i] not defined\n" + if (!defined($opt{"PATCHCHECK_TYPE[$i]"})); + + my $start = $opt{"PATCHCHECK_START[$i]"}; + + my $end = "HEAD"; + if (defined($opt{"PATCHCHECK_END[$i]"})) { + $end = $opt{"PATCHCHECK_END[$i]"}; + } + + my $type = $opt{"PATCHCHECK_TYPE[$i]"}; + + # Can't have a test without having a test to run + if ($type eq "test" && !defined($run_test)) { + $type = "boot"; + } + + open (IN, "git log --pretty=oneline $end|") or + dodie "could not get git list"; + + my @list; + + while () { + chomp; + $list[$#list+1] = $_; + last if (/^$start/); + } + close(IN); + + if ($list[$#list] !~ /^$start/) { + dodie "SHA1 $start not found"; + } + + # go backwards in the list + @list = reverse @list; + + my $save_clean = $noclean; + + $in_patchcheck = 1; + foreach my $item (@list) { + my $sha1 = $item; + $sha1 =~ s/^([[:xdigit:]]+).*/$1/; + + doprint "\nProcessing commit $item\n\n"; + + run_command "git checkout $sha1" or + die "Failed to checkout $sha1"; + + # only clean on the first and last patch + if ($item eq $list[0] || + $item eq $list[$#list]) { + $noclean = $save_clean; + } else { + $noclean = 1; + } + + if (defined($minconfig)) { + build "useconfig:$minconfig"; + } else { + # ?? no config to use? + build "oldconfig"; + } + + check_buildlog $sha1; + + next if ($type eq "build"); + + get_grub_index; + get_version; + install; + monitor; + + next if ($type eq "boot"); + do_run_test; + } + $in_patchcheck = 0; + success $i; +} + read_config $ARGV[0]; # mandatory configs @@ -656,9 +785,18 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { doprint "\n\n"; doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; + my $checkout = $opt{"CHECKOUT[$i]"}; + if (defined($checkout)) { + run_command "git checkout $checkout" or + die "failed to checkout $checkout"; + } + if ($opt{$type} eq "bisect") { bisect $i; next; + } elsif ($opt{$type} eq "patchcheck") { + patchcheck $i; + next; } if ($opt{$type} ne "nobuild") { -- cgit v0.10.2 From d6ce2a0b33eb71f6862dfb6cbddd0e842f8132de Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:05 -0400 Subject: ktest: Add reverse bisect, better logging, copyright Added the ability to do a reverse bisect. Better logging of running commands. Added the copyright statement. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index e0ded14..0dc403e 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1,4 +1,8 @@ #!/usr/bin/perl -w +# +# Copywrite 2010 - Steven Rostedt , Red Hat Inc. +# Licensed under the terms of the GNU GPL License version 2 +# use strict; use IPC::Open2; @@ -34,6 +38,7 @@ my $noclean; my $minconfig; my $in_bisect = 0; my $bisect_bad = ""; +my $reverse_bisect; my $in_patchcheck = 0; my $run_test; my $redirect; @@ -89,22 +94,39 @@ sub dodie { sub run_command { my ($command) = @_; - my $redirect_log = ""; - my $redirect_tee = ""; + my $dolog = 0; + my $dord = 0; + my $pid; + + doprint("$command ... "); + + $pid = open(CMD, "$command 2>&1 |") or + dodie "unable to exec $command"; if (defined($opt{"LOG_FILE"})) { - $redirect_log = "| tee -a $opt{LOG_FILE}"; + open(LOG, ">>$opt{LOG_FILE}") or + dodie "failed to write to log"; + $dolog = 1; } if (defined($redirect)) { - $redirect_tee = "| tee $redirect" + open (RD, ">$redirect") or + dodie "failed to write to redirect $redirect"; + $dord = 1; } - doprint "$command ... "; - `$command 2>&1 $redirect_tee $redirect_log > /dev/null`; + while () { + print LOG if ($dolog); + print RD if ($dord); + } + waitpid($pid, 0); my $failed = $?; + close(CMD); + close(LOG) if ($dolog); + close(RD) if ($dord); + if ($failed) { doprint "FAILED!\n"; } else { @@ -574,6 +596,15 @@ sub run_bisect { $result = "good"; } + # Are we looking for where it worked, not failed? + if ($reverse_bisect) { + if ($failed) { + $result = "good"; + } else { + $result = "bad"; + } + } + doprint "git bisect $result ... "; $output = `git bisect $result 2>&1`; $ret = $?; @@ -614,6 +645,14 @@ sub bisect { my $bad = $opt{"BISECT_BAD[$i]"}; my $type = $opt{"BISECT_TYPE[$i]"}; + if (defined($opt{"BISECT_REVERSE[$i]"}) && + $opt{"BISECT_REVERSE[$i]"} == 1) { + doprint "Performing a reverse bisect (bad is good, good is bad!)\n"; + $reverse_bisect = 1; + } else { + $reverse_bisect = 0; + } + $in_bisect = 1; run_command "git bisect start" or -- cgit v0.10.2 From 2b7d9b21426f10448cb047d1d7c3be05da848fd2 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:15 -0400 Subject: ktest: Added continuing on success, clear log and timeout Add option to continue after a test fails. Add option to reset the log at start of running ktest. Update default timeout to 2 minutes. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 0dc403e..0a0b1b1 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -19,7 +19,7 @@ my %opt; $opt{"NUM_BUILDS"} = 5; $opt{"DEFAULT_BUILD_TYPE"} = "randconfig"; $opt{"MAKE_CMD"} = "make"; -$opt{"TIMEOUT"} = 50; +$opt{"TIMEOUT"} = 120; $opt{"TMP_DIR"} = "/tmp/autotest"; $opt{"SLEEP_TIME"} = 60; # sleep time between tests $opt{"BUILD_NOCLEAN"} = 0; @@ -29,6 +29,10 @@ $opt{"REBOOT_ON_SUCCESS"} = 1; $opt{"POWEROFF_ON_SUCCESS"} = 0; $opt{"BUILD_OPTIONS"} = ""; $opt{"BISECT_SLEEP_TIME"} = 10; # sleep time between bisects +$opt{"CLEAR_LOG"} = 0; +$opt{"SUCCESS_LINE"} = "login:"; +$opt{"BOOTED_TIMEOUT"} = 1; +$opt{"DIE_ON_FAILURE"} = 1; my $version; my $grub_number; @@ -36,6 +40,7 @@ my $target; my $make; my $noclean; my $minconfig; +my $addconfig; my $in_bisect = 0; my $bisect_bad = ""; my $reverse_bisect; @@ -92,6 +97,16 @@ sub dodie { die @_; } +sub fail { + + if ($opt{"DIE_ON_FAILURE"}) { + dodie @_; + } + + doprint "Failed: ", @_, "\n"; + return 1; +} + sub run_command { my ($command) = @_; my $dolog = 0; @@ -101,7 +116,7 @@ sub run_command { doprint("$command ... "); $pid = open(CMD, "$command 2>&1 |") or - dodie "unable to exec $command"; + (fail "unable to exec $command" and return 0); if (defined($opt{"LOG_FILE"})) { open(LOG, ">>$opt{LOG_FILE}") or @@ -228,6 +243,7 @@ sub monitor { my $pid; my $skip_call_trace = 0; my $fp = \*IN; + my $loops; $pid = open_console($fp); @@ -244,7 +260,11 @@ sub monitor { for (;;) { - $line = wait_for_input($fp); + if ($booted) { + $line = wait_for_input($fp, $opt{"BOOTED_TIMEOUT"}); + } else { + $line = wait_for_input($fp); + } last if (!defined($line)); @@ -253,7 +273,7 @@ sub monitor { # we are not guaranteed to get a full line $full_line .= $line; - if ($full_line =~ /login:/) { + if ($full_line =~ /$opt{"SUCCESS_LINE"}/) { $booted = 1; } @@ -281,16 +301,16 @@ sub monitor { close_console($fp, $pid); if (!$booted) { - return 1 if ($in_bisect); - dodie "failed - never got a boot prompt.\n"; + return 0 if ($in_bisect); + fail "failed - never got a boot prompt.\n" and return 0; } if ($bug) { - return 1 if ($in_bisect); - dodie "failed - got a bug report\n"; + return 0 if ($in_bisect); + fail "failed - got a bug report\n" and return 0; } - return 0; + return 1; } sub install { @@ -363,12 +383,14 @@ sub check_buildlog { foreach my $file (@files) { my $fullpath = "$opt{BUILD_DIR}/$file"; if ($file eq $err || $fullpath eq $err) { - dodie "$file built with warnings"; + fail "$file built with warnings" and return 0; } } } } close(IN); + + return 1; } sub build { @@ -426,12 +448,12 @@ sub build { if (!run_command "$make $opt{BUILD_OPTIONS}") { undef $redirect; # bisect may need this to pass - return 1 if ($in_bisect); - dodie "failed build"; + return 0 if ($in_bisect); + fail "failed build" and return 0; } undef $redirect; - return 0; + return 1; } sub reboot { @@ -545,41 +567,40 @@ sub do_run_test { close_console($fp, $pid); if ($bug || $child_exit) { - return 1 if $in_bisect; - dodie "test failed"; + return 0 if $in_bisect; + fail "test failed" and return 0; } - return 0; + return 1; } sub run_bisect { my ($type) = @_; - my $failed; + my $failed = 0; my $result; my $output; my $ret; - if (defined($minconfig)) { - $failed = build "useconfig:$minconfig"; + build "useconfig:$minconfig" or $failed = 1; } else { # ?? no config to use? - $failed = build "oldconfig"; + build "oldconfig" or $failed = 1; } if ($type ne "build") { - dodie "Failed on build" if $failed; + fail "Failed on build" if $failed; # Now boot the box get_grub_index; get_version; install; - $failed = monitor; + monitor or $failed = 1; if ($type ne "boot") { - dodie "Failed on boot" if $failed; + fail "Failed on boot" if $failed; - $failed = do_run_test; + do_run_test or $failed = 1; } } @@ -613,7 +634,7 @@ sub run_bisect { if ($ret) { doprint "FAILED\n"; - dodie "Failed to git bisect"; + fail "Failed to git bisect"; } doprint "SUCCESS\n"; @@ -656,13 +677,13 @@ sub bisect { $in_bisect = 1; run_command "git bisect start" or - dodie "could not start bisect"; + fail "could not start bisect"; run_command "git bisect good $good" or - dodie "could not set bisect good to $good"; + fail "could not set bisect good to $good"; run_command "git bisect bad $bad" or - dodie "could not set bisect good to $bad"; + fail "could not set bisect good to $bad"; # Can't have a test without having a test to run if ($type eq "test" && !defined($run_test)) { @@ -721,7 +742,7 @@ sub patchcheck { close(IN); if ($list[$#list] !~ /^$start/) { - dodie "SHA1 $start not found"; + fail "SHA1 $start not found"; } # go backwards in the list @@ -748,26 +769,28 @@ sub patchcheck { } if (defined($minconfig)) { - build "useconfig:$minconfig"; + build "useconfig:$minconfig" or return 0; } else { # ?? no config to use? - build "oldconfig"; + build "oldconfig" or return 0; } - check_buildlog $sha1; + check_buildlog $sha1 or return 0; next if ($type eq "build"); get_grub_index; get_version; install; - monitor; + monitor or return 0; next if ($type eq "boot"); - do_run_test; + do_run_test or next; } $in_patchcheck = 0; success $i; + + return 1; } read_config $ARGV[0]; @@ -788,8 +811,15 @@ chdir $opt{"BUILD_DIR"} || die "can't change directory to $opt{BUILD_DIR}"; $target = "$opt{SSH_USER}\@$opt{MACHINE}"; -doprint "\n\nSTARTING AUTOMATED TESTS\n"; +if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) { + unlink $opt{"LOG_FILE"}; +} +doprint "\n\nSTARTING AUTOMATED TESTS\n\n"; + +foreach my $option (sort keys %opt) { + doprint "$option = $opt{$option}\n"; +} $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; @@ -820,10 +850,20 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { $noclean = set_build_option("BUILD_NOCLEAN", $i); $minconfig = set_build_option("MIN_CONFIG", $i); $run_test = set_build_option("TEST", $i); + $addconfig = set_build_option("ADD_CONFIG", $i); doprint "\n\n"; doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; + if (!defined($minconfig)) { + $minconfig = $addconfig; + + } elsif (defined($addconfig)) { + run_command "cat $addconfig $minconfig > $opt{TMP_DIR}/use_config" or + dodie "Failed to create temp config"; + $minconfig = "$opt{TMP_DIR}/use_config"; + } + my $checkout = $opt{"CHECKOUT[$i]"}; if (defined($checkout)) { run_command "git checkout $checkout" or @@ -839,16 +879,16 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { } if ($opt{$type} ne "nobuild") { - build $opt{$type}; + build $opt{$type} or next; } get_grub_index; get_version; install; - monitor; + monitor or next; if (defined($run_test)) { - do_run_test; + do_run_test or next; } success $i; -- cgit v0.10.2 From 7faafbd69639b53b6cc2d450c283d9cc12e62c70 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:22 -0400 Subject: ktest: Add open and close console and start stop monitor It is much better to keep the monitor running throughout a test than to constantly start and stop it. Some console readers will show everything that has happened before when opening the console, and by opening it several times, causes the old content to be read multiple times in a single test. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 0a0b1b1..ff6283a 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -7,6 +7,8 @@ use strict; use IPC::Open2; use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); +use File::Path qw(mkpath); +use File::Copy qw(cp); use FileHandle; $#ARGV >= 0 || die "usage: autotest.pl config-file\n"; @@ -17,7 +19,7 @@ my %opt; #default opts $opt{"NUM_BUILDS"} = 5; -$opt{"DEFAULT_BUILD_TYPE"} = "randconfig"; +$opt{"BUILD_TYPE"} = "randconfig"; $opt{"MAKE_CMD"} = "make"; $opt{"TIMEOUT"} = 120; $opt{"TMP_DIR"} = "/tmp/autotest"; @@ -35,6 +37,7 @@ $opt{"BOOTED_TIMEOUT"} = 1; $opt{"DIE_ON_FAILURE"} = 1; my $version; +my $build_type; my $grub_number; my $target; my $make; @@ -47,6 +50,11 @@ my $reverse_bisect; my $in_patchcheck = 0; my $run_test; my $redirect; +my $buildlog; +my $dmesg; +my $monitor_fp; +my $monitor_pid; +my $monitor_cnt = 0; sub read_config { my ($config) = @_; @@ -82,12 +90,22 @@ sub doprint { logit @_; } +sub run_command; + +sub reboot { + # try to reboot normally + if (!run_command "ssh $target reboot") { + # nope? power cycle it. + run_command "$opt{POWER_CYCLE}"; + } +} + sub dodie { doprint "CRITICAL FAILURE... ", @_, "\n"; if ($opt{"REBOOT_ON_ERROR"}) { doprint "REBOOTING\n"; - `$opt{"POWER_CYCLE"}`; + reboot; } elsif ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) { doprint "POWERING OFF\n"; @@ -97,6 +115,59 @@ sub dodie { die @_; } +sub open_console { + my ($fp) = @_; + + my $flags; + + my $pid = open($fp, "$opt{CONSOLE}|") or + dodie "Can't open console $opt{CONSOLE}"; + + $flags = fcntl($fp, F_GETFL, 0) or + dodie "Can't get flags for the socket: $!\n"; + $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or + dodie "Can't set flags for the socket: $!\n"; + + return $pid; +} + +sub close_console { + my ($fp, $pid) = @_; + + doprint "kill child process $pid\n"; + kill 2, $pid; + + print "closing!\n"; + close($fp); +} + +sub start_monitor { + if ($monitor_cnt++) { + return; + } + $monitor_fp = \*MONFD; + $monitor_pid = open_console $monitor_fp; +} + +sub end_monitor { + if (--$monitor_cnt) { + return; + } + close_console($monitor_fp, $monitor_pid); +} + +sub wait_for_monitor { + my ($time) = @_; + my $line; + + doprint "Wait for monitor to settle down.\n"; + + # read the monitor and wait for the system to calm down + do { + $line = wait_for_input($monitor_fp, $time); + } while (defined($line)); +} + sub fail { if ($opt{"DIE_ON_FAILURE"}) { @@ -104,6 +175,41 @@ sub fail { } doprint "Failed: ", @_, "\n"; + + doprint "REBOOTING\n"; + reboot; + start_monitor; + wait_for_monitor $opt{"SLEEP_TIME"}; + end_monitor; + + return 1 if (!defined($opt{"STORE_FAILURES"})); + + my @t = localtime; + my $date = sprintf "%04d%02d%02d%02d%02d%02d", + 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; + + my $dir = "$opt{MACHINE}-$build_type-fail-$date"; + my $faildir = "$opt{STORE_FAILURES}/$dir"; + + if (!-d $faildir) { + mkpath($faildir) or + die "can't create $opt{STORE_FAILURES}"; + } + if (-f "$opt{OUTPUT_DIR}/.config") { + cp "$opt{OUTPUT_DIR}/.config", "$faildir/config" or + die "failed to copy .config"; + } + if (-f $buildlog) { + cp $buildlog, "$faildir/buildlog" or + die "failed to move $buildlog"; + } + if (-f $dmesg) { + cp $dmesg, "$faildir/dmesg" or + die "failed to move $dmesg"; + } + + doprint "*** Saved info to $faildir ***\n"; + return 1; } @@ -211,64 +317,34 @@ sub reboot_to { run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; } -sub open_console { - my ($fp) = @_; - - my $flags; - - my $pid = open($fp, "$opt{CONSOLE}|") or - dodie "Can't open console $opt{CONSOLE}"; - - $flags = fcntl($fp, F_GETFL, 0) or - dodie "Can't get flags for the socket: $!\n"; - $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or - dodie "Can't set flags for the socket: $!\n"; - - return $pid; -} - -sub close_console { - my ($fp, $pid) = @_; - - doprint "kill child process $pid\n"; - kill 2, $pid; - - print "closing!\n"; - close($fp); -} - sub monitor { my $booted = 0; my $bug = 0; - my $pid; my $skip_call_trace = 0; - my $fp = \*IN; my $loops; - $pid = open_console($fp); + wait_for_monitor 5; my $line; my $full_line = ""; - doprint "Wait for monitor to settle down.\n"; - # read the monitor and wait for the system to calm down - do { - $line = wait_for_input($fp, 5); - } while (defined($line)); + open(DMESG, "> $dmesg") or + die "unable to write to $dmesg"; reboot_to; for (;;) { if ($booted) { - $line = wait_for_input($fp, $opt{"BOOTED_TIMEOUT"}); + $line = wait_for_input($monitor_fp, $opt{"BOOTED_TIMEOUT"}); } else { - $line = wait_for_input($fp); + $line = wait_for_input($monitor_fp); } last if (!defined($line)); doprint $line; + print DMESG $line; # we are not guaranteed to get a full line $full_line .= $line; @@ -298,7 +374,7 @@ sub monitor { } } - close_console($fp, $pid); + close(DMESG); if (!$booted) { return 0 if ($in_bisect); @@ -363,7 +439,6 @@ sub install { sub check_buildlog { my ($patch) = @_; - my $buildlog = "$opt{TMP_DIR}/buildlog"; my @files = `git show $patch | diffstat -l`; open(IN, "git show $patch |") or @@ -398,6 +473,8 @@ sub build { my $defconfig = ""; my $append = ""; + unlink $buildlog; + if ($type =~ /^useconfig:(.*)/) { run_command "cp $1 $opt{OUTPUT_DIR}/.config" or dodie "could not copy $1 to .config"; @@ -440,11 +517,7 @@ sub build { run_command "$defconfig $append $make $type" or dodie "failed make config"; - # patch check will examine the log - if ($in_patchcheck) { - $redirect = "$opt{TMP_DIR}/buildlog"; - } - + $redirect = "$opt{TMP_DIR}/buildlog"; if (!run_command "$make $opt{BUILD_OPTIONS}") { undef $redirect; # bisect may need this to pass @@ -456,14 +529,6 @@ sub build { return 1; } -sub reboot { - # try to reboot normally - if (!run_command "ssh $target reboot") { - # nope? power cycle it. - run_command "$opt{POWER_CYCLE}"; - } -} - sub halt { if (!run_command "ssh $target halt" or defined($opt{"POWER_OFF"})) { # nope? the zap it! @@ -481,9 +546,11 @@ sub success { doprint "*******************************************\n"; if ($i != $opt{"NUM_BUILDS"}) { + doprint "Reboot and wait $opt{SLEEP_TIME} seconds\n"; reboot; - doprint "Sleeping $opt{SLEEP_TIME} seconds\n"; - sleep "$opt{SLEEP_TIME}"; + start_monitor; + wait_for_monitor $opt{"SLEEP_TIME"}; + end_monitor; } } @@ -496,9 +563,14 @@ sub get_version { } sub child_run_test { - my $failed; + my $failed = 0; - $failed = !run_command $run_test; + # child should have no power + $opt{"REBOOT_ON_ERROR"} = 0; + $opt{"POWEROFF_ON_ERROR"} = 0; + $opt{"DIE_ON_FAILURE"} = 1; + + run_command $run_test or $failed = 1; exit $failed; } @@ -511,18 +583,13 @@ sub child_finished { sub do_run_test { my $child_pid; my $child_exit; - my $pid; my $line; my $full_line; my $bug = 0; - my $fp = \*IN; - $pid = open_console($fp); + wait_for_monitor 1; - # read the monitor and wait for the system to calm down - do { - $line = wait_for_input($fp, 1); - } while (defined($line)); + doprint "run test $run_test\n"; $child_done = 0; @@ -535,7 +602,7 @@ sub do_run_test { $full_line = ""; do { - $line = wait_for_input($fp, 1); + $line = wait_for_input($monitor_fp, 1); if (defined($line)) { # we are not guaranteed to get a full line @@ -564,8 +631,6 @@ sub do_run_test { waitpid $child_pid, 0; $child_exit = $?; - close_console($fp, $pid); - if ($bug || $child_exit) { return 0 if $in_bisect; fail "test failed" and return 0; @@ -589,19 +654,22 @@ sub run_bisect { } if ($type ne "build") { - fail "Failed on build" if $failed; + dodie "Failed on build" if $failed; # Now boot the box get_grub_index; get_version; install; + + start_monitor; monitor or $failed = 1; if ($type ne "boot") { - fail "Failed on boot" if $failed; + dodie "Failed on boot" if $failed; do_run_test or $failed = 1; } + end_monitor; } if ($failed) { @@ -609,9 +677,11 @@ sub run_bisect { # reboot the box to a good kernel if ($type eq "boot") { + doprint "Reboot and sleep $opt{BISECT_SLEEP_TIME} seconds\n"; reboot; - doprint "sleep a little for reboot\n"; - sleep $opt{"BISECT_SLEEP_TIME"}; + start_monitor; + wait_for_monitor $opt{"BISECT_SLEEP_TIME"}; + end_monitor; } } else { $result = "good"; @@ -782,10 +852,18 @@ sub patchcheck { get_grub_index; get_version; install; - monitor or return 0; - next if ($type eq "boot"); - do_run_test or next; + my $failed = 0; + + start_monitor; + monitor or $failed = 1; + + if (!$failed && $type ne "boot"){ + do_run_test or $failed = 1; + } + end_monitor; + return 0 if ($failed); + } $in_patchcheck = 0; success $i; @@ -821,6 +899,8 @@ foreach my $option (sort keys %opt) { doprint "$option = $opt{$option}\n"; } +$buildlog = "$opt{TMP_DIR}/buildlog"; +$dmesg = "$opt{TMP_DIR}/dmesg"; $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; sub set_build_option { @@ -841,19 +921,18 @@ sub set_build_option { # First we need to do is the builds for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { - my $type = "BUILD_TYPE[$i]"; - - if (!defined($opt{$type})) { - $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; - } + $build_type = set_build_option("BUILD_TYPE", $i); $noclean = set_build_option("BUILD_NOCLEAN", $i); $minconfig = set_build_option("MIN_CONFIG", $i); $run_test = set_build_option("TEST", $i); $addconfig = set_build_option("ADD_CONFIG", $i); doprint "\n\n"; - doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; + doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $build_type\n\n"; + + unlink $dmesg; + unlink $buildlog; if (!defined($minconfig)) { $minconfig = $addconfig; @@ -870,26 +949,30 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { die "failed to checkout $checkout"; } - if ($opt{$type} eq "bisect") { + if ($build_type eq "bisect") { bisect $i; next; - } elsif ($opt{$type} eq "patchcheck") { + } elsif ($build_type eq "patchcheck") { patchcheck $i; next; } - if ($opt{$type} ne "nobuild") { - build $opt{$type} or next; + if ($build_type ne "nobuild") { + build $build_type or next; } get_grub_index; get_version; install; - monitor or next; - if (defined($run_test)) { - do_run_test or next; + my $failed = 0; + start_monitor; + monitor or $failed = 1;; + if (!$failed && defined($run_test)) { + do_run_test or $failed = 1; } + end_monitor; + next if ($failed); success $i; } -- cgit v0.10.2 From a75fececff3cc1e86d74e98de634ea2ed1b47697 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:27 -0400 Subject: ktest: Added sample.conf, new %default option format Added sample.conf as a nice document to show new users. Use a %default hash to separate out the options that are default and allow us to complain about options being set twice. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index ff6283a..1f28c45 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -16,28 +16,45 @@ $#ARGV >= 0 || die "usage: autotest.pl config-file\n"; $| = 1; my %opt; +my %default; #default opts -$opt{"NUM_BUILDS"} = 5; -$opt{"BUILD_TYPE"} = "randconfig"; -$opt{"MAKE_CMD"} = "make"; -$opt{"TIMEOUT"} = 120; -$opt{"TMP_DIR"} = "/tmp/autotest"; -$opt{"SLEEP_TIME"} = 60; # sleep time between tests -$opt{"BUILD_NOCLEAN"} = 0; -$opt{"REBOOT_ON_ERROR"} = 0; -$opt{"POWEROFF_ON_ERROR"} = 0; -$opt{"REBOOT_ON_SUCCESS"} = 1; -$opt{"POWEROFF_ON_SUCCESS"} = 0; -$opt{"BUILD_OPTIONS"} = ""; -$opt{"BISECT_SLEEP_TIME"} = 10; # sleep time between bisects -$opt{"CLEAR_LOG"} = 0; -$opt{"SUCCESS_LINE"} = "login:"; -$opt{"BOOTED_TIMEOUT"} = 1; -$opt{"DIE_ON_FAILURE"} = 1; +$default{"NUM_TESTS"} = 5; +$default{"REBOOT_TYPE"} = "grub"; +$default{"TEST_TYPE"} = "test"; +$default{"BUILD_TYPE"} = "randconfig"; +$default{"MAKE_CMD"} = "make"; +$default{"TIMEOUT"} = 120; +$default{"TMP_DIR"} = "/tmp/autotest"; +$default{"SLEEP_TIME"} = 60; # sleep time between tests +$default{"BUILD_NOCLEAN"} = 0; +$default{"REBOOT_ON_ERROR"} = 0; +$default{"POWEROFF_ON_ERROR"} = 0; +$default{"REBOOT_ON_SUCCESS"} = 1; +$default{"POWEROFF_ON_SUCCESS"} = 0; +$default{"BUILD_OPTIONS"} = ""; +$default{"BISECT_SLEEP_TIME"} = 60; # sleep time between bisects +$default{"CLEAR_LOG"} = 0; +$default{"SUCCESS_LINE"} = "login:"; +$default{"BOOTED_TIMEOUT"} = 1; +$default{"DIE_ON_FAILURE"} = 1; my $version; +my $machine; +my $tmpdir; +my $builddir; +my $outputdir; +my $test_type; my $build_type; +my $build_options; +my $reboot_type; +my $reboot_script; +my $power_cycle; +my $reboot_on_error; +my $poweroff_on_error; +my $die_on_failure; +my $power_off; +my $grub_menu; my $grub_number; my $target; my $make; @@ -55,6 +72,16 @@ my $dmesg; my $monitor_fp; my $monitor_pid; my $monitor_cnt = 0; +my $sleep_time; +my $bisect_sleep_time; +my $store_failures; +my $timeout; +my $booted_timeout; +my $console; +my $success_line; +my $build_target; +my $target_image; +my $localversion; sub read_config { my ($config) = @_; @@ -70,11 +97,22 @@ sub read_config { my $lvalue = $1; my $rvalue = $2; + if (defined($opt{$lvalue})) { + die "Error: Option $lvalue defined more than once!\n"; + } $opt{$lvalue} = $rvalue; } } close(IN); + + # set any defaults + + foreach my $default (keys %default) { + if (!defined($opt{$default})) { + $opt{$default} = $default{$default}; + } + } } sub logit { @@ -96,20 +134,20 @@ sub reboot { # try to reboot normally if (!run_command "ssh $target reboot") { # nope? power cycle it. - run_command "$opt{POWER_CYCLE}"; + run_command "$power_cycle"; } } sub dodie { doprint "CRITICAL FAILURE... ", @_, "\n"; - if ($opt{"REBOOT_ON_ERROR"}) { + if ($reboot_on_error && $test_type ne "build") { doprint "REBOOTING\n"; reboot; - } elsif ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) { + } elsif ($poweroff_on_error && defined($power_off)) { doprint "POWERING OFF\n"; - `$opt{"POWER_OFF"}`; + `$power_off`; } die @_; @@ -120,8 +158,8 @@ sub open_console { my $flags; - my $pid = open($fp, "$opt{CONSOLE}|") or - dodie "Can't open console $opt{CONSOLE}"; + my $pid = open($fp, "$console|") or + dodie "Can't open console $console"; $flags = fcntl($fp, F_GETFL, 0) or dodie "Can't get flags for the socket: $!\n"; @@ -147,6 +185,10 @@ sub start_monitor { } $monitor_fp = \*MONFD; $monitor_pid = open_console $monitor_fp; + + return; + + open(MONFD, "Stop perl from warning about single use of MONFD"); } sub end_monitor { @@ -160,43 +202,50 @@ sub wait_for_monitor { my ($time) = @_; my $line; - doprint "Wait for monitor to settle down.\n"; + doprint "** Wait for monitor to settle down **\n"; # read the monitor and wait for the system to calm down do { $line = wait_for_input($monitor_fp, $time); + print "$line" if (defined($line)); } while (defined($line)); + print "** Monitor flushed **\n"; } sub fail { - if ($opt{"DIE_ON_FAILURE"}) { + if ($die_on_failure) { dodie @_; } - doprint "Failed: ", @_, "\n"; + doprint "FAILED\n"; - doprint "REBOOTING\n"; - reboot; - start_monitor; - wait_for_monitor $opt{"SLEEP_TIME"}; - end_monitor; + # no need to reboot for just building. + if ($test_type ne "build") { + doprint "REBOOTING\n"; + reboot; + start_monitor; + wait_for_monitor $sleep_time; + end_monitor; + } - return 1 if (!defined($opt{"STORE_FAILURES"})); + doprint "**** Failed: ", @_, " ****\n"; + + return 1 if (!defined($store_failures)); my @t = localtime; my $date = sprintf "%04d%02d%02d%02d%02d%02d", 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; - my $dir = "$opt{MACHINE}-$build_type-fail-$date"; - my $faildir = "$opt{STORE_FAILURES}/$dir"; + my $dir = "$machine-$test_type-$build_type-fail-$date"; + my $faildir = "$store_failures/$dir"; if (!-d $faildir) { mkpath($faildir) or - die "can't create $opt{STORE_FAILURES}"; + die "can't create $faildir"; } - if (-f "$opt{OUTPUT_DIR}/.config") { - cp "$opt{OUTPUT_DIR}/.config", "$faildir/config" or + if (-f "$outputdir/.config") { + cp "$outputdir/.config", "$faildir/config" or die "failed to copy .config"; } if (-f $buildlog) { @@ -259,6 +308,9 @@ sub run_command { sub get_grub_index { + if ($reboot_type ne "grub") { + return; + } return if (defined($grub_number)); doprint "Find grub menu ... "; @@ -266,7 +318,7 @@ sub get_grub_index { open(IN, "ssh $target cat /boot/grub/menu.lst |") or die "unable to get menu.lst"; while () { - if (/^\s*title\s+$opt{GRUB_MENU}\s*$/) { + if (/^\s*title\s+$grub_menu\s*$/) { $grub_number++; last; } elsif (/^\s*title\s/) { @@ -275,13 +327,11 @@ sub get_grub_index { } close(IN); - die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}" + die "Could not find '$grub_menu' in /boot/grub/menu on $machine" if ($grub_number < 0); doprint "$grub_number\n"; } -my $timeout = $opt{"TIMEOUT"}; - sub wait_for_input { my ($fp, $time) = @_; @@ -314,7 +364,12 @@ sub wait_for_input } sub reboot_to { - run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; + if ($reboot_type eq "grub") { + run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; + return; + } + + run_command "$reboot_script"; } sub monitor { @@ -336,7 +391,7 @@ sub monitor { for (;;) { if ($booted) { - $line = wait_for_input($monitor_fp, $opt{"BOOTED_TIMEOUT"}); + $line = wait_for_input($monitor_fp, $booted_timeout); } else { $line = wait_for_input($monitor_fp); } @@ -349,7 +404,7 @@ sub monitor { # we are not guaranteed to get a full line $full_line .= $line; - if ($full_line =~ /$opt{"SUCCESS_LINE"}/) { + if ($full_line =~ /$success_line/) { $booted = 1; } @@ -376,14 +431,14 @@ sub monitor { close(DMESG); - if (!$booted) { + if ($bug) { return 0 if ($in_bisect); - fail "failed - never got a boot prompt.\n" and return 0; + fail "failed - got a bug report\n" and return 0; } - if ($bug) { + if (!$booted) { return 0 if ($in_bisect); - fail "failed - got a bug report\n" and return 0; + fail "failed - never got a boot prompt.\n" and return 0; } return 1; @@ -391,14 +446,14 @@ sub monitor { sub install { - run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}" or + run_command "scp $outputdir/$build_target $target:$target_image" or dodie "failed to copy image"; my $install_mods = 0; # should we process modules? $install_mods = 0; - open(IN, "$opt{OUTPUT_DIR}/.config") or dodie("Can't read config file"); + open(IN, "$outputdir/.config") or dodie("Can't read config file"); while () { if (/CONFIG_MODULES(=y)?/) { $install_mods = 1 if (defined($1)); @@ -412,7 +467,7 @@ sub install { return; } - run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install" or + run_command "$make INSTALL_MOD_PATH=$tmpdir modules_install" or dodie "Failed to install modules"; my $modlib = "/lib/modules/$version"; @@ -422,13 +477,13 @@ sub install { dodie "failed to remove old mods: $modlib"; # would be nice if scp -r did not follow symbolic links - run_command "cd $opt{TMP_DIR} && tar -cjf $modtar lib/modules/$version" or + run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or dodie "making tarball"; - run_command "scp $opt{TMP_DIR}/$modtar $target:/tmp" or + run_command "scp $tmpdir/$modtar $target:/tmp" or dodie "failed to copy modules"; - unlink "$opt{TMP_DIR}/$modtar"; + unlink "$tmpdir/$modtar"; run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'" or dodie "failed to tar modules"; @@ -456,7 +511,7 @@ sub check_buildlog { if (/^\s*(.*?):.*(warning|error)/) { my $err = $1; foreach my $file (@files) { - my $fullpath = "$opt{BUILD_DIR}/$file"; + my $fullpath = "$builddir/$file"; if ($file eq $err || $fullpath eq $err) { fail "$file built with warnings" and return 0; } @@ -476,7 +531,7 @@ sub build { unlink $buildlog; if ($type =~ /^useconfig:(.*)/) { - run_command "cp $1 $opt{OUTPUT_DIR}/.config" or + run_command "cp $1 $outputdir/.config" or dodie "could not copy $1 to .config"; $type = "oldconfig"; @@ -487,38 +542,38 @@ sub build { $append = "yes ''|"; # allow for empty configs - run_command "touch $opt{OUTPUT_DIR}/.config"; + run_command "touch $outputdir/.config"; - run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp" or + run_command "mv $outputdir/.config $outputdir/config_temp" or dodie "moving .config"; if (!$noclean && !run_command "$make mrproper") { dodie "make mrproper"; } - run_command "mv $opt{OUTPUT_DIR}/config_temp $opt{OUTPUT_DIR}/.config" or + run_command "mv $outputdir/config_temp $outputdir/.config" or dodie "moving config_temp"; } elsif (!$noclean) { - unlink "$opt{OUTPUT_DIR}/.config"; + unlink "$outputdir/.config"; run_command "$make mrproper" or dodie "make mrproper"; } # add something to distinguish this build - open(OUT, "> $opt{OUTPUT_DIR}/localversion") or dodie("Can't make localversion file"); - print OUT "$opt{LOCALVERSION}\n"; + open(OUT, "> $outputdir/localversion") or dodie("Can't make localversion file"); + print OUT "$localversion\n"; close(OUT); if (defined($minconfig)) { $defconfig = "KCONFIG_ALLCONFIG=$minconfig"; } - run_command "$defconfig $append $make $type" or + run_command "$append $defconfig $make $type" or dodie "failed make config"; - $redirect = "$opt{TMP_DIR}/buildlog"; - if (!run_command "$make $opt{BUILD_OPTIONS}") { + $redirect = "$buildlog"; + if (!run_command "$make $build_options") { undef $redirect; # bisect may need this to pass return 0 if ($in_bisect); @@ -530,9 +585,9 @@ sub build { } sub halt { - if (!run_command "ssh $target halt" or defined($opt{"POWER_OFF"})) { + if (!run_command "ssh $target halt" or defined($power_off)) { # nope? the zap it! - run_command "$opt{POWER_OFF}"; + run_command "$power_off"; } } @@ -541,15 +596,17 @@ sub success { doprint "\n\n*******************************************\n"; doprint "*******************************************\n"; - doprint "** SUCCESS!!!! **\n"; + doprint "** TEST $i SUCCESS!!!! **\n"; doprint "*******************************************\n"; doprint "*******************************************\n"; - if ($i != $opt{"NUM_BUILDS"}) { - doprint "Reboot and wait $opt{SLEEP_TIME} seconds\n"; + if ($i != $opt{"NUM_TESTS"} && $test_type ne "build" && + !($test_type eq "patchcheck" && $opt{"PATCHCHECK_TYPE[$i]"} eq "build") && + !($test_type eq "bisect" && $opt{"BISECT_TYPE[$i]"} eq "build")) { + doprint "Reboot and wait $sleep_time seconds\n"; reboot; start_monitor; - wait_for_monitor $opt{"SLEEP_TIME"}; + wait_for_monitor $sleep_time; end_monitor; } } @@ -566,9 +623,9 @@ sub child_run_test { my $failed = 0; # child should have no power - $opt{"REBOOT_ON_ERROR"} = 0; - $opt{"POWEROFF_ON_ERROR"} = 0; - $opt{"DIE_ON_FAILURE"} = 1; + $reboot_on_error = 0; + $poweroff_on_error = 0; + $die_on_failure = 1; run_command $run_test or $failed = 1; exit $failed; @@ -638,6 +695,36 @@ sub do_run_test { return 1; } +sub run_git_bisect { + my ($command) = @_; + + doprint "$command ... "; + + my $output = `$command 2>&1`; + my $ret = $?; + + logit $output; + + if ($ret) { + doprint "FAILED\n"; + dodie "Failed to git bisect"; + } + + doprint "SUCCESS\n"; + if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\))\s+\[([[:xdigit:]]+)\]/) { + doprint "$1 [$2]\n"; + } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) { + $bisect_bad = $1; + doprint "Found bad commit... $1\n"; + return 0; + } else { + # we already logged it, just print it now. + print $output; + } + + return 1; +} + sub run_bisect { my ($type) = @_; @@ -676,11 +763,11 @@ sub run_bisect { $result = "bad"; # reboot the box to a good kernel - if ($type eq "boot") { - doprint "Reboot and sleep $opt{BISECT_SLEEP_TIME} seconds\n"; + if ($type ne "build") { + doprint "Reboot and sleep $bisect_sleep_time seconds\n"; reboot; start_monitor; - wait_for_monitor $opt{"BISECT_SLEEP_TIME"}; + wait_for_monitor $bisect_sleep_time; end_monitor; } } else { @@ -696,31 +783,7 @@ sub run_bisect { } } - doprint "git bisect $result ... "; - $output = `git bisect $result 2>&1`; - $ret = $?; - - logit $output; - - if ($ret) { - doprint "FAILED\n"; - fail "Failed to git bisect"; - } - - doprint "SUCCESS\n"; - if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\))\s+\[([[:xdigit:]]+)\]/) { - doprint "$1 [$2]\n"; - } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) { - $bisect_bad = $1; - doprint "Found bad commit... $1\n"; - return 0; - } else { - # we already logged it, just print it now. - print $output; - } - - - return 1; + return $result; } sub bisect { @@ -735,6 +798,8 @@ sub bisect { my $good = $opt{"BISECT_GOOD[$i]"}; my $bad = $opt{"BISECT_BAD[$i]"}; my $type = $opt{"BISECT_TYPE[$i]"}; + my $start = $opt{"BISECT_START[$i]"}; + my $replay = $opt{"BISECT_REPLAY[$i]"}; if (defined($opt{"BISECT_REVERSE[$i]"}) && $opt{"BISECT_REVERSE[$i]"} == 1) { @@ -746,23 +811,83 @@ sub bisect { $in_bisect = 1; + # Can't have a test without having a test to run + if ($type eq "test" && !defined($run_test)) { + $type = "boot"; + } + + my $check = $opt{"BISECT_CHECK[$i]"}; + if (defined($check) && $check ne "0") { + + # get current HEAD + doprint "git rev-list HEAD --max-count=1 ... "; + my $head = `git rev-list HEAD --max-count=1`; + my $ret = $?; + + logit $head; + + if ($ret) { + doprint "FAILED\n"; + dodie "Failed to get git HEAD"; + } + + print "SUCCESS\n"; + + chomp $head; + + if ($check ne "good") { + doprint "TESTING BISECT BAD [$bad]\n"; + run_command "git checkout $bad" or + die "Failed to checkout $bad"; + + $result = run_bisect $type; + + if ($result ne "bad") { + fail "Tested BISECT_BAD [$bad] and it succeeded" and return 0; + } + } + + if ($check ne "bad") { + doprint "TESTING BISECT GOOD [$good]\n"; + run_command "git checkout $good" or + die "Failed to checkout $good"; + + $result = run_bisect $type; + + if ($result ne "good") { + fail "Tested BISECT_GOOD [$good] and it failed" and return 0; + } + } + + # checkout where we started + run_command "git checkout $head" or + die "Failed to checkout $head"; + } + run_command "git bisect start" or - fail "could not start bisect"; + dodie "could not start bisect"; run_command "git bisect good $good" or - fail "could not set bisect good to $good"; + dodie "could not set bisect good to $good"; - run_command "git bisect bad $bad" or - fail "could not set bisect good to $bad"; + run_git_bisect "git bisect bad $bad" or + dodie "could not set bisect bad to $bad"; - # Can't have a test without having a test to run - if ($type eq "test" && !defined($run_test)) { - $type = "boot"; + if (defined($replay)) { + run_command "git bisect replay $replay" or + dodie "failed to run replay"; } + if (defined($start)) { + run_command "git checkout $start" or + dodie "failed to checkout $start"; + } + + my $test; do { $result = run_bisect $type; - } while ($result); + $test = run_git_bisect "git bisect $result"; + } while ($test); run_command "git bisect log" or dodie "could not capture git bisect log"; @@ -883,11 +1008,6 @@ die "TARGET_IMAGE not defined\n" if (!defined($opt{"TARGET_IMAGE"})); die "POWER_CYCLE not defined\n" if (!defined($opt{"POWER_CYCLE"})); die "CONSOLE not defined\n" if (!defined($opt{"CONSOLE"})); die "LOCALVERSION not defined\n" if (!defined($opt{"LOCALVERSION"})); -die "GRUB_MENU not defined\n" if (!defined($opt{"GRUB_MENU"})); - -chdir $opt{"BUILD_DIR"} || die "can't change directory to $opt{BUILD_DIR}"; - -$target = "$opt{SSH_USER}\@$opt{MACHINE}"; if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) { unlink $opt{"LOG_FILE"}; @@ -899,11 +1019,7 @@ foreach my $option (sort keys %opt) { doprint "$option = $opt{$option}\n"; } -$buildlog = "$opt{TMP_DIR}/buildlog"; -$dmesg = "$opt{TMP_DIR}/dmesg"; -$make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; - -sub set_build_option { +sub set_test_option { my ($name, $i) = @_; my $option = "$name\[$i\]"; @@ -920,16 +1036,74 @@ sub set_build_option { } # First we need to do is the builds -for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { +for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { + + my $ssh_user = set_test_option("SSH_USER", $i); + my $makecmd = set_test_option("MAKE_CMD", $i); + + $machine = set_test_option("MACHINE", $i); + $tmpdir = set_test_option("TMP_DIR", $i); + $outputdir = set_test_option("OUTPUT_DIR", $i); + $builddir = set_test_option("BUILD_DIR", $i); + $test_type = set_test_option("TEST_TYPE", $i); + $build_type = set_test_option("BUILD_TYPE", $i); + $build_options = set_test_option("BUILD_OPTIONS", $i); + $power_cycle = set_test_option("POWER_CYCLE", $i); + $noclean = set_test_option("BUILD_NOCLEAN", $i); + $minconfig = set_test_option("MIN_CONFIG", $i); + $run_test = set_test_option("TEST", $i); + $addconfig = set_test_option("ADD_CONFIG", $i); + $reboot_type = set_test_option("REBOOT_TYPE", $i); + $grub_menu = set_test_option("GRUB_MENU", $i); + $reboot_script = set_test_option("REBOOT_SCRIPT", $i); + $reboot_on_error = set_test_option("REBOOT_ON_ERROR", $i); + $poweroff_on_error = set_test_option("POWEROFF_ON_ERROR", $i); + $die_on_failure = set_test_option("DIE_ON_FAILURE", $i); + $power_off = set_test_option("POWER_OFF", $i); + $sleep_time = set_test_option("SLEEP_TIME", $i); + $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i); + $store_failures = set_test_option("STORE_FAILURES", $i); + $timeout = set_test_option("TIMEOUT", $i); + $booted_timeout = set_test_option("BOOTED_TIMEOUT", $i); + $console = set_test_option("CONSOLE", $i); + $success_line = set_test_option("SUCCESS_LINE", $i); + $build_target = set_test_option("BUILD_TARGET", $i); + $target_image = set_test_option("TARGET_IMAGE", $i); + $localversion = set_test_option("LOCALVERSION", $i); + + chdir $builddir || die "can't change directory to $builddir"; + + if (!-d $tmpdir) { + mkpath($tmpdir) or + die "can't create $tmpdir"; + } - $build_type = set_build_option("BUILD_TYPE", $i); - $noclean = set_build_option("BUILD_NOCLEAN", $i); - $minconfig = set_build_option("MIN_CONFIG", $i); - $run_test = set_build_option("TEST", $i); - $addconfig = set_build_option("ADD_CONFIG", $i); + $target = "$ssh_user\@$machine"; + + $buildlog = "$tmpdir/buildlog-$machine"; + $dmesg = "$tmpdir/dmesg-$machine"; + $make = "$makecmd O=$outputdir"; + + if ($reboot_type eq "grub") { + dodie "GRUB_MENU not defined\n" if (!defined($grub_menu)); + } elsif (!defined($reboot_script)) { + dodie "REBOOT_SCRIPT not defined\n" + } + + my $run_type = $build_type; + if ($test_type eq "patchcheck") { + $run_type = $opt{"PATCHCHECK_TYPE[$i]"}; + } elsif ($test_type eq "bisect") { + $run_type = $opt{"BISECT_TYPE[$i]"}; + } + + # mistake in config file? + if (!defined($run_type)) { + $run_type = "ERROR"; + } doprint "\n\n"; - doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $build_type\n\n"; + doprint "RUNNING TEST $i of $opt{NUM_TESTS} with option $test_type $run_type\n\n"; unlink $dmesg; unlink $buildlog; @@ -938,9 +1112,9 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { $minconfig = $addconfig; } elsif (defined($addconfig)) { - run_command "cat $addconfig $minconfig > $opt{TMP_DIR}/use_config" or + run_command "cat $addconfig $minconfig > $tmpdir/use_config" or dodie "Failed to create temp config"; - $minconfig = "$opt{TMP_DIR}/use_config"; + $minconfig = "$tmpdir/use_config"; } my $checkout = $opt{"CHECKOUT[$i]"}; @@ -949,10 +1123,10 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { die "failed to checkout $checkout"; } - if ($build_type eq "bisect") { + if ($test_type eq "bisect") { bisect $i; next; - } elsif ($build_type eq "patchcheck") { + } elsif ($test_type eq "patchcheck") { patchcheck $i; next; } @@ -961,25 +1135,28 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { build $build_type or next; } - get_grub_index; - get_version; - install; + if ($test_type ne "build") { + get_grub_index; + get_version; + install; - my $failed = 0; - start_monitor; - monitor or $failed = 1;; - if (!$failed && defined($run_test)) { - do_run_test or $failed = 1; + my $failed = 0; + start_monitor; + monitor or $failed = 1;; + + if (!$failed && $test_type ne "boot" && defined($run_test)) { + do_run_test or $failed = 1; + } + end_monitor; + next if ($failed); } - end_monitor; - next if ($failed); success $i; } if ($opt{"POWEROFF_ON_SUCCESS"}) { halt; -} elsif ($opt{"REBOOT_ON_SUCCESS"}) { +} elsif ($opt{"REBOOT_ON_SUCCESS"} && $test_type ne "build") { reboot; } diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf new file mode 100644 index 0000000..42f803f --- /dev/null +++ b/tools/testing/ktest/sample.conf @@ -0,0 +1,330 @@ +# +# Config file for autotest.pl +# +# Note, all paths must be absolute +# + +# Almost all options may be overwritten per test run, by appending +# a [x] to the config. For example, to change the test type for +# the third iteration of tests, you can specify: +# (1 is for the first test, 2 for the second, and so on) +# +# TEST_TYPE[3] = build +# +# The options that can not be changed like this are: +# NUM_TESTS +# LOG_FILE +# CLEAR_LOG +# POWEROFF_ON_SUCCESS +# REBOOT_ON_SUCCESS +# + +#### Mandatory Config Options #### + +# The machine hostname that you will test +#MACHINE = target + +# The box is expected to have ssh on normal bootup, provide the user +# (most likely root, since you need privileged operations) +#SSH_USER = root + +# The directory that contains the Linux source code +#BUILD_DIR = /home/test/linux.git + +# The directory that the objects will be built +# (can not be same as BUILD_DIR) +#OUTPUT_DIR = /home/test/build/target + +# The location of the compiled file to copy to the target +# (relative to OUTPUT_DIR) +#BUILD_TARGET = arch/x86/boot/bzImage + +# The place to put your image on the test machine +#TARGET_IMAGE = /boot/vmlinuz-test + +# A script or command to reboot the box +# Here is a digital loggers power switch example +#POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=CCL' +# Here is an example to reboot a virtual box on the current host +# with the name "Guest". +#POWER_CYCLE = virsh list | grep '\' | awk '{printf ("%d", $1)}' | xargs virsh destroy; sleep 5; virsh start Guest + +# The script or command that reads the console +# If you use ttywatch server, something like the following would work. +#CONSOLE = nc -d localhost 3001 +# For a virtual machine with guest name "Guest". +#CONSOLE = virsh console `virsh list | grep '\' | awk '{printf ("%d", $1)}'` + +# Required version ending to differentiate the test +# from other linux builds on the system. +#LOCALVERSION = -test + +# The grub title name for the test kernel to boot +# (Only mandatory if REBOOT_TYPE = grub) +# +# For example, if in the /boot/grub/menu.lst the test kernel title has: +# title Test Kernel +#GRUB_MENU = Test Kernel + +# A script to reboot the target into the test kernel +# (Only mandatory if REBOOT_TYPE = script) +#REBOOT_SCRIPT = + +#### Optional Config Options (all have defaults) #### + +# The number of tests to run (default 5) +#NUM_TESTS = 5 + +# The default test type (default test) +# The test types may be: +# build - only build the kernel, do nothing else +# boot - build and boot the kernel +# test - build, boot and if TEST is set, run the test script +# bisect - Perform a bisect on the kernel (see BISECT_TYPE below) +# patchcheck - Do a test on a series of commits in git (see PATCHCHECK below) +#TEST_TYPE = test + +# The build type is any make config type or a command. +# (default randconfig) +# nobuild - skip the clean and build step +#BUILD_TYPE = randconfig + +# The make command (default make) +# If you are building a 32bit x86 on a 64 bit host +#MAKE_CMD = CC=i386-gcc AS=i386-as make ARCH=i386 + +# Way to reboot the box to the test kernel. +# Only valid options so far are "grub" and "script" +# (default grub) +# If you specify grub, it will assume grub version 1 +# and will search in /boot/grub/menu.lst for the title $GRUB_MENU +# and select that target to reboot to the kernel. If this is not +# your setup, then specify "script" and have a command or script +# specified in REBOOT_SCRIPT to boot to the target. +#REBOOT_TYPE = grub + +# Line to define success in output. (default "login:") +# This is what the line contains, not the entire line. If you need +# the entire line to match, then use regural expression syntax like +# ^MyBox Login:$ +#SUCCESS_LINE = login: + +# As the test reads the console, after it hits the SUCCESS_LINE +# the time it waits for the monitor to settle down between reads +# can usually be lowered. +# (in seconds) (default 1) +#BOOTED_TIMEOUT = 1 + +# The timeout in seconds when we consider the box hung after +# the console stop producing output. +# (default 120) +#TIMEOUT = 120 + +# The location on the host where to write temp files +# (default /tmp/autotest) +#TMP_DIR = /tmp/autotest + +# In between tests, a reboot of the box may occur, and this +# is the time to wait for the console after it stops producing +# output. Some machines may not produce a large lag on reboot +# so this should accommodate it. +# (default 60) +#SLEEP_TIME = 60 + +# The time in between bisects to sleep (in seconds) +# Can be less than SLEEP_TIME since bisects do more work +# in between boots. (default 60) +#BISECT_SLEEP_TIME = 60 + +# Build without doing a make mrproper, or removing .config +# (default 0) +#BUILD_NOCLEAN = 0 + +# Reboot the target box on error (default 0) +#REBOOT_ON_ERROR = 0 + +# Power off the target on error (ignored if REBOOT_ON_ERROR is set) +# (default 0) +#POWEROFF_ON_ERROR = 0 + +# Power off the target after all tests have completed successfully +# (default 0) +#POWEROFF_ON_SUCCESS = 0 + +# Reboot the target after all test completed successfully (default 1) +# (ignored if POWEROFF_ON_SUCCESS is set) +#REBOOT_ON_SUCCESS = 1 + +# Stop testing if a build fails. If set, the script will end if +# a failure is detected, otherwise it will save off the .config, +# dmesg and bootlog in a directory called +# MACHINE-TEST_TYPE_BUILD_TYPE-fail-yyyymmddhhmmss +# if the STORE_FAILURES directory is set. +# (default 1) +# Note, even if this is set to zero, there are some errors that still +# stop the tests. +#DIE_ON_FAILURE = 1 + +# Directory to store failure directories on failure. If this is not +# set, DIE_ON_FAILURE=0 will not save off the .config, dmesg and +# bootlog. +#STORE_FAILURES = /home/test/failures + +# A script or command to power off the box (default undef) +# Needed for POWEROFF_ON_ERROR and SUCCESS +# Example for digital loggers power switch: +#POWER_OFF = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=OFF' +# Example for a virtual guest call "Guest". +#POWER_OFF = virsh list | grep '\' | awk '{printf ("%d", $1)}' | xargs virsh destroy + +# Any build options for the make (default "") +#BUILD_OPTIONS = -j20 + +# Optional log file to write the status (recommended) +# (default undef) +#LOG_FILE = /home/test/logfiles/target.log + +# Remove old logfile if it exists before starting all tests. +# (default 0) +#CLEAR_LOG = 0 + +# Test to run if there is a successful boot and TEST_TYPE is test. +# Must exit with 0 on success and non zero on error +# default (undef) +#TEST = ssh user@machine /root/run_test +#TEST[1] = ssh root@mxtest /root/run_test + +# The min config that is needed to build for the machine +# A nice way to get this to work, is to do a "lsmod > mymods" on the target +# copy it to the build server, and then run "make LSMOD=mymods localyesconfig". +# Then copy all the options that are set: "grep '^CONFIG' > /home/test/config-min" +# +# You might want to set: +# CONFIG_CMDLINE="" +# randconfig may set the above and override your real command +# line options. +# (default undef) +#MIN_CONFIG = /home/test/config-min + +# Sometimes there's options that just break the boot and +# you do not care about. Here are a few: +# # CONFIG_STAGING is not set +# Staging drivers are horrible, and can break the build. +# # CONFIG_SCSI_DEBUG is not set +# SCSI_DEBUG may change your root partition +# # CONFIG_KGDB_SERIAL_CONSOLE is not set +# KGDB may cause oops waiting for a connection that's not there. +# This option points to the file containing config options that will be prepended +# to the MIN_CONFIG (or be the MIN_CONFIG if it is not set) +# before running it through randconfig +# (default undef) +#ADD_CONFIG = /home/test/config-broken + +#### Per test run options #### +# These are options are per build only. The only exist with the [x] +# syntax, and there is no general option. +# +# All are optional and undef by default +# +# CHECKOUT[x] = branch +# +# If the BUILD_DIR is a git repository, then you can set this option +# to checkout the given branch before running the TEST. If you +# specify this for the first run, that branch will be used for +# all preceding tests until a new CHECKOUT[x] is set. +# +# For TEST_TYPE[x] = patchcheck +# +# This expects the BUILD_DIR to be a git repository, and +# will checkout the PATCHCHECK_START[x]. +# +# PATCHCHECK_START[x] is required and is the first patch to +# test (the SHA1 of the commit). +# +# PATCHCHECK_END[x] is the last patch to check (default HEAD) +# +# PATCHCHECK_TYPE[x] is required and is the type of test to run: +# build, boot, test. +# +# Note, the build test will look for warnings, if a warning occurred +# in a file that a commit touches, the build will fail. +# +# If BUILD_NOCLEAN is set, then make mrproper will not be run on +# any of the builds, just like all other TEST_TYPE tests. But +# what makes patchcheck different from the other tests, is if +# BUILD_NOCLEAN is not set, only the first and last patch run +# make mrproper. This helps speed up the test. +# +# Example: +# TEST_TYPE[1] = patchcheck +# CHECKOUT[1] = mybranch +# PATCHCHECK_TYPE[1] = boot +# PATCHCHECK_START[1] = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 +# PATCHCHEKC_END[1] = b8b2663bd7c9da04ac804659b9f617c199d0252c +# +# +# For TEST_TYPE[x] = bisect +# +# You can specify a git bisect if the BUILD_DIR is a git repository. +# The MIN_CONFIG will be used for all builds of the bisect. The build type +# used for bisecting is oldconfig. +# +# BISECT_TYPE[x] is the type of test to perform: +# build - bad fails to build +# boot - bad builds but fails to boot +# test - bad boots but fails a test +# +# BISECT_GOOD[x] is the commit (SHA1) to label as good +# BISECT_BAD[x] is the commit to label as bad +# +# The above three options are required for a bisect operation. +# +# BISECT_REPLAY[x] = /path/to/replay/file (optional, default undefined) +# +# If an operation failed in the bisect that was not expected to +# fail. Then the test ends. The state of the BUILD_DIR will be +# left off at where the failur occurred. You can examine the +# reason for the failure, and perhaps even find a git commit +# that would work to continue with. You can run: +# +# git bisect log > /path/to/replay/file +# +# and if BISECT_REPLAY[x] is set, the test will run git bisect replay +# before continuing with the bisect. +# +# BISECT_START[x] = commit (optional, default undefined) +# +# As with BISECT_REPLAY[x], if the test failed on a commit that +# just happen to have a bad commit in the middle of the bisect, +# and you need to skip it. If BISECT_START[x] is defined, it +# will checkout that commit before continuing with the bisect. +# +# Note, BISECT_REPLAY[x] is executed before BISECT_START[x]. +# +# BISECT_REVERSE[x] = 1 (optional, default 0) +# +# In those strange instances where it was broken forever +# and you are trying to find where it started to work! +# Set BISECT_GOOD[x] to the commit that was last known to fail +# Set BISECT_BAD[x] to the commit that is known where it started +# to work. With BISECT_REVERSE[x] = 1, The test will consider +# failures as good, and success as bad. +# +# BISECT_CHECK[x] = 1 (optional, default 0) +# +# Just to be sure the good is good and bad is bad, setting +# BISECT_CHECK[x] to 1 will start the bisect by first checking +# out BISECT_BAD[x] and makes sure it fails, then it will check +# out BISECT_GOOD[x] and makes sure it succeeds before starting +# the bisect (it works for BISECT_REVERSE[x] too). +# +# You can limit the test to just check BISECT_GOOD[x] or +# BISECT_BAD[x] with BISECT_CHECK[x] = good or +# BISECT_CHECK[x] = bad, respectively. +# +# Example: +# TEST_TYPE[1] = bisect +# BISECT_GOOD[1] = v2.6.36 +# BISECT_BAD[1] = b5153163ed580e00c67bdfecb02b2e3843817b3e +# BISECT_TYPE[1] = build +# MIN_CONFIG[1] = /home/test/config-bisect -- cgit v0.10.2 From 8b37ca8cac46b2c108386e3901cc8611dc81c7aa Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:33 -0400 Subject: ktest: Add POST_INSTALL to allow initrds to be created Add a POST_INSTALL option that runs after the build and install but before rebooting to the test kernel. This alls the user to run a script that will install an initrd (or anything else that may be special) before booting. An environment variable KERNEL_VERSION is set. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 1f28c45..687a854 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -58,6 +58,7 @@ my $grub_menu; my $grub_number; my $target; my $make; +my $post_install; my $noclean; my $minconfig; my $addconfig; @@ -489,6 +490,15 @@ sub install { dodie "failed to tar modules"; run_command "ssh $target rm -f /tmp/$modtar"; + + return if (!defined($post_install)); + + my $save_env = $ENV{KERNEL_VERSION}; + + $ENV{KERNEL_VERSION} = $version; + run_command "$post_install"; + + $ENV{KERNEL_VERSION} = $save_env; } sub check_buildlog { @@ -1055,6 +1065,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $addconfig = set_test_option("ADD_CONFIG", $i); $reboot_type = set_test_option("REBOOT_TYPE", $i); $grub_menu = set_test_option("GRUB_MENU", $i); + $post_install = set_test_option("POST_INSTALL", $i); $reboot_script = set_test_option("REBOOT_SCRIPT", $i); $reboot_on_error = set_test_option("REBOOT_ON_ERROR", $i); $poweroff_on_error = set_test_option("POWEROFF_ON_ERROR", $i); diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 42f803f..0115a67 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -93,6 +93,11 @@ # If you are building a 32bit x86 on a 64 bit host #MAKE_CMD = CC=i386-gcc AS=i386-as make ARCH=i386 +# If you need an initrd, you can add a script or code here to install +# it. The environment variable KERNEL_VERSION will be set to the +# kernel version that is used. +#POST_INSTALL = ssh user@target /sbin/mkinitrd --allow-missing -f /boot/initramfs-test.img $KERNEL_VERSION + # Way to reboot the box to the test kernel. # Only valid options so far are "grub" and "script" # (default grub) -- cgit v0.10.2 From 576f627c817dff0b7081374287a77247325b9cc0 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:38 -0400 Subject: ktest: Add poweroff after halt and powercycle after reboot Added the options POWEROFF_AFTER_HALT to handle boxes that do not really shut off after a halt is called. Added POWERCYCLE_AFTER_REBOOT to force a power cycle for boxes that don't reboot but get stuck during the reboot. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 687a854..ef97817 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -53,6 +53,8 @@ my $power_cycle; my $reboot_on_error; my $poweroff_on_error; my $die_on_failure; +my $powercycle_after_reboot; +my $poweroff_after_halt; my $power_off; my $grub_menu; my $grub_number; @@ -83,6 +85,7 @@ my $success_line; my $build_target; my $target_image; my $localversion; +my $iteration = 0; sub read_config { my ($config) = @_; @@ -133,16 +136,32 @@ sub run_command; sub reboot { # try to reboot normally - if (!run_command "ssh $target reboot") { + if (run_command "ssh $target reboot") { + if (defined($powercycle_after_reboot)) { + sleep $powercycle_after_reboot; + run_command "$power_cycle"; + } + } else { # nope? power cycle it. run_command "$power_cycle"; } } +sub do_not_reboot { + my $i = $iteration; + + return $test_type eq "build" || + ($test_type eq "patchcheck" && $opt{"PATCHCHECK_TYPE[$i]"} eq "build") || + ($test_type eq "bisect" && $opt{"BISECT_TYPE[$i]"} eq "build"); +} + sub dodie { doprint "CRITICAL FAILURE... ", @_, "\n"; - if ($reboot_on_error && $test_type ne "build") { + my $i = $iteration; + + if ($reboot_on_error && !do_not_reboot) { + doprint "REBOOTING\n"; reboot; @@ -151,7 +170,7 @@ sub dodie { `$power_off`; } - die @_; + die @_, "\n"; } sub open_console { @@ -163,9 +182,9 @@ sub open_console { dodie "Can't open console $console"; $flags = fcntl($fp, F_GETFL, 0) or - dodie "Can't get flags for the socket: $!\n"; + dodie "Can't get flags for the socket: $!"; $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or - dodie "Can't set flags for the socket: $!\n"; + dodie "Can't set flags for the socket: $!"; return $pid; } @@ -221,8 +240,10 @@ sub fail { doprint "FAILED\n"; + my $i = $iteration; + # no need to reboot for just building. - if ($test_type ne "build") { + if (!do_not_reboot) { doprint "REBOOTING\n"; reboot; start_monitor; @@ -230,7 +251,11 @@ sub fail { end_monitor; } + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; doprint "**** Failed: ", @_, " ****\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; return 1 if (!defined($store_failures)); @@ -434,12 +459,12 @@ sub monitor { if ($bug) { return 0 if ($in_bisect); - fail "failed - got a bug report\n" and return 0; + fail "failed - got a bug report" and return 0; } if (!$booted) { return 0 if ($in_bisect); - fail "failed - never got a boot prompt.\n" and return 0; + fail "failed - never got a boot prompt." and return 0; } return 1; @@ -496,7 +521,8 @@ sub install { my $save_env = $ENV{KERNEL_VERSION}; $ENV{KERNEL_VERSION} = $version; - run_command "$post_install"; + run_command "$post_install" or + dodie "Failed to run post install"; $ENV{KERNEL_VERSION} = $save_env; } @@ -596,6 +622,11 @@ sub build { sub halt { if (!run_command "ssh $target halt" or defined($power_off)) { + if (defined($poweroff_after_halt)) { + sleep $poweroff_after_halt; + run_command "$power_off"; + } + } else { # nope? the zap it! run_command "$power_off"; } @@ -610,9 +641,7 @@ sub success { doprint "*******************************************\n"; doprint "*******************************************\n"; - if ($i != $opt{"NUM_TESTS"} && $test_type ne "build" && - !($test_type eq "patchcheck" && $opt{"PATCHCHECK_TYPE[$i]"} eq "build") && - !($test_type eq "bisect" && $opt{"BISECT_TYPE[$i]"} eq "build")) { + if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) { doprint "Reboot and wait $sleep_time seconds\n"; reboot; start_monitor; @@ -1048,6 +1077,8 @@ sub set_test_option { # First we need to do is the builds for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { + $iteration = $i; + my $ssh_user = set_test_option("SSH_USER", $i); my $makecmd = set_test_option("MAKE_CMD", $i); @@ -1071,6 +1102,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $poweroff_on_error = set_test_option("POWEROFF_ON_ERROR", $i); $die_on_failure = set_test_option("DIE_ON_FAILURE", $i); $power_off = set_test_option("POWER_OFF", $i); + $powercycle_after_reboot = set_test_option("POWERCYCLE_AFTER_REBOOT", $i); + $poweroff_after_halt = set_test_option("POWEROFF_AFTER_HALT", $i); $sleep_time = set_test_option("SLEEP_TIME", $i); $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i); $store_failures = set_test_option("STORE_FAILURES", $i); @@ -1096,9 +1129,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $make = "$makecmd O=$outputdir"; if ($reboot_type eq "grub") { - dodie "GRUB_MENU not defined\n" if (!defined($grub_menu)); + dodie "GRUB_MENU not defined" if (!defined($grub_menu)); } elsif (!defined($reboot_script)) { - dodie "REBOOT_SCRIPT not defined\n" + dodie "REBOOT_SCRIPT not defined" } my $run_type = $build_type; @@ -1167,7 +1200,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { if ($opt{"POWEROFF_ON_SUCCESS"}) { halt; -} elsif ($opt{"REBOOT_ON_SUCCESS"} && $test_type ne "build") { +} elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot) { reboot; } diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 0115a67..546014a 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -160,6 +160,24 @@ # (ignored if POWEROFF_ON_SUCCESS is set) #REBOOT_ON_SUCCESS = 1 +# In case there's isses with rebooting, you can specify this +# to always powercycle after this amount of time after calling +# reboot. +# Note, POWERCYCLE_AFTER_REBOOT = 0 does NOT disable it. It just +# makes it powercycle immediately after rebooting. Do not define +# it if you do not want it. +# (default undefined) +#POWERCYCLE_AFTER_REBOOT = 5 + +# In case there's isses with halting, you can specify this +# to always poweroff after this amount of time after calling +# halt. +# Note, POWEROFF_AFTER_HALT = 0 does NOT disable it. It just +# makes it poweroff immediately after halting. Do not define +# it if you do not want it. +# (default undefined) +#POWEROFF_AFTER_HALT = 20 + # Stop testing if a build fails. If set, the script will end if # a failure is detected, otherwise it will save off the .config, # dmesg and bootlog in a directory called -- cgit v0.10.2 From a57419b366a20009c9363c3cdad2449dc2fa8b9c Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 15:13:54 -0400 Subject: ktest: New TEST_START instead of using [], and use real SHA1s Change the config to use TEST_START where the options after a TEST_START automatically get the [] as it is read and they do not need to exist in the config file; TEST_START MIN_CONFIG = myconfig is the same as MIN_CONFIG[1] = myconfig The benefit is that you no longer need to keep track of test numbers with tests. Also process the commit ids that are passed to the options to get the actually SHA1 so it is no longer relative to the branch. Ie, saying HEAD will get the current SHA1 and then that will be used, and will work even if another branch is checked out. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index ef97817..a7e86e3 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -11,21 +11,23 @@ use File::Path qw(mkpath); use File::Copy qw(cp); use FileHandle; -$#ARGV >= 0 || die "usage: autotest.pl config-file\n"; +$#ARGV >= 0 || die "usage: ktest.pl config-file\n"; $| = 1; my %opt; +my %repeat_tests; +my %repeats; my %default; #default opts -$default{"NUM_TESTS"} = 5; +$default{"NUM_TESTS"} = 1; $default{"REBOOT_TYPE"} = "grub"; $default{"TEST_TYPE"} = "test"; $default{"BUILD_TYPE"} = "randconfig"; $default{"MAKE_CMD"} = "make"; $default{"TIMEOUT"} = 120; -$default{"TMP_DIR"} = "/tmp/autotest"; +$default{"TMP_DIR"} = "/tmp/ktest"; $default{"SLEEP_TIME"} = 60; # sleep time between tests $default{"BUILD_NOCLEAN"} = 0; $default{"REBOOT_ON_ERROR"} = 0; @@ -87,29 +89,138 @@ my $target_image; my $localversion; my $iteration = 0; +sub set_value { + my ($lvalue, $rvalue) = @_; + + if (defined($opt{$lvalue})) { + die "Error: Option $lvalue defined more than once!\n"; + } + $opt{$lvalue} = $rvalue; +} + sub read_config { my ($config) = @_; open(IN, $config) || die "can't read file $config"; + my $name = $config; + $name =~ s,.*/(.*),$1,; + + my $test_num = 0; + my $default = 1; + my $repeat = 1; + my $num_tests_set = 0; + my $skip = 0; + my $rest; + while () { # ignore blank lines and comments next if (/^\s*$/ || /\s*\#/); - if (/^\s*(\S+)\s*=\s*(.*?)\s*$/) { + if (/^\s*TEST_START(.*)/) { + + $rest = $1; + + if ($num_tests_set) { + die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n"; + } + + my $old_test_num = $test_num; + + $test_num += $repeat; + $default = 0; + $repeat = 1; + + if ($rest =~ /\s+SKIP(.*)/) { + $rest = $1; + $skip = 1; + } else { + $skip = 0; + } + + if ($rest =~ /\s+ITERATE\s+(\d+)(.*)$/) { + $repeat = $1; + $rest = $2; + $repeat_tests{"$test_num"} = $repeat; + } + + if ($rest =~ /\s+SKIP(.*)/) { + $rest = $1; + $skip = 1; + } + + if ($rest !~ /^\s*$/) { + die "$name: $.: Gargbage found after TEST_START\n$_"; + } + + if ($skip) { + $test_num = $old_test_num; + $repeat = 1; + } + + } elsif (/^\s*DEFAULTS(.*)$/) { + $default = 1; + + $rest = $1; + + if ($rest =~ /\s+SKIP(.*)/) { + $rest = $1; + $skip = 1; + } else { + $skip = 0; + } + + if ($rest !~ /^\s*$/) { + die "$name: $.: Gargbage found after DEFAULTS\n$_"; + } + + } elsif (/^\s*([A-Z_\[\]\d]+)\s*=\s*(.*?)\s*$/) { + + next if ($skip); + my $lvalue = $1; my $rvalue = $2; - if (defined($opt{$lvalue})) { - die "Error: Option $lvalue defined more than once!\n"; + if (!$default && + ($lvalue eq "NUM_TESTS" || + $lvalue eq "LOG_FILE" || + $lvalue eq "CLEAR_LOG")) { + die "$name: $.: $lvalue must be set in DEFAULTS section\n"; + } + + if ($lvalue eq "NUM_TESTS") { + if ($test_num) { + die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n"; + } + if (!$default) { + die "$name: $.: NUM_TESTS must be set in default section\n"; + } + $num_tests_set = 1; + } + + if ($default || $lvalue =~ /\[\d+\]$/) { + set_value($lvalue, $rvalue); + } else { + my $val = "$lvalue\[$test_num\]"; + set_value($val, $rvalue); + + if ($repeat > 1) { + $repeats{$val} = $repeat; + } } - $opt{$lvalue} = $rvalue; + } else { + die "$name: $.: Garbage found in config\n$_"; } } close(IN); + if ($test_num) { + $test_num += $repeat - 1; + $opt{"NUM_TESTS"} = $test_num; + } + # set any defaults foreach my $default (keys %default) { @@ -398,6 +509,27 @@ sub reboot_to { run_command "$reboot_script"; } +sub get_sha1 { + my ($commit) = @_; + + doprint "git rev-list --max-count=1 $commit ... "; + my $sha1 = `git rev-list --max-count=1 $commit`; + my $ret = $?; + + logit $sha1; + + if ($ret) { + doprint "FAILED\n"; + dodie "Failed to get git $commit"; + } + + print "SUCCESS\n"; + + chomp $sha1; + + return $sha1; +} + sub monitor { my $booted = 0; my $bug = 0; @@ -497,7 +629,7 @@ sub install { dodie "Failed to install modules"; my $modlib = "/lib/modules/$version"; - my $modtar = "autotest-mods.tar.bz2"; + my $modtar = "ktest-mods.tar.bz2"; run_command "ssh $target rm -rf $modlib" or dodie "failed to remove old mods: $modlib"; @@ -840,6 +972,10 @@ sub bisect { my $start = $opt{"BISECT_START[$i]"}; my $replay = $opt{"BISECT_REPLAY[$i]"}; + # convert to true sha1's + $good = get_sha1($good); + $bad = get_sha1($bad); + if (defined($opt{"BISECT_REVERSE[$i]"}) && $opt{"BISECT_REVERSE[$i]"} == 1) { doprint "Performing a reverse bisect (bad is good, good is bad!)\n"; @@ -859,20 +995,7 @@ sub bisect { if (defined($check) && $check ne "0") { # get current HEAD - doprint "git rev-list HEAD --max-count=1 ... "; - my $head = `git rev-list HEAD --max-count=1`; - my $ret = $?; - - logit $head; - - if ($ret) { - doprint "FAILED\n"; - dodie "Failed to get git HEAD"; - } - - print "SUCCESS\n"; - - chomp $head; + my $head = get_sha1("HEAD"); if ($check ne "good") { doprint "TESTING BISECT BAD [$bad]\n"; @@ -956,6 +1079,10 @@ sub patchcheck { $end = $opt{"PATCHCHECK_END[$i]"}; } + # Get the true sha1's since we can use things like HEAD~3 + $start = get_sha1($start); + $end = get_sha1($end); + my $type = $opt{"PATCHCHECK_TYPE[$i]"}; # Can't have a test without having a test to run @@ -1054,8 +1181,29 @@ if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) { doprint "\n\nSTARTING AUTOMATED TESTS\n\n"; -foreach my $option (sort keys %opt) { - doprint "$option = $opt{$option}\n"; +for (my $i = 0, my $repeat = 1; $i <= $opt{"NUM_TESTS"}; $i += $repeat) { + + if (!$i) { + doprint "DEFAULT OPTIONS:\n"; + } else { + doprint "\nTEST $i OPTIONS"; + if (defined($repeat_tests{$i})) { + $repeat = $repeat_tests{$i}; + doprint " ITERATE $repeat"; + } + doprint "\n"; + } + + foreach my $option (sort keys %opt) { + + if ($option =~ /\[(\d+)\]$/) { + next if ($i != $1); + } else { + next if ($i); + } + + doprint "$option = $opt{$option}\n"; + } } sub set_test_option { @@ -1067,6 +1215,16 @@ sub set_test_option { return $opt{$option}; } + foreach my $test (keys %repeat_tests) { + if ($i >= $test && + $i < $test + $repeat_tests{$test}) { + $option = "$name\[$test\]"; + if (defined($opt{$option})) { + return $opt{$option}; + } + } + } + if (defined($opt{$name})) { return $opt{$name}; } diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 546014a..9236fe9 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -1,25 +1,83 @@ # -# Config file for autotest.pl +# Config file for ktest.pl # # Note, all paths must be absolute # -# Almost all options may be overwritten per test run, by appending -# a [x] to the config. For example, to change the test type for -# the third iteration of tests, you can specify: -# (1 is for the first test, 2 for the second, and so on) +# Options set in the beginning of the file are considered to be +# default options. These options can be overriden by test specific +# options, with the following exceptions: # -# TEST_TYPE[3] = build -# -# The options that can not be changed like this are: -# NUM_TESTS # LOG_FILE # CLEAR_LOG # POWEROFF_ON_SUCCESS # REBOOT_ON_SUCCESS # +# Test specific options are set after the label: +# +# TEST_START +# +# The options after a TEST_START label are specific to that test. +# Each TEST_START label will set up a new test. If you want to +# perform a test more than once, you can add the ITERATE label +# to it followed by the number of times you want that test +# to iterate. If the ITERATE is left off, the test will only +# be performed once. +# +# TEST_START ITERATE 10 +# +# You can skip a test by adding SKIP (before or after the ITERATE +# and number) +# +# TEST_START SKIP +# +# TEST_START SKIP ITERATE 10 +# +# TEST_START ITERATE 10 SKIP +# +# The SKIP label causes the options and the test itself to be ignored. +# This is useful to set up several different tests in one config file, and +# only enabling the ones you want to use for a current test run. +# +# You can add default options anywhere in the file as well +# with the DEFAULTS tag. This allows you to have default options +# after the test options to keep the test options at the top +# of the file. You can even place the DEFAULTS tag between +# test cases (but not in the middle of a single test case) +# +# TEST_START +# MIN_CONFIG = /home/test/config-test1 +# +# DEFAULTS +# MIN_CONFIG = /home/test/config-default +# +# TEST_START ITERATE 10 +# +# The above will run the first test with MIN_CONFIG set to +# /home/test/config-test-1. Then 10 tests will be executed +# with MIN_CONFIG with /home/test/config-default. +# +# You can also disable defaults with the SKIP option +# +# DEFAULTS SKIP +# MIN_CONFIG = /home/test/config-use-sometimes +# +# DEFAULTS +# MIN_CONFIG = /home/test/config-most-times +# +# The above will ignore the first MIN_CONFIG. If you want to +# use the first MIN_CONFIG, remove the SKIP from the first +# DEFAULTS tag and add it to the second. Be careful, options +# may only be declared once per test or default. If you have +# the same option name under the same test or as default +# ktest will fail to execute, and no tests will run. +# + + +#### Mandatory Default Options #### -#### Mandatory Config Options #### +# These options must be in the default section, although most +# may be overridden by test options. # The machine hostname that you will test #MACHINE = target @@ -43,17 +101,21 @@ #TARGET_IMAGE = /boot/vmlinuz-test # A script or command to reboot the box +# # Here is a digital loggers power switch example #POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=CCL' +# # Here is an example to reboot a virtual box on the current host # with the name "Guest". -#POWER_CYCLE = virsh list | grep '\' | awk '{printf ("%d", $1)}' | xargs virsh destroy; sleep 5; virsh start Guest +#POWER_CYCLE = virsh destroy Guest; sleep 5; virsh start Guest # The script or command that reads the console +# # If you use ttywatch server, something like the following would work. #CONSOLE = nc -d localhost 3001 +# # For a virtual machine with guest name "Guest". -#CONSOLE = virsh console `virsh list | grep '\' | awk '{printf ("%d", $1)}'` +#CONSOLE = virsh console Guest # Required version ending to differentiate the test # from other linux builds on the system. @@ -62,8 +124,14 @@ # The grub title name for the test kernel to boot # (Only mandatory if REBOOT_TYPE = grub) # +# Note, ktest.pl will not update the grub menu.lst, you need to +# manually add an option for the test. ktest.pl will search +# the grub menu.lst for this option to find what kernel to +# reboot into. +# # For example, if in the /boot/grub/menu.lst the test kernel title has: # title Test Kernel +# kernel vmlinuz-test #GRUB_MENU = Test Kernel # A script to reboot the target into the test kernel @@ -72,21 +140,37 @@ #### Optional Config Options (all have defaults) #### -# The number of tests to run (default 5) -#NUM_TESTS = 5 +# Start a test setup. If you leave this off, all options +# will be default and the test will run once. +# This is a label and not really an option (it takes no value). +# You can append ITERATE and a number after it to iterate the +# test a number of times, or SKIP to ignore this test. +# +#TEST_START +#TEST_START ITERATE 5 +#TEST_START SKIP # The default test type (default test) # The test types may be: # build - only build the kernel, do nothing else # boot - build and boot the kernel # test - build, boot and if TEST is set, run the test script +# (If TEST is not set, it defaults back to boot) # bisect - Perform a bisect on the kernel (see BISECT_TYPE below) # patchcheck - Do a test on a series of commits in git (see PATCHCHECK below) #TEST_TYPE = test -# The build type is any make config type or a command. +# Test to run if there is a successful boot and TEST_TYPE is test. +# Must exit with 0 on success and non zero on error +# default (undefined) +#TEST = ssh user@machine /root/run_test + +# The build type is any make config type or special command # (default randconfig) # nobuild - skip the clean and build step +# useconfig:/path/to/config - use the given config and run +# oldconfig on it. +# This option is ignored if TEST_TYPE is patchcheck or bisect #BUILD_TYPE = randconfig # The make command (default make) @@ -95,8 +179,14 @@ # If you need an initrd, you can add a script or code here to install # it. The environment variable KERNEL_VERSION will be set to the -# kernel version that is used. +# kernel version that is used. Remember to add the initrd line +# to your grub menu.lst file. +# +# Here's a couple of examples to use: #POST_INSTALL = ssh user@target /sbin/mkinitrd --allow-missing -f /boot/initramfs-test.img $KERNEL_VERSION +# +# or on some systems: +#POST_INSTALL = ssh user@target /sbin/dracut -f /boot/initramfs-test.img $KERNEL_VERSION # Way to reboot the box to the test kernel. # Only valid options so far are "grub" and "script" @@ -106,12 +196,19 @@ # and select that target to reboot to the kernel. If this is not # your setup, then specify "script" and have a command or script # specified in REBOOT_SCRIPT to boot to the target. +# +# The entry in /boot/grub/menu.lst must be entered in manually. +# The test will not modify that file. #REBOOT_TYPE = grub -# Line to define success in output. (default "login:") +# Line to define a successful boot up in console output. # This is what the line contains, not the entire line. If you need -# the entire line to match, then use regural expression syntax like -# ^MyBox Login:$ +# the entire line to match, then use regural expression syntax like: +# (do not add any quotes around it) +# +# SUCCESS_LINE = ^MyBox Login:$ +# +# (default "login:") #SUCCESS_LINE = login: # As the test reads the console, after it hits the SUCCESS_LINE @@ -121,24 +218,33 @@ #BOOTED_TIMEOUT = 1 # The timeout in seconds when we consider the box hung after -# the console stop producing output. +# the console stop producing output. Be sure to leave enough +# time here to get pass a reboot. Some machines may not produce +# any console output for a long time during a reboot. You do +# not want the test to fail just because the system was in +# the process of rebooting to the test kernel. # (default 120) #TIMEOUT = 120 # The location on the host where to write temp files -# (default /tmp/autotest) -#TMP_DIR = /tmp/autotest +# (default /tmp/ktest) +#TMP_DIR = /tmp/ktest # In between tests, a reboot of the box may occur, and this # is the time to wait for the console after it stops producing # output. Some machines may not produce a large lag on reboot # so this should accommodate it. +# The difference between this and TIMEOUT, is that TIMEOUT happens +# when rebooting to the test kernel. This sleep time happens +# after a test has completed and we are about to start running +# another test. If a reboot to the reliable kernel happens, +# we wait SLEEP_TIME for the console to stop producing output +# before starting the next test. # (default 60) #SLEEP_TIME = 60 # The time in between bisects to sleep (in seconds) -# Can be less than SLEEP_TIME since bisects do more work -# in between boots. (default 60) +# (default 60) #BISECT_SLEEP_TIME = 60 # Build without doing a make mrproper, or removing .config @@ -149,10 +255,12 @@ #REBOOT_ON_ERROR = 0 # Power off the target on error (ignored if REBOOT_ON_ERROR is set) +# Note, this is a DEFAULT section only option. # (default 0) #POWEROFF_ON_ERROR = 0 # Power off the target after all tests have completed successfully +# Note, this is a DEFAULT section only option. # (default 0) #POWEROFF_ON_SUCCESS = 0 @@ -160,7 +268,7 @@ # (ignored if POWEROFF_ON_SUCCESS is set) #REBOOT_ON_SUCCESS = 1 -# In case there's isses with rebooting, you can specify this +# In case there are isses with rebooting, you can specify this # to always powercycle after this amount of time after calling # reboot. # Note, POWERCYCLE_AFTER_REBOOT = 0 does NOT disable it. It just @@ -190,43 +298,68 @@ # Directory to store failure directories on failure. If this is not # set, DIE_ON_FAILURE=0 will not save off the .config, dmesg and -# bootlog. +# bootlog. This option is ignored if DIE_ON_FAILURE is not set. +# (default undefined) #STORE_FAILURES = /home/test/failures -# A script or command to power off the box (default undef) +# A script or command to power off the box (default undefined) # Needed for POWEROFF_ON_ERROR and SUCCESS +# # Example for digital loggers power switch: #POWER_OFF = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=OFF' +# # Example for a virtual guest call "Guest". -#POWER_OFF = virsh list | grep '\' | awk '{printf ("%d", $1)}' | xargs virsh destroy +#POWER_OFF = virsh destroy Guest -# Any build options for the make (default "") +# Any build options for the make of the kernel (not for other makes, like configs) +# (default "") #BUILD_OPTIONS = -j20 # Optional log file to write the status (recommended) -# (default undef) +# Note, this is a DEFAULT section only option. +# (default undefined) #LOG_FILE = /home/test/logfiles/target.log # Remove old logfile if it exists before starting all tests. +# Note, this is a DEFAULT section only option. # (default 0) #CLEAR_LOG = 0 -# Test to run if there is a successful boot and TEST_TYPE is test. -# Must exit with 0 on success and non zero on error -# default (undef) -#TEST = ssh user@machine /root/run_test -#TEST[1] = ssh root@mxtest /root/run_test - # The min config that is needed to build for the machine -# A nice way to get this to work, is to do a "lsmod > mymods" on the target -# copy it to the build server, and then run "make LSMOD=mymods localyesconfig". -# Then copy all the options that are set: "grep '^CONFIG' > /home/test/config-min" +# A nice way to create this is with the following: +# +# $ ssh target +# $ lsmod > mymods +# $ scp mymods host:/tmp +# $ exit +# $ cd linux.git +# $ rm .config +# $ make LSMOD=mymods localyesconfig +# $ grep '^CONFIG' .config > /home/test/config-min +# +# If you want even less configs: +# +# log in directly to target (do not ssh) +# +# $ su +# # lsmod | cut -d' ' -f1 | xargs rmmod +# +# repeat the above several times # -# You might want to set: +# # lsmod > mymods +# # reboot +# +# May need to reboot to get your network back to copy the mymods +# to the host, and then remove the previous .config and run the +# localyesconfig again. The CONFIG_MIN generated like this will +# not guarantee network activity to the box so the TEST_TYPE of +# test may fail. +# +# You might also want to set: # CONFIG_CMDLINE="" # randconfig may set the above and override your real command # line options. -# (default undef) +# (default undefined) #MIN_CONFIG = /home/test/config-min # Sometimes there's options that just break the boot and @@ -239,34 +372,47 @@ # KGDB may cause oops waiting for a connection that's not there. # This option points to the file containing config options that will be prepended # to the MIN_CONFIG (or be the MIN_CONFIG if it is not set) -# before running it through randconfig -# (default undef) +# +# Note, config options in MIN_CONFIG will override these options. +# +# (default undefined) #ADD_CONFIG = /home/test/config-broken #### Per test run options #### -# These are options are per build only. The only exist with the [x] -# syntax, and there is no general option. +# The following options are only allowed in TEST_START sections. +# They are ignored in the DEFAULTS sections. # -# All are optional and undef by default +# All of these are optional and undefined by default, although +# some of these options are required for TEST_TYPE of patchcheck +# and bisect. # -# CHECKOUT[x] = branch +# +# CHECKOUT = branch # # If the BUILD_DIR is a git repository, then you can set this option # to checkout the given branch before running the TEST. If you # specify this for the first run, that branch will be used for -# all preceding tests until a new CHECKOUT[x] is set. +# all preceding tests until a new CHECKOUT is set. +# # -# For TEST_TYPE[x] = patchcheck +# +# For TEST_TYPE = patchcheck # # This expects the BUILD_DIR to be a git repository, and -# will checkout the PATCHCHECK_START[x]. +# will checkout the PATCHCHECK_START commit. +# +# The option BUILD_TYPE will be ignored. # -# PATCHCHECK_START[x] is required and is the first patch to -# test (the SHA1 of the commit). +# The MIN_CONFIG will be used for all builds of the patchcheck. The build type +# used for patchcheck is oldconfig. # -# PATCHCHECK_END[x] is the last patch to check (default HEAD) +# PATCHCHECK_START is required and is the first patch to +# test (the SHA1 of the commit). You may also specify anything +# that git checkout allows (branch name, tage, HEAD~3). # -# PATCHCHECK_TYPE[x] is required and is the type of test to run: +# PATCHCHECK_END is the last patch to check (default HEAD) +# +# PATCHCHECK_TYPE is required and is the type of test to run: # build, boot, test. # # Note, the build test will look for warnings, if a warning occurred @@ -279,75 +425,86 @@ # make mrproper. This helps speed up the test. # # Example: -# TEST_TYPE[1] = patchcheck -# CHECKOUT[1] = mybranch -# PATCHCHECK_TYPE[1] = boot -# PATCHCHECK_START[1] = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 -# PATCHCHEKC_END[1] = b8b2663bd7c9da04ac804659b9f617c199d0252c +# TEST_START +# TEST_TYPE = patchcheck +# CHECKOUT = mybranch +# PATCHCHECK_TYPE = boot +# PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 +# PATCHCHEKC_END = HEAD~2 +# # # -# For TEST_TYPE[x] = bisect +# For TEST_TYPE = bisect # -# You can specify a git bisect if the BUILD_DIR is a git repository. -# The MIN_CONFIG will be used for all builds of the bisect. The build type -# used for bisecting is oldconfig. +# You can specify a git bisect if the BUILD_DIR is a git repository. +# The MIN_CONFIG will be used for all builds of the bisect. The build type +# used for bisecting is oldconfig. # -# BISECT_TYPE[x] is the type of test to perform: +# The option BUILD_TYPE will be ignored. +# +# BISECT_TYPE is the type of test to perform: # build - bad fails to build # boot - bad builds but fails to boot # test - bad boots but fails a test # -# BISECT_GOOD[x] is the commit (SHA1) to label as good -# BISECT_BAD[x] is the commit to label as bad +# BISECT_GOOD is the commit (SHA1) to label as good (accepts all git good commit types) +# BISECT_BAD is the commit to label as bad (accepts all git bad commit types) # # The above three options are required for a bisect operation. # -# BISECT_REPLAY[x] = /path/to/replay/file (optional, default undefined) +# BISECT_REPLAY = /path/to/replay/file (optional, default undefined) # # If an operation failed in the bisect that was not expected to # fail. Then the test ends. The state of the BUILD_DIR will be -# left off at where the failur occurred. You can examine the +# left off at where the failure occurred. You can examine the # reason for the failure, and perhaps even find a git commit # that would work to continue with. You can run: # # git bisect log > /path/to/replay/file # -# and if BISECT_REPLAY[x] is set, the test will run git bisect replay -# before continuing with the bisect. +# The adding: # -# BISECT_START[x] = commit (optional, default undefined) +# BISECT_REPLAY= /path/to/replay/file # -# As with BISECT_REPLAY[x], if the test failed on a commit that -# just happen to have a bad commit in the middle of the bisect, -# and you need to skip it. If BISECT_START[x] is defined, it -# will checkout that commit before continuing with the bisect. +# And running the test again. The test will perform the initial +# git bisect start, git bisect good, and git bisect bad, and +# then it will run git bisect replay on this file, before +# continuing with the bisect. # -# Note, BISECT_REPLAY[x] is executed before BISECT_START[x]. +# BISECT_START = commit (optional, default undefined) +# +# As with BISECT_REPLAY, if the test failed on a commit that +# just happen to have a bad commit in the middle of the bisect, +# and you need to skip it. If BISECT_START is defined, it +# will checkout that commit after doing the initial git bisect start, +# git bisect good, git bisect bad, and running the git bisect replay +# if the BISECT_REPLAY is set. # -# BISECT_REVERSE[x] = 1 (optional, default 0) +# BISECT_REVERSE = 1 (optional, default 0) # # In those strange instances where it was broken forever # and you are trying to find where it started to work! -# Set BISECT_GOOD[x] to the commit that was last known to fail -# Set BISECT_BAD[x] to the commit that is known where it started -# to work. With BISECT_REVERSE[x] = 1, The test will consider -# failures as good, and success as bad. +# Set BISECT_GOOD to the commit that was last known to fail +# Set BISECT_BAD to the commit that is known to start working. +# With BISECT_REVERSE = 1, The test will consider failures as +# good, and success as bad. # -# BISECT_CHECK[x] = 1 (optional, default 0) +# BISECT_CHECK = 1 (optional, default 0) # # Just to be sure the good is good and bad is bad, setting -# BISECT_CHECK[x] to 1 will start the bisect by first checking -# out BISECT_BAD[x] and makes sure it fails, then it will check -# out BISECT_GOOD[x] and makes sure it succeeds before starting -# the bisect (it works for BISECT_REVERSE[x] too). +# BISECT_CHECK to 1 will start the bisect by first checking +# out BISECT_BAD and makes sure it fails, then it will check +# out BISECT_GOOD and makes sure it succeeds before starting +# the bisect (it works for BISECT_REVERSE too). # -# You can limit the test to just check BISECT_GOOD[x] or -# BISECT_BAD[x] with BISECT_CHECK[x] = good or -# BISECT_CHECK[x] = bad, respectively. +# You can limit the test to just check BISECT_GOOD or +# BISECT_BAD with BISECT_CHECK = good or +# BISECT_CHECK = bad, respectively. # # Example: -# TEST_TYPE[1] = bisect -# BISECT_GOOD[1] = v2.6.36 -# BISECT_BAD[1] = b5153163ed580e00c67bdfecb02b2e3843817b3e -# BISECT_TYPE[1] = build -# MIN_CONFIG[1] = /home/test/config-bisect +# TEST_START +# TEST_TYPE = bisect +# BISECT_GOOD = v2.6.36 +# BISECT_BAD = b5153163ed580e00c67bdfecb02b2e3843817b3e +# BISECT_TYPE = build +# MIN_CONFIG = /home/test/config-bisect -- cgit v0.10.2 From dc89568884ae1b8b96ca6fffe83b404ae472750e Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 15:22:53 -0400 Subject: ktest: Update the sample config file with more documentation Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 9236fe9..03d6e91 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -150,6 +150,18 @@ #TEST_START ITERATE 5 #TEST_START SKIP +# Have the following options as default again. Used after tests +# have already been defined by TEST_START. Optionally, you can +# just define all default options before the first TEST_START +# and you do not need this option. +# +# This is a label and not really an option (it takes no value). +# You can append SKIP to this label and the options within this +# section will be ignored. +# +# DEFAULTS +# DEFAULTS SKIP + # The default test type (default test) # The test types may be: # build - only build the kernel, do nothing else @@ -177,6 +189,10 @@ # If you are building a 32bit x86 on a 64 bit host #MAKE_CMD = CC=i386-gcc AS=i386-as make ARCH=i386 +# Any build options for the make of the kernel (not for other makes, like configs) +# (default "") +#BUILD_OPTIONS = -j20 + # If you need an initrd, you can add a script or code here to install # it. The environment variable KERNEL_VERSION will be set to the # kernel version that is used. Remember to add the initrd line @@ -201,6 +217,73 @@ # The test will not modify that file. #REBOOT_TYPE = grub +# The min config that is needed to build for the machine +# A nice way to create this is with the following: +# +# $ ssh target +# $ lsmod > mymods +# $ scp mymods host:/tmp +# $ exit +# $ cd linux.git +# $ rm .config +# $ make LSMOD=mymods localyesconfig +# $ grep '^CONFIG' .config > /home/test/config-min +# +# If you want even less configs: +# +# log in directly to target (do not ssh) +# +# $ su +# # lsmod | cut -d' ' -f1 | xargs rmmod +# +# repeat the above several times +# +# # lsmod > mymods +# # reboot +# +# May need to reboot to get your network back to copy the mymods +# to the host, and then remove the previous .config and run the +# localyesconfig again. The CONFIG_MIN generated like this will +# not guarantee network activity to the box so the TEST_TYPE of +# test may fail. +# +# You might also want to set: +# CONFIG_CMDLINE="" +# randconfig may set the above and override your real command +# line options. +# (default undefined) +#MIN_CONFIG = /home/test/config-min + +# Sometimes there's options that just break the boot and +# you do not care about. Here are a few: +# # CONFIG_STAGING is not set +# Staging drivers are horrible, and can break the build. +# # CONFIG_SCSI_DEBUG is not set +# SCSI_DEBUG may change your root partition +# # CONFIG_KGDB_SERIAL_CONSOLE is not set +# KGDB may cause oops waiting for a connection that's not there. +# This option points to the file containing config options that will be prepended +# to the MIN_CONFIG (or be the MIN_CONFIG if it is not set) +# +# Note, config options in MIN_CONFIG will override these options. +# +# (default undefined) +#ADD_CONFIG = /home/test/config-broken + +# The location on the host where to write temp files +# (default /tmp/ktest) +#TMP_DIR = /tmp/ktest + +# Optional log file to write the status (recommended) +# Note, this is a DEFAULT section only option. +# (default undefined) +#LOG_FILE = /home/test/logfiles/target.log + +# Remove old logfile if it exists before starting all tests. +# Note, this is a DEFAULT section only option. +# (default 0) +#CLEAR_LOG = 0 + # Line to define a successful boot up in console output. # This is what the line contains, not the entire line. If you need # the entire line to match, then use regural expression syntax like: @@ -211,6 +294,26 @@ # (default "login:") #SUCCESS_LINE = login: +# Stop testing if a build fails. If set, the script will end if +# a failure is detected, otherwise it will save off the .config, +# dmesg and bootlog in a directory called +# MACHINE-TEST_TYPE_BUILD_TYPE-fail-yyyymmddhhmmss +# if the STORE_FAILURES directory is set. +# (default 1) +# Note, even if this is set to zero, there are some errors that still +# stop the tests. +#DIE_ON_FAILURE = 1 + +# Directory to store failure directories on failure. If this is not +# set, DIE_ON_FAILURE=0 will not save off the .config, dmesg and +# bootlog. This option is ignored if DIE_ON_FAILURE is not set. +# (default undefined) +#STORE_FAILURES = /home/test/failures + +# Build without doing a make mrproper, or removing .config +# (default 0) +#BUILD_NOCLEAN = 0 + # As the test reads the console, after it hits the SUCCESS_LINE # the time it waits for the monitor to settle down between reads # can usually be lowered. @@ -226,10 +329,6 @@ # (default 120) #TIMEOUT = 120 -# The location on the host where to write temp files -# (default /tmp/ktest) -#TMP_DIR = /tmp/ktest - # In between tests, a reboot of the box may occur, and this # is the time to wait for the console after it stops producing # output. Some machines may not produce a large lag on reboot @@ -247,10 +346,6 @@ # (default 60) #BISECT_SLEEP_TIME = 60 -# Build without doing a make mrproper, or removing .config -# (default 0) -#BUILD_NOCLEAN = 0 - # Reboot the target box on error (default 0) #REBOOT_ON_ERROR = 0 @@ -286,22 +381,6 @@ # (default undefined) #POWEROFF_AFTER_HALT = 20 -# Stop testing if a build fails. If set, the script will end if -# a failure is detected, otherwise it will save off the .config, -# dmesg and bootlog in a directory called -# MACHINE-TEST_TYPE_BUILD_TYPE-fail-yyyymmddhhmmss -# if the STORE_FAILURES directory is set. -# (default 1) -# Note, even if this is set to zero, there are some errors that still -# stop the tests. -#DIE_ON_FAILURE = 1 - -# Directory to store failure directories on failure. If this is not -# set, DIE_ON_FAILURE=0 will not save off the .config, dmesg and -# bootlog. This option is ignored if DIE_ON_FAILURE is not set. -# (default undefined) -#STORE_FAILURES = /home/test/failures - # A script or command to power off the box (default undefined) # Needed for POWEROFF_ON_ERROR and SUCCESS # @@ -311,73 +390,6 @@ # Example for a virtual guest call "Guest". #POWER_OFF = virsh destroy Guest -# Any build options for the make of the kernel (not for other makes, like configs) -# (default "") -#BUILD_OPTIONS = -j20 - -# Optional log file to write the status (recommended) -# Note, this is a DEFAULT section only option. -# (default undefined) -#LOG_FILE = /home/test/logfiles/target.log - -# Remove old logfile if it exists before starting all tests. -# Note, this is a DEFAULT section only option. -# (default 0) -#CLEAR_LOG = 0 - -# The min config that is needed to build for the machine -# A nice way to create this is with the following: -# -# $ ssh target -# $ lsmod > mymods -# $ scp mymods host:/tmp -# $ exit -# $ cd linux.git -# $ rm .config -# $ make LSMOD=mymods localyesconfig -# $ grep '^CONFIG' .config > /home/test/config-min -# -# If you want even less configs: -# -# log in directly to target (do not ssh) -# -# $ su -# # lsmod | cut -d' ' -f1 | xargs rmmod -# -# repeat the above several times -# -# # lsmod > mymods -# # reboot -# -# May need to reboot to get your network back to copy the mymods -# to the host, and then remove the previous .config and run the -# localyesconfig again. The CONFIG_MIN generated like this will -# not guarantee network activity to the box so the TEST_TYPE of -# test may fail. -# -# You might also want to set: -# CONFIG_CMDLINE="" -# randconfig may set the above and override your real command -# line options. -# (default undefined) -#MIN_CONFIG = /home/test/config-min - -# Sometimes there's options that just break the boot and -# you do not care about. Here are a few: -# # CONFIG_STAGING is not set -# Staging drivers are horrible, and can break the build. -# # CONFIG_SCSI_DEBUG is not set -# SCSI_DEBUG may change your root partition -# # CONFIG_KGDB_SERIAL_CONSOLE is not set -# KGDB may cause oops waiting for a connection that's not there. -# This option points to the file containing config options that will be prepended -# to the MIN_CONFIG (or be the MIN_CONFIG if it is not set) -# -# Note, config options in MIN_CONFIG will override these options. -# -# (default undefined) -#ADD_CONFIG = /home/test/config-broken - #### Per test run options #### # The following options are only allowed in TEST_START sections. # They are ignored in the DEFAULTS sections. -- cgit v0.10.2 From 9386c6ab7a33044d9907b00fc80976292bb02c2d Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 16:35:48 -0500 Subject: ktest: Use oldnoconfig instead of yes command Running the command "yes ''" through the make oldconfig may enable things we do not want enabled. If something is default enabled, the yes command with '' as an argument will enable it. Use oldnoconfig, which runs everything as if 'no' was used. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index a7e86e3..5bd0075 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -694,7 +694,6 @@ sub check_buildlog { sub build { my ($type) = @_; my $defconfig = ""; - my $append = ""; unlink $buildlog; @@ -707,7 +706,7 @@ sub build { # old config can ask questions if ($type eq "oldconfig") { - $append = "yes ''|"; + $type = "oldnoconfig"; # allow for empty configs run_command "touch $outputdir/.config"; @@ -737,7 +736,7 @@ sub build { $defconfig = "KCONFIG_ALLCONFIG=$minconfig"; } - run_command "$append $defconfig $make $type" or + run_command "$defconfig $make $type" or dodie "failed make config"; $redirect = "$buildlog"; -- cgit v0.10.2 From d1e2f22ad718c83ab19297e7717679c5ed17c020 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 16:39:57 -0500 Subject: ktest: Write to stdout if no log file is given If no LOG_FILE option is set, then write what would be logged to that file to standard output. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl old mode 100644 new mode 100755 index 5bd0075..0a5ed0d --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -230,7 +230,7 @@ sub read_config { } } -sub logit { +sub _logit { if (defined($opt{"LOG_FILE"})) { open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; print OUT @_; @@ -238,9 +238,17 @@ sub logit { } } +sub logit { + if (defined($opt{"LOG_FILE"})) { + _logit @_; + } else { + print @_; + } +} + sub doprint { print @_; - logit @_; + _logit @_; } sub run_command; -- cgit v0.10.2 From 51ad1dd1034684e9c490eb41c17cde8ffb682ab1 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 16:43:21 -0500 Subject: ktest: Use $output_config instead of typing $outputdir/.config To help prevent typos, use $output_config as the reference to "$outputdir/.config". Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 0a5ed0d..3d88be7 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -46,6 +46,7 @@ my $machine; my $tmpdir; my $builddir; my $outputdir; +my $output_config; my $test_type; my $build_type; my $build_options; @@ -389,8 +390,8 @@ sub fail { mkpath($faildir) or die "can't create $faildir"; } - if (-f "$outputdir/.config") { - cp "$outputdir/.config", "$faildir/config" or + if (-f "$output_config") { + cp "$output_config", "$faildir/config" or die "failed to copy .config"; } if (-f $buildlog) { @@ -619,7 +620,7 @@ sub install { # should we process modules? $install_mods = 0; - open(IN, "$outputdir/.config") or dodie("Can't read config file"); + open(IN, "$output_config") or dodie("Can't read config file"); while () { if (/CONFIG_MODULES(=y)?/) { $install_mods = 1 if (defined($1)); @@ -706,7 +707,7 @@ sub build { unlink $buildlog; if ($type =~ /^useconfig:(.*)/) { - run_command "cp $1 $outputdir/.config" or + run_command "cp $1 $output_config" or dodie "could not copy $1 to .config"; $type = "oldconfig"; @@ -717,20 +718,20 @@ sub build { $type = "oldnoconfig"; # allow for empty configs - run_command "touch $outputdir/.config"; + run_command "touch $output_config"; - run_command "mv $outputdir/.config $outputdir/config_temp" or + run_command "mv $output_config $outputdir/config_temp" or dodie "moving .config"; if (!$noclean && !run_command "$make mrproper") { dodie "make mrproper"; } - run_command "mv $outputdir/config_temp $outputdir/.config" or + run_command "mv $outputdir/config_temp $output_config" or dodie "moving config_temp"; } elsif (!$noclean) { - unlink "$outputdir/.config"; + unlink "$output_config"; run_command "$make mrproper" or dodie "make mrproper"; } @@ -1292,6 +1293,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $buildlog = "$tmpdir/buildlog-$machine"; $dmesg = "$tmpdir/dmesg-$machine"; $make = "$makecmd O=$outputdir"; + $output_config = "$outputdir/.config"; if ($reboot_type eq "grub") { dodie "GRUB_MENU not defined" if (!defined($grub_menu)); -- cgit v0.10.2 From 21a9679feadf6b215c4f932b0df5d252b4822c45 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 16:45:50 -0500 Subject: ktest: Allow a test case to undefine a default value Allow a test case in the config file to undefine a default value by specifying the option and equal sign but not assigning it a value: OPTION = Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 3d88be7..962c0f7 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -97,6 +97,11 @@ sub set_value { die "Error: Option $lvalue defined more than once!\n"; } $opt{$lvalue} = $rvalue; + if ($rvalue =~ /^\s*$/) { + delete $opt{$lvalue}; + } else { + $opt{$lvalue} = $rvalue; + } } sub read_config { -- cgit v0.10.2 From 7a849cd93ad2cf7d32427f3dbf5f524d5f588d20 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 16:49:25 -0500 Subject: ktest: Output something easy to parse for failure or success Have a easy way to parse the log file for success or failure. KTEST RESULT: ... Suggested-by: Tim Bird Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 962c0f7..e0e5935 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -378,7 +378,7 @@ sub fail { doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; - doprint "**** Failed: ", @_, " ****\n"; + doprint "KTEST RESULT: TEST $i Failed: ", @_, "\n"; doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; @@ -782,7 +782,7 @@ sub success { doprint "\n\n*******************************************\n"; doprint "*******************************************\n"; - doprint "** TEST $i SUCCESS!!!! **\n"; + doprint "KTEST RESULT: TEST $i SUCCESS!!!! **\n"; doprint "*******************************************\n"; doprint "*******************************************\n"; -- cgit v0.10.2 From e48c5293bde398e253f83fdd0247fb2bc71cc92f Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:35:37 -0400 Subject: ktest/cleanups: Added version 0.2, ssh as options Updated to version 0.2. Now have SSH_EXEC options. Also added some cleanups for keeping track of success and reading the config file. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index e0e5935..7b6f8e1 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -11,7 +11,9 @@ use File::Path qw(mkpath); use File::Copy qw(cp); use FileHandle; -$#ARGV >= 0 || die "usage: ktest.pl config-file\n"; +my $VERSION = "0.2"; + +$#ARGV >= 0 || die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; $| = 1; @@ -40,9 +42,13 @@ $default{"CLEAR_LOG"} = 0; $default{"SUCCESS_LINE"} = "login:"; $default{"BOOTED_TIMEOUT"} = 1; $default{"DIE_ON_FAILURE"} = 1; +$default{"SSH_EXEC"} = "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND"; +$default{"SCP_TO_TARGET"} = "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE"; +$default{"REBOOT"} = "ssh \$SSH_USER\@\$MACHINE reboot"; my $version; my $machine; +my $ssh_user; my $tmpdir; my $builddir; my $outputdir; @@ -53,11 +59,14 @@ my $build_options; my $reboot_type; my $reboot_script; my $power_cycle; +my $reboot; my $reboot_on_error; my $poweroff_on_error; my $die_on_failure; my $powercycle_after_reboot; my $poweroff_after_halt; +my $ssh_exec; +my $scp_to_target; my $power_off; my $grub_menu; my $grub_number; @@ -89,6 +98,7 @@ my $build_target; my $target_image; my $localversion; my $iteration = 0; +my $successes = 0; sub set_value { my ($lvalue, $rvalue) = @_; @@ -133,6 +143,7 @@ sub read_config { } my $old_test_num = $test_num; + my $old_repeat = $repeat; $test_num += $repeat; $default = 0; @@ -162,7 +173,7 @@ sub read_config { if ($skip) { $test_num = $old_test_num; - $repeat = 1; + $repeat = $old_repeat; } } elsif (/^\s*DEFAULTS(.*)$/) { @@ -261,7 +272,7 @@ sub run_command; sub reboot { # try to reboot normally - if (run_command "ssh $target reboot") { + if (run_command $reboot) { if (defined($powercycle_after_reboot)) { sleep $powercycle_after_reboot; run_command "$power_cycle"; @@ -419,6 +430,9 @@ sub run_command { my $dord = 0; my $pid; + $command =~ s/\$SSH_USER/$ssh_user/g; + $command =~ s/\$MACHINE/$machine/g; + doprint("$command ... "); $pid = open(CMD, "$command 2>&1 |") or @@ -457,6 +471,24 @@ sub run_command { return !$failed; } +sub run_ssh { + my ($cmd) = @_; + my $cp_exec = $ssh_exec; + + $cp_exec =~ s/\$SSH_COMMAND/$cmd/g; + return run_command "$cp_exec"; +} + +sub run_scp { + my ($src, $dst) = @_; + my $cp_scp = $scp_to_target; + + $cp_scp =~ s/\$SRC_FILE/$src/g; + $cp_scp =~ s/\$DST_FILE/$dst/g; + + return run_command "$cp_scp"; +} + sub get_grub_index { if ($reboot_type ne "grub") { @@ -466,8 +498,13 @@ sub get_grub_index { doprint "Find grub menu ... "; $grub_number = -1; - open(IN, "ssh $target cat /boot/grub/menu.lst |") + + my $ssh_grub = $ssh_exec; + $ssh_grub =~ s,\$SSH_COMMAND,cat /boot/grub/menu.lst,g; + + open(IN, "$ssh_grub |") or die "unable to get menu.lst"; + while () { if (/^\s*title\s+$grub_menu\s*$/) { $grub_number++; @@ -516,7 +553,7 @@ sub wait_for_input sub reboot_to { if ($reboot_type eq "grub") { - run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; + run_command "$ssh_exec '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; return; } @@ -618,7 +655,7 @@ sub monitor { sub install { - run_command "scp $outputdir/$build_target $target:$target_image" or + run_scp "$outputdir/$build_target", "$target_image" or dodie "failed to copy image"; my $install_mods = 0; @@ -645,32 +682,29 @@ sub install { my $modlib = "/lib/modules/$version"; my $modtar = "ktest-mods.tar.bz2"; - run_command "ssh $target rm -rf $modlib" or + run_ssh "rm -rf $modlib" or dodie "failed to remove old mods: $modlib"; # would be nice if scp -r did not follow symbolic links run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or dodie "making tarball"; - run_command "scp $tmpdir/$modtar $target:/tmp" or + run_scp "$tmpdir/$modtar", "/tmp" or dodie "failed to copy modules"; unlink "$tmpdir/$modtar"; - run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'" or + run_ssh "'(cd / && tar xf /tmp/$modtar)'" or dodie "failed to tar modules"; - run_command "ssh $target rm -f /tmp/$modtar"; + run_ssh "rm -f /tmp/$modtar"; return if (!defined($post_install)); - my $save_env = $ENV{KERNEL_VERSION}; - - $ENV{KERNEL_VERSION} = $version; - run_command "$post_install" or + my $cp_post_install = $post_install; + $cp_post_install = s/\$KERNEL_VERSION/$version/g; + run_command "$cp_post_install" or dodie "Failed to run post install"; - - $ENV{KERNEL_VERSION} = $save_env; } sub check_buildlog { @@ -766,7 +800,7 @@ sub build { } sub halt { - if (!run_command "ssh $target halt" or defined($power_off)) { + if (!run_ssh "halt" or defined($power_off)) { if (defined($poweroff_after_halt)) { sleep $poweroff_after_halt; run_command "$power_off"; @@ -780,6 +814,8 @@ sub halt { sub success { my ($i) = @_; + $successes++; + doprint "\n\n*******************************************\n"; doprint "*******************************************\n"; doprint "KTEST RESULT: TEST $i SUCCESS!!!! **\n"; @@ -1250,10 +1286,10 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $iteration = $i; - my $ssh_user = set_test_option("SSH_USER", $i); my $makecmd = set_test_option("MAKE_CMD", $i); $machine = set_test_option("MACHINE", $i); + $ssh_user = set_test_option("SSH_USER", $i); $tmpdir = set_test_option("TMP_DIR", $i); $outputdir = set_test_option("OUTPUT_DIR", $i); $builddir = set_test_option("BUILD_DIR", $i); @@ -1261,6 +1297,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $build_type = set_test_option("BUILD_TYPE", $i); $build_options = set_test_option("BUILD_OPTIONS", $i); $power_cycle = set_test_option("POWER_CYCLE", $i); + $reboot = set_test_option("REBOOT", $i); $noclean = set_test_option("BUILD_NOCLEAN", $i); $minconfig = set_test_option("MIN_CONFIG", $i); $run_test = set_test_option("TEST", $i); @@ -1283,6 +1320,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $console = set_test_option("CONSOLE", $i); $success_line = set_test_option("SUCCESS_LINE", $i); $build_target = set_test_option("BUILD_TARGET", $i); + $ssh_exec = set_test_option("SSH_EXEC", $i); + $scp_to_target = set_test_option("SCP_TO_TARGET", $i); $target_image = set_test_option("TARGET_IMAGE", $i); $localversion = set_test_option("LOCALVERSION", $i); @@ -1293,12 +1332,16 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { die "can't create $tmpdir"; } + $ENV{"SSH_USER"} = $ssh_user; + $ENV{"MACHINE"} = $machine; + $target = "$ssh_user\@$machine"; $buildlog = "$tmpdir/buildlog-$machine"; $dmesg = "$tmpdir/dmesg-$machine"; $make = "$makecmd O=$outputdir"; $output_config = "$outputdir/.config"; + $output_config = "$outputdir/.config"; if ($reboot_type eq "grub") { dodie "GRUB_MENU not defined" if (!defined($grub_menu)); @@ -1376,4 +1419,6 @@ if ($opt{"POWEROFF_ON_SUCCESS"}) { reboot; } +doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n"; + exit 0; -- cgit v0.10.2 From 0a05c769a9de554aefa773061c592c7054d5e7a0 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 11:14:10 -0500 Subject: ktest: Added config_bisect test type Added the ability to do a config_bisect. It starts with a bad config and does the following loop. Enable half the configs. if none of the configs to check are not enabled (caused by missing dependencies) enable the other half. Run the test if the test passes, remove the configs from the check but enabled them for further tests (to satisfy dependencies). else Remove any config that was not enabled, as we have found a new config that can cause a failure. loop till we have only one config left. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 7b6f8e1..0d10805 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -106,7 +106,6 @@ sub set_value { if (defined($opt{$lvalue})) { die "Error: Option $lvalue defined more than once!\n"; } - $opt{$lvalue} = $rvalue; if ($rvalue =~ /^\s*$/) { delete $opt{$lvalue}; } else { @@ -945,20 +944,18 @@ sub run_git_bisect { return 1; } -sub run_bisect { - my ($type) = @_; +# returns 1 on success, 0 on failure +sub run_bisect_test { + my ($type, $buildtype) = @_; my $failed = 0; my $result; my $output; my $ret; - if (defined($minconfig)) { - build "useconfig:$minconfig" or $failed = 1; - } else { - # ?? no config to use? - build "oldconfig" or $failed = 1; - } + $in_bisect = 1; + + build $buildtype or $failed = 1; if ($type ne "build") { dodie "Failed on build" if $failed; @@ -980,7 +977,7 @@ sub run_bisect { } if ($failed) { - $result = "bad"; + $result = 0; # reboot the box to a good kernel if ($type ne "build") { @@ -991,19 +988,35 @@ sub run_bisect { end_monitor; } } else { - $result = "good"; + $result = 1; + } + $in_bisect = 0; + + return $result; +} + +sub run_bisect { + my ($type) = @_; + my $buildtype = "oldconfig"; + + # We should have a minconfig to use? + if (defined($minconfig)) { + $buildtype = "useconfig:$minconfig"; } + my $ret = run_bisect_test $type, $buildtype; + + # Are we looking for where it worked, not failed? if ($reverse_bisect) { - if ($failed) { - $result = "good"; - } else { - $result = "bad"; - } + $ret = !$ret; } - return $result; + if ($ret) { + return "good"; + } else { + return "bad"; + } } sub bisect { @@ -1033,8 +1046,6 @@ sub bisect { $reverse_bisect = 0; } - $in_bisect = 1; - # Can't have a test without having a test to run if ($type eq "test" && !defined($run_test)) { $type = "boot"; @@ -1108,7 +1119,359 @@ sub bisect { doprint "Bad commit was [$bisect_bad]\n"; - $in_bisect = 0; + success $i; +} + +my %config_ignore; +my %config_set; + +my %config_list; +my %null_config; + +my %dependency; + +sub process_config_ignore { + my ($config) = @_; + + open (IN, $config) + or dodie "Failed to read $config"; + + while () { + if (/^(.*?(CONFIG\S*)(=.*| is not set))/) { + $config_ignore{$2} = $1; + } + } + + close(IN); +} + +sub read_current_config { + my ($config_ref) = @_; + + %{$config_ref} = (); + undef %{$config_ref}; + + my @key = keys %{$config_ref}; + if ($#key >= 0) { + print "did not delete!\n"; + exit; + } + open (IN, "$output_config"); + + while () { + if (/^(CONFIG\S+)=(.*)/) { + ${$config_ref}{$1} = $2; + } + } + close(IN); +} + +sub get_dependencies { + my ($config) = @_; + + my $arr = $dependency{$config}; + if (!defined($arr)) { + return (); + } + + my @deps = @{$arr}; + + foreach my $dep (@{$arr}) { + print "ADD DEP $dep\n"; + @deps = (@deps, get_dependencies $dep); + } + + return @deps; +} + +sub create_config { + my @configs = @_; + + open(OUT, ">$output_config") or dodie "Can not write to $output_config"; + + foreach my $config (@configs) { + print OUT "$config_set{$config}\n"; + my @deps = get_dependencies $config; + foreach my $dep (@deps) { + print OUT "$config_set{$dep}\n"; + } + } + + foreach my $config (keys %config_ignore) { + print OUT "$config_ignore{$config}\n"; + } + close(OUT); + +# exit; + run_command "$make oldnoconfig" or + dodie "failed make config oldconfig"; + +} + +sub compare_configs { + my (%a, %b) = @_; + + foreach my $item (keys %a) { + if (!defined($b{$item})) { + print "diff $item\n"; + return 1; + } + delete $b{$item}; + } + + my @keys = keys %b; + if ($#keys) { + print "diff2 $keys[0]\n"; + } + return -1 if ($#keys >= 0); + + return 0; +} + +sub run_config_bisect_test { + my ($type) = @_; + + return run_bisect_test $type, "oldconfig"; +} + +sub process_passed { + my (%configs) = @_; + + doprint "These configs had no failure: (Enabling them for further compiles)\n"; + # Passed! All these configs are part of a good compile. + # Add them to the min options. + foreach my $config (keys %configs) { + if (defined($config_list{$config})) { + doprint " removing $config\n"; + $config_ignore{$config} = $config_list{$config}; + delete $config_list{$config}; + } + } +} + +sub process_failed { + my ($config) = @_; + + doprint "\n\n***************************************\n"; + doprint "Found bad config: $config\n"; + doprint "***************************************\n\n"; +} + +sub run_config_bisect { + + my @start_list = keys %config_list; + + if ($#start_list < 0) { + doprint "No more configs to test!!!\n"; + return -1; + } + + doprint "***** RUN TEST ***\n"; + my $type = $opt{"CONFIG_BISECT_TYPE[$iteration]"}; + my $ret; + my %current_config; + + my $count = $#start_list + 1; + doprint " $count configs to test\n"; + + my $half = int($#start_list / 2); + + do { + my @tophalf = @start_list[0 .. $half]; + + create_config @tophalf; + read_current_config \%current_config; + + $count = $#tophalf + 1; + doprint "Testing $count configs\n"; + my $found = 0; + # make sure we test something + foreach my $config (@tophalf) { + if (defined($current_config{$config})) { + logit " $config\n"; + $found = 1; + } + } + if (!$found) { + # try the other half + doprint "Top half produced no set configs, trying bottom half\n"; + @tophalf = @start_list[$half .. $#start_list]; + create_config @tophalf; + read_current_config \%current_config; + foreach my $config (@tophalf) { + if (defined($current_config{$config})) { + logit " $config\n"; + $found = 1; + } + } + if (!$found) { + doprint "Failed: Can't make new config with current configs\n"; + foreach my $config (@start_list) { + doprint " CONFIG: $config\n"; + } + return -1; + } + $count = $#tophalf + 1; + doprint "Testing $count configs\n"; + } + + $ret = run_config_bisect_test $type; + + if ($ret) { + process_passed %current_config; + return 0; + } + + doprint "This config had a failure.\n"; + doprint "Removing these configs that were not set in this config:\n"; + + # A config exists in this group that was bad. + foreach my $config (keys %config_list) { + if (!defined($current_config{$config})) { + doprint " removing $config\n"; + delete $config_list{$config}; + } + } + + @start_list = @tophalf; + + if ($#start_list == 0) { + process_failed $start_list[0]; + return 1; + } + + # remove half the configs we are looking at and see if + # they are good. + $half = int($#start_list / 2); + } while ($half > 0); + + # we found a single config, try it again + my @tophalf = @start_list[0 .. 0]; + + $ret = run_config_bisect_test $type; + if ($ret) { + process_passed %current_config; + return 0; + } + + process_failed $start_list[0]; + return 1; +} + +sub config_bisect { + my ($i) = @_; + + my $start_config = $opt{"CONFIG_BISECT[$i]"}; + + my $tmpconfig = "$tmpdir/use_config"; + + # Make the file with the bad config and the min config + if (defined($minconfig)) { + # read the min config for things to ignore + run_command "cp $minconfig $tmpconfig" or + dodie "failed to copy $minconfig to $tmpconfig"; + } else { + unlink $tmpconfig; + } + + # Add other configs + if (defined($addconfig)) { + run_command "cat $addconfig >> $tmpconfig" or + dodie "failed to append $addconfig"; + } + + my $defconfig = ""; + if (-f $tmpconfig) { + $defconfig = "KCONFIG_ALLCONFIG=$tmpconfig"; + process_config_ignore $tmpconfig; + } + + # now process the start config + run_command "cp $start_config $output_config" or + dodie "failed to copy $start_config to $output_config"; + + # read directly what we want to check + my %config_check; + open (IN, $output_config) + or dodie "faied to open $output_config"; + + while () { + if (/^((CONFIG\S*)=.*)/) { + $config_check{$2} = $1; + } + } + close(IN); + + # Now run oldconfig with the minconfig (and addconfigs) + run_command "$defconfig $make oldnoconfig" or + dodie "failed make config oldconfig"; + + # check to see what we lost (or gained) + open (IN, $output_config) + or dodie "Failed to read $start_config"; + + my %removed_configs; + my %added_configs; + + while () { + if (/^((CONFIG\S*)=.*)/) { + # save off all options + $config_set{$2} = $1; + if (defined($config_check{$2})) { + if (defined($config_ignore{$2})) { + $removed_configs{$2} = $1; + } else { + $config_list{$2} = $1; + } + } elsif (!defined($config_ignore{$2})) { + $added_configs{$2} = $1; + $config_list{$2} = $1; + } + } + } + close(IN); + + my @confs = keys %removed_configs; + if ($#confs >= 0) { + doprint "Configs overridden by default configs and removed from check:\n"; + foreach my $config (@confs) { + doprint " $config\n"; + } + } + @confs = keys %added_configs; + if ($#confs >= 0) { + doprint "Configs appearing in make oldconfig and added:\n"; + foreach my $config (@confs) { + doprint " $config\n"; + } + } + + my %config_test; + my $once = 0; + + # Sometimes kconfig does weird things. We must make sure + # that the config we autocreate has everything we need + # to test, otherwise we may miss testing configs, or + # may not be able to create a new config. + # Here we create a config with everything set. + create_config (keys %config_list); + read_current_config \%config_test; + foreach my $config (keys %config_list) { + if (!defined($config_test{$config})) { + if (!$once) { + $once = 1; + doprint "Configs not produced by kconfig (will not be checked):\n"; + } + doprint " $config\n"; + delete $config_list{$config}; + } + } + my $ret; + do { + $ret = run_config_bisect; + } while (!$ret); + + return $ret if ($ret < 0); success $i; } @@ -1341,7 +1704,6 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $dmesg = "$tmpdir/dmesg-$machine"; $make = "$makecmd O=$outputdir"; $output_config = "$outputdir/.config"; - $output_config = "$outputdir/.config"; if ($reboot_type eq "grub") { dodie "GRUB_MENU not defined" if (!defined($grub_menu)); @@ -1354,6 +1716,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $run_type = $opt{"PATCHCHECK_TYPE[$i]"}; } elsif ($test_type eq "bisect") { $run_type = $opt{"BISECT_TYPE[$i]"}; + } elsif ($test_type eq "config_bisect") { + $run_type = $opt{"CONFIG_BISECT_TYPE[$i]"}; } # mistake in config file? @@ -1385,6 +1749,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { if ($test_type eq "bisect") { bisect $i; next; + } elsif ($test_type eq "config_bisect") { + config_bisect $i; + next; } elsif ($test_type eq "patchcheck") { patchcheck $i; next; -- cgit v0.10.2 From dbc6d0aa8ae38cf9cfe39c6076cc5378ef4ca4a2 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 17:18:37 -0500 Subject: ktest: Added compare script to test ktest.pl to sample.conf Add a compare script that makes sure that all the options in sample.conf are used in ktest.pl, and all the options in ktest.pl are described in sample.conf. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/compare-ktest-sample.pl b/tools/testing/ktest/compare-ktest-sample.pl new file mode 100755 index 0000000..9a571e7 --- /dev/null +++ b/tools/testing/ktest/compare-ktest-sample.pl @@ -0,0 +1,30 @@ +#!/usr/bin/perl + +open (IN,"ktest.pl"); +while () { + if (/\$opt\{"?([A-Z].*?)(\[.*\])?"?\}/ || + /set_test_option\("(.*?)"/) { + $opt{$1} = 1; + } +} +close IN; + +open (IN, "sample.conf"); +while () { + if (/^\s*#?\s*(\S+)\s*=/) { + $samp{$1} = 1; + } +} +close IN; + +foreach $opt (keys %opt) { + if (!defined($samp{$opt})) { + print "opt = $opt\n"; + } +} + +foreach $samp (keys %samp) { + if (!defined($opt{$samp})) { + print "samp = $samp\n"; + } +} -- cgit v0.10.2 From d1fbd7e6a6a4c67ab895a0219a2bbcf09d039c3a Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 17:41:37 -0500 Subject: ktest: Updated the sample.conf for the latest options Added documentation for SSH_EXEC, SCP_TO_TARGET, REBOOT, and CONFIG_BISECT and friends. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 03d6e91..e127274 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -390,6 +390,21 @@ # Example for a virtual guest call "Guest". #POWER_OFF = virsh destroy Guest +# The way to execute a command on the target +# (default ssh $SSH_USER@$MACHINE $SSH_COMMAND";) +# The variables SSH_USER, MACHINE and SSH_COMMAND are defined +#SSH_EXEC = ssh $SSH_USER@$MACHINE $SSH_COMMAND"; + +# The way to copy a file to the target +# (default scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE) +# The variables SSH_USER, MACHINE, SRC_FILE and DST_FILE are defined. +#SCP_TO_TARGET = scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE + +# The nice way to reboot the target +# (default ssh $SSH_USER@$MACHINE reboot) +# The variables SSH_USER and MACHINE are defined. +#REBOOT = ssh $SSH_USER@$MACHINE reboot + #### Per test run options #### # The following options are only allowed in TEST_START sections. # They are ignored in the DEFAULTS sections. @@ -442,7 +457,7 @@ # CHECKOUT = mybranch # PATCHCHECK_TYPE = boot # PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 -# PATCHCHEKC_END = HEAD~2 +# PATCHCHECK_END = HEAD~2 # # # @@ -520,3 +535,76 @@ # BISECT_BAD = b5153163ed580e00c67bdfecb02b2e3843817b3e # BISECT_TYPE = build # MIN_CONFIG = /home/test/config-bisect +# +# +# +# For TEST_TYPE = config_bisect +# +# In those cases that you have two different configs. One of them +# work, the other does not, and you do not know what config causes +# the problem. +# The TEST_TYPE config_bisect will bisect the bad config looking for +# what config causes the failure. +# +# The way it works is this: +# +# First it finds a config to work with. Since a different version, or +# MIN_CONFIG may cause different dependecies, it must run through this +# preparation. +# +# Overwrites any config set in the bad config with a config set in +# either the MIN_CONFIG or ADD_CONFIG. Thus, make sure these configs +# are minimal and do not disable configs you want to test: +# (ie. # CONFIG_FOO is not set). +# +# An oldconfig is run on the bad config and any new config that +# appears will be added to the configs to test. +# +# Finally, it generates a config with the above result and runs it +# again through make oldconfig to produce a config that should be +# satisfied by kconfig. +# +# Then it starts the bisect. +# +# The configs to test are cut in half. If all the configs in this +# half depend on a config in the other half, then the other half +# is tested instead. If no configs are enabled by either half, then +# this means a circular dependency exists and the test fails. +# +# A config is created with the test half, and the bisect test is run. +# +# If the bisect succeeds, then all configs in the generated config +# are removed from the configs to test and added to the configs that +# will be enabled for all builds (they will be enabled, but not be part +# of the configs to examine). +# +# If the bisect fails, then all test configs that were not enabled by +# the config file are removed from the test. These configs will not +# be enabled in future tests. Since current config failed, we consider +# this to be a subset of the config that we started with. +# +# When we are down to one config, it is considered the bad config. +# +# Note, the config chosen may not be the true bad config. Due to +# dependencies and selections of the kbuild system, mulitple +# configs may be needed to cause a failure. If you disable the +# config that was found and restart the test, if the test fails +# again, it is recommended to rerun the config_bisect with a new +# bad config without the found config enabled. +# +# The option BUILD_TYPE will be ignored. +# +# CONFIG_BISECT_TYPE is the type of test to perform: +# build - bad fails to build +# boot - bad builds but fails to boot +# test - bad boots but fails a test +# +# CONFIG_BISECT is the config that failed to boot +# +# Example: +# TEST_START +# TEST_TYPE = config_bisect +# CONFIG_BISECT_TYPE = build +# CONFIG_BISECT = /home/test/¢onfig-bad +# MIN_CONFIG = /home/test/config-min +# -- cgit v0.10.2 From 9be2e6b590b5d9cb6d6e38b6362a552bbcb118e0 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 9 Nov 2010 12:20:21 -0500 Subject: ktest: Use different temp config name for minconfig By using the "use_config" for minconfig and addconfig we risk trying to copy itself to itself, which will cause an unexpected failure. Use a different name instead. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 0d10805..a7a74f0 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1735,9 +1735,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $minconfig = $addconfig; } elsif (defined($addconfig)) { - run_command "cat $addconfig $minconfig > $tmpdir/use_config" or + run_command "cat $addconfig $minconfig > $tmpdir/add_config" or dodie "Failed to create temp config"; - $minconfig = "$tmpdir/use_config"; + $minconfig = "$tmpdir/add_config"; } my $checkout = $opt{"CHECKOUT[$i]"}; -- cgit v0.10.2 From cccae1a62a81dc8e32bf787024fdcf7ef71f1285 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 9 Nov 2010 12:21:32 -0500 Subject: ktest: Parse off the directory name in useconfig for failures When we store failures, we create a directory that has the build_type in it. For useconfig, it also contains the name path of the config file it uses. This unfortunately gets its own directory on failure. Parse off the directory name when creating the directory to store the failures. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index a7a74f0..52e45b8 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -398,7 +398,12 @@ sub fail { my $date = sprintf "%04d%02d%02d%02d%02d%02d", 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; - my $dir = "$machine-$test_type-$build_type-fail-$date"; + my $type = $build_type; + if ($type =~ /useconfig/) { + $type = "useconfig"; + } + + my $dir = "$machine-$test_type-$type-fail-$date"; my $faildir = "$store_failures/$dir"; if (!-d $faildir) { -- cgit v0.10.2 From 1c8a617a274c4065681d964cd5a5afb921de4a87 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 9 Nov 2010 12:55:40 -0500 Subject: ktest: Added force stop after success and failure Added the options STOP_AFTER_SUCCESS and STOP_AFTER_FAILURE to allow the user to give a time (in seconds) to stop the monitor after a stack trace or login has been detected. Sometimes the kernel constantly prints out to the console and this may cause the test to run indefinitely. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 52e45b8..6e85973 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -45,6 +45,8 @@ $default{"DIE_ON_FAILURE"} = 1; $default{"SSH_EXEC"} = "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND"; $default{"SCP_TO_TARGET"} = "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE"; $default{"REBOOT"} = "ssh \$SSH_USER\@\$MACHINE reboot"; +$default{"STOP_AFTER_SUCCESS"} = 10; +$default{"STOP_AFTER_FAILURE"} = 60; my $version; my $machine; @@ -94,6 +96,8 @@ my $timeout; my $booted_timeout; my $console; my $success_line; +my $stop_after_success; +my $stop_after_failure; my $build_target; my $target_image; my $localversion; @@ -601,6 +605,9 @@ sub monitor { reboot_to; + my $success_start; + my $failure_start; + for (;;) { if ($booted) { @@ -619,6 +626,16 @@ sub monitor { if ($full_line =~ /$success_line/) { $booted = 1; + $success_start = time; + } + + if ($booted && defined($stop_after_success) && + $stop_after_success >= 0) { + my $now = time; + if ($now - $success_start >= $stop_after_success) { + doprint "Test forced to stop after $stop_after_success seconds after success\n"; + last; + } } if ($full_line =~ /\[ backtrace testing \]/) { @@ -626,7 +643,19 @@ sub monitor { } if ($full_line =~ /call trace:/i) { - $bug = 1 if (!$skip_call_trace); + if (!$skip_call_trace) { + $bug = 1; + $failure_start = time; + } + } + + if ($bug && defined($stop_after_failure) && + $stop_after_failure >= 0) { + my $now = time; + if ($now - $failure_start >= $stop_after_failure) { + doprint "Test forced to stop after $stop_after_failure seconds after failure\n"; + last; + } } if ($full_line =~ /\[ end of backtrace testing \]/) { @@ -1687,6 +1716,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $booted_timeout = set_test_option("BOOTED_TIMEOUT", $i); $console = set_test_option("CONSOLE", $i); $success_line = set_test_option("SUCCESS_LINE", $i); + $stop_after_success = set_test_option("STOP_AFTER_SUCCESS", $i); + $stop_after_failure = set_test_option("STOP_AFTER_FAILURE", $i); $build_target = set_test_option("BUILD_TARGET", $i); $ssh_exec = set_test_option("SSH_EXEC", $i); $scp_to_target = set_test_option("SCP_TO_TARGET", $i); diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index e127274..3408c59 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -294,6 +294,18 @@ # (default "login:") #SUCCESS_LINE = login: +# In case the console constantly fills the screen, having +# a specified time to stop the test after success is recommended. +# (in seconds) +# (default 10) +#STOP_AFTER_SUCCESS = 10 + +# In case the console constantly fills the screen, having +# a specified time to stop the test after failure is recommended. +# (in seconds) +# (default 60) +#STOP_AFTER_FAILURE = 60 + # Stop testing if a build fails. If set, the script will end if # a failure is detected, otherwise it will save off the .config, # dmesg and bootlog in a directory called -- cgit v0.10.2 From eec5646031a54858362f7192a928511a23612f6b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 10 Nov 2010 09:08:20 -0500 Subject: ktest: For grub reboot, use run_ssh instead of run_command The run_ssh handles the ssh variable $SSH_COMMAND, which was not being used by the run_command in reboot_to function. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 6e85973..08a875f 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -561,7 +561,7 @@ sub wait_for_input sub reboot_to { if ($reboot_type eq "grub") { - run_command "$ssh_exec '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; + run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; return; } -- cgit v0.10.2 From f1a27850095ebc66c138c940c1efedb8a95f92c4 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 11 Nov 2010 11:34:38 -0500 Subject: ktest: Copy the last good and bad configs in config_bisect During the config_bisect, in case of failure, it is nice to have the last good and bad .configs that were used. This would let us restart the config_bisect from those configs. Copy the last good config into the output dir as config_good, and the last bad config as config_bad. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 08a875f..04a3227 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1281,6 +1281,8 @@ sub process_passed { delete $config_list{$config}; } } + doprint "config copied to $outputdir/config_good\n"; + run_command "cp -f $output_config $outputdir/config_good"; } sub process_failed { @@ -1358,6 +1360,8 @@ sub run_config_bisect { doprint "This config had a failure.\n"; doprint "Removing these configs that were not set in this config:\n"; + doprint "config copied to $outputdir/config_bad\n"; + run_command "cp -f $output_config $outputdir/config_bad"; # A config exists in this group that was bad. foreach my $config (keys %config_list) { -- cgit v0.10.2 From 8d1491bae72e3500b74e1855afa10f0544068cea Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 18 Nov 2010 15:39:48 -0500 Subject: ktest: Ask for the manditory config options instead of just failing In keeping with the notion that all tools should be simple for all to use. I've changed ktest.pl to ask for mandatory options instead of just failing. It will append (or create) the options the user types in, onto the config file. Signed-off-by: Steven Rostedt diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 04a3227..e1c62ee 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -13,8 +13,6 @@ use FileHandle; my $VERSION = "0.2"; -$#ARGV >= 0 || die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; - $| = 1; my %opt; @@ -47,7 +45,9 @@ $default{"SCP_TO_TARGET"} = "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE"; $default{"REBOOT"} = "ssh \$SSH_USER\@\$MACHINE reboot"; $default{"STOP_AFTER_SUCCESS"} = 10; $default{"STOP_AFTER_FAILURE"} = 60; +$default{"LOCALVERSION"} = "-test"; +my $ktest_config; my $version; my $machine; my $ssh_user; @@ -104,6 +104,156 @@ my $localversion; my $iteration = 0; my $successes = 0; +my %entered_configs; +my %config_help; + +$config_help{"MACHINE"} = << "EOF" + The machine hostname that you will test. +EOF + ; +$config_help{"SSH_USER"} = << "EOF" + The box is expected to have ssh on normal bootup, provide the user + (most likely root, since you need privileged operations) +EOF + ; +$config_help{"BUILD_DIR"} = << "EOF" + The directory that contains the Linux source code (full path). +EOF + ; +$config_help{"OUTPUT_DIR"} = << "EOF" + The directory that the objects will be built (full path). + (can not be same as BUILD_DIR) +EOF + ; +$config_help{"BUILD_TARGET"} = << "EOF" + The location of the compiled file to copy to the target. + (relative to OUTPUT_DIR) +EOF + ; +$config_help{"TARGET_IMAGE"} = << "EOF" + The place to put your image on the test machine. +EOF + ; +$config_help{"POWER_CYCLE"} = << "EOF" + A script or command to reboot the box. + + Here is a digital loggers power switch example + POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin\@power/outlet?5=CCL' + + Here is an example to reboot a virtual box on the current host + with the name "Guest". + POWER_CYCLE = virsh destroy Guest; sleep 5; virsh start Guest +EOF + ; +$config_help{"CONSOLE"} = << "EOF" + The script or command that reads the console + + If you use ttywatch server, something like the following would work. +CONSOLE = nc -d localhost 3001 + + For a virtual machine with guest name "Guest". +CONSOLE = virsh console Guest +EOF + ; +$config_help{"LOCALVERSION"} = << "EOF" + Required version ending to differentiate the test + from other linux builds on the system. +EOF + ; +$config_help{"REBOOT_TYPE"} = << "EOF" + Way to reboot the box to the test kernel. + Only valid options so far are "grub" and "script". + + If you specify grub, it will assume grub version 1 + and will search in /boot/grub/menu.lst for the title \$GRUB_MENU + and select that target to reboot to the kernel. If this is not + your setup, then specify "script" and have a command or script + specified in REBOOT_SCRIPT to boot to the target. + + The entry in /boot/grub/menu.lst must be entered in manually. + The test will not modify that file. +EOF + ; +$config_help{"GRUB_MENU"} = << "EOF" + The grub title name for the test kernel to boot + (Only mandatory if REBOOT_TYPE = grub) + + Note, ktest.pl will not update the grub menu.lst, you need to + manually add an option for the test. ktest.pl will search + the grub menu.lst for this option to find what kernel to + reboot into. + + For example, if in the /boot/grub/menu.lst the test kernel title has: + title Test Kernel + kernel vmlinuz-test + GRUB_MENU = Test Kernel +EOF + ; +$config_help{"REBOOT_SCRIPT"} = << "EOF" + A script to reboot the target into the test kernel + (Only mandatory if REBOOT_TYPE = script) +EOF + ; + + +sub get_ktest_config { + my ($config) = @_; + + return if (defined($opt{$config})); + + if (defined($config_help{$config})) { + print "\n"; + print $config_help{$config}; + } + + for (;;) { + print "$config = "; + if (defined($default{$config})) { + print "\[$default{$config}\] "; + } + $entered_configs{$config} = ; + $entered_configs{$config} =~ s/^\s*(.*\S)\s*$/$1/; + if ($entered_configs{$config} =~ /^\s*$/) { + if ($default{$config}) { + $entered_configs{$config} = $default{$config}; + } else { + print "Your answer can not be blank\n"; + next; + } + } + last; + } +} + +sub get_ktest_configs { + get_ktest_config("MACHINE"); + get_ktest_config("SSH_USER"); + get_ktest_config("BUILD_DIR"); + get_ktest_config("OUTPUT_DIR"); + get_ktest_config("BUILD_TARGET"); + get_ktest_config("TARGET_IMAGE"); + get_ktest_config("POWER_CYCLE"); + get_ktest_config("CONSOLE"); + get_ktest_config("LOCALVERSION"); + + my $rtype = $opt{"REBOOT_TYPE"}; + + if (!defined($rtype)) { + if (!defined($opt{"GRUB_MENU"})) { + get_ktest_config("REBOOT_TYPE"); + $rtype = $entered_configs{"REBOOT_TYPE"}; + } else { + $rtype = "grub"; + } + } + + if ($rtype eq "grub") { + get_ktest_config("GRUB_MENU"); + } else { + get_ktest_config("REBOOT_SCRIPT"); + } +} + sub set_value { my ($lvalue, $rvalue) = @_; @@ -241,6 +391,9 @@ sub read_config { $opt{"NUM_TESTS"} = $test_num; } + # make sure we have all mandatory configs + get_ktest_configs; + # set any defaults foreach my $default (keys %default) { @@ -1612,18 +1765,57 @@ sub patchcheck { return 1; } -read_config $ARGV[0]; - -# mandatory configs -die "MACHINE not defined\n" if (!defined($opt{"MACHINE"})); -die "SSH_USER not defined\n" if (!defined($opt{"SSH_USER"})); -die "BUILD_DIR not defined\n" if (!defined($opt{"BUILD_DIR"})); -die "OUTPUT_DIR not defined\n" if (!defined($opt{"OUTPUT_DIR"})); -die "BUILD_TARGET not defined\n" if (!defined($opt{"BUILD_TARGET"})); -die "TARGET_IMAGE not defined\n" if (!defined($opt{"TARGET_IMAGE"})); -die "POWER_CYCLE not defined\n" if (!defined($opt{"POWER_CYCLE"})); -die "CONSOLE not defined\n" if (!defined($opt{"CONSOLE"})); -die "LOCALVERSION not defined\n" if (!defined($opt{"LOCALVERSION"})); +$#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; + +if ($#ARGV == 0) { + $ktest_config = $ARGV[0]; + if (! -f $ktest_config) { + print "$ktest_config does not exist.\n"; + my $ans; + for (;;) { + print "Create it? [Y/n] "; + $ans = ; + chomp $ans; + if ($ans =~ /^\s*$/) { + $ans = "y"; + } + last if ($ans =~ /^y$/i || $ans =~ /^n$/i); + print "Please answer either 'y' or 'n'.\n"; + } + if ($ans !~ /^y$/i) { + exit 0; + } + } +} else { + $ktest_config = "ktest.conf"; +} + +if (! -f $ktest_config) { + open(OUT, ">$ktest_config") or die "Can not create $ktest_config"; + print OUT << "EOF" +# Generated by ktest.pl +# +# Define each test with TEST_START +# The config options below it will override the defaults +TEST_START + +DEFAULTS +EOF +; + close(OUT); +} +read_config $ktest_config; + +# Append any configs entered in manually to the config file. +my @new_configs = keys %entered_configs; +if ($#new_configs >= 0) { + print "\nAppending entered in configs to $ktest_config\n"; + open(OUT, ">>$ktest_config") or die "Can not append to $ktest_config"; + foreach my $config (@new_configs) { + print OUT "$config = $entered_configs{$config}\n"; + $opt{$config} = $entered_configs{$config}; + } +} if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) { unlink $opt{"LOG_FILE"}; -- cgit v0.10.2 From 2de06cc1f18d638cc7ab1169f61a8599045c2d4f Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 9 Feb 2009 12:05:51 -0800 Subject: xen: separate out frontend xenbus Impact: refactor Make a distinct frontend xenbus, in preparation for adding a backend xenbus. Signed-off-by: Ian Campbell Signed-off-by: Jeremy Fitzhardinge [corresponds to 2fd433a4188f in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git with adjustments to reflect changes in the code which is moved] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 4b9359a..83c32cb 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -464,6 +464,7 @@ config XEN_BLKDEV_FRONTEND tristate "Xen virtual block device support" depends on XEN default y + select XEN_XENBUS_FRONTEND help This driver implements the front-end of the Xen virtual block device driver. It communicates with a back-end driver diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 6e6180c..10b6bef 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -62,6 +62,9 @@ config XEN_SYS_HYPERVISOR virtual environment, /sys/hypervisor will still be present, but will have no xen contents. +config XEN_XENBUS_FRONTEND + tristate + config XEN_PLATFORM_PCI tristate "xen platform pci device driver" depends on XEN_PVHVM diff --git a/drivers/xen/xenbus/Makefile b/drivers/xen/xenbus/Makefile index 5571f5b..33e8cf8 100644 --- a/drivers/xen/xenbus/Makefile +++ b/drivers/xen/xenbus/Makefile @@ -5,3 +5,5 @@ xenbus-objs += xenbus_client.o xenbus-objs += xenbus_comms.o xenbus-objs += xenbus_xs.o xenbus-objs += xenbus_probe.o + +obj-$(CONFIG_XEN_XENBUS_FRONTEND) += xenbus_probe_frontend.o diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index deb9c4b..d462058 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -56,7 +56,6 @@ #include #include -#include #include #include "xenbus_comms.h" @@ -73,15 +72,6 @@ static unsigned long xen_store_mfn; static BLOCKING_NOTIFIER_HEAD(xenstore_chain); -static void wait_for_devices(struct xenbus_driver *xendrv); - -static int xenbus_probe_frontend(const char *type, const char *name); - -static void xenbus_dev_shutdown(struct device *_dev); - -static int xenbus_dev_suspend(struct device *dev, pm_message_t state); -static int xenbus_dev_resume(struct device *dev); - /* If something in array of ids matches this device, return it. */ static const struct xenbus_device_id * match_device(const struct xenbus_device_id *arr, struct xenbus_device *dev) @@ -102,8 +92,10 @@ int xenbus_match(struct device *_dev, struct device_driver *_drv) return match_device(drv->ids, to_xenbus_device(_dev)) != NULL; } +EXPORT_SYMBOL_GPL(xenbus_match); + -static int xenbus_uevent(struct device *_dev, struct kobj_uevent_env *env) +int xenbus_uevent(struct device *_dev, struct kobj_uevent_env *env) { struct xenbus_device *dev = to_xenbus_device(_dev); @@ -112,25 +104,7 @@ static int xenbus_uevent(struct device *_dev, struct kobj_uevent_env *env) return 0; } - -/* device// => - */ -static int frontend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename) -{ - nodename = strchr(nodename, '/'); - if (!nodename || strlen(nodename + 1) >= XEN_BUS_ID_SIZE) { - printk(KERN_WARNING "XENBUS: bad frontend %s\n", nodename); - return -EINVAL; - } - - strlcpy(bus_id, nodename + 1, XEN_BUS_ID_SIZE); - if (!strchr(bus_id, '/')) { - printk(KERN_WARNING "XENBUS: bus_id %s no slash\n", bus_id); - return -EINVAL; - } - *strchr(bus_id, '/') = '-'; - return 0; -} - +EXPORT_SYMBOL_GPL(xenbus_uevent); static void free_otherend_details(struct xenbus_device *dev) { @@ -149,7 +123,28 @@ static void free_otherend_watch(struct xenbus_device *dev) } -int read_otherend_details(struct xenbus_device *xendev, +static int talk_to_otherend(struct xenbus_device *dev) +{ + struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver); + + free_otherend_watch(dev); + free_otherend_details(dev); + + return drv->read_otherend_details(dev); +} + + + +static int watch_otherend(struct xenbus_device *dev) +{ + struct xen_bus_type *bus = container_of(dev->dev.bus, struct xen_bus_type, bus); + + return xenbus_watch_pathfmt(dev, &dev->otherend_watch, bus->otherend_changed, + "%s/%s", dev->otherend, "state"); +} + + +int xenbus_read_otherend_details(struct xenbus_device *xendev, char *id_node, char *path_node) { int err = xenbus_gather(XBT_NIL, xendev->nodename, @@ -174,39 +169,11 @@ int read_otherend_details(struct xenbus_device *xendev, return 0; } +EXPORT_SYMBOL_GPL(xenbus_read_otherend_details); - -static int read_backend_details(struct xenbus_device *xendev) -{ - return read_otherend_details(xendev, "backend-id", "backend"); -} - -static struct device_attribute xenbus_dev_attrs[] = { - __ATTR_NULL -}; - -/* Bus type for frontend drivers. */ -static struct xen_bus_type xenbus_frontend = { - .root = "device", - .levels = 2, /* device/type/ */ - .get_bus_id = frontend_bus_id, - .probe = xenbus_probe_frontend, - .bus = { - .name = "xen", - .match = xenbus_match, - .uevent = xenbus_uevent, - .probe = xenbus_dev_probe, - .remove = xenbus_dev_remove, - .shutdown = xenbus_dev_shutdown, - .dev_attrs = xenbus_dev_attrs, - - .suspend = xenbus_dev_suspend, - .resume = xenbus_dev_resume, - }, -}; - -static void otherend_changed(struct xenbus_watch *watch, - const char **vec, unsigned int len) +void xenbus_otherend_changed(struct xenbus_watch *watch, + const char **vec, unsigned int len, + int ignore_on_shutdown) { struct xenbus_device *dev = container_of(watch, struct xenbus_device, otherend_watch); @@ -234,11 +201,7 @@ static void otherend_changed(struct xenbus_watch *watch, * work that can fail e.g., when the rootfs is gone. */ if (system_state > SYSTEM_RUNNING) { - struct xen_bus_type *bus = bus; - bus = container_of(dev->dev.bus, struct xen_bus_type, bus); - /* If we're frontend, drive the state machine to Closed. */ - /* This should cause the backend to release our resources. */ - if ((bus == &xenbus_frontend) && (state == XenbusStateClosing)) + if (ignore_on_shutdown && (state == XenbusStateClosing)) xenbus_frontend_closed(dev); return; } @@ -246,25 +209,7 @@ static void otherend_changed(struct xenbus_watch *watch, if (drv->otherend_changed) drv->otherend_changed(dev, state); } - - -static int talk_to_otherend(struct xenbus_device *dev) -{ - struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver); - - free_otherend_watch(dev); - free_otherend_details(dev); - - return drv->read_otherend_details(dev); -} - - -static int watch_otherend(struct xenbus_device *dev) -{ - return xenbus_watch_pathfmt(dev, &dev->otherend_watch, otherend_changed, - "%s/%s", dev->otherend, "state"); -} - +EXPORT_SYMBOL_GPL(xenbus_otherend_changed); int xenbus_dev_probe(struct device *_dev) { @@ -310,6 +255,7 @@ fail: xenbus_switch_state(dev, XenbusStateClosed); return -ENODEV; } +EXPORT_SYMBOL_GPL(xenbus_dev_probe); int xenbus_dev_remove(struct device *_dev) { @@ -327,8 +273,9 @@ int xenbus_dev_remove(struct device *_dev) xenbus_switch_state(dev, XenbusStateClosed); return 0; } +EXPORT_SYMBOL_GPL(xenbus_dev_remove); -static void xenbus_dev_shutdown(struct device *_dev) +void xenbus_dev_shutdown(struct device *_dev) { struct xenbus_device *dev = to_xenbus_device(_dev); unsigned long timeout = 5*HZ; @@ -349,6 +296,7 @@ static void xenbus_dev_shutdown(struct device *_dev) out: put_device(&dev->dev); } +EXPORT_SYMBOL_GPL(xenbus_dev_shutdown); int xenbus_register_driver_common(struct xenbus_driver *drv, struct xen_bus_type *bus, @@ -362,25 +310,7 @@ int xenbus_register_driver_common(struct xenbus_driver *drv, return driver_register(&drv->driver); } - -int __xenbus_register_frontend(struct xenbus_driver *drv, - struct module *owner, const char *mod_name) -{ - int ret; - - drv->read_otherend_details = read_backend_details; - - ret = xenbus_register_driver_common(drv, &xenbus_frontend, - owner, mod_name); - if (ret) - return ret; - - /* If this driver is loaded as a module wait for devices to attach. */ - wait_for_devices(drv); - - return 0; -} -EXPORT_SYMBOL_GPL(__xenbus_register_frontend); +EXPORT_SYMBOL_GPL(xenbus_register_driver_common); void xenbus_unregister_driver(struct xenbus_driver *drv) { @@ -551,24 +481,7 @@ fail: kfree(xendev); return err; } - -/* device// */ -static int xenbus_probe_frontend(const char *type, const char *name) -{ - char *nodename; - int err; - - nodename = kasprintf(GFP_KERNEL, "%s/%s/%s", - xenbus_frontend.root, type, name); - if (!nodename) - return -ENOMEM; - - DPRINTK("%s", nodename); - - err = xenbus_probe_node(&xenbus_frontend, type, nodename); - kfree(nodename); - return err; -} +EXPORT_SYMBOL_GPL(xenbus_probe_node); static int xenbus_probe_device_type(struct xen_bus_type *bus, const char *type) { @@ -577,15 +490,23 @@ static int xenbus_probe_device_type(struct xen_bus_type *bus, const char *type) unsigned int dir_n = 0; int i; + printk(KERN_CRIT "%s type %s\n", __func__, type); + dir = xenbus_directory(XBT_NIL, bus->root, type, &dir_n); - if (IS_ERR(dir)) + if (IS_ERR(dir)) { + printk(KERN_CRIT "%s failed xenbus_directory\n", __func__); return PTR_ERR(dir); + } for (i = 0; i < dir_n; i++) { - err = bus->probe(type, dir[i]); - if (err) + printk(KERN_CRIT "%s %d/%d %s\n", __func__, i+1,dir_n, dir[i]); + err = bus->probe(bus, type, dir[i]); + if (err) { + printk(KERN_CRIT "%s failed\n", __func__); break; + } } + printk("%s done\n", __func__); kfree(dir); return err; } @@ -596,18 +517,27 @@ int xenbus_probe_devices(struct xen_bus_type *bus) char **dir; unsigned int i, dir_n; + printk(KERN_CRIT "%s %s\n", __func__, bus->root); + dir = xenbus_directory(XBT_NIL, bus->root, "", &dir_n); - if (IS_ERR(dir)) + if (IS_ERR(dir)) { + printk(KERN_CRIT "%s failed xenbus_directory\n", __func__); return PTR_ERR(dir); + } for (i = 0; i < dir_n; i++) { + printk(KERN_CRIT "%s %d/%d %s\n", __func__, i+1,dir_n, dir[i]); err = xenbus_probe_device_type(bus, dir[i]); - if (err) + if (err) { + printk(KERN_CRIT "%s failed\n", __func__); break; + } } + printk("%s done\n", __func__); kfree(dir); return err; } +EXPORT_SYMBOL_GPL(xenbus_probe_devices); static unsigned int char_count(const char *str, char c) { @@ -670,32 +600,17 @@ void xenbus_dev_changed(const char *node, struct xen_bus_type *bus) } EXPORT_SYMBOL_GPL(xenbus_dev_changed); -static void frontend_changed(struct xenbus_watch *watch, - const char **vec, unsigned int len) -{ - DPRINTK(""); - - xenbus_dev_changed(vec[XS_WATCH_PATH], &xenbus_frontend); -} - -/* We watch for devices appearing and vanishing. */ -static struct xenbus_watch fe_watch = { - .node = "device", - .callback = frontend_changed, -}; - -static int xenbus_dev_suspend(struct device *dev, pm_message_t state) +int xenbus_dev_suspend(struct device *dev, pm_message_t state) { int err = 0; struct xenbus_driver *drv; - struct xenbus_device *xdev; + struct xenbus_device *xdev = container_of(dev, struct xenbus_device, dev); - DPRINTK(""); + DPRINTK("%s", xdev->nodename); if (dev->driver == NULL) return 0; drv = to_xenbus_driver(dev->driver); - xdev = container_of(dev, struct xenbus_device, dev); if (drv->suspend) err = drv->suspend(xdev, state); if (err) @@ -703,21 +618,19 @@ static int xenbus_dev_suspend(struct device *dev, pm_message_t state) "xenbus: suspend %s failed: %i\n", dev_name(dev), err); return 0; } +EXPORT_SYMBOL_GPL(xenbus_dev_suspend); -static int xenbus_dev_resume(struct device *dev) +int xenbus_dev_resume(struct device *dev) { int err; struct xenbus_driver *drv; - struct xenbus_device *xdev; + struct xenbus_device *xdev = container_of(dev, struct xenbus_device, dev); - DPRINTK(""); + DPRINTK("%s", xdev->nodename); if (dev->driver == NULL) return 0; - drv = to_xenbus_driver(dev->driver); - xdev = container_of(dev, struct xenbus_device, dev); - err = talk_to_otherend(xdev); if (err) { printk(KERN_WARNING @@ -748,6 +661,7 @@ static int xenbus_dev_resume(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(xenbus_dev_resume); /* A flag to determine if xenstored is 'ready' (i.e. has started) */ int xenstored_ready = 0; @@ -776,11 +690,6 @@ void xenbus_probe(struct work_struct *unused) { xenstored_ready = 1; - /* Enumerate devices in xenstore and watch for changes. */ - xenbus_probe_devices(&xenbus_frontend); - register_xenbus_watch(&fe_watch); - xenbus_backend_probe_and_watch(); - /* Notify others that xenstore is up */ blocking_notifier_call_chain(&xenstore_chain, 0, NULL); } @@ -811,15 +720,6 @@ static int __init xenbus_init(void) if (!xen_domain()) goto out_error; - /* Register ourselves with the kernel bus subsystem */ - err = bus_register(&xenbus_frontend.bus); - if (err) - goto out_error; - - err = xenbus_backend_bus_register(); - if (err) - goto out_unreg_front; - /* * Domain0 doesn't have a store_evtchn or store_mfn yet. */ @@ -874,7 +774,7 @@ static int __init xenbus_init(void) if (err) { printk(KERN_WARNING "XENBUS: Error initializing xenstore comms: %i\n", err); - goto out_unreg_back; + goto out_error; } #ifdef CONFIG_XEN_COMPAT_XENFS @@ -885,135 +785,16 @@ static int __init xenbus_init(void) proc_mkdir("xen", NULL); #endif + printk(KERN_CRIT "%s ok\n", __func__); return 0; - out_unreg_back: - xenbus_backend_bus_unregister(); - - out_unreg_front: - bus_unregister(&xenbus_frontend.bus); - out_error: if (page != 0) free_page(page); + return err; } postcore_initcall(xenbus_init); MODULE_LICENSE("GPL"); - -static int is_device_connecting(struct device *dev, void *data) -{ - struct xenbus_device *xendev = to_xenbus_device(dev); - struct device_driver *drv = data; - struct xenbus_driver *xendrv; - - /* - * A device with no driver will never connect. We care only about - * devices which should currently be in the process of connecting. - */ - if (!dev->driver) - return 0; - - /* Is this search limited to a particular driver? */ - if (drv && (dev->driver != drv)) - return 0; - - xendrv = to_xenbus_driver(dev->driver); - return (xendev->state < XenbusStateConnected || - (xendev->state == XenbusStateConnected && - xendrv->is_ready && !xendrv->is_ready(xendev))); -} - -static int exists_connecting_device(struct device_driver *drv) -{ - return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, - is_device_connecting); -} - -static int print_device_status(struct device *dev, void *data) -{ - struct xenbus_device *xendev = to_xenbus_device(dev); - struct device_driver *drv = data; - - /* Is this operation limited to a particular driver? */ - if (drv && (dev->driver != drv)) - return 0; - - if (!dev->driver) { - /* Information only: is this too noisy? */ - printk(KERN_INFO "XENBUS: Device with no driver: %s\n", - xendev->nodename); - } else if (xendev->state < XenbusStateConnected) { - enum xenbus_state rstate = XenbusStateUnknown; - if (xendev->otherend) - rstate = xenbus_read_driver_state(xendev->otherend); - printk(KERN_WARNING "XENBUS: Timeout connecting " - "to device: %s (local state %d, remote state %d)\n", - xendev->nodename, xendev->state, rstate); - } - - return 0; -} - -/* We only wait for device setup after most initcalls have run. */ -static int ready_to_wait_for_devices; - -/* - * On a 5-minute timeout, wait for all devices currently configured. We need - * to do this to guarantee that the filesystems and / or network devices - * needed for boot are available, before we can allow the boot to proceed. - * - * This needs to be on a late_initcall, to happen after the frontend device - * drivers have been initialised, but before the root fs is mounted. - * - * A possible improvement here would be to have the tools add a per-device - * flag to the store entry, indicating whether it is needed at boot time. - * This would allow people who knew what they were doing to accelerate their - * boot slightly, but of course needs tools or manual intervention to set up - * those flags correctly. - */ -static void wait_for_devices(struct xenbus_driver *xendrv) -{ - unsigned long start = jiffies; - struct device_driver *drv = xendrv ? &xendrv->driver : NULL; - unsigned int seconds_waited = 0; - - if (!ready_to_wait_for_devices || !xen_domain()) - return; - - while (exists_connecting_device(drv)) { - if (time_after(jiffies, start + (seconds_waited+5)*HZ)) { - if (!seconds_waited) - printk(KERN_WARNING "XENBUS: Waiting for " - "devices to initialise: "); - seconds_waited += 5; - printk("%us...", 300 - seconds_waited); - if (seconds_waited == 300) - break; - } - - schedule_timeout_interruptible(HZ/10); - } - - if (seconds_waited) - printk("\n"); - - bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, - print_device_status); -} - -#ifndef MODULE -static int __init boot_wait_for_devices(void) -{ - if (xen_hvm_domain() && !xen_platform_pci_unplug) - return -ENODEV; - - ready_to_wait_for_devices = 1; - wait_for_devices(NULL); - return 0; -} - -late_initcall(boot_wait_for_devices); -#endif diff --git a/drivers/xen/xenbus/xenbus_probe.h b/drivers/xen/xenbus/xenbus_probe.h index 6c5e318..be390ce 100644 --- a/drivers/xen/xenbus/xenbus_probe.h +++ b/drivers/xen/xenbus/xenbus_probe.h @@ -36,26 +36,13 @@ #define XEN_BUS_ID_SIZE 20 -#ifdef CONFIG_XEN_BACKEND -extern void xenbus_backend_suspend(int (*fn)(struct device *, void *)); -extern void xenbus_backend_resume(int (*fn)(struct device *, void *)); -extern void xenbus_backend_probe_and_watch(void); -extern int xenbus_backend_bus_register(void); -extern void xenbus_backend_bus_unregister(void); -#else -static inline void xenbus_backend_suspend(int (*fn)(struct device *, void *)) {} -static inline void xenbus_backend_resume(int (*fn)(struct device *, void *)) {} -static inline void xenbus_backend_probe_and_watch(void) {} -static inline int xenbus_backend_bus_register(void) { return 0; } -static inline void xenbus_backend_bus_unregister(void) {} -#endif - struct xen_bus_type { char *root; unsigned int levels; int (*get_bus_id)(char bus_id[XEN_BUS_ID_SIZE], const char *nodename); - int (*probe)(const char *type, const char *dir); + int (*probe)(struct xen_bus_type *bus, const char *type, const char *dir); + void (*otherend_changed)(struct xenbus_watch *watch, const char **vec, unsigned int len); struct bus_type bus; }; @@ -73,4 +60,17 @@ extern int xenbus_probe_devices(struct xen_bus_type *bus); extern void xenbus_dev_changed(const char *node, struct xen_bus_type *bus); +extern int xenbus_uevent(struct device *_dev, struct kobj_uevent_env *env); +extern void xenbus_dev_shutdown(struct device *_dev); + +extern int xenbus_dev_suspend(struct device *dev, pm_message_t state); +extern int xenbus_dev_resume(struct device *dev); + +extern void xenbus_otherend_changed(struct xenbus_watch *watch, + const char **vec, unsigned int len, + int ignore_on_shutdown); + +extern int xenbus_read_otherend_details(struct xenbus_device *xendev, + char *id_node, char *path_node); + #endif diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c new file mode 100644 index 0000000..04c4f01 --- /dev/null +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -0,0 +1,275 @@ +#define DPRINTK(fmt, args...) \ + pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \ + __func__, __LINE__, ##args) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "xenbus_comms.h" +#include "xenbus_probe.h" + + +/* device// => - */ +static int frontend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename) +{ + nodename = strchr(nodename, '/'); + if (!nodename || strlen(nodename + 1) >= XEN_BUS_ID_SIZE) { + printk(KERN_WARNING "XENBUS: bad frontend %s\n", nodename); + return -EINVAL; + } + + strlcpy(bus_id, nodename + 1, XEN_BUS_ID_SIZE); + if (!strchr(bus_id, '/')) { + printk(KERN_WARNING "XENBUS: bus_id %s no slash\n", bus_id); + return -EINVAL; + } + *strchr(bus_id, '/') = '-'; + return 0; +} + +/* device// */ +static int xenbus_probe_frontend(struct xen_bus_type *bus, const char *type, const char *name) +{ + char *nodename; + int err; + + nodename = kasprintf(GFP_KERNEL, "%s/%s/%s", bus->root, type, name); + if (!nodename) + return -ENOMEM; + + DPRINTK("%s", nodename); + + err = xenbus_probe_node(bus, type, nodename); + kfree(nodename); + return err; +} + +static void backend_changed(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + xenbus_otherend_changed(watch, vec, len, 1); +} + +static struct device_attribute xenbus_frontend_dev_attrs[] = { + __ATTR_NULL +}; + +static struct xen_bus_type xenbus_frontend = { + .root = "device", + .levels = 2, /* device/type/ */ + .get_bus_id = frontend_bus_id, + .probe = xenbus_probe_frontend, + .otherend_changed = backend_changed, + .bus = { + .name = "xen", + .match = xenbus_match, + .uevent = xenbus_uevent, + .probe = xenbus_dev_probe, + .remove = xenbus_dev_remove, + .shutdown = xenbus_dev_shutdown, + .dev_attrs= xenbus_frontend_dev_attrs, + + .suspend = xenbus_dev_suspend, + .resume = xenbus_dev_resume, + }, +}; + +static void frontend_changed(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + DPRINTK(""); + + xenbus_dev_changed(vec[XS_WATCH_PATH], &xenbus_frontend); +} + + +/* We watch for devices appearing and vanishing. */ +static struct xenbus_watch fe_watch = { + .node = "device", + .callback = frontend_changed, +}; + +static int read_backend_details(struct xenbus_device *xendev) +{ + return xenbus_read_otherend_details(xendev, "backend-id", "backend"); +} + +static int is_device_connecting(struct device *dev, void *data) +{ + struct xenbus_device *xendev = to_xenbus_device(dev); + struct device_driver *drv = data; + struct xenbus_driver *xendrv; + + /* + * A device with no driver will never connect. We care only about + * devices which should currently be in the process of connecting. + */ + if (!dev->driver) + return 0; + + /* Is this search limited to a particular driver? */ + if (drv && (dev->driver != drv)) + return 0; + + xendrv = to_xenbus_driver(dev->driver); + return (xendev->state < XenbusStateConnected || + (xendev->state == XenbusStateConnected && + xendrv->is_ready && !xendrv->is_ready(xendev))); +} + +static int exists_connecting_device(struct device_driver *drv) +{ + return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, + is_device_connecting); +} + +static int print_device_status(struct device *dev, void *data) +{ + struct xenbus_device *xendev = to_xenbus_device(dev); + struct device_driver *drv = data; + + /* Is this operation limited to a particular driver? */ + if (drv && (dev->driver != drv)) + return 0; + + if (!dev->driver) { + /* Information only: is this too noisy? */ + printk(KERN_INFO "XENBUS: Device with no driver: %s\n", + xendev->nodename); + } else if (xendev->state < XenbusStateConnected) { + enum xenbus_state rstate = XenbusStateUnknown; + if (xendev->otherend) + rstate = xenbus_read_driver_state(xendev->otherend); + printk(KERN_WARNING "XENBUS: Timeout connecting " + "to device: %s (local state %d, remote state %d)\n", + xendev->nodename, xendev->state, rstate); + } + + return 0; +} + +/* We only wait for device setup after most initcalls have run. */ +static int ready_to_wait_for_devices; + +/* + * On a 5-minute timeout, wait for all devices currently configured. We need + * to do this to guarantee that the filesystems and / or network devices + * needed for boot are available, before we can allow the boot to proceed. + * + * This needs to be on a late_initcall, to happen after the frontend device + * drivers have been initialised, but before the root fs is mounted. + * + * A possible improvement here would be to have the tools add a per-device + * flag to the store entry, indicating whether it is needed at boot time. + * This would allow people who knew what they were doing to accelerate their + * boot slightly, but of course needs tools or manual intervention to set up + * those flags correctly. + */ +static void wait_for_devices(struct xenbus_driver *xendrv) +{ + unsigned long start = jiffies; + struct device_driver *drv = xendrv ? &xendrv->driver : NULL; + unsigned int seconds_waited = 0; + + if (!ready_to_wait_for_devices || !xen_domain()) + return; + + while (exists_connecting_device(drv)) { + if (time_after(jiffies, start + (seconds_waited+5)*HZ)) { + if (!seconds_waited) + printk(KERN_WARNING "XENBUS: Waiting for " + "devices to initialise: "); + seconds_waited += 5; + printk("%us...", 300 - seconds_waited); + if (seconds_waited == 300) + break; + } + + schedule_timeout_interruptible(HZ/10); + } + + if (seconds_waited) + printk("\n"); + + bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, + print_device_status); +} + +int __xenbus_register_frontend(struct xenbus_driver *drv, + struct module *owner, const char *mod_name) +{ + int ret; + + drv->read_otherend_details = read_backend_details; + + ret = xenbus_register_driver_common(drv, &xenbus_frontend, + owner, mod_name); + if (ret) + return ret; + + /* If this driver is loaded as a module wait for devices to attach. */ + wait_for_devices(drv); + + return 0; +} +EXPORT_SYMBOL_GPL(__xenbus_register_frontend); + +static int __init xenbus_probe_frontend_init(void) +{ + int err; + + DPRINTK(""); + + /* Register ourselves with the kernel bus subsystem */ + err = bus_register(&xenbus_frontend.bus); + if (err) { + printk(KERN_CRIT "%s didn't register bus!\n", __func__); + return err; + } + printk(KERN_CRIT "%s bus registered ok\n", __func__); + + if (!xen_initial_domain()) { + /* Enumerate devices in xenstore and watch for changes. */ + xenbus_probe_devices(&xenbus_frontend); + printk(KERN_CRIT "%s devices probed ok\n", __func__); + register_xenbus_watch(&fe_watch); + printk(KERN_CRIT "%s watch add ok ok\n", __func__); + printk(KERN_CRIT "%s all done\n", __func__); + } + + return 0; +} + +module_init(xenbus_probe_frontend_init); + +#ifndef MODULE +static int __init boot_wait_for_devices(void) +{ + if (xen_hvm_domain() && !xen_platform_pci_unplug) + return -ENODEV; + + ready_to_wait_for_devices = 1; + wait_for_devices(NULL); + return 0; +} + +late_initcall(boot_wait_for_devices); +#endif -- cgit v0.10.2 From df660251eb534649f90f9dcfe1da1cef4ea48a3e Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 9 Feb 2009 12:05:51 -0800 Subject: xen: add backend driver support Impact: backend device support Add the basic machinery to support backend drivers. Signed-off-by: Ian Campbell Signed-off-by: Jeremy Fitzhardinge [corresponds to 79727b851bac in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 10b6bef..84bf17b 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -29,6 +29,14 @@ config XEN_DEV_EVTCHN firing. If in doubt, say yes. +config XEN_BACKEND + tristate "Backend driver support" + depends on XEN_DOM0 + default y + help + Support for backend device drivers that provide I/O services + to other virtual machines. + config XENFS tristate "Xen filesystem" default y diff --git a/drivers/xen/xenbus/Makefile b/drivers/xen/xenbus/Makefile index 33e8cf8..8dca685 100644 --- a/drivers/xen/xenbus/Makefile +++ b/drivers/xen/xenbus/Makefile @@ -6,4 +6,7 @@ xenbus-objs += xenbus_comms.o xenbus-objs += xenbus_xs.o xenbus-objs += xenbus_probe.o +xenbus-be-objs-$(CONFIG_XEN_BACKEND) += xenbus_probe_backend.o +xenbus-objs += $(xenbus-be-objs-y) + obj-$(CONFIG_XEN_XENBUS_FRONTEND) += xenbus_probe_frontend.o diff --git a/drivers/xen/xenbus/xenbus_comms.c b/drivers/xen/xenbus/xenbus_comms.c index 090c61e..700dc77 100644 --- a/drivers/xen/xenbus/xenbus_comms.c +++ b/drivers/xen/xenbus/xenbus_comms.c @@ -49,6 +49,7 @@ static DECLARE_WAIT_QUEUE_HEAD(xb_waitq); static irqreturn_t wake_waiting(int irq, void *unused) { if (unlikely(xenstored_ready == 0)) { + printk(KERN_CRIT "xenbus_probe wake_waiting\n"); xenstored_ready = 1; schedule_work(&probe_work); } diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index d462058..b09eb17 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -95,17 +95,6 @@ int xenbus_match(struct device *_dev, struct device_driver *_drv) EXPORT_SYMBOL_GPL(xenbus_match); -int xenbus_uevent(struct device *_dev, struct kobj_uevent_env *env) -{ - struct xenbus_device *dev = to_xenbus_device(_dev); - - if (add_uevent_var(env, "MODALIAS=xen:%s", dev->devicetype)) - return -ENOMEM; - - return 0; -} -EXPORT_SYMBOL_GPL(xenbus_uevent); - static void free_otherend_details(struct xenbus_device *dev) { kfree(dev->otherend); @@ -690,6 +679,8 @@ void xenbus_probe(struct work_struct *unused) { xenstored_ready = 1; + printk(KERN_CRIT "xenbus_probe wake_waiting\n"); + /* Notify others that xenstore is up */ blocking_notifier_call_chain(&xenstore_chain, 0, NULL); } diff --git a/drivers/xen/xenbus/xenbus_probe.h b/drivers/xen/xenbus/xenbus_probe.h index be390ce..0e5fc4c 100644 --- a/drivers/xen/xenbus/xenbus_probe.h +++ b/drivers/xen/xenbus/xenbus_probe.h @@ -60,7 +60,6 @@ extern int xenbus_probe_devices(struct xen_bus_type *bus); extern void xenbus_dev_changed(const char *node, struct xen_bus_type *bus); -extern int xenbus_uevent(struct device *_dev, struct kobj_uevent_env *env); extern void xenbus_dev_shutdown(struct device *_dev); extern int xenbus_dev_suspend(struct device *dev, pm_message_t state); diff --git a/drivers/xen/xenbus/xenbus_probe_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c new file mode 100644 index 0000000..7459cce --- /dev/null +++ b/drivers/xen/xenbus/xenbus_probe_backend.c @@ -0,0 +1,300 @@ +/****************************************************************************** + * Talks to Xen Store to figure out what devices we have (backend half). + * + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * Copyright (C) 2005 Mike Wray, Hewlett-Packard + * Copyright (C) 2005, 2006 XenSource Ltd + * Copyright (C) 2007 Solarflare Communications, Inc. + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (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 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. + */ + +#define DPRINTK(fmt, args...) \ + pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \ + __func__, __LINE__, ##args) + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "xenbus_comms.h" +#include "xenbus_probe.h" + +/* backend/// => -- */ +static int backend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename) +{ + int domid, err; + const char *devid, *type, *frontend; + unsigned int typelen; + + type = strchr(nodename, '/'); + if (!type) + return -EINVAL; + type++; + typelen = strcspn(type, "/"); + if (!typelen || type[typelen] != '/') + return -EINVAL; + + devid = strrchr(nodename, '/') + 1; + + err = xenbus_gather(XBT_NIL, nodename, "frontend-id", "%i", &domid, + "frontend", NULL, &frontend, + NULL); + if (err) + return err; + if (strlen(frontend) == 0) + err = -ERANGE; + if (!err && !xenbus_exists(XBT_NIL, frontend, "")) + err = -ENOENT; + kfree(frontend); + + if (err) + return err; + + if (snprintf(bus_id, XEN_BUS_ID_SIZE, + "%.*s-%i-%s", typelen, type, domid, devid) >= XEN_BUS_ID_SIZE) + return -ENOSPC; + return 0; +} + +static int xenbus_uevent_backend(struct device *dev, + struct kobj_uevent_env *env) +{ + struct xenbus_device *xdev; + struct xenbus_driver *drv; + struct xen_bus_type *bus; + + DPRINTK(""); + + if (dev == NULL) + return -ENODEV; + + xdev = to_xenbus_device(dev); + bus = container_of(xdev->dev.bus, struct xen_bus_type, bus); + if (xdev == NULL) + return -ENODEV; + + /* stuff we want to pass to /sbin/hotplug */ + if (add_uevent_var(env, "XENBUS_TYPE=%s", xdev->devicetype)) + return -ENOMEM; + + if (add_uevent_var(env, "XENBUS_PATH=%s", xdev->nodename)) + return -ENOMEM; + + if (add_uevent_var(env, "XENBUS_BASE_PATH=%s", bus->root)) + return -ENOMEM; + + if (dev->driver) { + drv = to_xenbus_driver(dev->driver); + if (drv && drv->uevent) + return drv->uevent(xdev, env); + } + + return 0; +} + +/* backend/// */ +static int xenbus_probe_backend_unit(struct xen_bus_type *bus, + const char *dir, + const char *type, + const char *name) +{ + char *nodename; + int err; + + nodename = kasprintf(GFP_KERNEL, "%s/%s", dir, name); + if (!nodename) + return -ENOMEM; + + DPRINTK("%s\n", nodename); + + err = xenbus_probe_node(bus, type, nodename); + kfree(nodename); + return err; +} + +/* backend// */ +static int xenbus_probe_backend(struct xen_bus_type *bus, const char *type, const char *domid) +{ + char *nodename; + int err = 0; + char **dir; + unsigned int i, dir_n = 0; + + DPRINTK(""); + + nodename = kasprintf(GFP_KERNEL, "%s/%s/%s", bus->root, type, domid); + if (!nodename) + return -ENOMEM; + + dir = xenbus_directory(XBT_NIL, nodename, "", &dir_n); + if (IS_ERR(dir)) { + kfree(nodename); + return PTR_ERR(dir); + } + + for (i = 0; i < dir_n; i++) { + err = xenbus_probe_backend_unit(bus, nodename, type, dir[i]); + if (err) + break; + } + kfree(dir); + kfree(nodename); + return err; +} + +static void frontend_changed(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + xenbus_otherend_changed(watch, vec, len, 0); +} + +static struct device_attribute xenbus_backend_dev_attrs[] = { + __ATTR_NULL +}; + +static struct xen_bus_type xenbus_backend = { + .root = "backend", + .levels = 3, /* backend/type// */ + .get_bus_id = backend_bus_id, + .probe = xenbus_probe_backend, + .otherend_changed = frontend_changed, + .bus = { + .name = "xen-backend", + .match = xenbus_match, + .uevent = xenbus_uevent_backend, + .probe = xenbus_dev_probe, + .remove = xenbus_dev_remove, + .shutdown = xenbus_dev_shutdown, + .dev_attrs = xenbus_backend_dev_attrs, + }, +}; + +static void backend_changed(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + DPRINTK(""); + + xenbus_dev_changed(vec[XS_WATCH_PATH], &xenbus_backend); +} + +static struct xenbus_watch be_watch = { + .node = "backend", + .callback = backend_changed, +}; + +static int read_frontend_details(struct xenbus_device *xendev) +{ + return xenbus_read_otherend_details(xendev, "frontend-id", "frontend"); +} + +//void xenbus_backend_suspend(int (*fn)(struct device *, void *)) +//{ +// DPRINTK(""); +// bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, fn); +//} + +//void xenbus_backend_resume(int (*fn)(struct device *, void *)) +//{ +// DPRINTK(""); +// bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, fn); +//} + +//int xenbus_for_each_backend(void *arg, int (*fn)(struct device *, void *)) +//{ +// return bus_for_each_dev(&xenbus_backend.bus, NULL, arg, fn); +//} +//EXPORT_SYMBOL_GPL(xenbus_for_each_backend); + +int xenbus_dev_is_online(struct xenbus_device *dev) +{ + int rc, val; + + rc = xenbus_scanf(XBT_NIL, dev->nodename, "online", "%d", &val); + if (rc != 1) + val = 0; /* no online node present */ + + return val; +} +EXPORT_SYMBOL_GPL(xenbus_dev_is_online); + +int __xenbus_register_backend(struct xenbus_driver *drv, + struct module *owner, const char *mod_name) +{ + drv->read_otherend_details = read_frontend_details; + + return xenbus_register_driver_common(drv, &xenbus_backend, + owner, mod_name); +} +EXPORT_SYMBOL_GPL(__xenbus_register_backend); + +static int backend_probe_and_watch(struct notifier_block *notifier, + unsigned long event, + void *data) +{ + /* Enumerate devices in xenstore and watch for changes. */ + xenbus_probe_devices(&xenbus_backend); + printk(KERN_CRIT "%s devices probed ok\n", __func__); + register_xenbus_watch(&be_watch); + printk(KERN_CRIT "%s watch add ok ok\n", __func__); + printk(KERN_CRIT "%s all done\n", __func__); + return NOTIFY_DONE; +} + +static int __init xenbus_probe_backend_init(void) +{ + static struct notifier_block xenstore_notifier = { + .notifier_call = backend_probe_and_watch + }; + int err; + + DPRINTK(""); + + /* Register ourselves with the kernel bus subsystem */ + err = bus_register(&xenbus_backend.bus); + if (err) { + printk(KERN_CRIT "%s didn't register bus!\n", __func__); + return err; + } + printk(KERN_CRIT "%s bus registered ok\n", __func__); + + register_xenstore_notifier(&xenstore_notifier); + + return 0; +} + +module_init(xenbus_probe_backend_init); diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index 04c4f01..dd31d5a4 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -62,6 +62,17 @@ static int xenbus_probe_frontend(struct xen_bus_type *bus, const char *type, con return err; } +static int xenbus_uevent_frontend(struct device *_dev, struct kobj_uevent_env *env) +{ + struct xenbus_device *dev = to_xenbus_device(_dev); + + if (add_uevent_var(env, "MODALIAS=xen:%s", dev->devicetype)) + return -ENOMEM; + + return 0; +} + + static void backend_changed(struct xenbus_watch *watch, const char **vec, unsigned int len) { @@ -81,7 +92,7 @@ static struct xen_bus_type xenbus_frontend = { .bus = { .name = "xen", .match = xenbus_match, - .uevent = xenbus_uevent, + .uevent = xenbus_uevent_frontend, .probe = xenbus_dev_probe, .remove = xenbus_dev_remove, .shutdown = xenbus_dev_shutdown, @@ -232,8 +243,25 @@ int __xenbus_register_frontend(struct xenbus_driver *drv, } EXPORT_SYMBOL_GPL(__xenbus_register_frontend); +static int frontend_probe_and_watch(struct notifier_block *notifier, + unsigned long event, + void *data) +{ + /* Enumerate devices in xenstore and watch for changes. */ + xenbus_probe_devices(&xenbus_frontend); + printk(KERN_CRIT "%s devices probed ok\n", __func__); + register_xenbus_watch(&fe_watch); + printk(KERN_CRIT "%s watch add ok ok\n", __func__); + printk(KERN_CRIT "%s all done\n", __func__); + return NOTIFY_DONE; +} + + static int __init xenbus_probe_frontend_init(void) { + static struct notifier_block xenstore_notifier = { + .notifier_call = frontend_probe_and_watch + }; int err; DPRINTK(""); @@ -246,14 +274,7 @@ static int __init xenbus_probe_frontend_init(void) } printk(KERN_CRIT "%s bus registered ok\n", __func__); - if (!xen_initial_domain()) { - /* Enumerate devices in xenstore and watch for changes. */ - xenbus_probe_devices(&xenbus_frontend); - printk(KERN_CRIT "%s devices probed ok\n", __func__); - register_xenbus_watch(&fe_watch); - printk(KERN_CRIT "%s watch add ok ok\n", __func__); - printk(KERN_CRIT "%s all done\n", __func__); - } + register_xenstore_notifier(&xenstore_notifier); return 0; } diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h index 43e2d7d..7a1d15f 100644 --- a/include/xen/xenbus.h +++ b/include/xen/xenbus.h @@ -94,7 +94,7 @@ struct xenbus_driver { int (*remove)(struct xenbus_device *dev); int (*suspend)(struct xenbus_device *dev, pm_message_t state); int (*resume)(struct xenbus_device *dev); - int (*uevent)(struct xenbus_device *, char **, int, char *, int); + int (*uevent)(struct xenbus_device *, struct kobj_uevent_env *); struct device_driver driver; int (*read_otherend_details)(struct xenbus_device *dev); int (*is_ready)(struct xenbus_device *dev); -- cgit v0.10.2 From 74c2ee98385fe2653d6a817f6f74c18eec4c2424 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 9 Feb 2009 16:37:23 -0800 Subject: xen: remove xen/evtchn.h Impact: cleanup It's a usermode header for users of /dev/xen/evtchn. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ian Campbell [corresponds to b60e48cec12f in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/xenbus/xenbus_probe_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c index 7459cce..3bd3812 100644 --- a/drivers/xen/xenbus/xenbus_probe_backend.c +++ b/drivers/xen/xenbus/xenbus_probe_backend.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #include "xenbus_comms.h" -- cgit v0.10.2 From 806f5463d2598ea23a7a688c47c2774846fe7f2f Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 4 Mar 2009 22:31:45 -0800 Subject: xenbus/frontend: register bus earlier Impact: bugfix Make sure the frontend bus is registered early, before any drivers want to attach. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ian Campbell [corresponds to 1d5df318f87f in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index dd31d5a4..ccd201f 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -278,8 +278,7 @@ static int __init xenbus_probe_frontend_init(void) return 0; } - -module_init(xenbus_probe_frontend_init); +subsys_initcall(xenbus_probe_frontend_init); #ifndef MODULE static int __init boot_wait_for_devices(void) -- cgit v0.10.2 From c3676e8462009d84dce89a454956460de3bfdca7 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 4 Mar 2009 22:32:16 -0800 Subject: xen/xenbus: make sure backend bus is registered earlier Impact: bugfix Need to register it before any drivers want to attach. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ian Campbell [corresponds to a7a9c3a942c6 in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/xenbus/xenbus_probe_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c index 3bd3812..a3cc535 100644 --- a/drivers/xen/xenbus/xenbus_probe_backend.c +++ b/drivers/xen/xenbus/xenbus_probe_backend.c @@ -295,5 +295,4 @@ static int __init xenbus_probe_backend_init(void) return 0; } - -module_init(xenbus_probe_backend_init); +subsys_initcall(xenbus_probe_backend_init); -- cgit v0.10.2 From 1b31a143450ea9b5e6168f3b21a02c4a6a63ad01 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 27 Mar 2009 16:29:44 -0700 Subject: xen/xenbus: make frontend bus GPL Make sure frontend xenbus has a GPL module license, so it can access all the xenbus symbols exported GPL only. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ian Campbell [corresponds to 525cbc8adcb5 in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index ccd201f..3a42365 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -293,3 +293,5 @@ static int __init boot_wait_for_devices(void) late_initcall(boot_wait_for_devices); #endif + +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 7432e4bd0bcec9f18d9c94de8925b8a59d3edf94 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Thu, 29 Oct 2009 14:19:42 -0700 Subject: xen/xenbus: clean up error handling Don't report errors when booting on non-Xen, because its just confusing. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ian Campbell [corresponds to 8aa08376d6aa in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index b09eb17..96bd1ef 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -242,7 +242,7 @@ int xenbus_dev_probe(struct device *_dev) fail: xenbus_dev_error(dev, err, "xenbus_dev_probe on %s", dev->nodename); xenbus_switch_state(dev, XenbusStateClosed); - return -ENODEV; + return err; } EXPORT_SYMBOL_GPL(xenbus_dev_probe); @@ -709,7 +709,7 @@ static int __init xenbus_init(void) err = -ENODEV; if (!xen_domain()) - goto out_error; + return err; /* * Domain0 doesn't have a store_evtchn or store_mfn yet. -- cgit v0.10.2 From a16448e071ca80e599ea4f780242f40a0a213b07 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 29 Mar 2010 14:37:44 -0700 Subject: xen/xenbus: cleanup debug noise in xenbus_comms.c Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ian Campbell [corresponds to 6796c12281dc in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/xenbus/xenbus_comms.c b/drivers/xen/xenbus/xenbus_comms.c index 700dc77..090c61e 100644 --- a/drivers/xen/xenbus/xenbus_comms.c +++ b/drivers/xen/xenbus/xenbus_comms.c @@ -49,7 +49,6 @@ static DECLARE_WAIT_QUEUE_HEAD(xb_waitq); static irqreturn_t wake_waiting(int irq, void *unused) { if (unlikely(xenstored_ready == 0)) { - printk(KERN_CRIT "xenbus_probe wake_waiting\n"); xenstored_ready = 1; schedule_work(&probe_work); } -- cgit v0.10.2 From a39bda27b35266beae945f48e0b6ab58d06a17d5 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 29 Mar 2010 14:38:12 -0700 Subject: xen/xenbus: clean up noise in xenbus_probe.c Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ian Campbell [corresponds to 01aded30aaef in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 96bd1ef..8119234 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -479,23 +479,16 @@ static int xenbus_probe_device_type(struct xen_bus_type *bus, const char *type) unsigned int dir_n = 0; int i; - printk(KERN_CRIT "%s type %s\n", __func__, type); - dir = xenbus_directory(XBT_NIL, bus->root, type, &dir_n); - if (IS_ERR(dir)) { - printk(KERN_CRIT "%s failed xenbus_directory\n", __func__); + if (IS_ERR(dir)) return PTR_ERR(dir); - } for (i = 0; i < dir_n; i++) { - printk(KERN_CRIT "%s %d/%d %s\n", __func__, i+1,dir_n, dir[i]); err = bus->probe(bus, type, dir[i]); - if (err) { - printk(KERN_CRIT "%s failed\n", __func__); + if (err) break; - } } - printk("%s done\n", __func__); + kfree(dir); return err; } @@ -506,23 +499,16 @@ int xenbus_probe_devices(struct xen_bus_type *bus) char **dir; unsigned int i, dir_n; - printk(KERN_CRIT "%s %s\n", __func__, bus->root); - dir = xenbus_directory(XBT_NIL, bus->root, "", &dir_n); - if (IS_ERR(dir)) { - printk(KERN_CRIT "%s failed xenbus_directory\n", __func__); + if (IS_ERR(dir)) return PTR_ERR(dir); - } for (i = 0; i < dir_n; i++) { - printk(KERN_CRIT "%s %d/%d %s\n", __func__, i+1,dir_n, dir[i]); err = xenbus_probe_device_type(bus, dir[i]); - if (err) { - printk(KERN_CRIT "%s failed\n", __func__); + if (err) break; - } } - printk("%s done\n", __func__); + kfree(dir); return err; } @@ -679,8 +665,6 @@ void xenbus_probe(struct work_struct *unused) { xenstored_ready = 1; - printk(KERN_CRIT "xenbus_probe wake_waiting\n"); - /* Notify others that xenstore is up */ blocking_notifier_call_chain(&xenstore_chain, 0, NULL); } @@ -776,7 +760,6 @@ static int __init xenbus_init(void) proc_mkdir("xen", NULL); #endif - printk(KERN_CRIT "%s ok\n", __func__); return 0; out_error: -- cgit v0.10.2 From b5d33d088f45bb62fdaaf1755da05aa5524a3a8f Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 29 Mar 2010 14:38:35 -0700 Subject: xen/xenbus: clean up noise in xenbus_probe_backend.c Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ian Campbell [corresponds to 98b833aaf81e in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/xenbus/xenbus_probe_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c index a3cc535..9b9dd36 100644 --- a/drivers/xen/xenbus/xenbus_probe_backend.c +++ b/drivers/xen/xenbus/xenbus_probe_backend.c @@ -267,10 +267,8 @@ static int backend_probe_and_watch(struct notifier_block *notifier, { /* Enumerate devices in xenstore and watch for changes. */ xenbus_probe_devices(&xenbus_backend); - printk(KERN_CRIT "%s devices probed ok\n", __func__); register_xenbus_watch(&be_watch); - printk(KERN_CRIT "%s watch add ok ok\n", __func__); - printk(KERN_CRIT "%s all done\n", __func__); + return NOTIFY_DONE; } @@ -285,11 +283,8 @@ static int __init xenbus_probe_backend_init(void) /* Register ourselves with the kernel bus subsystem */ err = bus_register(&xenbus_backend.bus); - if (err) { - printk(KERN_CRIT "%s didn't register bus!\n", __func__); + if (err) return err; - } - printk(KERN_CRIT "%s bus registered ok\n", __func__); register_xenstore_notifier(&xenstore_notifier); -- cgit v0.10.2 From 0ff4fdf065101504cd84ac36924fcdad6641a4f7 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 29 Mar 2010 14:38:54 -0700 Subject: xen/xenbus: clean up noise in xenbus_probe_frontend.c Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ian Campbell [corresponds to 616ff7a06a3f in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index 3a42365..60c311a 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -249,10 +249,8 @@ static int frontend_probe_and_watch(struct notifier_block *notifier, { /* Enumerate devices in xenstore and watch for changes. */ xenbus_probe_devices(&xenbus_frontend); - printk(KERN_CRIT "%s devices probed ok\n", __func__); register_xenbus_watch(&fe_watch); - printk(KERN_CRIT "%s watch add ok ok\n", __func__); - printk(KERN_CRIT "%s all done\n", __func__); + return NOTIFY_DONE; } @@ -268,11 +266,8 @@ static int __init xenbus_probe_frontend_init(void) /* Register ourselves with the kernel bus subsystem */ err = bus_register(&xenbus_frontend.bus); - if (err) { - printk(KERN_CRIT "%s didn't register bus!\n", __func__); + if (err) return err; - } - printk(KERN_CRIT "%s bus registered ok\n", __func__); register_xenstore_notifier(&xenstore_notifier); -- cgit v0.10.2 From 7003087ce0fcdaf57a331b4ad627195a7f97245e Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 27 Mar 2009 16:28:34 -0700 Subject: xen/netfront: select XEN_XENBUS_FRONTEND Make sure the Xen frontend xenbus is enabled. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ian Campbell [corresponds to c40912891c3b in git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git] Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 4f1755b..cbf0635 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2960,6 +2960,7 @@ config TILE_NET config XEN_NETDEV_FRONTEND tristate "Xen network device frontend driver" depends on XEN + select XEN_XENBUS_FRONTEND default y help The network device frontend driver allows the kernel to -- cgit v0.10.2 From 6bac7f9f9e8e549c18ec4b77c499a45a1fae61b9 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Fri, 10 Dec 2010 14:39:15 +0000 Subject: xen/xenbus: fixup checkpatch issues in xenbus_probe* Signed-off-by: Ian Campbell Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 84bf17b..554ed57 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -72,7 +72,7 @@ config XEN_SYS_HYPERVISOR config XEN_XENBUS_FRONTEND tristate - + config XEN_PLATFORM_PCI tristate "xen platform pci device driver" depends on XEN_PVHVM diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 8119234..baa65e7 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -126,9 +126,11 @@ static int talk_to_otherend(struct xenbus_device *dev) static int watch_otherend(struct xenbus_device *dev) { - struct xen_bus_type *bus = container_of(dev->dev.bus, struct xen_bus_type, bus); + struct xen_bus_type *bus = + container_of(dev->dev.bus, struct xen_bus_type, bus); - return xenbus_watch_pathfmt(dev, &dev->otherend_watch, bus->otherend_changed, + return xenbus_watch_pathfmt(dev, &dev->otherend_watch, + bus->otherend_changed, "%s/%s", dev->otherend, "state"); } @@ -579,7 +581,8 @@ int xenbus_dev_suspend(struct device *dev, pm_message_t state) { int err = 0; struct xenbus_driver *drv; - struct xenbus_device *xdev = container_of(dev, struct xenbus_device, dev); + struct xenbus_device *xdev + = container_of(dev, struct xenbus_device, dev); DPRINTK("%s", xdev->nodename); @@ -599,7 +602,8 @@ int xenbus_dev_resume(struct device *dev) { int err; struct xenbus_driver *drv; - struct xenbus_device *xdev = container_of(dev, struct xenbus_device, dev); + struct xenbus_device *xdev + = container_of(dev, struct xenbus_device, dev); DPRINTK("%s", xdev->nodename); diff --git a/drivers/xen/xenbus/xenbus_probe.h b/drivers/xen/xenbus/xenbus_probe.h index 0e5fc4c..2466581 100644 --- a/drivers/xen/xenbus/xenbus_probe.h +++ b/drivers/xen/xenbus/xenbus_probe.h @@ -41,8 +41,10 @@ struct xen_bus_type char *root; unsigned int levels; int (*get_bus_id)(char bus_id[XEN_BUS_ID_SIZE], const char *nodename); - int (*probe)(struct xen_bus_type *bus, const char *type, const char *dir); - void (*otherend_changed)(struct xenbus_watch *watch, const char **vec, unsigned int len); + int (*probe)(struct xen_bus_type *bus, const char *type, + const char *dir); + void (*otherend_changed)(struct xenbus_watch *watch, const char **vec, + unsigned int len); struct bus_type bus; }; diff --git a/drivers/xen/xenbus/xenbus_probe_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c index 9b9dd36..6cf467b 100644 --- a/drivers/xen/xenbus/xenbus_probe_backend.c +++ b/drivers/xen/xenbus/xenbus_probe_backend.c @@ -84,8 +84,8 @@ static int backend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename) if (err) return err; - if (snprintf(bus_id, XEN_BUS_ID_SIZE, - "%.*s-%i-%s", typelen, type, domid, devid) >= XEN_BUS_ID_SIZE) + if (snprintf(bus_id, XEN_BUS_ID_SIZE, "%.*s-%i-%s", + typelen, type, domid, devid) >= XEN_BUS_ID_SIZE) return -ENOSPC; return 0; } @@ -147,7 +147,8 @@ static int xenbus_probe_backend_unit(struct xen_bus_type *bus, } /* backend// */ -static int xenbus_probe_backend(struct xen_bus_type *bus, const char *type, const char *domid) +static int xenbus_probe_backend(struct xen_bus_type *bus, const char *type, + const char *domid) { char *nodename; int err = 0; @@ -188,18 +189,18 @@ static struct device_attribute xenbus_backend_dev_attrs[] = { static struct xen_bus_type xenbus_backend = { .root = "backend", - .levels = 3, /* backend/type// */ + .levels = 3, /* backend/type// */ .get_bus_id = backend_bus_id, .probe = xenbus_probe_backend, .otherend_changed = frontend_changed, .bus = { - .name = "xen-backend", - .match = xenbus_match, - .uevent = xenbus_uevent_backend, - .probe = xenbus_dev_probe, - .remove = xenbus_dev_remove, - .shutdown = xenbus_dev_shutdown, - .dev_attrs = xenbus_backend_dev_attrs, + .name = "xen-backend", + .match = xenbus_match, + .uevent = xenbus_uevent_backend, + .probe = xenbus_dev_probe, + .remove = xenbus_dev_remove, + .shutdown = xenbus_dev_shutdown, + .dev_attrs = xenbus_backend_dev_attrs, }, }; @@ -221,24 +222,6 @@ static int read_frontend_details(struct xenbus_device *xendev) return xenbus_read_otherend_details(xendev, "frontend-id", "frontend"); } -//void xenbus_backend_suspend(int (*fn)(struct device *, void *)) -//{ -// DPRINTK(""); -// bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, fn); -//} - -//void xenbus_backend_resume(int (*fn)(struct device *, void *)) -//{ -// DPRINTK(""); -// bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, fn); -//} - -//int xenbus_for_each_backend(void *arg, int (*fn)(struct device *, void *)) -//{ -// return bus_for_each_dev(&xenbus_backend.bus, NULL, arg, fn); -//} -//EXPORT_SYMBOL_GPL(xenbus_for_each_backend); - int xenbus_dev_is_online(struct xenbus_device *dev) { int rc, val; diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index 60c311a..5bcc2d6 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -46,7 +46,8 @@ static int frontend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename) } /* device// */ -static int xenbus_probe_frontend(struct xen_bus_type *bus, const char *type, const char *name) +static int xenbus_probe_frontend(struct xen_bus_type *bus, const char *type, + const char *name) { char *nodename; int err; @@ -62,7 +63,8 @@ static int xenbus_probe_frontend(struct xen_bus_type *bus, const char *type, con return err; } -static int xenbus_uevent_frontend(struct device *_dev, struct kobj_uevent_env *env) +static int xenbus_uevent_frontend(struct device *_dev, + struct kobj_uevent_env *env) { struct xenbus_device *dev = to_xenbus_device(_dev); @@ -85,21 +87,21 @@ static struct device_attribute xenbus_frontend_dev_attrs[] = { static struct xen_bus_type xenbus_frontend = { .root = "device", - .levels = 2, /* device/type/ */ + .levels = 2, /* device/type/ */ .get_bus_id = frontend_bus_id, .probe = xenbus_probe_frontend, .otherend_changed = backend_changed, .bus = { - .name = "xen", - .match = xenbus_match, - .uevent = xenbus_uevent_frontend, - .probe = xenbus_dev_probe, - .remove = xenbus_dev_remove, - .shutdown = xenbus_dev_shutdown, - .dev_attrs= xenbus_frontend_dev_attrs, - - .suspend = xenbus_dev_suspend, - .resume = xenbus_dev_resume, + .name = "xen", + .match = xenbus_match, + .uevent = xenbus_uevent_frontend, + .probe = xenbus_dev_probe, + .remove = xenbus_dev_remove, + .shutdown = xenbus_dev_shutdown, + .dev_attrs = xenbus_frontend_dev_attrs, + + .suspend = xenbus_dev_suspend, + .resume = xenbus_dev_resume, }, }; -- cgit v0.10.2 From fce263c141faca8db85acb0524bdfdfae3ec0725 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 10 Dec 2010 22:33:15 -0500 Subject: xen/pci: Make xen-pcifront be dependent on XEN_XENBUS_FRONTEND Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 5b1630e..a9523fd 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -45,6 +45,7 @@ config XEN_PCIDEV_FRONTEND depends on PCI && X86 && XEN select HOTPLUG select PCI_XEN + select XEN_XENBUS_FRONTEND default y help The PCI device frontend driver allows the kernel to import arbitrary -- cgit v0.10.2 From 329620a878cf89184b28500d37fa33cc870a3357 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sat, 21 Mar 2009 23:29:34 -0700 Subject: xen/xenbus: making backend support modular is too complex Impact: build fix Making the xenbus backend support a separate module is needlessly complex and causes build failures. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 554ed57..5a48ce9 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -30,7 +30,7 @@ config XEN_DEV_EVTCHN If in doubt, say yes. config XEN_BACKEND - tristate "Backend driver support" + bool "Backend driver support" depends on XEN_DOM0 default y help -- cgit v0.10.2 From 858bc21f0637c407601a05626854ae58b242f75d Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 4 Jan 2011 10:46:49 -0800 Subject: drm/i915: check eDP encoder correctly when setting modes We were using a stale pointer in the check which caused us to use CPU attached DP params when we should have been using PCH attached params. Signed-off-by: Jesse Barnes Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=31988 Tested-by: Jan-Hendrik Zab Tested-by: Christoph Lukas Signed-off-by: Chris Wilson Cc: stable@kernel.org diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0abe79f..f4d1797 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3951,7 +3951,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, int lane = 0, link_bw, bpp; /* CPU eDP doesn't require FDI link, so just set DP M/N according to current link config */ - if (has_edp_encoder && !intel_encoder_is_pch_edp(&encoder->base)) { + if (has_edp_encoder && !intel_encoder_is_pch_edp(&has_edp_encoder->base)) { target_clock = mode->clock; intel_edp_link_config(has_edp_encoder, &lane, &link_bw); -- cgit v0.10.2 From 37f809755845cc3e18e8216c04525bdb885fa13b Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 5 Jan 2011 14:45:24 -0800 Subject: drm/i915: make DP training try a little harder When trying to do channel equalization, we need to make sure we still have clock recovery on all lanes while training. We also need to try clock recovery again if we lose the clock or if channel eq fails 5 times. We'll try clock recovery up to 5 more times before giving up entirely. Gets suspend/resume working on my Vaio again and brings us back into compliance with the DP training sequence spec. Signed-off-by: Jesse Barnes Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1dc6040..c768e30e 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1334,17 +1334,24 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; bool channel_eq = false; - int tries; + int tries, cr_tries; u32 reg; uint32_t DP = intel_dp->DP; /* channel equalization */ tries = 0; + cr_tries = 0; channel_eq = false; for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ uint32_t signal_levels; + if (cr_tries > 5) { + DRM_ERROR("failed to train DP, aborting\n"); + intel_dp_link_down(intel_dp); + break; + } + if (IS_GEN6(dev) && is_edp(intel_dp)) { signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; @@ -1367,14 +1374,26 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) if (!intel_dp_get_link_status(intel_dp)) break; + /* Make sure clock is still ok */ + if (!intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + intel_dp_start_link_train(intel_dp); + cr_tries++; + continue; + } + if (intel_channel_eq_ok(intel_dp)) { channel_eq = true; break; } - /* Try 5 times */ - if (tries > 5) - break; + /* Try 5 times, then try clock recovery if that fails */ + if (tries > 5) { + intel_dp_link_down(intel_dp); + intel_dp_start_link_train(intel_dp); + tries = 0; + cr_tries++; + continue; + } /* Compute new intel_dp->train_set as requested by target */ intel_get_adjust_train(intel_dp); -- cgit v0.10.2 From f5afcd3dd0dca7fe869311c51da54d5a889191ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller?= Date: Thu, 6 Jan 2011 12:29:32 +0000 Subject: drm/i915/crt: Check for a analog monitor in case of DVI-I MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since Linux 2.6.36 the digital output on my system (855GME + DVI-I) is not working any longer. The analog output is always activated regardless of the type of monitor attached. The culprit seems to be intel_crt_detect_ddc(), which returns true as soon as an ACK from the EDID device is received. Obviously this approach does not work with DVI-I where the analog and digital outputs share a common DDC bus. In a similar manner to the shared DDC wire, ala the "Mac Mini Hack", we need an additional check to make sure that there really is an analog device attached to the DDC. Signed-off-by: David Müller Signed-off-by: Chris Wilson Cc: stable@kernel.org diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 8df5743..17035b8 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -30,6 +30,7 @@ #include "drm.h" #include "drm_crtc.h" #include "drm_crtc_helper.h" +#include "drm_edid.h" #include "intel_drv.h" #include "i915_drm.h" #include "i915_drv.h" @@ -287,8 +288,9 @@ static bool intel_crt_ddc_probe(struct drm_i915_private *dev_priv, int ddc_bus) return i2c_transfer(&dev_priv->gmbus[ddc_bus].adapter, msgs, 1) == 1; } -static bool intel_crt_detect_ddc(struct intel_crt *crt) +static bool intel_crt_detect_ddc(struct drm_connector *connector) { + struct intel_crt *crt = intel_attached_crt(connector); struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private; /* CRT should always be at 0, but check anyway */ @@ -301,8 +303,26 @@ static bool intel_crt_detect_ddc(struct intel_crt *crt) } if (intel_ddc_probe(&crt->base, dev_priv->crt_ddc_pin)) { - DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n"); - return true; + struct edid *edid; + bool is_digital = false; + + edid = drm_get_edid(connector, + &dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter); + /* + * This may be a DVI-I connector with a shared DDC + * link between analog and digital outputs, so we + * have to check the EDID input spec of the attached device. + */ + if (edid != NULL) { + is_digital = edid->input & DRM_EDID_INPUT_DIGITAL; + connector->display_info.raw_edid = NULL; + kfree(edid); + } + + if (!is_digital) { + DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n"); + return true; + } } return false; @@ -458,7 +478,7 @@ intel_crt_detect(struct drm_connector *connector, bool force) } } - if (intel_crt_detect_ddc(crt)) + if (intel_crt_detect_ddc(connector)) return connector_status_connected; if (!force) @@ -472,7 +492,7 @@ intel_crt_detect(struct drm_connector *connector, bool force) crtc = intel_get_load_detect_pipe(&crt->base, connector, NULL, &dpms_mode); if (crtc) { - if (intel_crt_detect_ddc(crt)) + if (intel_crt_detect_ddc(connector)) status = connector_status_connected; else status = intel_crt_load_detect(crtc, crt); -- cgit v0.10.2 From 3c5a62b5226ca5db993660281e9c2a7275d9fb02 Mon Sep 17 00:00:00 2001 From: Yuanhan Liu Date: Thu, 6 Jan 2011 18:26:08 +0800 Subject: drm/i915: fix calculation of eDP signal levels on Sandybridge Some voltage swing/pre-emphasis level use the same value on eDP Sandybridge, like 400mv_0db and 600mv_0db are with the same value of (0x0 << 22). So, fix them, and point out the value if it isn't a supported voltage swing/pre-emphasis level. Signed-off-by: Yuanhan Liu Signed-off-by: Chris Wilson Cc: stable@kernel.org diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 8f948a6..677eca6 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3174,10 +3174,11 @@ #define EDP_LINK_TRAIN_600MV_3_5DB_SNB_A (0x01<<22) #define EDP_LINK_TRAIN_800MV_0DB_SNB_A (0x0<<22) /* SNB B-stepping */ -#define EDP_LINK_TRAIN_400MV_0DB_SNB_B (0x0<<22) -#define EDP_LINK_TRAIN_400MV_6DB_SNB_B (0x3a<<22) -#define EDP_LINK_TRAIN_600MV_3_5DB_SNB_B (0x39<<22) -#define EDP_LINK_TRAIN_800MV_0DB_SNB_B (0x38<<22) +#define EDP_LINK_TRAIN_400_600MV_0DB_SNB_B (0x0<<22) +#define EDP_LINK_TRAIN_400MV_3_5DB_SNB_B (0x1<<22) +#define EDP_LINK_TRAIN_400_600MV_6DB_SNB_B (0x3a<<22) +#define EDP_LINK_TRAIN_600_800MV_3_5DB_SNB_B (0x39<<22) +#define EDP_LINK_TRAIN_800_1200MV_0DB_SNB_B (0x38<<22) #define EDP_LINK_TRAIN_VOL_EMP_MASK_SNB (0x3f<<22) #define FORCEWAKE 0xA18C diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index c768e30e..1f4242b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1153,18 +1153,27 @@ intel_dp_signal_levels(uint8_t train_set, int lane_count) static uint32_t intel_gen6_edp_signal_levels(uint8_t train_set) { - switch (train_set & (DP_TRAIN_VOLTAGE_SWING_MASK|DP_TRAIN_PRE_EMPHASIS_MASK)) { + int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | + DP_TRAIN_PRE_EMPHASIS_MASK); + switch (signal_levels) { case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: - return EDP_LINK_TRAIN_400MV_0DB_SNB_B; + case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: + return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B; + case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: + return EDP_LINK_TRAIN_400MV_3_5DB_SNB_B; case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: - return EDP_LINK_TRAIN_400MV_6DB_SNB_B; + case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6: + return EDP_LINK_TRAIN_400_600MV_6DB_SNB_B; case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: - return EDP_LINK_TRAIN_600MV_3_5DB_SNB_B; + case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: + return EDP_LINK_TRAIN_600_800MV_3_5DB_SNB_B; case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: - return EDP_LINK_TRAIN_800MV_0DB_SNB_B; + case DP_TRAIN_VOLTAGE_SWING_1200 | DP_TRAIN_PRE_EMPHASIS_0: + return EDP_LINK_TRAIN_800_1200MV_0DB_SNB_B; default: - DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level\n"); - return EDP_LINK_TRAIN_400MV_0DB_SNB_B; + DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:" + "0x%x\n", signal_levels); + return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B; } } -- cgit v0.10.2 From 97aaf910731b03b27b1c4c8a58006a1dc99dcd9a Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 4 Jan 2011 20:10:52 +0000 Subject: drm/i915/sdvo: Defer detection of output capabilities until probing Alex Fiestas reported an issue with his HDMI connector being misdetected as DVI unless he had something connected upon boot. By moving the decision as to whether to use HDMI or DVI encoding for the HDMI capable output until we probe the monitor means that we should avoid sending a HDMI signal to a DVI monitor and also correctly detect hardware like Alex's. However, to really determine what connector is soldered onto the wire we need to inspect the VBT sdvo child devices - but can we trust it? Reported-by: Alex Fiestas Tested-by: Alex Fiestas Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=32828 Signed-off-by: Chris Wilson Cc: stable@kernel.org diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 9d0af36..45cd376 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1024,9 +1024,13 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, if (!intel_sdvo_set_target_input(intel_sdvo)) return; - if (intel_sdvo->has_hdmi_monitor && - !intel_sdvo_set_avi_infoframe(intel_sdvo)) - return; + if (intel_sdvo->has_hdmi_monitor) { + intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI); + intel_sdvo_set_colorimetry(intel_sdvo, + SDVO_COLORIMETRY_RGB256); + intel_sdvo_set_avi_infoframe(intel_sdvo); + } else + intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_DVI); if (intel_sdvo->is_tv && !intel_sdvo_set_tv_format(intel_sdvo)) @@ -1398,6 +1402,9 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) intel_sdvo->attached_output = response; + intel_sdvo->has_hdmi_monitor = false; + intel_sdvo->has_hdmi_audio = false; + if ((intel_sdvo_connector->output_flag & response) == 0) ret = connector_status_disconnected; else if (response & SDVO_TMDS_MASK) @@ -1922,20 +1929,7 @@ intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv, static bool intel_sdvo_is_hdmi_connector(struct intel_sdvo *intel_sdvo, int device) { - int is_hdmi; - - if (!intel_sdvo_check_supp_encode(intel_sdvo)) - return false; - - if (!intel_sdvo_set_target_output(intel_sdvo, - device == 0 ? SDVO_OUTPUT_TMDS0 : SDVO_OUTPUT_TMDS1)) - return false; - - is_hdmi = 0; - if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE, &is_hdmi, 1)) - return false; - - return !!is_hdmi; + return intel_sdvo_check_supp_encode(intel_sdvo); } static u8 @@ -2037,12 +2031,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) connector->connector_type = DRM_MODE_CONNECTOR_DVID; if (intel_sdvo_is_hdmi_connector(intel_sdvo, device)) { - /* enable hdmi encoding mode if supported */ - intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI); - intel_sdvo_set_colorimetry(intel_sdvo, - SDVO_COLORIMETRY_RGB256); connector->connector_type = DRM_MODE_CONNECTOR_HDMIA; - intel_sdvo->is_hdmi = true; } intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) | -- cgit v0.10.2 From 47356eb67285014527a5ab87543ba1fae3d1e10a Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 11 Jan 2011 17:06:04 +0000 Subject: drm/i915/panel: Only record the backlight level when it is enabled By tracking the current status of the backlight we can prevent recording the value of the current backlight when we have disabled it. And so prevent restoring it to 'off' after an unbalanced sequence of intel_lvds_disable/enable. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=22672 Tested-by: Alex Riesen Tested-by: Larry Finger Signed-off-by: Chris Wilson Cc: stable@kernel.org diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index aac1bf3..972e08e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -332,6 +332,7 @@ typedef struct drm_i915_private { /* LVDS info */ int backlight_level; /* restore backlight to this value */ + bool backlight_enabled; struct drm_display_mode *panel_fixed_mode; struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f4d1797..bc829bb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5802,6 +5802,8 @@ static void intel_setup_outputs(struct drm_device *dev) encoder->base.possible_clones = intel_encoder_clones(dev, encoder->clone_mask); } + + intel_panel_setup_backlight(dev); } static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d782ad9..74db255 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -257,6 +257,9 @@ extern void intel_pch_panel_fitting(struct drm_device *dev, extern u32 intel_panel_get_max_backlight(struct drm_device *dev); extern u32 intel_panel_get_backlight(struct drm_device *dev); extern void intel_panel_set_backlight(struct drm_device *dev, u32 level); +extern void intel_panel_setup_backlight(struct drm_device *dev); +extern void intel_panel_enable_backlight(struct drm_device *dev); +extern void intel_panel_disable_backlight(struct drm_device *dev); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_encoder_prepare (struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index aa23070..4e7dcb3 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -106,7 +106,7 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds) I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); POSTING_READ(lvds_reg); - intel_panel_set_backlight(dev, dev_priv->backlight_level); + intel_panel_enable_backlight(dev); } static void intel_lvds_disable(struct intel_lvds *intel_lvds) @@ -123,8 +123,7 @@ static void intel_lvds_disable(struct intel_lvds *intel_lvds) lvds_reg = LVDS; } - dev_priv->backlight_level = intel_panel_get_backlight(dev); - intel_panel_set_backlight(dev, 0); + intel_panel_disable_backlight(dev); I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); @@ -398,8 +397,6 @@ static void intel_lvds_prepare(struct drm_encoder *encoder) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_lvds *intel_lvds = to_intel_lvds(encoder); - dev_priv->backlight_level = intel_panel_get_backlight(dev); - /* We try to do the minimum that is necessary in order to unlock * the registers for mode setting. * @@ -430,9 +427,6 @@ static void intel_lvds_commit(struct drm_encoder *encoder) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_lvds *intel_lvds = to_intel_lvds(encoder); - if (dev_priv->backlight_level == 0) - dev_priv->backlight_level = intel_panel_get_max_backlight(dev); - /* Undo any unlocking done in prepare to prevent accidental * adjustment of the registers. */ diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 7350ec2..e00d200 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -250,3 +250,34 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level) tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK; I915_WRITE(BLC_PWM_CTL, tmp | level); } + +void intel_panel_disable_backlight(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->backlight_enabled) { + dev_priv->backlight_level = intel_panel_get_backlight(dev); + dev_priv->backlight_enabled = false; + } + + intel_panel_set_backlight(dev, 0); +} + +void intel_panel_enable_backlight(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->backlight_level == 0) + dev_priv->backlight_level = intel_panel_get_max_backlight(dev); + + intel_panel_set_backlight(dev, dev_priv->backlight_level); + dev_priv->backlight_enabled = true; +} + +void intel_panel_setup_backlight(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + dev_priv->backlight_level = intel_panel_get_max_backlight(dev); + dev_priv->backlight_enabled = dev_priv->backlight_level != 0; +} -- cgit v0.10.2 From bee17e5ae6b68d21b9d193f34ccefeef9d4fffe0 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 11 Jan 2011 18:09:58 +0000 Subject: drm/i915/lvds: Always use 0 to disable the pfit controller ... and just any combination of bits & ~PFIT_ENABLE. This way we do not attempt disable to the panel fitter controller uselessly upon intel_lvds_disable(). Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 4e7dcb3..8f4f6bd 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -374,6 +374,10 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, } out: + if ((pfit_control & PFIT_ENABLE) == 0) { + pfit_control = 0; + pfit_pgm_ratios = 0; + } if (pfit_control != intel_lvds->pfit_control || pfit_pgm_ratios != intel_lvds->pfit_pgm_ratios) { intel_lvds->pfit_control = pfit_control; -- cgit v0.10.2 From a6044e23b784544fe567db75dbf9c4f684bd6d5b Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 20 Dec 2010 11:34:20 -0800 Subject: drm/i915: support overclocking on Sandy Bridge In some configuration, the PCU may allow us to overclock the GPU. Check for this case and adjust the max frequency as appropriate. Also initialize the min/max frequencies to default values as indicated by hardware. Signed-off-by: Jesse Barnes Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 677eca6..1bc816f 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3240,6 +3240,7 @@ #define GEN6_PCODE_MAILBOX 0x138124 #define GEN6_PCODE_READY (1<<31) +#define GEN6_READ_OC_PARAMS 0xc #define GEN6_PCODE_WRITE_MIN_FREQ_TABLE 0x9 #define GEN6_PCODE_DATA 0x138128 diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bc829bb..efa8855 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6147,6 +6147,10 @@ void intel_init_emon(struct drm_device *dev) void gen6_enable_rps(struct drm_i915_private *dev_priv) { + u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); + u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); + u32 pcu_mbox; + int cur_freq, min_freq, max_freq; int i; /* Here begins a magic sequence of register writes to enable @@ -6218,6 +6222,29 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv) 500)) DRM_ERROR("timeout waiting for pcode mailbox to finish\n"); + min_freq = (rp_state_cap & 0xff0000) >> 16; + max_freq = rp_state_cap & 0xff; + cur_freq = (gt_perf_status & 0xff00) >> 8; + + /* Check for overclock support */ + if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0, + 500)) + DRM_ERROR("timeout waiting for pcode mailbox to become idle\n"); + I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_READ_OC_PARAMS); + pcu_mbox = I915_READ(GEN6_PCODE_DATA); + if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0, + 500)) + DRM_ERROR("timeout waiting for pcode mailbox to finish\n"); + if (pcu_mbox & (1<<31)) { /* OC supported */ + max_freq = pcu_mbox & 0xff; + DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency max to %dMHz\n", pcu_mbox * 100); + } + + /* In units of 100MHz */ + dev_priv->max_delay = max_freq; + dev_priv->min_delay = min_freq; + dev_priv->cur_delay = cur_freq; + /* requires MSI enabled */ I915_WRITE(GEN6_PMIER, GEN6_PM_MBOX_EVENT | -- cgit v0.10.2 From 35c3047ad15849335242b847c94f180ef45db490 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 22 Dec 2010 14:07:12 +0000 Subject: drm/i915: Use the mappable sizes determined by GTT for consistency. There should be no difference, but we can eliminate redundant code. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index efa8855..9d97fd4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6694,12 +6694,7 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; } - - /* set memory base */ - if (IS_GEN2(dev)) - dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0); - else - dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2); + dev->mode_config.fb_base = dev->agp->base; if (IS_MOBILE(dev) || !IS_GEN2(dev)) dev_priv->num_pipe = 2; diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 701e830..ee145a2 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -62,6 +62,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev, struct drm_fb_helper_surface_size *sizes) { struct drm_device *dev = ifbdev->helper.dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct fb_info *info; struct drm_framebuffer *fb; struct drm_mode_fb_cmd mode_cmd; @@ -77,7 +78,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev, mode_cmd.height = sizes->surface_height; mode_cmd.bpp = sizes->surface_bpp; - mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 1) / 8), 64); + mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 7) / 8), 64); mode_cmd.depth = sizes->surface_depth; size = mode_cmd.pitch * mode_cmd.height; @@ -120,6 +121,11 @@ static int intelfb_create(struct intel_fbdev *ifbdev, info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; info->fbops = &intelfb_ops; + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + goto out_unpin; + } /* setup aperture base/size for vesafb takeover */ info->apertures = alloc_apertures(1); if (!info->apertures) { @@ -127,10 +133,8 @@ static int intelfb_create(struct intel_fbdev *ifbdev, goto out_unpin; } info->apertures->ranges[0].base = dev->mode_config.fb_base; - if (!IS_GEN2(dev)) - info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 2); - else - info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0); + info->apertures->ranges[0].size = + dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset; info->fix.smem_len = size; @@ -140,12 +144,6 @@ static int intelfb_create(struct intel_fbdev *ifbdev, ret = -ENOSPC; goto out_unpin; } - - ret = fb_alloc_cmap(&info->cmap, 256, 0); - if (ret) { - ret = -ENOMEM; - goto out_unpin; - } info->screen_size = size; // memset(info->screen_base, 0, size); -- cgit v0.10.2 From 55249baaa5cd188ebd9acdb047eeaed8092e4a93 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 22 Dec 2010 14:04:47 +0000 Subject: drm/i915: Workaround erratum on i830 for TAIL pointer within last 2 cachelines On i830 if the tail pointer is set to within 2 cachelines of the end of the buffer, the chip may hang. So instead if the tail were to land in that location, we pad the end of the buffer with NOPs, and start again at the beginning. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 56bc95c..2de0e45 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -785,6 +785,14 @@ int intel_init_ring_buffer(struct drm_device *dev, if (ret) goto err_unmap; + /* Workaround an erratum on the i830 which causes a hang if + * the TAIL pointer points to within the last 2 cachelines + * of the buffer. + */ + ring->effective_size = ring->size; + if (IS_I830(ring->dev)) + ring->effective_size -= 128; + return 0; err_unmap: @@ -827,8 +835,7 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring) static int intel_wrap_ring_buffer(struct intel_ring_buffer *ring) { unsigned int *virt; - int rem; - rem = ring->size - ring->tail; + int rem = ring->size - ring->tail; if (ring->space < rem) { int ret = intel_wait_ring_buffer(ring, rem); @@ -895,7 +902,7 @@ int intel_ring_begin(struct intel_ring_buffer *ring, int n = 4*num_dwords; int ret; - if (unlikely(ring->tail + n > ring->size)) { + if (unlikely(ring->tail + n > ring->effective_size)) { ret = intel_wrap_ring_buffer(ring); if (unlikely(ret)) return ret; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 8e2e357..bbbf505 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -49,6 +49,7 @@ struct intel_ring_buffer { u32 tail; int space; int size; + int effective_size; struct intel_hw_status_page status_page; u32 irq_seqno; /* last seq seem at irq time */ -- cgit v0.10.2 From c97689d8860f086125e7ff9cd730027a0057ca4f Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 23 Dec 2010 10:40:38 +0000 Subject: agp/intel: Flush the chipset write buffers when changing GTT base Flush the chipset write buffers before and after adjusting the GTT base register, just in case. We only modify this value upon initialisation (boot and resume) so there should be no outstanding writes, however there are always those persistent PGTBL_ER that keep getting reported upon resume. Signed-off-by: Chris Wilson diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index 010e3de..c195bfe 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -94,6 +94,8 @@ #define G4x_GMCH_SIZE_VT_1_5M (0xa << 8) #define G4x_GMCH_SIZE_VT_2M (0xc << 8) +#define GFX_FLSH_CNTL 0x2170 /* 915+ */ + #define I810_DRAM_CTL 0x3000 #define I810_DRAM_ROW_0 0x00000001 #define I810_DRAM_ROW_0_SDRAM 0x00000001 diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 356f73e..da81618 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -814,6 +814,12 @@ static bool intel_enable_gtt(void) } } + /* On the resume path we may be adjusting the PGTBL value, so + * be paranoid and flush all chipset write buffers... + */ + if (INTEL_GTT_GEN >= 3) + writel(0, intel_private.registers+GFX_FLSH_CNTL); + reg = intel_private.registers+I810_PGETBL_CTL; writel(intel_private.PGETBL_save, reg); if (HAS_PGTBL_EN && (readl(reg) & I810_PGETBL_ENABLED) == 0) { @@ -823,6 +829,9 @@ static bool intel_enable_gtt(void) return false; } + if (INTEL_GTT_GEN >= 3) + writel(0, intel_private.registers+GFX_FLSH_CNTL); + return true; } -- cgit v0.10.2 From b79d4990226defc3789f9ba492b27e9e56790857 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 21 Dec 2010 13:10:23 -0800 Subject: drm/i915: support low power watermarks on Ironlake This patch actually makes the watermark code even uglier (if that's possible), but has the advantage of sharing code between SNB and ILK at least. Longer term we should refactor the watermark stuff into its own file and clean it up now that we know how it's supposed to work. Supporting WM2 on my Vaio reduced power consumption by around 0.5W, so this patch is definitely worthwhile (though it also needs lots of test coverage). Signed-off-by: Jesse Barnes [ickle: pass the watermark structs arounds] Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 1bc816f..ecfb002 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2345,8 +2345,13 @@ /* Memory latency timer register */ #define MLTR_ILK 0x11222 +#define MLTR_WM1_SHIFT 0 +#define MLTR_WM2_SHIFT 8 /* the unit of memory self-refresh latency time is 0.5us */ #define ILK_SRLT_MASK 0x3f +#define ILK_LATENCY(shift) (I915_READ(MLTR_ILK) >> (shift) & ILK_SRLT_MASK) +#define ILK_READ_WM1_LATENCY() ILK_LATENCY(MLTR_WM1_SHIFT) +#define ILK_READ_WM2_LATENCY() ILK_LATENCY(MLTR_WM2_SHIFT) /* define the fifo size on Ironlake */ #define ILK_DISPLAY_FIFO 128 diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9d97fd4..79753b8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3456,113 +3456,17 @@ static bool ironlake_compute_wm0(struct drm_device *dev, return true; } -static void ironlake_update_wm(struct drm_device *dev, - int planea_clock, int planeb_clock, - int sr_hdisplay, int sr_htotal, - int pixel_size) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - int plane_wm, cursor_wm, enabled; - int tmp; - - enabled = 0; - if (ironlake_compute_wm0(dev, 0, - &ironlake_display_wm_info, - ILK_LP0_PLANE_LATENCY, - &ironlake_cursor_wm_info, - ILK_LP0_CURSOR_LATENCY, - &plane_wm, &cursor_wm)) { - I915_WRITE(WM0_PIPEA_ILK, - (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); - DRM_DEBUG_KMS("FIFO watermarks For pipe A -" - " plane %d, " "cursor: %d\n", - plane_wm, cursor_wm); - enabled++; - } - - if (ironlake_compute_wm0(dev, 1, - &ironlake_display_wm_info, - ILK_LP0_PLANE_LATENCY, - &ironlake_cursor_wm_info, - ILK_LP0_CURSOR_LATENCY, - &plane_wm, &cursor_wm)) { - I915_WRITE(WM0_PIPEB_ILK, - (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); - DRM_DEBUG_KMS("FIFO watermarks For pipe B -" - " plane %d, cursor: %d\n", - plane_wm, cursor_wm); - enabled++; - } - - /* - * Calculate and update the self-refresh watermark only when one - * display plane is used. - */ - tmp = 0; - if (enabled == 1) { - unsigned long line_time_us; - int small, large, plane_fbc; - int sr_clock, entries; - int line_count, line_size; - /* Read the self-refresh latency. The unit is 0.5us */ - int ilk_sr_latency = I915_READ(MLTR_ILK) & ILK_SRLT_MASK; - - sr_clock = planea_clock ? planea_clock : planeb_clock; - line_time_us = (sr_htotal * 1000) / sr_clock; - - /* Use ns/us then divide to preserve precision */ - line_count = ((ilk_sr_latency * 500) / line_time_us + 1000) - / 1000; - line_size = sr_hdisplay * pixel_size; - - /* Use the minimum of the small and large buffer method for primary */ - small = ((sr_clock * pixel_size / 1000) * (ilk_sr_latency * 500)) / 1000; - large = line_count * line_size; - - entries = DIV_ROUND_UP(min(small, large), - ironlake_display_srwm_info.cacheline_size); - - plane_fbc = entries * 64; - plane_fbc = DIV_ROUND_UP(plane_fbc, line_size); - - plane_wm = entries + ironlake_display_srwm_info.guard_size; - if (plane_wm > (int)ironlake_display_srwm_info.max_wm) - plane_wm = ironlake_display_srwm_info.max_wm; - - /* calculate the self-refresh watermark for display cursor */ - entries = line_count * pixel_size * 64; - entries = DIV_ROUND_UP(entries, - ironlake_cursor_srwm_info.cacheline_size); - - cursor_wm = entries + ironlake_cursor_srwm_info.guard_size; - if (cursor_wm > (int)ironlake_cursor_srwm_info.max_wm) - cursor_wm = ironlake_cursor_srwm_info.max_wm; - - /* configure watermark and enable self-refresh */ - tmp = (WM1_LP_SR_EN | - (ilk_sr_latency << WM1_LP_LATENCY_SHIFT) | - (plane_fbc << WM1_LP_FBC_SHIFT) | - (plane_wm << WM1_LP_SR_SHIFT) | - cursor_wm); - DRM_DEBUG_KMS("self-refresh watermark: display plane %d, fbc lines %d," - " cursor %d\n", plane_wm, plane_fbc, cursor_wm); - } - I915_WRITE(WM1_LP_ILK, tmp); - /* XXX setup WM2 and WM3 */ -} - /* * Check the wm result. * * If any calculated watermark values is larger than the maximum value that * can be programmed into the associated watermark register, that watermark * must be disabled. - * - * Also return true if all of those watermark values is 0, which is set by - * sandybridge_compute_srwm, to indicate the latency is ZERO. */ -static bool sandybridge_check_srwm(struct drm_device *dev, int level, - int fbc_wm, int display_wm, int cursor_wm) +static bool ironlake_check_srwm(struct drm_device *dev, int level, + int fbc_wm, int display_wm, int cursor_wm, + const struct intel_watermark_params *display, + const struct intel_watermark_params *cursor) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3571,7 +3475,7 @@ static bool sandybridge_check_srwm(struct drm_device *dev, int level, if (fbc_wm > SNB_FBC_MAX_SRWM) { DRM_DEBUG_KMS("fbc watermark(%d) is too large(%d), disabling wm%d+\n", - fbc_wm, SNB_FBC_MAX_SRWM, level); + fbc_wm, SNB_FBC_MAX_SRWM, level); /* fbc has it's own way to disable FBC WM */ I915_WRITE(DISP_ARB_CTL, @@ -3579,15 +3483,15 @@ static bool sandybridge_check_srwm(struct drm_device *dev, int level, return false; } - if (display_wm > SNB_DISPLAY_MAX_SRWM) { + if (display_wm > display->max_wm) { DRM_DEBUG_KMS("display watermark(%d) is too large(%d), disabling wm%d+\n", - display_wm, SNB_DISPLAY_MAX_SRWM, level); + display_wm, SNB_DISPLAY_MAX_SRWM, level); return false; } - if (cursor_wm > SNB_CURSOR_MAX_SRWM) { + if (cursor_wm > cursor->max_wm) { DRM_DEBUG_KMS("cursor watermark(%d) is too large(%d), disabling wm%d+\n", - cursor_wm, SNB_CURSOR_MAX_SRWM, level); + cursor_wm, SNB_CURSOR_MAX_SRWM, level); return false; } @@ -3602,16 +3506,18 @@ static bool sandybridge_check_srwm(struct drm_device *dev, int level, /* * Compute watermark values of WM[1-3], */ -static bool sandybridge_compute_srwm(struct drm_device *dev, int level, - int hdisplay, int htotal, int pixel_size, - int clock, int latency_ns, int *fbc_wm, - int *display_wm, int *cursor_wm) +static bool ironlake_compute_srwm(struct drm_device *dev, int level, + int hdisplay, int htotal, + int pixel_size, int clock, int latency_ns, + const struct intel_watermark_params *display, + const struct intel_watermark_params *cursor, + int *fbc_wm, int *display_wm, int *cursor_wm) { unsigned long line_time_us; + int line_count, line_size; int small, large; int entries; - int line_count, line_size; if (!latency_ns) { *fbc_wm = *display_wm = *cursor_wm = 0; @@ -3626,24 +3532,110 @@ static bool sandybridge_compute_srwm(struct drm_device *dev, int level, small = ((clock * pixel_size / 1000) * latency_ns) / 1000; large = line_count * line_size; - entries = DIV_ROUND_UP(min(small, large), - sandybridge_display_srwm_info.cacheline_size); - *display_wm = entries + sandybridge_display_srwm_info.guard_size; + entries = DIV_ROUND_UP(min(small, large), display->cacheline_size); + *display_wm = entries + display->guard_size; /* - * Spec said: + * Spec says: * FBC WM = ((Final Primary WM * 64) / number of bytes per line) + 2 */ *fbc_wm = DIV_ROUND_UP(*display_wm * 64, line_size) + 2; /* calculate the self-refresh watermark for display cursor */ entries = line_count * pixel_size * 64; - entries = DIV_ROUND_UP(entries, - sandybridge_cursor_srwm_info.cacheline_size); - *cursor_wm = entries + sandybridge_cursor_srwm_info.guard_size; + entries = DIV_ROUND_UP(entries, cursor->cacheline_size); + *cursor_wm = entries + cursor->guard_size; - return sandybridge_check_srwm(dev, level, - *fbc_wm, *display_wm, *cursor_wm); + return ironlake_check_srwm(dev, level, + *fbc_wm, *display_wm, *cursor_wm, + display, cursor); +} + +static void ironlake_update_wm(struct drm_device *dev, + int planea_clock, int planeb_clock, + int hdisplay, int htotal, + int pixel_size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int fbc_wm, plane_wm, cursor_wm, enabled; + int clock; + + enabled = 0; + if (ironlake_compute_wm0(dev, 0, + &ironlake_display_wm_info, + ILK_LP0_PLANE_LATENCY, + &ironlake_cursor_wm_info, + ILK_LP0_CURSOR_LATENCY, + &plane_wm, &cursor_wm)) { + I915_WRITE(WM0_PIPEA_ILK, + (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); + DRM_DEBUG_KMS("FIFO watermarks For pipe A -" + " plane %d, " "cursor: %d\n", + plane_wm, cursor_wm); + enabled++; + } + + if (ironlake_compute_wm0(dev, 1, + &ironlake_display_wm_info, + ILK_LP0_PLANE_LATENCY, + &ironlake_cursor_wm_info, + ILK_LP0_CURSOR_LATENCY, + &plane_wm, &cursor_wm)) { + I915_WRITE(WM0_PIPEB_ILK, + (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); + DRM_DEBUG_KMS("FIFO watermarks For pipe B -" + " plane %d, cursor: %d\n", + plane_wm, cursor_wm); + enabled++; + } + + /* + * Calculate and update the self-refresh watermark only when one + * display plane is used. + */ + I915_WRITE(WM3_LP_ILK, 0); + I915_WRITE(WM2_LP_ILK, 0); + I915_WRITE(WM1_LP_ILK, 0); + + if (enabled != 1) + return; + + clock = planea_clock ? planea_clock : planeb_clock; + + /* WM1 */ + if (!ironlake_compute_srwm(dev, 1, hdisplay, htotal, pixel_size, + clock, ILK_READ_WM1_LATENCY() * 500, + &ironlake_display_srwm_info, + &ironlake_cursor_srwm_info, + &fbc_wm, &plane_wm, &cursor_wm)) + return; + + I915_WRITE(WM1_LP_ILK, + WM1_LP_SR_EN | + (ILK_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) | + (fbc_wm << WM1_LP_FBC_SHIFT) | + (plane_wm << WM1_LP_SR_SHIFT) | + cursor_wm); + + /* WM2 */ + if (!ironlake_compute_srwm(dev, 2, hdisplay, htotal, pixel_size, + clock, ILK_READ_WM2_LATENCY() * 500, + &ironlake_display_srwm_info, + &ironlake_cursor_srwm_info, + &fbc_wm, &plane_wm, &cursor_wm)) + return; + + I915_WRITE(WM2_LP_ILK, + WM2_LP_EN | + (ILK_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) | + (fbc_wm << WM1_LP_FBC_SHIFT) | + (plane_wm << WM1_LP_SR_SHIFT) | + cursor_wm); + + /* + * WM3 is unsupported on ILK, probably because we don't have latency + * data for that power state + */ } static void sandybridge_update_wm(struct drm_device *dev, @@ -3701,9 +3693,11 @@ static void sandybridge_update_wm(struct drm_device *dev, clock = planea_clock ? planea_clock : planeb_clock; /* WM1 */ - if (!sandybridge_compute_srwm(dev, 1, hdisplay, htotal, pixel_size, - clock, SNB_READ_WM1_LATENCY() * 500, - &fbc_wm, &plane_wm, &cursor_wm)) + if (!ironlake_compute_srwm(dev, 1, hdisplay, htotal, pixel_size, + clock, SNB_READ_WM1_LATENCY() * 500, + &sandybridge_display_srwm_info, + &sandybridge_cursor_srwm_info, + &fbc_wm, &plane_wm, &cursor_wm)) return; I915_WRITE(WM1_LP_ILK, @@ -3714,10 +3708,12 @@ static void sandybridge_update_wm(struct drm_device *dev, cursor_wm); /* WM2 */ - if (!sandybridge_compute_srwm(dev, 2, - hdisplay, htotal, pixel_size, - clock, SNB_READ_WM2_LATENCY() * 500, - &fbc_wm, &plane_wm, &cursor_wm)) + if (!ironlake_compute_srwm(dev, 2, + hdisplay, htotal, pixel_size, + clock, SNB_READ_WM2_LATENCY() * 500, + &sandybridge_display_srwm_info, + &sandybridge_cursor_srwm_info, + &fbc_wm, &plane_wm, &cursor_wm)) return; I915_WRITE(WM2_LP_ILK, @@ -3728,10 +3724,12 @@ static void sandybridge_update_wm(struct drm_device *dev, cursor_wm); /* WM3 */ - if (!sandybridge_compute_srwm(dev, 3, - hdisplay, htotal, pixel_size, - clock, SNB_READ_WM3_LATENCY() * 500, - &fbc_wm, &plane_wm, &cursor_wm)) + if (!ironlake_compute_srwm(dev, 3, + hdisplay, htotal, pixel_size, + clock, SNB_READ_WM3_LATENCY() * 500, + &sandybridge_display_srwm_info, + &sandybridge_cursor_srwm_info, + &fbc_wm, &plane_wm, &cursor_wm)) return; I915_WRITE(WM3_LP_ILK, -- cgit v0.10.2 From a0fa62d3b6afaa260cad8ccd6944e81ad01c7cf3 Mon Sep 17 00:00:00 2001 From: Yuanhan Liu Date: Thu, 23 Dec 2010 16:35:40 +0800 Subject: drm/i915: fix the wrong latency value while computing wm0 On Ironlake, the LP0 latency is hardcoded and in ns unit, while on Sandybridge, it comes from a register and with unit 0.1 us. So, fix the wrong latency value while computing wm0 on Ironlake and Sandybridge. Signed-off-by: Yuanhan Liu Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 79753b8..75c433e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3418,9 +3418,9 @@ static void i830_update_wm(struct drm_device *dev, int planea_clock, int unused, static bool ironlake_compute_wm0(struct drm_device *dev, int pipe, const struct intel_watermark_params *display, - int display_latency, + int display_latency_ns, const struct intel_watermark_params *cursor, - int cursor_latency, + int cursor_latency_ns, int *plane_wm, int *cursor_wm) { @@ -3438,7 +3438,7 @@ static bool ironlake_compute_wm0(struct drm_device *dev, pixel_size = crtc->fb->bits_per_pixel / 8; /* Use the small buffer method to calculate plane watermark */ - entries = ((clock * pixel_size / 1000) * display_latency * 100) / 1000; + entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000; entries = DIV_ROUND_UP(entries, display->cacheline_size); *plane_wm = entries + display->guard_size; if (*plane_wm > (int)display->max_wm) @@ -3446,7 +3446,7 @@ static bool ironlake_compute_wm0(struct drm_device *dev, /* Use the large buffer method to calculate cursor watermark */ line_time_us = ((htotal * 1000) / clock); - line_count = (cursor_latency * 100 / line_time_us + 1000) / 1000; + line_count = (cursor_latency_ns / line_time_us + 1000) / 1000; entries = line_count * 64 * pixel_size; entries = DIV_ROUND_UP(entries, cursor->cacheline_size); *cursor_wm = entries + cursor->guard_size; @@ -3644,7 +3644,7 @@ static void sandybridge_update_wm(struct drm_device *dev, int pixel_size) { struct drm_i915_private *dev_priv = dev->dev_private; - int latency = SNB_READ_WM0_LATENCY(); + int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */ int fbc_wm, plane_wm, cursor_wm, enabled; int clock; -- cgit v0.10.2 From d78cb50baa9177353d6719612b83558a9bf2d59b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 23 Dec 2010 13:33:15 +0000 Subject: drm/i915: add 'reset' parameter When bringing up new hardware, or otherwise experimenting, GPU hangs are a way of life. However, the automatic GPU reset can do more harm than good under these circumstances, as we may wish to capture a full trace for debugging. Based on a patch by Zhenyu Wang. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 8724933..2913496 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -49,6 +49,9 @@ module_param_named(powersave, i915_powersave, int, 0600); unsigned int i915_lvds_downclock = 0; module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400); +bool i915_try_reset = true; +module_param_named(reset, i915_try_reset, bool, 0600); + static struct drm_driver driver; extern int intel_agp_enabled; @@ -475,6 +478,9 @@ int i915_reset(struct drm_device *dev, u8 flags) bool need_display = true; int ret; + if (!i915_try_reset) + return 0; + if (!mutex_trylock(&dev->struct_mutex)) return -EBUSY; -- cgit v0.10.2 From dbdc647927a0f4b34e7cf486889d8f671f73d2e5 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 30 Dec 2010 09:36:39 -0800 Subject: drm/i915: avoid reading non-existent PLL reg on Ironlake+ These functions need to be reworked for Ironlake and above, but until then at least avoid reading non-existent registers. Signed-off-by: Jesse Barnes [ickle: combine with a gratuitous tidy] Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 75c433e..043825c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5036,8 +5036,8 @@ static void intel_increase_pllclock(struct drm_crtc *crtc) drm_i915_private_t *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dpll = I915_READ(dpll_reg); + int dpll_reg = DPLL(pipe); + int dpll; if (HAS_PCH_SPLIT(dev)) return; @@ -5045,17 +5045,19 @@ static void intel_increase_pllclock(struct drm_crtc *crtc) if (!dev_priv->lvds_downclock_avail) return; + dpll = I915_READ(dpll_reg); if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) { DRM_DEBUG_DRIVER("upclocking LVDS\n"); /* Unlock panel regs */ - I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | - PANEL_UNLOCK_REGS); + I915_WRITE(PP_CONTROL, + I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); dpll &= ~DISPLAY_RATE_SELECT_FPA1; I915_WRITE(dpll_reg, dpll); - dpll = I915_READ(dpll_reg); + POSTING_READ(dpll_reg); intel_wait_for_vblank(dev, pipe); + dpll = I915_READ(dpll_reg); if (dpll & DISPLAY_RATE_SELECT_FPA1) DRM_DEBUG_DRIVER("failed to upclock LVDS!\n"); -- cgit v0.10.2 From 759010728b1323aec03c5baae13fde8f76e44a99 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 3 Jan 2011 16:39:21 +0000 Subject: drm/i915: Remove impossible test As has_gem is unconditionally set to true, the conditional immediately following that assignment is superfluous. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0568dbd..844f3c9 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1962,13 +1962,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) /* enable GEM by default */ dev_priv->has_gem = 1; - if (dev_priv->has_gem == 0 && - drm_core_check_feature(dev, DRIVER_MODESET)) { - DRM_ERROR("kernel modesetting requires GEM, disabling driver.\n"); - ret = -ENODEV; - goto out_workqueue_free; - } - dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev)) { @@ -2055,7 +2048,6 @@ out_gem_unload: intel_teardown_gmbus(dev); intel_teardown_mchbar(dev); -out_workqueue_free: destroy_workqueue(dev_priv->wq); out_iomapfree: io_mapping_free(dev_priv->mm.gtt_mapping); -- cgit v0.10.2 From 63256ec5347fb2344a42adbae732b90603c92f35 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 4 Jan 2011 18:42:07 +0000 Subject: drm/i915: Enforce write ordering through the GTT We need to ensure that writes through the GTT land before any modification to the MMIO registers and so must impose a mandatory write barrier when flushing the GTT domain. This was revealed by relaxing the write ordering by experimentally mapping the registers and the GATT as write-combining. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c79c0b6..f9c093c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2393,6 +2393,12 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj, obj->last_fenced_ring = NULL; } + /* Ensure that all CPU reads are completed before installing a fence + * and all writes before removing the fence. + */ + if (obj->base.read_domains & I915_GEM_DOMAIN_GTT) + mb(); + return 0; } @@ -2833,10 +2839,16 @@ i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj) if (obj->base.write_domain != I915_GEM_DOMAIN_GTT) return; - /* No actual flushing is required for the GTT write domain. Writes + /* No actual flushing is required for the GTT write domain. Writes * to it immediately go to main memory as far as we know, so there's * no chipset flush. It also doesn't land in render cache. + * + * However, we do have to enforce the order so that all writes through + * the GTT land before any writes to the device, such as updates to + * the GATT itself. */ + wmb(); + i915_gem_release_mmap(obj); old_write_domain = obj->base.write_domain; diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 61129e6..0d42de4 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -725,6 +725,9 @@ i915_gem_execbuffer_flush(struct drm_device *dev, if (flush_domains & I915_GEM_DOMAIN_CPU) intel_gtt_chipset_flush(); + if (flush_domains & I915_GEM_DOMAIN_GTT) + wmb(); + if ((flush_domains | invalidate_domains) & I915_GEM_GPU_DOMAINS) { for (i = 0; i < I915_NUM_RINGS; i++) if (flush_rings & (1 << i)) -- cgit v0.10.2 From b72f3acb71646de073abdc070fe1108866c96634 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 4 Jan 2011 17:34:02 +0000 Subject: drm/i915: Handle ringbuffer stalls when flushing Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f9c093c..07b6244 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2148,8 +2148,8 @@ i915_gem_flush_ring(struct drm_device *dev, uint32_t invalidate_domains, uint32_t flush_domains) { - ring->flush(ring, invalidate_domains, flush_domains); - i915_gem_process_flushing_list(dev, flush_domains, ring); + if (ring->flush(ring, invalidate_domains, flush_domains) == 0) + i915_gem_process_flushing_list(dev, flush_domains, ring); } static int i915_ring_idle(struct drm_device *dev, diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 0d42de4..1b78b66 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -924,7 +924,7 @@ i915_gem_execbuffer_retire_commands(struct drm_device *dev, struct intel_ring_buffer *ring) { struct drm_i915_gem_request *request; - u32 flush_domains; + u32 invalidate; /* * Ensure that the commands in the batch buffer are @@ -932,11 +932,13 @@ i915_gem_execbuffer_retire_commands(struct drm_device *dev, * * The sampler always gets flushed on i965 (sigh). */ - flush_domains = 0; + invalidate = I915_GEM_DOMAIN_COMMAND; if (INTEL_INFO(dev)->gen >= 4) - flush_domains |= I915_GEM_DOMAIN_SAMPLER; - - ring->flush(ring, I915_GEM_DOMAIN_COMMAND, flush_domains); + invalidate |= I915_GEM_DOMAIN_SAMPLER; + if (ring->flush(ring, invalidate, 0)) { + i915_gem_next_request_seqno(dev, ring); + return; + } /* Add a breadcrumb for the completion of the batch buffer */ request = kzalloc(sizeof(*request), GFP_KERNEL); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 2de0e45..aa8f6ab 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -48,7 +48,7 @@ static u32 i915_gem_get_seqno(struct drm_device *dev) return seqno; } -static void +static int render_ring_flush(struct intel_ring_buffer *ring, u32 invalidate_domains, u32 flush_domains) @@ -56,6 +56,7 @@ render_ring_flush(struct intel_ring_buffer *ring, struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; u32 cmd; + int ret; #if WATCH_EXEC DRM_INFO("%s: invalidate %08x flush %08x\n", __func__, @@ -116,12 +117,16 @@ render_ring_flush(struct intel_ring_buffer *ring, #if WATCH_EXEC DRM_INFO("%s: queue flush %08x to ring\n", __func__, cmd); #endif - if (intel_ring_begin(ring, 2) == 0) { - intel_ring_emit(ring, cmd); - intel_ring_emit(ring, MI_NOOP); - intel_ring_advance(ring); - } + ret = intel_ring_begin(ring, 2); + if (ret) + return ret; + + intel_ring_emit(ring, cmd); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); } + + return 0; } static void ring_write_tail(struct intel_ring_buffer *ring, @@ -534,19 +539,24 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring) POSTING_READ(mmio); } -static void +static int bsd_ring_flush(struct intel_ring_buffer *ring, u32 invalidate_domains, u32 flush_domains) { + int ret; + if ((flush_domains & I915_GEM_DOMAIN_RENDER) == 0) - return; + return 0; - if (intel_ring_begin(ring, 2) == 0) { - intel_ring_emit(ring, MI_FLUSH); - intel_ring_emit(ring, MI_NOOP); - intel_ring_advance(ring); - } + ret = intel_ring_begin(ring, 2); + if (ret) + return ret; + + intel_ring_emit(ring, MI_FLUSH); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); + return 0; } static int @@ -980,20 +990,25 @@ static void gen6_bsd_ring_write_tail(struct intel_ring_buffer *ring, GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_ENABLE); } -static void gen6_ring_flush(struct intel_ring_buffer *ring, - u32 invalidate_domains, - u32 flush_domains) +static int gen6_ring_flush(struct intel_ring_buffer *ring, + u32 invalidate_domains, + u32 flush_domains) { + int ret; + if ((flush_domains & I915_GEM_DOMAIN_RENDER) == 0) - return; + return 0; - if (intel_ring_begin(ring, 4) == 0) { - intel_ring_emit(ring, MI_FLUSH_DW); - intel_ring_emit(ring, 0); - intel_ring_emit(ring, 0); - intel_ring_emit(ring, 0); - intel_ring_advance(ring); - } + ret = intel_ring_begin(ring, 4); + if (ret) + return ret; + + intel_ring_emit(ring, MI_FLUSH_DW); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_advance(ring); + return 0; } static int @@ -1122,20 +1137,25 @@ static int blt_ring_begin(struct intel_ring_buffer *ring, return intel_ring_begin(ring, 4); } -static void blt_ring_flush(struct intel_ring_buffer *ring, +static int blt_ring_flush(struct intel_ring_buffer *ring, u32 invalidate_domains, u32 flush_domains) { + int ret; + if ((flush_domains & I915_GEM_DOMAIN_RENDER) == 0) - return; + return 0; - if (blt_ring_begin(ring, 4) == 0) { - intel_ring_emit(ring, MI_FLUSH_DW); - intel_ring_emit(ring, 0); - intel_ring_emit(ring, 0); - intel_ring_emit(ring, 0); - intel_ring_advance(ring); - } + ret = blt_ring_begin(ring, 4); + if (ret) + return ret; + + intel_ring_emit(ring, MI_FLUSH_DW); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_advance(ring); + return 0; } static void blt_ring_cleanup(struct intel_ring_buffer *ring) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index bbbf505..5969c2e 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -63,9 +63,9 @@ struct intel_ring_buffer { void (*write_tail)(struct intel_ring_buffer *ring, u32 value); - void (*flush)(struct intel_ring_buffer *ring, - u32 invalidate_domains, - u32 flush_domains); + int __must_check (*flush)(struct intel_ring_buffer *ring, + u32 invalidate_domains, + u32 flush_domains); int (*add_request)(struct intel_ring_buffer *ring, u32 *seqno); u32 (*get_seqno)(struct intel_ring_buffer *ring); -- cgit v0.10.2 From 0f46832fab779a9a3314ce5e833155fe4cf18f6c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 4 Jan 2011 17:35:21 +0000 Subject: drm/i915: Mask USER interrupts on gen6 (until required) Otherwise we may consume 20% of the CPU just handling IRQs whilst rendering. Ouch. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 972e08e..1f77d8c 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1007,12 +1007,6 @@ extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc); extern u32 gm45_get_vblank_counter(struct drm_device *dev, int crtc); extern int i915_vblank_swap(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask); -extern void i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask); -extern void ironlake_enable_graphics_irq(drm_i915_private_t *dev_priv, - u32 mask); -extern void ironlake_disable_graphics_irq(drm_i915_private_t *dev_priv, - u32 mask); void i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0dadc02..826873a2 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -64,26 +64,6 @@ #define DRM_I915_VBLANK_PIPE_ALL (DRM_I915_VBLANK_PIPE_A | \ DRM_I915_VBLANK_PIPE_B) -void -ironlake_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask) -{ - if ((dev_priv->gt_irq_mask & mask) != 0) { - dev_priv->gt_irq_mask &= ~mask; - I915_WRITE(GTIMR, dev_priv->gt_irq_mask); - POSTING_READ(GTIMR); - } -} - -void -ironlake_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask) -{ - if ((dev_priv->gt_irq_mask & mask) != mask) { - dev_priv->gt_irq_mask |= mask; - I915_WRITE(GTIMR, dev_priv->gt_irq_mask); - POSTING_READ(GTIMR); - } -} - /* For display hotplug interrupt */ static void ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask) @@ -105,26 +85,6 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask) } } -void -i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask) -{ - if ((dev_priv->irq_mask & mask) != 0) { - dev_priv->irq_mask &= ~mask; - I915_WRITE(IMR, dev_priv->irq_mask); - POSTING_READ(IMR); - } -} - -void -i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask) -{ - if ((dev_priv->irq_mask & mask) != mask) { - dev_priv->irq_mask |= mask; - I915_WRITE(IMR, dev_priv->irq_mask); - POSTING_READ(IMR); - } -} - static inline u32 i915_pipestat(int pipe) { @@ -1673,11 +1633,6 @@ static int ironlake_irq_postinstall(struct drm_device *dev) I915_WRITE(GTIIR, I915_READ(GTIIR)); I915_WRITE(GTIMR, dev_priv->gt_irq_mask); - if (IS_GEN6(dev)) { - I915_WRITE(GEN6_RENDER_IMR, ~GEN6_RENDER_USER_INTERRUPT); - I915_WRITE(GEN6_BSD_IMR, ~GEN6_BSD_USER_INTERRUPT); - I915_WRITE(GEN6_BLITTER_IMR, ~GEN6_BLITTER_USER_INTERRUPT); - } if (IS_GEN6(dev)) render_irqs = diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index ecfb002..b0ab424 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -288,6 +288,7 @@ #define RING_HWS_PGA_GEN6(base) ((base)+0x2080) #define RING_ACTHD(base) ((base)+0x74) #define RING_NOPID(base) ((base)+0x94) +#define RING_IMR(base) ((base)+0xa8) #define TAIL_ADDR 0x001FFFF8 #define HEAD_WRAP_COUNT 0xFFE00000 #define HEAD_WRAP_ONE 0x00200000 diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index aa8f6ab..3bff7fb 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -485,6 +485,38 @@ pc_render_get_seqno(struct intel_ring_buffer *ring) return pc->cpu_page[0]; } +static void +ironlake_enable_irq(drm_i915_private_t *dev_priv, u32 mask) +{ + dev_priv->gt_irq_mask &= ~mask; + I915_WRITE(GTIMR, dev_priv->gt_irq_mask); + POSTING_READ(GTIMR); +} + +static void +ironlake_disable_irq(drm_i915_private_t *dev_priv, u32 mask) +{ + dev_priv->gt_irq_mask |= mask; + I915_WRITE(GTIMR, dev_priv->gt_irq_mask); + POSTING_READ(GTIMR); +} + +static void +i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask) +{ + dev_priv->irq_mask &= ~mask; + I915_WRITE(IMR, dev_priv->irq_mask); + POSTING_READ(IMR); +} + +static void +i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask) +{ + dev_priv->irq_mask |= mask; + I915_WRITE(IMR, dev_priv->irq_mask); + POSTING_READ(IMR); +} + static bool render_ring_get_irq(struct intel_ring_buffer *ring) { @@ -499,8 +531,8 @@ render_ring_get_irq(struct intel_ring_buffer *ring) spin_lock_irqsave(&dev_priv->irq_lock, irqflags); if (HAS_PCH_SPLIT(dev)) - ironlake_enable_graphics_irq(dev_priv, - GT_PIPE_NOTIFY | GT_USER_INTERRUPT); + ironlake_enable_irq(dev_priv, + GT_PIPE_NOTIFY | GT_USER_INTERRUPT); else i915_enable_irq(dev_priv, I915_USER_INTERRUPT); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); @@ -520,9 +552,9 @@ render_ring_put_irq(struct intel_ring_buffer *ring) spin_lock_irqsave(&dev_priv->irq_lock, irqflags); if (HAS_PCH_SPLIT(dev)) - ironlake_disable_graphics_irq(dev_priv, - GT_USER_INTERRUPT | - GT_PIPE_NOTIFY); + ironlake_disable_irq(dev_priv, + GT_USER_INTERRUPT | + GT_PIPE_NOTIFY); else i915_disable_irq(dev_priv, I915_USER_INTERRUPT); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); @@ -596,7 +628,7 @@ ring_get_irq(struct intel_ring_buffer *ring, u32 flag) unsigned long irqflags; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - ironlake_enable_graphics_irq(dev_priv, flag); + ironlake_enable_irq(dev_priv, flag); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } @@ -613,7 +645,46 @@ ring_put_irq(struct intel_ring_buffer *ring, u32 flag) unsigned long irqflags; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - ironlake_disable_graphics_irq(dev_priv, flag); + ironlake_disable_irq(dev_priv, flag); + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + } +} + +static bool +gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) +{ + struct drm_device *dev = ring->dev; + + if (!dev->irq_enabled) + return false; + + if (atomic_inc_return(&ring->irq_refcount) == 1) { + drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + ring->irq_mask &= ~rflag; + I915_WRITE_IMR(ring, ring->irq_mask); + ironlake_enable_irq(dev_priv, gflag); + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + } + + return true; +} + +static void +gen6_ring_put_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) +{ + struct drm_device *dev = ring->dev; + + if (atomic_dec_and_test(&ring->irq_refcount)) { + drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + ring->irq_mask |= rflag; + I915_WRITE_IMR(ring, ring->irq_mask); + ironlake_disable_irq(dev_priv, gflag); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } } @@ -757,6 +828,7 @@ int intel_init_ring_buffer(struct drm_device *dev, INIT_LIST_HEAD(&ring->active_list); INIT_LIST_HEAD(&ring->request_list); INIT_LIST_HEAD(&ring->gpu_write_list); + ring->irq_mask = ~0; if (I915_NEED_GFX_HWS(dev)) { ret = init_status_page(ring); @@ -1030,15 +1102,35 @@ gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring, } static bool +gen6_render_ring_get_irq(struct intel_ring_buffer *ring) +{ + return gen6_ring_get_irq(ring, + GT_USER_INTERRUPT, + GEN6_RENDER_USER_INTERRUPT); +} + +static void +gen6_render_ring_put_irq(struct intel_ring_buffer *ring) +{ + return gen6_ring_put_irq(ring, + GT_USER_INTERRUPT, + GEN6_RENDER_USER_INTERRUPT); +} + +static bool gen6_bsd_ring_get_irq(struct intel_ring_buffer *ring) { - return ring_get_irq(ring, GT_GEN6_BSD_USER_INTERRUPT); + return gen6_ring_get_irq(ring, + GT_GEN6_BSD_USER_INTERRUPT, + GEN6_BSD_USER_INTERRUPT); } static void gen6_bsd_ring_put_irq(struct intel_ring_buffer *ring) { - ring_put_irq(ring, GT_GEN6_BSD_USER_INTERRUPT); + return gen6_ring_put_irq(ring, + GT_GEN6_BSD_USER_INTERRUPT, + GEN6_BSD_USER_INTERRUPT); } /* ring buffer for Video Codec for Gen6+ */ @@ -1062,13 +1154,17 @@ static const struct intel_ring_buffer gen6_bsd_ring = { static bool blt_ring_get_irq(struct intel_ring_buffer *ring) { - return ring_get_irq(ring, GT_BLT_USER_INTERRUPT); + return gen6_ring_get_irq(ring, + GT_BLT_USER_INTERRUPT, + GEN6_BLITTER_USER_INTERRUPT); } static void blt_ring_put_irq(struct intel_ring_buffer *ring) { - ring_put_irq(ring, GT_BLT_USER_INTERRUPT); + gen6_ring_put_irq(ring, + GT_BLT_USER_INTERRUPT, + GEN6_BLITTER_USER_INTERRUPT); } @@ -1192,6 +1288,8 @@ int intel_init_render_ring_buffer(struct drm_device *dev) *ring = render_ring; if (INTEL_INFO(dev)->gen >= 6) { ring->add_request = gen6_add_request; + ring->irq_get = gen6_render_ring_get_irq; + ring->irq_put = gen6_render_ring_put_irq; } else if (IS_GEN5(dev)) { ring->add_request = pc_render_add_request; ring->get_seqno = pc_render_get_seqno; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 5969c2e..634f6f8 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -28,6 +28,8 @@ struct intel_hw_status_page { #define I915_READ_CTL(ring) I915_RING_READ(RING_CTL(ring->mmio_base)) #define I915_WRITE_CTL(ring, val) I915_WRITE(RING_CTL(ring->mmio_base), val) +#define I915_WRITE_IMR(ring, val) I915_WRITE(RING_IMR(ring->mmio_base), val) + #define I915_READ_NOPID(ring) I915_RING_READ(RING_NOPID(ring->mmio_base)) #define I915_READ_SYNC_0(ring) I915_RING_READ(RING_SYNC_0(ring->mmio_base)) #define I915_READ_SYNC_1(ring) I915_RING_READ(RING_SYNC_1(ring->mmio_base)) @@ -52,6 +54,7 @@ struct intel_ring_buffer { int effective_size; struct intel_hw_status_page status_page; + u32 irq_mask; u32 irq_seqno; /* last seq seem at irq time */ u32 waiting_seqno; u32 sync_seqno[I915_NUM_RINGS-1]; -- cgit v0.10.2 From 9862e600cef87de0e301bad7d1435b87e03ea84d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 4 Jan 2011 22:22:17 +0000 Subject: drm/i915/debugfs: Show the per-ring IMR Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 92f7578..7243d64 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -456,8 +456,14 @@ static int i915_interrupt_info(struct seq_file *m, void *data) } seq_printf(m, "Interrupts received: %d\n", atomic_read(&dev_priv->irq_received)); - for (i = 0; i < I915_NUM_RINGS; i++) + for (i = 0; i < I915_NUM_RINGS; i++) { + if (IS_GEN6(dev)) { + seq_printf(m, "Graphics Interrupt mask (%s): %08x\n", + dev_priv->ring[i].name, + I915_READ_IMR(&dev_priv->ring[i])); + } i915_ring_seqno_info(m, &dev_priv->ring[i]); + } mutex_unlock(&dev->struct_mutex); return 0; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 826873a2..c9adcdd 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -349,9 +349,12 @@ static void notify_ring(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; u32 seqno = ring->get_seqno(ring); - ring->irq_seqno = seqno; + trace_i915_gem_request_complete(dev, seqno); + + ring->irq_seqno = seqno; wake_up_all(&ring->irq_queue); + dev_priv->hangcheck_count = 0; mod_timer(&dev_priv->hangcheck_timer, jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 634f6f8..9b134b8 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -16,23 +16,24 @@ struct intel_hw_status_page { #define I915_RING_READ(reg) i915_safe_read(dev_priv, reg) -#define I915_READ_TAIL(ring) I915_RING_READ(RING_TAIL(ring->mmio_base)) -#define I915_WRITE_TAIL(ring, val) I915_WRITE(RING_TAIL(ring->mmio_base), val) +#define I915_READ_TAIL(ring) I915_RING_READ(RING_TAIL((ring)->mmio_base)) +#define I915_WRITE_TAIL(ring, val) I915_WRITE(RING_TAIL((ring)->mmio_base), val) -#define I915_READ_START(ring) I915_RING_READ(RING_START(ring->mmio_base)) -#define I915_WRITE_START(ring, val) I915_WRITE(RING_START(ring->mmio_base), val) +#define I915_READ_START(ring) I915_RING_READ(RING_START((ring)->mmio_base)) +#define I915_WRITE_START(ring, val) I915_WRITE(RING_START((ring)->mmio_base), val) -#define I915_READ_HEAD(ring) I915_RING_READ(RING_HEAD(ring->mmio_base)) -#define I915_WRITE_HEAD(ring, val) I915_WRITE(RING_HEAD(ring->mmio_base), val) +#define I915_READ_HEAD(ring) I915_RING_READ(RING_HEAD((ring)->mmio_base)) +#define I915_WRITE_HEAD(ring, val) I915_WRITE(RING_HEAD((ring)->mmio_base), val) -#define I915_READ_CTL(ring) I915_RING_READ(RING_CTL(ring->mmio_base)) -#define I915_WRITE_CTL(ring, val) I915_WRITE(RING_CTL(ring->mmio_base), val) +#define I915_READ_CTL(ring) I915_RING_READ(RING_CTL((ring)->mmio_base)) +#define I915_WRITE_CTL(ring, val) I915_WRITE(RING_CTL((ring)->mmio_base), val) -#define I915_WRITE_IMR(ring, val) I915_WRITE(RING_IMR(ring->mmio_base), val) +#define I915_WRITE_IMR(ring, val) I915_WRITE(RING_IMR((ring)->mmio_base), val) +#define I915_READ_IMR(ring) I915_RING_READ(RING_IMR((ring)->mmio_base)) -#define I915_READ_NOPID(ring) I915_RING_READ(RING_NOPID(ring->mmio_base)) -#define I915_READ_SYNC_0(ring) I915_RING_READ(RING_SYNC_0(ring->mmio_base)) -#define I915_READ_SYNC_1(ring) I915_RING_READ(RING_SYNC_1(ring->mmio_base)) +#define I915_READ_NOPID(ring) I915_RING_READ(RING_NOPID((ring)->mmio_base)) +#define I915_READ_SYNC_0(ring) I915_RING_READ(RING_SYNC_0((ring)->mmio_base)) +#define I915_READ_SYNC_1(ring) I915_RING_READ(RING_SYNC_1((ring)->mmio_base)) struct intel_ring_buffer { const char *name; -- cgit v0.10.2 From 01a03331e5fe91861937f8b8e72c259f5e9eae67 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 4 Jan 2011 22:22:56 +0000 Subject: drm/i915/ringbuffer: Simplify the ring irq refcounting ... and move it under the spinlock to gain the appropriate memory barriers. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=32752 Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 3bff7fb..13cad98 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -521,22 +521,20 @@ static bool render_ring_get_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; if (!dev->irq_enabled) return false; - if (atomic_inc_return(&ring->irq_refcount) == 1) { - drm_i915_private_t *dev_priv = dev->dev_private; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + spin_lock(&dev_priv->irq_lock); + if (ring->irq_refcount++ == 0) { if (HAS_PCH_SPLIT(dev)) ironlake_enable_irq(dev_priv, GT_PIPE_NOTIFY | GT_USER_INTERRUPT); else i915_enable_irq(dev_priv, I915_USER_INTERRUPT); - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } + spin_unlock(&dev_priv->irq_lock); return true; } @@ -545,20 +543,18 @@ static void render_ring_put_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; - if (atomic_dec_and_test(&ring->irq_refcount)) { - drm_i915_private_t *dev_priv = dev->dev_private; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + spin_lock(&dev_priv->irq_lock); + if (--ring->irq_refcount == 0) { if (HAS_PCH_SPLIT(dev)) ironlake_disable_irq(dev_priv, GT_USER_INTERRUPT | GT_PIPE_NOTIFY); else i915_disable_irq(dev_priv, I915_USER_INTERRUPT); - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } + spin_unlock(&dev_priv->irq_lock); } void intel_ring_setup_status_page(struct intel_ring_buffer *ring) @@ -619,18 +615,15 @@ static bool ring_get_irq(struct intel_ring_buffer *ring, u32 flag) { struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; if (!dev->irq_enabled) return false; - if (atomic_inc_return(&ring->irq_refcount) == 1) { - drm_i915_private_t *dev_priv = dev->dev_private; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + spin_lock(&dev_priv->irq_lock); + if (ring->irq_refcount++ == 0) ironlake_enable_irq(dev_priv, flag); - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - } + spin_unlock(&dev_priv->irq_lock); return true; } @@ -639,35 +632,30 @@ static void ring_put_irq(struct intel_ring_buffer *ring, u32 flag) { struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; - if (atomic_dec_and_test(&ring->irq_refcount)) { - drm_i915_private_t *dev_priv = dev->dev_private; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + spin_lock(&dev_priv->irq_lock); + if (--ring->irq_refcount == 0) ironlake_disable_irq(dev_priv, flag); - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - } + spin_unlock(&dev_priv->irq_lock); } static bool gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) { struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; if (!dev->irq_enabled) return false; - if (atomic_inc_return(&ring->irq_refcount) == 1) { - drm_i915_private_t *dev_priv = dev->dev_private; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + spin_lock(&dev_priv->irq_lock); + if (ring->irq_refcount++ == 0) { ring->irq_mask &= ~rflag; I915_WRITE_IMR(ring, ring->irq_mask); ironlake_enable_irq(dev_priv, gflag); - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } + spin_unlock(&dev_priv->irq_lock); return true; } @@ -676,17 +664,15 @@ static void gen6_ring_put_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) { struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; - if (atomic_dec_and_test(&ring->irq_refcount)) { - drm_i915_private_t *dev_priv = dev->dev_private; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + spin_lock(&dev_priv->irq_lock); + if (--ring->irq_refcount == 0) { ring->irq_mask |= rflag; I915_WRITE_IMR(ring, ring->irq_mask); ironlake_disable_irq(dev_priv, gflag); - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } + spin_unlock(&dev_priv->irq_lock); } static bool diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 9b134b8..6b1d9a5 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -55,11 +55,11 @@ struct intel_ring_buffer { int effective_size; struct intel_hw_status_page status_page; + u32 irq_refcount; u32 irq_mask; u32 irq_seqno; /* last seq seem at irq time */ u32 waiting_seqno; u32 sync_seqno[I915_NUM_RINGS-1]; - atomic_t irq_refcount; bool __must_check (*irq_get)(struct intel_ring_buffer *ring); void (*irq_put)(struct intel_ring_buffer *ring); -- cgit v0.10.2 From 0dc79fb2a36efcadbb39bd8b28933d8aa40408b1 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 5 Jan 2011 10:32:24 +0000 Subject: drm/i915: Make the ring IMR handling private As the IMR for the USER interrupts are not modified elsewhere, we can separate the spinlock used for these from that of hpd and pipestats. Those two IMR are manipulated under an IRQ and so need heavier locking. Reported-and-tested-by: Alexey Fisher Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 13cad98..03e3370 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -526,7 +526,7 @@ render_ring_get_irq(struct intel_ring_buffer *ring) if (!dev->irq_enabled) return false; - spin_lock(&dev_priv->irq_lock); + spin_lock(&ring->irq_lock); if (ring->irq_refcount++ == 0) { if (HAS_PCH_SPLIT(dev)) ironlake_enable_irq(dev_priv, @@ -534,7 +534,7 @@ render_ring_get_irq(struct intel_ring_buffer *ring) else i915_enable_irq(dev_priv, I915_USER_INTERRUPT); } - spin_unlock(&dev_priv->irq_lock); + spin_unlock(&ring->irq_lock); return true; } @@ -545,7 +545,7 @@ render_ring_put_irq(struct intel_ring_buffer *ring) struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; - spin_lock(&dev_priv->irq_lock); + spin_lock(&ring->irq_lock); if (--ring->irq_refcount == 0) { if (HAS_PCH_SPLIT(dev)) ironlake_disable_irq(dev_priv, @@ -554,7 +554,7 @@ render_ring_put_irq(struct intel_ring_buffer *ring) else i915_disable_irq(dev_priv, I915_USER_INTERRUPT); } - spin_unlock(&dev_priv->irq_lock); + spin_unlock(&ring->irq_lock); } void intel_ring_setup_status_page(struct intel_ring_buffer *ring) @@ -620,10 +620,10 @@ ring_get_irq(struct intel_ring_buffer *ring, u32 flag) if (!dev->irq_enabled) return false; - spin_lock(&dev_priv->irq_lock); + spin_lock(&ring->irq_lock); if (ring->irq_refcount++ == 0) ironlake_enable_irq(dev_priv, flag); - spin_unlock(&dev_priv->irq_lock); + spin_unlock(&ring->irq_lock); return true; } @@ -634,10 +634,10 @@ ring_put_irq(struct intel_ring_buffer *ring, u32 flag) struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; - spin_lock(&dev_priv->irq_lock); + spin_lock(&ring->irq_lock); if (--ring->irq_refcount == 0) ironlake_disable_irq(dev_priv, flag); - spin_unlock(&dev_priv->irq_lock); + spin_unlock(&ring->irq_lock); } static bool @@ -649,13 +649,13 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) if (!dev->irq_enabled) return false; - spin_lock(&dev_priv->irq_lock); + spin_lock(&ring->irq_lock); if (ring->irq_refcount++ == 0) { ring->irq_mask &= ~rflag; I915_WRITE_IMR(ring, ring->irq_mask); ironlake_enable_irq(dev_priv, gflag); } - spin_unlock(&dev_priv->irq_lock); + spin_unlock(&ring->irq_lock); return true; } @@ -666,13 +666,13 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; - spin_lock(&dev_priv->irq_lock); + spin_lock(&ring->irq_lock); if (--ring->irq_refcount == 0) { ring->irq_mask |= rflag; I915_WRITE_IMR(ring, ring->irq_mask); ironlake_disable_irq(dev_priv, gflag); } - spin_unlock(&dev_priv->irq_lock); + spin_unlock(&ring->irq_lock); } static bool @@ -814,6 +814,8 @@ int intel_init_ring_buffer(struct drm_device *dev, INIT_LIST_HEAD(&ring->active_list); INIT_LIST_HEAD(&ring->request_list); INIT_LIST_HEAD(&ring->gpu_write_list); + + spin_lock_init(&ring->irq_lock); ring->irq_mask = ~0; if (I915_NEED_GFX_HWS(dev)) { diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 6b1d9a5..be9087e 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -55,6 +55,7 @@ struct intel_ring_buffer { int effective_size; struct intel_hw_status_page status_page; + spinlock_t irq_lock; u32 irq_refcount; u32 irq_mask; u32 irq_seqno; /* last seq seem at irq time */ -- cgit v0.10.2 From 88271da3f3da75d6eaef5e768c82a1627edf7088 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 5 Jan 2011 12:01:24 -0800 Subject: drm/i915: re-enable rc6 support for Ironlake+ Re-enable rc6 support on Ironlake for power savings. Adds a debugfs file to check current RC state, adds a missing workaround for Ironlake MI_SET_CONTEXT instructions, and renames MCHBAR_RENDER_STANDBY to RSTDBYCTL to match the docs. Keep RC6 and the power context disabled on pre-ILK. It only seems to hang and doesn't save any power. Signed-off-by: Jesse Barnes Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 7243d64..9c4cdc1 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -896,7 +896,7 @@ static int i915_drpc_info(struct seq_file *m, void *unused) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; u32 rgvmodectl = I915_READ(MEMMODECTL); - u32 rstdbyctl = I915_READ(MCHBAR_RENDER_STANDBY); + u32 rstdbyctl = I915_READ(RSTDBYCTL); u16 crstandvid = I915_READ16(CRSTANDVID); seq_printf(m, "HD boost: %s\n", (rgvmodectl & MEMMODE_BOOST_EN) ? @@ -919,6 +919,30 @@ static int i915_drpc_info(struct seq_file *m, void *unused) seq_printf(m, "RS2 VID: %d\n", ((crstandvid >> 8) & 0x3f)); seq_printf(m, "Render standby enabled: %s\n", (rstdbyctl & RCX_SW_EXIT) ? "no" : "yes"); + seq_printf(m, "Current RS state: "); + switch (rstdbyctl & RSX_STATUS_MASK) { + case RSX_STATUS_ON: + seq_printf(m, "on\n"); + break; + case RSX_STATUS_RC1: + seq_printf(m, "RC1\n"); + break; + case RSX_STATUS_RC1E: + seq_printf(m, "RC1E\n"); + break; + case RSX_STATUS_RS1: + seq_printf(m, "RS1\n"); + break; + case RSX_STATUS_RS2: + seq_printf(m, "RS2 (RC6)\n"); + break; + case RSX_STATUS_RS3: + seq_printf(m, "RC3 (RC6+)\n"); + break; + default: + seq_printf(m, "unknown\n"); + break; + } return 0; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index b0ab424..33ddf31 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -145,6 +145,8 @@ #define MI_END_SCENE (1 << 4) /* flush binner and incr scene count */ #define MI_INVALIDATE_ISP (1 << 5) /* invalidate indirect state pointers */ #define MI_BATCH_BUFFER_END MI_INSTR(0x0a, 0) +#define MI_SUSPEND_FLUSH MI_INSTR(0x0b, 0) +#define MI_SUSPEND_FLUSH_EN (1<<0) #define MI_REPORT_HEAD MI_INSTR(0x07, 0) #define MI_OVERLAY_FLIP MI_INSTR(0x11,0) #define MI_OVERLAY_CONTINUE (0x0<<21) @@ -159,6 +161,7 @@ #define MI_MM_SPACE_PHYSICAL (0<<8) #define MI_SAVE_EXT_STATE_EN (1<<3) #define MI_RESTORE_EXT_STATE_EN (1<<2) +#define MI_FORCE_RESTORE (1<<1) #define MI_RESTORE_INHIBIT (1<<0) #define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1) #define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */ @@ -1131,9 +1134,50 @@ #define RCBMINAVG 0x111a0 #define RCUPEI 0x111b0 #define RCDNEI 0x111b4 -#define MCHBAR_RENDER_STANDBY 0x111b8 -#define RCX_SW_EXIT (1<<23) -#define RSX_STATUS_MASK 0x00700000 +#define RSTDBYCTL 0x111b8 +#define RS1EN (1<<31) +#define RS2EN (1<<30) +#define RS3EN (1<<29) +#define D3RS3EN (1<<28) /* Display D3 imlies RS3 */ +#define SWPROMORSX (1<<27) /* RSx promotion timers ignored */ +#define RCWAKERW (1<<26) /* Resetwarn from PCH causes wakeup */ +#define DPRSLPVREN (1<<25) /* Fast voltage ramp enable */ +#define GFXTGHYST (1<<24) /* Hysteresis to allow trunk gating */ +#define RCX_SW_EXIT (1<<23) /* Leave RSx and prevent re-entry */ +#define RSX_STATUS_MASK (7<<20) +#define RSX_STATUS_ON (0<<20) +#define RSX_STATUS_RC1 (1<<20) +#define RSX_STATUS_RC1E (2<<20) +#define RSX_STATUS_RS1 (3<<20) +#define RSX_STATUS_RS2 (4<<20) /* aka rc6 */ +#define RSX_STATUS_RSVD (5<<20) /* deep rc6 unsupported on ilk */ +#define RSX_STATUS_RS3 (6<<20) /* rs3 unsupported on ilk */ +#define RSX_STATUS_RSVD2 (7<<20) +#define UWRCRSXE (1<<19) /* wake counter limit prevents rsx */ +#define RSCRP (1<<18) /* rs requests control on rs1/2 reqs */ +#define JRSC (1<<17) /* rsx coupled to cpu c-state */ +#define RS2INC0 (1<<16) /* allow rs2 in cpu c0 */ +#define RS1CONTSAV_MASK (3<<14) +#define RS1CONTSAV_NO_RS1 (0<<14) /* rs1 doesn't save/restore context */ +#define RS1CONTSAV_RSVD (1<<14) +#define RS1CONTSAV_SAVE_RS1 (2<<14) /* rs1 saves context */ +#define RS1CONTSAV_FULL_RS1 (3<<14) /* rs1 saves and restores context */ +#define NORMSLEXLAT_MASK (3<<12) +#define SLOW_RS123 (0<<12) +#define SLOW_RS23 (1<<12) +#define SLOW_RS3 (2<<12) +#define NORMAL_RS123 (3<<12) +#define RCMODE_TIMEOUT (1<<11) /* 0 is eval interval method */ +#define IMPROMOEN (1<<10) /* promo is immediate or delayed until next idle interval (only for timeout method above) */ +#define RCENTSYNC (1<<9) /* rs coupled to cpu c-state (3/6/7) */ +#define STATELOCK (1<<7) /* locked to rs_cstate if 0 */ +#define RS_CSTATE_MASK (3<<4) +#define RS_CSTATE_C367_RS1 (0<<4) +#define RS_CSTATE_C36_RS1_C7_RS2 (1<<4) +#define RS_CSTATE_RSVD (2<<4) +#define RS_CSTATE_C367_RS2 (3<<4) +#define REDSAVES (1<<3) /* no context save if was idle during rs0 */ +#define REDRESTORES (1<<2) /* no restore if was idle during rs0 */ #define VIDCTL 0x111c0 #define VIDSTS 0x111c8 #define VIDSTART 0x111cc /* 8 bits */ diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 4107724..af53063 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -740,7 +740,7 @@ void i915_restore_display(struct drm_device *dev) I915_WRITE(PCH_PP_OFF_DELAYS, dev_priv->savePP_OFF_DELAYS); I915_WRITE(PCH_PP_DIVISOR, dev_priv->savePP_DIVISOR); I915_WRITE(PCH_PP_CONTROL, dev_priv->savePP_CONTROL); - I915_WRITE(MCHBAR_RENDER_STANDBY, + I915_WRITE(RSTDBYCTL, dev_priv->saveMCHBAR_RENDER_STANDBY); } else { I915_WRITE(PFIT_PGM_RATIOS, dev_priv->savePFIT_PGM_RATIOS); @@ -811,7 +811,7 @@ int i915_save_state(struct drm_device *dev) dev_priv->saveFDI_RXA_IMR = I915_READ(FDI_RXA_IMR); dev_priv->saveFDI_RXB_IMR = I915_READ(FDI_RXB_IMR); dev_priv->saveMCHBAR_RENDER_STANDBY = - I915_READ(MCHBAR_RENDER_STANDBY); + I915_READ(RSTDBYCTL); } else { dev_priv->saveIER = I915_READ(IER); dev_priv->saveIMR = I915_READ(IMR); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 043825c..f3c0525 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6420,35 +6420,37 @@ void intel_enable_clock_gating(struct drm_device *dev) * GPU can automatically power down the render unit if given a page * to save state. */ - if (IS_IRONLAKE_M(dev) && 0) { /* XXX causes a failure during suspend */ + if (IS_IRONLAKE_M(dev)) { if (dev_priv->renderctx == NULL) dev_priv->renderctx = intel_alloc_context_page(dev); if (dev_priv->renderctx) { struct drm_i915_gem_object *obj = dev_priv->renderctx; - if (BEGIN_LP_RING(4) == 0) { - OUT_RING(MI_SET_CONTEXT); - OUT_RING(obj->gtt_offset | - MI_MM_SPACE_GTT | - MI_SAVE_EXT_STATE_EN | - MI_RESTORE_EXT_STATE_EN | - MI_RESTORE_INHIBIT); - OUT_RING(MI_NOOP); - OUT_RING(MI_FLUSH); - ADVANCE_LP_RING(); + if (BEGIN_LP_RING(6) != 0) { + i915_gem_object_unpin(obj); + drm_gem_object_unreference(&obj->base); + dev_priv->renderctx = NULL; + return; } + OUT_RING(MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN); + OUT_RING(MI_SET_CONTEXT); + OUT_RING(obj->gtt_offset | + MI_MM_SPACE_GTT | + MI_SAVE_EXT_STATE_EN | + MI_RESTORE_EXT_STATE_EN | + MI_RESTORE_INHIBIT); + OUT_RING(MI_SUSPEND_FLUSH); + OUT_RING(MI_NOOP); + OUT_RING(MI_FLUSH); + ADVANCE_LP_RING(); } else DRM_DEBUG_KMS("Failed to allocate render context." "Disable RC6\n"); - } - if (IS_GEN4(dev) && IS_MOBILE(dev)) { if (dev_priv->pwrctx == NULL) dev_priv->pwrctx = intel_alloc_context_page(dev); if (dev_priv->pwrctx) { struct drm_i915_gem_object *obj = dev_priv->pwrctx; I915_WRITE(PWRCTXA, obj->gtt_offset | PWRCTX_EN); - I915_WRITE(MCHBAR_RENDER_STANDBY, - I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT); } } } -- cgit v0.10.2 From 1daed3fb8324d517a1f9da43f1a1d3619d1b0ddc Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 5 Jan 2011 12:01:25 -0800 Subject: drm/i915: fix rc6 enabling around suspend/resume Enabling RC6 implies setting a graphics context. Make sure we do that only after the ring has been enabled, otherwise our ring commands will hang. Signed-off-by: Jesse Barnes Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 2913496..02fce7f 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -357,6 +357,9 @@ static int i915_drm_thaw(struct drm_device *dev) drm_helper_resume_force_mode(dev); } + /* Clock gating state */ + intel_enable_clock_gating(dev); + intel_opregion_init(dev); dev_priv->modeset_on_lid = 0; diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index af53063..147cd96 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -868,9 +868,6 @@ int i915_restore_state(struct drm_device *dev) I915_WRITE (IMR, dev_priv->saveIMR); } - /* Clock gating state */ - intel_enable_clock_gating(dev); - if (IS_IRONLAKE_M(dev)) { ironlake_enable_drps(dev); intel_init_emon(dev); -- cgit v0.10.2 From d5bb081b027b520f9e59b4fb8faea83a136ec15e Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 5 Jan 2011 12:01:26 -0800 Subject: drm/i915: cleanup rc6 code Cleanup several aspects of the rc6 code: - misnamed intel_disable_clock_gating function (was only about rc6) - remove commented call to intel_disable_clock_gating - rc6 enabling code belongs in its own function (allows us to move the actual clock gating enable call back into restore_state) - allocate power & render contexts up front, only free on unload (avoids ugly lazy init at rc6 enable time) Signed-off-by: Jesse Barnes [ickle: checkpatch cleanup] Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 02fce7f..0de75a2 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -355,10 +355,10 @@ static int i915_drm_thaw(struct drm_device *dev) /* Resume the modeset for every activated CRTC */ drm_helper_resume_force_mode(dev); - } - /* Clock gating state */ - intel_enable_clock_gating(dev); + if (dev_priv->renderctx && dev_priv->pwrctx) + ironlake_enable_rc6(dev); + } intel_opregion_init(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1f77d8c..4552600 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1260,6 +1260,7 @@ extern void intel_disable_fbc(struct drm_device *dev); extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval); extern bool intel_fbc_enabled(struct drm_device *dev); extern bool ironlake_set_drps(struct drm_device *dev, u8 val); +extern void ironlake_enable_rc6(struct drm_device *dev); extern void gen6_set_rps(struct drm_device *dev, u8 val); extern void intel_detect_pch (struct drm_device *dev); extern int intel_trans_dp_port_sel (struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 147cd96..0521ecf2 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -822,10 +822,6 @@ int i915_save_state(struct drm_device *dev) if (IS_GEN6(dev)) gen6_disable_rps(dev); - /* XXX disabling the clock gating breaks suspend on gm45 - intel_disable_clock_gating(dev); - */ - /* Cache mode state */ dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0); @@ -868,6 +864,9 @@ int i915_restore_state(struct drm_device *dev) I915_WRITE (IMR, dev_priv->saveIMR); } + /* Clock gating state */ + intel_enable_clock_gating(dev); + if (IS_IRONLAKE_M(dev)) { ironlake_enable_drps(dev); intel_init_emon(dev); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f3c0525..1190efa 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6415,44 +6415,6 @@ void intel_enable_clock_gating(struct drm_device *dev) } else if (IS_I830(dev)) { I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE); } - - /* - * GPU can automatically power down the render unit if given a page - * to save state. - */ - if (IS_IRONLAKE_M(dev)) { - if (dev_priv->renderctx == NULL) - dev_priv->renderctx = intel_alloc_context_page(dev); - if (dev_priv->renderctx) { - struct drm_i915_gem_object *obj = dev_priv->renderctx; - if (BEGIN_LP_RING(6) != 0) { - i915_gem_object_unpin(obj); - drm_gem_object_unreference(&obj->base); - dev_priv->renderctx = NULL; - return; - } - OUT_RING(MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN); - OUT_RING(MI_SET_CONTEXT); - OUT_RING(obj->gtt_offset | - MI_MM_SPACE_GTT | - MI_SAVE_EXT_STATE_EN | - MI_RESTORE_EXT_STATE_EN | - MI_RESTORE_INHIBIT); - OUT_RING(MI_SUSPEND_FLUSH); - OUT_RING(MI_NOOP); - OUT_RING(MI_FLUSH); - ADVANCE_LP_RING(); - } else - DRM_DEBUG_KMS("Failed to allocate render context." - "Disable RC6\n"); - - if (dev_priv->pwrctx == NULL) - dev_priv->pwrctx = intel_alloc_context_page(dev); - if (dev_priv->pwrctx) { - struct drm_i915_gem_object *obj = dev_priv->pwrctx; - I915_WRITE(PWRCTXA, obj->gtt_offset | PWRCTX_EN); - } - } } void intel_disable_clock_gating(struct drm_device *dev) @@ -6482,6 +6444,57 @@ void intel_disable_clock_gating(struct drm_device *dev) } } +static void ironlake_disable_rc6(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + /* Wake the GPU, prevent RC6, then restore RSTDBYCTL */ + I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) | RCX_SW_EXIT); + wait_for(((I915_READ(RSTDBYCTL) & RSX_STATUS_MASK) == RSX_STATUS_ON), + 10); + POSTING_READ(CCID); + I915_WRITE(PWRCTXA, 0); + POSTING_READ(PWRCTXA); + I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT); + POSTING_READ(RSTDBYCTL); + i915_gem_object_unpin(dev_priv->renderctx); + drm_gem_object_unreference(&dev_priv->renderctx->base); + dev_priv->renderctx = NULL; + i915_gem_object_unpin(dev_priv->pwrctx); + drm_gem_object_unreference(&dev_priv->pwrctx->base); + dev_priv->pwrctx = NULL; +} + +void ironlake_enable_rc6(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + /* + * GPU can automatically power down the render unit if given a page + * to save state. + */ + ret = BEGIN_LP_RING(6); + if (ret) { + ironlake_disable_rc6(dev); + return; + } + OUT_RING(MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN); + OUT_RING(MI_SET_CONTEXT); + OUT_RING(dev_priv->renderctx->gtt_offset | + MI_MM_SPACE_GTT | + MI_SAVE_EXT_STATE_EN | + MI_RESTORE_EXT_STATE_EN | + MI_RESTORE_INHIBIT); + OUT_RING(MI_SUSPEND_FLUSH); + OUT_RING(MI_NOOP); + OUT_RING(MI_FLUSH); + ADVANCE_LP_RING(); + + I915_WRITE(PWRCTXA, dev_priv->pwrctx->gtt_offset | PWRCTX_EN); + I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT); +} + /* Set up chip specific display functions */ static void intel_init_display(struct drm_device *dev) { @@ -6724,6 +6737,21 @@ void intel_modeset_init(struct drm_device *dev) if (IS_GEN6(dev)) gen6_enable_rps(dev_priv); + if (IS_IRONLAKE_M(dev)) { + dev_priv->renderctx = intel_alloc_context_page(dev); + if (!dev_priv->renderctx) + goto skip_rc6; + dev_priv->pwrctx = intel_alloc_context_page(dev); + if (!dev_priv->pwrctx) { + i915_gem_object_unpin(dev_priv->renderctx); + drm_gem_object_unreference(&dev_priv->renderctx->base); + dev_priv->renderctx = NULL; + goto skip_rc6; + } + ironlake_enable_rc6(dev); + } + +skip_rc6: INIT_WORK(&dev_priv->idle_work, intel_idle_update); setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, (unsigned long)dev); @@ -6760,7 +6788,8 @@ void intel_modeset_cleanup(struct drm_device *dev) if (IS_GEN6(dev)) gen6_disable_rps(dev); - intel_disable_clock_gating(dev); + if (IS_IRONLAKE_M(dev)) + ironlake_disable_rc6(dev); mutex_unlock(&dev->struct_mutex); -- cgit v0.10.2 From 776ad8062bb77697b8728a9794e3a394b28cf885 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 4 Jan 2011 15:09:39 -0800 Subject: drm/i915: detect & report PCH display error interrupts FDI and the transcoders can fail for various reasons, so detect those conditions and report on them. Signed-off-by: Jesse Barnes Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c9adcdd..d431fc4 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -398,6 +398,50 @@ static void gen6_pm_irq_handler(struct drm_device *dev) I915_WRITE(GEN6_PMIIR, pm_iir); } +static void pch_irq_handler(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 pch_iir; + + pch_iir = I915_READ(SDEIIR); + + if (pch_iir & SDE_AUDIO_POWER_MASK) + DRM_DEBUG_DRIVER("PCH audio power change on port %d\n", + (pch_iir & SDE_AUDIO_POWER_MASK) >> + SDE_AUDIO_POWER_SHIFT); + + if (pch_iir & SDE_GMBUS) + DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n"); + + if (pch_iir & SDE_AUDIO_HDCP_MASK) + DRM_DEBUG_DRIVER("PCH HDCP audio interrupt\n"); + + if (pch_iir & SDE_AUDIO_TRANS_MASK) + DRM_DEBUG_DRIVER("PCH transcoder audio interrupt\n"); + + if (pch_iir & SDE_POISON) + DRM_ERROR("PCH poison interrupt\n"); + + if (pch_iir & SDE_FDI_MASK) { + u32 fdia, fdib; + + fdia = I915_READ(FDI_RXA_IIR); + fdib = I915_READ(FDI_RXB_IIR); + DRM_DEBUG_DRIVER("PCH FDI RX interrupt; FDI RXA IIR: 0x%08x, FDI RXB IIR: 0x%08x\n", fdia, fdib); + } + + if (pch_iir & (SDE_TRANSB_CRC_DONE | SDE_TRANSA_CRC_DONE)) + DRM_DEBUG_DRIVER("PCH transcoder CRC done interrupt\n"); + + if (pch_iir & (SDE_TRANSB_CRC_ERR | SDE_TRANSA_CRC_ERR)) + DRM_DEBUG_DRIVER("PCH transcoder CRC error interrupt\n"); + + if (pch_iir & SDE_TRANSB_FIFO_UNDER) + DRM_DEBUG_DRIVER("PCH transcoder B underrun interrupt\n"); + if (pch_iir & SDE_TRANSA_FIFO_UNDER) + DRM_DEBUG_DRIVER("PCH transcoder A underrun interrupt\n"); +} + static irqreturn_t ironlake_irq_handler(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -465,8 +509,11 @@ static irqreturn_t ironlake_irq_handler(struct drm_device *dev) drm_handle_vblank(dev, 1); /* check event from PCH */ - if ((de_iir & DE_PCH_EVENT) && (pch_iir & hotplug_mask)) - queue_work(dev_priv->wq, &dev_priv->hotplug_work); + if (de_iir & DE_PCH_EVENT) { + if (pch_iir & hotplug_mask) + queue_work(dev_priv->wq, &dev_priv->hotplug_work); + pch_irq_handler(dev); + } if (de_iir & DE_PCU_EVENT) { I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS)); @@ -1656,6 +1703,9 @@ static int ironlake_irq_postinstall(struct drm_device *dev) } else { hotplug_mask = SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG | SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG; + hotplug_mask |= SDE_AUX_MASK | SDE_FDI_MASK | SDE_TRANS_MASK; + I915_WRITE(FDI_RXA_IMR, 0); + I915_WRITE(FDI_RXB_IMR, 0); } dev_priv->pch_irq_mask = ~hotplug_mask; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 33ddf31..40a407f 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2778,12 +2778,41 @@ /* PCH */ /* south display engine interrupt */ +#define SDE_AUDIO_POWER_D (1 << 27) +#define SDE_AUDIO_POWER_C (1 << 26) +#define SDE_AUDIO_POWER_B (1 << 25) +#define SDE_AUDIO_POWER_SHIFT (25) +#define SDE_AUDIO_POWER_MASK (7 << SDE_AUDIO_POWER_SHIFT) +#define SDE_GMBUS (1 << 24) +#define SDE_AUDIO_HDCP_TRANSB (1 << 23) +#define SDE_AUDIO_HDCP_TRANSA (1 << 22) +#define SDE_AUDIO_HDCP_MASK (3 << 22) +#define SDE_AUDIO_TRANSB (1 << 21) +#define SDE_AUDIO_TRANSA (1 << 20) +#define SDE_AUDIO_TRANS_MASK (3 << 20) +#define SDE_POISON (1 << 19) +/* 18 reserved */ +#define SDE_FDI_RXB (1 << 17) +#define SDE_FDI_RXA (1 << 16) +#define SDE_FDI_MASK (3 << 16) +#define SDE_AUXD (1 << 15) +#define SDE_AUXC (1 << 14) +#define SDE_AUXB (1 << 13) +#define SDE_AUX_MASK (7 << 13) +/* 12 reserved */ #define SDE_CRT_HOTPLUG (1 << 11) #define SDE_PORTD_HOTPLUG (1 << 10) #define SDE_PORTC_HOTPLUG (1 << 9) #define SDE_PORTB_HOTPLUG (1 << 8) #define SDE_SDVOB_HOTPLUG (1 << 6) #define SDE_HOTPLUG_MASK (0xf << 8) +#define SDE_TRANSB_CRC_DONE (1 << 5) +#define SDE_TRANSB_CRC_ERR (1 << 4) +#define SDE_TRANSB_FIFO_UNDER (1 << 3) +#define SDE_TRANSA_CRC_DONE (1 << 2) +#define SDE_TRANSA_CRC_ERR (1 << 1) +#define SDE_TRANSA_FIFO_UNDER (1 << 0) +#define SDE_TRANS_MASK (0x3f) /* CPT */ #define SDE_CRT_HOTPLUG_CPT (1 << 19) #define SDE_PORTD_HOTPLUG_CPT (1 << 23) -- cgit v0.10.2 From 882417851a0f2e09e110038a13e88e9b5a100800 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 7 Jan 2011 17:09:48 +0000 Subject: drm/i915: Propagate error from flushing the ring ... in order to avoid a BUG() and potential unbounded waits. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4552600..3e78314 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1086,10 +1086,10 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); int i915_gem_init_object(struct drm_gem_object *obj); -void i915_gem_flush_ring(struct drm_device *dev, - struct intel_ring_buffer *ring, - uint32_t invalidate_domains, - uint32_t flush_domains); +int __must_check i915_gem_flush_ring(struct drm_device *dev, + struct intel_ring_buffer *ring, + uint32_t invalidate_domains, + uint32_t flush_domains); struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, size_t size); void i915_gem_free_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 07b6244..2873d06 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -35,18 +35,18 @@ #include #include -static void i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj); +static __must_check int i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj); static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj); static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj); -static int i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, - bool write); -static int i915_gem_object_set_cpu_read_domain_range(struct drm_i915_gem_object *obj, - uint64_t offset, - uint64_t size); +static __must_check int i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, + bool write); +static __must_check int i915_gem_object_set_cpu_read_domain_range(struct drm_i915_gem_object *obj, + uint64_t offset, + uint64_t size); static void i915_gem_object_set_to_full_cpu_read_domain(struct drm_i915_gem_object *obj); -static int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, - unsigned alignment, - bool map_and_fenceable); +static __must_check int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, + unsigned alignment, + bool map_and_fenceable); static void i915_gem_clear_fence_reg(struct drm_device *dev, struct drm_i915_fence_reg *reg); static int i915_gem_phys_pwrite(struct drm_device *dev, @@ -2142,25 +2142,37 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) return ret; } -void +int i915_gem_flush_ring(struct drm_device *dev, struct intel_ring_buffer *ring, uint32_t invalidate_domains, uint32_t flush_domains) { - if (ring->flush(ring, invalidate_domains, flush_domains) == 0) - i915_gem_process_flushing_list(dev, flush_domains, ring); + int ret; + + ret = ring->flush(ring, invalidate_domains, flush_domains); + if (ret) + return ret; + + i915_gem_process_flushing_list(dev, flush_domains, ring); + return 0; } static int i915_ring_idle(struct drm_device *dev, struct intel_ring_buffer *ring) { + int ret; + if (list_empty(&ring->gpu_write_list) && list_empty(&ring->active_list)) return 0; - if (!list_empty(&ring->gpu_write_list)) - i915_gem_flush_ring(dev, ring, + if (!list_empty(&ring->gpu_write_list)) { + ret = i915_gem_flush_ring(dev, ring, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); + if (ret) + return ret; + } + return i915_wait_request(dev, i915_gem_next_request_seqno(dev, ring), ring); @@ -2370,10 +2382,13 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj, int ret; if (obj->fenced_gpu_access) { - if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) - i915_gem_flush_ring(obj->base.dev, - obj->last_fenced_ring, - 0, obj->base.write_domain); + if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) { + ret = i915_gem_flush_ring(obj->base.dev, + obj->last_fenced_ring, + 0, obj->base.write_domain); + if (ret) + return ret; + } obj->fenced_gpu_access = false; } @@ -2529,9 +2544,12 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj, return ret; } else if (obj->tiling_changed) { if (obj->fenced_gpu_access) { - if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) - i915_gem_flush_ring(obj->base.dev, obj->ring, - 0, obj->base.write_domain); + if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) { + ret = i915_gem_flush_ring(obj->base.dev, obj->ring, + 0, obj->base.write_domain); + if (ret) + return ret; + } obj->fenced_gpu_access = false; } @@ -2817,17 +2835,16 @@ i915_gem_clflush_object(struct drm_i915_gem_object *obj) } /** Flushes any GPU write domain for the object if it's dirty. */ -static void +static int i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj) { struct drm_device *dev = obj->base.dev; if ((obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0) - return; + return 0; /* Queue the GPU write cache flushing we need. */ - i915_gem_flush_ring(dev, obj->ring, 0, obj->base.write_domain); - BUG_ON(obj->base.write_domain); + return i915_gem_flush_ring(dev, obj->ring, 0, obj->base.write_domain); } /** Flushes the GTT write domain for the object if it's dirty. */ @@ -2894,7 +2911,10 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) if (obj->gtt_space == NULL) return -EINVAL; - i915_gem_object_flush_gpu_write_domain(obj); + ret = i915_gem_object_flush_gpu_write_domain(obj); + if (ret) + return ret; + if (obj->pending_gpu_write || write) { ret = i915_gem_object_wait_rendering(obj, true); if (ret) @@ -2939,7 +2959,10 @@ i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj, if (obj->gtt_space == NULL) return -EINVAL; - i915_gem_object_flush_gpu_write_domain(obj); + ret = i915_gem_object_flush_gpu_write_domain(obj); + if (ret) + return ret; + /* Currently, we are always called from an non-interruptible context. */ if (pipelined != obj->ring) { @@ -2964,12 +2987,17 @@ int i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj, bool interruptible) { + int ret; + if (!obj->active) return 0; - if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) - i915_gem_flush_ring(obj->base.dev, obj->ring, - 0, obj->base.write_domain); + if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) { + ret = i915_gem_flush_ring(obj->base.dev, obj->ring, + 0, obj->base.write_domain); + if (ret) + return ret; + } return i915_gem_object_wait_rendering(obj, interruptible); } @@ -2986,7 +3014,10 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write) uint32_t old_write_domain, old_read_domains; int ret; - i915_gem_object_flush_gpu_write_domain(obj); + ret = i915_gem_object_flush_gpu_write_domain(obj); + if (ret) + return ret; + ret = i915_gem_object_wait_rendering(obj, true); if (ret) return ret; @@ -3081,7 +3112,10 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_i915_gem_object *obj, if (offset == 0 && size == obj->base.size) return i915_gem_object_set_to_cpu_domain(obj, 0); - i915_gem_object_flush_gpu_write_domain(obj); + ret = i915_gem_object_flush_gpu_write_domain(obj); + if (ret) + return ret; + ret = i915_gem_object_wait_rendering(obj, true); if (ret) return ret; @@ -3374,8 +3408,8 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, * flush earlier is beneficial. */ if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) { - i915_gem_flush_ring(dev, obj->ring, - 0, obj->base.write_domain); + ret = i915_gem_flush_ring(dev, obj->ring, + 0, obj->base.write_domain); } else if (obj->ring->outstanding_lazy_request == obj->last_rendering_seqno) { struct drm_i915_gem_request *request; diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 1b78b66..97d5fbd8 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -713,14 +713,14 @@ err: return ret; } -static void +static int i915_gem_execbuffer_flush(struct drm_device *dev, uint32_t invalidate_domains, uint32_t flush_domains, uint32_t flush_rings) { drm_i915_private_t *dev_priv = dev->dev_private; - int i; + int i, ret; if (flush_domains & I915_GEM_DOMAIN_CPU) intel_gtt_chipset_flush(); @@ -730,11 +730,17 @@ i915_gem_execbuffer_flush(struct drm_device *dev, if ((flush_domains | invalidate_domains) & I915_GEM_GPU_DOMAINS) { for (i = 0; i < I915_NUM_RINGS; i++) - if (flush_rings & (1 << i)) - i915_gem_flush_ring(dev, &dev_priv->ring[i], - invalidate_domains, - flush_domains); + if (flush_rings & (1 << i)) { + ret = i915_gem_flush_ring(dev, + &dev_priv->ring[i], + invalidate_domains, + flush_domains); + if (ret) + return ret; + } } + + return 0; } static int @@ -798,10 +804,12 @@ i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, cd.invalidate_domains, cd.flush_domains); #endif - i915_gem_execbuffer_flush(ring->dev, - cd.invalidate_domains, - cd.flush_domains, - cd.flush_rings); + ret = i915_gem_execbuffer_flush(ring->dev, + cd.invalidate_domains, + cd.flush_domains, + cd.flush_rings); + if (ret) + return ret; } list_for_each_entry(obj, objects, exec_list) { -- cgit v0.10.2 From db66e37d239b45f36a3f6495cf4ec49391b2c089 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 8 Jan 2011 09:02:21 +0000 Subject: drm/i915: Include TLB miss overhead for computing WM The docs recommend that if 8 display lines fit inside the FIFO buffer, then the number of watermark entries should be increased to hide the latency of filling the rest of the FIFO buffer. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1190efa..25d9688 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3425,8 +3425,9 @@ static bool ironlake_compute_wm0(struct drm_device *dev, int *cursor_wm) { struct drm_crtc *crtc; - int htotal, hdisplay, clock, pixel_size = 0; - int line_time_us, line_count, entries; + int htotal, hdisplay, clock, pixel_size; + int line_time_us, line_count; + int entries, tlb_miss; crtc = intel_get_crtc_for_pipe(dev, pipe); if (crtc->fb == NULL || !crtc->enabled) @@ -3439,6 +3440,9 @@ static bool ironlake_compute_wm0(struct drm_device *dev, /* Use the small buffer method to calculate plane watermark */ entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000; + tlb_miss = display->fifo_size*display->cacheline_size - hdisplay * 8; + if (tlb_miss > 0) + entries += tlb_miss; entries = DIV_ROUND_UP(entries, display->cacheline_size); *plane_wm = entries + display->guard_size; if (*plane_wm > (int)display->max_wm) @@ -3448,6 +3452,9 @@ static bool ironlake_compute_wm0(struct drm_device *dev, line_time_us = ((htotal * 1000) / clock); line_count = (cursor_latency_ns / line_time_us + 1000) / 1000; entries = line_count * 64 * pixel_size; + tlb_miss = cursor->fifo_size*cursor->cacheline_size - hdisplay * 8; + if (tlb_miss > 0) + entries += tlb_miss; entries = DIV_ROUND_UP(entries, cursor->cacheline_size); *cursor_wm = entries + cursor->guard_size; if (*cursor_wm > (int)cursor->max_wm) -- cgit v0.10.2 From bcfb2e285827bf0cfea8bbfad18a4fca57fbabae Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 7 Jan 2011 21:06:07 +0000 Subject: drm/i915: Record the error batchbuffer on each ring Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 9c4cdc1..a7c194a 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -750,7 +750,9 @@ static int i915_error_state(struct seq_file *m, void *unused) if (error->batchbuffer[i]) { struct drm_i915_error_object *obj = error->batchbuffer[i]; - seq_printf(m, "--- gtt_offset = 0x%08x\n", obj->gtt_offset); + seq_printf(m, "%s --- gtt_offset = 0x%08x\n", + dev_priv->ring[i].name, + obj->gtt_offset); offset = 0; for (page = 0; page < obj->page_count; page++) { for (elt = 0; elt < PAGE_SIZE/4; elt++) { diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3e78314..6c9a042 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -172,7 +172,7 @@ struct drm_i915_error_state { int page_count; u32 gtt_offset; u32 *pages[0]; - } *ringbuffer, *batchbuffer[2]; + } *ringbuffer, *batchbuffer[I915_NUM_RINGS]; struct drm_i915_error_buffer { size_t size; u32 name; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d431fc4..cf61235 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -566,10 +566,9 @@ static void i915_error_work_func(struct work_struct *work) #ifdef CONFIG_DEBUG_FS static struct drm_i915_error_object * -i915_error_object_create(struct drm_device *dev, +i915_error_object_create(struct drm_i915_private *dev_priv, struct drm_i915_gem_object *src) { - drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_error_object *dst; int page, page_count; u32 reloc_offset; @@ -642,52 +641,6 @@ i915_error_state_free(struct drm_device *dev, kfree(error); } -static u32 -i915_get_bbaddr(struct drm_device *dev, u32 *ring) -{ - u32 cmd; - - if (IS_I830(dev) || IS_845G(dev)) - cmd = MI_BATCH_BUFFER; - else if (INTEL_INFO(dev)->gen >= 4) - cmd = (MI_BATCH_BUFFER_START | (2 << 6) | - MI_BATCH_NON_SECURE_I965); - else - cmd = (MI_BATCH_BUFFER_START | (2 << 6)); - - return ring[0] == cmd ? ring[1] : 0; -} - -static u32 -i915_ringbuffer_last_batch(struct drm_device *dev, - struct intel_ring_buffer *ring) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 head, bbaddr; - u32 *val; - - /* Locate the current position in the ringbuffer and walk back - * to find the most recently dispatched batch buffer. - */ - head = I915_READ_HEAD(ring) & HEAD_ADDR; - - val = (u32 *)(ring->virtual_start + head); - while (--val >= (u32 *)ring->virtual_start) { - bbaddr = i915_get_bbaddr(dev, val); - if (bbaddr) - return bbaddr; - } - - val = (u32 *)(ring->virtual_start + ring->size); - while (--val >= (u32 *)ring->virtual_start) { - bbaddr = i915_get_bbaddr(dev, val); - if (bbaddr) - return bbaddr; - } - - return 0; -} - static u32 capture_bo_list(struct drm_i915_error_buffer *err, int count, struct list_head *head) @@ -751,6 +704,36 @@ static void i915_gem_record_fences(struct drm_device *dev, } } +static struct drm_i915_error_object * +i915_error_first_batchbuffer(struct drm_i915_private *dev_priv, + struct intel_ring_buffer *ring) +{ + struct drm_i915_gem_object *obj; + u32 seqno; + + if (!ring->get_seqno) + return NULL; + + seqno = ring->get_seqno(ring); + list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { + if (obj->ring != ring) + continue; + + if (!i915_seqno_passed(obj->last_rendering_seqno, seqno)) + continue; + + if ((obj->base.read_domains & I915_GEM_DOMAIN_COMMAND) == 0) + continue; + + /* We need to copy these to an anonymous buffer as the simplest + * method to avoid being overwritten by userspace. + */ + return i915_error_object_create(dev_priv, obj); + } + + return NULL; +} + /** * i915_capture_error_state - capture an error record for later analysis * @dev: drm device @@ -765,10 +748,8 @@ static void i915_capture_error_state(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; struct drm_i915_error_state *error; - struct drm_i915_gem_object *batchbuffer[2]; unsigned long flags; - u32 bbaddr; - int count; + int i; spin_lock_irqsave(&dev_priv->error_lock, flags); error = dev_priv->first_error; @@ -827,83 +808,30 @@ static void i915_capture_error_state(struct drm_device *dev) } i915_gem_record_fences(dev, error); - bbaddr = i915_ringbuffer_last_batch(dev, &dev_priv->ring[RCS]); - - /* Grab the current batchbuffer, most likely to have crashed. */ - batchbuffer[0] = NULL; - batchbuffer[1] = NULL; - count = 0; - list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { - if (batchbuffer[0] == NULL && - bbaddr >= obj->gtt_offset && - bbaddr < obj->gtt_offset + obj->base.size) - batchbuffer[0] = obj; - - if (batchbuffer[1] == NULL && - error->acthd >= obj->gtt_offset && - error->acthd < obj->gtt_offset + obj->base.size) - batchbuffer[1] = obj; - - count++; - } - /* Scan the other lists for completeness for those bizarre errors. */ - if (batchbuffer[0] == NULL || batchbuffer[1] == NULL) { - list_for_each_entry(obj, &dev_priv->mm.flushing_list, mm_list) { - if (batchbuffer[0] == NULL && - bbaddr >= obj->gtt_offset && - bbaddr < obj->gtt_offset + obj->base.size) - batchbuffer[0] = obj; - - if (batchbuffer[1] == NULL && - error->acthd >= obj->gtt_offset && - error->acthd < obj->gtt_offset + obj->base.size) - batchbuffer[1] = obj; - - if (batchbuffer[0] && batchbuffer[1]) - break; - } - } - if (batchbuffer[0] == NULL || batchbuffer[1] == NULL) { - list_for_each_entry(obj, &dev_priv->mm.inactive_list, mm_list) { - if (batchbuffer[0] == NULL && - bbaddr >= obj->gtt_offset && - bbaddr < obj->gtt_offset + obj->base.size) - batchbuffer[0] = obj; - - if (batchbuffer[1] == NULL && - error->acthd >= obj->gtt_offset && - error->acthd < obj->gtt_offset + obj->base.size) - batchbuffer[1] = obj; - - if (batchbuffer[0] && batchbuffer[1]) - break; - } - } - - /* We need to copy these to an anonymous buffer as the simplest - * method to avoid being overwritten by userspace. - */ - error->batchbuffer[0] = i915_error_object_create(dev, batchbuffer[0]); - if (batchbuffer[1] != batchbuffer[0]) - error->batchbuffer[1] = i915_error_object_create(dev, batchbuffer[1]); - else - error->batchbuffer[1] = NULL; + /* Record the active batchbuffers */ + for (i = 0; i < I915_NUM_RINGS; i++) + error->batchbuffer[i] = + i915_error_first_batchbuffer(dev_priv, + &dev_priv->ring[i]); /* Record the ringbuffer */ - error->ringbuffer = i915_error_object_create(dev, + error->ringbuffer = i915_error_object_create(dev_priv, dev_priv->ring[RCS].obj); /* Record buffers on the active and pinned lists. */ error->active_bo = NULL; error->pinned_bo = NULL; - error->active_bo_count = count; + i = 0; + list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) + i++; + error->active_bo_count = i; list_for_each_entry(obj, &dev_priv->mm.pinned_list, mm_list) - count++; - error->pinned_bo_count = count - error->active_bo_count; + i++; + error->pinned_bo_count = i - error->active_bo_count; - if (count) { - error->active_bo = kmalloc(sizeof(*error->active_bo)*count, + if (i) { + error->active_bo = kmalloc(sizeof(*error->active_bo)*i, GFP_ATOMIC); if (error->active_bo) error->pinned_bo = -- cgit v0.10.2 From d9126400580e2caada85fa68799952956a6062fd Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 11 Jan 2011 11:07:54 +0000 Subject: drm/i915/gtt: Unmap the PCI pages after unbinding them from the GTT Dave Airlie spotted that his ILK laptop with DMAR enabled was generating the occasional DMAR warning. "The ordering in the previous code was to rewrite the GTT table before unmapping the pages and that makes sense to me." This is his stable patch ported to d-i-n. Reported-by: Dave Airlie Original-patch-by: Dave Airlie Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 86673e7..70433ae 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -85,15 +85,11 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj) void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; + intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT, + obj->base.size >> PAGE_SHIFT); - if (dev_priv->mm.gtt->needs_dmar) { + if (obj->sg_list) { intel_gtt_unmap_memory(obj->sg_list, obj->num_sg); obj->sg_list = NULL; - obj->num_sg = 0; } - - intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT, - obj->base.size >> PAGE_SHIFT); } -- cgit v0.10.2 From 0a58705b2fc3fa29525cf2fdae3d4276a5771280 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 9 Jan 2011 21:05:44 +0000 Subject: drm/i915: Periodically flush the active lists and requests In order to retire active buffers whilst no client is active, we need to insert our own flush requests onto the ring. This is useful for servers that queue up some rendering and then go to sleep as it allows us to the complete processing of those requests, potentially making that memory available again much earlier. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 2873d06..87c2df7 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1935,6 +1935,8 @@ i915_gem_retire_work_handler(struct work_struct *work) { drm_i915_private_t *dev_priv; struct drm_device *dev; + bool idle; + int i; dev_priv = container_of(work, drm_i915_private_t, mm.retire_work.work); @@ -1948,11 +1950,31 @@ i915_gem_retire_work_handler(struct work_struct *work) i915_gem_retire_requests(dev); - if (!dev_priv->mm.suspended && - (!list_empty(&dev_priv->ring[RCS].request_list) || - !list_empty(&dev_priv->ring[VCS].request_list) || - !list_empty(&dev_priv->ring[BCS].request_list))) + /* Send a periodic flush down the ring so we don't hold onto GEM + * objects indefinitely. + */ + idle = true; + for (i = 0; i < I915_NUM_RINGS; i++) { + struct intel_ring_buffer *ring = &dev_priv->ring[i]; + + if (!list_empty(&ring->gpu_write_list)) { + struct drm_i915_gem_request *request; + int ret; + + ret = i915_gem_flush_ring(dev, ring, 0, + I915_GEM_GPU_DOMAINS); + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (ret || request == NULL || + i915_add_request(dev, NULL, request, ring)) + kfree(request); + } + + idle &= list_empty(&ring->request_list); + } + + if (!dev_priv->mm.suspended && !idle) queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ); + mutex_unlock(&dev->struct_mutex); } -- cgit v0.10.2 From a779e5abda0367aa9d53c0931d9687743afe503d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 9 Jan 2011 21:07:49 +0000 Subject: drm/i915: Record AGP memory type upon error Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a7c194a..73914d8 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -624,6 +624,15 @@ static const char *ring_str(int ring) } } +static const char *agp_type_str(int type) +{ + switch (type) { + case 0: return " uncached"; + case 1: return " snooped"; + default: return ""; + } +} + static const char *pin_flag(int pinned) { if (pinned > 0) @@ -662,7 +671,7 @@ static void print_error_buffers(struct seq_file *m, seq_printf(m, "%s [%d]:\n", name, count); while (count--) { - seq_printf(m, " %08x %8zd %04x %04x %08x%s%s%s%s%s", + seq_printf(m, " %08x %8zd %04x %04x %08x%s%s%s%s%s%s", err->gtt_offset, err->size, err->read_domains, @@ -672,7 +681,8 @@ static void print_error_buffers(struct seq_file *m, tiling_flag(err->tiling), dirty_flag(err->dirty), purgeable_flag(err->purgeable), - ring_str(err->ring)); + ring_str(err->ring), + agp_type_str(err->agp_type)); if (err->name) seq_printf(m, " (name: %d)", err->name); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6c9a042..6130f77 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -174,18 +174,19 @@ struct drm_i915_error_state { u32 *pages[0]; } *ringbuffer, *batchbuffer[I915_NUM_RINGS]; struct drm_i915_error_buffer { - size_t size; + u32 size; u32 name; u32 seqno; u32 gtt_offset; u32 read_domains; u32 write_domain; - u32 fence_reg; + s32 fence_reg:5; s32 pinned:2; u32 tiling:2; u32 dirty:1; u32 purgeable:1; u32 ring:4; + u32 agp_type:1; } *active_bo, *pinned_bo; u32 active_bo_count, pinned_bo_count; struct intel_overlay_error_state *overlay; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index cf61235..e418e8b 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -665,6 +665,7 @@ static u32 capture_bo_list(struct drm_i915_error_buffer *err, err->dirty = obj->dirty; err->purgeable = obj->madv != I915_MADV_WILLNEED; err->ring = obj->ring ? obj->ring->id : 0; + err->agp_type = obj->agp_type == AGP_USER_CACHED_MEMORY; if (++i == count) break; -- cgit v0.10.2 From 08c18323547ce6d70eab3b37eca894baf114ad85 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 10 Jan 2011 00:00:24 +0000 Subject: drm/i915/debugfs: Show all objects in the gtt Useful for determining the layout. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 73914d8..19a3d58 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -106,10 +106,19 @@ static const char *get_tiling_flag(struct drm_i915_gem_object *obj) } } +static const char *agp_type_str(int type) +{ + switch (type) { + case 0: return " uncached"; + case 1: return " snooped"; + default: return ""; + } +} + static void describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) { - seq_printf(m, "%p: %s%s %8zd %04x %04x %d %d%s%s", + seq_printf(m, "%p: %s%s %8zd %04x %04x %d %d%s%s%s", &obj->base, get_pin_flag(obj), get_tiling_flag(obj), @@ -118,6 +127,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) obj->base.write_domain, obj->last_rendering_seqno, obj->last_fenced_seqno, + agp_type_str(obj->agp_type == AGP_USER_CACHED_MEMORY), obj->dirty ? " dirty" : "", obj->madv == I915_MADV_DONTNEED ? " purgeable" : ""); if (obj->base.name) @@ -276,6 +286,37 @@ static int i915_gem_object_info(struct seq_file *m, void* data) return 0; } +static int i915_gem_gtt_info(struct seq_file *m, void* data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj; + size_t total_obj_size, total_gtt_size; + int count, ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + total_obj_size = total_gtt_size = count = 0; + list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { + seq_printf(m, " "); + describe_obj(m, obj); + seq_printf(m, "\n"); + total_obj_size += obj->base.size; + total_gtt_size += obj->gtt_space->size; + count++; + } + + mutex_unlock(&dev->struct_mutex); + + seq_printf(m, "Total %d objects, %zu bytes, %zu GTT size\n", + count, total_obj_size, total_gtt_size); + + return 0; +} + static int i915_gem_pageflip_info(struct seq_file *m, void *data) { @@ -624,15 +665,6 @@ static const char *ring_str(int ring) } } -static const char *agp_type_str(int type) -{ - switch (type) { - case 0: return " uncached"; - case 1: return " snooped"; - default: return ""; - } -} - static const char *pin_flag(int pinned) { if (pinned > 0) @@ -1229,6 +1261,7 @@ static int i915_wedged_create(struct dentry *root, struct drm_minor *minor) static struct drm_info_list i915_debugfs_list[] = { {"i915_capabilities", i915_capabilities, 0, 0}, {"i915_gem_objects", i915_gem_object_info, 0}, + {"i915_gem_gtt", i915_gem_gtt_info, 0}, {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST}, {"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST}, {"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST}, -- cgit v0.10.2 From 36cf17423095882ec0f8f2c04d1bd0ee812149df Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 10 Jan 2011 12:09:12 +0000 Subject: drm/i915/execbuffer: Correctly clear the current object list upon EFAULT Before releasing the lock in order to copy the relocation list from user pages, we need to drop all the object references as another thread may usurp and execute another batchbuffer before we reacquire the lock. However, the code was buggy and failed to clear the list... Signed-off-by: Chris Wilson Cc: stable@kernel.org diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 97d5fbd8..0445770 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -622,7 +622,7 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, int i, total, ret; /* We may process another execbuffer during the unlock... */ - while (list_empty(objects)) { + while (!list_empty(objects)) { obj = list_first_entry(objects, struct drm_i915_gem_object, exec_list); @@ -665,7 +665,6 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, } /* reacquire the objects */ - INIT_LIST_HEAD(objects); eb_reset(eb); for (i = 0; i < count; i++) { struct drm_i915_gem_object *obj; @@ -1353,4 +1352,3 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, drm_free_large(exec2_list); return ret; } - -- cgit v0.10.2 From 092de6f225638ec300936bfcbdc67805733cc78c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 10 Jan 2011 14:21:05 +0000 Subject: drm/i915/evict: Ensure we completely cleanup on failure ... and not leave the objects in a inconsistent state. Signed-off-by: Chris Wilson Cc: stable@kernel.org diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 78b8cf9..3d39005 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -127,9 +127,15 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, } /* Nothing found, clean up and bail out! */ - list_for_each_entry(obj, &unwind_list, exec_list) { + while (!list_empty(&unwind_list)) { + obj = list_first_entry(&unwind_list, + struct drm_i915_gem_object, + exec_list); + ret = drm_mm_scan_remove_block(obj->gtt_space); BUG_ON(ret); + + list_del_init(&obj->exec_list); drm_gem_object_unreference(&obj->base); } @@ -162,6 +168,7 @@ found: exec_list); if (ret == 0) ret = i915_gem_object_unbind(obj); + list_del_init(&obj->exec_list); drm_gem_object_unreference(&obj->base); } -- cgit v0.10.2 From 809b63349ce6eb6603e7dab482c642f28135a2db Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 10 Jan 2011 17:33:15 +0000 Subject: drm/i915: If we hit OOM when allocating GTT pages, clear the aperture Rather than evicting an object at random, which is unlikely to alleviate the memory pressure sufficient to allow us to continue, zap the entire aperture. That should give the system long enough to recover and reap some pages from the evicted objects, forestalling the allocation error for the new object. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 87c2df7..3dfc848 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2782,10 +2782,8 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, obj->gtt_space = NULL; if (ret == -ENOMEM) { - /* first try to clear up some space from the GTT */ - ret = i915_gem_evict_something(dev, size, - alignment, - map_and_fenceable); + /* first try to reclaim some memory by clearing the GTT */ + ret = i915_gem_evict_everything(dev, false); if (ret) { /* now try to shrink everyone else */ if (gfpmask) { @@ -2793,7 +2791,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, goto search_free; } - return ret; + return -ENOMEM; } goto search_free; @@ -2808,9 +2806,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, drm_mm_put_block(obj->gtt_space); obj->gtt_space = NULL; - ret = i915_gem_evict_something(dev, size, - alignment, map_and_fenceable); - if (ret) + if (i915_gem_evict_everything(dev, false)) return ret; goto search_free; -- cgit v0.10.2 From 6fe4f14044f181e146cdc15485428f95fa541ce8 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 10 Jan 2011 17:35:37 +0000 Subject: drm/i915/execbuffer: Reorder binding of objects to favour restrictions As the mappable portion of the aperture is always a small subset at the start of the GTT, it is allocated preferentially by drm_mm. This is useful in case we ever need to map an object later. However, if you have a large object that can consume the entire mappable region of the GTT this prevents the batchbuffer from fitting and so causing an error. Instead allocate all those that require a mapping up front in order to improve the likelihood of finding sufficient space to bind them. Signed-off-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6130f77..385fc7e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -796,6 +796,7 @@ struct drm_i915_gem_object { */ struct hlist_node exec_node; unsigned long exec_handle; + struct drm_i915_gem_exec_object2 *exec_entry; /** * Current offset of the object in GTT space. diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 0445770..e698343 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -268,7 +268,6 @@ eb_destroy(struct eb_objects *eb) static int i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, struct eb_objects *eb, - struct drm_i915_gem_exec_object2 *entry, struct drm_i915_gem_relocation_entry *reloc) { struct drm_device *dev = obj->base.dev; @@ -411,10 +410,10 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, static int i915_gem_execbuffer_relocate_object(struct drm_i915_gem_object *obj, - struct eb_objects *eb, - struct drm_i915_gem_exec_object2 *entry) + struct eb_objects *eb) { struct drm_i915_gem_relocation_entry __user *user_relocs; + struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; int i, ret; user_relocs = (void __user *)(uintptr_t)entry->relocs_ptr; @@ -426,7 +425,7 @@ i915_gem_execbuffer_relocate_object(struct drm_i915_gem_object *obj, sizeof(reloc))) return -EFAULT; - ret = i915_gem_execbuffer_relocate_entry(obj, eb, entry, &reloc); + ret = i915_gem_execbuffer_relocate_entry(obj, eb, &reloc); if (ret) return ret; @@ -442,13 +441,13 @@ i915_gem_execbuffer_relocate_object(struct drm_i915_gem_object *obj, static int i915_gem_execbuffer_relocate_object_slow(struct drm_i915_gem_object *obj, struct eb_objects *eb, - struct drm_i915_gem_exec_object2 *entry, struct drm_i915_gem_relocation_entry *relocs) { + const struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; int i, ret; for (i = 0; i < entry->relocation_count; i++) { - ret = i915_gem_execbuffer_relocate_entry(obj, eb, entry, &relocs[i]); + ret = i915_gem_execbuffer_relocate_entry(obj, eb, &relocs[i]); if (ret) return ret; } @@ -459,8 +458,7 @@ i915_gem_execbuffer_relocate_object_slow(struct drm_i915_gem_object *obj, static int i915_gem_execbuffer_relocate(struct drm_device *dev, struct eb_objects *eb, - struct list_head *objects, - struct drm_i915_gem_exec_object2 *exec) + struct list_head *objects) { struct drm_i915_gem_object *obj; int ret; @@ -468,7 +466,7 @@ i915_gem_execbuffer_relocate(struct drm_device *dev, list_for_each_entry(obj, objects, exec_list) { obj->base.pending_read_domains = 0; obj->base.pending_write_domain = 0; - ret = i915_gem_execbuffer_relocate_object(obj, eb, exec++); + ret = i915_gem_execbuffer_relocate_object(obj, eb); if (ret) return ret; } @@ -479,13 +477,36 @@ i915_gem_execbuffer_relocate(struct drm_device *dev, static int i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, struct drm_file *file, - struct list_head *objects, - struct drm_i915_gem_exec_object2 *exec) + struct list_head *objects) { struct drm_i915_gem_object *obj; - struct drm_i915_gem_exec_object2 *entry; int ret, retry; bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; + struct list_head ordered_objects; + + INIT_LIST_HEAD(&ordered_objects); + while (!list_empty(objects)) { + struct drm_i915_gem_exec_object2 *entry; + bool need_fence, need_mappable; + + obj = list_first_entry(objects, + struct drm_i915_gem_object, + exec_list); + entry = obj->exec_entry; + + need_fence = + has_fenced_gpu_access && + entry->flags & EXEC_OBJECT_NEEDS_FENCE && + obj->tiling_mode != I915_TILING_NONE; + need_mappable = + entry->relocation_count ? true : need_fence; + + if (need_mappable) + list_move(&obj->exec_list, &ordered_objects); + else + list_move_tail(&obj->exec_list, &ordered_objects); + } + list_splice(&ordered_objects, objects); /* Attempt to pin all of the buffers into the GTT. * This is done in 3 phases: @@ -504,14 +525,11 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, ret = 0; /* Unbind any ill-fitting objects or pin. */ - entry = exec; list_for_each_entry(obj, objects, exec_list) { + struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; bool need_fence, need_mappable; - - if (!obj->gtt_space) { - entry++; + if (!obj->gtt_space) continue; - } need_fence = has_fenced_gpu_access && @@ -534,8 +552,8 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, } /* Bind fresh objects */ - entry = exec; list_for_each_entry(obj, objects, exec_list) { + struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; bool need_fence; need_fence = @@ -570,7 +588,6 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, } entry->offset = obj->gtt_offset; - entry++; } /* Decrement pin count for bound objects */ @@ -680,10 +697,11 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, list_add_tail(&obj->exec_list, objects); obj->exec_handle = exec[i].handle; + obj->exec_entry = &exec[i]; eb_add_object(eb, obj); } - ret = i915_gem_execbuffer_reserve(ring, file, objects, exec); + ret = i915_gem_execbuffer_reserve(ring, file, objects); if (ret) goto err; @@ -692,7 +710,6 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, obj->base.pending_read_domains = 0; obj->base.pending_write_domain = 0; ret = i915_gem_execbuffer_relocate_object_slow(obj, eb, - exec, reloc + total); if (ret) goto err; @@ -1110,16 +1127,22 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, list_add_tail(&obj->exec_list, &objects); obj->exec_handle = exec[i].handle; + obj->exec_entry = &exec[i]; eb_add_object(eb, obj); } + /* take note of the batch buffer before we might reorder the lists */ + batch_obj = list_entry(objects.prev, + struct drm_i915_gem_object, + exec_list); + /* Move the objects en-masse into the GTT, evicting if necessary. */ - ret = i915_gem_execbuffer_reserve(ring, file, &objects, exec); + ret = i915_gem_execbuffer_reserve(ring, file, &objects); if (ret) goto err; /* The objects are in their final locations, apply the relocations. */ - ret = i915_gem_execbuffer_relocate(dev, eb, &objects, exec); + ret = i915_gem_execbuffer_relocate(dev, eb, &objects); if (ret) { if (ret == -EFAULT) { ret = i915_gem_execbuffer_relocate_slow(dev, file, ring, @@ -1133,9 +1156,6 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } /* Set the pending read domains for the batch buffer to COMMAND */ - batch_obj = list_entry(objects.prev, - struct drm_i915_gem_object, - exec_list); if (batch_obj->base.pending_write_domain) { DRM_ERROR("Attempting to use self-modifying batch buffer\n"); ret = -EINVAL; -- cgit v0.10.2 From a46f3108b1cd8bf11d46ac8a5f30df6f6dbdf738 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 12 Jan 2011 11:38:37 +1000 Subject: i915/gtt: fix ordering issues with status setup and DMAR This code was setting up the status page before setting the DMAR-is-on-bit, so we were getting DMAR errors on the status page. Reverse the two bits of init code to the correct result. Signed-off-by: Dave Airlie diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index da81618..b7c0c7e 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -688,14 +688,14 @@ static int intel_gtt_init(void) intel_private.base.stolen_size = intel_gtt_stolen_size(); + intel_private.base.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2; + ret = intel_gtt_setup_scratch_page(); if (ret != 0) { intel_gtt_cleanup(); return ret; } - intel_private.base.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2; - return 0; } -- cgit v0.10.2 From d15eda5c6edff4987af6f4423af0bab0c3251e74 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 12 Jan 2011 11:39:48 +1000 Subject: i915/gtt: fix ordering causing DMAR errors on object teardown. Previous to the last GTT rework we always rewrote the GTT then unmapped the object, somehow this got reversed in the rework in 2.6.37-rc5 timeframe. This fix needs to go to stable in an alternate form since the code changed. This fixes DMAR reports on my Ironlake HP2540p. Signed-off-by: Dave Airlie diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index b7c0c7e..e921b69 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -1000,14 +1000,14 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem, if (mem->page_count == 0) return 0; + intel_gtt_clear_range(pg_start, mem->page_count); + if (intel_private.base.needs_dmar) { intel_gtt_unmap_memory(mem->sg_list, mem->num_sg); mem->sg_list = NULL; mem->num_sg = 0; } - intel_gtt_clear_range(pg_start, mem->page_count); - return 0; } -- cgit v0.10.2 From 103a8fea9b420d5faef43bb87332a28e2129816a Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 22 Oct 2010 23:53:03 -0400 Subject: tools: create power/x86/turbostat turbostat is a Linux tool to observe proper operation of Intel(R) Turbo Boost Technology. turbostat displays the actual processor frequency on x86 processors that include APERF and MPERF MSRs. Note that turbostat is of limited utility on Linux kernels 2.6.29 and older, as acpi_cpufreq cleared APERF/MPERF up through that release. On Intel Core i3/i5/i7 (Nehalem) and newer processors, turbostat also displays residency in idle power saving states, which are necessary for diagnosing any cpuidle issues that may have an effect on turbo-mode. See the turbostat.8 man page for example usage. Signed-off-by: Len Brown diff --git a/tools/power/x86/turbostat/Makefile b/tools/power/x86/turbostat/Makefile new file mode 100644 index 0000000..fd8e1f1 --- /dev/null +++ b/tools/power/x86/turbostat/Makefile @@ -0,0 +1,8 @@ +turbostat : turbostat.c + +clean : + rm -f turbostat + +install : + install turbostat /usr/bin/turbostat + install turbostat.8 /usr/share/man/man8 diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 new file mode 100644 index 0000000..ff75125 --- /dev/null +++ b/tools/power/x86/turbostat/turbostat.8 @@ -0,0 +1,172 @@ +.TH TURBOSTAT 8 +.SH NAME +turbostat \- Report processor frequency and idle statistics +.SH SYNOPSIS +.ft B +.B turbostat +.RB [ "\-v" ] +.RB [ "\-M MSR#" ] +.RB command +.br +.B turbostat +.RB [ "\-v" ] +.RB [ "\-M MSR#" ] +.RB [ "\-i interval_sec" ] +.SH DESCRIPTION +\fBturbostat \fP reports processor topology, frequency +and idle power state statistics on modern X86 processors. +Either \fBcommand\fP is forked and statistics are printed +upon its completion, or statistics are printed periodically. + +\fBturbostat \fP +requires that the processor +supports an "invariant" TSC, plus the APERF and MPERF MSRs. +\fBturbostat \fP will report idle cpu power state residency +on processors that additionally support C-state residency counters. + +.SS Options +The \fB-v\fP option increases verbosity. +.PP +The \fB-M MSR#\fP option dumps the specified MSR, +in addition to the usual frequency and idle statistics. +.PP +The \fB-i interval_sec\fP option prints statistics every \fiinterval_sec\fP seconds. +The default is 5 seconds. +.PP +The \fBcommand\fP parameter forks \fBcommand\fP and upon its exit, +displays the statistics gathered since it was forked. +.PP +.SH FIELD DESCRIPTIONS +.nf +\fBpkg\fP processor package number. +\fBcore\fP processor core number. +\fBCPU\fP Linux CPU (logical processor) number. +\fB%c0\fP percent of the interval that the CPU retired instructions. +\fBGHz\fP average clock rate while the CPU was in c0 state. +\fBTSC\fP average GHz that the TSC ran during the entire interval. +\fB%c1, %c3, %c6\fP show the percentage residency in hardware core idle states. +\fB%pc3, %pc6\fP percentage residency in hardware package idle states. +.fi +.PP +.SH EXAMPLE +Without any parameters, turbostat prints out counters ever 5 seconds. +(override interval with "-i sec" option, or specify a command +for turbostat to fork). + +The first row of statistics reflect the average for the entire system. +Subsequent rows show per-CPU statistics. + +.nf +[root@x980]# ./turbostat +core CPU %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6 + 0.04 1.62 3.38 0.11 0.00 99.85 0.00 95.07 + 0 0 0.04 1.62 3.38 0.06 0.00 99.90 0.00 95.07 + 0 6 0.02 1.62 3.38 0.08 0.00 99.90 0.00 95.07 + 1 2 0.10 1.62 3.38 0.29 0.00 99.61 0.00 95.07 + 1 8 0.11 1.62 3.38 0.28 0.00 99.61 0.00 95.07 + 2 4 0.01 1.62 3.38 0.01 0.00 99.98 0.00 95.07 + 2 10 0.01 1.61 3.38 0.02 0.00 99.98 0.00 95.07 + 8 1 0.07 1.62 3.38 0.15 0.00 99.78 0.00 95.07 + 8 7 0.03 1.62 3.38 0.19 0.00 99.78 0.00 95.07 + 9 3 0.01 1.62 3.38 0.02 0.00 99.98 0.00 95.07 + 9 9 0.01 1.62 3.38 0.02 0.00 99.98 0.00 95.07 + 10 5 0.01 1.62 3.38 0.13 0.00 99.86 0.00 95.07 + 10 11 0.08 1.62 3.38 0.05 0.00 99.86 0.00 95.07 +.fi +.SH VERBOSE EXAMPLE +The "-v" option adds verbosity to the output: + +.nf +GenuineIntel 11 CPUID levels; family:model:stepping 0x6:2c:2 (6:44:2) +12 * 133 = 1600 MHz max efficiency +25 * 133 = 3333 MHz TSC frequency +26 * 133 = 3467 MHz max turbo 4 active cores +26 * 133 = 3467 MHz max turbo 3 active cores +27 * 133 = 3600 MHz max turbo 2 active cores +27 * 133 = 3600 MHz max turbo 1 active cores + +.fi +The \fBmax efficiency\fP frequency, a.k.a. Low Frequency Mode, is the frequency +available at the minimum package voltage. The \fBTSC frequency\fP is the nominal +maximum frequency of the processor if turbo-mode were not available. This frequency +should be sustainable on all CPUs indefinitely, given nominal power and cooling. +The remaining rows show what maximum turbo frequency is possible +depending on the number of idle cores. Note that this information is +not available on all processors. +.SH FORK EXAMPLE +If turbostat is invoked with a command, it will fork that command +and output the statistics gathered when the command exits. +eg. Here a cycle soaker is run on 1 CPU (see %c0) for a few seconds +until ^C while the other CPUs are mostly idle: + +.nf +[root@x980 lenb]# ./turbostat cat /dev/zero > /dev/null + +^Ccore CPU %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6 + 8.49 3.63 3.38 16.23 0.66 74.63 0.00 0.00 + 0 0 1.22 3.62 3.38 32.18 0.00 66.60 0.00 0.00 + 0 6 0.40 3.61 3.38 33.00 0.00 66.60 0.00 0.00 + 1 2 0.11 3.14 3.38 0.19 3.95 95.75 0.00 0.00 + 1 8 0.05 2.88 3.38 0.25 3.95 95.75 0.00 0.00 + 2 4 0.00 3.13 3.38 0.02 0.00 99.98 0.00 0.00 + 2 10 0.00 3.09 3.38 0.02 0.00 99.98 0.00 0.00 + 8 1 0.04 3.50 3.38 14.43 0.00 85.54 0.00 0.00 + 8 7 0.03 2.98 3.38 14.43 0.00 85.54 0.00 0.00 + 9 3 0.00 3.16 3.38 100.00 0.00 0.00 0.00 0.00 + 9 9 99.93 3.63 3.38 0.06 0.00 0.00 0.00 0.00 + 10 5 0.01 2.82 3.38 0.08 0.00 99.91 0.00 0.00 + 10 11 0.02 3.36 3.38 0.06 0.00 99.91 0.00 0.00 +6.950866 sec + +.fi +Above the cycle soaker drives cpu9 up 3.6 Ghz turbo limit +while the other processors are generally in various states of idle. + +Note that cpu3 is an HT sibling sharing core9 +with cpu9, and thus it is unable to get to an idle state +deeper than c1 while cpu9 is busy. + +Note that turbostat reports average GHz of 3.61, while +the arithmetic average of the GHz column above is 3.24. +This is a weighted average, where the weight is %c0. ie. it is the total number of +un-halted cycles elapsed per time divided by the number of CPUs. +.SH NOTES + +.B "turbostat " +must be run as root. + +.B "turbostat " +reads hardware counters, but doesn't write them. +So it will not interfere with the OS or other programs, including +multiple invocations of itself. + +\fBturbostat \fP +may work poorly on Linux-2.6.20 through 2.6.29, +as \fBacpi-cpufreq \fPperiodically cleared the APERF and MPERF +in those kernels. + +The APERF, MPERF MSRs are defined to count non-halted cycles. +Although it is not guaranteed by the architecture, turbostat assumes +that they count at TSC rate, which is true on all processors tested to date. + +.SH REFERENCES +"Intel® Turbo Boost Technology +in Intel® Coreâ„¢ Microarchitecture (Nehalem) Based Processors" +http://download.intel.com/design/processor/applnots/320354.pdf + +"Intel® 64 and IA-32 Architectures Software Developer's Manual +Volume 3B: System Programming Guide" +http://www.intel.com/products/processor/manuals/ + +.SH FILES +.ta +.nf +/dev/cpu/*/msr +.fi + +.SH "SEE ALSO" +msr(4), vmstat(8) +.PP +.SH AUTHORS +.nf +Written by Len Brown diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c new file mode 100644 index 0000000..4c6983d --- /dev/null +++ b/tools/power/x86/turbostat/turbostat.c @@ -0,0 +1,1048 @@ +/* + * turbostat -- show CPU frequency and C-state residency + * on modern Intel turbo-capable processors. + * + * Copyright (c) 2010, Intel Corporation. + * Len Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSR_TSC 0x10 +#define MSR_NEHALEM_PLATFORM_INFO 0xCE +#define MSR_NEHALEM_TURBO_RATIO_LIMIT 0x1AD +#define MSR_APERF 0xE8 +#define MSR_MPERF 0xE7 +#define MSR_PKG_C2_RESIDENCY 0x60D /* SNB only */ +#define MSR_PKG_C3_RESIDENCY 0x3F8 +#define MSR_PKG_C6_RESIDENCY 0x3F9 +#define MSR_PKG_C7_RESIDENCY 0x3FA /* SNB only */ +#define MSR_CORE_C3_RESIDENCY 0x3FC +#define MSR_CORE_C6_RESIDENCY 0x3FD +#define MSR_CORE_C7_RESIDENCY 0x3FE /* SNB only */ + +char *proc_stat = "/proc/stat"; +unsigned int interval_sec = 5; /* set with -i interval_sec */ +unsigned int verbose; /* set with -v */ +unsigned int skip_c0; +unsigned int skip_c1; +unsigned int do_nhm_cstates; +unsigned int do_snb_cstates; +unsigned int has_aperf; +unsigned int units = 1000000000; /* Ghz etc */ +unsigned int genuine_intel; +unsigned int has_invariant_tsc; +unsigned int do_nehalem_platform_info; +unsigned int do_nehalem_turbo_ratio_limit; +unsigned int extra_msr_offset; +double bclk; +unsigned int show_pkg; +unsigned int show_core; +unsigned int show_cpu; + +int aperf_mperf_unstable; +int backwards_count; +char *progname; +int need_reinitialize; + +int num_cpus; + +typedef struct per_cpu_counters { + unsigned long long tsc; /* per thread */ + unsigned long long aperf; /* per thread */ + unsigned long long mperf; /* per thread */ + unsigned long long c1; /* per thread (calculated) */ + unsigned long long c3; /* per core */ + unsigned long long c6; /* per core */ + unsigned long long c7; /* per core */ + unsigned long long pc2; /* per package */ + unsigned long long pc3; /* per package */ + unsigned long long pc6; /* per package */ + unsigned long long pc7; /* per package */ + unsigned long long extra_msr; /* per thread */ + int pkg; + int core; + int cpu; + struct per_cpu_counters *next; +} PCC; + +PCC *pcc_even; +PCC *pcc_odd; +PCC *pcc_delta; +PCC *pcc_average; +struct timeval tv_even; +struct timeval tv_odd; +struct timeval tv_delta; + +unsigned long long get_msr(int cpu, off_t offset) +{ + ssize_t retval; + unsigned long long msr; + char pathname[32]; + int fd; + + sprintf(pathname, "/dev/cpu/%d/msr", cpu); + fd = open(pathname, O_RDONLY); + if (fd < 0) { + perror(pathname); + need_reinitialize = 1; + return 0; + } + + retval = pread(fd, &msr, sizeof msr, offset); + if (retval != sizeof msr) { + fprintf(stderr, "cpu%d pread(..., 0x%zx) = %jd\n", + cpu, offset, retval); + exit(-2); + } + + close(fd); + return msr; +} + +void print_header() +{ + if (show_pkg) + fprintf(stderr, "pkg "); + if (show_core) + fprintf(stderr, "core"); + if (show_cpu) + fprintf(stderr, " CPU"); + if (do_nhm_cstates) + fprintf(stderr, " %%c0 "); + if (has_aperf) + fprintf(stderr, " GHz"); + fprintf(stderr, " TSC"); + if (do_nhm_cstates) + fprintf(stderr, " %%c1 "); + if (do_nhm_cstates) + fprintf(stderr, " %%c3 "); + if (do_nhm_cstates) + fprintf(stderr, " %%c6 "); + if (do_snb_cstates) + fprintf(stderr, " %%c7 "); + if (do_snb_cstates) + fprintf(stderr, " %%pc2 "); + if (do_nhm_cstates) + fprintf(stderr, " %%pc3 "); + if (do_nhm_cstates) + fprintf(stderr, " %%pc6 "); + if (do_snb_cstates) + fprintf(stderr, " %%pc7 "); + if (extra_msr_offset) + fprintf(stderr, " MSR 0x%x ", extra_msr_offset); + + putc('\n', stderr); +} + +void dump_pcc(PCC *pcc) +{ + fprintf(stderr, "package: %d ", pcc->pkg); + fprintf(stderr, "core:: %d ", pcc->core); + fprintf(stderr, "CPU: %d ", pcc->cpu); + fprintf(stderr, "TSC: %016llX\n", pcc->tsc); + fprintf(stderr, "c3: %016llX\n", pcc->c3); + fprintf(stderr, "c6: %016llX\n", pcc->c6); + fprintf(stderr, "c7: %016llX\n", pcc->c7); + fprintf(stderr, "aperf: %016llX\n", pcc->aperf); + fprintf(stderr, "pc2: %016llX\n", pcc->pc2); + fprintf(stderr, "pc3: %016llX\n", pcc->pc3); + fprintf(stderr, "pc6: %016llX\n", pcc->pc6); + fprintf(stderr, "pc7: %016llX\n", pcc->pc7); + fprintf(stderr, "msr0x%x: %016llX\n", extra_msr_offset, pcc->extra_msr); +} + +void dump_list(PCC *pcc) +{ + printf("dump_list 0x%p\n", pcc); + + for (; pcc; pcc = pcc->next) + dump_pcc(pcc); +} + +void print_pcc(PCC *p) +{ + double interval_float; + + interval_float = tv_delta.tv_sec + tv_delta.tv_usec/1000000.0; + + /* topology columns, print blanks on 1st (average) line */ + if (p == pcc_average) { + if (show_pkg) + fprintf(stderr, " "); + if (show_core) + fprintf(stderr, " "); + if (show_cpu) + fprintf(stderr, " "); + } else { + if (show_pkg) + fprintf(stderr, "%4d", p->pkg); + if (show_core) + fprintf(stderr, "%4d", p->core); + if (show_cpu) + fprintf(stderr, "%4d", p->cpu); + } + + /* %c0 */ + if (do_nhm_cstates) { + if (!skip_c0) + fprintf(stderr, "%7.2f", 100.0 * p->mperf/p->tsc); + else + fprintf(stderr, " ****"); + } + + /* GHz */ + if (has_aperf) { + if (!aperf_mperf_unstable) { + fprintf(stderr, "%5.2f", + 1.0 * p->tsc / units * p->aperf / + p->mperf / interval_float); + } else { + if (p->aperf > p->tsc || p->mperf > p->tsc) { + fprintf(stderr, " ****"); + } else { + fprintf(stderr, "%4.1f*", + 1.0 * p->tsc / + units * p->aperf / + p->mperf / interval_float); + } + } + } + + /* TSC */ + fprintf(stderr, "%5.2f", 1.0 * p->tsc/units/interval_float); + + if (do_nhm_cstates) { + if (!skip_c1) + fprintf(stderr, "%7.2f", 100.0 * p->c1/p->tsc); + else + fprintf(stderr, " ****"); + } + if (do_nhm_cstates) + fprintf(stderr, "%7.2f", 100.0 * p->c3/p->tsc); + if (do_nhm_cstates) + fprintf(stderr, "%7.2f", 100.0 * p->c6/p->tsc); + if (do_snb_cstates) + fprintf(stderr, "%7.2f", 100.0 * p->c7/p->tsc); + if (do_snb_cstates) + fprintf(stderr, "%7.2f", 100.0 * p->pc2/p->tsc); + if (do_nhm_cstates) + fprintf(stderr, "%7.2f", 100.0 * p->pc3/p->tsc); + if (do_nhm_cstates) + fprintf(stderr, "%7.2f", 100.0 * p->pc6/p->tsc); + if (do_snb_cstates) + fprintf(stderr, "%7.2f", 100.0 * p->pc7/p->tsc); + if (extra_msr_offset) + fprintf(stderr, " 0x%016llx", p->extra_msr); + putc('\n', stderr); +} + +void print_counters(PCC *cnt) +{ + PCC *pcc; + + print_header(); + + if (num_cpus > 1) + print_pcc(pcc_average); + + for (pcc = cnt; pcc != NULL; pcc = pcc->next) + print_pcc(pcc); + +} + +#define SUBTRACT_COUNTER(after, before, delta) (delta = (after - before), (before > after)) + + +int compute_delta(PCC *after, PCC *before, PCC *delta) +{ + int errors = 0; + int perf_err = 0; + + skip_c0 = skip_c1 = 0; + + for ( ; after && before && delta; + after = after->next, before = before->next, delta = delta->next) { + if (before->cpu != after->cpu) { + printf("cpu configuration changed: %d != %d\n", + before->cpu, after->cpu); + return -1; + } + + if (SUBTRACT_COUNTER(after->tsc, before->tsc, delta->tsc)) { + fprintf(stderr, "cpu%d TSC went backwards %llX to %llX\n", + before->cpu, before->tsc, after->tsc); + errors++; + } + /* check for TSC < 1 Mcycles over interval */ + if (delta->tsc < (1000 * 1000)) { + fprintf(stderr, "Insanely slow TSC rate," + " TSC stops in idle?\n"); + fprintf(stderr, "You can disable all c-states" + " by booting with \"idle=poll\"\n"); + fprintf(stderr, "or just the deep ones with" + " \"processor.max_cstate=1\"\n"); + exit(-3); + } + if (SUBTRACT_COUNTER(after->c3, before->c3, delta->c3)) { + fprintf(stderr, "cpu%d c3 counter went backwards %llX to %llX\n", + before->cpu, before->c3, after->c3); + errors++; + } + if (SUBTRACT_COUNTER(after->c6, before->c6, delta->c6)) { + fprintf(stderr, "cpu%d c6 counter went backwards %llX to %llX\n", + before->cpu, before->c6, after->c6); + errors++; + } + if (SUBTRACT_COUNTER(after->c7, before->c7, delta->c7)) { + fprintf(stderr, "cpu%d c7 counter went backwards %llX to %llX\n", + before->cpu, before->c7, after->c7); + errors++; + } + if (SUBTRACT_COUNTER(after->pc2, before->pc2, delta->pc2)) { + fprintf(stderr, "cpu%d pc2 counter went backwards %llX to %llX\n", + before->cpu, before->pc2, after->pc2); + errors++; + } + if (SUBTRACT_COUNTER(after->pc3, before->pc3, delta->pc3)) { + fprintf(stderr, "cpu%d pc3 counter went backwards %llX to %llX\n", + before->cpu, before->pc3, after->pc3); + errors++; + } + if (SUBTRACT_COUNTER(after->pc6, before->pc6, delta->pc6)) { + fprintf(stderr, "cpu%d pc6 counter went backwards %llX to %llX\n", + before->cpu, before->pc6, after->pc6); + errors++; + } + if (SUBTRACT_COUNTER(after->pc7, before->pc7, delta->pc7)) { + fprintf(stderr, "cpu%d pc7 counter went backwards %llX to %llX\n", + before->cpu, before->pc7, after->pc7); + errors++; + } + + perf_err = SUBTRACT_COUNTER(after->aperf, before->aperf, delta->aperf); + if (perf_err) { + fprintf(stderr, "cpu%d aperf counter went backwards %llX to %llX\n", + before->cpu, before->aperf, after->aperf); + } + perf_err |= SUBTRACT_COUNTER(after->mperf, before->mperf, delta->mperf); + if (perf_err) { + fprintf(stderr, "cpu%d mperf counter went backwards %llX to %llX\n", + before->cpu, before->mperf, after->mperf); + } + if (perf_err) { + if (!aperf_mperf_unstable) { + fprintf(stderr, "%s: APERF or MPERF went backwards *\n", progname); + fprintf(stderr, "* Frequency results do not cover entire interval *\n"); + fprintf(stderr, "* fix this by running Linux-2.6.30 or later *\n"); + + aperf_mperf_unstable = 1; + } + /* + * mperf delta is likely a huge "positive" number + * can not use it for calculating c0 time + */ + skip_c0 = 1; + skip_c1 = 1; + } + + /* + * As mperf and tsc collection are not atomic, + * it is possible for mperf's non-halted cycles + * to exceed TSC's all cycles: show c1 = 0% in that case. + */ + if (delta->mperf > delta->tsc) + delta->c1 = 0; + else /* normal case, derive c1 */ + delta->c1 = delta->tsc - delta->mperf + - delta->c3 - delta->c6 - delta->c7; + + if (delta->mperf == 0) + delta->mperf = 1; /* divide by 0 protection */ + + /* + * for "extra msr", just copy the latest w/o subtracting + */ + delta->extra_msr = after->extra_msr; + if (errors) { + fprintf(stderr, "ERROR cpu%d before:\n", before->cpu); + dump_pcc(before); + fprintf(stderr, "ERROR cpu%d after:\n", before->cpu); + dump_pcc(after); + errors = 0; + } + } + return 0; +} + +void compute_average(PCC *delta, PCC *avg) +{ + PCC *sum; + + sum = calloc(1, sizeof(PCC)); + if (sum == NULL) { + perror("calloc sum"); + exit(1); + } + + for (; delta; delta = delta->next) { + sum->tsc += delta->tsc; + sum->c1 += delta->c1; + sum->c3 += delta->c3; + sum->c6 += delta->c6; + sum->c7 += delta->c7; + sum->aperf += delta->aperf; + sum->mperf += delta->mperf; + sum->pc2 += delta->pc2; + sum->pc3 += delta->pc3; + sum->pc6 += delta->pc6; + sum->pc7 += delta->pc7; + } + avg->tsc = sum->tsc/num_cpus; + avg->c1 = sum->c1/num_cpus; + avg->c3 = sum->c3/num_cpus; + avg->c6 = sum->c6/num_cpus; + avg->c7 = sum->c7/num_cpus; + avg->aperf = sum->aperf/num_cpus; + avg->mperf = sum->mperf/num_cpus; + avg->pc2 = sum->pc2/num_cpus; + avg->pc3 = sum->pc3/num_cpus; + avg->pc6 = sum->pc6/num_cpus; + avg->pc7 = sum->pc7/num_cpus; + + free(sum); +} + +void get_counters(PCC *pcc) +{ + for ( ; pcc; pcc = pcc->next) { + pcc->tsc = get_msr(pcc->cpu, MSR_TSC); + if (do_nhm_cstates) + pcc->c3 = get_msr(pcc->cpu, MSR_CORE_C3_RESIDENCY); + if (do_nhm_cstates) + pcc->c6 = get_msr(pcc->cpu, MSR_CORE_C6_RESIDENCY); + if (do_snb_cstates) + pcc->c7 = get_msr(pcc->cpu, MSR_CORE_C7_RESIDENCY); + if (has_aperf) + pcc->aperf = get_msr(pcc->cpu, MSR_APERF); + if (has_aperf) + pcc->mperf = get_msr(pcc->cpu, MSR_MPERF); + if (do_snb_cstates) + pcc->pc2 = get_msr(pcc->cpu, MSR_PKG_C2_RESIDENCY); + if (do_nhm_cstates) + pcc->pc3 = get_msr(pcc->cpu, MSR_PKG_C3_RESIDENCY); + if (do_nhm_cstates) + pcc->pc6 = get_msr(pcc->cpu, MSR_PKG_C6_RESIDENCY); + if (do_snb_cstates) + pcc->pc7 = get_msr(pcc->cpu, MSR_PKG_C7_RESIDENCY); + if (extra_msr_offset) + pcc->extra_msr = get_msr(pcc->cpu, extra_msr_offset); + } +} + + +void print_nehalem_info() +{ + unsigned long long msr; + unsigned int ratio; + + if (!do_nehalem_platform_info) + return; + + msr = get_msr(0, MSR_NEHALEM_PLATFORM_INFO); + + ratio = (msr >> 40) & 0xFF; + fprintf(stderr, "%d * %.0f = %.0f MHz max efficiency\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 8) & 0xFF; + fprintf(stderr, "%d * %.0f = %.0f MHz TSC frequency\n", + ratio, bclk, ratio * bclk); + + if (verbose > 1) + fprintf(stderr, "MSR_NEHALEM_PLATFORM_INFO: 0x%llx\n", msr); + + if (!do_nehalem_turbo_ratio_limit) + return; + + msr = get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT); + + ratio = (msr >> 24) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 4 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 16) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 3 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 8) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 2 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 0) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 1 active cores\n", + ratio, bclk, ratio * bclk); + +} + +void free_counter_list(PCC *list) +{ + PCC *p; + + for (p = list; p; ) { + PCC *free_me; + + free_me = p; + p = p->next; + free(free_me); + } + return; +} + +void free_all_counters(void) +{ + free_counter_list(pcc_even); + pcc_even = NULL; + + free_counter_list(pcc_odd); + pcc_odd = NULL; + + free_counter_list(pcc_delta); + pcc_delta = NULL; + + free_counter_list(pcc_average); + pcc_average = NULL; +} + +void insert_cpu_counters(PCC **list, PCC *new) +{ + PCC *prev; + + /* + * list was empty + */ + if (*list == NULL) { + new->next = *list; + *list = new; + return; + } + + show_cpu = 1; /* there is more than one CPU */ + + /* + * insert on front of list. + * It is sorted by ascending package#, core#, cpu# + */ + if (((*list)->pkg > new->pkg) || + (((*list)->pkg == new->pkg) && ((*list)->core > new->core)) || + (((*list)->pkg == new->pkg) && ((*list)->core == new->core) && ((*list)->cpu > new->cpu))) { + new->next = *list; + *list = new; + return; + } + + prev = *list; + + while (prev->next && (prev->next->pkg < new->pkg)) { + prev = prev->next; + show_pkg = 1; /* there is more than 1 package */ + } + + while (prev->next && (prev->next->pkg == new->pkg) + && (prev->next->core < new->core)) { + prev = prev->next; + show_core = 1; /* there is more than 1 core */ + } + + while (prev->next && (prev->next->pkg == new->pkg) + && (prev->next->core == new->core) + && (prev->next->cpu < new->cpu)) { + prev = prev->next; + } + + /* + * insert after "prev" + */ + new->next = prev->next; + prev->next = new; + + return; +} + +void alloc_new_cpu_counters(int pkg, int core, int cpu) +{ + PCC *new; + + if (verbose > 1) + printf("pkg%d core%d, cpu%d\n", pkg, core, cpu); + + new = (PCC *)calloc(1, sizeof(PCC)); + if (new == NULL) { + perror("calloc"); + exit(1); + } + new->pkg = pkg; + new->core = core; + new->cpu = cpu; + insert_cpu_counters(&pcc_odd, new); + + new = (PCC *)calloc(1, sizeof(PCC)); + if (new == NULL) { + perror("calloc"); + exit(1); + } + new->pkg = pkg; + new->core = core; + new->cpu = cpu; + insert_cpu_counters(&pcc_even, new); + + new = (PCC *)calloc(1, sizeof(PCC)); + if (new == NULL) { + perror("calloc"); + exit(1); + } + new->pkg = pkg; + new->core = core; + new->cpu = cpu; + insert_cpu_counters(&pcc_delta, new); + + new = (PCC *)calloc(1, sizeof(PCC)); + if (new == NULL) { + perror("calloc"); + exit(1); + } + new->pkg = pkg; + new->core = core; + new->cpu = cpu; + pcc_average = new; +} + +int get_physical_package_id(int cpu) +{ + char path[64]; + FILE *filep; + int pkg; + + sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/physical_package_id", cpu); + filep = fopen(path, "r"); + if (filep == NULL) { + perror(path); + exit(1); + } + fscanf(filep, "%d", &pkg); + fclose(filep); + return pkg; +} + +int get_core_id(int cpu) +{ + char path[64]; + FILE *filep; + int core; + + sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/core_id", cpu); + filep = fopen(path, "r"); + if (filep == NULL) { + perror(path); + exit(1); + } + fscanf(filep, "%d", &core); + fclose(filep); + return core; +} + +/* + * run func(index, cpu) on every cpu in /proc/stat + */ + +int for_all_cpus(void (func)(int, int, int)) +{ + FILE *fp; + int cpu_count; + int retval; + + fp = fopen(proc_stat, "r"); + if (fp == NULL) { + perror(proc_stat); + exit(1); + } + + retval = fscanf(fp, "cpu %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n"); + if (retval != 0) { + perror("/proc/stat format"); + exit(1); + } + + for (cpu_count = 0; ; cpu_count++) { + int cpu; + + retval = fscanf(fp, "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n", &cpu); + if (retval != 1) + break; + + func(get_physical_package_id(cpu), get_core_id(cpu), cpu); + } + fclose(fp); + return cpu_count; +} + +void re_initialize(void) +{ + printf("turbostat: topology changed, re-initializing.\n"); + free_all_counters(); + num_cpus = for_all_cpus(alloc_new_cpu_counters); + need_reinitialize = 0; + printf("num_cpus is now %d\n", num_cpus); +} + +void dummy(int pkg, int core, int cpu) { return; } +/* + * check to see if a cpu came on-line + */ +void verify_num_cpus() +{ + int new_num_cpus; + + new_num_cpus = for_all_cpus(dummy); + + if (new_num_cpus != num_cpus) { + if (verbose) + printf("num_cpus was %d, is now %d\n", + num_cpus, new_num_cpus); + need_reinitialize = 1; + } + + return; +} + +void turbostat_loop() +{ +restart: + get_counters(pcc_even); + gettimeofday(&tv_even, (struct timezone *)NULL); + + while (1) { + verify_num_cpus(); + if (need_reinitialize) { + re_initialize(); + goto restart; + } + sleep(interval_sec); + get_counters(pcc_odd); + gettimeofday(&tv_odd, (struct timezone *)NULL); + + compute_delta(pcc_odd, pcc_even, pcc_delta); + timersub(&tv_odd, &tv_even, &tv_delta); + compute_average(pcc_delta, pcc_average); + print_counters(pcc_delta); + if (need_reinitialize) { + re_initialize(); + goto restart; + } + sleep(interval_sec); + get_counters(pcc_even); + gettimeofday(&tv_even, (struct timezone *)NULL); + compute_delta(pcc_even, pcc_odd, pcc_delta); + timersub(&tv_even, &tv_odd, &tv_delta); + compute_average(pcc_delta, pcc_average); + print_counters(pcc_delta); + } +} + +void check_dev_msr() +{ + struct stat sb; + + if (stat("/dev/cpu/0/msr", &sb)) { + fprintf(stderr, "no /dev/cpu/0/msr\n"); + fprintf(stderr, "Try \"# modprobe msr\"\n"); + exit(-5); + } +} + +void check_super_user() +{ + if (getuid() != 0) { + fprintf(stderr, "must be root\n"); + exit(-6); + } +} + +int has_nehalem_turbo_ratio_limit(unsigned int family, unsigned int model) +{ + if (!genuine_intel) + return 0; + + if (family != 6) + return 0; + + switch (model) { + case 0x1A: /* Core i7, Xeon 5500 series - Bloomfield, Gainstown NHM-EP */ + case 0x1E: /* Core i7 and i5 Processor - Clarksfield, Lynnfield, Jasper Forest */ + case 0x1F: /* Core i7 and i5 Processor - Nehalem */ + case 0x25: /* Westmere Client - Clarkdale, Arrandale */ + case 0x2C: /* Westmere EP - Gulftown */ + case 0x2A: /* SNB */ + case 0x2D: /* SNB Xeon */ + return 1; + case 0x2E: /* Nehalem-EX Xeon - Beckton */ + case 0x2F: /* Westmere-EX Xeon - Eagleton */ + default: + return 0; + } +} + +int is_snb(unsigned int family, unsigned int model) +{ + if (!genuine_intel) + return 0; + + switch (model) { + case 0x2A: + case 0x2D: + return 1; + } + return 0; +} + +double discover_bclk(unsigned int family, unsigned int model) +{ + if (is_snb(family, model)) + return 100.00; + else + return 133.33; +} + +void check_cpuid() +{ + unsigned int eax, ebx, ecx, edx, max_level; + unsigned int fms, family, model, stepping; + + eax = ebx = ecx = edx = 0; + + asm("cpuid" : "=a" (max_level), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0)); + + if (ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e) + genuine_intel = 1; + + if (verbose) + fprintf(stderr, "%.4s%.4s%.4s ", + (char *)&ebx, (char *)&edx, (char *)&ecx); + + asm("cpuid" : "=a" (fms), "=c" (ecx), "=d" (edx) : "a" (1) : "ebx"); + family = (fms >> 8) & 0xf; + model = (fms >> 4) & 0xf; + stepping = fms & 0xf; + if (family == 6 || family == 0xf) + model += ((fms >> 16) & 0xf) << 4; + + if (verbose) + fprintf(stderr, "%d CPUID levels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n", + max_level, family, model, stepping, family, model, stepping); + + if (!(edx & (1 << 5))) { + fprintf(stderr, "CPUID: no MSR\n"); + exit(1); + } + + /* + * check max extended function levels of CPUID. + * This is needed to check for invariant TSC. + * This check is valid for both Intel and AMD. + */ + ebx = ecx = edx = 0; + asm("cpuid" : "=a" (max_level), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x80000000)); + + if (max_level < 0x80000007) { + fprintf(stderr, "CPUID: no invariant TSC (max_level 0x%x)\n", max_level); + exit(1); + } + + /* + * Non-Stop TSC is advertised by CPUID.EAX=0x80000007: EDX.bit8 + * this check is valid for both Intel and AMD + */ + asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x80000007)); + has_invariant_tsc = edx && (1 << 8); + + if (!has_invariant_tsc) { + fprintf(stderr, "No invariant TSC\n"); + exit(1); + } + + /* + * APERF/MPERF is advertised by CPUID.EAX=0x6: ECX.bit0 + * this check is valid for both Intel and AMD + */ + + asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x6)); + has_aperf = ecx && (1 << 0); + if (!has_aperf) { + fprintf(stderr, "No APERF MSR\n"); + exit(1); + } + + do_nehalem_platform_info = genuine_intel && has_invariant_tsc; + do_nhm_cstates = genuine_intel; /* all Intel w/ non-stop TSC have NHM counters */ + do_snb_cstates = is_snb(family, model); + bclk = discover_bclk(family, model); + + do_nehalem_turbo_ratio_limit = has_nehalem_turbo_ratio_limit(family, model); +} + + +void usage() +{ + fprintf(stderr, "%s: [-v] [-M MSR#] [-i interval_sec | command ...]\n", + progname); + exit(1); +} + + +/* + * in /dev/cpu/ return success for names that are numbers + * ie. filter out ".", "..", "microcode". + */ +int dir_filter(const struct dirent *dirp) +{ + if (isdigit(dirp->d_name[0])) + return 1; + else + return 0; +} + +int open_dev_cpu_msr(int dummy1) +{ + return 0; +} + +void turbostat_init() +{ + check_cpuid(); + + check_dev_msr(); + check_super_user(); + + num_cpus = for_all_cpus(alloc_new_cpu_counters); + + if (verbose) + print_nehalem_info(); +} + +int fork_it(char **argv) +{ + int retval; + pid_t child_pid; + get_counters(pcc_even); + gettimeofday(&tv_even, (struct timezone *)NULL); + + child_pid = fork(); + if (!child_pid) { + /* child */ + execvp(argv[0], argv); + } else { + int status; + + /* parent */ + if (child_pid == -1) { + perror("fork"); + exit(1); + } + + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + if (waitpid(child_pid, &status, 0) == -1) { + perror("wait"); + exit(1); + } + } + get_counters(pcc_odd); + gettimeofday(&tv_odd, (struct timezone *)NULL); + retval = compute_delta(pcc_odd, pcc_even, pcc_delta); + + timersub(&tv_odd, &tv_even, &tv_delta); + compute_average(pcc_delta, pcc_average); + if (!retval) + print_counters(pcc_delta); + + fprintf(stderr, "%.6f sec\n", tv_delta.tv_sec + tv_delta.tv_usec/1000000.0);; + + return 0; +} + +void cmdline(int argc, char **argv) +{ + int opt; + + progname = argv[0]; + + while ((opt = getopt(argc, argv, "+vi:M:")) != -1) { + switch (opt) { + case 'v': + verbose++; + break; + case 'i': + interval_sec = atoi(optarg); + break; + case 'M': + sscanf(optarg, "%x", &extra_msr_offset); + if (verbose > 1) + fprintf(stderr, "MSR 0x%X\n", extra_msr_offset); + break; + default: + usage(); + } + } +} + +int main(int argc, char **argv) +{ + cmdline(argc, argv); + + if (verbose > 1) + fprintf(stderr, "turbostat Dec 6, 2010" + " - Len Brown \n"); + if (verbose > 1) + fprintf(stderr, "http://userweb.kernel.org/~lenb/acpi/utils/pmtools/turbostat/\n"); + + turbostat_init(); + + /* + * if any params left, it must be a command to fork + */ + if (argc - optind) + return fork_it(argv + optind); + else + turbostat_loop(); + + return 0; +} -- cgit v0.10.2 From d5532ee7b40b4a64e605e543b0387694430ecb79 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 22 Oct 2010 23:53:03 -0400 Subject: tools: create power/x86/x86_energy_perf_policy MSR_IA32_ENERGY_PERF_BIAS first became available on Westmere Xeon. It is implemented in all Sandy Bridge processors -- mobile, desktop and server. It is expected to become increasingly important in subsequent generations. x86_energy_perf_policy is a user-space utility to set the hardware energy vs performance policy hint in the processor. Most systems would benefit from "x86_energy_perf_policy normal" at system startup, as the hardware default is maximum performance at the expense of energy efficiency. See x86_energy_perf_policy.8 man page for more information. Background: Linux-2.6.36 added "epb" to /proc/cpuinfo to indicate if an x86 processor supports MSR_IA32_ENERGY_PERF_BIAS, without actually modifying the MSR. In March, 2010, Venkatesh Pallipadi proposed a small driver that programmed MSR_IA32_ENERGY_PERF_BIAS, based on the cpufreq governor in use. It also offered a boot-time cmdline option to override. http://lkml.org/lkml/2010/3/4/457 But hiding the hardware policy behind the governor choice was deemed "kinda icky". In June, 2010, I proposed a generic user/kernel API to generalize the power/performance policy trade-off. "RFC: /sys/power/policy_preference" http://lkml.org/lkml/2010/6/16/399 That is my preference for implementing this capability, but I received no support on the list. So in September, 2010, I sent x86_energy_perf_policy.c to LKML, a user-space utility that scribbles directly to the MSR. http://lkml.org/lkml/2010/9/28/246 Here is that same utility, after responding to some review feedback, to live in tools/power/, where it is easily found. Signed-off-by: Len Brown diff --git a/tools/power/x86/x86_energy_perf_policy/Makefile b/tools/power/x86/x86_energy_perf_policy/Makefile new file mode 100644 index 0000000..f458237 --- /dev/null +++ b/tools/power/x86/x86_energy_perf_policy/Makefile @@ -0,0 +1,8 @@ +x86_energy_perf_policy : x86_energy_perf_policy.c + +clean : + rm -f x86_energy_perf_policy + +install : + install x86_energy_perf_policy /usr/bin/ + install x86_energy_perf_policy.8 /usr/share/man/man8/ diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 new file mode 100644 index 0000000..8eaaad6 --- /dev/null +++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 @@ -0,0 +1,104 @@ +.\" This page Copyright (C) 2010 Len Brown +.\" Distributed under the GPL, Copyleft 1994. +.TH X86_ENERGY_PERF_POLICY 8 +.SH NAME +x86_energy_perf_policy \- read or write MSR_IA32_ENERGY_PERF_BIAS +.SH SYNOPSIS +.ft B +.B x86_energy_perf_policy +.RB [ "\-c cpu" ] +.RB [ "\-v" ] +.RB "\-r" +.br +.B x86_energy_perf_policy +.RB [ "\-c cpu" ] +.RB [ "\-v" ] +.RB 'performance' +.br +.B x86_energy_perf_policy +.RB [ "\-c cpu" ] +.RB [ "\-v" ] +.RB 'normal' +.br +.B x86_energy_perf_policy +.RB [ "\-c cpu" ] +.RB [ "\-v" ] +.RB 'powersave' +.br +.B x86_energy_perf_policy +.RB [ "\-c cpu" ] +.RB [ "\-v" ] +.RB n +.br +.SH DESCRIPTION +\fBx86_energy_perf_policy\fP +allows software to convey +its policy for the relative importance of performance +versus energy savings to the processor. + +The processor uses this information in model-specific ways +when it must select trade-offs between performance and +energy efficiency. + +This policy hint does not supersede Processor Performance states +(P-states) or CPU Idle power states (C-states), but allows +software to have influence where it would otherwise be unable +to express a preference. + +For example, this setting may tell the hardware how +aggressively or conservatively to control frequency +in the "turbo range" above the explicitly OS-controlled +P-state frequency range. It may also tell the hardware +how aggressively is should enter the OS requested C-states. + +Support for this feature is indicated by CPUID.06H.ECX.bit3 +per the Intel Architectures Software Developer's Manual. + +.SS Options +\fB-c\fP limits operation to a single CPU. +The default is to operate on all CPUs. +Note that MSR_IA32_ENERGY_PERF_BIAS is defined per +logical processor, but that the initial implementations +of the MSR were shared among all processors in each package. +.PP +\fB-v\fP increases verbosity. By default +x86_energy_perf_policy is silent. +.PP +\fB-r\fP is for "read-only" mode - the unchanged state +is read and displayed. +.PP +.I performance +Set a policy where performance is paramount. +The processor will be unwilling to sacrifice any performance +for the sake of energy saving. This is the hardware default. +.PP +.I normal +Set a policy with a normal balance between performance and energy efficiency. +The processor will tolerate minor performance compromise +for potentially significant energy savings. +This reasonable default for most desktops and servers. +.PP +.I powersave +Set a policy where the processor can accept +a measurable performance hit to maximize energy efficiency. +.PP +.I n +Set MSR_IA32_ENERGY_PERF_BIAS to the specified number. +The range of valid numbers is 0-15, where 0 is maximum +performance and 15 is maximum energy efficiency. + +.SH NOTES +.B "x86_energy_perf_policy " +runs only as root. +.SH FILES +.ta +.nf +/dev/cpu/*/msr +.fi + +.SH "SEE ALSO" +msr(4) +.PP +.SH AUTHORS +.nf +Written by Len Brown diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c new file mode 100644 index 0000000..d9678a3 --- /dev/null +++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c @@ -0,0 +1,325 @@ +/* + * x86_energy_perf_policy -- set the energy versus performance + * policy preference bias on recent X86 processors. + */ +/* + * Copyright (c) 2010, Intel Corporation. + * Len Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned int verbose; /* set with -v */ +unsigned int read_only; /* set with -r */ +char *progname; +unsigned long long new_bias; +int cpu = -1; + +/* + * Usage: + * + * -c cpu: limit action to a single CPU (default is all CPUs) + * -v: verbose output (can invoke more than once) + * -r: read-only, don't change any settings + * + * performance + * Performance is paramount. + * Unwilling to sacrafice any performance + * for the sake of energy saving. (hardware default) + * + * normal + * Can tolerate minor performance compromise + * for potentially significant energy savings. + * (reasonable default for most desktops and servers) + * + * powersave + * Can tolerate significant performance hit + * to maximize energy savings. + * + * n + * a numerical value to write to the underlying MSR. + */ +void usage(void) +{ + printf("%s: [-c cpu] [-v] " + "(-r | 'performance' | 'normal' | 'powersave' | n)\n", + progname); + exit(1); +} + +#define MSR_IA32_ENERGY_PERF_BIAS 0x000001b0 + +#define BIAS_PERFORMANCE 0 +#define BIAS_BALANCE 6 +#define BIAS_POWERSAVE 15 + +void cmdline(int argc, char **argv) +{ + int opt; + + progname = argv[0]; + + while ((opt = getopt(argc, argv, "+rvc:")) != -1) { + switch (opt) { + case 'c': + cpu = atoi(optarg); + break; + case 'r': + read_only = 1; + break; + case 'v': + verbose++; + break; + default: + usage(); + } + } + /* if -r, then should be no additional optind */ + if (read_only && (argc > optind)) + usage(); + + /* + * if no -r , then must be one additional optind + */ + if (!read_only) { + + if (argc != optind + 1) { + printf("must supply -r or policy param\n"); + usage(); + } + + if (!strcmp("performance", argv[optind])) { + new_bias = BIAS_PERFORMANCE; + } else if (!strcmp("normal", argv[optind])) { + new_bias = BIAS_BALANCE; + } else if (!strcmp("powersave", argv[optind])) { + new_bias = BIAS_POWERSAVE; + } else { + char *endptr; + + new_bias = strtoull(argv[optind], &endptr, 0); + if (endptr == argv[optind] || + new_bias > BIAS_POWERSAVE) { + fprintf(stderr, "invalid value: %s\n", + argv[optind]); + usage(); + } + } + } +} + +/* + * validate_cpuid() + * returns on success, quietly exits on failure (make verbose with -v) + */ +void validate_cpuid(void) +{ + unsigned int eax, ebx, ecx, edx, max_level; + char brand[16]; + unsigned int fms, family, model, stepping; + + eax = ebx = ecx = edx = 0; + + asm("cpuid" : "=a" (max_level), "=b" (ebx), "=c" (ecx), + "=d" (edx) : "a" (0)); + + if (ebx != 0x756e6547 || edx != 0x49656e69 || ecx != 0x6c65746e) { + if (verbose) + fprintf(stderr, "%.4s%.4s%.4s != GenuineIntel", + (char *)&ebx, (char *)&edx, (char *)&ecx); + exit(1); + } + + asm("cpuid" : "=a" (fms), "=c" (ecx), "=d" (edx) : "a" (1) : "ebx"); + family = (fms >> 8) & 0xf; + model = (fms >> 4) & 0xf; + stepping = fms & 0xf; + if (family == 6 || family == 0xf) + model += ((fms >> 16) & 0xf) << 4; + + if (verbose > 1) + printf("CPUID %s %d levels family:model:stepping " + "0x%x:%x:%x (%d:%d:%d)\n", brand, max_level, + family, model, stepping, family, model, stepping); + + if (!(edx & (1 << 5))) { + if (verbose) + printf("CPUID: no MSR\n"); + exit(1); + } + + /* + * Support for MSR_IA32_ENERGY_PERF_BIAS + * is indicated by CPUID.06H.ECX.bit3 + */ + asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (6)); + if (verbose) + printf("CPUID.06H.ECX: 0x%x\n", ecx); + if (!(ecx & (1 << 3))) { + if (verbose) + printf("CPUID: No MSR_IA32_ENERGY_PERF_BIAS\n"); + exit(1); + } + return; /* success */ +} + +unsigned long long get_msr(int cpu, int offset) +{ + unsigned long long msr; + char msr_path[32]; + int retval; + int fd; + + sprintf(msr_path, "/dev/cpu/%d/msr", cpu); + fd = open(msr_path, O_RDONLY); + if (fd < 0) { + printf("Try \"# modprobe msr\"\n"); + perror(msr_path); + exit(1); + } + + retval = pread(fd, &msr, sizeof msr, offset); + + if (retval != sizeof msr) { + printf("pread cpu%d 0x%x = %d\n", cpu, offset, retval); + exit(-2); + } + close(fd); + return msr; +} + +unsigned long long put_msr(int cpu, unsigned long long new_msr, int offset) +{ + unsigned long long old_msr; + char msr_path[32]; + int retval; + int fd; + + sprintf(msr_path, "/dev/cpu/%d/msr", cpu); + fd = open(msr_path, O_RDWR); + if (fd < 0) { + perror(msr_path); + exit(1); + } + + retval = pread(fd, &old_msr, sizeof old_msr, offset); + if (retval != sizeof old_msr) { + perror("pwrite"); + printf("pread cpu%d 0x%x = %d\n", cpu, offset, retval); + exit(-2); + } + + retval = pwrite(fd, &new_msr, sizeof new_msr, offset); + if (retval != sizeof new_msr) { + perror("pwrite"); + printf("pwrite cpu%d 0x%x = %d\n", cpu, offset, retval); + exit(-2); + } + + close(fd); + + return old_msr; +} + +void print_msr(int cpu) +{ + printf("cpu%d: 0x%016llx\n", + cpu, get_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS)); +} + +void update_msr(int cpu) +{ + unsigned long long previous_msr; + + previous_msr = put_msr(cpu, new_bias, MSR_IA32_ENERGY_PERF_BIAS); + + if (verbose) + printf("cpu%d msr0x%x 0x%016llx -> 0x%016llx\n", + cpu, MSR_IA32_ENERGY_PERF_BIAS, previous_msr, new_bias); + + return; +} + +char *proc_stat = "/proc/stat"; +/* + * run func() on every cpu in /dev/cpu + */ +void for_every_cpu(void (func)(int)) +{ + FILE *fp; + int retval; + + fp = fopen(proc_stat, "r"); + if (fp == NULL) { + perror(proc_stat); + exit(1); + } + + retval = fscanf(fp, "cpu %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n"); + if (retval != 0) { + perror("/proc/stat format"); + exit(1); + } + + while (1) { + int cpu; + + retval = fscanf(fp, + "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n", + &cpu); + if (retval != 1) + return; + + func(cpu); + } + fclose(fp); +} + +int main(int argc, char **argv) +{ + cmdline(argc, argv); + + if (verbose > 1) + printf("x86_energy_perf_policy Nov 24, 2010" + " - Len Brown \n"); + if (verbose > 1 && !read_only) + printf("new_bias %lld\n", new_bias); + + validate_cpuid(); + + if (cpu != -1) { + if (read_only) + print_msr(cpu); + else + update_msr(cpu); + } else { + if (read_only) + for_every_cpu(print_msr); + else + for_every_cpu(update_msr); + } + + return 0; +} -- cgit v0.10.2 From 2818ef50c4dc103ce52e12d14ce2dfbde5268120 Mon Sep 17 00:00:00 2001 From: Anton Altaparmakov Date: Wed, 12 Jan 2011 10:34:35 +0000 Subject: NTFS: writev() fix and maintenance/contact details update Fix writev() to not keep writing the first segment over and over again instead of moving onto subsequent segments and update the NTFS entry in MAINTAINERS to reflect that Tuxera Inc. now supports the NTFS driver. Signed-off-by: Anton Altaparmakov Signed-off-by: Linus Torvalds diff --git a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt index ac2a261..6ef8cf3 100644 --- a/Documentation/filesystems/ntfs.txt +++ b/Documentation/filesystems/ntfs.txt @@ -457,6 +457,9 @@ ChangeLog Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog. +2.1.30: + - Fix writev() (it kept writing the first segment over and over again + instead of moving onto subsequent segments). 2.1.29: - Fix a deadlock when mounting read-write. 2.1.28: diff --git a/MAINTAINERS b/MAINTAINERS index 42f991e..64d7621 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4383,11 +4383,11 @@ F: Documentation/scsi/NinjaSCSI.txt F: drivers/scsi/nsp32* NTFS FILESYSTEM -M: Anton Altaparmakov +M: Anton Altaparmakov L: linux-ntfs-dev@lists.sourceforge.net -W: http://www.linux-ntfs.org/ +W: http://www.tuxera.com/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/aia21/ntfs-2.6.git -S: Maintained +S: Supported F: Documentation/filesystems/ntfs.txt F: fs/ntfs/ diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile index 58b6be9..4ff028f 100644 --- a/fs/ntfs/Makefile +++ b/fs/ntfs/Makefile @@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \ index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \ unistr.o upcase.o -EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.29\" +EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.30\" ifeq ($(CONFIG_NTFS_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 113ebd9..f4b1057 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -1,7 +1,7 @@ /* * file.c - NTFS kernel file operations. Part of the Linux-NTFS project. * - * Copyright (c) 2001-2007 Anton Altaparmakov + * Copyright (c) 2001-2011 Anton Altaparmakov and Tuxera Inc. * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -1380,15 +1380,14 @@ static inline void ntfs_set_next_iovec(const struct iovec **iovp, * pages (out to offset + bytes), to emulate ntfs_copy_from_user()'s * single-segment behaviour. * - * We call the same helper (__ntfs_copy_from_user_iovec_inatomic()) both - * when atomic and when not atomic. This is ok because - * __ntfs_copy_from_user_iovec_inatomic() calls __copy_from_user_inatomic() - * and it is ok to call this when non-atomic. - * Infact, the only difference between __copy_from_user_inatomic() and + * We call the same helper (__ntfs_copy_from_user_iovec_inatomic()) both when + * atomic and when not atomic. This is ok because it calls + * __copy_from_user_inatomic() and it is ok to call this when non-atomic. In + * fact, the only difference between __copy_from_user_inatomic() and * __copy_from_user() is that the latter calls might_sleep() and the former - * should not zero the tail of the buffer on error. And on many - * architectures __copy_from_user_inatomic() is just defined to - * __copy_from_user() so it makes no difference at all on those architectures. + * should not zero the tail of the buffer on error. And on many architectures + * __copy_from_user_inatomic() is just defined to __copy_from_user() so it + * makes no difference at all on those architectures. */ static inline size_t ntfs_copy_from_user_iovec(struct page **pages, unsigned nr_pages, unsigned ofs, const struct iovec **iov, @@ -1409,28 +1408,28 @@ static inline size_t ntfs_copy_from_user_iovec(struct page **pages, if (unlikely(copied != len)) { /* Do it the slow way. */ addr = kmap(*pages); - copied = __ntfs_copy_from_user_iovec_inatomic(addr + ofs, - *iov, *iov_ofs, len); - /* - * Zero the rest of the target like __copy_from_user(). - */ - memset(addr + ofs + copied, 0, len - copied); - kunmap(*pages); + copied = __ntfs_copy_from_user_iovec_inatomic(addr + + ofs, *iov, *iov_ofs, len); if (unlikely(copied != len)) goto err_out; + kunmap(*pages); } total += len; + ntfs_set_next_iovec(iov, iov_ofs, len); bytes -= len; if (!bytes) break; - ntfs_set_next_iovec(iov, iov_ofs, len); ofs = 0; } while (++pages < last_page); out: return total; err_out: - total += copied; + BUG_ON(copied > len); /* Zero the rest of the target like __copy_from_user(). */ + memset(addr + ofs + copied, 0, len - copied); + kunmap(*pages); + total += copied; + ntfs_set_next_iovec(iov, iov_ofs, copied); while (++pages < last_page) { bytes -= len; if (!bytes) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index a30ecacc..29099a0 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1,7 +1,7 @@ /* * super.c - NTFS kernel super block handling. Part of the Linux-NTFS project. * - * Copyright (c) 2001-2007 Anton Altaparmakov + * Copyright (c) 2001-2011 Anton Altaparmakov and Tuxera Inc. * Copyright (c) 2001,2002 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -3193,8 +3193,8 @@ static void __exit exit_ntfs_fs(void) ntfs_sysctl(0); } -MODULE_AUTHOR("Anton Altaparmakov "); -MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2007 Anton Altaparmakov"); +MODULE_AUTHOR("Anton Altaparmakov "); +MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2011 Anton Altaparmakov and Tuxera Inc."); MODULE_VERSION(NTFS_VERSION); MODULE_LICENSE("GPL"); #ifdef DEBUG -- cgit v0.10.2 From 704bf317fd21683e5c71a542f5fb5f65271a1582 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 13 Jan 2011 18:31:25 +0900 Subject: sh: Add a new mach type for alpha project boards. Signed-off-by: Paul Mundt diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig index 2018c7e..3bac817 100644 --- a/arch/sh/boards/Kconfig +++ b/arch/sh/boards/Kconfig @@ -3,6 +3,9 @@ menu "Board support" config SOLUTION_ENGINE bool +config SH_ALPHA_BOARD + bool + config SH_SOLUTION_ENGINE bool "SolutionEngine" select SOLUTION_ENGINE diff --git a/arch/sh/tools/mach-types b/arch/sh/tools/mach-types index 0e68465..765cf79 100644 --- a/arch/sh/tools/mach-types +++ b/arch/sh/tools/mach-types @@ -9,6 +9,7 @@ SE SH_SOLUTION_ENGINE HIGHLANDER SH_HIGHLANDER RTS7751R2D SH_RTS7751R2D RSK SH_RSK +ALPHA_BOARD SH_ALPHA_BOARD # # List of companion chips / MFDs. -- cgit v0.10.2 From bc34b0850be0aa99a49b714ea8a495fbe9a8c273 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 13 Jan 2011 18:32:42 +0900 Subject: sh: Add support for AP-SH4A-3A board. This adds preliminary support for the alpha project AP-SH4A-3A reference platform (SH7785 based). Additional paltform information available at: http://www.apnet.co.jp/product/superh/ap-sh4a-3a.html Signed-off-by: Paul Mundt diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig index 3bac817..4875d0b 100644 --- a/arch/sh/boards/Kconfig +++ b/arch/sh/boards/Kconfig @@ -323,6 +323,13 @@ config SH_SH2007 Compact Flash socket, two serial ports and PC-104 bus. More information at . +config SH_APSH4A3A + bool "AP-SH4A-3A" + select SH_ALPHA_BOARD + depends on CPU_SUBTYPE_SH7785 + help + Select AP-SH4A-3A if configuring for an ALPHAPROJECT AP-SH4A-3A. + endmenu source "arch/sh/boards/mach-r2d/Kconfig" diff --git a/arch/sh/boards/Makefile b/arch/sh/boards/Makefile index be7d11d..54c0458 100644 --- a/arch/sh/boards/Makefile +++ b/arch/sh/boards/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_SH_ESPT) += board-espt.o obj-$(CONFIG_SH_POLARIS) += board-polaris.o obj-$(CONFIG_SH_TITAN) += board-titan.o obj-$(CONFIG_SH_SH7757LCR) += board-sh7757lcr.o +obj-$(CONFIG_SH_APSH4A3A) += board-apsh4a3a.o diff --git a/arch/sh/boards/board-apsh4a3a.c b/arch/sh/boards/board-apsh4a3a.c new file mode 100644 index 0000000..8e2a270 --- /dev/null +++ b/arch/sh/boards/board-apsh4a3a.c @@ -0,0 +1,175 @@ +/* + * ALPHAPROJECT AP-SH4A-3A Support. + * + * Copyright (C) 2010 ALPHAPROJECT Co.,Ltd. + * Copyright (C) 2008 Yoshihiro Shimoda + * Copyright (C) 2009 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mtd_partition nor_flash_partitions[] = { + { + .name = "loader", + .offset = 0x00000000, + .size = 512 * 1024, + }, + { + .name = "bootenv", + .offset = MTDPART_OFS_APPEND, + .size = 512 * 1024, + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 4 * 1024 * 1024, + }, + { + .name = "data", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct physmap_flash_data nor_flash_data = { + .width = 4, + .parts = nor_flash_partitions, + .nr_parts = ARRAY_SIZE(nor_flash_partitions), +}; + +static struct resource nor_flash_resources[] = { + [0] = { + .start = 0x00000000, + .end = 0x01000000 - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device nor_flash_device = { + .name = "physmap-flash", + .dev = { + .platform_data = &nor_flash_data, + }, + .num_resources = ARRAY_SIZE(nor_flash_resources), + .resource = nor_flash_resources, +}; + +static struct resource smsc911x_resources[] = { + [0] = { + .name = "smsc911x-memory", + .start = 0xA4000000, + .end = 0xA4000000 + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "smsc911x-irq", + .start = evt2irq(0x200), + .end = evt2irq(0x200), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct smsc911x_platform_config smsc911x_config = { + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN, + .flags = SMSC911X_USE_16BIT, + .phy_interface = PHY_INTERFACE_MODE_MII, +}; + +static struct platform_device smsc911x_device = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(smsc911x_resources), + .resource = smsc911x_resources, + .dev = { + .platform_data = &smsc911x_config, + }, +}; + +static struct platform_device *apsh4a3a_devices[] __initdata = { + &nor_flash_device, + &smsc911x_device, +}; + +static int __init apsh4a3a_devices_setup(void) +{ + return platform_add_devices(apsh4a3a_devices, + ARRAY_SIZE(apsh4a3a_devices)); +} +device_initcall(apsh4a3a_devices_setup); + +static int apsh4a3a_clk_init(void) +{ + struct clk *clk; + int ret; + + clk = clk_get(NULL, "extal"); + if (!clk || IS_ERR(clk)) + return PTR_ERR(clk); + ret = clk_set_rate(clk, 33333000); + clk_put(clk); + + return ret; +} + +/* Initialize the board */ +static void __init apsh4a3a_setup(char **cmdline_p) +{ + printk(KERN_INFO "Alpha Project AP-SH4A-3A support:\n"); +} + +static void __init apsh4a3a_init_irq(void) +{ + plat_irq_setup_pins(IRQ_MODE_IRQ7654); +} + +/* Return the board specific boot mode pin configuration */ +static int apsh4a3a_mode_pins(void) +{ + int value = 0; + + /* These are the factory default settings of SW1 and SW2. + * If you change these dip switches then you will need to + * adjust the values below as well. + */ + value &= ~MODE_PIN0; /* Clock Mode 16 */ + value &= ~MODE_PIN1; + value &= ~MODE_PIN2; + value &= ~MODE_PIN3; + value |= MODE_PIN4; + value &= ~MODE_PIN5; /* 16-bit Area0 bus width */ + value |= MODE_PIN6; /* Area 0 SRAM interface */ + value |= MODE_PIN7; + value |= MODE_PIN8; /* Little Endian */ + value |= MODE_PIN9; /* Master Mode */ + value |= MODE_PIN10; /* Crystal resonator */ + value |= MODE_PIN11; /* Display Unit */ + value |= MODE_PIN12; + value &= ~MODE_PIN13; /* 29-bit address mode */ + value |= MODE_PIN14; /* No PLL step-up */ + + return value; +} + +/* + * The Machine Vector + */ +static struct sh_machine_vector mv_apsh4a3a __initmv = { + .mv_name = "AP-SH4A-3A", + .mv_setup = apsh4a3a_setup, + .mv_clk_init = apsh4a3a_clk_init, + .mv_init_irq = apsh4a3a_init_irq, + .mv_mode_pins = apsh4a3a_mode_pins, +}; diff --git a/arch/sh/configs/apsh4a3a_defconfig b/arch/sh/configs/apsh4a3a_defconfig new file mode 100644 index 0000000..6cb3279 --- /dev/null +++ b/arch/sh/configs/apsh4a3a_defconfig @@ -0,0 +1,102 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_CPU_SUBTYPE_SH7785=y +CONFIG_MEMORY_START=0x0C000000 +CONFIG_FLATMEM_MANUAL=y +CONFIG_SH_STORE_QUEUES=y +CONFIG_SH_APSH4A3A=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_KEXEC=y +CONFIG_PREEMPT=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_LRO is not set +# CONFIG_IPV6 is not set +# CONFIG_WIRELESS is not set +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_FW_LOADER is not set +CONFIG_MTD=y +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_PHYSMAP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_NETDEVICES=y +CONFIG_NET_ETHERNET=y +CONFIG_SMSC911X=y +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +# CONFIG_WLAN is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_SERIO is not set +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_SERIAL_SH_SCI=y +CONFIG_SERIAL_SH_SCI_NR_UARTS=6 +CONFIG_SERIAL_SH_SCI_CONSOLE=y +CONFIG_HW_RANDOM=y +# CONFIG_HWMON is not set +CONFIG_FB=y +CONFIG_FB_SH7785FB=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_SUPPORT is not set +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=y +CONFIG_NTFS_RW=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_CRAMFS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +CONFIG_CIFS=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_932=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_FTRACE is not set +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_HW is not set diff --git a/arch/sh/tools/mach-types b/arch/sh/tools/mach-types index 765cf79..116e437 100644 --- a/arch/sh/tools/mach-types +++ b/arch/sh/tools/mach-types @@ -62,3 +62,4 @@ ESPT SH_ESPT POLARIS SH_POLARIS KFR2R09 SH_KFR2R09 ECOVEC SH_ECOVEC +APSH4A3A SH_APSH4A3A -- cgit v0.10.2 From 8a453cac94803910305f7e95cbd157b6bbd88811 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 13 Jan 2011 18:36:21 +0900 Subject: sh: Add support for AP-SH4AD-0A board. This adds preliminary support for the alpha project AP-SH4AD-0A reference platform (SH7786 based). Additional platform information available at: http://www.apnet.co.jp/product/superh/ap-sh4ad-0a.html Signed-off-by: Paul Mundt diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig index 4875d0b..d893411 100644 --- a/arch/sh/boards/Kconfig +++ b/arch/sh/boards/Kconfig @@ -330,6 +330,14 @@ config SH_APSH4A3A help Select AP-SH4A-3A if configuring for an ALPHAPROJECT AP-SH4A-3A. +config SH_APSH4AD0A + bool "AP-SH4AD-0A" + select SH_ALPHA_BOARD + select SYS_SUPPORTS_PCI + depends on CPU_SUBTYPE_SH7786 + help + Select AP-SH4AD-0A if configuring for an ALPHAPROJECT AP-SH4AD-0A. + endmenu source "arch/sh/boards/mach-r2d/Kconfig" diff --git a/arch/sh/boards/Makefile b/arch/sh/boards/Makefile index 54c0458..975a0f6 100644 --- a/arch/sh/boards/Makefile +++ b/arch/sh/boards/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_SH_POLARIS) += board-polaris.o obj-$(CONFIG_SH_TITAN) += board-titan.o obj-$(CONFIG_SH_SH7757LCR) += board-sh7757lcr.o obj-$(CONFIG_SH_APSH4A3A) += board-apsh4a3a.o +obj-$(CONFIG_SH_APSH4AD0A) += board-apsh4ad0a.o diff --git a/arch/sh/boards/board-apsh4ad0a.c b/arch/sh/boards/board-apsh4ad0a.c new file mode 100644 index 0000000..e2bd218 --- /dev/null +++ b/arch/sh/boards/board-apsh4ad0a.c @@ -0,0 +1,125 @@ +/* + * ALPHAPROJECT AP-SH4AD-0A Support. + * + * Copyright (C) 2010 ALPHAPROJECT Co.,Ltd. + * Copyright (C) 2010 Matt Fleming + * Copyright (C) 2010 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +static struct resource smsc911x_resources[] = { + [0] = { + .name = "smsc911x-memory", + .start = 0xA4000000, + .end = 0xA4000000 + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "smsc911x-irq", + .start = evt2irq(0x200), + .end = evt2irq(0x200), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct smsc911x_platform_config smsc911x_config = { + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN, + .flags = SMSC911X_USE_16BIT, + .phy_interface = PHY_INTERFACE_MODE_MII, +}; + +static struct platform_device smsc911x_device = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(smsc911x_resources), + .resource = smsc911x_resources, + .dev = { + .platform_data = &smsc911x_config, + }, +}; + +static struct platform_device *apsh4ad0a_devices[] __initdata = { + &smsc911x_device, +}; + +static int __init apsh4ad0a_devices_setup(void) +{ + return platform_add_devices(apsh4ad0a_devices, + ARRAY_SIZE(apsh4ad0a_devices)); +} +device_initcall(apsh4ad0a_devices_setup); + +static int apsh4ad0a_mode_pins(void) +{ + int value = 0; + + /* These are the factory default settings of SW1 and SW2. + * If you change these dip switches then you will need to + * adjust the values below as well. + */ + value |= MODE_PIN0; /* Clock Mode 3 */ + value |= MODE_PIN1; + value &= ~MODE_PIN2; + value &= ~MODE_PIN3; + value &= ~MODE_PIN4; /* 16-bit Area0 bus width */ + value |= MODE_PIN5; + value |= MODE_PIN6; + value |= MODE_PIN7; /* Normal mode */ + value |= MODE_PIN8; /* Little Endian */ + value |= MODE_PIN9; /* Crystal resonator */ + value &= ~MODE_PIN10; /* 29-bit address mode */ + value &= ~MODE_PIN11; /* PCI-E Root port */ + value &= ~MODE_PIN12; /* 4 lane + 1 lane */ + value |= MODE_PIN13; /* AUD Enable */ + value &= ~MODE_PIN14; /* Normal Operation */ + + return value; +} + +static int apsh4ad0a_clk_init(void) +{ + struct clk *clk; + int ret; + + clk = clk_get(NULL, "extal"); + if (!clk || IS_ERR(clk)) + return PTR_ERR(clk); + ret = clk_set_rate(clk, 33333000); + clk_put(clk); + + return ret; +} + +/* Initialize the board */ +static void __init apsh4ad0a_setup(char **cmdline_p) +{ + pr_info("Alpha Project AP-SH4AD-0A support:\n"); +} + +static void __init apsh4ad0a_init_irq(void) +{ + plat_irq_setup_pins(IRQ_MODE_IRQ3210); +} + +/* + * The Machine Vector + */ +static struct sh_machine_vector mv_apsh4ad0a __initmv = { + .mv_name = "AP-SH4AD-0A", + .mv_setup = apsh4ad0a_setup, + .mv_mode_pins = apsh4ad0a_mode_pins, + .mv_clk_init = apsh4ad0a_clk_init, + .mv_init_irq = apsh4ad0a_init_irq, +}; diff --git a/arch/sh/configs/apsh4ad0a_defconfig b/arch/sh/configs/apsh4ad0a_defconfig new file mode 100644 index 0000000..e71a531 --- /dev/null +++ b/arch/sh/configs/apsh4ad0a_defconfig @@ -0,0 +1,133 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_RCU_TRACE=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_CGROUPS=y +CONFIG_CGROUP_NS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_BLK_CGROUP=y +CONFIG_NAMESPACES=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +CONFIG_CFQ_GROUP_IOSCHED=y +CONFIG_CPU_SUBTYPE_SH7786=y +CONFIG_MEMORY_SIZE=0x10000000 +CONFIG_HUGETLB_PAGE_SIZE_1MB=y +CONFIG_MEMORY_HOTPLUG=y +CONFIG_MEMORY_HOTREMOVE=y +CONFIG_KSM=y +CONFIG_SH_STORE_QUEUES=y +CONFIG_SH_APSH4AD0A=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=m +CONFIG_CPU_FREQ_GOV_ONDEMAND=m +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_SH_CPU_FREQ=y +CONFIG_KEXEC=y +CONFIG_SECCOMP=y +CONFIG_PREEMPT=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_MISC=y +CONFIG_PM=y +CONFIG_PM_DEBUG=y +CONFIG_PM_VERBOSE=y +CONFIG_PM_RUNTIME=y +CONFIG_CPU_IDLE=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_NET_KEY=y +CONFIG_INET=y +# CONFIG_INET_LRO is not set +# CONFIG_IPV6 is not set +# CONFIG_WIRELESS is not set +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_FW_LOADER is not set +CONFIG_MTD=y +CONFIG_MTD_CFI=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_NETDEVICES=y +CONFIG_MDIO_BITBANG=y +CONFIG_NET_ETHERNET=y +CONFIG_SMSC911X=y +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +# CONFIG_WLAN is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_SERIO is not set +CONFIG_SERIAL_SH_SCI=y +CONFIG_SERIAL_SH_SCI_NR_UARTS=6 +CONFIG_SERIAL_SH_SCI_CONSOLE=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +CONFIG_FB_SH7785FB=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_USB=y +CONFIG_USB_DEBUG=y +CONFIG_USB_MON=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=y +CONFIG_NTFS_RW=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_HUGETLBFS=y +CONFIG_JFFS2_FS=y +CONFIG_CRAMFS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +CONFIG_CIFS=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_932=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_SHIRQ=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_VM=y +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +CONFIG_DWARF_UNWINDER=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set diff --git a/arch/sh/tools/mach-types b/arch/sh/tools/mach-types index 116e437..6dd56c4 100644 --- a/arch/sh/tools/mach-types +++ b/arch/sh/tools/mach-types @@ -63,3 +63,4 @@ POLARIS SH_POLARIS KFR2R09 SH_KFR2R09 ECOVEC SH_ECOVEC APSH4A3A SH_APSH4A3A +APSH4AD0A SH_APSH4AD0A -- cgit v0.10.2