OSDN Git Service

Merge branch 'sh/alphaproject' into sh-latest
authorPaul Mundt <lethal@linux-sh.org>
Thu, 13 Jan 2011 09:38:28 +0000 (18:38 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Thu, 13 Jan 2011 09:38:28 +0000 (18:38 +0900)
54 files changed:
Documentation/filesystems/ntfs.txt
MAINTAINERS
arch/sh/boards/Kconfig
arch/sh/boards/Makefile
arch/sh/boards/board-apsh4a3a.c [new file with mode: 0644]
arch/sh/boards/board-apsh4ad0a.c [new file with mode: 0644]
arch/sh/configs/apsh4a3a_defconfig [new file with mode: 0644]
arch/sh/configs/apsh4ad0a_defconfig [new file with mode: 0644]
arch/sh/tools/mach-types
drivers/block/Kconfig
drivers/char/agp/intel-agp.h
drivers/char/agp/intel-gtt.c
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_evict.c
drivers/gpu/drm/i915/i915_gem_execbuffer.c
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_fb.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/i915/intel_ringbuffer.h
drivers/gpu/drm/i915/intel_sdvo.c
drivers/net/Kconfig
drivers/pci/Kconfig
drivers/xen/Kconfig
drivers/xen/xenbus/Makefile
drivers/xen/xenbus/xenbus_probe.c
drivers/xen/xenbus/xenbus_probe.h
drivers/xen/xenbus/xenbus_probe_backend.c [new file with mode: 0644]
drivers/xen/xenbus/xenbus_probe_frontend.c [new file with mode: 0644]
fs/ntfs/Makefile
fs/ntfs/file.c
fs/ntfs/super.c
include/xen/xenbus.h
tools/power/x86/turbostat/Makefile [new file with mode: 0644]
tools/power/x86/turbostat/turbostat.8 [new file with mode: 0644]
tools/power/x86/turbostat/turbostat.c [new file with mode: 0644]
tools/power/x86/x86_energy_perf_policy/Makefile [new file with mode: 0644]
tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 [new file with mode: 0644]
tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c [new file with mode: 0644]
tools/testing/ktest/compare-ktest-sample.pl [new file with mode: 0755]
tools/testing/ktest/ktest.pl [new file with mode: 0755]
tools/testing/ktest/sample.conf [new file with mode: 0644]

index ac2a261..6ef8cf3 100644 (file)
@@ -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:
index 42f991e..64d7621 100644 (file)
@@ -4383,11 +4383,11 @@ F:      Documentation/scsi/NinjaSCSI.txt
 F:     drivers/scsi/nsp32*
 
 NTFS FILESYSTEM
-M:     Anton Altaparmakov <aia21@cantab.net>
+M:     Anton Altaparmakov <anton@tuxera.com>
 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/
 
index 2018c7e..d893411 100644 (file)
@@ -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
@@ -320,6 +323,21 @@ config SH_SH2007
          Compact Flash socket, two serial ports and PC-104 bus.
          More information at <http://sh2000.sh-linux.org>.
 
+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.
+
+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"
index be7d11d..975a0f6 100644 (file)
@@ -13,3 +13,5 @@ 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
+obj-$(CONFIG_SH_APSH4AD0A)     += board-apsh4ad0a.o
diff --git a/arch/sh/boards/board-apsh4a3a.c b/arch/sh/boards/board-apsh4a3a.c
new file mode 100644 (file)
index 0000000..8e2a270
--- /dev/null
@@ -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 <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/mtd/physmap.h>
+#include <linux/smsc911x.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <asm/machvec.h>
+#include <asm/sizes.h>
+#include <asm/clock.h>
+
+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/boards/board-apsh4ad0a.c b/arch/sh/boards/board-apsh4ad0a.c
new file mode 100644 (file)
index 0000000..e2bd218
--- /dev/null
@@ -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 <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/smsc911x.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <asm/machvec.h>
+#include <asm/sizes.h>
+
+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/apsh4a3a_defconfig b/arch/sh/configs/apsh4a3a_defconfig
new file mode 100644 (file)
index 0000000..6cb3279
--- /dev/null
@@ -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/configs/apsh4ad0a_defconfig b/arch/sh/configs/apsh4ad0a_defconfig
new file mode 100644 (file)
index 0000000..e71a531
--- /dev/null
@@ -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
index 0e68465..6dd56c4 100644 (file)
@@ -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.
@@ -61,3 +62,5 @@ ESPT                  SH_ESPT
 POLARIS                        SH_POLARIS
 KFR2R09                        SH_KFR2R09
 ECOVEC                 SH_ECOVEC
+APSH4A3A               SH_APSH4A3A
+APSH4AD0A              SH_APSH4AD0A
index 4b9359a..83c32cb 100644 (file)
@@ -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
index 010e3de..c195bfe 100644 (file)
@@ -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
index 356f73e..e921b69 100644 (file)
@@ -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;
 }
 
@@ -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;
 }
 
@@ -991,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;
 }
 
index 92f7578..19a3d58 100644 (file)
@@ -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)
 {
@@ -456,8 +497,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;
@@ -656,7 +703,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,
@@ -666,7 +713,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);
@@ -744,7 +792,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++) {
@@ -890,7 +940,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) ?
@@ -913,6 +963,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;
 }
@@ -1187,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},
index 0568dbd..844f3c9 100644 (file)
@@ -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);
index 8724933..0de75a2 100644 (file)
@@ -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;
 
@@ -352,6 +355,9 @@ static int i915_drm_thaw(struct drm_device *dev)
 
                /* Resume the modeset for every activated CRTC */
                drm_helper_resume_force_mode(dev);
+
+               if (dev_priv->renderctx && dev_priv->pwrctx)
+                       ironlake_enable_rc6(dev);
        }
 
        intel_opregion_init(dev);
@@ -475,6 +481,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;
 
index aac1bf3..385fc7e 100644 (file)
@@ -172,20 +172,21 @@ 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 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;
@@ -332,6 +333,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 */
@@ -794,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.
@@ -1006,12 +1009,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);
@@ -1091,10 +1088,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);
@@ -1265,6 +1262,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);
index c79c0b6..3dfc848 100644 (file)
 #include <linux/swap.h>
 #include <linux/pci.h>
 
-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,
@@ -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);
 }
 
@@ -2142,25 +2164,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)
 {
-       ring->flush(ring, invalidate_domains, flush_domains);
+       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 +2404,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;
        }
@@ -2393,6 +2430,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;
 }
 
@@ -2523,9 +2566,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;
                        }
@@ -2736,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) {
@@ -2747,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;
@@ -2762,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;
@@ -2811,17 +2853,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. */
@@ -2833,10 +2874,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;
@@ -2882,7 +2929,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)
@@ -2927,7 +2977,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) {
@@ -2952,12 +3005,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);
 }
@@ -2974,7 +3032,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;
@@ -3069,7 +3130,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;
@@ -3362,8 +3426,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;
index 78b8cf9..3d39005 100644 (file)
@@ -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);
        }
index 61129e6..e698343 100644 (file)
@@ -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 */
@@ -622,7 +639,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 +682,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;
@@ -681,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;
 
@@ -693,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;
@@ -713,25 +729,34 @@ 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();
 
+       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))
-                               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
@@ -795,10 +820,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) {
@@ -921,7 +948,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
@@ -929,11 +956,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);
@@ -1098,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,
@@ -1121,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;
@@ -1340,4 +1372,3 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
        drm_free_large(exec2_list);
        return ret;
 }
-
index 86673e7..70433ae 100644 (file)
@@ -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);
 }
index 0dadc02..e418e8b 100644 (file)
 #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)
 {
@@ -389,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));
@@ -435,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;
@@ -502,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));
@@ -556,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;
@@ -632,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)
@@ -702,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;
@@ -741,6 +705,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
@@ -755,10 +749,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;
@@ -817,83 +809,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 =
@@ -1673,11 +1612,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 =
@@ -1698,6 +1632,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;
index 8f948a6..40a407f 100644 (file)
 #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)
 #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 */
 #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
 #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 */
 
 /* 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
 /* 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)
 #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
 
 #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
 
index 4107724..0521ecf 100644 (file)
@@ -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);
@@ -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);
 
index 8df5743..17035b8 100644 (file)
@@ -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);
index 0abe79f..25d9688 100644 (file)
@@ -3418,15 +3418,16 @@ 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)
 {
        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)
@@ -3438,7 +3439,10 @@ 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;
+       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)
@@ -3446,8 +3450,11 @@ 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;
+       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)
@@ -3456,113 +3463,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 +3482,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 +3490,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 +3513,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 +3539,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,
@@ -3652,7 +3651,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;
 
@@ -3701,9 +3700,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 +3715,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 +3731,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,
@@ -3951,7 +3956,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);
@@ -5038,8 +5043,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;
@@ -5047,17 +5052,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");
@@ -5802,6 +5809,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)
@@ -6145,6 +6154,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
@@ -6216,6 +6229,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 |
@@ -6386,42 +6422,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) && 0) { /* XXX causes a failure during suspend */
-               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();
-                       }
-               } 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);
-               }
-       }
 }
 
 void intel_disable_clock_gating(struct drm_device *dev)
@@ -6451,6 +6451,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)
 {
@@ -6665,12 +6716,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;
@@ -6698,6 +6744,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);
@@ -6734,7 +6795,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);
 
index 1dc6040..1f4242b 100644 (file)
@@ -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;
        }
 }
 
@@ -1334,17 +1343,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 +1383,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);
index d782ad9..74db255 100644 (file)
@@ -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);
index 701e830..ee145a2 100644 (file)
@@ -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);
index aa23070..8f4f6bd 100644 (file)
@@ -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);
 
@@ -375,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;
@@ -398,8 +401,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 +431,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.
         */
index 7350ec2..e00d200 100644 (file)
@@ -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;
+}
index 56bc95c..03e3370 100644 (file)
@@ -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,
@@ -480,26 +485,56 @@ 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)
 {
        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(&ring->irq_lock);
+       if (ring->irq_refcount++ == 0) {
                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);
        }
+       spin_unlock(&ring->irq_lock);
 
        return true;
 }
@@ -508,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(&ring->irq_lock);
+       if (--ring->irq_refcount == 0) {
                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);
        }
+       spin_unlock(&ring->irq_lock);
 }
 
 void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
@@ -534,19 +567,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
@@ -577,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);
-               ironlake_enable_graphics_irq(dev_priv, flag);
-               spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
-       }
+       spin_lock(&ring->irq_lock);
+       if (ring->irq_refcount++ == 0)
+               ironlake_enable_irq(dev_priv, flag);
+       spin_unlock(&ring->irq_lock);
 
        return true;
 }
@@ -597,15 +632,47 @@ 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(&ring->irq_lock);
+       if (--ring->irq_refcount == 0)
+               ironlake_disable_irq(dev_priv, flag);
+       spin_unlock(&ring->irq_lock);
+}
 
-               spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-               ironlake_disable_graphics_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;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+
+       if (!dev->irq_enabled)
+              return false;
+
+       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(&ring->irq_lock);
+
+       return true;
+}
+
+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;
+
+       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(&ring->irq_lock);
 }
 
 static bool
@@ -748,6 +815,9 @@ int intel_init_ring_buffer(struct drm_device *dev,
        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)) {
                ret = init_status_page(ring);
                if (ret)
@@ -785,6 +855,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 +905,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 +972,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;
@@ -973,20 +1050,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
@@ -1008,15 +1090,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+ */
@@ -1040,13 +1142,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);
 }
 
 
@@ -1115,20 +1221,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)
@@ -1165,6 +1276,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;
index 8e2e357..be9087e 100644 (file)
@@ -16,21 +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_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_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))
 
 struct  intel_ring_buffer {
        const char      *name;
@@ -49,12 +52,15 @@ struct  intel_ring_buffer {
        u32             tail;
        int             space;
        int             size;
+       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 */
        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);
 
@@ -62,9 +68,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);
index 9d0af36..45cd376 100644 (file)
@@ -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) |
index ff652c7..4c8bfc9 100644 (file)
@@ -2963,6 +2963,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
index 5b1630e..a9523fd 100644 (file)
@@ -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
index 6e6180c..5a48ce9 100644 (file)
@@ -29,6 +29,14 @@ config XEN_DEV_EVTCHN
          firing.
          If in doubt, say yes.
 
+config XEN_BACKEND
+       bool "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
@@ -62,6 +70,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
index 5571f5b..8dca685 100644 (file)
@@ -5,3 +5,8 @@ xenbus-objs += xenbus_client.o
 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
index deb9c4b..baa65e7 100644 (file)
@@ -56,7 +56,6 @@
 #include <xen/events.h>
 #include <xen/page.h>
 
-#include <xen/platform_pci.h>
 #include <xen/hvm.h>
 
 #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,34 +92,7 @@ int xenbus_match(struct device *_dev, struct device_driver *_drv)
 
        return match_device(drv->ids, to_xenbus_device(_dev)) != NULL;
 }
-
-static 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;
-}
-
-/* device/<type>/<id> => <type>-<id> */
-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_match);
 
 
 static void free_otherend_details(struct xenbus_device *dev)
@@ -149,7 +112,30 @@ 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 +160,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/<id> */
-       .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 +192,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 +200,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)
 {
@@ -308,8 +244,9 @@ 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);
 
 int xenbus_dev_remove(struct device *_dev)
 {
@@ -327,8 +264,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 +287,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 +301,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 +472,7 @@ fail:
        kfree(xendev);
        return err;
 }
-
-/* device/<typename>/<name> */
-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)
 {
@@ -582,10 +486,11 @@ static int xenbus_probe_device_type(struct xen_bus_type *bus, const char *type)
                return PTR_ERR(dir);
 
        for (i = 0; i < dir_n; i++) {
-               err = bus->probe(type, dir[i]);
+               err = bus->probe(bus, type, dir[i]);
                if (err)
                        break;
        }
+
        kfree(dir);
        return err;
 }
@@ -605,9 +510,11 @@ int xenbus_probe_devices(struct xen_bus_type *bus)
                if (err)
                        break;
        }
+
        kfree(dir);
        return err;
 }
+EXPORT_SYMBOL_GPL(xenbus_probe_devices);
 
 static unsigned int char_count(const char *str, char c)
 {
@@ -670,32 +577,18 @@ 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 +596,20 @@ 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 +640,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 +669,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);
 }
@@ -809,16 +697,7 @@ static int __init xenbus_init(void)
 
        err = -ENODEV;
        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;
+               return err;
 
        /*
         * Domain0 doesn't have a store_evtchn or store_mfn yet.
@@ -874,7 +753,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
@@ -887,133 +766,13 @@ static int __init xenbus_init(void)
 
        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
index 6c5e318..2466581 100644 (file)
 
 #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 +62,16 @@ extern int xenbus_probe_devices(struct xen_bus_type *bus);
 
 extern void xenbus_dev_changed(const char *node, struct xen_bus_type *bus);
 
+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_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c
new file mode 100644 (file)
index 0000000..6cf467b
--- /dev/null
@@ -0,0 +1,276 @@
+/******************************************************************************
+ * 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 <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/notifier.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/xen/hypervisor.h>
+#include <asm/hypervisor.h>
+#include <xen/xenbus.h>
+#include <xen/features.h>
+
+#include "xenbus_comms.h"
+#include "xenbus_probe.h"
+
+/* backend/<type>/<fe-uuid>/<id> => <type>-<fe-domid>-<id> */
+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/<typename>/<frontend-uuid>/<name> */
+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/<typename>/<frontend-domid> */
+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/<frontend>/<id> */
+       .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");
+}
+
+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);
+       register_xenbus_watch(&be_watch);
+
+       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)
+               return err;
+
+       register_xenstore_notifier(&xenstore_notifier);
+
+       return 0;
+}
+subsys_initcall(xenbus_probe_backend_init);
diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c
new file mode 100644 (file)
index 0000000..5bcc2d6
--- /dev/null
@@ -0,0 +1,294 @@
+#define DPRINTK(fmt, args...)                          \
+       pr_debug("xenbus_probe (%s:%d) " fmt ".\n",     \
+                __func__, __LINE__, ##args)
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/notifier.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/xen/hypervisor.h>
+#include <xen/xenbus.h>
+#include <xen/events.h>
+#include <xen/page.h>
+
+#include <xen/platform_pci.h>
+
+#include "xenbus_comms.h"
+#include "xenbus_probe.h"
+
+
+/* device/<type>/<id> => <type>-<id> */
+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/<typename>/<name> */
+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 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)
+{
+       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/<id> */
+       .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,
+       },
+};
+
+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 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);
+       register_xenbus_watch(&fe_watch);
+
+       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("");
+
+       /* Register ourselves with the kernel bus subsystem */
+       err = bus_register(&xenbus_frontend.bus);
+       if (err)
+               return err;
+
+       register_xenstore_notifier(&xenstore_notifier);
+
+       return 0;
+}
+subsys_initcall(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
+
+MODULE_LICENSE("GPL");
index 58b6be9..4ff028f 100644 (file)
@@ -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
index 113ebd9..f4b1057 100644 (file)
@@ -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)
index a30ecac..29099a0 100644 (file)
@@ -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 <aia21@cantab.net>");
-MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2007 Anton Altaparmakov");
+MODULE_AUTHOR("Anton Altaparmakov <anton@tuxera.com>");
+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
index 43e2d7d..7a1d15f 100644 (file)
@@ -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);
diff --git a/tools/power/x86/turbostat/Makefile b/tools/power/x86/turbostat/Makefile
new file mode 100644 (file)
index 0000000..fd8e1f1
--- /dev/null
@@ -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 (file)
index 0000000..ff75125
--- /dev/null
@@ -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 <len.brown@intel.com>
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
new file mode 100644 (file)
index 0000000..4c6983d
--- /dev/null
@@ -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 <len.brown@intel.com>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <ctype.h>
+
+#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 <lenb@kernel.org>\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;
+}
diff --git a/tools/power/x86/x86_energy_perf_policy/Makefile b/tools/power/x86/x86_energy_perf_policy/Makefile
new file mode 100644 (file)
index 0000000..f458237
--- /dev/null
@@ -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 (file)
index 0000000..8eaaad6
--- /dev/null
@@ -0,0 +1,104 @@
+.\"  This page Copyright (C) 2010 Len Brown <len.brown@intel.com>
+.\"  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 <len.brown@intel.com>
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 (file)
index 0000000..d9678a3
--- /dev/null
@@ -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 <len.brown@intel.com>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <string.h>
+
+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 <lenb@kernel.org>\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;
+}
diff --git a/tools/testing/ktest/compare-ktest-sample.pl b/tools/testing/ktest/compare-ktest-sample.pl
new file mode 100755 (executable)
index 0000000..9a571e7
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+
+open (IN,"ktest.pl");
+while (<IN>) {
+    if (/\$opt\{"?([A-Z].*?)(\[.*\])?"?\}/ ||
+       /set_test_option\("(.*?)"/) {
+       $opt{$1} = 1;
+    }
+}
+close IN;
+
+open (IN, "sample.conf");
+while (<IN>) {
+    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";
+    }
+}
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl
new file mode 100755 (executable)
index 0000000..e1c62ee
--- /dev/null
@@ -0,0 +1,2023 @@
+#!/usr/bin/perl -w
+#
+# Copywrite 2010 - Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
+# Licensed under the terms of the GNU GPL License version 2
+#
+
+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;
+
+my $VERSION = "0.2";
+
+$| = 1;
+
+my %opt;
+my %repeat_tests;
+my %repeats;
+my %default;
+
+#default opts
+$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/ktest";
+$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;
+$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;
+$default{"LOCALVERSION"}       = "-test";
+
+my $ktest_config;
+my $version;
+my $machine;
+my $ssh_user;
+my $tmpdir;
+my $builddir;
+my $outputdir;
+my $output_config;
+my $test_type;
+my $build_type;
+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;
+my $target;
+my $make;
+my $post_install;
+my $noclean;
+my $minconfig;
+my $addconfig;
+my $in_bisect = 0;
+my $bisect_bad = "";
+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;
+my $sleep_time;
+my $bisect_sleep_time;
+my $store_failures;
+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;
+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} = <STDIN>;
+       $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) = @_;
+
+    if (defined($opt{$lvalue})) {
+       die "Error: Option $lvalue defined more than once!\n";
+    }
+    if ($rvalue =~ /^\s*$/) {
+       delete $opt{$lvalue};
+    } else {
+       $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 (<IN>) {
+
+       # ignore blank lines and comments
+       next if (/^\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;
+           my $old_repeat = $repeat;
+
+           $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 = $old_repeat;
+           }
+
+       } 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 (!$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;
+               }
+           }
+       } else {
+           die "$name: $.: Garbage found in config\n$_";
+       }
+    }
+
+    close(IN);
+
+    if ($test_num) {
+       $test_num += $repeat - 1;
+       $opt{"NUM_TESTS"} = $test_num;
+    }
+
+    # make sure we have all mandatory configs
+    get_ktest_configs;
+
+    # set any defaults
+
+    foreach my $default (keys %default) {
+       if (!defined($opt{$default})) {
+           $opt{$default} = $default{$default};
+       }
+    }
+}
+
+sub _logit {
+    if (defined($opt{"LOG_FILE"})) {
+       open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}";
+       print OUT @_;
+       close(OUT);
+    }
+}
+
+sub logit {
+    if (defined($opt{"LOG_FILE"})) {
+       _logit @_;
+    } else {
+       print @_;
+    }
+}
+
+sub doprint {
+    print @_;
+    _logit @_;
+}
+
+sub run_command;
+
+sub reboot {
+    # try to reboot normally
+    if (run_command $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";
+
+    my $i = $iteration;
+
+    if ($reboot_on_error && !do_not_reboot) {
+
+       doprint "REBOOTING\n";
+       reboot;
+
+    } elsif ($poweroff_on_error && defined($power_off)) {
+       doprint "POWERING OFF\n";
+       `$power_off`;
+    }
+
+    die @_, "\n";
+}
+
+sub open_console {
+    my ($fp) = @_;
+
+    my $flags;
+
+    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: $!";
+    $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or
+       dodie "Can't set flags for the socket: $!";
+
+    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;
+
+    return;
+
+    open(MONFD, "Stop perl from warning about single use of MONFD");
+}
+
+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);
+       print "$line" if (defined($line));
+    } while (defined($line));
+    print "** Monitor flushed **\n";
+}
+
+sub fail {
+
+       if ($die_on_failure) {
+               dodie @_;
+       }
+
+       doprint "FAILED\n";
+
+       my $i = $iteration;
+
+       # no need to reboot for just building.
+       if (!do_not_reboot) {
+           doprint "REBOOTING\n";
+           reboot;
+           start_monitor;
+           wait_for_monitor $sleep_time;
+           end_monitor;
+       }
+
+       doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
+       doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
+       doprint "KTEST RESULT: TEST $i Failed: ", @_, "\n";
+       doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
+       doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\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 $type = $build_type;
+       if ($type =~ /useconfig/) {
+           $type = "useconfig";
+       }
+
+       my $dir = "$machine-$test_type-$type-fail-$date";
+       my $faildir = "$store_failures/$dir";
+
+       if (!-d $faildir) {
+           mkpath($faildir) or
+               die "can't create $faildir";
+       }
+       if (-f "$output_config") {
+           cp "$output_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;
+}
+
+sub run_command {
+    my ($command) = @_;
+    my $dolog = 0;
+    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
+       (fail "unable to exec $command" and return 0);
+
+    if (defined($opt{"LOG_FILE"})) {
+       open(LOG, ">>$opt{LOG_FILE}") or
+           dodie "failed to write to log";
+       $dolog = 1;
+    }
+
+    if (defined($redirect)) {
+       open (RD, ">$redirect") or
+           dodie "failed to write to redirect $redirect";
+       $dord = 1;
+    }
+
+    while (<CMD>) {
+       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 {
+       doprint "SUCCESS\n";
+    }
+
+    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") {
+       return;
+    }
+    return if (defined($grub_number));
+
+    doprint "Find grub menu ... ";
+    $grub_number = -1;
+
+    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 (<IN>) {
+       if (/^\s*title\s+$grub_menu\s*$/) {
+           $grub_number++;
+           last;
+       } elsif (/^\s*title\s/) {
+           $grub_number++;
+       }
+    }
+    close(IN);
+
+    die "Could not find '$grub_menu' in /boot/grub/menu on $machine"
+       if ($grub_number < 0);
+    doprint "$grub_number\n";
+}
+
+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_to {
+    if ($reboot_type eq "grub") {
+       run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'";
+       return;
+    }
+
+    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;
+    my $skip_call_trace = 0;
+    my $loops;
+
+    wait_for_monitor 5;
+
+    my $line;
+    my $full_line = "";
+
+    open(DMESG, "> $dmesg") or
+       die "unable to write to $dmesg";
+
+    reboot_to;
+
+    my $success_start;
+    my $failure_start;
+
+    for (;;) {
+
+       if ($booted) {
+           $line = wait_for_input($monitor_fp, $booted_timeout);
+       } else {
+           $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;
+
+       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 \]/) {
+           $skip_call_trace = 1;
+       }
+
+       if ($full_line =~ /call trace:/i) {
+           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 \]/) {
+           $skip_call_trace = 0;
+       }
+
+       if ($full_line =~ /Kernel panic -/) {
+           $bug = 1;
+       }
+
+       if ($line =~ /\n/) {
+           $full_line = "";
+       }
+    }
+
+    close(DMESG);
+
+    if ($bug) {
+       return 0 if ($in_bisect);
+       fail "failed - got a bug report" and return 0;
+    }
+
+    if (!$booted) {
+       return 0 if ($in_bisect);
+       fail "failed - never got a boot prompt." and return 0;
+    }
+
+    return 1;
+}
+
+sub install {
+
+    run_scp "$outputdir/$build_target", "$target_image" or
+       dodie "failed to copy image";
+
+    my $install_mods = 0;
+
+    # should we process modules?
+    $install_mods = 0;
+    open(IN, "$output_config") or dodie("Can't read config file");
+    while (<IN>) {
+       if (/CONFIG_MODULES(=y)?/) {
+           $install_mods = 1 if (defined($1));
+           last;
+       }
+    }
+    close(IN);
+
+    if (!$install_mods) {
+       doprint "No modules needed\n";
+       return;
+    }
+
+    run_command "$make INSTALL_MOD_PATH=$tmpdir modules_install" or
+       dodie "Failed to install modules";
+
+    my $modlib = "/lib/modules/$version";
+    my $modtar = "ktest-mods.tar.bz2";
+
+    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_scp "$tmpdir/$modtar", "/tmp" or
+       dodie "failed to copy modules";
+
+    unlink "$tmpdir/$modtar";
+
+    run_ssh "'(cd / && tar xf /tmp/$modtar)'" or
+       dodie "failed to tar modules";
+
+    run_ssh "rm -f /tmp/$modtar";
+
+    return if (!defined($post_install));
+
+    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";
+}
+
+sub check_buildlog {
+    my ($patch) = @_;
+
+    my @files = `git show $patch | diffstat -l`;
+
+    open(IN, "git show $patch |") or
+       dodie "failed to show $patch";
+    while (<IN>) {
+       if (m,^--- a/(.*),) {
+           chomp $1;
+           $files[$#files] = $1;
+       }
+    }
+    close(IN);
+
+    open(IN, $buildlog) or dodie "Can't open $buildlog";
+    while (<IN>) {
+       if (/^\s*(.*?):.*(warning|error)/) {
+           my $err = $1;
+           foreach my $file (@files) {
+               my $fullpath = "$builddir/$file";
+               if ($file eq $err || $fullpath eq $err) {
+                   fail "$file built with warnings" and return 0;
+               }
+           }
+       }
+    }
+    close(IN);
+
+    return 1;
+}
+
+sub build {
+    my ($type) = @_;
+    my $defconfig = "";
+
+    unlink $buildlog;
+
+    if ($type =~ /^useconfig:(.*)/) {
+       run_command "cp $1 $output_config" or
+           dodie "could not copy $1 to .config";
+
+       $type = "oldconfig";
+    }
+
+    # old config can ask questions
+    if ($type eq "oldconfig") {
+       $type = "oldnoconfig";
+
+       # allow for empty configs
+       run_command "touch $output_config";
+
+       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 $output_config" or
+           dodie "moving config_temp";
+
+    } elsif (!$noclean) {
+       unlink "$output_config";
+       run_command "$make mrproper" or
+           dodie "make mrproper";
+    }
+
+    # add something to distinguish this build
+    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 $make $type" or
+       dodie "failed make config";
+
+    $redirect = "$buildlog";
+    if (!run_command "$make $build_options") {
+       undef $redirect;
+       # bisect may need this to pass
+       return 0 if ($in_bisect);
+       fail "failed build" and return 0;
+    }
+    undef $redirect;
+
+    return 1;
+}
+
+sub halt {
+    if (!run_ssh "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";
+    }
+}
+
+sub success {
+    my ($i) = @_;
+
+    $successes++;
+
+    doprint "\n\n*******************************************\n";
+    doprint     "*******************************************\n";
+    doprint     "KTEST RESULT: TEST $i SUCCESS!!!!         **\n";
+    doprint     "*******************************************\n";
+    doprint     "*******************************************\n";
+
+    if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) {
+       doprint "Reboot and wait $sleep_time seconds\n";
+       reboot;
+       start_monitor;
+       wait_for_monitor $sleep_time;
+       end_monitor;
+    }
+}
+
+sub get_version {
+    # get the release name
+    doprint "$make kernelrelease ... ";
+    $version = `$make kernelrelease | tail -1`;
+    chomp($version);
+    doprint "$version\n";
+}
+
+sub child_run_test {
+    my $failed = 0;
+
+    # child should have no power
+    $reboot_on_error = 0;
+    $poweroff_on_error = 0;
+    $die_on_failure = 1;
+
+    run_command $run_test or $failed = 1;
+    exit $failed;
+}
+
+my $child_done;
+
+sub child_finished {
+    $child_done = 1;
+}
+
+sub do_run_test {
+    my $child_pid;
+    my $child_exit;
+    my $line;
+    my $full_line;
+    my $bug = 0;
+
+    wait_for_monitor 1;
+
+    doprint "run test $run_test\n";
+
+    $child_done = 0;
+
+    $SIG{CHLD} = qw(child_finished);
+
+    $child_pid = fork;
+
+    child_run_test if (!$child_pid);
+
+    $full_line = "";
+
+    do {
+       $line = wait_for_input($monitor_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 = $?;
+
+    if ($bug || $child_exit) {
+       return 0 if $in_bisect;
+       fail "test failed" and return 0;
+    }
+    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;
+}
+
+# returns 1 on success, 0 on failure
+sub run_bisect_test {
+    my ($type, $buildtype) = @_;
+
+    my $failed = 0;
+    my $result;
+    my $output;
+    my $ret;
+
+    $in_bisect = 1;
+
+    build $buildtype or $failed = 1;
+
+    if ($type ne "build") {
+       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") {
+           dodie "Failed on boot" if $failed;
+
+           do_run_test or $failed = 1;
+       }
+       end_monitor;
+    }
+
+    if ($failed) {
+       $result = 0;
+
+       # reboot the box to a good kernel
+       if ($type ne "build") {
+           doprint "Reboot and sleep $bisect_sleep_time seconds\n";
+           reboot;
+           start_monitor;
+           wait_for_monitor $bisect_sleep_time;
+           end_monitor;
+       }
+    } else {
+       $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) {
+       $ret = !$ret;
+    }
+
+    if ($ret) {
+       return "good";
+    } else {
+       return  "bad";
+    }
+}
+
+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]"};
+    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";
+       $reverse_bisect = 1;
+    } else {
+       $reverse_bisect = 0;
+    }
+
+    # 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
+       my $head = get_sha1("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
+       dodie "could not start bisect";
+
+    run_command "git bisect good $good" or
+       dodie "could not set bisect good to $good";
+
+    run_git_bisect "git bisect bad $bad" or
+       dodie "could not set bisect bad to $bad";
+
+    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;
+       $test = run_git_bisect "git bisect $result";
+    } while ($test);
+
+    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";
+
+    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 (<IN>) {
+       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 (<IN>) {
+       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};
+       }
+    }
+    doprint "config copied to $outputdir/config_good\n";
+    run_command "cp -f $output_config $outputdir/config_good";
+}
+
+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";
+       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) {
+           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 (<IN>) {
+       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 (<IN>) {
+       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;
+}
+
+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]"};
+    }
+
+    # 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
+    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 (<IN>) {
+       chomp;
+       $list[$#list+1] = $_;
+       last if (/^$start/);
+    }
+    close(IN);
+
+    if ($list[$#list] !~ /^$start/) {
+       fail "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" or return 0;
+       } else {
+           # ?? no config to use?
+           build "oldconfig" or return 0;
+       }
+
+       check_buildlog $sha1 or return 0;
+
+       next if ($type eq "build");
+
+       get_grub_index;
+       get_version;
+       install;
+
+       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;
+
+    return 1;
+}
+
+$#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 = <STDIN>;
+           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"};
+}
+
+doprint "\n\nSTARTING AUTOMATED TESTS\n\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 {
+    my ($name, $i) = @_;
+
+    my $option = "$name\[$i\]";
+
+    if (defined($opt{$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};
+    }
+
+    return undef;
+}
+
+# First we need to do is the builds
+for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
+
+    $iteration = $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);
+    $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);
+    $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);
+    $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);
+    $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);
+    $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);
+    $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);
+    $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";
+    }
+
+    $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";
+
+    if ($reboot_type eq "grub") {
+       dodie "GRUB_MENU not defined" if (!defined($grub_menu));
+    } elsif (!defined($reboot_script)) {
+       dodie "REBOOT_SCRIPT not defined"
+    }
+
+    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]"};
+    } elsif ($test_type eq "config_bisect") {
+       $run_type = $opt{"CONFIG_BISECT_TYPE[$i]"};
+    }
+
+    # mistake in config file?
+    if (!defined($run_type)) {
+       $run_type = "ERROR";
+    }
+
+    doprint "\n\n";
+    doprint "RUNNING TEST $i of $opt{NUM_TESTS} with option $test_type $run_type\n\n";
+
+    unlink $dmesg;
+    unlink $buildlog;
+
+    if (!defined($minconfig)) {
+       $minconfig = $addconfig;
+
+    } elsif (defined($addconfig)) {
+       run_command "cat $addconfig $minconfig > $tmpdir/add_config" or
+           dodie "Failed to create temp config";
+       $minconfig = "$tmpdir/add_config";
+    }
+
+    my $checkout = $opt{"CHECKOUT[$i]"};
+    if (defined($checkout)) {
+       run_command "git checkout $checkout" or
+           die "failed to checkout $checkout";
+    }
+
+    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;
+    }
+
+    if ($build_type ne "nobuild") {
+       build $build_type or next;
+    }
+
+    if ($test_type ne "build") {
+       get_grub_index;
+       get_version;
+       install;
+
+       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);
+    }
+
+    success $i;
+}
+
+if ($opt{"POWEROFF_ON_SUCCESS"}) {
+    halt;
+} elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot) {
+    reboot;
+}
+
+doprint "\n    $successes of $opt{NUM_TESTS} tests were successful\n\n";
+
+exit 0;
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf
new file mode 100644 (file)
index 0000000..3408c59
--- /dev/null
@@ -0,0 +1,622 @@
+#
+# Config file for ktest.pl
+#
+# Note, all paths must be absolute
+#
+
+# 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:
+#
+#  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 ####
+
+# 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
+
+# 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 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 Guest
+
+# 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)
+#
+# 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
+# (Only mandatory if REBOOT_TYPE = script)
+#REBOOT_SCRIPT =
+
+#### Optional Config Options (all have defaults) ####
+
+# 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
+
+# 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
+#   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
+
+# 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)
+# 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
+# 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"
+# (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.
+#
+# The entry in /boot/grub/menu.lst must be entered in manually.
+# 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="<your options here>"
+#  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:
+#  (do not add any quotes around it)
+#
+#  SUCCESS_LINE = ^MyBox Login:$
+#
+# (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
+# 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.
+# (in seconds) (default 1)
+#BOOTED_TIMEOUT = 1
+
+# The timeout in seconds when we consider the box hung after
+# 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
+
+# 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)
+# (default 60)
+#BISECT_SLEEP_TIME = 60
+
+# 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)
+#  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
+
+# Reboot the target after all test completed successfully (default 1)
+# (ignored if POWEROFF_ON_SUCCESS is set)
+#REBOOT_ON_SUCCESS = 1
+
+# 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
+# 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
+
+# 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 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.
+#
+# All of these are optional and undefined by default, although
+#  some of these options are required for TEST_TYPE of patchcheck
+#  and bisect.
+#
+#
+# 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 is set.
+#
+#
+#
+# For TEST_TYPE = patchcheck
+#
+#  This expects the BUILD_DIR to be a git repository, and
+#  will checkout the PATCHCHECK_START commit.
+#
+#  The option BUILD_TYPE will be ignored.
+#
+#  The MIN_CONFIG will be used for all builds of the patchcheck. The build type
+#  used for patchcheck is oldconfig.
+#
+#  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_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
+#     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_START
+#   TEST_TYPE = patchcheck
+#   CHECKOUT = mybranch
+#   PATCHCHECK_TYPE = boot
+#   PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7
+#   PATCHCHECK_END = HEAD~2
+#
+#
+#
+# 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.
+#
+#  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 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 = /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 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
+#
+#   The adding:
+#
+#    BISECT_REPLAY= /path/to/replay/file
+#
+#   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.
+#
+# 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 = 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 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 = 1 (optional, default 0)
+#
+#   Just to be sure the good is good and bad is bad, setting
+#   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 or
+#   BISECT_BAD with BISECT_CHECK = good or
+#   BISECT_CHECK = bad, respectively.
+#
+# Example:
+#   TEST_START
+#   TEST_TYPE = bisect
+#   BISECT_GOOD = v2.6.36
+#   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
+#