Merge branches 'pm-core', 'pm-clk', 'pm-domains' and 'pm-pci'
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 25 Jul 2016 11:45:27 +0000 (13:45 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 25 Jul 2016 11:45:27 +0000 (13:45 +0200)
* pm-core:
  PM / runtime: Asynchronous "idle" in pm_runtime_allow()
  PM / runtime: print error when activating a child to unactive parent

* pm-clk:
  PM / clk: Add support for adding a specific clock from device-tree
  PM / clk: export symbols for existing pm_clk_<...> API fcns

* pm-domains:
  PM / Domains: Convert pm_genpd_init() to return an error code
  PM / Domains: Stop/start devices during system PM suspend/resume in genpd
  PM / Domains: Allow runtime PM during system PM phases
  PM / Runtime: Avoid resuming devices again in pm_runtime_force_resume()
  PM / Domains: Remove redundant pm_request_idle() call in genpd
  PM / Domains: Remove redundant wrapper functions for system PM
  PM / Domains: Allow genpd to power on during system PM phases

* pm-pci:
  PCI / PM: check all fields in pci_set_platform_pm()

255 files changed:
Documentation/gdb-kernel-debugging.txt
Documentation/kernel-parameters.txt
MAINTAINERS
Makefile
arch/arm/boot/dts/armada-385-linksys.dtsi
arch/arm/boot/dts/sun4i-a10.dtsi
arch/arm/boot/dts/sun5i-a10s.dtsi
arch/arm/boot/dts/sun5i-r8-chip.dts
arch/arm/boot/dts/sun7i-a20.dtsi
arch/arm/boot/dts/tegra30-beaver.dts
arch/arm/mach-mvebu/Makefile
arch/arm/mach-mvebu/coherency.c
arch/m32r/boot/compressed/m32r_sio.c
arch/m68k/coldfire/head.S
arch/m68k/coldfire/m5272.c
arch/m68k/coldfire/pci.c
arch/m68k/configs/amiga_defconfig
arch/m68k/configs/apollo_defconfig
arch/m68k/configs/atari_defconfig
arch/m68k/configs/bvme6000_defconfig
arch/m68k/configs/hp300_defconfig
arch/m68k/configs/mac_defconfig
arch/m68k/configs/multi_defconfig
arch/m68k/configs/mvme147_defconfig
arch/m68k/configs/mvme16x_defconfig
arch/m68k/configs/q40_defconfig
arch/m68k/configs/sun3_defconfig
arch/m68k/configs/sun3x_defconfig
arch/m68k/ifpsp060/src/fpsp.S
arch/m68k/ifpsp060/src/pfpsp.S
arch/m68k/include/asm/dma.h
arch/m68k/include/asm/m525xsim.h
arch/m68k/include/asm/mcfmmu.h
arch/m68k/include/asm/q40_master.h
arch/m68k/mac/iop.c
arch/m68k/math-emu/fp_decode.h
arch/x86/events/core.c
arch/x86/include/asm/cpufeatures.h
arch/x86/include/asm/smp.h
arch/x86/kernel/early-quirks.c
arch/x86/kernel/smpboot.c
arch/x86/mm/kasan_init_64.c
arch/x86/power/cpu.c
crypto/asymmetric_keys/mscode_parser.c
crypto/asymmetric_keys/pkcs7_verify.c
crypto/asymmetric_keys/restrict.c
crypto/rsa-pkcs1pad.c
drivers/acpi/acpica/exconfig.c
drivers/acpi/acpica/nsload.c
drivers/acpi/ec.c
drivers/acpi/nfit.c
drivers/acpi/utils.c
drivers/ata/libata-core.c
drivers/base/power/clock_ops.c
drivers/base/power/domain.c
drivers/base/power/runtime.c
drivers/bcma/bcma_private.h
drivers/clk/at91/clk-programmable.c
drivers/clk/sunxi/clk-sun4i-display.c
drivers/clk/sunxi/clk-sun4i-tcon-ch1.c
drivers/crypto/qat/qat_common/Makefile
drivers/devfreq/Kconfig
drivers/devfreq/devfreq-event.c
drivers/devfreq/devfreq.c
drivers/devfreq/event/Kconfig
drivers/devfreq/event/exynos-ppmu.c
drivers/devfreq/exynos-bus.c
drivers/edac/sb_edac.c
drivers/gpio/Kconfig
drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
drivers/gpu/drm/amd/amdgpu/atombios_i2c.c
drivers/gpu/drm/amd/amdgpu/atombios_i2c.h
drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem_shrinker.c
drivers/gpu/drm/i915/i915_gem_stolen.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_csr.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_lrc.c
drivers/gpu/drm/i915/intel_opregion.c
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/ttm/ttm_bo.c
drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/gpu/drm/vmwgfx/vmwgfx_msg.c
drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
drivers/i2c/busses/i2c-qup.c
drivers/i2c/busses/i2c-tegra.c
drivers/i2c/i2c-boardinfo.c
drivers/i2c/muxes/i2c-mux-reg.c
drivers/infiniband/core/sysfs.c
drivers/infiniband/hw/hfi1/chip.c
drivers/infiniband/hw/hfi1/ud.c
drivers/infiniband/hw/i40iw/i40iw_main.c
drivers/infiniband/hw/i40iw/i40iw_verbs.c
drivers/input/joystick/xpad.c
drivers/input/rmi4/rmi_bus.c
drivers/input/rmi4/rmi_f12.c
drivers/input/touchscreen/ts4800-ts.c
drivers/input/touchscreen/tsc2004.c
drivers/input/touchscreen/tsc2005.c
drivers/input/touchscreen/tsc200x-core.c
drivers/input/touchscreen/tsc200x-core.h
drivers/input/touchscreen/wacom_w8001.c
drivers/media/i2c/adv7604.c
drivers/media/usb/airspy/airspy.c
drivers/media/v4l2-core/v4l2-ioctl.c
drivers/mmc/card/block.c
drivers/mmc/host/pxamci.c
drivers/mtd/nand/omap2.c
drivers/net/bonding/bond_netlink.c
drivers/net/ethernet/agere/et131x.c
drivers/net/ethernet/aurora/nb8800.c
drivers/net/ethernet/broadcom/bgmac.c
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
drivers/net/ethernet/cavium/liquidio/lio_main.c
drivers/net/ethernet/ethoc.c
drivers/net/ethernet/ezchip/nps_enet.c
drivers/net/ethernet/ibm/ibmvnic.c
drivers/net/ethernet/ibm/ibmvnic.h
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40evf/i40e_txrx.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
drivers/net/ethernet/mellanox/mlx4/en_netdev.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/vxlan.c
drivers/net/ethernet/mellanox/mlxsw/reg.h
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c
drivers/net/ppp/ppp_generic.c
drivers/net/usb/r8152.c
drivers/nvme/host/core.c
drivers/pps/clients/pps_parport.c
drivers/regulator/qcom_smd-regulator.c
drivers/tty/vt/keyboard.c
fs/9p/vfs_inode.c
fs/9p/vfs_inode_dotl.c
fs/ceph/file.c
fs/cifs/dir.c
fs/fuse/dir.c
fs/gfs2/inode.c
fs/nfs/dir.c
fs/overlayfs/dir.c
fs/overlayfs/inode.c
fs/overlayfs/overlayfs.h
fs/xfs/xfs_ioctl.c
include/acpi/acpixf.h
include/asm-generic/vmlinux.lds.h
include/drm/ttm/ttm_bo_api.h
include/linux/bcma/bcma.h
include/linux/filter.h
include/linux/huge_mm.h
include/linux/memcontrol.h
include/linux/netdevice.h
include/linux/pm_clock.h
include/linux/pm_domain.h
include/linux/posix_acl.h
include/linux/radix-tree.h
include/linux/rmap.h
include/linux/suspend.h
include/net/netfilter/nf_conntrack.h
include/net/sock.h
include/net/switchdev.h
include/uapi/linux/Kbuild
include/uapi/linux/input-event-codes.h
kernel/cpu.c
kernel/gcov/gcc_4_7.c
kernel/power/Makefile
kernel/power/console.c
kernel/power/hibernate.c
kernel/power/main.c
kernel/power/power.h
kernel/power/process.c
kernel/power/snapshot.c
kernel/power/suspend.c
kernel/power/swap.c
kernel/power/user.c
kernel/sched/core.c
kernel/sched/loadavg.c
kernel/sched/sched.h
kernel/time/posix-cpu-timers.c
kernel/workqueue.c
mm/compaction.c
mm/huge_memory.c
mm/hugetlb.c
mm/kasan/quarantine.c
mm/memcontrol.c
mm/memory.c
mm/page_alloc.c
mm/rmap.c
mm/slab_common.c
mm/workingset.c
net/8021q/vlan_dev.c
net/8021q/vlan_netlink.c
net/batman-adv/bridge_loop_avoidance.c
net/batman-adv/distributed-arp-table.c
net/batman-adv/originator.c
net/batman-adv/routing.c
net/batman-adv/send.c
net/batman-adv/types.h
net/ceph/osdmap.c
net/core/filter.c
net/core/sock.c
net/dccp/ipv4.c
net/dccp/ipv6.c
net/ipv4/fib_semantics.c
net/ipv4/tcp_input.c
net/ipv4/udp.c
net/ipv6/udp.c
net/netfilter/ipvs/ip_vs_sync.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_tables_api.c
net/netfilter/nft_ct.c
net/netfilter/nft_meta.c
net/packet/af_packet.c
net/rose/rose_in.c
net/sched/sch_htb.c
net/sctp/input.c
net/tipc/bearer.c
net/tipc/bearer.h
net/tipc/link.c
net/tipc/node.c
net/wireless/nl80211.c
net/wireless/util.c
scripts/analyze_suspend.py
scripts/gdb/linux/.gitignore
scripts/gdb/linux/Makefile
scripts/gdb/linux/constants.py.in
scripts/gdb/linux/radixtree.py [deleted file]
scripts/gdb/linux/symbols.py
scripts/gdb/vmlinux-gdb.py
sound/core/control.c
sound/core/pcm.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_realtek.c
sound/usb/card.c
tools/objtool/builtin-check.c
tools/power/x86/turbostat/Makefile
tools/testing/radix-tree/tag_check.c
tools/vm/slabinfo.c

index 4ab7d43..7050ce8 100644 (file)
@@ -139,27 +139,6 @@ Examples of using the Linux-provided gdb helpers
       start_comm = "swapper/2\000\000\000\000\000\000"
     }
 
- o Dig into a radix tree data structure, such as the IRQ descriptors:
-    (gdb) print (struct irq_desc)$lx_radix_tree_lookup(irq_desc_tree, 18)
-    $6 = {
-      irq_common_data = {
-        state_use_accessors = 67584,
-        handler_data = 0x0 <__vectors_start>,
-        msi_desc = 0x0 <__vectors_start>,
-        affinity = {{
-            bits = {65535}
-          }}
-      },
-      irq_data = {
-        mask = 0,
-        irq = 18,
-        hwirq = 27,
-        common = 0xee803d80,
-        chip = 0xc0eb0854 <gic_data>,
-        domain = 0xee808000,
-        parent_data = 0x0 <__vectors_start>,
-        chip_data = 0xc0eb0854 <gic_data>
-      } <... trimmed ...>
 
 List of commands and functions
 ------------------------------
index 82b42c9..07960c2 100644 (file)
@@ -3594,6 +3594,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                                present during boot.
                nocompress      Don't compress/decompress hibernation images.
                no              Disable hibernation and resume.
+               protect_image   Turn on image protection during restoration
+                               (that will set all pages holding image data
+                               during restoration read-only).
 
        retain_initrd   [RAM] Keep initrd memory after extraction
 
index 1209323..8c20323 100644 (file)
@@ -1694,8 +1694,6 @@ S:        Maintained
 F:     drivers/edac/altera_edac.
 
 ARM/STI ARCHITECTURE
-M:     Srinivas Kandagatla <srinivas.kandagatla@gmail.com>
-M:     Maxime Coquelin <maxime.coquelin@st.com>
 M:     Patrice Chotard <patrice.chotard@st.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 L:     kernel@stlinux.com
@@ -1728,6 +1726,7 @@ F:        drivers/ata/ahci_st.c
 
 ARM/STM32 ARCHITECTURE
 M:     Maxime Coquelin <mcoquelin.stm32@gmail.com>
+M:     Alexandre Torgue <alexandre.torgue@st.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mcoquelin/stm32.git
@@ -4477,7 +4476,7 @@ S:        Orphan
 F:     fs/efs/
 
 EHEA (IBM pSeries eHEA 10Gb ethernet adapter) DRIVER
-M:     Thadeu Lima de Souza Cascardo <cascardo@linux.vnet.ibm.com>
+M:     Douglas Miller <dougmill@linux.vnet.ibm.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ethernet/ibm/ehea/
@@ -7476,6 +7475,7 @@ Q:        http://patchwork.ozlabs.org/project/linux-mtd/list/
 T:     git git://git.infradead.org/linux-mtd.git
 T:     git git://git.infradead.org/l2-mtd.git
 S:     Maintained
+F:     Documentation/devicetree/bindings/mtd/
 F:     drivers/mtd/
 F:     include/linux/mtd/
 F:     include/uapi/mtd/
index 81b2262..66da9a3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 4
 PATCHLEVEL = 7
 SUBLEVEL = 0
-EXTRAVERSION = -rc7
+EXTRAVERSION =
 NAME = Psychotic Stoned Sheep
 
 # *DOCUMENTATION*
index 8450944..22f7a13 100644 (file)
@@ -58,8 +58,8 @@
        soc {
                ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
                          MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
-                         MBUS_ID(0x09, 0x09) 0 0xf1100000 0x10000
-                         MBUS_ID(0x09, 0x05) 0 0xf1110000 0x10000>;
+                         MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
+                         MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
 
                internal-regs {
 
index a03e56f..ca58eb2 100644 (file)
@@ -65,8 +65,9 @@
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0-hdmi";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 43>,
-                                <&ahb_gates 44>, <&dram_gates 26>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 43>, <&ahb_gates 44>,
+                                <&dram_gates 26>;
                        status = "disabled";
                };
 
@@ -74,8 +75,9 @@
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_fe0-de_be0-lcd0-hdmi";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 43>,
-                                <&ahb_gates 44>, <&ahb_gates 46>,
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 43>, <&ahb_gates 44>,
+                                <&ahb_gates 46>,
                                 <&dram_gates 25>, <&dram_gates 26>;
                        status = "disabled";
                };
@@ -84,9 +86,9 @@
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_fe0-de_be0-lcd0";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 44>,
-                                <&ahb_gates 46>, <&dram_gates 25>,
-                                <&dram_gates 26>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 44>, <&ahb_gates 46>,
+                                <&dram_gates 25>, <&dram_gates 26>;
                        status = "disabled";
                };
 
@@ -94,8 +96,9 @@
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_fe0-de_be0-lcd0-tve0";
-                       clocks = <&pll5 1>, <&ahb_gates 34>, <&ahb_gates 36>,
-                                <&ahb_gates 44>, <&ahb_gates 46>,
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 34>,
+                                <&ahb_gates 36>, <&ahb_gates 44>,
+                                <&ahb_gates 46>,
                                 <&dram_gates 5>, <&dram_gates 25>, <&dram_gates 26>;
                        status = "disabled";
                };
index bddd0de..367f330 100644 (file)
@@ -65,8 +65,8 @@
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0-hdmi";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 43>,
-                                <&ahb_gates 44>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 43>, <&ahb_gates 44>;
                        status = "disabled";
                };
 
@@ -74,7 +74,8 @@
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 44>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 44>;
                        status = "disabled";
                };
 
@@ -82,8 +83,8 @@
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0-tve0";
-                       clocks = <&pll5 1>, <&ahb_gates 34>, <&ahb_gates 36>,
-                                <&ahb_gates 44>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 34>,
+                                <&ahb_gates 36>, <&ahb_gates 44>;
                        status = "disabled";
                };
        };
index a8d8b45..f694482 100644 (file)
@@ -52,7 +52,7 @@
 
 / {
        model = "NextThing C.H.I.P.";
-       compatible = "nextthing,chip", "allwinner,sun5i-r8";
+       compatible = "nextthing,chip", "allwinner,sun5i-r8", "allwinner,sun5i-a13";
 
        aliases {
                i2c0 = &i2c0;
index febdf4c..2c34bbb 100644 (file)
@@ -67,8 +67,9 @@
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0-hdmi";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 43>,
-                                <&ahb_gates 44>, <&dram_gates 26>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 43>, <&ahb_gates 44>,
+                                <&dram_gates 26>;
                        status = "disabled";
                };
 
@@ -76,8 +77,8 @@
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 44>,
-                                <&dram_gates 26>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 44>, <&dram_gates 26>;
                        status = "disabled";
                };
 
@@ -85,7 +86,7 @@
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0-tve0";
-                       clocks = <&pll5 1>,
+                       clocks = <&pll3>, <&pll5 1>,
                                 <&ahb_gates 34>, <&ahb_gates 36>, <&ahb_gates 44>,
                                 <&dram_gates 5>, <&dram_gates 26>;
                        status = "disabled";
                pll3x2: pll3x2_clk {
                        #clock-cells = <0>;
                        compatible = "fixed-factor-clock";
+                       clocks = <&pll3>;
                        clock-div = <1>;
                        clock-mult = <2>;
                        clock-output-names = "pll3-2x";
                pll7x2: pll7x2_clk {
                        #clock-cells = <0>;
                        compatible = "fixed-factor-clock";
+                       clocks = <&pll7>;
                        clock-div = <1>;
                        clock-mult = <2>;
                        clock-output-names = "pll7-2x";
index 1eca3b2..b6da15d 100644 (file)
 
                                ldo5_reg: ldo5 {
                                        regulator-name = "vddio_sdmmc,avdd_vdac";
-                                       regulator-min-microvolt = <3300000>;
+                                       regulator-min-microvolt = <1800000>;
                                        regulator-max-microvolt = <3300000>;
                                        regulator-always-on;
                                };
 
        sdhci@78000000 {
                status = "okay";
+               vqmmc-supply = <&ldo5_reg>;
                cd-gpios = <&gpio TEGRA_GPIO(I, 5) GPIO_ACTIVE_LOW>;
                wp-gpios = <&gpio TEGRA_GPIO(T, 3) GPIO_ACTIVE_HIGH>;
                power-gpios = <&gpio TEGRA_GPIO(D, 7) GPIO_ACTIVE_HIGH>;
index ecf9e0c..e53c6cf 100644 (file)
@@ -7,9 +7,15 @@ CFLAGS_pmsu.o                  := -march=armv7-a
 obj-$(CONFIG_MACH_MVEBU_ANY)    += system-controller.o mvebu-soc-id.o
 
 ifeq ($(CONFIG_MACH_MVEBU_V7),y)
-obj-y                           += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o pm.o pm-board.o
+obj-y                           += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o
+
+obj-$(CONFIG_PM)                += pm.o pm-board.o
 obj-$(CONFIG_SMP)               += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o
 endif
 
 obj-$(CONFIG_MACH_DOVE)                 += dove.o
-obj-$(CONFIG_MACH_KIRKWOOD)     += kirkwood.o kirkwood-pm.o
+
+ifeq ($(CONFIG_MACH_KIRKWOOD),y)
+obj-y                           += kirkwood.o
+obj-$(CONFIG_PM)                += kirkwood-pm.o
+endif
index 7e989d6..e80f0dd 100644 (file)
@@ -162,22 +162,16 @@ exit:
 }
 
 /*
- * This ioremap hook is used on Armada 375/38x to ensure that PCIe
- * memory areas are mapped as MT_UNCACHED instead of MT_DEVICE. This
- * is needed as a workaround for a deadlock issue between the PCIe
- * interface and the cache controller.
+ * This ioremap hook is used on Armada 375/38x to ensure that all MMIO
+ * areas are mapped as MT_UNCACHED instead of MT_DEVICE. This is
+ * needed for the HW I/O coherency mechanism to work properly without
+ * deadlock.
  */
 static void __iomem *
-armada_pcie_wa_ioremap_caller(phys_addr_t phys_addr, size_t size,
-                             unsigned int mtype, void *caller)
+armada_wa_ioremap_caller(phys_addr_t phys_addr, size_t size,
+                        unsigned int mtype, void *caller)
 {
-       struct resource pcie_mem;
-
-       mvebu_mbus_get_pcie_mem_aperture(&pcie_mem);
-
-       if (pcie_mem.start <= phys_addr && (phys_addr + size) <= pcie_mem.end)
-               mtype = MT_UNCACHED;
-
+       mtype = MT_UNCACHED;
        return __arm_ioremap_caller(phys_addr, size, mtype, caller);
 }
 
@@ -186,7 +180,8 @@ static void __init armada_375_380_coherency_init(struct device_node *np)
        struct device_node *cache_dn;
 
        coherency_cpu_base = of_iomap(np, 0);
-       arch_ioremap_caller = armada_pcie_wa_ioremap_caller;
+       arch_ioremap_caller = armada_wa_ioremap_caller;
+       pci_ioremap_set_mem_type(MT_UNCACHED);
 
        /*
         * We should switch the PL310 to I/O coherency mode only if
index 01d877c..cf3023d 100644 (file)
@@ -8,12 +8,13 @@
 
 #include <asm/processor.h>
 
-static void putc(char c);
+static void m32r_putc(char c);
 
 static int puts(const char *s)
 {
        char c;
-       while ((c = *s++)) putc(c);
+       while ((c = *s++))
+               m32r_putc(c);
        return 0;
 }
 
@@ -41,7 +42,7 @@ static int puts(const char *s)
 #define BOOT_SIO0TXB   PLD_ESIO0TXB
 #endif
 
-static void putc(char c)
+static void m32r_putc(char c)
 {
        while ((*BOOT_SIO0STS & 0x3) != 0x3)
                cpu_relax();
@@ -61,7 +62,7 @@ static void putc(char c)
 #define SIO0TXB        (volatile unsigned short *)(0x00efd000 + 30)
 #endif
 
-static void putc(char c)
+static void m32r_putc(char c)
 {
        while ((*SIO0STS & 0x1) == 0)
                cpu_relax();
index fa31be2..73d92ea 100644 (file)
@@ -288,7 +288,7 @@ _clear_bss:
 #endif
 
        /*
-        *      Assember start up done, start code proper.
+        *      Assembler start up done, start code proper.
         */
        jsr     start_kernel                    /* start Linux kernel */
 
index c525e4c..9abb1a4 100644 (file)
@@ -111,7 +111,7 @@ void __init config_BSP(char *commandp, int size)
 /***************************************************************************/
 
 /*
- * Some 5272 based boards have the FEC ethernet diectly connected to
+ * Some 5272 based boards have the FEC ethernet directly connected to
  * an ethernet switch. In this case we need to use the fixed phy type,
  * and we need to declare it early in boot.
  */
index 821de92..6a640be 100644 (file)
@@ -42,7 +42,7 @@ static unsigned long iospace;
 
 /*
  * We need to be carefull probing on bus 0 (directly connected to host
- * bridge). We should only acccess the well defined possible devices in
+ * bridge). We should only access the well defined possible devices in
  * use, ignore aliases and the like.
  */
 static unsigned char mcf_host_slot2sid[32] = {
index 3ee6976..8f5b6f7 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -359,6 +360,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -553,7 +555,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index e96787f..31bded9 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -341,6 +342,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -512,7 +514,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 083fe6b..0d7739e 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -350,6 +351,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -533,7 +535,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 475130c..2cbb5c4 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -340,6 +341,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -504,7 +506,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 4339658..96102a4 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -341,6 +342,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -514,7 +516,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 831cc8c..97d88f7 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -357,6 +358,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -536,7 +538,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 6377afe..be25ef2 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -390,6 +391,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -616,7 +618,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 4304b3d..a008344 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -339,6 +340,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -504,7 +506,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 074bda4..6735a25 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -340,6 +341,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -504,7 +506,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 07b9fa8..780c6e9 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -346,6 +347,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -527,7 +529,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 36e6fae..44693cf 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -337,6 +338,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -506,7 +508,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 903acf9..ef0071d 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -337,6 +338,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -506,7 +508,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 78cb60f..9bbffeb 100644 (file)
@@ -10191,7 +10191,7 @@ xdnrm_con:
 xdnrm_sd:
        mov.l           %a1,-(%sp)
        tst.b           LOCAL_EX(%a0)           # is denorm pos or neg?
-       smi.b           %d1                     # set d0 accodingly
+       smi.b           %d1                     # set d0 accordingly
        bsr.l           unf_sub
        mov.l           (%sp)+,%a1
 xdnrm_exit:
@@ -10990,7 +10990,7 @@ src_qnan_m:
 # routines where an instruction is selected by an index into
 # a large jump table corresponding to a given instruction which
 # has been decoded. Flow continues here where we now decode
-# further accoding to the source operand type.
+# further according to the source operand type.
 #
 
        global          fsinh
@@ -23196,14 +23196,14 @@ m_sign:
 #
 #  1. Branch on the sign of the adjusted exponent.
 #  2p.(positive exp)
-#   2. Check M16 and the digits in lwords 2 and 3 in decending order.
+#   2. Check M16 and the digits in lwords 2 and 3 in descending order.
 #   3. Add one for each zero encountered until a non-zero digit.
 #   4. Subtract the count from the exp.
 #   5. Check if the exp has crossed zero in #3 above; make the exp abs
 #         and set SE.
 #      6. Multiply the mantissa by 10**count.
 #  2n.(negative exp)
-#   2. Check the digits in lwords 3 and 2 in decending order.
+#   2. Check the digits in lwords 3 and 2 in descending order.
 #   3. Add one for each zero encountered until a non-zero digit.
 #   4. Add the count to the exp.
 #   5. Check if the exp has crossed zero in #3 above; clear SE.
index 4aedef9..3535e6c 100644 (file)
@@ -13156,14 +13156,14 @@ m_sign:
 #
 #  1. Branch on the sign of the adjusted exponent.
 #  2p.(positive exp)
-#   2. Check M16 and the digits in lwords 2 and 3 in decending order.
+#   2. Check M16 and the digits in lwords 2 and 3 in descending order.
 #   3. Add one for each zero encountered until a non-zero digit.
 #   4. Subtract the count from the exp.
 #   5. Check if the exp has crossed zero in #3 above; make the exp abs
 #         and set SE.
 #      6. Multiply the mantissa by 10**count.
 #  2n.(negative exp)
-#   2. Check the digits in lwords 3 and 2 in decending order.
+#   2. Check the digits in lwords 3 and 2 in descending order.
 #   3. Add one for each zero encountered until a non-zero digit.
 #   4. Add the count to the exp.
 #   5. Check if the exp has crossed zero in #3 above; clear SE.
index 429fe26..208b4da 100644 (file)
@@ -18,7 +18,7 @@
  * AUG/22/2000 : added support for 32-bit Dual-Address-Mode (K) 2000
  *               Oliver Kamphenkel (O.Kamphenkel@tu-bs.de)
  *
- * AUG/25/2000 : addad support for 8, 16 and 32-bit Single-Address-Mode (K)2000
+ * AUG/25/2000 : added support for 8, 16 and 32-bit Single-Address-Mode (K)2000
  *               Oliver Kamphenkel (O.Kamphenkel@tu-bs.de)
  *
  * APR/18/2002 : added proper support for MCF5272 DMA controller.
index f186459..699f20c 100644 (file)
 /*
  *     I2C module.
  */
-#define MCFI2C_BASE0           (MCF_MBAR + 0x280)      /* Base addreess I2C0 */
+#define MCFI2C_BASE0           (MCF_MBAR + 0x280)      /* Base address I2C0 */
 #define MCFI2C_SIZE0           0x20                    /* Register set size */
 
-#define MCFI2C_BASE1           (MCF_MBAR2 + 0x440)     /* Base addreess I2C1 */
+#define MCFI2C_BASE1           (MCF_MBAR2 + 0x440)     /* Base address I2C1 */
 #define MCFI2C_SIZE1           0x20                    /* Register set size */
 
 /*
index 26cc3d5..8824236 100644 (file)
@@ -38,7 +38,7 @@
 /*
  *     MMU Operation register.
  */
-#define        MMUOR_UAA       0x00000001              /* Update allocatiom address */
+#define        MMUOR_UAA       0x00000001              /* Update allocation address */
 #define        MMUOR_ACC       0x00000002              /* TLB access */
 #define        MMUOR_RD        0x00000004              /* TLB access read */
 #define        MMUOR_WR        0x00000000              /* TLB access write */
index fc5b362..c48d21b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Q40 master Chip Control
- * RTC stuff merged for compactnes..
+ * RTC stuff merged for compactness.
 */
 
 #ifndef _Q40_MASTER_H
index 4d2adfb..7990b6f 100644 (file)
@@ -60,7 +60,7 @@
  *
  * The host talks to the IOPs using a rather simple message-passing scheme via
  * a shared memory area in the IOP RAM. Each IOP has seven "channels"; each
- * channel is conneced to a specific software driver on the IOP. For example
+ * channel is connected to a specific software driver on the IOP. For example
  * on the SCC IOP there is one channel for each serial port. Each channel has
  * an incoming and and outgoing message queue with a depth of one.
  *
index 759679d..6d1e760 100644 (file)
@@ -130,7 +130,7 @@ do_fscc=0
        bfextu  %d2{#13,#3},%d0
 .endm
 
-| decode the 8bit diplacement from the brief extension word
+| decode the 8bit displacement from the brief extension word
 .macro fp_decode_disp8
        move.b  %d2,%d0
        ext.w   %d0
index 26ced53..91eac39 100644 (file)
@@ -263,7 +263,7 @@ static bool check_hw_exists(void)
 
 msr_fail:
        pr_cont("Broken PMU hardware detected, using software events only.\n");
-       pr_info("%sFailed to access perfctr msr (MSR %x is %Lx)\n",
+       printk("%sFailed to access perfctr msr (MSR %x is %Lx)\n",
                boot_cpu_has(X86_FEATURE_HYPERVISOR) ? KERN_INFO : KERN_ERR,
                reg, val_new);
 
index 4a41348..c64b1e9 100644 (file)
 #define X86_BUG_FXSAVE_LEAK    X86_BUG(6) /* FXSAVE leaks FOP/FIP/FOP */
 #define X86_BUG_CLFLUSH_MONITOR        X86_BUG(7) /* AAI65, CLFLUSH required before MONITOR */
 #define X86_BUG_SYSRET_SS_ATTRS        X86_BUG(8) /* SYSRET doesn't fix up SS attrs */
-#define X86_BUG_NULL_SEG       X86_BUG(9) /* Nulling a selector preserves the base */
-#define X86_BUG_SWAPGS_FENCE   X86_BUG(10) /* SWAPGS without input dep on GS */
-
-
 #ifdef CONFIG_X86_32
 /*
  * 64-bit kernels don't use X86_BUG_ESPFIX.  Make the define conditional
  */
 #define X86_BUG_ESPFIX         X86_BUG(9) /* "" IRET to 16-bit SS corrupts ESP/RSP high bits */
 #endif
+#define X86_BUG_NULL_SEG       X86_BUG(10) /* Nulling a selector preserves the base */
+#define X86_BUG_SWAPGS_FENCE   X86_BUG(11) /* SWAPGS without input dep on GS */
 
 #endif /* _ASM_X86_CPUFEATURES_H */
index 66b0573..7427ca8 100644 (file)
@@ -135,6 +135,7 @@ int native_cpu_up(unsigned int cpunum, struct task_struct *tidle);
 int native_cpu_disable(void);
 int common_cpu_die(unsigned int cpu);
 void native_cpu_die(unsigned int cpu);
+void hlt_play_dead(void);
 void native_play_dead(void);
 void play_dead_common(void);
 void wbinvd_on_cpu(int cpu);
index bca14c8..57b7137 100644 (file)
 
 #include <linux/pci.h>
 #include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
 #include <linux/pci_ids.h>
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
 #include <drm/i915_drm.h>
 #include <asm/pci-direct.h>
 #include <asm/dma.h>
@@ -21,6 +25,9 @@
 #include <asm/iommu.h>
 #include <asm/gart.h>
 #include <asm/irq_remapping.h>
+#include <asm/early_ioremap.h>
+
+#define dev_err(msg)  pr_err("pci 0000:%02x:%02x.%d: %s", bus, slot, func, msg)
 
 static void __init fix_hypertransport_config(int num, int slot, int func)
 {
@@ -75,6 +82,13 @@ static void __init nvidia_bugs(int num, int slot, int func)
 {
 #ifdef CONFIG_ACPI
 #ifdef CONFIG_X86_IO_APIC
+       /*
+        * Only applies to Nvidia root ports (bus 0) and not to
+        * Nvidia graphics cards with PCI ports on secondary buses.
+        */
+       if (num)
+               return;
+
        /*
         * All timer overrides on Nvidia are
         * wrong unless HPET is enabled.
@@ -590,6 +604,61 @@ static void __init force_disable_hpet(int num, int slot, int func)
 #endif
 }
 
+#define BCM4331_MMIO_SIZE      16384
+#define BCM4331_PM_CAP         0x40
+#define bcma_aread32(reg)      ioread32(mmio + 1 * BCMA_CORE_SIZE + reg)
+#define bcma_awrite32(reg, val)        iowrite32(val, mmio + 1 * BCMA_CORE_SIZE + reg)
+
+static void __init apple_airport_reset(int bus, int slot, int func)
+{
+       void __iomem *mmio;
+       u16 pmcsr;
+       u64 addr;
+       int i;
+
+       if (!dmi_match(DMI_SYS_VENDOR, "Apple Inc."))
+               return;
+
+       /* Card may have been put into PCI_D3hot by grub quirk */
+       pmcsr = read_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL);
+
+       if ((pmcsr & PCI_PM_CTRL_STATE_MASK) != PCI_D0) {
+               pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+               write_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL, pmcsr);
+               mdelay(10);
+
+               pmcsr = read_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL);
+               if ((pmcsr & PCI_PM_CTRL_STATE_MASK) != PCI_D0) {
+                       dev_err("Cannot power up Apple AirPort card\n");
+                       return;
+               }
+       }
+
+       addr  =      read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
+       addr |= (u64)read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_1) << 32;
+       addr &= PCI_BASE_ADDRESS_MEM_MASK;
+
+       mmio = early_ioremap(addr, BCM4331_MMIO_SIZE);
+       if (!mmio) {
+               dev_err("Cannot iomap Apple AirPort card\n");
+               return;
+       }
+
+       pr_info("Resetting Apple AirPort card (left enabled by EFI)\n");
+
+       for (i = 0; bcma_aread32(BCMA_RESET_ST) && i < 30; i++)
+               udelay(10);
+
+       bcma_awrite32(BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
+       bcma_aread32(BCMA_RESET_CTL);
+       udelay(1);
+
+       bcma_awrite32(BCMA_RESET_CTL, 0);
+       bcma_aread32(BCMA_RESET_CTL);
+       udelay(10);
+
+       early_iounmap(mmio, BCM4331_MMIO_SIZE);
+}
 
 #define QFLAG_APPLY_ONCE       0x1
 #define QFLAG_APPLIED          0x2
@@ -603,12 +672,6 @@ struct chipset {
        void (*f)(int num, int slot, int func);
 };
 
-/*
- * Only works for devices on the root bus. If you add any devices
- * not on bus 0 readd another loop level in early_quirks(). But
- * be careful because at least the Nvidia quirk here relies on
- * only matching on bus 0.
- */
 static struct chipset early_qrk[] __initdata = {
        { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
          PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, nvidia_bugs },
@@ -638,9 +701,13 @@ static struct chipset early_qrk[] __initdata = {
         */
        { PCI_VENDOR_ID_INTEL, 0x0f00,
                PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet},
+       { PCI_VENDOR_ID_BROADCOM, 0x4331,
+         PCI_CLASS_NETWORK_OTHER, PCI_ANY_ID, 0, apple_airport_reset},
        {}
 };
 
+static void __init early_pci_scan_bus(int bus);
+
 /**
  * check_dev_quirk - apply early quirks to a given PCI device
  * @num: bus number
@@ -649,7 +716,7 @@ static struct chipset early_qrk[] __initdata = {
  *
  * Check the vendor & device ID against the early quirks table.
  *
- * If the device is single function, let early_quirks() know so we don't
+ * If the device is single function, let early_pci_scan_bus() know so we don't
  * poke at this device again.
  */
 static int __init check_dev_quirk(int num, int slot, int func)
@@ -658,6 +725,7 @@ static int __init check_dev_quirk(int num, int slot, int func)
        u16 vendor;
        u16 device;
        u8 type;
+       u8 sec;
        int i;
 
        class = read_pci_config_16(num, slot, func, PCI_CLASS_DEVICE);
@@ -685,25 +753,36 @@ static int __init check_dev_quirk(int num, int slot, int func)
 
        type = read_pci_config_byte(num, slot, func,
                                    PCI_HEADER_TYPE);
+
+       if ((type & 0x7f) == PCI_HEADER_TYPE_BRIDGE) {
+               sec = read_pci_config_byte(num, slot, func, PCI_SECONDARY_BUS);
+               if (sec > num)
+                       early_pci_scan_bus(sec);
+       }
+
        if (!(type & 0x80))
                return -1;
 
        return 0;
 }
 
-void __init early_quirks(void)
+static void __init early_pci_scan_bus(int bus)
 {
        int slot, func;
 
-       if (!early_pci_allowed())
-               return;
-
        /* Poor man's PCI discovery */
-       /* Only scan the root bus */
        for (slot = 0; slot < 32; slot++)
                for (func = 0; func < 8; func++) {
                        /* Only probe function 0 on single fn devices */
-                       if (check_dev_quirk(0, slot, func))
+                       if (check_dev_quirk(bus, slot, func))
                                break;
                }
 }
+
+void __init early_quirks(void)
+{
+       if (!early_pci_allowed())
+               return;
+
+       early_pci_scan_bus(0);
+}
index fafe8b9..8264dfa 100644 (file)
@@ -1622,7 +1622,7 @@ static inline void mwait_play_dead(void)
        }
 }
 
-static inline void hlt_play_dead(void)
+void hlt_play_dead(void)
 {
        if (__this_cpu_read(cpu_info.x86) >= 4)
                wbinvd();
index 1b1110f..0493c17 100644 (file)
@@ -54,8 +54,8 @@ static int kasan_die_handler(struct notifier_block *self,
                             void *data)
 {
        if (val == DIE_GPF) {
-               pr_emerg("CONFIG_KASAN_INLINE enabled");
-               pr_emerg("GPF could be caused by NULL-ptr deref or user memory access");
+               pr_emerg("CONFIG_KASAN_INLINE enabled\n");
+               pr_emerg("GPF could be caused by NULL-ptr deref or user memory access\n");
        }
        return NOTIFY_OK;
 }
index d5f6499..b12c26e 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/export.h>
 #include <linux/smp.h>
 #include <linux/perf_event.h>
+#include <linux/tboot.h>
 
 #include <asm/pgtable.h>
 #include <asm/proto.h>
@@ -266,6 +267,35 @@ void notrace restore_processor_state(void)
 EXPORT_SYMBOL(restore_processor_state);
 #endif
 
+#if defined(CONFIG_HIBERNATION) && defined(CONFIG_HOTPLUG_CPU)
+static void resume_play_dead(void)
+{
+       play_dead_common();
+       tboot_shutdown(TB_SHUTDOWN_WFS);
+       hlt_play_dead();
+}
+
+int hibernate_resume_nonboot_cpu_disable(void)
+{
+       void (*play_dead)(void) = smp_ops.play_dead;
+       int ret;
+
+       /*
+        * Ensure that MONITOR/MWAIT will not be used in the "play dead" loop
+        * during hibernate image restoration, because it is likely that the
+        * monitored address will be actually written to at that time and then
+        * the "dead" CPU will attempt to execute instructions again, but the
+        * address in its instruction pointer may not be possible to resolve
+        * any more at that point (the page tables used by it previously may
+        * have been overwritten by hibernate image data).
+        */
+       smp_ops.play_dead = resume_play_dead;
+       ret = disable_nonboot_cpus();
+       smp_ops.play_dead = play_dead;
+       return ret;
+}
+#endif
+
 /*
  * When bsp_check() is called in hibernate and suspend, cpu hotplug
  * is disabled already. So it's unnessary to handle race condition between
index 6a76d5c..9492e1c 100644 (file)
@@ -124,5 +124,10 @@ int mscode_note_digest(void *context, size_t hdrlen,
        struct pefile_context *ctx = context;
 
        ctx->digest = kmemdup(value, vlen, GFP_KERNEL);
-       return ctx->digest ? 0 : -ENOMEM;
+       if (!ctx->digest)
+               return -ENOMEM;
+
+       ctx->digest_len = vlen;
+
+       return 0;
 }
index 44b746e..2ffd697 100644 (file)
@@ -227,7 +227,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                                if (asymmetric_key_id_same(p->id, auth))
                                        goto found_issuer_check_skid;
                        }
-               } else {
+               } else if (sig->auth_ids[1]) {
                        auth = sig->auth_ids[1];
                        pr_debug("- want %*phN\n", auth->len, auth->data);
                        for (p = pkcs7->certs; p; p = p->next) {
index ac4bddf..19d1afb 100644 (file)
@@ -87,7 +87,7 @@ int restrict_link_by_signature(struct key *trust_keyring,
 
        sig = payload->data[asym_auth];
        if (!sig->auth_ids[0] && !sig->auth_ids[1])
-               return 0;
+               return -ENOKEY;
 
        if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid))
                return -EPERM;
index ead8dc0..8ba4266 100644 (file)
@@ -102,10 +102,10 @@ struct pkcs1pad_inst_ctx {
 };
 
 struct pkcs1pad_request {
-       struct akcipher_request child_req;
-
        struct scatterlist in_sg[3], out_sg[2];
        uint8_t *in_buf, *out_buf;
+
+       struct akcipher_request child_req;
 };
 
 static int pkcs1pad_set_pub_key(struct crypto_akcipher *tfm, const void *key,
index 21932d6..a1d177d 100644 (file)
@@ -108,9 +108,7 @@ acpi_ex_add_table(u32 table_index,
 
        /* Add the table to the namespace */
 
-       acpi_ex_exit_interpreter();
        status = acpi_ns_load_table(table_index, parent_node);
-       acpi_ex_enter_interpreter();
        if (ACPI_FAILURE(status)) {
                acpi_ut_remove_reference(obj_desc);
                *ddb_handle = NULL;
index 297f6aa..b5e2b0a 100644 (file)
@@ -46,7 +46,6 @@
 #include "acnamesp.h"
 #include "acdispat.h"
 #include "actables.h"
-#include "acinterp.h"
 
 #define _COMPONENT          ACPI_NAMESPACE
 ACPI_MODULE_NAME("nsload")
@@ -79,8 +78,6 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
 
        ACPI_FUNCTION_TRACE(ns_load_table);
 
-       acpi_ex_enter_interpreter();
-
        /*
         * Parse the table and load the namespace with all named
         * objects found within. Control methods are NOT parsed
@@ -92,7 +89,7 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
         */
        status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
        if (ACPI_FAILURE(status)) {
-               goto unlock_interp;
+               return_ACPI_STATUS(status);
        }
 
        /* If table already loaded into namespace, just return */
@@ -133,8 +130,6 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
 
 unlock:
        (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
-unlock_interp:
-       (void)acpi_ex_exit_interpreter();
 
        if (ACPI_FAILURE(status)) {
                return_ACPI_STATUS(status);
index 73c76d6..290d6f5 100644 (file)
@@ -1331,8 +1331,6 @@ static int ec_install_handlers(struct acpi_ec *ec)
 
 static void ec_remove_handlers(struct acpi_ec *ec)
 {
-       acpi_ec_stop(ec, false);
-
        if (test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) {
                if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
                                        ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
@@ -1340,6 +1338,19 @@ static void ec_remove_handlers(struct acpi_ec *ec)
                clear_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
        }
 
+       /*
+        * Stops handling the EC transactions after removing the operation
+        * region handler. This is required because _REG(DISCONNECT)
+        * invoked during the removal can result in new EC transactions.
+        *
+        * Flushes the EC requests and thus disables the GPE before
+        * removing the GPE handler. This is required by the current ACPICA
+        * GPE core. ACPICA GPE core will automatically disable a GPE when
+        * it is indicated but there is no way to handle it. So the drivers
+        * must disable the GPEs prior to removing the GPE handlers.
+        */
+       acpi_ec_stop(ec, false);
+
        if (test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) {
                if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe,
                                        &acpi_ec_gpe_handler)))
index ac6ddcc..1f0e060 100644 (file)
@@ -1131,11 +1131,11 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
 
        /*
         * Until standardization materializes we need to consider up to 3
-        * different command sets.  Note, that checking for zero functions
-        * tells us if any commands might be reachable through this uuid.
+        * different command sets.  Note, that checking for function0 (bit0)
+        * tells us if any commands are reachable through this uuid.
         */
        for (i = NVDIMM_FAMILY_INTEL; i <= NVDIMM_FAMILY_HPE2; i++)
-               if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 0))
+               if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1))
                        break;
 
        /* limit the supported commands to those that are publicly documented */
@@ -1151,9 +1151,10 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
                if (disable_vendor_specific)
                        dsm_mask &= ~(1 << 8);
        } else {
-               dev_err(dev, "unknown dimm command family\n");
+               dev_dbg(dev, "unknown dimm command family\n");
                nfit_mem->family = -1;
-               return force_enable_dimms ? 0 : -ENODEV;
+               /* DSMs are optional, continue loading the driver... */
+               return 0;
        }
 
        uuid = to_nfit_uuid(nfit_mem->family);
index b4de130..22c0995 100644 (file)
@@ -680,6 +680,9 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 funcs)
        u64 mask = 0;
        union acpi_object *obj;
 
+       if (funcs == 0)
+               return false;
+
        obj = acpi_evaluate_dsm(handle, uuid, rev, 0, NULL);
        if (!obj)
                return false;
@@ -692,9 +695,6 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 funcs)
                        mask |= (((u64)obj->buffer.pointer[i]) << (i * 8));
        ACPI_FREE(obj);
 
-       if (funcs == 0)
-               return true;
-
        /*
         * Bit 0 indicates whether there's support for any functions other than
         * function 0 for the specified UUID and revision.
index 6be7770..31c183a 100644 (file)
@@ -4314,6 +4314,12 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
         */
        { "ST380013AS",         "3.20",         ATA_HORKAGE_MAX_SEC_1024 },
 
+       /*
+        * Device times out with higher max sects.
+        * https://bugzilla.kernel.org/show_bug.cgi?id=121671
+        */
+       { "LITEON CX1-JB256-HP", NULL,          ATA_HORKAGE_MAX_SEC_1024 },
+
        /* Devices we expect to fail diagnostics */
 
        /* Devices where NCQ should be avoided */
index 3657ac1..8e2e475 100644 (file)
@@ -121,6 +121,7 @@ int pm_clk_add(struct device *dev, const char *con_id)
 {
        return __pm_clk_add(dev, con_id, NULL);
 }
+EXPORT_SYMBOL_GPL(pm_clk_add);
 
 /**
  * pm_clk_add_clk - Start using a device clock for power management.
@@ -136,8 +137,41 @@ int pm_clk_add_clk(struct device *dev, struct clk *clk)
 {
        return __pm_clk_add(dev, NULL, clk);
 }
+EXPORT_SYMBOL_GPL(pm_clk_add_clk);
 
 
+/**
+ * of_pm_clk_add_clk - Start using a device clock for power management.
+ * @dev: Device whose clock is going to be used for power management.
+ * @name: Name of clock that is going to be used for power management.
+ *
+ * Add the clock described in the 'clocks' device-tree node that matches
+ * with the 'name' provided, to the list of clocks used for the power
+ * management of @dev. On success, returns 0. Returns a negative error
+ * code if the clock is not found or cannot be added.
+ */
+int of_pm_clk_add_clk(struct device *dev, const char *name)
+{
+       struct clk *clk;
+       int ret;
+
+       if (!dev || !dev->of_node || !name)
+               return -EINVAL;
+
+       clk = of_clk_get_by_name(dev->of_node, name);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       ret = pm_clk_add_clk(dev, clk);
+       if (ret) {
+               clk_put(clk);
+               return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(of_pm_clk_add_clk);
+
 /**
  * of_pm_clk_add_clks - Start using device clock(s) for power management.
  * @dev: Device whose clock(s) is going to be used for power management.
@@ -192,6 +226,7 @@ error:
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(of_pm_clk_add_clks);
 
 /**
  * __pm_clk_remove - Destroy PM clock entry.
@@ -252,6 +287,7 @@ void pm_clk_remove(struct device *dev, const char *con_id)
 
        __pm_clk_remove(ce);
 }
+EXPORT_SYMBOL_GPL(pm_clk_remove);
 
 /**
  * pm_clk_remove_clk - Stop using a device clock for power management.
@@ -285,6 +321,7 @@ void pm_clk_remove_clk(struct device *dev, struct clk *clk)
 
        __pm_clk_remove(ce);
 }
+EXPORT_SYMBOL_GPL(pm_clk_remove_clk);
 
 /**
  * pm_clk_init - Initialize a device's list of power management clocks.
@@ -299,6 +336,7 @@ void pm_clk_init(struct device *dev)
        if (psd)
                INIT_LIST_HEAD(&psd->clock_list);
 }
+EXPORT_SYMBOL_GPL(pm_clk_init);
 
 /**
  * pm_clk_create - Create and initialize a device's list of PM clocks.
@@ -311,6 +349,7 @@ int pm_clk_create(struct device *dev)
 {
        return dev_pm_get_subsys_data(dev);
 }
+EXPORT_SYMBOL_GPL(pm_clk_create);
 
 /**
  * pm_clk_destroy - Destroy a device's list of power management clocks.
@@ -345,6 +384,7 @@ void pm_clk_destroy(struct device *dev)
                __pm_clk_remove(ce);
        }
 }
+EXPORT_SYMBOL_GPL(pm_clk_destroy);
 
 /**
  * pm_clk_suspend - Disable clocks in a device's PM clock list.
@@ -375,6 +415,7 @@ int pm_clk_suspend(struct device *dev)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(pm_clk_suspend);
 
 /**
  * pm_clk_resume - Enable clocks in a device's PM clock list.
@@ -400,6 +441,7 @@ int pm_clk_resume(struct device *dev)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(pm_clk_resume);
 
 /**
  * pm_clk_notify - Notify routine for device addition and removal.
@@ -480,6 +522,7 @@ int pm_clk_runtime_suspend(struct device *dev)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(pm_clk_runtime_suspend);
 
 int pm_clk_runtime_resume(struct device *dev)
 {
@@ -495,6 +538,7 @@ int pm_clk_runtime_resume(struct device *dev)
 
        return pm_generic_runtime_resume(dev);
 }
+EXPORT_SYMBOL_GPL(pm_clk_runtime_resume);
 
 #else /* !CONFIG_PM_CLK */
 
@@ -598,3 +642,4 @@ void pm_clk_add_notifier(struct bus_type *bus,
        clknb->nb.notifier_call = pm_clk_notify;
        bus_register_notifier(bus, &clknb->nb);
 }
+EXPORT_SYMBOL_GPL(pm_clk_add_notifier);
index de23b64..a1f2aff 100644 (file)
@@ -187,8 +187,7 @@ static int genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth)
        struct gpd_link *link;
        int ret = 0;
 
-       if (genpd->status == GPD_STATE_ACTIVE
-           || (genpd->prepared_count > 0 && genpd->suspend_power_off))
+       if (genpd->status == GPD_STATE_ACTIVE)
                return 0;
 
        /*
@@ -735,81 +734,23 @@ static int pm_genpd_prepare(struct device *dev)
 
        mutex_lock(&genpd->lock);
 
-       if (genpd->prepared_count++ == 0) {
+       if (genpd->prepared_count++ == 0)
                genpd->suspended_count = 0;
-               genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
-       }
 
        mutex_unlock(&genpd->lock);
 
-       if (genpd->suspend_power_off)
-               return 0;
-
-       /*
-        * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
-        * so genpd_poweron() will return immediately, but if the device
-        * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need
-        * to make it operational.
-        */
-       pm_runtime_resume(dev);
-       __pm_runtime_disable(dev, false);
-
        ret = pm_generic_prepare(dev);
        if (ret) {
                mutex_lock(&genpd->lock);
 
-               if (--genpd->prepared_count == 0)
-                       genpd->suspend_power_off = false;
+               genpd->prepared_count--;
 
                mutex_unlock(&genpd->lock);
-               pm_runtime_enable(dev);
        }
 
        return ret;
 }
 
-/**
- * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
- * @dev: Device to suspend.
- *
- * Suspend a device under the assumption that its pm_domain field points to the
- * domain member of an object of type struct generic_pm_domain representing
- * a PM domain consisting of I/O devices.
- */
-static int pm_genpd_suspend(struct device *dev)
-{
-       struct generic_pm_domain *genpd;
-
-       dev_dbg(dev, "%s()\n", __func__);
-
-       genpd = dev_to_genpd(dev);
-       if (IS_ERR(genpd))
-               return -EINVAL;
-
-       return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
-}
-
-/**
- * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain.
- * @dev: Device to suspend.
- *
- * Carry out a late suspend of a device under the assumption that its
- * pm_domain field points to the domain member of an object of type
- * struct generic_pm_domain representing a PM domain consisting of I/O devices.
- */
-static int pm_genpd_suspend_late(struct device *dev)
-{
-       struct generic_pm_domain *genpd;
-
-       dev_dbg(dev, "%s()\n", __func__);
-
-       genpd = dev_to_genpd(dev);
-       if (IS_ERR(genpd))
-               return -EINVAL;
-
-       return genpd->suspend_power_off ? 0 : pm_generic_suspend_late(dev);
-}
-
 /**
  * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
  * @dev: Device to suspend.
@@ -820,6 +761,7 @@ static int pm_genpd_suspend_late(struct device *dev)
 static int pm_genpd_suspend_noirq(struct device *dev)
 {
        struct generic_pm_domain *genpd;
+       int ret;
 
        dev_dbg(dev, "%s()\n", __func__);
 
@@ -827,11 +769,14 @@ static int pm_genpd_suspend_noirq(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       if (genpd->suspend_power_off
-           || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
+       if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))
                return 0;
 
-       genpd_stop_dev(genpd, dev);
+       if (genpd->dev_ops.stop && genpd->dev_ops.start) {
+               ret = pm_runtime_force_suspend(dev);
+               if (ret)
+                       return ret;
+       }
 
        /*
         * Since all of the "noirq" callbacks are executed sequentially, it is
@@ -853,6 +798,7 @@ static int pm_genpd_suspend_noirq(struct device *dev)
 static int pm_genpd_resume_noirq(struct device *dev)
 {
        struct generic_pm_domain *genpd;
+       int ret = 0;
 
        dev_dbg(dev, "%s()\n", __func__);
 
@@ -860,8 +806,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       if (genpd->suspend_power_off
-           || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
+       if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))
                return 0;
 
        /*
@@ -872,93 +817,10 @@ static int pm_genpd_resume_noirq(struct device *dev)
        pm_genpd_sync_poweron(genpd, true);
        genpd->suspended_count--;
 
-       return genpd_start_dev(genpd, dev);
-}
-
-/**
- * pm_genpd_resume_early - Early resume of a device in an I/O PM domain.
- * @dev: Device to resume.
- *
- * Carry out an early resume of a device under the assumption that its
- * pm_domain field points to the domain member of an object of type
- * struct generic_pm_domain representing a power domain consisting of I/O
- * devices.
- */
-static int pm_genpd_resume_early(struct device *dev)
-{
-       struct generic_pm_domain *genpd;
-
-       dev_dbg(dev, "%s()\n", __func__);
-
-       genpd = dev_to_genpd(dev);
-       if (IS_ERR(genpd))
-               return -EINVAL;
-
-       return genpd->suspend_power_off ? 0 : pm_generic_resume_early(dev);
-}
-
-/**
- * pm_genpd_resume - Resume of device in an I/O PM domain.
- * @dev: Device to resume.
- *
- * Resume a device under the assumption that its pm_domain field points to the
- * domain member of an object of type struct generic_pm_domain representing
- * a power domain consisting of I/O devices.
- */
-static int pm_genpd_resume(struct device *dev)
-{
-       struct generic_pm_domain *genpd;
-
-       dev_dbg(dev, "%s()\n", __func__);
-
-       genpd = dev_to_genpd(dev);
-       if (IS_ERR(genpd))
-               return -EINVAL;
-
-       return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
-}
-
-/**
- * pm_genpd_freeze - Freezing a device in an I/O PM domain.
- * @dev: Device to freeze.
- *
- * Freeze a device under the assumption that its pm_domain field points to the
- * domain member of an object of type struct generic_pm_domain representing
- * a power domain consisting of I/O devices.
- */
-static int pm_genpd_freeze(struct device *dev)
-{
-       struct generic_pm_domain *genpd;
-
-       dev_dbg(dev, "%s()\n", __func__);
-
-       genpd = dev_to_genpd(dev);
-       if (IS_ERR(genpd))
-               return -EINVAL;
-
-       return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
-}
+       if (genpd->dev_ops.stop && genpd->dev_ops.start)
+               ret = pm_runtime_force_resume(dev);
 
-/**
- * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain.
- * @dev: Device to freeze.
- *
- * Carry out a late freeze of a device under the assumption that its
- * pm_domain field points to the domain member of an object of type
- * struct generic_pm_domain representing a power domain consisting of I/O
- * devices.
- */
-static int pm_genpd_freeze_late(struct device *dev)
-{
-       struct generic_pm_domain *genpd;
-
-       dev_dbg(dev, "%s()\n", __func__);
-
-       genpd = dev_to_genpd(dev);
-       if (IS_ERR(genpd))
-               return -EINVAL;
-
-       return genpd->suspend_power_off ? 0 : pm_generic_freeze_late(dev);
+       return ret;
 }
 
 /**
@@ -973,6 +835,7 @@ static int pm_genpd_freeze_late(struct device *dev)
 static int pm_genpd_freeze_noirq(struct device *dev)
 {
        struct generic_pm_domain *genpd;
+       int ret = 0;
 
        dev_dbg(dev, "%s()\n", __func__);
 
@@ -980,7 +843,10 @@ static int pm_genpd_freeze_noirq(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev);
+       if (genpd->dev_ops.stop && genpd->dev_ops.start)
+               ret = pm_runtime_force_suspend(dev);
+
+       return ret;
 }
 
 /**
@@ -993,6 +859,7 @@ static int pm_genpd_freeze_noirq(struct device *dev)
 static int pm_genpd_thaw_noirq(struct device *dev)
 {
        struct generic_pm_domain *genpd;
+       int ret = 0;
 
        dev_dbg(dev, "%s()\n", __func__);
 
@@ -1000,51 +867,10 @@ static int pm_genpd_thaw_noirq(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ?
-               0 : genpd_start_dev(genpd, dev);
-}
-
-/**
- * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain.
- * @dev: Device to thaw.
- *
- * Carry out an early thaw of a device under the assumption that its
- * pm_domain field points to the domain member of an object of type
- * struct generic_pm_domain representing a power domain consisting of I/O
- * devices.
- */
-static int pm_genpd_thaw_early(struct device *dev)
-{
-       struct generic_pm_domain *genpd;
-
-       dev_dbg(dev, "%s()\n", __func__);
-
-       genpd = dev_to_genpd(dev);
-       if (IS_ERR(genpd))
-               return -EINVAL;
-
-       return genpd->suspend_power_off ? 0 : pm_generic_thaw_early(dev);
-}
-
-/**
- * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
- * @dev: Device to thaw.
- *
- * Thaw a device under the assumption that its pm_domain field points to the
- * domain member of an object of type struct generic_pm_domain representing
- * a power domain consisting of I/O devices.
- */
-static int pm_genpd_thaw(struct device *dev)
-{
-       struct generic_pm_domain *genpd;
-
-       dev_dbg(dev, "%s()\n", __func__);
+       if (genpd->dev_ops.stop && genpd->dev_ops.start)
+               ret = pm_runtime_force_resume(dev);
 
-       genpd = dev_to_genpd(dev);
-       if (IS_ERR(genpd))
-               return -EINVAL;
-
-       return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+       return ret;
 }
 
 /**
@@ -1057,6 +883,7 @@ static int pm_genpd_thaw(struct device *dev)
 static int pm_genpd_restore_noirq(struct device *dev)
 {
        struct generic_pm_domain *genpd;
+       int ret = 0;
 
        dev_dbg(dev, "%s()\n", __func__);
 
@@ -1072,30 +899,20 @@ static int pm_genpd_restore_noirq(struct device *dev)
         * At this point suspended_count == 0 means we are being run for the
         * first time for the given domain in the present cycle.
         */
-       if (genpd->suspended_count++ == 0) {
+       if (genpd->suspended_count++ == 0)
                /*
                 * The boot kernel might put the domain into arbitrary state,
                 * so make it appear as powered off to pm_genpd_sync_poweron(),
                 * so that it tries to power it on in case it was really off.
                 */
                genpd->status = GPD_STATE_POWER_OFF;
-               if (genpd->suspend_power_off) {
-                       /*
-                        * If the domain was off before the hibernation, make
-                        * sure it will be off going forward.
-                        */
-                       genpd_power_off(genpd, true);
-
-                       return 0;
-               }
-       }
-
-       if (genpd->suspend_power_off)
-               return 0;
 
        pm_genpd_sync_poweron(genpd, true);
 
-       return genpd_start_dev(genpd, dev);
+       if (genpd->dev_ops.stop && genpd->dev_ops.start)
+               ret = pm_runtime_force_resume(dev);
+
+       return ret;
 }
 
 /**
@@ -1110,7 +927,6 @@ static int pm_genpd_restore_noirq(struct device *dev)
 static void pm_genpd_complete(struct device *dev)
 {
        struct generic_pm_domain *genpd;
-       bool run_complete;
 
        dev_dbg(dev, "%s()\n", __func__);
 
@@ -1118,20 +934,15 @@ static void pm_genpd_complete(struct device *dev)
        if (IS_ERR(genpd))
                return;
 
+       pm_generic_complete(dev);
+
        mutex_lock(&genpd->lock);
 
-       run_complete = !genpd->suspend_power_off;
-       if (--genpd->prepared_count == 0)
-               genpd->suspend_power_off = false;
+       genpd->prepared_count--;
+       if (!genpd->prepared_count)
+               genpd_queue_power_off_work(genpd);
 
        mutex_unlock(&genpd->lock);
-
-       if (run_complete) {
-               pm_generic_complete(dev);
-               pm_runtime_set_active(dev);
-               pm_runtime_enable(dev);
-               pm_request_idle(dev);
-       }
 }
 
 /**
@@ -1173,18 +984,10 @@ EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron);
 #else /* !CONFIG_PM_SLEEP */
 
 #define pm_genpd_prepare               NULL
-#define pm_genpd_suspend               NULL
-#define pm_genpd_suspend_late          NULL
 #define pm_genpd_suspend_noirq         NULL
-#define pm_genpd_resume_early          NULL
 #define pm_genpd_resume_noirq          NULL
-#define pm_genpd_resume                        NULL
-#define pm_genpd_freeze                        NULL
-#define pm_genpd_freeze_late           NULL
 #define pm_genpd_freeze_noirq          NULL
-#define pm_genpd_thaw_early            NULL
 #define pm_genpd_thaw_noirq            NULL
-#define pm_genpd_thaw                  NULL
 #define pm_genpd_restore_noirq         NULL
 #define pm_genpd_complete              NULL
 
@@ -1455,12 +1258,14 @@ EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain);
  * @genpd: PM domain object to initialize.
  * @gov: PM domain governor to associate with the domain (may be NULL).
  * @is_off: Initial value of the domain's power_is_off field.
+ *
+ * Returns 0 on successful initialization, else a negative error code.
  */
-void pm_genpd_init(struct generic_pm_domain *genpd,
-                  struct dev_power_governor *gov, bool is_off)
+int pm_genpd_init(struct generic_pm_domain *genpd,
+                 struct dev_power_governor *gov, bool is_off)
 {
        if (IS_ERR_OR_NULL(genpd))
-               return;
+               return -EINVAL;
 
        INIT_LIST_HEAD(&genpd->master_links);
        INIT_LIST_HEAD(&genpd->slave_links);
@@ -1476,24 +1281,24 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
        genpd->domain.ops.runtime_suspend = genpd_runtime_suspend;
        genpd->domain.ops.runtime_resume = genpd_runtime_resume;
        genpd->domain.ops.prepare = pm_genpd_prepare;
-       genpd->domain.ops.suspend = pm_genpd_suspend;
-       genpd->domain.ops.suspend_late = pm_genpd_suspend_late;
+       genpd->domain.ops.suspend = pm_generic_suspend;
+       genpd->domain.ops.suspend_late = pm_generic_suspend_late;
        genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
        genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
-       genpd->domain.ops.resume_early = pm_genpd_resume_early;
-       genpd->domain.ops.resume = pm_genpd_resume;
-       genpd->domain.ops.freeze = pm_genpd_freeze;
-       genpd->domain.ops.freeze_late = pm_genpd_freeze_late;
+       genpd->domain.ops.resume_early = pm_generic_resume_early;
+       genpd->domain.ops.resume = pm_generic_resume;
+       genpd->domain.ops.freeze = pm_generic_freeze;
+       genpd->domain.ops.freeze_late = pm_generic_freeze_late;
        genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
        genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
-       genpd->domain.ops.thaw_early = pm_genpd_thaw_early;
-       genpd->domain.ops.thaw = pm_genpd_thaw;
-       genpd->domain.ops.poweroff = pm_genpd_suspend;
-       genpd->domain.ops.poweroff_late = pm_genpd_suspend_late;
+       genpd->domain.ops.thaw_early = pm_generic_thaw_early;
+       genpd->domain.ops.thaw = pm_generic_thaw;
+       genpd->domain.ops.poweroff = pm_generic_poweroff;
+       genpd->domain.ops.poweroff_late = pm_generic_poweroff_late;
        genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
        genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
-       genpd->domain.ops.restore_early = pm_genpd_resume_early;
-       genpd->domain.ops.restore = pm_genpd_resume;
+       genpd->domain.ops.restore_early = pm_generic_restore_early;
+       genpd->domain.ops.restore = pm_generic_restore;
        genpd->domain.ops.complete = pm_genpd_complete;
 
        if (genpd->flags & GENPD_FLAG_PM_CLK) {
@@ -1518,6 +1323,8 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
        mutex_lock(&gpd_list_lock);
        list_add(&genpd->gpd_list_node, &gpd_list);
        mutex_unlock(&gpd_list_lock);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(pm_genpd_init);
 
index b746904..e097d35 100644 (file)
@@ -1045,10 +1045,14 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
                 */
                if (!parent->power.disable_depth
                    && !parent->power.ignore_children
-                   && parent->power.runtime_status != RPM_ACTIVE)
+                   && parent->power.runtime_status != RPM_ACTIVE) {
+                       dev_err(dev, "runtime PM trying to activate child device %s but parent (%s) is not active\n",
+                               dev_name(dev),
+                               dev_name(parent));
                        error = -EBUSY;
-               else if (dev->power.runtime_status == RPM_SUSPENDED)
+               } else if (dev->power.runtime_status == RPM_SUSPENDED) {
                        atomic_inc(&parent->power.child_count);
+               }
 
                spin_unlock(&parent->power.lock);
 
@@ -1256,7 +1260,7 @@ void pm_runtime_allow(struct device *dev)
 
        dev->power.runtime_auto = true;
        if (atomic_dec_and_test(&dev->power.usage_count))
-               rpm_idle(dev, RPM_AUTO);
+               rpm_idle(dev, RPM_AUTO | RPM_ASYNC);
 
  out:
        spin_unlock_irq(&dev->power.lock);
@@ -1506,6 +1510,9 @@ int pm_runtime_force_resume(struct device *dev)
                goto out;
        }
 
+       if (!pm_runtime_status_suspended(dev))
+               goto out;
+
        ret = pm_runtime_set_active(dev);
        if (ret)
                goto out;
index eda0909..f642c42 100644 (file)
@@ -8,8 +8,6 @@
 #include <linux/bcma/bcma.h>
 #include <linux/delay.h>
 
-#define BCMA_CORE_SIZE         0x1000
-
 #define bcma_err(bus, fmt, ...) \
        pr_err("bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
 #define bcma_warn(bus, fmt, ...) \
index 10f846c..25d5906 100644 (file)
@@ -99,7 +99,7 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
        struct clk_programmable *prog = to_clk_programmable(hw);
        const struct clk_programmable_layout *layout = prog->layout;
        unsigned int mask = layout->css_mask;
-       unsigned int pckr = 0;
+       unsigned int pckr = index;
 
        if (layout->have_slck_mck)
                mask |= AT91_PMC_CSSMCK_MCK;
index 445a749..9780fac 100644 (file)
@@ -33,6 +33,8 @@ struct sun4i_a10_display_clk_data {
 
        u8      width_div;
        u8      width_mux;
+
+       u32     flags;
 };
 
 struct reset_data {
@@ -166,7 +168,7 @@ static void __init sun4i_a10_display_init(struct device_node *node,
                                     data->has_div ? &div->hw : NULL,
                                     data->has_div ? &clk_divider_ops : NULL,
                                     &gate->hw, &clk_gate_ops,
-                                    0);
+                                    data->flags);
        if (IS_ERR(clk)) {
                pr_err("%s: Couldn't register the clock\n", clk_name);
                goto free_div;
@@ -232,6 +234,7 @@ static const struct sun4i_a10_display_clk_data sun4i_a10_tcon_ch0_data __initcon
        .offset_rst     = 29,
        .offset_mux     = 24,
        .width_mux      = 2,
+       .flags          = CLK_SET_RATE_PARENT,
 };
 
 static void __init sun4i_a10_tcon_ch0_setup(struct device_node *node)
index 98a4582..b6d29d1 100644 (file)
@@ -79,15 +79,11 @@ static int tcon_ch1_is_enabled(struct clk_hw *hw)
 static u8 tcon_ch1_get_parent(struct clk_hw *hw)
 {
        struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
-       int num_parents = clk_hw_get_num_parents(hw);
        u32 reg;
 
        reg = readl(tclk->reg) >> TCON_CH1_SCLK2_MUX_SHIFT;
        reg &= reg >> TCON_CH1_SCLK2_MUX_MASK;
 
-       if (reg >= num_parents)
-               return -EINVAL;
-
        return reg;
 }
 
index 6d74b91..5fc3dbb 100644 (file)
@@ -2,6 +2,7 @@ $(obj)/qat_rsapubkey-asn1.o: $(obj)/qat_rsapubkey-asn1.c \
                             $(obj)/qat_rsapubkey-asn1.h
 $(obj)/qat_rsaprivkey-asn1.o: $(obj)/qat_rsaprivkey-asn1.c \
                              $(obj)/qat_rsaprivkey-asn1.h
+$(obj)/qat_asym_algs.o: $(obj)/qat_rsapubkey-asn1.h $(obj)/qat_rsaprivkey-asn1.h
 
 clean-files += qat_rsapubkey-asn1.c qat_rsapubkey-asn1.h
 clean-files += qat_rsaprivkey-asn1.c qat_rsaprivkey-asn1.h
index 78dac0e..a5be56e 100644 (file)
@@ -75,7 +75,7 @@ config DEVFREQ_GOV_PASSIVE
 comment "DEVFREQ Drivers"
 
 config ARM_EXYNOS_BUS_DEVFREQ
-       bool "ARM EXYNOS Generic Memory Bus DEVFREQ Driver"
+       tristate "ARM EXYNOS Generic Memory Bus DEVFREQ Driver"
        depends on ARCH_EXYNOS
        select DEVFREQ_GOV_SIMPLE_ONDEMAND
        select DEVFREQ_GOV_PASSIVE
index 39b048e..9aea2c7 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/init.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/list.h>
 #include <linux/of.h>
@@ -481,13 +481,3 @@ static int __init devfreq_event_init(void)
        return 0;
 }
 subsys_initcall(devfreq_event_init);
-
-static void __exit devfreq_event_exit(void)
-{
-       class_destroy(devfreq_event_class);
-}
-module_exit(devfreq_event_exit);
-
-MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
-MODULE_DESCRIPTION("DEVFREQ-Event class support");
-MODULE_LICENSE("GPL");
index e92418f..478006b 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/init.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/stat.h>
 #include <linux/pm_opp.h>
@@ -707,10 +707,12 @@ struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index)
                if (devfreq->dev.parent
                        && devfreq->dev.parent->of_node == node) {
                        mutex_unlock(&devfreq_list_lock);
+                       of_node_put(node);
                        return devfreq;
                }
        }
        mutex_unlock(&devfreq_list_lock);
+       of_node_put(node);
 
        return ERR_PTR(-EPROBE_DEFER);
 }
@@ -1199,13 +1201,6 @@ static int __init devfreq_init(void)
 }
 subsys_initcall(devfreq_init);
 
-static void __exit devfreq_exit(void)
-{
-       class_destroy(devfreq_class);
-       destroy_workqueue(devfreq_wq);
-}
-module_exit(devfreq_exit);
-
 /*
  * The followings are helper functions for devfreq user device drivers with
  * OPP framework.
@@ -1471,7 +1466,3 @@ void devm_devfreq_unregister_notifier(struct device *dev,
                               devm_devfreq_dev_match, devfreq));
 }
 EXPORT_SYMBOL(devm_devfreq_unregister_notifier);
-
-MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
-MODULE_DESCRIPTION("devfreq class support");
-MODULE_LICENSE("GPL");
index 1e8b4f4..eb6f74a 100644 (file)
@@ -14,7 +14,7 @@ menuconfig PM_DEVFREQ_EVENT
 if PM_DEVFREQ_EVENT
 
 config DEVFREQ_EVENT_EXYNOS_NOCP
-       bool "EXYNOS NoC (Network On Chip) Probe DEVFREQ event Driver"
+       tristate "EXYNOS NoC (Network On Chip) Probe DEVFREQ event Driver"
        depends on ARCH_EXYNOS
        select PM_OPP
        help
@@ -22,7 +22,7 @@ config DEVFREQ_EVENT_EXYNOS_NOCP
          (Network on Chip) Probe counters to measure the bandwidth of AXI bus.
 
 config DEVFREQ_EVENT_EXYNOS_PPMU
-       bool "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver"
+       tristate "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver"
        depends on ARCH_EXYNOS
        select PM_OPP
        help
index f312485..845bf25 100644 (file)
@@ -482,7 +482,8 @@ static int exynos_ppmu_probe(struct platform_device *pdev)
        if (!info->edev) {
                dev_err(&pdev->dev,
                        "failed to allocate memory devfreq-event devices\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err;
        }
        edev = info->edev;
        platform_set_drvdata(pdev, info);
index 2363d0a..29866f7 100644 (file)
@@ -383,7 +383,7 @@ err_clk:
 static int exynos_bus_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
-       struct device_node *np = dev->of_node;
+       struct device_node *np = dev->of_node, *node;
        struct devfreq_dev_profile *profile;
        struct devfreq_simple_ondemand_data *ondemand_data;
        struct devfreq_passive_data *passive_data;
@@ -407,7 +407,7 @@ static int exynos_bus_probe(struct platform_device *pdev)
        /* Parse the device-tree to get the resource information */
        ret = exynos_bus_parse_of(np, bus);
        if (ret < 0)
-               goto err;
+               return ret;
 
        profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL);
        if (!profile) {
@@ -415,10 +415,13 @@ static int exynos_bus_probe(struct platform_device *pdev)
                goto err;
        }
 
-       if (of_parse_phandle(dev->of_node, "devfreq", 0))
+       node = of_parse_phandle(dev->of_node, "devfreq", 0);
+       if (node) {
+               of_node_put(node);
                goto passive;
-       else
+       } else {
                ret = exynos_bus_parent_parse_of(np, bus);
+       }
 
        if (ret < 0)
                goto err;
index 6744d88..4fb2eb7 100644 (file)
@@ -2378,22 +2378,19 @@ static int sbridge_get_onedevice(struct pci_dev **prev,
  * @num_mc: pointer to the memory controllers count, to be incremented in case
  *         of success.
  * @table: model specific table
- * @allow_dups: allow for multiple devices to exist with the same device id
- *              (as implemented, this isn't expected to work correctly in the
- *              multi-socket case).
- * @multi_bus: don't assume devices on different buses belong to different
- *             memory controllers.
  *
  * returns 0 in case of success or error code
  */
-static int sbridge_get_all_devices_full(u8 *num_mc,
-                                       const struct pci_id_table *table,
-                                       int allow_dups,
-                                       int multi_bus)
+static int sbridge_get_all_devices(u8 *num_mc,
+                                       const struct pci_id_table *table)
 {
        int i, rc;
        struct pci_dev *pdev = NULL;
+       int allow_dups = 0;
+       int multi_bus = 0;
 
+       if (table->type == KNIGHTS_LANDING)
+               allow_dups = multi_bus = 1;
        while (table && table->descr) {
                for (i = 0; i < table->n_devs; i++) {
                        if (!allow_dups || i == 0 ||
@@ -2420,11 +2417,6 @@ static int sbridge_get_all_devices_full(u8 *num_mc,
        return 0;
 }
 
-#define sbridge_get_all_devices(num_mc, table) \
-               sbridge_get_all_devices_full(num_mc, table, 0, 0)
-#define sbridge_get_all_devices_knl(num_mc, table) \
-               sbridge_get_all_devices_full(num_mc, table, 1, 1)
-
 static int sbridge_mci_bind_devs(struct mem_ctl_info *mci,
                                 struct sbridge_dev *sbridge_dev)
 {
index 536112f..d786061 100644 (file)
@@ -402,9 +402,12 @@ config GPIO_TB10X
        select OF_GPIO
 
 config GPIO_TEGRA
-       bool
-       default y
+       bool "NVIDIA Tegra GPIO support"
+       default ARCH_TEGRA
        depends on ARCH_TEGRA || COMPILE_TEST
+       depends on OF
+       help
+         Say yes here to support GPIO pins on NVIDIA Tegra SoCs.
 
 config GPIO_TS4800
        tristate "TS-4800 DIO blocks and compatibles"
index 252edba..892d60f 100644 (file)
@@ -421,29 +421,6 @@ static int acp_suspend(void *handle)
 
 static int acp_resume(void *handle)
 {
-       int i, ret;
-       struct acp_pm_domain *apd;
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       /* return early if no ACP */
-       if (!adev->acp.acp_genpd)
-               return 0;
-
-       /* SMU block will power on ACP irrespective of ACP runtime status.
-        * Power off explicitly based on genpd ACP runtime status so that ACP
-        * hw and ACP-genpd status are in sync.
-        * 'suspend_power_off' represents "Power status before system suspend"
-       */
-       if (adev->acp.acp_genpd->gpd.suspend_power_off == true) {
-               apd = container_of(&adev->acp.acp_genpd->gpd,
-                                       struct acp_pm_domain, gpd);
-
-               for (i = 4; i >= 0 ; i--) {
-                       ret = acp_suspend_tile(apd->cgs_dev, ACP_TILE_P1 + i);
-                       if (ret)
-                               pr_err("ACP tile %d tile suspend failed\n", i);
-               }
-       }
        return 0;
 }
 
index 13cdb01..bc56c8a 100644 (file)
@@ -156,3 +156,18 @@ u32 amdgpu_atombios_i2c_func(struct i2c_adapter *adap)
        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
 }
 
+void amdgpu_atombios_i2c_channel_trans(struct amdgpu_device* adev, u8 slave_addr, u8 line_number, u8 offset, u8 data)
+{
+       PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args;
+       int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction);
+
+       args.ucRegIndex = offset;
+       args.lpI2CDataOut = data;
+       args.ucFlag = 1;
+       args.ucI2CSpeed = TARGET_HW_I2C_CLOCK;
+       args.ucTransBytes = 1;
+       args.ucSlaveAddr = slave_addr;
+       args.ucLineNumber = line_number;
+
+       amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
+}
index d6128d9..251aaf4 100644 (file)
@@ -27,5 +27,7 @@
 int amdgpu_atombios_i2c_xfer(struct i2c_adapter *i2c_adap,
                      struct i2c_msg *msgs, int num);
 u32 amdgpu_atombios_i2c_func(struct i2c_adapter *adap);
+void amdgpu_atombios_i2c_channel_trans(struct amdgpu_device* adev,
+               u8 slave_addr, u8 line_number, u8 offset, u8 data);
 
 #endif
index b2ebd4f..c2ef945 100644 (file)
@@ -28,6 +28,7 @@
 #include "vid.h"
 #include "amdgpu_ucode.h"
 #include "amdgpu_atombios.h"
+#include "atombios_i2c.h"
 #include "clearstate_vi.h"
 
 #include "gmc/gmc_8_2_d.h"
@@ -284,6 +285,7 @@ static const u32 golden_settings_polaris11_a11[] =
        mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000f3,
        mmTCP_CHAN_STEER_HI, 0xffffffff, 0x00000000,
        mmTCP_CHAN_STEER_LO, 0xffffffff, 0x00003210,
+       mmVGT_RESET_DEBUG, 0x00000004, 0x00000004,
 };
 
 static const u32 polaris11_golden_common_all[] =
@@ -314,6 +316,7 @@ static const u32 golden_settings_polaris10_a11[] =
        mmTCC_CTRL, 0x00100000, 0xf31fff7f,
        mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000f7,
        mmTCP_CHAN_STEER_HI, 0xffffffff, 0x00000000,
+       mmVGT_RESET_DEBUG, 0x00000004, 0x00000004,
 };
 
 static const u32 polaris10_golden_common_all[] =
@@ -696,6 +699,10 @@ static void gfx_v8_0_init_golden_registers(struct amdgpu_device *adev)
                                                 polaris10_golden_common_all,
                                                 (const u32)ARRAY_SIZE(polaris10_golden_common_all));
                WREG32_SMC(ixCG_ACLK_CNTL, 0x0000001C);
+               if (adev->pdev->revision == 0xc7) {
+                       amdgpu_atombios_i2c_channel_trans(adev, 0x10, 0x96, 0x1E, 0xDD);
+                       amdgpu_atombios_i2c_channel_trans(adev, 0x10, 0x96, 0x1F, 0xD0);
+               }
                break;
        case CHIP_CARRIZO:
                amdgpu_program_register_sequence(adev,
index f313b4d..85c4deb 100644 (file)
@@ -512,6 +512,10 @@ void intel_detect_pch(struct drm_device *dev)
                                DRM_DEBUG_KMS("Found SunrisePoint LP PCH\n");
                                WARN_ON(!IS_SKYLAKE(dev) &&
                                        !IS_KABYLAKE(dev));
+                       } else if (id == INTEL_PCH_KBP_DEVICE_ID_TYPE) {
+                               dev_priv->pch_type = PCH_KBP;
+                               DRM_DEBUG_KMS("Found KabyPoint PCH\n");
+                               WARN_ON(!IS_KABYLAKE(dev));
                        } else if ((id == INTEL_PCH_P2X_DEVICE_ID_TYPE) ||
                                   (id == INTEL_PCH_P3X_DEVICE_ID_TYPE) ||
                                   ((id == INTEL_PCH_QEMU_DEVICE_ID_TYPE) &&
index 7c334e9..bc3f2e6 100644 (file)
@@ -990,6 +990,7 @@ enum intel_pch {
        PCH_CPT,        /* Cougarpoint PCH */
        PCH_LPT,        /* Lynxpoint PCH */
        PCH_SPT,        /* Sunrisepoint PCH */
+       PCH_KBP,        /* Kabypoint PCH */
        PCH_NOP,
 };
 
@@ -2600,6 +2601,15 @@ struct drm_i915_cmd_table {
 
 #define IS_BXT_REVID(p, since, until) (IS_BROXTON(p) && IS_REVID(p, since, until))
 
+#define KBL_REVID_A0           0x0
+#define KBL_REVID_B0           0x1
+#define KBL_REVID_C0           0x2
+#define KBL_REVID_D0           0x3
+#define KBL_REVID_E0           0x4
+
+#define IS_KBL_REVID(p, since, until) \
+       (IS_KABYLAKE(p) && IS_REVID(p, since, until))
+
 /*
  * The genX designation typically refers to the render engine, so render
  * capability related checks should use IS_GEN, while display and other checks
@@ -2708,11 +2718,13 @@ struct drm_i915_cmd_table {
 #define INTEL_PCH_LPT_LP_DEVICE_ID_TYPE                0x9c00
 #define INTEL_PCH_SPT_DEVICE_ID_TYPE           0xA100
 #define INTEL_PCH_SPT_LP_DEVICE_ID_TYPE                0x9D00
+#define INTEL_PCH_KBP_DEVICE_ID_TYPE           0xA200
 #define INTEL_PCH_P2X_DEVICE_ID_TYPE           0x7100
 #define INTEL_PCH_P3X_DEVICE_ID_TYPE           0x7000
 #define INTEL_PCH_QEMU_DEVICE_ID_TYPE          0x2900 /* qemu q35 has 2918 */
 
 #define INTEL_PCH_TYPE(dev) (__I915__(dev)->pch_type)
+#define HAS_PCH_KBP(dev) (INTEL_PCH_TYPE(dev) == PCH_KBP)
 #define HAS_PCH_SPT(dev) (INTEL_PCH_TYPE(dev) == PCH_SPT)
 #define HAS_PCH_LPT(dev) (INTEL_PCH_TYPE(dev) == PCH_LPT)
 #define HAS_PCH_LPT_LP(dev) (__I915__(dev)->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE)
index 425e721..6657146 100644 (file)
@@ -40,7 +40,7 @@ static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
        if (!mutex_is_locked(mutex))
                return false;
 
-#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_MUTEXES)
+#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)
        return mutex->owner == task;
 #else
        /* Since UP may be pre-empted, we cannot assume that we own the lock */
index b7ce963..44004e3 100644 (file)
@@ -55,8 +55,10 @@ int i915_gem_stolen_insert_node_in_range(struct drm_i915_private *dev_priv,
                return -ENODEV;
 
        /* See the comment at the drm_mm_init() call for more about this check.
-        * WaSkipStolenMemoryFirstPage:bdw,chv (incomplete) */
-       if (INTEL_INFO(dev_priv)->gen == 8 && start < 4096)
+        * WaSkipStolenMemoryFirstPage:bdw,chv,kbl (incomplete)
+        */
+       if (start < 4096 && (IS_GEN8(dev_priv) ||
+                            IS_KBL_REVID(dev_priv, 0, KBL_REVID_A0)))
                start = 4096;
 
        mutex_lock(&dev_priv->mm.stolen_lock);
index 2f6fd33..aab47f7 100644 (file)
@@ -2471,7 +2471,7 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl)
                        I915_WRITE(SDEIIR, iir);
                        ret = IRQ_HANDLED;
 
-                       if (HAS_PCH_SPT(dev_priv))
+                       if (HAS_PCH_SPT(dev_priv) || HAS_PCH_KBP(dev_priv))
                                spt_irq_handler(dev, iir);
                        else
                                cpt_irq_handler(dev, iir);
@@ -4661,7 +4661,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
                dev->driver->disable_vblank = gen8_disable_vblank;
                if (IS_BROXTON(dev))
                        dev_priv->display.hpd_irq_setup = bxt_hpd_irq_setup;
-               else if (HAS_PCH_SPT(dev))
+               else if (HAS_PCH_SPT(dev) || HAS_PCH_KBP(dev))
                        dev_priv->display.hpd_irq_setup = spt_hpd_irq_setup;
                else
                        dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup;
index b407411..3fcf7dd 100644 (file)
@@ -220,6 +220,9 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
 #define   ECOCHK_PPGTT_WT_HSW          (0x2<<3)
 #define   ECOCHK_PPGTT_WB_HSW          (0x3<<3)
 
+#define GEN8_CONFIG0                   _MMIO(0xD00)
+#define  GEN9_DEFAULT_FIXES            (1 << 3 | 1 << 2 | 1 << 1)
+
 #define GAC_ECO_BITS                   _MMIO(0x14090)
 #define   ECOBITS_SNB_BIT              (1<<13)
 #define   ECOBITS_PPGTT_CACHE64B       (3<<8)
@@ -1669,6 +1672,9 @@ enum skl_disp_power_wells {
 
 #define GEN7_TLB_RD_ADDR       _MMIO(0x4700)
 
+#define GAMT_CHKN_BIT_REG      _MMIO(0x4ab8)
+#define   GAMT_CHKN_DISABLE_DYNAMIC_CREDIT_SHARING     (1<<28)
+
 #if 0
 #define PRB0_TAIL      _MMIO(0x2030)
 #define PRB0_HEAD      _MMIO(0x2034)
@@ -1804,6 +1810,10 @@ enum skl_disp_power_wells {
 #define   GEN9_IZ_HASHING_MASK(slice)                  (0x3 << ((slice) * 2))
 #define   GEN9_IZ_HASHING(slice, val)                  ((val) << ((slice) * 2))
 
+/* chicken reg for WaConextSwitchWithConcurrentTLBInvalidate */
+#define GEN9_CSFE_CHICKEN1_RCS _MMIO(0x20D4)
+#define   GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE (1 << 2)
+
 /* WaClearTdlStateAckDirtyBits */
 #define GEN8_STATE_ACK         _MMIO(0x20F0)
 #define GEN9_STATE_ACK_SLICE1  _MMIO(0x20F8)
@@ -2200,6 +2210,8 @@ enum skl_disp_power_wells {
 #define ILK_DPFC_STATUS                _MMIO(0x43210)
 #define ILK_DPFC_FENCE_YOFF    _MMIO(0x43218)
 #define ILK_DPFC_CHICKEN       _MMIO(0x43224)
+#define   ILK_DPFC_DISABLE_DUMMY0 (1<<8)
+#define   ILK_DPFC_NUKE_ON_ANY_MODIFICATION    (1<<23)
 #define ILK_FBC_RT_BASE                _MMIO(0x2128)
 #define   ILK_FBC_RT_VALID     (1<<0)
 #define   SNB_FBC_FRONT_BUFFER (1<<1)
@@ -6031,6 +6043,7 @@ enum skl_disp_power_wells {
 #define CHICKEN_PAR1_1         _MMIO(0x42080)
 #define  DPA_MASK_VBLANK_SRD   (1 << 15)
 #define  FORCE_ARB_IDLE_PLANES (1 << 14)
+#define  SKL_EDP_PSR_FIX_RDWRAP        (1 << 3)
 
 #define _CHICKEN_PIPESL_1_A    0x420b0
 #define _CHICKEN_PIPESL_1_B    0x420b4
@@ -6039,6 +6052,7 @@ enum skl_disp_power_wells {
 #define CHICKEN_PIPESL_1(pipe) _MMIO_PIPE(pipe, _CHICKEN_PIPESL_1_A, _CHICKEN_PIPESL_1_B)
 
 #define DISP_ARB_CTL   _MMIO(0x45000)
+#define  DISP_FBC_MEMORY_WAKE          (1<<31)
 #define  DISP_TILE_SURFACE_SWIZZLING   (1<<13)
 #define  DISP_FBC_WM_DIS               (1<<15)
 #define DISP_ARB_CTL2  _MMIO(0x45004)
@@ -6052,6 +6066,9 @@ enum skl_disp_power_wells {
 #define HSW_NDE_RSTWRN_OPT     _MMIO(0x46408)
 #define  RESET_PCH_HANDSHAKE_ENABLE    (1<<4)
 
+#define GEN8_CHICKEN_DCPR_1            _MMIO(0x46430)
+#define   MASK_WAKEMEM                 (1<<13)
+
 #define SKL_DFSM                       _MMIO(0x51000)
 #define SKL_DFSM_CDCLK_LIMIT_MASK      (3 << 23)
 #define SKL_DFSM_CDCLK_LIMIT_675       (0 << 23)
@@ -6069,6 +6086,7 @@ enum skl_disp_power_wells {
 #define  GEN9_TSG_BARRIER_ACK_DISABLE          (1<<8)
 
 #define GEN9_CS_DEBUG_MODE1            _MMIO(0x20ec)
+#define GEN9_CTX_PREEMPT_REG           _MMIO(0x2248)
 #define GEN8_CS_CHICKEN1               _MMIO(0x2580)
 
 /* GEN7 chicken */
@@ -6076,6 +6094,7 @@ enum skl_disp_power_wells {
 # define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC     ((1<<10) | (1<<26))
 # define GEN9_RHWO_OPTIMIZATION_DISABLE                (1<<14)
 #define COMMON_SLICE_CHICKEN2                  _MMIO(0x7014)
+# define GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION (1<<8)
 # define GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE  (1<<0)
 
 #define HIZ_CHICKEN                                    _MMIO(0x7018)
@@ -6921,6 +6940,7 @@ enum skl_disp_power_wells {
 #define    EDRAM_SETS_IDX(cap)                 (((cap) >> 8) & 0x3)
 
 #define GEN6_UCGCTL1                           _MMIO(0x9400)
+# define GEN6_GAMUNIT_CLOCK_GATE_DISABLE               (1 << 22)
 # define GEN6_EU_TCUNIT_CLOCK_GATE_DISABLE             (1 << 16)
 # define GEN6_BLBUNIT_CLOCK_GATE_DISABLE               (1 << 5)
 # define GEN6_CSUNIT_CLOCK_GATE_DISABLE                        (1 << 7)
@@ -6937,6 +6957,7 @@ enum skl_disp_power_wells {
 
 #define GEN7_UCGCTL4                           _MMIO(0x940c)
 #define  GEN7_L3BANK2X_CLOCK_GATE_DISABLE      (1<<25)
+#define  GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE     (1<<14)
 
 #define GEN6_RCGCTL1                           _MMIO(0x9410)
 #define GEN6_RCGCTL2                           _MMIO(0x9414)
index a34c23e..2b3b428 100644 (file)
  * be moved to FW_FAILED.
  */
 
+#define I915_CSR_KBL "i915/kbl_dmc_ver1.bin"
+MODULE_FIRMWARE(I915_CSR_KBL);
+#define KBL_CSR_VERSION_REQUIRED       CSR_VERSION(1, 1)
+
 #define I915_CSR_SKL "i915/skl_dmc_ver1.bin"
+MODULE_FIRMWARE(I915_CSR_SKL);
+#define SKL_CSR_VERSION_REQUIRED       CSR_VERSION(1, 23)
+
 #define I915_CSR_BXT "i915/bxt_dmc_ver1.bin"
+MODULE_FIRMWARE(I915_CSR_BXT);
+#define BXT_CSR_VERSION_REQUIRED       CSR_VERSION(1, 7)
 
 #define FIRMWARE_URL  "https://01.org/linuxgraphics/intel-linux-graphics-firmwares"
 
-MODULE_FIRMWARE(I915_CSR_SKL);
-MODULE_FIRMWARE(I915_CSR_BXT);
 
-#define SKL_CSR_VERSION_REQUIRED       CSR_VERSION(1, 23)
-#define BXT_CSR_VERSION_REQUIRED       CSR_VERSION(1, 7)
+
 
 #define CSR_MAX_FW_SIZE                        0x2FFF
 #define CSR_DEFAULT_FW_OFFSET          0xFFFFFFFF
@@ -169,12 +175,10 @@ struct stepping_info {
        char substepping;
 };
 
-/*
- * Kabylake derivated from Skylake H0, so SKL H0
- * is the right firmware for KBL A0 (revid 0).
- */
 static const struct stepping_info kbl_stepping_info[] = {
-       {'H', '0'}, {'I', '0'}
+       {'A', '0'}, {'B', '0'}, {'C', '0'},
+       {'D', '0'}, {'E', '0'}, {'F', '0'},
+       {'G', '0'}, {'H', '0'}, {'I', '0'},
 };
 
 static const struct stepping_info skl_stepping_info[] = {
@@ -298,7 +302,9 @@ static uint32_t *parse_csr_fw(struct drm_i915_private *dev_priv,
 
        csr->version = css_header->version;
 
-       if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
+       if (IS_KABYLAKE(dev_priv)) {
+               required_min_version = KBL_CSR_VERSION_REQUIRED;
+       } else if (IS_SKYLAKE(dev_priv)) {
                required_min_version = SKL_CSR_VERSION_REQUIRED;
        } else if (IS_BROXTON(dev_priv)) {
                required_min_version = BXT_CSR_VERSION_REQUIRED;
@@ -446,7 +452,9 @@ void intel_csr_ucode_init(struct drm_i915_private *dev_priv)
        if (!HAS_CSR(dev_priv))
                return;
 
-       if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv))
+       if (IS_KABYLAKE(dev_priv))
+               csr->fw_path = I915_CSR_KBL;
+       else if (IS_SKYLAKE(dev_priv))
                csr->fw_path = I915_CSR_SKL;
        else if (IS_BROXTON(dev_priv))
                csr->fw_path = I915_CSR_BXT;
index 04452cf..3074c56 100644 (file)
@@ -11997,6 +11997,12 @@ static int intel_crtc_atomic_check(struct drm_crtc *crtc,
                ret = intel_color_check(crtc, crtc_state);
                if (ret)
                        return ret;
+
+               /*
+                * Changing color management on Intel hardware is
+                * handled as part of planes update.
+                */
+               crtc_state->planes_changed = true;
        }
 
        ret = 0;
index 40745e3..891107f 100644 (file)
@@ -4645,7 +4645,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)
 
        intel_dp->detect_done = false;
 
-       if (intel_connector->detect_edid)
+       if (is_edp(intel_dp) || intel_connector->detect_edid)
                return connector_status_connected;
        else
                return connector_status_disconnected;
index 42eac37..7f2d841 100644 (file)
@@ -1103,15 +1103,17 @@ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine,
                                                uint32_t *const batch,
                                                uint32_t index)
 {
+       struct drm_i915_private *dev_priv = engine->dev->dev_private;
        uint32_t l3sqc4_flush = (0x40400000 | GEN8_LQSC_FLUSH_COHERENT_LINES);
 
        /*
-        * WaDisableLSQCROPERFforOCL:skl
+        * WaDisableLSQCROPERFforOCL:skl,kbl
         * This WA is implemented in skl_init_clock_gating() but since
         * this batch updates GEN8_L3SQCREG4 with default value we need to
         * set this bit here to retain the WA during flush.
         */
-       if (IS_SKL_REVID(engine->dev, 0, SKL_REVID_E0))
+       if (IS_SKL_REVID(dev_priv, 0, SKL_REVID_E0) ||
+           IS_KBL_REVID(dev_priv, 0, KBL_REVID_E0))
                l3sqc4_flush |= GEN8_LQSC_RO_PERF_DIS;
 
        wa_ctx_emit(batch, index, (MI_STORE_REGISTER_MEM_GEN8 |
@@ -1273,6 +1275,7 @@ static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine,
 {
        int ret;
        struct drm_device *dev = engine->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        uint32_t index = wa_ctx_start(wa_ctx, *offset, CACHELINE_DWORDS);
 
        /* WaDisableCtxRestoreArbitration:skl,bxt */
@@ -1286,6 +1289,22 @@ static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine,
                return ret;
        index = ret;
 
+       /* WaClearSlmSpaceAtContextSwitch:kbl */
+       /* Actual scratch location is at 128 bytes offset */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_A0)) {
+               uint32_t scratch_addr
+                       = engine->scratch.gtt_offset + 2*CACHELINE_BYTES;
+
+               wa_ctx_emit(batch, index, GFX_OP_PIPE_CONTROL(6));
+               wa_ctx_emit(batch, index, (PIPE_CONTROL_FLUSH_L3 |
+                                          PIPE_CONTROL_GLOBAL_GTT_IVB |
+                                          PIPE_CONTROL_CS_STALL |
+                                          PIPE_CONTROL_QW_WRITE));
+               wa_ctx_emit(batch, index, scratch_addr);
+               wa_ctx_emit(batch, index, 0);
+               wa_ctx_emit(batch, index, 0);
+               wa_ctx_emit(batch, index, 0);
+       }
        /* Pad to end of cacheline */
        while (index % CACHELINE_DWORDS)
                wa_ctx_emit(batch, index, MI_NOOP);
@@ -1687,9 +1706,10 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request,
        struct intel_ringbuffer *ringbuf = request->ringbuf;
        struct intel_engine_cs *engine = ringbuf->engine;
        u32 scratch_addr = engine->scratch.gtt_offset + 2 * CACHELINE_BYTES;
-       bool vf_flush_wa = false;
+       bool vf_flush_wa = false, dc_flush_wa = false;
        u32 flags = 0;
        int ret;
+       int len;
 
        flags |= PIPE_CONTROL_CS_STALL;
 
@@ -1716,9 +1736,21 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request,
                 */
                if (IS_GEN9(engine->dev))
                        vf_flush_wa = true;
+
+               /* WaForGAMHang:kbl */
+               if (IS_KBL_REVID(request->i915, 0, KBL_REVID_B0))
+                       dc_flush_wa = true;
        }
 
-       ret = intel_ring_begin(request, vf_flush_wa ? 12 : 6);
+       len = 6;
+
+       if (vf_flush_wa)
+               len += 6;
+
+       if (dc_flush_wa)
+               len += 12;
+
+       ret = intel_ring_begin(request, len);
        if (ret)
                return ret;
 
@@ -1731,12 +1763,31 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request,
                intel_logical_ring_emit(ringbuf, 0);
        }
 
+       if (dc_flush_wa) {
+               intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
+               intel_logical_ring_emit(ringbuf, PIPE_CONTROL_DC_FLUSH_ENABLE);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+       }
+
        intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
        intel_logical_ring_emit(ringbuf, flags);
        intel_logical_ring_emit(ringbuf, scratch_addr);
        intel_logical_ring_emit(ringbuf, 0);
        intel_logical_ring_emit(ringbuf, 0);
        intel_logical_ring_emit(ringbuf, 0);
+
+       if (dc_flush_wa) {
+               intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
+               intel_logical_ring_emit(ringbuf, PIPE_CONTROL_CS_STALL);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+       }
+
        intel_logical_ring_advance(ringbuf);
 
        return 0;
index 99e2603..16e209d 100644 (file)
@@ -1038,5 +1038,16 @@ intel_opregion_get_panel_type(struct drm_device *dev)
                return -ENODEV;
        }
 
+       /*
+        * FIXME On Dell XPS 13 9350 the OpRegion panel type (0) gives us
+        * low vswing for eDP, whereas the VBT panel type (2) gives us normal
+        * vswing instead. Low vswing results in some display flickers, so
+        * let's simply ignore the OpRegion panel type on SKL for now.
+        */
+       if (IS_SKYLAKE(dev)) {
+               DRM_DEBUG_KMS("Ignoring OpRegion panel type (%d)\n", ret - 1);
+               return -ENODEV;
+       }
+
        return ret - 1;
 }
index 8357d57..aba9409 100644 (file)
@@ -1731,7 +1731,8 @@ intel_panel_init_backlight_funcs(struct intel_panel *panel)
                panel->backlight.set = bxt_set_backlight;
                panel->backlight.get = bxt_get_backlight;
                panel->backlight.hz_to_pwm = bxt_hz_to_pwm;
-       } else if (HAS_PCH_LPT(dev_priv) || HAS_PCH_SPT(dev_priv)) {
+       } else if (HAS_PCH_LPT(dev_priv) || HAS_PCH_SPT(dev_priv) ||
+                  HAS_PCH_KBP(dev_priv)) {
                panel->backlight.setup = lpt_setup_backlight;
                panel->backlight.enable = lpt_enable_backlight;
                panel->backlight.disable = lpt_disable_backlight;
index a7ef45d..2863b92 100644 (file)
 #define INTEL_RC6p_ENABLE                      (1<<1)
 #define INTEL_RC6pp_ENABLE                     (1<<2)
 
+static void gen9_init_clock_gating(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /* See Bspec note for PSR2_CTL bit 31, Wa#828:skl,bxt,kbl */
+       I915_WRITE(CHICKEN_PAR1_1,
+                  I915_READ(CHICKEN_PAR1_1) | SKL_EDP_PSR_FIX_RDWRAP);
+
+       I915_WRITE(GEN8_CONFIG0,
+                  I915_READ(GEN8_CONFIG0) | GEN9_DEFAULT_FIXES);
+
+       /* WaEnableChickenDCPR:skl,bxt,kbl */
+       I915_WRITE(GEN8_CHICKEN_DCPR_1,
+                  I915_READ(GEN8_CHICKEN_DCPR_1) | MASK_WAKEMEM);
+
+       /* WaFbcTurnOffFbcWatermark:skl,bxt,kbl */
+       /* WaFbcWakeMemOn:skl,bxt,kbl */
+       I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) |
+                  DISP_FBC_WM_DIS |
+                  DISP_FBC_MEMORY_WAKE);
+
+       /* WaFbcHighMemBwCorruptionAvoidance:skl,bxt,kbl */
+       I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) |
+                  ILK_DPFC_DISABLE_DUMMY0);
+}
+
 static void bxt_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       gen9_init_clock_gating(dev);
+
        /* WaDisableSDEUnitClockGating:bxt */
        I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) |
                   GEN8_SDEUNIT_CLOCK_GATE_DISABLE);
@@ -6698,6 +6726,38 @@ static void lpt_suspend_hw(struct drm_device *dev)
        }
 }
 
+static void kabylake_init_clock_gating(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       gen9_init_clock_gating(dev);
+
+       /* WaDisableSDEUnitClockGating:kbl */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
+               I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) |
+                          GEN8_SDEUNIT_CLOCK_GATE_DISABLE);
+
+       /* WaDisableGamClockGating:kbl */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
+               I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) |
+                          GEN6_GAMUNIT_CLOCK_GATE_DISABLE);
+
+       /* WaFbcNukeOnHostModify:kbl */
+       I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) |
+                  ILK_DPFC_NUKE_ON_ANY_MODIFICATION);
+}
+
+static void skylake_init_clock_gating(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       gen9_init_clock_gating(dev);
+
+       /* WaFbcNukeOnHostModify:skl */
+       I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) |
+                  ILK_DPFC_NUKE_ON_ANY_MODIFICATION);
+}
+
 static void broadwell_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -7163,9 +7223,9 @@ static void nop_init_clock_gating(struct drm_device *dev)
 void intel_init_clock_gating_hooks(struct drm_i915_private *dev_priv)
 {
        if (IS_SKYLAKE(dev_priv))
-               dev_priv->display.init_clock_gating = nop_init_clock_gating;
+               dev_priv->display.init_clock_gating = skylake_init_clock_gating;
        else if (IS_KABYLAKE(dev_priv))
-               dev_priv->display.init_clock_gating = nop_init_clock_gating;
+               dev_priv->display.init_clock_gating = kabylake_init_clock_gating;
        else if (IS_BROXTON(dev_priv))
                dev_priv->display.init_clock_gating = bxt_init_clock_gating;
        else if (IS_BROADWELL(dev_priv))
index 04402bb..68c5af0 100644 (file)
@@ -913,24 +913,26 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
 {
        struct drm_device *dev = engine->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       uint32_t tmp;
        int ret;
 
-       /* WaEnableLbsSlaRetryTimerDecrement:skl */
+       /* WaConextSwitchWithConcurrentTLBInvalidate:skl,bxt,kbl */
+       I915_WRITE(GEN9_CSFE_CHICKEN1_RCS, _MASKED_BIT_ENABLE(GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE));
+
+       /* WaEnableLbsSlaRetryTimerDecrement:skl,bxt,kbl */
        I915_WRITE(BDW_SCRATCH1, I915_READ(BDW_SCRATCH1) |
                   GEN9_LBS_SLA_RETRY_TIMER_DECREMENT_ENABLE);
 
-       /* WaDisableKillLogic:bxt,skl */
+       /* WaDisableKillLogic:bxt,skl,kbl */
        I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
                   ECOCHK_DIS_TLB);
 
-       /* WaClearFlowControlGpgpuContextSave:skl,bxt */
-       /* WaDisablePartialInstShootdown:skl,bxt */
+       /* WaClearFlowControlGpgpuContextSave:skl,bxt,kbl */
+       /* WaDisablePartialInstShootdown:skl,bxt,kbl */
        WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN,
                          FLOW_CONTROL_ENABLE |
                          PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE);
 
-       /* Syncing dependencies between camera and graphics:skl,bxt */
+       /* Syncing dependencies between camera and graphics:skl,bxt,kbl */
        WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3,
                          GEN9_DISABLE_OCL_OOB_SUPPRESS_LOGIC);
 
@@ -952,18 +954,18 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
                 */
        }
 
-       /* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt */
-       /* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt */
+       /* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt,kbl */
+       /* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt,kbl */
        WA_SET_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN7,
                          GEN9_ENABLE_YV12_BUGFIX |
                          GEN9_ENABLE_GPGPU_PREEMPTION);
 
-       /* Wa4x4STCOptimizationDisable:skl,bxt */
-       /* WaDisablePartialResolveInVc:skl,bxt */
+       /* Wa4x4STCOptimizationDisable:skl,bxt,kbl */
+       /* WaDisablePartialResolveInVc:skl,bxt,kbl */
        WA_SET_BIT_MASKED(CACHE_MODE_1, (GEN8_4x4_STC_OPTIMIZATION_DISABLE |
                                         GEN9_PARTIAL_RESOLVE_IN_VC_DISABLE));
 
-       /* WaCcsTlbPrefetchDisable:skl,bxt */
+       /* WaCcsTlbPrefetchDisable:skl,bxt,kbl */
        WA_CLR_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN5,
                          GEN9_CCS_TLB_PREFETCH_ENABLE);
 
@@ -973,31 +975,57 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
                WA_SET_BIT_MASKED(SLICE_ECO_CHICKEN0,
                                  PIXEL_MASK_CAMMING_DISABLE);
 
-       /* WaForceContextSaveRestoreNonCoherent:skl,bxt */
-       tmp = HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT;
-       if (IS_SKL_REVID(dev, SKL_REVID_F0, REVID_FOREVER) ||
-           IS_BXT_REVID(dev, BXT_REVID_B0, REVID_FOREVER))
-               tmp |= HDC_FORCE_CSR_NON_COHERENT_OVR_DISABLE;
-       WA_SET_BIT_MASKED(HDC_CHICKEN0, tmp);
+       /* WaForceContextSaveRestoreNonCoherent:skl,bxt,kbl */
+       WA_SET_BIT_MASKED(HDC_CHICKEN0,
+                         HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT |
+                         HDC_FORCE_CSR_NON_COHERENT_OVR_DISABLE);
+
+       /* WaForceEnableNonCoherent and WaDisableHDCInvalidation are
+        * both tied to WaForceContextSaveRestoreNonCoherent
+        * in some hsds for skl. We keep the tie for all gen9. The
+        * documentation is a bit hazy and so we want to get common behaviour,
+        * even though there is no clear evidence we would need both on kbl/bxt.
+        * This area has been source of system hangs so we play it safe
+        * and mimic the skl regardless of what bspec says.
+        *
+        * Use Force Non-Coherent whenever executing a 3D context. This
+        * is a workaround for a possible hang in the unlikely event
+        * a TLB invalidation occurs during a PSD flush.
+        */
 
-       /* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt */
-       if (IS_SKYLAKE(dev) || IS_BXT_REVID(dev, 0, BXT_REVID_B0))
+       /* WaForceEnableNonCoherent:skl,bxt,kbl */
+       WA_SET_BIT_MASKED(HDC_CHICKEN0,
+                         HDC_FORCE_NON_COHERENT);
+
+       /* WaDisableHDCInvalidation:skl,bxt,kbl */
+       I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
+                  BDW_DISABLE_HDC_INVALIDATION);
+
+       /* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt,kbl */
+       if (IS_SKYLAKE(dev_priv) ||
+           IS_KABYLAKE(dev_priv) ||
+           IS_BXT_REVID(dev_priv, 0, BXT_REVID_B0))
                WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3,
                                  GEN8_SAMPLER_POWER_BYPASS_DIS);
 
-       /* WaDisableSTUnitPowerOptimization:skl,bxt */
+       /* WaDisableSTUnitPowerOptimization:skl,bxt,kbl */
        WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN2, GEN8_ST_PO_DISABLE);
 
-       /* WaOCLCoherentLineFlush:skl,bxt */
+       /* WaOCLCoherentLineFlush:skl,bxt,kbl */
        I915_WRITE(GEN8_L3SQCREG4, (I915_READ(GEN8_L3SQCREG4) |
                                    GEN8_LQSC_FLUSH_COHERENT_LINES));
 
-       /* WaEnablePreemptionGranularityControlByUMD:skl,bxt */
+       /* WaVFEStateAfterPipeControlwithMediaStateClear:skl,bxt */
+       ret = wa_ring_whitelist_reg(engine, GEN9_CTX_PREEMPT_REG);
+       if (ret)
+               return ret;
+
+       /* WaEnablePreemptionGranularityControlByUMD:skl,bxt,kbl */
        ret= wa_ring_whitelist_reg(engine, GEN8_CS_CHICKEN1);
        if (ret)
                return ret;
 
-       /* WaAllowUMDToModifyHDCChicken1:skl,bxt */
+       /* WaAllowUMDToModifyHDCChicken1:skl,bxt,kbl */
        ret = wa_ring_whitelist_reg(engine, GEN8_HDC_CHICKEN1);
        if (ret)
                return ret;
@@ -1092,22 +1120,6 @@ static int skl_init_workarounds(struct intel_engine_cs *engine)
                WA_SET_BIT_MASKED(HIZ_CHICKEN,
                                  BDW_HIZ_POWER_COMPILER_CLOCK_GATING_DISABLE);
 
-       /* This is tied to WaForceContextSaveRestoreNonCoherent */
-       if (IS_SKL_REVID(dev, 0, REVID_FOREVER)) {
-               /*
-                *Use Force Non-Coherent whenever executing a 3D context. This
-                * is a workaround for a possible hang in the unlikely event
-                * a TLB invalidation occurs during a PSD flush.
-                */
-               /* WaForceEnableNonCoherent:skl */
-               WA_SET_BIT_MASKED(HDC_CHICKEN0,
-                                 HDC_FORCE_NON_COHERENT);
-
-               /* WaDisableHDCInvalidation:skl */
-               I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
-                          BDW_DISABLE_HDC_INVALIDATION);
-       }
-
        /* WaBarrierPerformanceFixDisable:skl */
        if (IS_SKL_REVID(dev, SKL_REVID_C0, SKL_REVID_D0))
                WA_SET_BIT_MASKED(HDC_CHICKEN0,
@@ -1120,6 +1132,9 @@ static int skl_init_workarounds(struct intel_engine_cs *engine)
                        GEN7_HALF_SLICE_CHICKEN1,
                        GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE);
 
+       /* WaDisableGafsUnitClkGating:skl */
+       WA_SET_BIT(GEN7_UCGCTL4, GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
+
        /* WaDisableLSQCROPERFforOCL:skl */
        ret = wa_ring_whitelist_reg(engine, GEN8_L3SQCREG4);
        if (ret)
@@ -1174,6 +1189,63 @@ static int bxt_init_workarounds(struct intel_engine_cs *engine)
                        return ret;
        }
 
+       /* WaInsertDummyPushConstPs:bxt */
+       if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_B0))
+               WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
+                                 GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
+
+       return 0;
+}
+
+static int kbl_init_workarounds(struct intel_engine_cs *engine)
+{
+       struct drm_i915_private *dev_priv = engine->dev->dev_private;
+       int ret;
+
+       ret = gen9_init_workarounds(engine);
+       if (ret)
+               return ret;
+
+       /* WaEnableGapsTsvCreditFix:kbl */
+       I915_WRITE(GEN8_GARBCNTL, (I915_READ(GEN8_GARBCNTL) |
+                                  GEN9_GAPS_TSV_CREDIT_DISABLE));
+
+       /* WaDisableDynamicCreditSharing:kbl */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
+               WA_SET_BIT(GAMT_CHKN_BIT_REG,
+                          GAMT_CHKN_DISABLE_DYNAMIC_CREDIT_SHARING);
+
+       /* WaDisableFenceDestinationToSLM:kbl (pre-prod) */
+       if (IS_KBL_REVID(dev_priv, KBL_REVID_A0, KBL_REVID_A0))
+               WA_SET_BIT_MASKED(HDC_CHICKEN0,
+                                 HDC_FENCE_DEST_SLM_DISABLE);
+
+       /* GEN8_L3SQCREG4 has a dependency with WA batch so any new changes
+        * involving this register should also be added to WA batch as required.
+        */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_E0))
+               /* WaDisableLSQCROPERFforOCL:kbl */
+               I915_WRITE(GEN8_L3SQCREG4, I915_READ(GEN8_L3SQCREG4) |
+                          GEN8_LQSC_RO_PERF_DIS);
+
+       /* WaInsertDummyPushConstPs:kbl */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
+               WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
+                                 GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
+
+       /* WaDisableGafsUnitClkGating:kbl */
+       WA_SET_BIT(GEN7_UCGCTL4, GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
+
+       /* WaDisableSbeCacheDispatchPortSharing:kbl */
+       WA_SET_BIT_MASKED(
+               GEN7_HALF_SLICE_CHICKEN1,
+               GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE);
+
+       /* WaDisableLSQCROPERFforOCL:kbl */
+       ret = wa_ring_whitelist_reg(engine, GEN8_L3SQCREG4);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
@@ -1199,6 +1271,9 @@ int init_workarounds_ring(struct intel_engine_cs *engine)
        if (IS_BROXTON(dev))
                return bxt_init_workarounds(engine);
 
+       if (IS_KABYLAKE(dev_priv))
+               return kbl_init_workarounds(engine);
+
        return 0;
 }
 
index 39386f5..a71cf98 100644 (file)
@@ -1034,9 +1034,9 @@ out_unlock:
        return ret;
 }
 
-static bool ttm_bo_mem_compat(struct ttm_placement *placement,
-                             struct ttm_mem_reg *mem,
-                             uint32_t *new_flags)
+bool ttm_bo_mem_compat(struct ttm_placement *placement,
+                      struct ttm_mem_reg *mem,
+                      uint32_t *new_flags)
 {
        int i;
 
@@ -1068,6 +1068,7 @@ static bool ttm_bo_mem_compat(struct ttm_placement *placement,
 
        return false;
 }
+EXPORT_SYMBOL(ttm_bo_mem_compat);
 
 int ttm_bo_validate(struct ttm_buffer_object *bo,
                        struct ttm_placement *placement,
index 9b078a4..0cd8890 100644 (file)
@@ -49,6 +49,7 @@ int vmw_dmabuf_pin_in_placement(struct vmw_private *dev_priv,
 {
        struct ttm_buffer_object *bo = &buf->base;
        int ret;
+       uint32_t new_flags;
 
        ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
        if (unlikely(ret != 0))
@@ -60,7 +61,12 @@ int vmw_dmabuf_pin_in_placement(struct vmw_private *dev_priv,
        if (unlikely(ret != 0))
                goto err;
 
-       ret = ttm_bo_validate(bo, placement, interruptible, false);
+       if (buf->pin_count > 0)
+               ret = ttm_bo_mem_compat(placement, &bo->mem,
+                                       &new_flags) == true ? 0 : -EINVAL;
+       else
+               ret = ttm_bo_validate(bo, placement, interruptible, false);
+
        if (!ret)
                vmw_bo_pin_reserved(buf, true);
 
@@ -91,6 +97,7 @@ int vmw_dmabuf_pin_in_vram_or_gmr(struct vmw_private *dev_priv,
 {
        struct ttm_buffer_object *bo = &buf->base;
        int ret;
+       uint32_t new_flags;
 
        ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
        if (unlikely(ret != 0))
@@ -102,6 +109,12 @@ int vmw_dmabuf_pin_in_vram_or_gmr(struct vmw_private *dev_priv,
        if (unlikely(ret != 0))
                goto err;
 
+       if (buf->pin_count > 0) {
+               ret = ttm_bo_mem_compat(&vmw_vram_gmr_placement, &bo->mem,
+                                       &new_flags) == true ? 0 : -EINVAL;
+               goto out_unreserve;
+       }
+
        ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, interruptible,
                              false);
        if (likely(ret == 0) || ret == -ERESTARTSYS)
@@ -161,6 +174,7 @@ int vmw_dmabuf_pin_in_start_of_vram(struct vmw_private *dev_priv,
        struct ttm_placement placement;
        struct ttm_place place;
        int ret = 0;
+       uint32_t new_flags;
 
        place = vmw_vram_placement.placement[0];
        place.lpfn = bo->num_pages;
@@ -185,10 +199,15 @@ int vmw_dmabuf_pin_in_start_of_vram(struct vmw_private *dev_priv,
         */
        if (bo->mem.mem_type == TTM_PL_VRAM &&
            bo->mem.start < bo->num_pages &&
-           bo->mem.start > 0)
+           bo->mem.start > 0 &&
+           buf->pin_count == 0)
                (void) ttm_bo_validate(bo, &vmw_sys_placement, false, false);
 
-       ret = ttm_bo_validate(bo, &placement, interruptible, false);
+       if (buf->pin_count > 0)
+               ret = ttm_bo_mem_compat(&placement, &bo->mem,
+                                       &new_flags) == true ? 0 : -EINVAL;
+       else
+               ret = ttm_bo_validate(bo, &placement, interruptible, false);
 
        /* For some reason we didn't end up at the start of vram */
        WARN_ON(ret == 0 && bo->offset != 0);
index 9fcd820..8d528fc 100644 (file)
@@ -233,6 +233,7 @@ static int vmw_force_iommu;
 static int vmw_restrict_iommu;
 static int vmw_force_coherent;
 static int vmw_restrict_dma_mask;
+static int vmw_assume_16bpp;
 
 static int vmw_probe(struct pci_dev *, const struct pci_device_id *);
 static void vmw_master_init(struct vmw_master *);
@@ -249,6 +250,8 @@ MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages");
 module_param_named(force_coherent, vmw_force_coherent, int, 0600);
 MODULE_PARM_DESC(restrict_dma_mask, "Restrict DMA mask to 44 bits with IOMMU");
 module_param_named(restrict_dma_mask, vmw_restrict_dma_mask, int, 0600);
+MODULE_PARM_DESC(assume_16bpp, "Assume 16-bpp when filtering modes");
+module_param_named(assume_16bpp, vmw_assume_16bpp, int, 0600);
 
 
 static void vmw_print_capabilities(uint32_t capabilities)
@@ -660,6 +663,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
        dev_priv->vram_start = pci_resource_start(dev->pdev, 1);
        dev_priv->mmio_start = pci_resource_start(dev->pdev, 2);
 
+       dev_priv->assume_16bpp = !!vmw_assume_16bpp;
+
        dev_priv->enable_fb = enable_fbdev;
 
        vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2);
@@ -706,6 +711,13 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
                        vmw_read(dev_priv,
                                 SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB);
 
+               /*
+                * Workaround for low memory 2D VMs to compensate for the
+                * allocation taken by fbdev
+                */
+               if (!(dev_priv->capabilities & SVGA_CAP_3D))
+                       mem_size *= 2;
+
                dev_priv->max_mob_pages = mem_size * 1024 / PAGE_SIZE;
                dev_priv->prim_bb_mem =
                        vmw_read(dev_priv,
index 1980e2a..89fb194 100644 (file)
@@ -386,6 +386,7 @@ struct vmw_private {
        spinlock_t hw_lock;
        spinlock_t cap_lock;
        bool has_dx;
+       bool assume_16bpp;
 
        /*
         * VGA registers.
index 679a4cb..d2d9395 100644 (file)
@@ -517,28 +517,6 @@ static int vmw_fb_kms_framebuffer(struct fb_info *info)
 
        par->set_fb = &vfb->base;
 
-       if (!par->bo_ptr) {
-               /*
-                * Pin before mapping. Since we don't know in what placement
-                * to pin, call into KMS to do it for us.
-                */
-               ret = vfb->pin(vfb);
-               if (ret) {
-                       DRM_ERROR("Could not pin the fbdev framebuffer.\n");
-                       return ret;
-               }
-
-               ret = ttm_bo_kmap(&par->vmw_bo->base, 0,
-                                 par->vmw_bo->base.num_pages, &par->map);
-               if (ret) {
-                       vfb->unpin(vfb);
-                       DRM_ERROR("Could not map the fbdev framebuffer.\n");
-                       return ret;
-               }
-
-               par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite);
-       }
-
        return 0;
 }
 
@@ -601,6 +579,31 @@ static int vmw_fb_set_par(struct fb_info *info)
        if (ret)
                goto out_unlock;
 
+       if (!par->bo_ptr) {
+               struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(set.fb);
+
+               /*
+                * Pin before mapping. Since we don't know in what placement
+                * to pin, call into KMS to do it for us.
+                */
+               ret = vfb->pin(vfb);
+               if (ret) {
+                       DRM_ERROR("Could not pin the fbdev framebuffer.\n");
+                       goto out_unlock;
+               }
+
+               ret = ttm_bo_kmap(&par->vmw_bo->base, 0,
+                                 par->vmw_bo->base.num_pages, &par->map);
+               if (ret) {
+                       vfb->unpin(vfb);
+                       DRM_ERROR("Could not map the fbdev framebuffer.\n");
+                       goto out_unlock;
+               }
+
+               par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite);
+       }
+
+
        vmw_fb_dirty_mark(par, par->fb_x, par->fb_y,
                          par->set_fb->width, par->set_fb->height);
 
index 55231cc..e29da45 100644 (file)
@@ -1553,14 +1553,10 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
                DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC)
        };
        int i;
-       u32 assumed_bpp = 2;
+       u32 assumed_bpp = 4;
 
-       /*
-        * If using screen objects, then assume 32-bpp because that's what the
-        * SVGA device is assuming
-        */
-       if (dev_priv->active_display_unit == vmw_du_screen_object)
-               assumed_bpp = 4;
+       if (dev_priv->assume_16bpp)
+               assumed_bpp = 2;
 
        if (dev_priv->active_display_unit == vmw_du_screen_target) {
                max_width  = min(max_width,  dev_priv->stdu_max_width);
index f0374f9..e57a0ba 100644 (file)
@@ -300,6 +300,9 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
                break;
        }
 
+       if (retries == RETRIES)
+               return -EINVAL;
+
        *msg_len = reply_len;
        *msg     = reply;
 
index 9ca818f..41932a7 100644 (file)
@@ -399,8 +399,10 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv,
 
        WARN_ON_ONCE(!stdu->defined);
 
-       if (!vfb->dmabuf && new_fb->width == mode->hdisplay &&
-           new_fb->height == mode->vdisplay)
+       new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb);
+
+       if (new_vfbs && new_vfbs->surface->base_size.width == mode->hdisplay &&
+           new_vfbs->surface->base_size.height == mode->vdisplay)
                new_content_type = SAME_AS_DISPLAY;
        else if (vfb->dmabuf)
                new_content_type = SEPARATE_DMA;
@@ -444,7 +446,6 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv,
                        content_srf.mip_levels[0]     = 1;
                        content_srf.multisample_count = 0;
                } else {
-                       new_vfbs = vmw_framebuffer_to_vfbs(new_fb);
                        content_srf = *new_vfbs->surface;
                }
 
@@ -464,7 +465,6 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv,
                        return ret;
                }
        } else if (new_content_type == SAME_AS_DISPLAY) {
-               new_vfbs = vmw_framebuffer_to_vfbs(new_fb);
                new_display_srf = vmw_surface_reference(new_vfbs->surface);
        }
 
index cc6439a..041050e 100644 (file)
@@ -1268,6 +1268,8 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
                }
        }
 
+       idx = 0;
+
        do {
                if (msgs[idx].len == 0) {
                        ret = -EINVAL;
index 445398c..b126dba 100644 (file)
@@ -912,7 +912,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
        ret = tegra_i2c_init(i2c_dev);
        if (ret) {
                dev_err(&pdev->dev, "Failed to initialize i2c controller");
-               goto unprepare_div_clk;
+               goto disable_div_clk;
        }
 
        ret = devm_request_irq(&pdev->dev, i2c_dev->irq,
index e33022e..6e5fac6 100644 (file)
@@ -56,9 +56,7 @@ EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);
  * The board info passed can safely be __initdata, but be careful of embedded
  * pointers (for platform_data, functions, etc) since that won't be copied.
  */
-int __init
-i2c_register_board_info(int busnum,
-       struct i2c_board_info const *info, unsigned len)
+int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
 {
        int status;
 
index 26e7c51..c6a90b4 100644 (file)
@@ -145,7 +145,7 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux,
                mux->data.idle_in_use = true;
 
        /* map address from "reg" if exists */
-       if (of_address_to_resource(np, 0, &res)) {
+       if (of_address_to_resource(np, 0, &res) == 0) {
                mux->data.reg_size = resource_size(&res);
                mux->data.reg = devm_ioremap_resource(&pdev->dev, &res);
                if (IS_ERR(mux->data.reg))
index a5793c8..60df4f8 100644 (file)
@@ -530,6 +530,7 @@ static PORT_PMA_ATTR(port_xmit_data             , 12, 32, 192);
 static PORT_PMA_ATTR(port_rcv_data                 , 13, 32, 224);
 static PORT_PMA_ATTR(port_xmit_packets             , 14, 32, 256);
 static PORT_PMA_ATTR(port_rcv_packets              , 15, 32, 288);
+static PORT_PMA_ATTR(port_xmit_wait                ,  0, 32, 320);
 
 /*
  * Counters added by extended set
@@ -560,6 +561,7 @@ static struct attribute *pma_attrs[] = {
        &port_pma_attr_port_rcv_data.attr.attr,
        &port_pma_attr_port_xmit_packets.attr.attr,
        &port_pma_attr_port_rcv_packets.attr.attr,
+       &port_pma_attr_port_xmit_wait.attr.attr,
        NULL
 };
 
@@ -579,6 +581,7 @@ static struct attribute *pma_attrs_ext[] = {
        &port_pma_attr_ext_port_xmit_data.attr.attr,
        &port_pma_attr_ext_port_rcv_data.attr.attr,
        &port_pma_attr_ext_port_xmit_packets.attr.attr,
+       &port_pma_attr_port_xmit_wait.attr.attr,
        &port_pma_attr_ext_port_rcv_packets.attr.attr,
        &port_pma_attr_ext_unicast_rcv_packets.attr.attr,
        &port_pma_attr_ext_unicast_xmit_packets.attr.attr,
@@ -604,6 +607,7 @@ static struct attribute *pma_attrs_noietf[] = {
        &port_pma_attr_ext_port_rcv_data.attr.attr,
        &port_pma_attr_ext_port_xmit_packets.attr.attr,
        &port_pma_attr_ext_port_rcv_packets.attr.attr,
+       &port_pma_attr_port_xmit_wait.attr.attr,
        NULL
 };
 
index f5de851..dad4d0e 100644 (file)
@@ -14113,8 +14113,14 @@ static int init_asic_data(struct hfi1_devdata *dd)
 {
        unsigned long flags;
        struct hfi1_devdata *tmp, *peer = NULL;
+       struct hfi1_asic_data *asic_data;
        int ret = 0;
 
+       /* pre-allocate the asic structure in case we are the first device */
+       asic_data = kzalloc(sizeof(*dd->asic_data), GFP_KERNEL);
+       if (!asic_data)
+               return -ENOMEM;
+
        spin_lock_irqsave(&hfi1_devs_lock, flags);
        /* Find our peer device */
        list_for_each_entry(tmp, &hfi1_dev_list, list) {
@@ -14126,18 +14132,14 @@ static int init_asic_data(struct hfi1_devdata *dd)
        }
 
        if (peer) {
+               /* use already allocated structure */
                dd->asic_data = peer->asic_data;
+               kfree(asic_data);
        } else {
-               dd->asic_data = kzalloc(sizeof(*dd->asic_data), GFP_KERNEL);
-               if (!dd->asic_data) {
-                       ret = -ENOMEM;
-                       goto done;
-               }
+               dd->asic_data = asic_data;
                mutex_init(&dd->asic_data->asic_resource_mutex);
        }
        dd->asic_data->dds[dd->hfi1_id] = dd; /* self back-pointer */
-
-done:
        spin_unlock_irqrestore(&hfi1_devs_lock, flags);
        return ret;
 }
index 1e503ad..be91f6f 100644 (file)
@@ -678,8 +678,7 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
        u32 tlen = packet->tlen;
        struct rvt_qp *qp = packet->qp;
        bool has_grh = rcv_flags & HFI1_HAS_GRH;
-       bool sc4_bit = has_sc4_bit(packet);
-       u8 sc;
+       u8 sc5 = hdr2sc((struct hfi1_message_header *)hdr, packet->rhf);
        u32 bth1;
        int is_mcast;
        struct ib_grh *grh = NULL;
@@ -697,10 +696,8 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
                 */
                struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
                u32 lqpn =  be32_to_cpu(ohdr->bth[1]) & RVT_QPN_MASK;
-               u8 sl, sc5;
+               u8 sl;
 
-               sc5 = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
-               sc5 |= sc4_bit;
                sl = ibp->sc_to_sl[sc5];
 
                process_becn(ppd, sl, 0, lqpn, 0, IB_CC_SVCTYPE_UD);
@@ -717,10 +714,6 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
 
        if (!is_mcast && (opcode != IB_OPCODE_CNP) && bth1 & HFI1_FECN_SMASK) {
                u16 slid = be16_to_cpu(hdr->lrh[3]);
-               u8 sc5;
-
-               sc5 = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
-               sc5 |= sc4_bit;
 
                return_cnp(ibp, qp, src_qp, pkey, dlid, slid, sc5, grh);
        }
@@ -745,10 +738,6 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
                if (qp->ibqp.qp_num > 1) {
                        struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
                        u16 slid;
-                       u8 sc5;
-
-                       sc5 = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
-                       sc5 |= sc4_bit;
 
                        slid = be16_to_cpu(hdr->lrh[3]);
                        if (unlikely(rcv_pkey_check(ppd, pkey, sc5, slid))) {
@@ -790,10 +779,6 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
                /* Received on QP0, and so by definition, this is an SMP */
                struct opa_smp *smp = (struct opa_smp *)data;
                u16 slid = be16_to_cpu(hdr->lrh[3]);
-               u8 sc5;
-
-               sc5 = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
-               sc5 |= sc4_bit;
 
                if (opa_smp_check(ibp, pkey, sc5, qp, slid, smp))
                        goto drop;
@@ -890,9 +875,7 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
        }
 
        wc.slid = be16_to_cpu(hdr->lrh[3]);
-       sc = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
-       sc |= sc4_bit;
-       wc.sl = ibp->sc_to_sl[sc];
+       wc.sl = ibp->sc_to_sl[sc5];
 
        /*
         * Save the LMC lower bits if the destination LID is a unicast LID.
index c963cad..6e90813 100644 (file)
@@ -600,8 +600,7 @@ static enum i40iw_status_code i40iw_create_cqp(struct i40iw_device *iwdev)
        cqp_init_info.scratch_array = cqp->scratch_array;
        status = dev->cqp_ops->cqp_init(dev->cqp, &cqp_init_info);
        if (status) {
-               i40iw_pr_err("cqp init status %d maj_err %d min_err %d\n",
-                            status, maj_err, min_err);
+               i40iw_pr_err("cqp init status %d\n", status);
                goto exit;
        }
        status = dev->cqp_ops->cqp_create(dev->cqp, true, &maj_err, &min_err);
index 33959ed..283b64c 100644 (file)
@@ -1474,6 +1474,7 @@ static int i40iw_hw_alloc_stag(struct i40iw_device *iwdev, struct i40iw_mr *iwmr
        info->stag_idx = iwmr->stag >> I40IW_CQPSQ_STAG_IDX_SHIFT;
        info->pd_id = iwpd->sc_pd.pd_id;
        info->total_len = iwmr->length;
+       info->remote_access = true;
        cqp_info->cqp_cmd = OP_ALLOC_STAG;
        cqp_info->post_sq = 1;
        cqp_info->in.u.alloc_stag.dev = &iwdev->sc_dev;
index 3438e98..a529a45 100644 (file)
@@ -1431,6 +1431,9 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
        int ep_irq_in_idx;
        int i, error;
 
+       if (intf->cur_altsetting->desc.bNumEndpoints != 2)
+               return -ENODEV;
+
        for (i = 0; xpad_device[i].idVendor; i++) {
                if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
                    (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
index b368b05..253df96 100644 (file)
@@ -157,11 +157,11 @@ static int rmi_function_match(struct device *dev, struct device_driver *drv)
 static void rmi_function_of_probe(struct rmi_function *fn)
 {
        char of_name[9];
+       struct device_node *node = fn->rmi_dev->xport->dev->of_node;
 
        snprintf(of_name, sizeof(of_name), "rmi4-f%02x",
                fn->fd.function_number);
-       fn->dev.of_node = of_find_node_by_name(
-                               fn->rmi_dev->xport->dev->of_node, of_name);
+       fn->dev.of_node = of_get_child_by_name(node, of_name);
 }
 #else
 static inline void rmi_function_of_probe(struct rmi_function *fn)
index 8dd3fb5..88e9155 100644 (file)
@@ -66,7 +66,7 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
        struct rmi_device *rmi_dev = fn->rmi_dev;
        int ret;
        int offset;
-       u8 buf[14];
+       u8 buf[15];
        int pitch_x = 0;
        int pitch_y = 0;
        int clip_x_low = 0;
@@ -86,9 +86,10 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
 
        offset = rmi_register_desc_calc_reg_offset(&f12->control_reg_desc, 8);
 
-       if (item->reg_size > 14) {
-               dev_err(&fn->dev, "F12 control8 should be 14 bytes, not: %ld\n",
-                       item->reg_size);
+       if (item->reg_size > sizeof(buf)) {
+               dev_err(&fn->dev,
+                       "F12 control8 should be no bigger than %zd bytes, not: %ld\n",
+                       sizeof(buf), item->reg_size);
                return -ENODEV;
        }
 
index 3c3dd78..fed73ee 100644 (file)
@@ -118,6 +118,13 @@ static int ts4800_parse_dt(struct platform_device *pdev,
                return -ENODEV;
        }
 
+       ts->regmap = syscon_node_to_regmap(syscon_np);
+       of_node_put(syscon_np);
+       if (IS_ERR(ts->regmap)) {
+               dev_err(dev, "cannot get parent's regmap\n");
+               return PTR_ERR(ts->regmap);
+       }
+
        error = of_property_read_u32_index(np, "syscon", 1, &reg);
        if (error < 0) {
                dev_err(dev, "no offset in syscon\n");
@@ -134,12 +141,6 @@ static int ts4800_parse_dt(struct platform_device *pdev,
 
        ts->bit = BIT(bit);
 
-       ts->regmap = syscon_node_to_regmap(syscon_np);
-       if (IS_ERR(ts->regmap)) {
-               dev_err(dev, "cannot get parent's regmap\n");
-               return PTR_ERR(ts->regmap);
-       }
-
        return 0;
 }
 
index 7295c19..6fe55d5 100644 (file)
 #include <linux/regmap.h>
 #include "tsc200x-core.h"
 
+static const struct input_id tsc2004_input_id = {
+       .bustype = BUS_I2C,
+       .product = 2004,
+};
+
 static int tsc2004_cmd(struct device *dev, u8 cmd)
 {
        u8 tx = TSC200X_CMD | TSC200X_CMD_12BIT | cmd;
@@ -42,7 +47,7 @@ static int tsc2004_probe(struct i2c_client *i2c,
                         const struct i2c_device_id *id)
 
 {
-       return tsc200x_probe(&i2c->dev, i2c->irq, BUS_I2C,
+       return tsc200x_probe(&i2c->dev, i2c->irq, &tsc2004_input_id,
                             devm_regmap_init_i2c(i2c, &tsc200x_regmap_config),
                             tsc2004_cmd);
 }
index b9f593d..f2c5f0e 100644 (file)
 #include <linux/regmap.h>
 #include "tsc200x-core.h"
 
+static const struct input_id tsc2005_input_id = {
+       .bustype = BUS_SPI,
+       .product = 2005,
+};
+
 static int tsc2005_cmd(struct device *dev, u8 cmd)
 {
        u8 tx = TSC200X_CMD | TSC200X_CMD_12BIT | cmd;
@@ -62,7 +67,7 @@ static int tsc2005_probe(struct spi_device *spi)
        if (error)
                return error;
 
-       return tsc200x_probe(&spi->dev, spi->irq, BUS_SPI,
+       return tsc200x_probe(&spi->dev, spi->irq, &tsc2005_input_id,
                             devm_regmap_init_spi(spi, &tsc200x_regmap_config),
                             tsc2005_cmd);
 }
index 15240c1..dfa7f1c 100644 (file)
@@ -450,7 +450,7 @@ static void tsc200x_close(struct input_dev *input)
        mutex_unlock(&ts->mutex);
 }
 
-int tsc200x_probe(struct device *dev, int irq, __u16 bustype,
+int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
                  struct regmap *regmap,
                  int (*tsc200x_cmd)(struct device *dev, u8 cmd))
 {
@@ -547,9 +547,18 @@ int tsc200x_probe(struct device *dev, int irq, __u16 bustype,
        snprintf(ts->phys, sizeof(ts->phys),
                 "%s/input-ts", dev_name(dev));
 
-       input_dev->name = "TSC200X touchscreen";
+       if (tsc_id->product == 2004) {
+               input_dev->name = "TSC200X touchscreen";
+       } else {
+               input_dev->name = devm_kasprintf(dev, GFP_KERNEL,
+                                                "TSC%04d touchscreen",
+                                                tsc_id->product);
+               if (!input_dev->name)
+                       return -ENOMEM;
+       }
+
        input_dev->phys = ts->phys;
-       input_dev->id.bustype = bustype;
+       input_dev->id = *tsc_id;
        input_dev->dev.parent = dev;
        input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
        input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
index 7a482d1..49a63a3 100644 (file)
@@ -70,7 +70,7 @@
 extern const struct regmap_config tsc200x_regmap_config;
 extern const struct dev_pm_ops tsc200x_pm_ops;
 
-int tsc200x_probe(struct device *dev, int irq, __u16 bustype,
+int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
                  struct regmap *regmap,
                  int (*tsc200x_cmd)(struct device *dev, u8 cmd));
 int tsc200x_remove(struct device *dev);
index 0c9191c..b6fc4bd 100644 (file)
@@ -155,6 +155,7 @@ static void parse_multi_touch(struct w8001 *w8001)
                bool touch = data[0] & (1 << i);
 
                input_mt_slot(dev, i);
+               input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
                if (touch) {
                        x = (data[6 * i + 1] << 7) | data[6 * i + 2];
                        y = (data[6 * i + 3] << 7) | data[6 * i + 4];
@@ -522,6 +523,8 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename,
                                        0, touch.x, 0, 0);
                input_set_abs_params(dev, ABS_MT_POSITION_Y,
                                        0, touch.y, 0, 0);
+               input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
+                                       0, MT_TOOL_MAX, 0, 0);
 
                strlcat(basename, " 2FG", basename_sz);
                if (w8001->max_pen_x && w8001->max_pen_y)
index beb2841..3f1ab49 100644 (file)
@@ -779,11 +779,31 @@ static const struct v4l2_dv_timings_cap adv76xx_timings_cap_digital = {
                        V4L2_DV_BT_CAP_CUSTOM)
 };
 
-static inline const struct v4l2_dv_timings_cap *
-adv76xx_get_dv_timings_cap(struct v4l2_subdev *sd)
+/*
+ * Return the DV timings capabilities for the requested sink pad. As a special
+ * case, pad value -1 returns the capabilities for the currently selected input.
+ */
+static const struct v4l2_dv_timings_cap *
+adv76xx_get_dv_timings_cap(struct v4l2_subdev *sd, int pad)
 {
-       return is_digital_input(sd) ? &adv76xx_timings_cap_digital :
-                                     &adv7604_timings_cap_analog;
+       if (pad == -1) {
+               struct adv76xx_state *state = to_state(sd);
+
+               pad = state->selected_input;
+       }
+
+       switch (pad) {
+       case ADV76XX_PAD_HDMI_PORT_A:
+       case ADV7604_PAD_HDMI_PORT_B:
+       case ADV7604_PAD_HDMI_PORT_C:
+       case ADV7604_PAD_HDMI_PORT_D:
+               return &adv76xx_timings_cap_digital;
+
+       case ADV7604_PAD_VGA_RGB:
+       case ADV7604_PAD_VGA_COMP:
+       default:
+               return &adv7604_timings_cap_analog;
+       }
 }
 
 
@@ -1329,7 +1349,7 @@ static int stdi2dv_timings(struct v4l2_subdev *sd,
                const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt;
 
                if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i],
-                                          adv76xx_get_dv_timings_cap(sd),
+                                          adv76xx_get_dv_timings_cap(sd, -1),
                                           adv76xx_check_dv_timings, NULL))
                        continue;
                if (vtotal(bt) != stdi->lcf + 1)
@@ -1430,18 +1450,22 @@ static int adv76xx_enum_dv_timings(struct v4l2_subdev *sd,
                return -EINVAL;
 
        return v4l2_enum_dv_timings_cap(timings,
-               adv76xx_get_dv_timings_cap(sd), adv76xx_check_dv_timings, NULL);
+               adv76xx_get_dv_timings_cap(sd, timings->pad),
+               adv76xx_check_dv_timings, NULL);
 }
 
 static int adv76xx_dv_timings_cap(struct v4l2_subdev *sd,
                        struct v4l2_dv_timings_cap *cap)
 {
        struct adv76xx_state *state = to_state(sd);
+       unsigned int pad = cap->pad;
 
        if (cap->pad >= state->source_pad)
                return -EINVAL;
 
-       *cap = *adv76xx_get_dv_timings_cap(sd);
+       *cap = *adv76xx_get_dv_timings_cap(sd, pad);
+       cap->pad = pad;
+
        return 0;
 }
 
@@ -1450,9 +1474,9 @@ static int adv76xx_dv_timings_cap(struct v4l2_subdev *sd,
 static void adv76xx_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
                struct v4l2_dv_timings *timings)
 {
-       v4l2_find_dv_timings_cap(timings, adv76xx_get_dv_timings_cap(sd),
-                       is_digital_input(sd) ? 250000 : 1000000,
-                       adv76xx_check_dv_timings, NULL);
+       v4l2_find_dv_timings_cap(timings, adv76xx_get_dv_timings_cap(sd, -1),
+                                is_digital_input(sd) ? 250000 : 1000000,
+                                adv76xx_check_dv_timings, NULL);
 }
 
 static unsigned int adv7604_read_hdmi_pixelclock(struct v4l2_subdev *sd)
@@ -1620,7 +1644,7 @@ static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
 
        bt = &timings->bt;
 
-       if (!v4l2_valid_dv_timings(timings, adv76xx_get_dv_timings_cap(sd),
+       if (!v4l2_valid_dv_timings(timings, adv76xx_get_dv_timings_cap(sd, -1),
                                   adv76xx_check_dv_timings, NULL))
                return -ERANGE;
 
index 87c1293..92d9d42 100644 (file)
@@ -1072,7 +1072,7 @@ static int airspy_probe(struct usb_interface *intf,
        if (ret) {
                dev_err(s->dev, "Failed to register as video device (%d)\n",
                                ret);
-               goto err_unregister_v4l2_dev;
+               goto err_free_controls;
        }
        dev_info(s->dev, "Registered as %s\n",
                        video_device_node_name(&s->vdev));
@@ -1081,7 +1081,6 @@ static int airspy_probe(struct usb_interface *intf,
 
 err_free_controls:
        v4l2_ctrl_handler_free(&s->hdl);
-err_unregister_v4l2_dev:
        v4l2_device_unregister(&s->v4l2_dev);
 err_free_mem:
        kfree(s);
index 28e5be2..528390f 100644 (file)
@@ -2171,7 +2171,7 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
         * The determine_valid_ioctls() call already should ensure
         * that this can never happen, but just in case...
         */
-       if (WARN_ON(!ops->vidioc_cropcap && !ops->vidioc_cropcap))
+       if (WARN_ON(!ops->vidioc_cropcap && !ops->vidioc_g_selection))
                return -ENOTTY;
 
        if (ops->vidioc_cropcap)
index e62fde3..c5472e3 100644 (file)
@@ -355,8 +355,10 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
                goto idata_err;
        }
 
-       if (!idata->buf_bytes)
+       if (!idata->buf_bytes) {
+               idata->buf = NULL;
                return idata;
+       }
 
        idata->buf = kmalloc(idata->buf_bytes, GFP_KERNEL);
        if (!idata->buf) {
@@ -1786,8 +1788,8 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
 
        packed_cmd_hdr = packed->cmd_hdr;
        memset(packed_cmd_hdr, 0, sizeof(packed->cmd_hdr));
-       packed_cmd_hdr[0] = (packed->nr_entries << 16) |
-               (PACKED_CMD_WR << 8) | PACKED_CMD_VER;
+       packed_cmd_hdr[0] = cpu_to_le32((packed->nr_entries << 16) |
+               (PACKED_CMD_WR << 8) | PACKED_CMD_VER);
        hdr_blocks = mmc_large_sector(card) ? 8 : 1;
 
        /*
@@ -1801,14 +1803,14 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
                        ((brq->data.blocks * brq->data.blksz) >=
                         card->ext_csd.data_tag_unit_size);
                /* Argument of CMD23 */
-               packed_cmd_hdr[(i * 2)] =
+               packed_cmd_hdr[(i * 2)] = cpu_to_le32(
                        (do_rel_wr ? MMC_CMD23_ARG_REL_WR : 0) |
                        (do_data_tag ? MMC_CMD23_ARG_TAG_REQ : 0) |
-                       blk_rq_sectors(prq);
+                       blk_rq_sectors(prq));
                /* Argument of CMD18 or CMD25 */
-               packed_cmd_hdr[((i * 2)) + 1] =
+               packed_cmd_hdr[((i * 2)) + 1] = cpu_to_le32(
                        mmc_card_blockaddr(card) ?
-                       blk_rq_pos(prq) : blk_rq_pos(prq) << 9;
+                       blk_rq_pos(prq) : blk_rq_pos(prq) << 9);
                packed->blocks += blk_rq_sectors(prq);
                i++;
        }
index 86fac3e..c763b40 100644 (file)
@@ -789,14 +789,16 @@ static int pxamci_probe(struct platform_device *pdev)
                gpio_direction_output(gpio_power,
                                      host->pdata->gpio_power_invert);
        }
-       if (gpio_is_valid(gpio_ro))
+       if (gpio_is_valid(gpio_ro)) {
                ret = mmc_gpio_request_ro(mmc, gpio_ro);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_ro);
-               goto out;
-       } else {
-               mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
-                       0 : MMC_CAP2_RO_ACTIVE_HIGH;
+               if (ret) {
+                       dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n",
+                               gpio_ro);
+                       goto out;
+               } else {
+                       mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
+                               0 : MMC_CAP2_RO_ACTIVE_HIGH;
+               }
        }
 
        if (gpio_is_valid(gpio_cd))
index 08e1588..a136da8 100644 (file)
@@ -1657,8 +1657,11 @@ static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
 
        /* detect availability of ELM module. Won't be present pre-OMAP4 */
        info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
-       if (!info->elm_of_node)
-               dev_dbg(dev, "ti,elm-id not in DT\n");
+       if (!info->elm_of_node) {
+               info->elm_of_node = of_parse_phandle(child, "elm_id", 0);
+               if (!info->elm_of_node)
+                       dev_dbg(dev, "ti,elm-id not in DT\n");
+       }
 
        /* select ecc-scheme for NAND */
        if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
index db760e8..b8df0f5 100644 (file)
@@ -446,7 +446,11 @@ static int bond_newlink(struct net *src_net, struct net_device *bond_dev,
        if (err < 0)
                return err;
 
-       return register_netdevice(bond_dev);
+       err = register_netdevice(bond_dev);
+
+       netif_carrier_off(bond_dev);
+
+       return err;
 }
 
 static size_t bond_get_size(const struct net_device *bond_dev)
index 30defe6..821d86c 100644 (file)
@@ -3851,7 +3851,7 @@ static void et131x_tx_timeout(struct net_device *netdev)
        unsigned long flags;
 
        /* If the device is closed, ignore the timeout */
-       if (~(adapter->flags & FMP_ADAPTER_INTERRUPT_IN_USE))
+       if (!(adapter->flags & FMP_ADAPTER_INTERRUPT_IN_USE))
                return;
 
        /* Any nonrecoverable hardware error?
index 08a23e6..1a3555d 100644 (file)
@@ -259,6 +259,7 @@ static void nb8800_receive(struct net_device *dev, unsigned int i,
                if (err) {
                        netdev_err(dev, "rx buffer allocation failed\n");
                        dev->stats.rx_dropped++;
+                       dev_kfree_skb(skb);
                        return;
                }
 
index a6333d3..25bbae5 100644 (file)
@@ -231,7 +231,7 @@ err_dma:
        dma_unmap_single(dma_dev, slot->dma_addr, skb_headlen(skb),
                         DMA_TO_DEVICE);
 
-       while (i > 0) {
+       while (i-- > 0) {
                int index = (ring->end + i) % BGMAC_TX_RING_SLOTS;
                struct bgmac_slot_info *slot = &ring->slots[index];
                u32 ctl1 = le32_to_cpu(ring->cpu_base[index].ctl1);
index a38cb04..1b0ae4a 100644 (file)
@@ -1591,7 +1591,7 @@ static int bnxt_get_module_eeprom(struct net_device *dev,
 {
        struct bnxt *bp = netdev_priv(dev);
        u16  start = eeprom->offset, length = eeprom->len;
-       int rc;
+       int rc = 0;
 
        memset(data, 0, eeprom->len);
 
index 8de79ae..0e7e7da 100644 (file)
@@ -2821,7 +2821,7 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
                if (!g) {
                        netif_info(lio, tx_err, lio->netdev,
                                   "Transmit scatter gather: glist null!\n");
-                       goto lio_xmit_failed;
+                       goto lio_xmit_dma_failed;
                }
 
                cmdsetup.s.gather = 1;
@@ -2892,7 +2892,7 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
        else
                status = octnet_send_nic_data_pkt(oct, &ndata, xmit_more);
        if (status == IQ_SEND_FAILED)
-               goto lio_xmit_failed;
+               goto lio_xmit_dma_failed;
 
        netif_info(lio, tx_queued, lio->netdev, "Transmit queued successfully\n");
 
@@ -2906,12 +2906,13 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
 
        return NETDEV_TX_OK;
 
+lio_xmit_dma_failed:
+       dma_unmap_single(&oct->pci_dev->dev, ndata.cmd.dptr,
+                        ndata.datasize, DMA_TO_DEVICE);
 lio_xmit_failed:
        stats->tx_dropped++;
        netif_info(lio, tx_err, lio->netdev, "IQ%d Transmit dropped:%llu\n",
                   iq_no, stats->tx_dropped);
-       dma_unmap_single(&oct->pci_dev->dev, ndata.cmd.dptr,
-                        ndata.datasize, DMA_TO_DEVICE);
        recv_buffer_free(skb);
        return NETDEV_TX_OK;
 }
index 4edb98c..4466a11 100644 (file)
@@ -860,6 +860,11 @@ static netdev_tx_t ethoc_start_xmit(struct sk_buff *skb, struct net_device *dev)
        unsigned int entry;
        void *dest;
 
+       if (skb_put_padto(skb, ETHOC_ZLEN)) {
+               dev->stats.tx_errors++;
+               goto out_no_free;
+       }
+
        if (unlikely(skb->len > ETHOC_BUFSIZ)) {
                dev->stats.tx_errors++;
                goto out;
@@ -894,6 +899,7 @@ static netdev_tx_t ethoc_start_xmit(struct sk_buff *skb, struct net_device *dev)
        skb_tx_timestamp(skb);
 out:
        dev_kfree_skb(skb);
+out_no_free:
        return NETDEV_TX_OK;
 }
 
@@ -1086,7 +1092,7 @@ static int ethoc_probe(struct platform_device *pdev)
        if (!priv->iobase) {
                dev_err(&pdev->dev, "cannot remap I/O memory space\n");
                ret = -ENXIO;
-               goto error;
+               goto free;
        }
 
        if (netdev->mem_end) {
@@ -1095,7 +1101,7 @@ static int ethoc_probe(struct platform_device *pdev)
                if (!priv->membase) {
                        dev_err(&pdev->dev, "cannot remap memory space\n");
                        ret = -ENXIO;
-                       goto error;
+                       goto free;
                }
        } else {
                /* Allocate buffer memory */
@@ -1106,7 +1112,7 @@ static int ethoc_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev, "cannot allocate %dB buffer\n",
                                buffer_size);
                        ret = -ENOMEM;
-                       goto error;
+                       goto free;
                }
                netdev->mem_end = netdev->mem_start + buffer_size;
                priv->dma_alloc = buffer_size;
@@ -1120,7 +1126,7 @@ static int ethoc_probe(struct platform_device *pdev)
                128, (netdev->mem_end - netdev->mem_start + 1) / ETHOC_BUFSIZ);
        if (num_bd < 4) {
                ret = -ENODEV;
-               goto error;
+               goto free;
        }
        priv->num_bd = num_bd;
        /* num_tx must be a power of two */
@@ -1133,7 +1139,7 @@ static int ethoc_probe(struct platform_device *pdev)
        priv->vma = devm_kzalloc(&pdev->dev, num_bd*sizeof(void *), GFP_KERNEL);
        if (!priv->vma) {
                ret = -ENOMEM;
-               goto error;
+               goto free;
        }
 
        /* Allow the platform setup code to pass in a MAC address. */
index 06f0317..9b7a3f5 100644 (file)
@@ -285,6 +285,7 @@ static void nps_enet_hw_reset(struct net_device *ndev)
        ge_rst_value |= NPS_ENET_ENABLE << RST_GMAC_0_SHIFT;
        nps_enet_reg_set(priv, NPS_ENET_REG_GE_RST, ge_rst_value);
        usleep_range(10, 20);
+       ge_rst_value = 0;
        nps_enet_reg_set(priv, NPS_ENET_REG_GE_RST, ge_rst_value);
 
        /* Tx fifo reset sequence */
index ecdb685..88f3c85 100644 (file)
@@ -75,6 +75,7 @@
 #include <linux/uaccess.h>
 #include <asm/firmware.h>
 #include <linux/seq_file.h>
+#include <linux/workqueue.h>
 
 #include "ibmvnic.h"
 
@@ -89,6 +90,7 @@ MODULE_VERSION(IBMVNIC_DRIVER_VERSION);
 static int ibmvnic_version = IBMVNIC_INITIAL_VERSION;
 static int ibmvnic_remove(struct vio_dev *);
 static void release_sub_crqs(struct ibmvnic_adapter *);
+static void release_sub_crqs_no_irqs(struct ibmvnic_adapter *);
 static int ibmvnic_reset_crq(struct ibmvnic_adapter *);
 static int ibmvnic_send_crq_init(struct ibmvnic_adapter *);
 static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *);
@@ -469,7 +471,8 @@ static int ibmvnic_open(struct net_device *netdev)
        crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_UP;
        ibmvnic_send_crq(adapter, &crq);
 
-       netif_start_queue(netdev);
+       netif_tx_start_all_queues(netdev);
+
        return 0;
 
 bounce_map_failed:
@@ -519,7 +522,7 @@ static int ibmvnic_close(struct net_device *netdev)
        for (i = 0; i < adapter->req_rx_queues; i++)
                napi_disable(&adapter->napi[i]);
 
-       netif_stop_queue(netdev);
+       netif_tx_stop_all_queues(netdev);
 
        if (adapter->bounce_buffer) {
                if (!dma_mapping_error(dev, adapter->bounce_buffer_dma)) {
@@ -1212,12 +1215,6 @@ static struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter
                goto reg_failed;
        }
 
-       scrq->irq = irq_create_mapping(NULL, scrq->hw_irq);
-       if (scrq->irq == NO_IRQ) {
-               dev_err(dev, "Error mapping irq\n");
-               goto map_irq_failed;
-       }
-
        scrq->adapter = adapter;
        scrq->size = 4 * PAGE_SIZE / sizeof(*scrq->msgs);
        scrq->cur = 0;
@@ -1230,12 +1227,6 @@ static struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter
 
        return scrq;
 
-map_irq_failed:
-       do {
-               rc = plpar_hcall_norets(H_FREE_SUB_CRQ,
-                                       adapter->vdev->unit_address,
-                                       scrq->crq_num);
-       } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
 reg_failed:
        dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE,
                         DMA_BIDIRECTIONAL);
@@ -1256,6 +1247,7 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter)
                        if (adapter->tx_scrq[i]) {
                                free_irq(adapter->tx_scrq[i]->irq,
                                         adapter->tx_scrq[i]);
+                               irq_dispose_mapping(adapter->tx_scrq[i]->irq);
                                release_sub_crq_queue(adapter,
                                                      adapter->tx_scrq[i]);
                        }
@@ -1267,6 +1259,7 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter)
                        if (adapter->rx_scrq[i]) {
                                free_irq(adapter->rx_scrq[i]->irq,
                                         adapter->rx_scrq[i]);
+                               irq_dispose_mapping(adapter->rx_scrq[i]->irq);
                                release_sub_crq_queue(adapter,
                                                      adapter->rx_scrq[i]);
                        }
@@ -1276,6 +1269,29 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter)
        adapter->requested_caps = 0;
 }
 
+static void release_sub_crqs_no_irqs(struct ibmvnic_adapter *adapter)
+{
+       int i;
+
+       if (adapter->tx_scrq) {
+               for (i = 0; i < adapter->req_tx_queues; i++)
+                       if (adapter->tx_scrq[i])
+                               release_sub_crq_queue(adapter,
+                                                     adapter->tx_scrq[i]);
+               adapter->tx_scrq = NULL;
+       }
+
+       if (adapter->rx_scrq) {
+               for (i = 0; i < adapter->req_rx_queues; i++)
+                       if (adapter->rx_scrq[i])
+                               release_sub_crq_queue(adapter,
+                                                     adapter->rx_scrq[i]);
+               adapter->rx_scrq = NULL;
+       }
+
+       adapter->requested_caps = 0;
+}
+
 static int disable_scrq_irq(struct ibmvnic_adapter *adapter,
                            struct ibmvnic_sub_crq_queue *scrq)
 {
@@ -1395,6 +1411,66 @@ static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance)
        return IRQ_HANDLED;
 }
 
+static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter)
+{
+       struct device *dev = &adapter->vdev->dev;
+       struct ibmvnic_sub_crq_queue *scrq;
+       int i = 0, j = 0;
+       int rc = 0;
+
+       for (i = 0; i < adapter->req_tx_queues; i++) {
+               scrq = adapter->tx_scrq[i];
+               scrq->irq = irq_create_mapping(NULL, scrq->hw_irq);
+
+               if (scrq->irq == NO_IRQ) {
+                       rc = -EINVAL;
+                       dev_err(dev, "Error mapping irq\n");
+                       goto req_tx_irq_failed;
+               }
+
+               rc = request_irq(scrq->irq, ibmvnic_interrupt_tx,
+                                0, "ibmvnic_tx", scrq);
+
+               if (rc) {
+                       dev_err(dev, "Couldn't register tx irq 0x%x. rc=%d\n",
+                               scrq->irq, rc);
+                       irq_dispose_mapping(scrq->irq);
+                       goto req_rx_irq_failed;
+               }
+       }
+
+       for (i = 0; i < adapter->req_rx_queues; i++) {
+               scrq = adapter->rx_scrq[i];
+               scrq->irq = irq_create_mapping(NULL, scrq->hw_irq);
+               if (scrq->irq == NO_IRQ) {
+                       rc = -EINVAL;
+                       dev_err(dev, "Error mapping irq\n");
+                       goto req_rx_irq_failed;
+               }
+               rc = request_irq(scrq->irq, ibmvnic_interrupt_rx,
+                                0, "ibmvnic_rx", scrq);
+               if (rc) {
+                       dev_err(dev, "Couldn't register rx irq 0x%x. rc=%d\n",
+                               scrq->irq, rc);
+                       irq_dispose_mapping(scrq->irq);
+                       goto req_rx_irq_failed;
+               }
+       }
+       return rc;
+
+req_rx_irq_failed:
+       for (j = 0; j < i; j++)
+               free_irq(adapter->rx_scrq[j]->irq, adapter->rx_scrq[j]);
+               irq_dispose_mapping(adapter->rx_scrq[j]->irq);
+       i = adapter->req_tx_queues;
+req_tx_irq_failed:
+       for (j = 0; j < i; j++)
+               free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]);
+               irq_dispose_mapping(adapter->rx_scrq[j]->irq);
+       release_sub_crqs_no_irqs(adapter);
+       return rc;
+}
+
 static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
 {
        struct device *dev = &adapter->vdev->dev;
@@ -1403,8 +1479,7 @@ static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
        union ibmvnic_crq crq;
        int total_queues;
        int more = 0;
-       int i, j;
-       int rc;
+       int i;
 
        if (!retry) {
                /* Sub-CRQ entries are 32 byte long */
@@ -1483,13 +1558,6 @@ static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
        for (i = 0; i < adapter->req_tx_queues; i++) {
                adapter->tx_scrq[i] = allqueues[i];
                adapter->tx_scrq[i]->pool_index = i;
-               rc = request_irq(adapter->tx_scrq[i]->irq, ibmvnic_interrupt_tx,
-                                0, "ibmvnic_tx", adapter->tx_scrq[i]);
-               if (rc) {
-                       dev_err(dev, "Couldn't register tx irq 0x%x. rc=%d\n",
-                               adapter->tx_scrq[i]->irq, rc);
-                       goto req_tx_irq_failed;
-               }
        }
 
        adapter->rx_scrq = kcalloc(adapter->req_rx_queues,
@@ -1500,13 +1568,6 @@ static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
        for (i = 0; i < adapter->req_rx_queues; i++) {
                adapter->rx_scrq[i] = allqueues[i + adapter->req_tx_queues];
                adapter->rx_scrq[i]->scrq_num = i;
-               rc = request_irq(adapter->rx_scrq[i]->irq, ibmvnic_interrupt_rx,
-                                0, "ibmvnic_rx", adapter->rx_scrq[i]);
-               if (rc) {
-                       dev_err(dev, "Couldn't register rx irq 0x%x. rc=%d\n",
-                               adapter->rx_scrq[i]->irq, rc);
-                       goto req_rx_irq_failed;
-               }
        }
 
        memset(&crq, 0, sizeof(crq));
@@ -1559,15 +1620,6 @@ static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
 
        return;
 
-req_rx_irq_failed:
-       for (j = 0; j < i; j++)
-               free_irq(adapter->rx_scrq[j]->irq, adapter->rx_scrq[j]);
-       i = adapter->req_tx_queues;
-req_tx_irq_failed:
-       for (j = 0; j < i; j++)
-               free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]);
-       kfree(adapter->rx_scrq);
-       adapter->rx_scrq = NULL;
 rx_failed:
        kfree(adapter->tx_scrq);
        adapter->tx_scrq = NULL;
@@ -2348,9 +2400,9 @@ static void handle_request_cap_rsp(union ibmvnic_crq *crq,
                         *req_value,
                         (long int)be32_to_cpu(crq->request_capability_rsp.
                                               number), name);
-               release_sub_crqs(adapter);
+               release_sub_crqs_no_irqs(adapter);
                *req_value = be32_to_cpu(crq->request_capability_rsp.number);
-               complete(&adapter->init_done);
+               init_sub_crqs(adapter, 1);
                return;
        default:
                dev_err(dev, "Error %d in request cap rsp\n",
@@ -2659,7 +2711,7 @@ static void handle_query_cap_rsp(union ibmvnic_crq *crq,
 
 out:
        if (atomic_read(&adapter->running_cap_queries) == 0)
-               complete(&adapter->init_done);
+               init_sub_crqs(adapter, 0);
                /* We're done querying the capabilities, initialize sub-crqs */
 }
 
@@ -3202,8 +3254,8 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq,
                        dev_info(dev, "Partner initialized\n");
                        /* Send back a response */
                        rc = ibmvnic_send_crq_init_complete(adapter);
-                       if (rc == 0)
-                               send_version_xchg(adapter);
+                       if (!rc)
+                               schedule_work(&adapter->vnic_crq_init);
                        else
                                dev_err(dev, "Can't send initrsp rc=%ld\n", rc);
                        break;
@@ -3555,8 +3607,63 @@ static const struct file_operations ibmvnic_dump_ops = {
        .release        = single_release,
 };
 
+static void handle_crq_init_rsp(struct work_struct *work)
+{
+       struct ibmvnic_adapter *adapter = container_of(work,
+                                                      struct ibmvnic_adapter,
+                                                      vnic_crq_init);
+       struct device *dev = &adapter->vdev->dev;
+       struct net_device *netdev = adapter->netdev;
+       unsigned long timeout = msecs_to_jiffies(30000);
+       int rc;
+
+       send_version_xchg(adapter);
+       reinit_completion(&adapter->init_done);
+       if (!wait_for_completion_timeout(&adapter->init_done, timeout)) {
+               dev_err(dev, "Passive init timeout\n");
+               goto task_failed;
+       }
+
+       do {
+               if (adapter->renegotiate) {
+                       adapter->renegotiate = false;
+                       release_sub_crqs_no_irqs(adapter);
+                       send_cap_queries(adapter);
+
+                       reinit_completion(&adapter->init_done);
+                       if (!wait_for_completion_timeout(&adapter->init_done,
+                                                        timeout)) {
+                               dev_err(dev, "Passive init timeout\n");
+                               goto task_failed;
+                       }
+               }
+       } while (adapter->renegotiate);
+       rc = init_sub_crq_irqs(adapter);
+
+       if (rc)
+               goto task_failed;
+
+       netdev->real_num_tx_queues = adapter->req_tx_queues;
+
+       rc = register_netdev(netdev);
+       if (rc) {
+               dev_err(dev,
+                       "failed to register netdev rc=%d\n", rc);
+               goto register_failed;
+       }
+       dev_info(dev, "ibmvnic registered\n");
+
+       return;
+
+register_failed:
+       release_sub_crqs(adapter);
+task_failed:
+       dev_err(dev, "Passive initialization was not successful\n");
+}
+
 static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
 {
+       unsigned long timeout = msecs_to_jiffies(30000);
        struct ibmvnic_adapter *adapter;
        struct net_device *netdev;
        unsigned char *mac_addr_p;
@@ -3593,6 +3700,8 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
        netdev->ethtool_ops = &ibmvnic_ethtool_ops;
        SET_NETDEV_DEV(netdev, &dev->dev);
 
+       INIT_WORK(&adapter->vnic_crq_init, handle_crq_init_rsp);
+
        spin_lock_init(&adapter->stats_lock);
 
        rc = ibmvnic_init_crq_queue(adapter);
@@ -3635,30 +3744,26 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
        ibmvnic_send_crq_init(adapter);
 
        init_completion(&adapter->init_done);
-       wait_for_completion(&adapter->init_done);
+       if (!wait_for_completion_timeout(&adapter->init_done, timeout))
+               return 0;
 
        do {
-               adapter->renegotiate = false;
-
-               init_sub_crqs(adapter, 0);
-               reinit_completion(&adapter->init_done);
-               wait_for_completion(&adapter->init_done);
-
                if (adapter->renegotiate) {
-                       release_sub_crqs(adapter);
+                       adapter->renegotiate = false;
+                       release_sub_crqs_no_irqs(adapter);
                        send_cap_queries(adapter);
 
                        reinit_completion(&adapter->init_done);
-                       wait_for_completion(&adapter->init_done);
+                       if (!wait_for_completion_timeout(&adapter->init_done,
+                                                        timeout))
+                               return 0;
                }
        } while (adapter->renegotiate);
 
-       /* if init_sub_crqs is partially successful, retry */
-       while (!adapter->tx_scrq || !adapter->rx_scrq) {
-               init_sub_crqs(adapter, 1);
-
-               reinit_completion(&adapter->init_done);
-               wait_for_completion(&adapter->init_done);
+       rc = init_sub_crq_irqs(adapter);
+       if (rc) {
+               dev_err(&dev->dev, "failed to initialize sub crq irqs\n");
+               goto free_debugfs;
        }
 
        netdev->real_num_tx_queues = adapter->req_tx_queues;
@@ -3666,12 +3771,14 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
        rc = register_netdev(netdev);
        if (rc) {
                dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc);
-               goto free_debugfs;
+               goto free_sub_crqs;
        }
        dev_info(&dev->dev, "ibmvnic registered\n");
 
        return 0;
 
+free_sub_crqs:
+       release_sub_crqs(adapter);
 free_debugfs:
        if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir))
                debugfs_remove_recursive(adapter->debugfs_dir);
index 0b66a50..e82898f 100644 (file)
@@ -1045,4 +1045,6 @@ struct ibmvnic_adapter {
        u64 opt_rxba_entries_per_subcrq;
        __be64 tx_rx_desc_req;
        u8 map_id;
+
+       struct work_struct vnic_crq_init;
 };
index 5ea2200..501f15d 100644 (file)
@@ -1344,6 +1344,13 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
        if (!vsi || !macaddr)
                return NULL;
 
+       /* Do not allow broadcast filter to be added since broadcast filter
+        * is added as part of add VSI for any newly created VSI except
+        * FDIR VSI
+        */
+       if (is_broadcast_ether_addr(macaddr))
+               return NULL;
+
        f = i40e_find_filter(vsi, macaddr, vlan, is_vf, is_netdev);
        if (!f) {
                f = kzalloc(sizeof(*f), GFP_ATOMIC);
@@ -2151,18 +2158,6 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                                         aq_ret, pf->hw.aq.asq_last_status);
                        }
                }
-               aq_ret = i40e_aq_set_vsi_broadcast(&vsi->back->hw,
-                                                  vsi->seid,
-                                                  cur_promisc, NULL);
-               if (aq_ret) {
-                       retval = i40e_aq_rc_to_posix(aq_ret,
-                                                    pf->hw.aq.asq_last_status);
-                       dev_info(&pf->pdev->dev,
-                                "set brdcast promisc failed, err %s, aq_err %s\n",
-                                i40e_stat_str(&pf->hw, aq_ret),
-                                i40e_aq_str(&pf->hw,
-                                            pf->hw.aq.asq_last_status));
-               }
        }
 out:
        /* if something went wrong then set the changed flag so we try again */
@@ -7726,10 +7721,11 @@ static int i40e_init_msix(struct i40e_pf *pf)
  * i40e_vsi_alloc_q_vector - Allocate memory for a single interrupt vector
  * @vsi: the VSI being configured
  * @v_idx: index of the vector in the vsi struct
+ * @cpu: cpu to be used on affinity_mask
  *
  * We allocate one q_vector.  If allocation fails we return -ENOMEM.
  **/
-static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
+static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx, int cpu)
 {
        struct i40e_q_vector *q_vector;
 
@@ -7740,7 +7736,8 @@ static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
 
        q_vector->vsi = vsi;
        q_vector->v_idx = v_idx;
-       cpumask_set_cpu(v_idx, &q_vector->affinity_mask);
+       cpumask_set_cpu(cpu, &q_vector->affinity_mask);
+
        if (vsi->netdev)
                netif_napi_add(vsi->netdev, &q_vector->napi,
                               i40e_napi_poll, NAPI_POLL_WEIGHT);
@@ -7764,8 +7761,7 @@ static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
 static int i40e_vsi_alloc_q_vectors(struct i40e_vsi *vsi)
 {
        struct i40e_pf *pf = vsi->back;
-       int v_idx, num_q_vectors;
-       int err;
+       int err, v_idx, num_q_vectors, current_cpu;
 
        /* if not MSIX, give the one vector only to the LAN VSI */
        if (pf->flags & I40E_FLAG_MSIX_ENABLED)
@@ -7775,10 +7771,15 @@ static int i40e_vsi_alloc_q_vectors(struct i40e_vsi *vsi)
        else
                return -EINVAL;
 
+       current_cpu = cpumask_first(cpu_online_mask);
+
        for (v_idx = 0; v_idx < num_q_vectors; v_idx++) {
-               err = i40e_vsi_alloc_q_vector(vsi, v_idx);
+               err = i40e_vsi_alloc_q_vector(vsi, v_idx, current_cpu);
                if (err)
                        goto err_out;
+               current_cpu = cpumask_next(current_cpu, cpu_online_mask);
+               if (unlikely(current_cpu >= nr_cpu_ids))
+                       current_cpu = cpumask_first(cpu_online_mask);
        }
 
        return 0;
@@ -9224,6 +9225,7 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi)
 static int i40e_add_vsi(struct i40e_vsi *vsi)
 {
        int ret = -ENODEV;
+       i40e_status aq_ret = 0;
        u8 laa_macaddr[ETH_ALEN];
        bool found_laa_mac_filter = false;
        struct i40e_pf *pf = vsi->back;
@@ -9413,6 +9415,18 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
                vsi->seid = ctxt.seid;
                vsi->id = ctxt.vsi_number;
        }
+       /* Except FDIR VSI, for all othet VSI set the broadcast filter */
+       if (vsi->type != I40E_VSI_FDIR) {
+               aq_ret = i40e_aq_set_vsi_broadcast(hw, vsi->seid, true, NULL);
+               if (aq_ret) {
+                       ret = i40e_aq_rc_to_posix(aq_ret,
+                                                 hw->aq.asq_last_status);
+                       dev_info(&pf->pdev->dev,
+                                "set brdcast promisc failed, err %s, aq_err %s\n",
+                                i40e_stat_str(hw, aq_ret),
+                                i40e_aq_str(hw, hw->aq.asq_last_status));
+               }
+       }
 
        spin_lock_bh(&vsi->mac_filter_list_lock);
        /* If macvlan filters already exist, force them to get loaded */
index 55f151f..a8868e1 100644 (file)
@@ -1280,8 +1280,8 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                    union i40e_rx_desc *rx_desc)
 {
        struct i40e_rx_ptype_decoded decoded;
-       bool ipv4, ipv6, tunnel = false;
        u32 rx_error, rx_status;
+       bool ipv4, ipv6;
        u8 ptype;
        u64 qword;
 
@@ -1336,19 +1336,23 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
        if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT))
                return;
 
-       /* The hardware supported by this driver does not validate outer
-        * checksums for tunneled VXLAN or GENEVE frames.  I don't agree
-        * with it but the specification states that you "MAY validate", it
-        * doesn't make it a hard requirement so if we have validated the
-        * inner checksum report CHECKSUM_UNNECESSARY.
+       /* If there is an outer header present that might contain a checksum
+        * we need to bump the checksum level by 1 to reflect the fact that
+        * we are indicating we validated the inner checksum.
         */
-       if (decoded.inner_prot & (I40E_RX_PTYPE_INNER_PROT_TCP |
-                                 I40E_RX_PTYPE_INNER_PROT_UDP |
-                                 I40E_RX_PTYPE_INNER_PROT_SCTP))
-               tunnel = true;
-
-       skb->ip_summed = CHECKSUM_UNNECESSARY;
-       skb->csum_level = tunnel ? 1 : 0;
+       if (decoded.tunnel_type >= I40E_RX_PTYPE_TUNNEL_IP_GRENAT)
+               skb->csum_level = 1;
+
+       /* Only report checksum unnecessary for TCP, UDP, or SCTP */
+       switch (decoded.inner_prot) {
+       case I40E_RX_PTYPE_INNER_PROT_TCP:
+       case I40E_RX_PTYPE_INNER_PROT_UDP:
+       case I40E_RX_PTYPE_INNER_PROT_SCTP:
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               /* fall though */
+       default:
+               break;
+       }
 
        return;
 
index be99189..79d99cd 100644 (file)
@@ -752,8 +752,8 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                    union i40e_rx_desc *rx_desc)
 {
        struct i40e_rx_ptype_decoded decoded;
-       bool ipv4, ipv6, tunnel = false;
        u32 rx_error, rx_status;
+       bool ipv4, ipv6;
        u8 ptype;
        u64 qword;
 
@@ -808,19 +808,23 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
        if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT))
                return;
 
-       /* The hardware supported by this driver does not validate outer
-        * checksums for tunneled VXLAN or GENEVE frames.  I don't agree
-        * with it but the specification states that you "MAY validate", it
-        * doesn't make it a hard requirement so if we have validated the
-        * inner checksum report CHECKSUM_UNNECESSARY.
+       /* If there is an outer header present that might contain a checksum
+        * we need to bump the checksum level by 1 to reflect the fact that
+        * we are indicating we validated the inner checksum.
         */
-       if (decoded.inner_prot & (I40E_RX_PTYPE_INNER_PROT_TCP |
-                                 I40E_RX_PTYPE_INNER_PROT_UDP |
-                                 I40E_RX_PTYPE_INNER_PROT_SCTP))
-               tunnel = true;
-
-       skb->ip_summed = CHECKSUM_UNNECESSARY;
-       skb->csum_level = tunnel ? 1 : 0;
+       if (decoded.tunnel_type >= I40E_RX_PTYPE_TUNNEL_IP_GRENAT)
+               skb->csum_level = 1;
+
+       /* Only report checksum unnecessary for TCP, UDP, or SCTP */
+       switch (decoded.inner_prot) {
+       case I40E_RX_PTYPE_INNER_PROT_TCP:
+       case I40E_RX_PTYPE_INNER_PROT_UDP:
+       case I40E_RX_PTYPE_INNER_PROT_SCTP:
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               /* fall though */
+       default:
+               break;
+       }
 
        return;
 
index 088c47c..8bebd86 100644 (file)
@@ -2887,7 +2887,7 @@ int ixgbe_poll(struct napi_struct *napi, int budget)
        if (!test_bit(__IXGBE_DOWN, &adapter->state))
                ixgbe_irq_enable_queues(adapter, BIT_ULL(q_vector->v_idx));
 
-       return 0;
+       return min(work_done, budget - 1);
 }
 
 /**
index d5d263b..f92018b 100644 (file)
 /* Various constants */
 
 /* Coalescing */
-#define MVNETA_TXDONE_COAL_PKTS                1
+#define MVNETA_TXDONE_COAL_PKTS                0       /* interrupt per packet */
 #define MVNETA_RX_COAL_PKTS            32
 #define MVNETA_RX_COAL_USEC            100
 
index fc95aff..44cf16d 100644 (file)
@@ -1042,6 +1042,8 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_port_profile new_prof;
+       struct mlx4_en_priv *tmp;
        u32 rx_size, tx_size;
        int port_up = 0;
        int err = 0;
@@ -1061,22 +1063,25 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
            tx_size == priv->tx_ring[0]->size)
                return 0;
 
+       tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+
        mutex_lock(&mdev->state_lock);
+       memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
+       new_prof.tx_ring_size = tx_size;
+       new_prof.rx_ring_size = rx_size;
+       err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof);
+       if (err)
+               goto out;
+
        if (priv->port_up) {
                port_up = 1;
                mlx4_en_stop_port(dev, 1);
        }
 
-       mlx4_en_free_resources(priv);
-
-       priv->prof->tx_ring_size = tx_size;
-       priv->prof->rx_ring_size = rx_size;
+       mlx4_en_safe_replace_resources(priv, tmp);
 
-       err = mlx4_en_alloc_resources(priv);
-       if (err) {
-               en_err(priv, "Failed reallocating port resources\n");
-               goto out;
-       }
        if (port_up) {
                err = mlx4_en_start_port(dev);
                if (err)
@@ -1084,8 +1089,8 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
        }
 
        err = mlx4_en_moderation_update(priv);
-
 out:
+       kfree(tmp);
        mutex_unlock(&mdev->state_lock);
        return err;
 }
@@ -1714,6 +1719,8 @@ static int mlx4_en_set_channels(struct net_device *dev,
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_port_profile new_prof;
+       struct mlx4_en_priv *tmp;
        int port_up = 0;
        int err = 0;
 
@@ -1723,23 +1730,26 @@ static int mlx4_en_set_channels(struct net_device *dev,
            !channel->tx_count || !channel->rx_count)
                return -EINVAL;
 
+       tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+
        mutex_lock(&mdev->state_lock);
+       memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
+       new_prof.num_tx_rings_p_up = channel->tx_count;
+       new_prof.tx_ring_num = channel->tx_count * MLX4_EN_NUM_UP;
+       new_prof.rx_ring_num = channel->rx_count;
+
+       err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof);
+       if (err)
+               goto out;
+
        if (priv->port_up) {
                port_up = 1;
                mlx4_en_stop_port(dev, 1);
        }
 
-       mlx4_en_free_resources(priv);
-
-       priv->num_tx_rings_p_up = channel->tx_count;
-       priv->tx_ring_num = channel->tx_count * MLX4_EN_NUM_UP;
-       priv->rx_ring_num = channel->rx_count;
-
-       err = mlx4_en_alloc_resources(priv);
-       if (err) {
-               en_err(priv, "Failed reallocating port resources\n");
-               goto out;
-       }
+       mlx4_en_safe_replace_resources(priv, tmp);
 
        netif_set_real_num_tx_queues(dev, priv->tx_ring_num);
        netif_set_real_num_rx_queues(dev, priv->rx_ring_num);
@@ -1757,8 +1767,8 @@ static int mlx4_en_set_channels(struct net_device *dev,
        }
 
        err = mlx4_en_moderation_update(priv);
-
 out:
+       kfree(tmp);
        mutex_unlock(&mdev->state_lock);
        return err;
 }
index 0c0dfd6..8359e9e 100644 (file)
@@ -1954,7 +1954,7 @@ static int mlx4_en_close(struct net_device *dev)
        return 0;
 }
 
-void mlx4_en_free_resources(struct mlx4_en_priv *priv)
+static void mlx4_en_free_resources(struct mlx4_en_priv *priv)
 {
        int i;
 
@@ -1979,7 +1979,7 @@ void mlx4_en_free_resources(struct mlx4_en_priv *priv)
 
 }
 
-int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
+static int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
 {
        struct mlx4_en_port_profile *prof = priv->prof;
        int i;
@@ -2044,6 +2044,77 @@ static void mlx4_en_shutdown(struct net_device *dev)
        rtnl_unlock();
 }
 
+static int mlx4_en_copy_priv(struct mlx4_en_priv *dst,
+                            struct mlx4_en_priv *src,
+                            struct mlx4_en_port_profile *prof)
+{
+       memcpy(&dst->hwtstamp_config, &prof->hwtstamp_config,
+              sizeof(dst->hwtstamp_config));
+       dst->num_tx_rings_p_up = src->mdev->profile.num_tx_rings_p_up;
+       dst->tx_ring_num = prof->tx_ring_num;
+       dst->rx_ring_num = prof->rx_ring_num;
+       dst->flags = prof->flags;
+       dst->mdev = src->mdev;
+       dst->port = src->port;
+       dst->dev = src->dev;
+       dst->prof = prof;
+       dst->stride = roundup_pow_of_two(sizeof(struct mlx4_en_rx_desc) +
+                                        DS_SIZE * MLX4_EN_MAX_RX_FRAGS);
+
+       dst->tx_ring = kzalloc(sizeof(struct mlx4_en_tx_ring *) * MAX_TX_RINGS,
+                               GFP_KERNEL);
+       if (!dst->tx_ring)
+               return -ENOMEM;
+
+       dst->tx_cq = kzalloc(sizeof(struct mlx4_en_cq *) * MAX_TX_RINGS,
+                             GFP_KERNEL);
+       if (!dst->tx_cq) {
+               kfree(dst->tx_ring);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static void mlx4_en_update_priv(struct mlx4_en_priv *dst,
+                               struct mlx4_en_priv *src)
+{
+       memcpy(dst->rx_ring, src->rx_ring,
+              sizeof(struct mlx4_en_rx_ring *) * src->rx_ring_num);
+       memcpy(dst->rx_cq, src->rx_cq,
+              sizeof(struct mlx4_en_cq *) * src->rx_ring_num);
+       memcpy(&dst->hwtstamp_config, &src->hwtstamp_config,
+              sizeof(dst->hwtstamp_config));
+       dst->tx_ring_num = src->tx_ring_num;
+       dst->rx_ring_num = src->rx_ring_num;
+       dst->tx_ring = src->tx_ring;
+       dst->tx_cq = src->tx_cq;
+       memcpy(dst->prof, src->prof, sizeof(struct mlx4_en_port_profile));
+}
+
+int mlx4_en_try_alloc_resources(struct mlx4_en_priv *priv,
+                               struct mlx4_en_priv *tmp,
+                               struct mlx4_en_port_profile *prof)
+{
+       mlx4_en_copy_priv(tmp, priv, prof);
+
+       if (mlx4_en_alloc_resources(tmp)) {
+               en_warn(priv,
+                       "%s: Resource allocation failed, using previous configuration\n",
+                       __func__);
+               kfree(tmp->tx_ring);
+               kfree(tmp->tx_cq);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+void mlx4_en_safe_replace_resources(struct mlx4_en_priv *priv,
+                                   struct mlx4_en_priv *tmp)
+{
+       mlx4_en_free_resources(priv);
+       mlx4_en_update_priv(priv, tmp);
+}
+
 void mlx4_en_destroy_netdev(struct net_device *dev)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
@@ -2080,6 +2151,10 @@ void mlx4_en_destroy_netdev(struct net_device *dev)
        mdev->upper[priv->port] = NULL;
        mutex_unlock(&mdev->state_lock);
 
+#ifdef CONFIG_RFS_ACCEL
+       mlx4_en_cleanup_filters(priv);
+#endif
+
        mlx4_en_free_resources(priv);
 
        kfree(priv->tx_ring);
@@ -3124,6 +3199,8 @@ int mlx4_en_reset_config(struct net_device *dev,
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_port_profile new_prof;
+       struct mlx4_en_priv *tmp;
        int port_up = 0;
        int err = 0;
 
@@ -3140,19 +3217,29 @@ int mlx4_en_reset_config(struct net_device *dev,
                return -EINVAL;
        }
 
+       tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+
        mutex_lock(&mdev->state_lock);
+
+       memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
+       memcpy(&new_prof.hwtstamp_config, &ts_config, sizeof(ts_config));
+
+       err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof);
+       if (err)
+               goto out;
+
        if (priv->port_up) {
                port_up = 1;
                mlx4_en_stop_port(dev, 1);
        }
 
-       mlx4_en_free_resources(priv);
-
        en_warn(priv, "Changing device configuration rx filter(%x) rx vlan(%x)\n",
-               ts_config.rx_filter, !!(features & NETIF_F_HW_VLAN_CTAG_RX));
+               ts_config.rx_filter,
+               !!(features & NETIF_F_HW_VLAN_CTAG_RX));
 
-       priv->hwtstamp_config.tx_type = ts_config.tx_type;
-       priv->hwtstamp_config.rx_filter = ts_config.rx_filter;
+       mlx4_en_safe_replace_resources(priv, tmp);
 
        if (DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX)) {
                if (features & NETIF_F_HW_VLAN_CTAG_RX)
@@ -3186,11 +3273,6 @@ int mlx4_en_reset_config(struct net_device *dev,
                dev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
        }
 
-       err = mlx4_en_alloc_resources(priv);
-       if (err) {
-               en_err(priv, "Failed reallocating port resources\n");
-               goto out;
-       }
        if (port_up) {
                err = mlx4_en_start_port(dev);
                if (err)
@@ -3199,6 +3281,8 @@ int mlx4_en_reset_config(struct net_device *dev,
 
 out:
        mutex_unlock(&mdev->state_lock);
-       netdev_features_change(dev);
+       kfree(tmp);
+       if (!err)
+               netdev_features_change(dev);
        return err;
 }
index c1b3a9c..99b5407 100644 (file)
@@ -514,9 +514,6 @@ void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv,
        ring->rx_info = NULL;
        kfree(ring);
        *pring = NULL;
-#ifdef CONFIG_RFS_ACCEL
-       mlx4_en_cleanup_filters(priv);
-#endif
 }
 
 void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv,
index 467d47e..13d297e 100644 (file)
@@ -353,12 +353,14 @@ struct mlx4_en_port_profile {
        u32 rx_ring_num;
        u32 tx_ring_size;
        u32 rx_ring_size;
+       u8 num_tx_rings_p_up;
        u8 rx_pause;
        u8 rx_ppp;
        u8 tx_pause;
        u8 tx_ppp;
        int rss_rings;
        int inline_thold;
+       struct hwtstamp_config hwtstamp_config;
 };
 
 struct mlx4_en_profile {
@@ -623,8 +625,11 @@ void mlx4_en_set_stats_bitmap(struct mlx4_dev *dev,
                              u8 rx_ppp, u8 rx_pause,
                              u8 tx_ppp, u8 tx_pause);
 
-void mlx4_en_free_resources(struct mlx4_en_priv *priv);
-int mlx4_en_alloc_resources(struct mlx4_en_priv *priv);
+int mlx4_en_try_alloc_resources(struct mlx4_en_priv *priv,
+                               struct mlx4_en_priv *tmp,
+                               struct mlx4_en_port_profile *prof);
+void mlx4_en_safe_replace_resources(struct mlx4_en_priv *priv,
+                                   struct mlx4_en_priv *tmp);
 
 int mlx4_en_create_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq,
                      int entries, int ring, enum cq_type mode, int node);
index 7a0dca2..5a4d88c 100644 (file)
@@ -1348,6 +1348,11 @@ static int mlx5e_open_channels(struct mlx5e_priv *priv)
                        goto err_close_channels;
        }
 
+       /* FIXME: This is a W/A for tx timeout watch dog false alarm when
+        * polling for inactive tx queues.
+        */
+       netif_tx_start_all_queues(priv->netdev);
+
        kfree(cparam);
        return 0;
 
@@ -1367,6 +1372,12 @@ static void mlx5e_close_channels(struct mlx5e_priv *priv)
 {
        int i;
 
+       /* FIXME: This is a W/A only for tx timeout watch dog false alarm when
+        * polling for inactive tx queues.
+        */
+       netif_tx_stop_all_queues(priv->netdev);
+       netif_tx_disable(priv->netdev);
+
        for (i = 0; i < priv->params.num_channels; i++)
                mlx5e_close_channel(priv->channel[i]);
 
@@ -2656,7 +2667,7 @@ static void mlx5e_tx_timeout(struct net_device *dev)
        for (i = 0; i < priv->params.num_channels * priv->params.num_tc; i++) {
                struct mlx5e_sq *sq = priv->txq_to_sq_map[i];
 
-               if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, i)))
+               if (!netif_xmit_stopped(netdev_get_tx_queue(dev, i)))
                        continue;
                sched_work = true;
                set_bit(MLX5E_SQ_STATE_TX_TIMEOUT, &sq->state);
index 05de772..e25a73e 100644 (file)
@@ -72,8 +72,8 @@ static int mlx5e_vxlan_core_del_port_cmd(struct mlx5_core_dev *mdev, u16 port)
        u32 in[MLX5_ST_SZ_DW(delete_vxlan_udp_dport_in)];
        u32 out[MLX5_ST_SZ_DW(delete_vxlan_udp_dport_out)];
 
-       memset(&in, 0, sizeof(in));
-       memset(&out, 0, sizeof(out));
+       memset(in, 0, sizeof(in));
+       memset(out, 0, sizeof(out));
 
        MLX5_SET(delete_vxlan_udp_dport_in, in, opcode,
                 MLX5_CMD_OP_DELETE_VXLAN_UDP_DPORT);
index 1977e7a..57d48da 100644 (file)
@@ -2718,7 +2718,7 @@ static inline void mlxsw_reg_ppcnt_pack(char *payload, u8 local_port,
  * Configures the switch priority to buffer table.
  */
 #define MLXSW_REG_PPTB_ID 0x500B
-#define MLXSW_REG_PPTB_LEN 0x0C
+#define MLXSW_REG_PPTB_LEN 0x10
 
 static const struct mlxsw_reg_info mlxsw_reg_pptb = {
        .id = MLXSW_REG_PPTB_ID,
@@ -2784,6 +2784,13 @@ MLXSW_ITEM32(reg, pptb, pm_msb, 0x08, 24, 8);
  */
 MLXSW_ITEM32(reg, pptb, untagged_buff, 0x08, 0, 4);
 
+/* reg_pptb_prio_to_buff_msb
+ * Mapping of switch priority <i+8> to one of the allocated receive port
+ * buffers.
+ * Access: RW
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, pptb, prio_to_buff_msb, 0x0C, 0x04, 4);
+
 #define MLXSW_REG_PPTB_ALL_PRIO 0xFF
 
 static inline void mlxsw_reg_pptb_pack(char *payload, u8 local_port)
@@ -2792,6 +2799,14 @@ static inline void mlxsw_reg_pptb_pack(char *payload, u8 local_port)
        mlxsw_reg_pptb_mm_set(payload, MLXSW_REG_PPTB_MM_UM);
        mlxsw_reg_pptb_local_port_set(payload, local_port);
        mlxsw_reg_pptb_pm_set(payload, MLXSW_REG_PPTB_ALL_PRIO);
+       mlxsw_reg_pptb_pm_msb_set(payload, MLXSW_REG_PPTB_ALL_PRIO);
+}
+
+static inline void mlxsw_reg_pptb_prio_to_buff_pack(char *payload, u8 prio,
+                                                   u8 buff)
+{
+       mlxsw_reg_pptb_prio_to_buff_set(payload, prio, buff);
+       mlxsw_reg_pptb_prio_to_buff_msb_set(payload, prio, buff);
 }
 
 /* PBMC - Port Buffer Management Control Register
index 660429e..3740800 100644 (file)
@@ -171,23 +171,6 @@ static int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(paos), paos_pl);
 }
 
-static int mlxsw_sp_port_oper_status_get(struct mlxsw_sp_port *mlxsw_sp_port,
-                                        bool *p_is_up)
-{
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-       char paos_pl[MLXSW_REG_PAOS_LEN];
-       u8 oper_status;
-       int err;
-
-       mlxsw_reg_paos_pack(paos_pl, mlxsw_sp_port->local_port, 0);
-       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(paos), paos_pl);
-       if (err)
-               return err;
-       oper_status = mlxsw_reg_paos_oper_status_get(paos_pl);
-       *p_is_up = oper_status == MLXSW_PORT_ADMIN_STATUS_UP ? true : false;
-       return 0;
-}
-
 static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port,
                                      unsigned char *addr)
 {
@@ -1434,7 +1417,8 @@ static int mlxsw_sp_port_get_settings(struct net_device *dev,
 
        cmd->supported = mlxsw_sp_from_ptys_supported_port(eth_proto_cap) |
                         mlxsw_sp_from_ptys_supported_link(eth_proto_cap) |
-                        SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+                        SUPPORTED_Pause | SUPPORTED_Asym_Pause |
+                        SUPPORTED_Autoneg;
        cmd->advertising = mlxsw_sp_from_ptys_advert_link(eth_proto_admin);
        mlxsw_sp_from_ptys_speed_duplex(netif_carrier_ok(dev),
                                        eth_proto_oper, cmd);
@@ -1493,7 +1477,6 @@ static int mlxsw_sp_port_set_settings(struct net_device *dev,
        u32 eth_proto_new;
        u32 eth_proto_cap;
        u32 eth_proto_admin;
-       bool is_up;
        int err;
 
        speed = ethtool_cmd_speed(cmd);
@@ -1525,12 +1508,7 @@ static int mlxsw_sp_port_set_settings(struct net_device *dev,
                return err;
        }
 
-       err = mlxsw_sp_port_oper_status_get(mlxsw_sp_port, &is_up);
-       if (err) {
-               netdev_err(dev, "Failed to get oper status");
-               return err;
-       }
-       if (!is_up)
+       if (!netif_running(dev))
                return 0;
 
        err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
index a3720a0..074cdda 100644 (file)
@@ -194,7 +194,7 @@ static int mlxsw_sp_port_pb_prio_init(struct mlxsw_sp_port *mlxsw_sp_port)
 
        mlxsw_reg_pptb_pack(pptb_pl, mlxsw_sp_port->local_port);
        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
-               mlxsw_reg_pptb_prio_to_buff_set(pptb_pl, i, 0);
+               mlxsw_reg_pptb_prio_to_buff_pack(pptb_pl, i, 0);
        return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pptb),
                               pptb_pl);
 }
index 0b32366..01cfb75 100644 (file)
@@ -103,7 +103,8 @@ static int mlxsw_sp_port_pg_prio_map(struct mlxsw_sp_port *mlxsw_sp_port,
 
        mlxsw_reg_pptb_pack(pptb_pl, mlxsw_sp_port->local_port);
        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
-               mlxsw_reg_pptb_prio_to_buff_set(pptb_pl, i, prio_tc[i]);
+               mlxsw_reg_pptb_prio_to_buff_pack(pptb_pl, i, prio_tc[i]);
+
        return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pptb),
                               pptb_pl);
 }
@@ -249,6 +250,7 @@ static int mlxsw_sp_dcbnl_ieee_setets(struct net_device *dev,
                return err;
 
        memcpy(mlxsw_sp_port->dcb.ets, ets, sizeof(*ets));
+       mlxsw_sp_port->dcb.ets->ets_cap = IEEE_8021QAZ_MAX_TCS;
 
        return 0;
 }
@@ -351,7 +353,8 @@ static int mlxsw_sp_dcbnl_ieee_setpfc(struct net_device *dev,
        struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
        int err;
 
-       if (mlxsw_sp_port->link.tx_pause || mlxsw_sp_port->link.rx_pause) {
+       if ((mlxsw_sp_port->link.tx_pause || mlxsw_sp_port->link.rx_pause) &&
+           pfc->pfc_en) {
                netdev_err(dev, "PAUSE frames already enabled on port\n");
                return -EINVAL;
        }
@@ -371,6 +374,7 @@ static int mlxsw_sp_dcbnl_ieee_setpfc(struct net_device *dev,
        }
 
        memcpy(mlxsw_sp_port->dcb.pfc, pfc, sizeof(*pfc));
+       mlxsw_sp_port->dcb.pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS;
 
        return 0;
 
index 8dedafa..a30ee42 100644 (file)
@@ -2601,8 +2601,6 @@ ppp_unregister_channel(struct ppp_channel *chan)
        spin_lock_bh(&pn->all_channels_lock);
        list_del(&pch->list);
        spin_unlock_bh(&pn->all_channels_lock);
-       put_net(pch->chan_net);
-       pch->chan_net = NULL;
 
        pch->file.dead = 1;
        wake_up_interruptible(&pch->file.rwait);
@@ -3136,6 +3134,9 @@ ppp_disconnect_channel(struct channel *pch)
  */
 static void ppp_destroy_channel(struct channel *pch)
 {
+       put_net(pch->chan_net);
+       pch->chan_net = NULL;
+
        atomic_dec(&channel_count);
 
        if (!pch->file.dead) {
index 0da72d3..e9654a6 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/mdio.h>
 #include <linux/usb/cdc.h>
 #include <linux/suspend.h>
+#include <linux/acpi.h>
 
 /* Information for net-next */
 #define NETNEXT_VERSION                "08"
 /* SRAM_IMPEDANCE */
 #define RX_DRIVING_MASK                0x6000
 
+/* MAC PASSTHRU */
+#define AD_MASK                        0xfee0
+#define EFUSE                  0xcfdb
+#define PASS_THRU_MASK         0x1
+
 enum rtl_register_content {
        _1000bps        = 0x10,
        _100bps         = 0x08,
@@ -1036,6 +1042,65 @@ out1:
        return ret;
 }
 
+/* Devices containing RTL8153-AD can support a persistent
+ * host system provided MAC address.
+ * Examples of this are Dell TB15 and Dell WD15 docks
+ */
+static int vendor_mac_passthru_addr_read(struct r8152 *tp, struct sockaddr *sa)
+{
+       acpi_status status;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+       int ret = -EINVAL;
+       u32 ocp_data;
+       unsigned char buf[6];
+
+       /* test for -AD variant of RTL8153 */
+       ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
+       if ((ocp_data & AD_MASK) != 0x1000)
+               return -ENODEV;
+
+       /* test for MAC address pass-through bit */
+       ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, EFUSE);
+       if ((ocp_data & PASS_THRU_MASK) != 1)
+               return -ENODEV;
+
+       /* returns _AUXMAC_#AABBCCDDEEFF# */
+       status = acpi_evaluate_object(NULL, "\\_SB.AMAC", NULL, &buffer);
+       obj = (union acpi_object *)buffer.pointer;
+       if (!ACPI_SUCCESS(status))
+               return -ENODEV;
+       if (obj->type != ACPI_TYPE_BUFFER || obj->string.length != 0x17) {
+               netif_warn(tp, probe, tp->netdev,
+                          "Invalid buffer when reading pass-thru MAC addr: "
+                          "(%d, %d)\n",
+                          obj->type, obj->string.length);
+               goto amacout;
+       }
+       if (strncmp(obj->string.pointer, "_AUXMAC_#", 9) != 0 ||
+           strncmp(obj->string.pointer + 0x15, "#", 1) != 0) {
+               netif_warn(tp, probe, tp->netdev,
+                          "Invalid header when reading pass-thru MAC addr\n");
+               goto amacout;
+       }
+       ret = hex2bin(buf, obj->string.pointer + 9, 6);
+       if (!(ret == 0 && is_valid_ether_addr(buf))) {
+               netif_warn(tp, probe, tp->netdev,
+                          "Invalid MAC when reading pass-thru MAC addr: "
+                          "%d, %pM\n", ret, buf);
+               ret = -EINVAL;
+               goto amacout;
+       }
+       memcpy(sa->sa_data, buf, 6);
+       ether_addr_copy(tp->netdev->dev_addr, sa->sa_data);
+       netif_info(tp, probe, tp->netdev,
+                  "Using pass-thru MAC addr %pM\n", sa->sa_data);
+
+amacout:
+       kfree(obj);
+       return ret;
+}
+
 static int set_ethernet_addr(struct r8152 *tp)
 {
        struct net_device *dev = tp->netdev;
@@ -1044,8 +1109,15 @@ static int set_ethernet_addr(struct r8152 *tp)
 
        if (tp->version == RTL_VER_01)
                ret = pla_ocp_read(tp, PLA_IDR, 8, sa.sa_data);
-       else
-               ret = pla_ocp_read(tp, PLA_BACKUP, 8, sa.sa_data);
+       else {
+               /* if this is not an RTL8153-AD, no eFuse mac pass thru set,
+                * or system doesn't provide valid _SB.AMAC this will be
+                * be expected to non-zero
+                */
+               ret = vendor_mac_passthru_addr_read(tp, &sa);
+               if (ret < 0)
+                       ret = pla_ocp_read(tp, PLA_BACKUP, 8, sa.sa_data);
+       }
 
        if (ret < 0) {
                netif_err(tp, probe, dev, "Get ether addr fail\n");
@@ -2296,10 +2368,6 @@ static u32 __rtl_get_wol(struct r8152 *tp)
        u32 ocp_data;
        u32 wolopts = 0;
 
-       ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5);
-       if (!(ocp_data & LAN_WAKE_EN))
-               return 0;
-
        ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34);
        if (ocp_data & LINK_ON_WAKE_EN)
                wolopts |= WAKE_PHY;
@@ -2332,15 +2400,13 @@ static void __rtl_set_wol(struct r8152 *tp, u32 wolopts)
        ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data);
 
        ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5);
-       ocp_data &= ~(UWF_EN | BWF_EN | MWF_EN | LAN_WAKE_EN);
+       ocp_data &= ~(UWF_EN | BWF_EN | MWF_EN);
        if (wolopts & WAKE_UCAST)
                ocp_data |= UWF_EN;
        if (wolopts & WAKE_BCAST)
                ocp_data |= BWF_EN;
        if (wolopts & WAKE_MCAST)
                ocp_data |= MWF_EN;
-       if (wolopts & WAKE_ANY)
-               ocp_data |= LAN_WAKE_EN;
        ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG5, ocp_data);
 
        ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
@@ -4359,3 +4425,4 @@ module_usb_driver(rtl8152_driver);
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
index 1a51584..d5fb55c 100644 (file)
@@ -1394,19 +1394,22 @@ static int ns_cmp(void *priv, struct list_head *a, struct list_head *b)
        return nsa->ns_id - nsb->ns_id;
 }
 
-static struct nvme_ns *nvme_find_ns(struct nvme_ctrl *ctrl, unsigned nsid)
+static struct nvme_ns *nvme_find_get_ns(struct nvme_ctrl *ctrl, unsigned nsid)
 {
-       struct nvme_ns *ns;
-
-       lockdep_assert_held(&ctrl->namespaces_mutex);
+       struct nvme_ns *ns, *ret = NULL;
 
+       mutex_lock(&ctrl->namespaces_mutex);
        list_for_each_entry(ns, &ctrl->namespaces, list) {
-               if (ns->ns_id == nsid)
-                       return ns;
+               if (ns->ns_id == nsid) {
+                       kref_get(&ns->kref);
+                       ret = ns;
+                       break;
+               }
                if (ns->ns_id > nsid)
                        break;
        }
-       return NULL;
+       mutex_unlock(&ctrl->namespaces_mutex);
+       return ret;
 }
 
 static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
@@ -1415,8 +1418,6 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
        struct gendisk *disk;
        int node = dev_to_node(ctrl->dev);
 
-       lockdep_assert_held(&ctrl->namespaces_mutex);
-
        ns = kzalloc_node(sizeof(*ns), GFP_KERNEL, node);
        if (!ns)
                return;
@@ -1457,7 +1458,10 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
        if (nvme_revalidate_disk(ns->disk))
                goto out_free_disk;
 
-       list_add_tail_rcu(&ns->list, &ctrl->namespaces);
+       mutex_lock(&ctrl->namespaces_mutex);
+       list_add_tail(&ns->list, &ctrl->namespaces);
+       mutex_unlock(&ctrl->namespaces_mutex);
+
        kref_get(&ctrl->kref);
        if (ns->type == NVME_NS_LIGHTNVM)
                return;
@@ -1480,8 +1484,6 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
 
 static void nvme_ns_remove(struct nvme_ns *ns)
 {
-       lockdep_assert_held(&ns->ctrl->namespaces_mutex);
-
        if (test_and_set_bit(NVME_NS_REMOVING, &ns->flags))
                return;
 
@@ -1494,8 +1496,11 @@ static void nvme_ns_remove(struct nvme_ns *ns)
                blk_mq_abort_requeue_list(ns->queue);
                blk_cleanup_queue(ns->queue);
        }
+
+       mutex_lock(&ns->ctrl->namespaces_mutex);
        list_del_init(&ns->list);
-       synchronize_rcu();
+       mutex_unlock(&ns->ctrl->namespaces_mutex);
+
        nvme_put_ns(ns);
 }
 
@@ -1503,10 +1508,11 @@ static void nvme_validate_ns(struct nvme_ctrl *ctrl, unsigned nsid)
 {
        struct nvme_ns *ns;
 
-       ns = nvme_find_ns(ctrl, nsid);
+       ns = nvme_find_get_ns(ctrl, nsid);
        if (ns) {
                if (revalidate_disk(ns->disk))
                        nvme_ns_remove(ns);
+               nvme_put_ns(ns);
        } else
                nvme_alloc_ns(ctrl, nsid);
 }
@@ -1535,9 +1541,11 @@ static int nvme_scan_ns_list(struct nvme_ctrl *ctrl, unsigned nn)
                        nvme_validate_ns(ctrl, nsid);
 
                        while (++prev < nsid) {
-                               ns = nvme_find_ns(ctrl, prev);
-                               if (ns)
+                               ns = nvme_find_get_ns(ctrl, prev);
+                               if (ns) {
                                        nvme_ns_remove(ns);
+                                       nvme_put_ns(ns);
+                               }
                        }
                }
                nn -= j;
@@ -1552,8 +1560,6 @@ static void nvme_scan_ns_sequential(struct nvme_ctrl *ctrl, unsigned nn)
        struct nvme_ns *ns, *next;
        unsigned i;
 
-       lockdep_assert_held(&ctrl->namespaces_mutex);
-
        for (i = 1; i <= nn; i++)
                nvme_validate_ns(ctrl, i);
 
@@ -1576,7 +1582,6 @@ static void nvme_scan_work(struct work_struct *work)
        if (nvme_identify_ctrl(ctrl, &id))
                return;
 
-       mutex_lock(&ctrl->namespaces_mutex);
        nn = le32_to_cpu(id->nn);
        if (ctrl->vs >= NVME_VS(1, 1) &&
            !(ctrl->quirks & NVME_QUIRK_IDENTIFY_CNS)) {
@@ -1585,6 +1590,7 @@ static void nvme_scan_work(struct work_struct *work)
        }
        nvme_scan_ns_sequential(ctrl, nn);
  done:
+       mutex_lock(&ctrl->namespaces_mutex);
        list_sort(NULL, &ctrl->namespaces, ns_cmp);
        mutex_unlock(&ctrl->namespaces_mutex);
        kfree(id);
@@ -1604,6 +1610,11 @@ void nvme_queue_scan(struct nvme_ctrl *ctrl)
 }
 EXPORT_SYMBOL_GPL(nvme_queue_scan);
 
+/*
+ * This function iterates the namespace list unlocked to allow recovery from
+ * controller failure. It is up to the caller to ensure the namespace list is
+ * not modified by scan work while this function is executing.
+ */
 void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
 {
        struct nvme_ns *ns, *next;
@@ -1617,10 +1628,8 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
        if (ctrl->state == NVME_CTRL_DEAD)
                nvme_kill_queues(ctrl);
 
-       mutex_lock(&ctrl->namespaces_mutex);
        list_for_each_entry_safe(ns, next, &ctrl->namespaces, list)
                nvme_ns_remove(ns);
-       mutex_unlock(&ctrl->namespaces_mutex);
 }
 EXPORT_SYMBOL_GPL(nvme_remove_namespaces);
 
@@ -1791,11 +1800,8 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
 {
        struct nvme_ns *ns;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(ns, &ctrl->namespaces, list) {
-               if (!kref_get_unless_zero(&ns->kref))
-                       continue;
-
+       mutex_lock(&ctrl->namespaces_mutex);
+       list_for_each_entry(ns, &ctrl->namespaces, list) {
                /*
                 * Revalidating a dead namespace sets capacity to 0. This will
                 * end buffered writers dirtying pages that can't be synced.
@@ -1806,10 +1812,8 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
                blk_set_queue_dying(ns->queue);
                blk_mq_abort_requeue_list(ns->queue);
                blk_mq_start_stopped_hw_queues(ns->queue, true);
-
-               nvme_put_ns(ns);
        }
-       rcu_read_unlock();
+       mutex_unlock(&ctrl->namespaces_mutex);
 }
 EXPORT_SYMBOL_GPL(nvme_kill_queues);
 
@@ -1817,8 +1821,8 @@ void nvme_stop_queues(struct nvme_ctrl *ctrl)
 {
        struct nvme_ns *ns;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(ns, &ctrl->namespaces, list) {
+       mutex_lock(&ctrl->namespaces_mutex);
+       list_for_each_entry(ns, &ctrl->namespaces, list) {
                spin_lock_irq(ns->queue->queue_lock);
                queue_flag_set(QUEUE_FLAG_STOPPED, ns->queue);
                spin_unlock_irq(ns->queue->queue_lock);
@@ -1826,7 +1830,7 @@ void nvme_stop_queues(struct nvme_ctrl *ctrl)
                blk_mq_cancel_requeue_work(ns->queue);
                blk_mq_stop_hw_queues(ns->queue);
        }
-       rcu_read_unlock();
+       mutex_unlock(&ctrl->namespaces_mutex);
 }
 EXPORT_SYMBOL_GPL(nvme_stop_queues);
 
@@ -1834,13 +1838,13 @@ void nvme_start_queues(struct nvme_ctrl *ctrl)
 {
        struct nvme_ns *ns;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(ns, &ctrl->namespaces, list) {
+       mutex_lock(&ctrl->namespaces_mutex);
+       list_for_each_entry(ns, &ctrl->namespaces, list) {
                queue_flag_clear_unlocked(QUEUE_FLAG_STOPPED, ns->queue);
                blk_mq_start_stopped_hw_queues(ns->queue, true);
                blk_mq_kick_requeue_list(ns->queue);
        }
-       rcu_read_unlock();
+       mutex_unlock(&ctrl->namespaces_mutex);
 }
 EXPORT_SYMBOL_GPL(nvme_start_queues);
 
index 38a8bbe..83797d8 100644 (file)
@@ -195,7 +195,7 @@ static void parport_detach(struct parport *port)
        struct pps_client_pp *device;
 
        /* FIXME: oooh, this is ugly! */
-       if (strcmp(pardev->name, KBUILD_MODNAME))
+       if (!pardev || strcmp(pardev->name, KBUILD_MODNAME))
                /* not our port */
                return;
 
index 526bf23..6c7fe47 100644 (file)
@@ -152,7 +152,6 @@ static const struct regulator_ops rpm_smps_ldo_ops_fixed = {
        .enable = rpm_reg_enable,
        .disable = rpm_reg_disable,
        .is_enabled = rpm_reg_is_enabled,
-       .list_voltage = regulator_list_voltage_linear_range,
 
        .get_voltage = rpm_reg_get_voltage,
        .set_voltage = rpm_reg_set_voltage,
index f973bfc..1e93a37 100644 (file)
@@ -366,34 +366,22 @@ static void to_utf8(struct vc_data *vc, uint c)
 
 static void do_compute_shiftstate(void)
 {
-       unsigned int i, j, k, sym, val;
+       unsigned int k, sym, val;
 
        shift_state = 0;
        memset(shift_down, 0, sizeof(shift_down));
 
-       for (i = 0; i < ARRAY_SIZE(key_down); i++) {
-
-               if (!key_down[i])
+       for_each_set_bit(k, key_down, min(NR_KEYS, KEY_CNT)) {
+               sym = U(key_maps[0][k]);
+               if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK)
                        continue;
 
-               k = i * BITS_PER_LONG;
-
-               for (j = 0; j < BITS_PER_LONG; j++, k++) {
-
-                       if (!test_bit(k, key_down))
-                               continue;
+               val = KVAL(sym);
+               if (val == KVAL(K_CAPSSHIFT))
+                       val = KVAL(K_SHIFT);
 
-                       sym = U(key_maps[0][k]);
-                       if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK)
-                               continue;
-
-                       val = KVAL(sym);
-                       if (val == KVAL(K_CAPSSHIFT))
-                               val = KVAL(K_SHIFT);
-
-                       shift_down[val]++;
-                       shift_state |= (1 << val);
-               }
+               shift_down[val]++;
+               shift_state |= BIT(val);
        }
 }
 
index f4645c5..e2e7c74 100644 (file)
@@ -853,7 +853,7 @@ v9fs_vfs_atomic_open(struct inode *dir, struct dentry *dentry,
        struct p9_fid *fid, *inode_fid;
        struct dentry *res = NULL;
 
-       if (d_unhashed(dentry)) {
+       if (d_in_lookup(dentry)) {
                res = v9fs_vfs_lookup(dir, dentry, 0);
                if (IS_ERR(res))
                        return PTR_ERR(res);
index a34702c..1b51eaa 100644 (file)
@@ -254,7 +254,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
        struct posix_acl *pacl = NULL, *dacl = NULL;
        struct dentry *res = NULL;
 
-       if (d_unhashed(dentry)) {
+       if (d_in_lookup(dentry)) {
                res = v9fs_vfs_lookup(dir, dentry, 0);
                if (IS_ERR(res))
                        return PTR_ERR(res);
index ce2f579..0daaf7c 100644 (file)
@@ -394,7 +394,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
        if ((flags & O_CREAT) && !req->r_reply_info.head->is_dentry)
                err = ceph_handle_notrace_create(dir, dentry);
 
-       if (d_unhashed(dentry)) {
+       if (d_in_lookup(dentry)) {
                dn = ceph_finish_lookup(req, dentry, err);
                if (IS_ERR(dn))
                        err = PTR_ERR(dn);
index c3eb998..fb0903f 100644 (file)
@@ -445,7 +445,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
                 * Check for hashed negative dentry. We have already revalidated
                 * the dentry and it is fine. No need to perform another lookup.
                 */
-               if (!d_unhashed(direntry))
+               if (!d_in_lookup(direntry))
                        return -ENOENT;
 
                res = cifs_lookup(inode, direntry, 0);
index 264f07c..cca7b04 100644 (file)
@@ -480,7 +480,7 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
        struct fuse_conn *fc = get_fuse_conn(dir);
        struct dentry *res = NULL;
 
-       if (d_unhashed(entry)) {
+       if (d_in_lookup(entry)) {
                res = fuse_lookup(dir, entry, 0);
                if (IS_ERR(res))
                        return PTR_ERR(res);
index 21dc784..9bad79f 100644 (file)
@@ -1189,7 +1189,7 @@ static int gfs2_atomic_open(struct inode *dir, struct dentry *dentry,
        struct dentry *d;
        bool excl = !!(flags & O_EXCL);
 
-       if (!d_unhashed(dentry))
+       if (!d_in_lookup(dentry))
                goto skip_lookup;
 
        d = __gfs2_lookup(dir, dentry, file, opened);
index d8015a0..19d93d0 100644 (file)
@@ -1485,11 +1485,13 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
                    struct file *file, unsigned open_flags,
                    umode_t mode, int *opened)
 {
+       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
        struct nfs_open_context *ctx;
        struct dentry *res;
        struct iattr attr = { .ia_valid = ATTR_OPEN };
        struct inode *inode;
        unsigned int lookup_flags = 0;
+       bool switched = false;
        int err;
 
        /* Expect a negative dentry */
@@ -1504,7 +1506,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
 
        /* NFS only supports OPEN on regular files */
        if ((open_flags & O_DIRECTORY)) {
-               if (!d_unhashed(dentry)) {
+               if (!d_in_lookup(dentry)) {
                        /*
                         * Hashed negative dentry with O_DIRECTORY: dentry was
                         * revalidated and is fine, no need to perform lookup
@@ -1528,6 +1530,17 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
                attr.ia_size = 0;
        }
 
+       if (!(open_flags & O_CREAT) && !d_in_lookup(dentry)) {
+               d_drop(dentry);
+               switched = true;
+               dentry = d_alloc_parallel(dentry->d_parent,
+                                         &dentry->d_name, &wq);
+               if (IS_ERR(dentry))
+                       return PTR_ERR(dentry);
+               if (unlikely(!d_in_lookup(dentry)))
+                       return finish_no_open(file, dentry);
+       }
+
        ctx = create_nfs_open_context(dentry, open_flags);
        err = PTR_ERR(ctx);
        if (IS_ERR(ctx))
@@ -1563,14 +1576,23 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
        trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
        put_nfs_open_context(ctx);
 out:
+       if (unlikely(switched)) {
+               d_lookup_done(dentry);
+               dput(dentry);
+       }
        return err;
 
 no_open:
        res = nfs_lookup(dir, dentry, lookup_flags);
-       err = PTR_ERR(res);
+       if (switched) {
+               d_lookup_done(dentry);
+               if (!res)
+                       res = dentry;
+               else
+                       dput(dentry);
+       }
        if (IS_ERR(res))
-               goto out;
-
+               return PTR_ERR(res);
        return finish_no_open(file, res);
 }
 EXPORT_SYMBOL_GPL(nfs_atomic_open);
index c2a6b08..5c9d2d8 100644 (file)
@@ -505,6 +505,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
        struct dentry *upper;
        struct dentry *opaquedir = NULL;
        int err;
+       int flags = 0;
 
        if (WARN_ON(!workdir))
                return -EROFS;
@@ -534,46 +535,39 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
        if (err)
                goto out_dput;
 
-       whiteout = ovl_whiteout(workdir, dentry);
-       err = PTR_ERR(whiteout);
-       if (IS_ERR(whiteout))
+       upper = lookup_one_len(dentry->d_name.name, upperdir,
+                              dentry->d_name.len);
+       err = PTR_ERR(upper);
+       if (IS_ERR(upper))
                goto out_unlock;
 
-       upper = ovl_dentry_upper(dentry);
-       if (!upper) {
-               upper = lookup_one_len(dentry->d_name.name, upperdir,
-                                      dentry->d_name.len);
-               err = PTR_ERR(upper);
-               if (IS_ERR(upper))
-                       goto kill_whiteout;
-
-               err = ovl_do_rename(wdir, whiteout, udir, upper, 0);
-               dput(upper);
-               if (err)
-                       goto kill_whiteout;
-       } else {
-               int flags = 0;
+       err = -ESTALE;
+       if ((opaquedir && upper != opaquedir) ||
+           (!opaquedir && ovl_dentry_upper(dentry) &&
+            upper != ovl_dentry_upper(dentry))) {
+               goto out_dput_upper;
+       }
 
-               if (opaquedir)
-                       upper = opaquedir;
-               err = -ESTALE;
-               if (upper->d_parent != upperdir)
-                       goto kill_whiteout;
+       whiteout = ovl_whiteout(workdir, dentry);
+       err = PTR_ERR(whiteout);
+       if (IS_ERR(whiteout))
+               goto out_dput_upper;
 
-               if (is_dir)
-                       flags |= RENAME_EXCHANGE;
+       if (d_is_dir(upper))
+               flags = RENAME_EXCHANGE;
 
-               err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
-               if (err)
-                       goto kill_whiteout;
+       err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
+       if (err)
+               goto kill_whiteout;
+       if (flags)
+               ovl_cleanup(wdir, upper);
 
-               if (is_dir)
-                       ovl_cleanup(wdir, upper);
-       }
        ovl_dentry_version_inc(dentry->d_parent);
 out_d_drop:
        d_drop(dentry);
        dput(whiteout);
+out_dput_upper:
+       dput(upper);
 out_unlock:
        unlock_rename(workdir, upperdir);
 out_dput:
index c831c2e..d1cdc60 100644 (file)
@@ -80,6 +80,9 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
                                goto out_drop_write;
                }
 
+               if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
+                       attr->ia_valid &= ~ATTR_MODE;
+
                inode_lock(upperdentry->d_inode);
                err = notify_change(upperdentry, attr, NULL);
                if (!err)
@@ -410,12 +413,11 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
        if (!inode)
                return NULL;
 
-       mode &= S_IFMT;
-
        inode->i_ino = get_next_ino();
        inode->i_mode = mode;
        inode->i_flags |= S_NOATIME | S_NOCMTIME;
 
+       mode &= S_IFMT;
        switch (mode) {
        case S_IFDIR:
                inode->i_private = oe;
index 4bd9b5b..cfbca53 100644 (file)
@@ -187,6 +187,7 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
 {
        to->i_uid = from->i_uid;
        to->i_gid = from->i_gid;
+       to->i_mode = from->i_mode;
 }
 
 /* dir.c */
index dbca737..63a6ff2 100644 (file)
@@ -1575,6 +1575,12 @@ xfs_ioc_swapext(
                goto out_put_tmp_file;
        }
 
+       if (f.file->f_op != &xfs_file_operations ||
+           tmp.file->f_op != &xfs_file_operations) {
+               error = -EINVAL;
+               goto out_put_tmp_file;
+       }
+
        ip = XFS_I(file_inode(f.file));
        tip = XFS_I(file_inode(tmp.file));
 
index 4e4c214..1ff3a76 100644 (file)
@@ -192,7 +192,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE);
 /*
  * Optionally support group module level code.
  */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_group_module_level_code, FALSE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_group_module_level_code, TRUE);
 
 /*
  * Optionally use 32-bit FADT addresses if and when there is a conflict
index 6a67ab9..081d0f2 100644 (file)
 
 #define INIT_TEXT                                                      \
        *(.init.text)                                                   \
+       *(.text.startup)                                                \
        MEM_DISCARD(init.text)
 
 #define EXIT_DATA                                                      \
        *(.exit.data)                                                   \
+       *(.fini_array)                                                  \
+       *(.dtors)                                                       \
        MEM_DISCARD(exit.data)                                          \
        MEM_DISCARD(exit.rodata)
 
 #define EXIT_TEXT                                                      \
        *(.exit.text)                                                   \
+       *(.text.exit)                                                   \
        MEM_DISCARD(exit.text)
 
 #define EXIT_CALL                                                      \
index c801d90..4cecb0b 100644 (file)
@@ -316,6 +316,20 @@ ttm_bo_reference(struct ttm_buffer_object *bo)
  */
 extern int ttm_bo_wait(struct ttm_buffer_object *bo,
                       bool interruptible, bool no_wait);
+
+/**
+ * ttm_bo_mem_compat - Check if proposed placement is compatible with a bo
+ *
+ * @placement:  Return immediately if buffer is busy.
+ * @mem:  The struct ttm_mem_reg indicating the region where the bo resides
+ * @new_flags: Describes compatible placement found
+ *
+ * Returns true if the placement is compatible
+ */
+extern bool ttm_bo_mem_compat(struct ttm_placement *placement,
+                             struct ttm_mem_reg *mem,
+                             uint32_t *new_flags);
+
 /**
  * ttm_bo_validate
  *
index e6b41f4..3db25df 100644 (file)
@@ -159,6 +159,7 @@ struct bcma_host_ops {
 #define BCMA_CORE_DEFAULT              0xFFF
 
 #define BCMA_MAX_NR_CORES              16
+#define BCMA_CORE_SIZE                 0x1000
 
 /* Chip IDs of PCIe devices */
 #define BCMA_CHIP_ID_BCM4313   0x4313
index 6fc31ef..8f74f3d 100644 (file)
@@ -467,7 +467,11 @@ static inline void bpf_prog_unlock_ro(struct bpf_prog *fp)
 }
 #endif /* CONFIG_DEBUG_SET_MODULE_RONX */
 
-int sk_filter(struct sock *sk, struct sk_buff *skb);
+int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap);
+static inline int sk_filter(struct sock *sk, struct sk_buff *skb)
+{
+       return sk_filter_trim_cap(sk, skb, 1);
+}
 
 struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err);
 void bpf_prog_free(struct bpf_prog *fp);
index 419fb9e..f0a7a03 100644 (file)
@@ -94,7 +94,7 @@ static inline int split_huge_page(struct page *page)
 void deferred_split_huge_page(struct page *page);
 
 void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
-               unsigned long address, bool freeze);
+               unsigned long address, bool freeze, struct page *page);
 
 #define split_huge_pmd(__vma, __pmd, __address)                                \
        do {                                                            \
@@ -102,7 +102,7 @@ void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
                if (pmd_trans_huge(*____pmd)                            \
                                        || pmd_devmap(*____pmd))        \
                        __split_huge_pmd(__vma, __pmd, __address,       \
-                                               false);                 \
+                                               false, NULL);           \
        }  while (0)
 
 
index a805474..56e6069 100644 (file)
@@ -97,6 +97,11 @@ enum mem_cgroup_events_target {
 #define MEM_CGROUP_ID_SHIFT    16
 #define MEM_CGROUP_ID_MAX      USHRT_MAX
 
+struct mem_cgroup_id {
+       int id;
+       atomic_t ref;
+};
+
 struct mem_cgroup_stat_cpu {
        long count[MEMCG_NR_STAT];
        unsigned long events[MEMCG_NR_EVENTS];
@@ -172,6 +177,9 @@ enum memcg_kmem_state {
 struct mem_cgroup {
        struct cgroup_subsys_state css;
 
+       /* Private memcg ID. Used to ID objects that outlive the cgroup */
+       struct mem_cgroup_id id;
+
        /* Accounted resources */
        struct page_counter memory;
        struct page_counter swap;
@@ -330,22 +338,9 @@ static inline unsigned short mem_cgroup_id(struct mem_cgroup *memcg)
        if (mem_cgroup_disabled())
                return 0;
 
-       return memcg->css.id;
-}
-
-/**
- * mem_cgroup_from_id - look up a memcg from an id
- * @id: the id to look up
- *
- * Caller must hold rcu_read_lock() and use css_tryget() as necessary.
- */
-static inline struct mem_cgroup *mem_cgroup_from_id(unsigned short id)
-{
-       struct cgroup_subsys_state *css;
-
-       css = css_from_id(id, &memory_cgrp_subsys);
-       return mem_cgroup_from_css(css);
+       return memcg->id.id;
 }
+struct mem_cgroup *mem_cgroup_from_id(unsigned short id);
 
 /**
  * parent_mem_cgroup - find the accounting parent of a memcg
index f45929c..da4b33b 100644 (file)
@@ -4145,6 +4145,13 @@ static inline void netif_keep_dst(struct net_device *dev)
        dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM);
 }
 
+/* return true if dev can't cope with mtu frames that need vlan tag insertion */
+static inline bool netif_reduces_vlan_mtu(struct net_device *dev)
+{
+       /* TODO: reserve and use an additional IFF bit, if we get more users */
+       return dev->priv_flags & IFF_MACSEC;
+}
+
 extern struct pernet_operations __net_initdata loopback_net_ops;
 
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
index 308d604..09779b0 100644 (file)
@@ -42,6 +42,7 @@ extern int pm_clk_create(struct device *dev);
 extern void pm_clk_destroy(struct device *dev);
 extern int pm_clk_add(struct device *dev, const char *con_id);
 extern int pm_clk_add_clk(struct device *dev, struct clk *clk);
+extern int of_pm_clk_add_clk(struct device *dev, const char *name);
 extern int of_pm_clk_add_clks(struct device *dev);
 extern void pm_clk_remove(struct device *dev, const char *con_id);
 extern void pm_clk_remove_clk(struct device *dev, struct clk *clk);
index 39285c7..31fec85 100644 (file)
@@ -57,7 +57,6 @@ struct generic_pm_domain {
        unsigned int device_count;      /* Number of devices */
        unsigned int suspended_count;   /* System suspend device counter */
        unsigned int prepared_count;    /* Suspend counter of prepared devices */
-       bool suspend_power_off; /* Power status before system suspend */
        int (*power_off)(struct generic_pm_domain *domain);
        int (*power_on)(struct generic_pm_domain *domain);
        struct gpd_dev_ops dev_ops;
@@ -128,8 +127,8 @@ extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
                                  struct generic_pm_domain *new_subdomain);
 extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
                                     struct generic_pm_domain *target);
-extern void pm_genpd_init(struct generic_pm_domain *genpd,
-                         struct dev_power_governor *gov, bool is_off);
+extern int pm_genpd_init(struct generic_pm_domain *genpd,
+                        struct dev_power_governor *gov, bool is_off);
 
 extern struct dev_power_governor simple_qos_governor;
 extern struct dev_power_governor pm_domain_always_on_gov;
@@ -164,9 +163,10 @@ static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
 {
        return -ENOSYS;
 }
-static inline void pm_genpd_init(struct generic_pm_domain *genpd,
-                                struct dev_power_governor *gov, bool is_off)
+static inline int pm_genpd_init(struct generic_pm_domain *genpd,
+                               struct dev_power_governor *gov, bool is_off)
 {
+       return -ENOSYS;
 }
 #endif
 
index 5b5a80c..c818772 100644 (file)
@@ -43,10 +43,8 @@ struct posix_acl_entry {
 };
 
 struct posix_acl {
-       union {
-               atomic_t                a_refcount;
-               struct rcu_head         a_rcu;
-       };
+       atomic_t                a_refcount;
+       struct rcu_head         a_rcu;
        unsigned int            a_count;
        struct posix_acl_entry  a_entries[0];
 };
index cb4b7e8..eca6f62 100644 (file)
@@ -407,6 +407,7 @@ static inline __must_check
 void **radix_tree_iter_retry(struct radix_tree_iter *iter)
 {
        iter->next_index = iter->index;
+       iter->tags = 0;
        return NULL;
 }
 
index 49eb4f8..2b0fad8 100644 (file)
@@ -158,7 +158,7 @@ struct anon_vma *page_get_anon_vma(struct page *page);
 /*
  * rmap interfaces called when adding or removing pte of page
  */
-void page_move_anon_rmap(struct page *, struct vm_area_struct *, unsigned long);
+void page_move_anon_rmap(struct page *, struct vm_area_struct *);
 void page_add_anon_rmap(struct page *, struct vm_area_struct *,
                unsigned long, bool);
 void do_page_add_anon_rmap(struct page *, struct vm_area_struct *,
index 8b6ec7e..7693e39 100644 (file)
@@ -18,12 +18,11 @@ static inline void pm_set_vt_switch(int do_switch)
 #endif
 
 #ifdef CONFIG_VT_CONSOLE_SLEEP
-extern int pm_prepare_console(void);
+extern void pm_prepare_console(void);
 extern void pm_restore_console(void);
 #else
-static inline int pm_prepare_console(void)
+static inline void pm_prepare_console(void)
 {
-       return 0;
 }
 
 static inline void pm_restore_console(void)
index dd78bea..b6083c3 100644 (file)
@@ -284,6 +284,14 @@ static inline bool nf_is_loopback_packet(const struct sk_buff *skb)
        return skb->dev && skb->skb_iif && skb->dev->flags & IFF_LOOPBACK;
 }
 
+/* jiffies until ct expires, 0 if already expired */
+static inline unsigned long nf_ct_expires(const struct nf_conn *ct)
+{
+       long timeout = (long)ct->timeout.expires - (long)jiffies;
+
+       return timeout > 0 ? timeout : 0;
+}
+
 struct kernel_param;
 
 int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp);
index 649d2a8..ff5be7e 100644 (file)
@@ -1576,7 +1576,13 @@ static inline void sock_put(struct sock *sk)
  */
 void sock_gen_put(struct sock *sk);
 
-int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested);
+int __sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested,
+                    unsigned int trim_cap);
+static inline int sk_receive_skb(struct sock *sk, struct sk_buff *skb,
+                                const int nested)
+{
+       return __sk_receive_skb(sk, skb, nested, 1);
+}
 
 static inline void sk_tx_queue_set(struct sock *sk, int tx_queue)
 {
index 985619a..1d8e158 100644 (file)
@@ -60,7 +60,7 @@ struct switchdev_attr {
                struct netdev_phys_item_id ppid;        /* PORT_PARENT_ID */
                u8 stp_state;                           /* PORT_STP_STATE */
                unsigned long brport_flags;             /* PORT_BRIDGE_FLAGS */
-               u32 ageing_time;                        /* BRIDGE_AGEING_TIME */
+               clock_t ageing_time;                    /* BRIDGE_AGEING_TIME */
                bool vlan_filtering;                    /* BRIDGE_VLAN_FILTERING */
        } u;
 };
index 8bdae34..ec10cfe 100644 (file)
@@ -245,6 +245,7 @@ endif
 header-y += hw_breakpoint.h
 header-y += l2tp.h
 header-y += libc-compat.h
+header-y += lirc.h
 header-y += limits.h
 header-y += llc.h
 header-y += loop.h
index 737fa32..d6d071f 100644 (file)
 #define SW_ROTATE_LOCK         0x0c  /* set = rotate locked/disabled */
 #define SW_LINEIN_INSERT       0x0d  /* set = inserted */
 #define SW_MUTE_DEVICE         0x0e  /* set = device disabled */
+#define SW_PEN_INSERTED                0x0f  /* set = pen inserted */
 #define SW_MAX                 0x0f
 #define SW_CNT                 (SW_MAX+1)
 
index d948e44..7b61887 100644 (file)
@@ -1201,6 +1201,8 @@ static struct cpuhp_step cpuhp_bp_states[] = {
                .teardown               = takedown_cpu,
                .cant_stop              = true,
        },
+#else
+       [CPUHP_BRINGUP_CPU] = { },
 #endif
 };
 
index e25e92f..6a5c239 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/vmalloc.h>
 #include "gcov.h"
 
-#if __GNUC__ == 5 && __GNUC_MINOR__ >= 1
+#if (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
 #define GCOV_COUNTERS                  10
 #elif __GNUC__ == 4 && __GNUC_MINOR__ >= 9
 #define GCOV_COUNTERS                  9
index cb880a1..eb4f717 100644 (file)
@@ -1,6 +1,8 @@
 
 ccflags-$(CONFIG_PM_DEBUG)     := -DDEBUG
 
+KASAN_SANITIZE_snapshot.o      := n
+
 obj-y                          += qos.o
 obj-$(CONFIG_PM)               += main.o
 obj-$(CONFIG_VT_CONSOLE_SLEEP) += console.o
index aba9c54..0e78179 100644 (file)
@@ -126,17 +126,17 @@ out:
        return ret;
 }
 
-int pm_prepare_console(void)
+void pm_prepare_console(void)
 {
        if (!pm_vt_switch())
-               return 0;
+               return;
 
        orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);
        if (orig_fgconsole < 0)
-               return 1;
+               return;
 
        orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
-       return 0;
+       return;
 }
 
 void pm_restore_console(void)
index fca9254..0ee1df0 100644 (file)
@@ -52,6 +52,7 @@ enum {
 #ifdef CONFIG_SUSPEND
        HIBERNATION_SUSPEND,
 #endif
+       HIBERNATION_TEST_RESUME,
        /* keep last */
        __HIBERNATION_AFTER_LAST
 };
@@ -409,6 +410,11 @@ int hibernation_snapshot(int platform_mode)
        goto Close;
 }
 
+int __weak hibernate_resume_nonboot_cpu_disable(void)
+{
+       return disable_nonboot_cpus();
+}
+
 /**
  * resume_target_kernel - Restore system state from a hibernation image.
  * @platform_mode: Whether or not to use the platform driver.
@@ -433,7 +439,7 @@ static int resume_target_kernel(bool platform_mode)
        if (error)
                goto Cleanup;
 
-       error = disable_nonboot_cpus();
+       error = hibernate_resume_nonboot_cpu_disable();
        if (error)
                goto Enable_cpus;
 
@@ -642,12 +648,39 @@ static void power_down(void)
                cpu_relax();
 }
 
+static int load_image_and_restore(void)
+{
+       int error;
+       unsigned int flags;
+
+       pr_debug("PM: Loading hibernation image.\n");
+
+       lock_device_hotplug();
+       error = create_basic_memory_bitmaps();
+       if (error)
+               goto Unlock;
+
+       error = swsusp_read(&flags);
+       swsusp_close(FMODE_READ);
+       if (!error)
+               hibernation_restore(flags & SF_PLATFORM_MODE);
+
+       printk(KERN_ERR "PM: Failed to load hibernation image, recovering.\n");
+       swsusp_free();
+       free_basic_memory_bitmaps();
+ Unlock:
+       unlock_device_hotplug();
+
+       return error;
+}
+
 /**
  * hibernate - Carry out system hibernation, including saving the image.
  */
 int hibernate(void)
 {
-       int error;
+       int error, nr_calls = 0;
+       bool snapshot_test = false;
 
        if (!hibernation_available()) {
                pr_debug("PM: Hibernation not available.\n");
@@ -662,9 +695,11 @@ int hibernate(void)
        }
 
        pm_prepare_console();
-       error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
-       if (error)
+       error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
+       if (error) {
+               nr_calls--;
                goto Exit;
+       }
 
        printk(KERN_INFO "PM: Syncing filesystems ... ");
        sys_sync();
@@ -697,8 +732,12 @@ int hibernate(void)
                pr_debug("PM: writing image.\n");
                error = swsusp_write(flags);
                swsusp_free();
-               if (!error)
-                       power_down();
+               if (!error) {
+                       if (hibernation_mode == HIBERNATION_TEST_RESUME)
+                               snapshot_test = true;
+                       else
+                               power_down();
+               }
                in_suspend = 0;
                pm_restore_gfp_mask();
        } else {
@@ -709,12 +748,18 @@ int hibernate(void)
        free_basic_memory_bitmaps();
  Thaw:
        unlock_device_hotplug();
+       if (snapshot_test) {
+               pr_debug("PM: Checking hibernation image\n");
+               error = swsusp_check();
+               if (!error)
+                       error = load_image_and_restore();
+       }
        thaw_processes();
 
        /* Don't bother checking whether freezer_test_done is true */
        freezer_test_done = false;
  Exit:
-       pm_notifier_call_chain(PM_POST_HIBERNATION);
+       __pm_notifier_call_chain(PM_POST_HIBERNATION, nr_calls, NULL);
        pm_restore_console();
        atomic_inc(&snapshot_device_available);
  Unlock:
@@ -740,8 +785,7 @@ int hibernate(void)
  */
 static int software_resume(void)
 {
-       int error;
-       unsigned int flags;
+       int error, nr_calls = 0;
 
        /*
         * If the user said "noresume".. bail out early.
@@ -827,35 +871,20 @@ static int software_resume(void)
        }
 
        pm_prepare_console();
-       error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
-       if (error)
+       error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls);
+       if (error) {
+               nr_calls--;
                goto Close_Finish;
+       }
 
        pr_debug("PM: Preparing processes for restore.\n");
        error = freeze_processes();
        if (error)
                goto Close_Finish;
-
-       pr_debug("PM: Loading hibernation image.\n");
-
-       lock_device_hotplug();
-       error = create_basic_memory_bitmaps();
-       if (error)
-               goto Thaw;
-
-       error = swsusp_read(&flags);
-       swsusp_close(FMODE_READ);
-       if (!error)
-               hibernation_restore(flags & SF_PLATFORM_MODE);
-
-       printk(KERN_ERR "PM: Failed to load hibernation image, recovering.\n");
-       swsusp_free();
-       free_basic_memory_bitmaps();
- Thaw:
-       unlock_device_hotplug();
+       error = load_image_and_restore();
        thaw_processes();
  Finish:
-       pm_notifier_call_chain(PM_POST_RESTORE);
+       __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
        pm_restore_console();
        atomic_inc(&snapshot_device_available);
        /* For success case, the suspend path will release the lock */
@@ -878,6 +907,7 @@ static const char * const hibernation_modes[] = {
 #ifdef CONFIG_SUSPEND
        [HIBERNATION_SUSPEND]   = "suspend",
 #endif
+       [HIBERNATION_TEST_RESUME]       = "test_resume",
 };
 
 /*
@@ -924,6 +954,7 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr,
 #ifdef CONFIG_SUSPEND
                case HIBERNATION_SUSPEND:
 #endif
+               case HIBERNATION_TEST_RESUME:
                        break;
                case HIBERNATION_PLATFORM:
                        if (hibernation_ops)
@@ -970,6 +1001,7 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
 #ifdef CONFIG_SUSPEND
                case HIBERNATION_SUSPEND:
 #endif
+               case HIBERNATION_TEST_RESUME:
                        hibernation_mode = mode;
                        break;
                case HIBERNATION_PLATFORM:
@@ -1115,13 +1147,16 @@ static int __init resume_offset_setup(char *str)
 
 static int __init hibernate_setup(char *str)
 {
-       if (!strncmp(str, "noresume", 8))
+       if (!strncmp(str, "noresume", 8)) {
                noresume = 1;
-       else if (!strncmp(str, "nocompress", 10))
+       } else if (!strncmp(str, "nocompress", 10)) {
                nocompress = 1;
-       else if (!strncmp(str, "no", 2)) {
+       else if (!strncmp(str, "no", 2)) {
                noresume = 1;
                nohibernate = 1;
+       } else if (IS_ENABLED(CONFIG_DEBUG_RODATA)
+                  && !strncmp(str, "protect_image", 13)) {
+               enable_restore_image_protection();
        }
        return 1;
 }
index 2794697..5ea50b1 100644 (file)
@@ -38,12 +38,19 @@ int unregister_pm_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL_GPL(unregister_pm_notifier);
 
-int pm_notifier_call_chain(unsigned long val)
+int __pm_notifier_call_chain(unsigned long val, int nr_to_call, int *nr_calls)
 {
-       int ret = blocking_notifier_call_chain(&pm_chain_head, val, NULL);
+       int ret;
+
+       ret = __blocking_notifier_call_chain(&pm_chain_head, val, NULL,
+                                               nr_to_call, nr_calls);
 
        return notifier_to_errno(ret);
 }
+int pm_notifier_call_chain(unsigned long val)
+{
+       return __pm_notifier_call_chain(val, -1, NULL);
+}
 
 /* If set, devices may be suspended and resumed asynchronously. */
 int pm_async_enabled = 1;
index efe1b3b..242d8b8 100644 (file)
@@ -38,6 +38,8 @@ static inline char *check_image_kernel(struct swsusp_info *info)
 }
 #endif /* CONFIG_ARCH_HIBERNATION_HEADER */
 
+extern int hibernate_resume_nonboot_cpu_disable(void);
+
 /*
  * Keep some memory free so that I/O operations can succeed without paging
  * [Might this be more than 4 MB?]
@@ -59,6 +61,13 @@ extern int hibernation_snapshot(int platform_mode);
 extern int hibernation_restore(int platform_mode);
 extern int hibernation_platform_enter(void);
 
+#ifdef CONFIG_DEBUG_RODATA
+/* kernel/power/snapshot.c */
+extern void enable_restore_image_protection(void);
+#else
+static inline void enable_restore_image_protection(void) {}
+#endif /* CONFIG_DEBUG_RODATA */
+
 #else /* !CONFIG_HIBERNATION */
 
 static inline void hibernate_reserved_size_init(void) {}
@@ -200,6 +209,8 @@ static inline void suspend_test_finish(const char *label) {}
 
 #ifdef CONFIG_PM_SLEEP
 /* kernel/power/main.c */
+extern int __pm_notifier_call_chain(unsigned long val, int nr_to_call,
+                                   int *nr_calls);
 extern int pm_notifier_call_chain(unsigned long val);
 #endif
 
index 0c2ee97..8f27d5a 100644 (file)
@@ -89,6 +89,9 @@ static int try_to_freeze_tasks(bool user_only)
                       elapsed_msecs / 1000, elapsed_msecs % 1000,
                       todo - wq_busy, wq_busy);
 
+               if (wq_busy)
+                       show_workqueue_state();
+
                if (!wakeup) {
                        read_lock(&tasklist_lock);
                        for_each_process_thread(g, p) {
index 3a97060..d90df92 100644 (file)
 
 #include "power.h"
 
+#ifdef CONFIG_DEBUG_RODATA
+static bool hibernate_restore_protection;
+static bool hibernate_restore_protection_active;
+
+void enable_restore_image_protection(void)
+{
+       hibernate_restore_protection = true;
+}
+
+static inline void hibernate_restore_protection_begin(void)
+{
+       hibernate_restore_protection_active = hibernate_restore_protection;
+}
+
+static inline void hibernate_restore_protection_end(void)
+{
+       hibernate_restore_protection_active = false;
+}
+
+static inline void hibernate_restore_protect_page(void *page_address)
+{
+       if (hibernate_restore_protection_active)
+               set_memory_ro((unsigned long)page_address, 1);
+}
+
+static inline void hibernate_restore_unprotect_page(void *page_address)
+{
+       if (hibernate_restore_protection_active)
+               set_memory_rw((unsigned long)page_address, 1);
+}
+#else
+static inline void hibernate_restore_protection_begin(void) {}
+static inline void hibernate_restore_protection_end(void) {}
+static inline void hibernate_restore_protect_page(void *page_address) {}
+static inline void hibernate_restore_unprotect_page(void *page_address) {}
+#endif /* CONFIG_DEBUG_RODATA */
+
 static int swsusp_page_is_free(struct page *);
 static void swsusp_set_page_forbidden(struct page *);
 static void swsusp_unset_page_forbidden(struct page *);
@@ -67,25 +104,32 @@ void __init hibernate_image_size_init(void)
        image_size = ((totalram_pages * 2) / 5) * PAGE_SIZE;
 }
 
-/* List of PBEs needed for restoring the pages that were allocated before
+/*
+ * List of PBEs needed for restoring the pages that were allocated before
  * the suspend and included in the suspend image, but have also been
  * allocated by the "resume" kernel, so their contents cannot be written
  * directly to their "original" page frames.
  */
 struct pbe *restore_pblist;
 
-/* Pointer to an auxiliary buffer (1 page) */
-static void *buffer;
+/* struct linked_page is used to build chains of pages */
 
-/**
- *     @safe_needed - on resume, for storing the PBE list and the image,
- *     we can only use memory pages that do not conflict with the pages
- *     used before suspend.  The unsafe pages have PageNosaveFree set
- *     and we count them using unsafe_pages.
- *
- *     Each allocated image page is marked as PageNosave and PageNosaveFree
- *     so that swsusp_free() can release it.
+#define LINKED_PAGE_DATA_SIZE  (PAGE_SIZE - sizeof(void *))
+
+struct linked_page {
+       struct linked_page *next;
+       char data[LINKED_PAGE_DATA_SIZE];
+} __packed;
+
+/*
+ * List of "safe" pages (ie. pages that were not used by the image kernel
+ * before hibernation) that may be used as temporary storage for image kernel
+ * memory contents.
  */
+static struct linked_page *safe_pages_list;
+
+/* Pointer to an auxiliary buffer (1 page) */
+static void *buffer;
 
 #define PG_ANY         0
 #define PG_SAFE                1
@@ -94,6 +138,19 @@ static void *buffer;
 
 static unsigned int allocated_unsafe_pages;
 
+/**
+ * get_image_page - Allocate a page for a hibernation image.
+ * @gfp_mask: GFP mask for the allocation.
+ * @safe_needed: Get pages that were not used before hibernation (restore only)
+ *
+ * During image restoration, for storing the PBE list and the image data, we can
+ * only use memory pages that do not conflict with the pages used before
+ * hibernation.  The "unsafe" pages have PageNosaveFree set and we count them
+ * using allocated_unsafe_pages.
+ *
+ * Each allocated image page is marked as PageNosave and PageNosaveFree so that
+ * swsusp_free() can release it.
+ */
 static void *get_image_page(gfp_t gfp_mask, int safe_needed)
 {
        void *res;
@@ -113,9 +170,21 @@ static void *get_image_page(gfp_t gfp_mask, int safe_needed)
        return res;
 }
 
+static void *__get_safe_page(gfp_t gfp_mask)
+{
+       if (safe_pages_list) {
+               void *ret = safe_pages_list;
+
+               safe_pages_list = safe_pages_list->next;
+               memset(ret, 0, PAGE_SIZE);
+               return ret;
+       }
+       return get_image_page(gfp_mask, PG_SAFE);
+}
+
 unsigned long get_safe_page(gfp_t gfp_mask)
 {
-       return (unsigned long)get_image_page(gfp_mask, PG_SAFE);
+       return (unsigned long)__get_safe_page(gfp_mask);
 }
 
 static struct page *alloc_image_page(gfp_t gfp_mask)
@@ -130,11 +199,22 @@ static struct page *alloc_image_page(gfp_t gfp_mask)
        return page;
 }
 
+static void recycle_safe_page(void *page_address)
+{
+       struct linked_page *lp = page_address;
+
+       lp->next = safe_pages_list;
+       safe_pages_list = lp;
+}
+
 /**
- *     free_image_page - free page represented by @addr, allocated with
- *     get_image_page (page flags set by it must be cleared)
+ * free_image_page - Free a page allocated for hibernation image.
+ * @addr: Address of the page to free.
+ * @clear_nosave_free: If set, clear the PageNosaveFree bit for the page.
+ *
+ * The page to free should have been allocated by get_image_page() (page flags
+ * set by it are affected).
  */
-
 static inline void free_image_page(void *addr, int clear_nosave_free)
 {
        struct page *page;
@@ -150,17 +230,8 @@ static inline void free_image_page(void *addr, int clear_nosave_free)
        __free_page(page);
 }
 
-/* struct linked_page is used to build chains of pages */
-
-#define LINKED_PAGE_DATA_SIZE  (PAGE_SIZE - sizeof(void *))
-
-struct linked_page {
-       struct linked_page *next;
-       char data[LINKED_PAGE_DATA_SIZE];
-} __packed;
-
-static inline void
-free_list_of_pages(struct linked_page *list, int clear_page_nosave)
+static inline void free_list_of_pages(struct linked_page *list,
+                                     int clear_page_nosave)
 {
        while (list) {
                struct linked_page *lp = list->next;
@@ -170,30 +241,28 @@ free_list_of_pages(struct linked_page *list, int clear_page_nosave)
        }
 }
 
-/**
-  *    struct chain_allocator is used for allocating small objects out of
-  *    a linked list of pages called 'the chain'.
-  *
-  *    The chain grows each time when there is no room for a new object in
-  *    the current page.  The allocated objects cannot be freed individually.
-  *    It is only possible to free them all at once, by freeing the entire
-  *    chain.
-  *
-  *    NOTE: The chain allocator may be inefficient if the allocated objects
-  *    are not much smaller than PAGE_SIZE.
-  */
-
+/*
+ * struct chain_allocator is used for allocating small objects out of
+ * a linked list of pages called 'the chain'.
+ *
+ * The chain grows each time when there is no room for a new object in
+ * the current page.  The allocated objects cannot be freed individually.
+ * It is only possible to free them all at once, by freeing the entire
+ * chain.
+ *
+ * NOTE: The chain allocator may be inefficient if the allocated objects
+ * are not much smaller than PAGE_SIZE.
+ */
 struct chain_allocator {
        struct linked_page *chain;      /* the chain */
        unsigned int used_space;        /* total size of objects allocated out
-                                        * of the current page
-                                        */
+                                          of the current page */
        gfp_t gfp_mask;         /* mask for allocating pages */
        int safe_needed;        /* if set, only "safe" pages are allocated */
 };
 
-static void
-chain_init(struct chain_allocator *ca, gfp_t gfp_mask, int safe_needed)
+static void chain_init(struct chain_allocator *ca, gfp_t gfp_mask,
+                      int safe_needed)
 {
        ca->chain = NULL;
        ca->used_space = LINKED_PAGE_DATA_SIZE;
@@ -208,7 +277,8 @@ static void *chain_alloc(struct chain_allocator *ca, unsigned int size)
        if (LINKED_PAGE_DATA_SIZE - ca->used_space < size) {
                struct linked_page *lp;
 
-               lp = get_image_page(ca->gfp_mask, ca->safe_needed);
+               lp = ca->safe_needed ? __get_safe_page(ca->gfp_mask) :
+                                       get_image_page(ca->gfp_mask, PG_ANY);
                if (!lp)
                        return NULL;
 
@@ -222,44 +292,44 @@ static void *chain_alloc(struct chain_allocator *ca, unsigned int size)
 }
 
 /**
- *     Data types related to memory bitmaps.
+ * Data types related to memory bitmaps.
  *
- *     Memory bitmap is a structure consiting of many linked lists of
- *     objects.  The main list's elements are of type struct zone_bitmap
- *     and each of them corresonds to one zone.  For each zone bitmap
- *     object there is a list of objects of type struct bm_block that
- *     represent each blocks of bitmap in which information is stored.
+ * Memory bitmap is a structure consiting of many linked lists of
+ * objects.  The main list's elements are of type struct zone_bitmap
+ * and each of them corresonds to one zone.  For each zone bitmap
+ * object there is a list of objects of type struct bm_block that
+ * represent each blocks of bitmap in which information is stored.
  *
- *     struct memory_bitmap contains a pointer to the main list of zone
- *     bitmap objects, a struct bm_position used for browsing the bitmap,
- *     and a pointer to the list of pages used for allocating all of the
- *     zone bitmap objects and bitmap block objects.
+ * struct memory_bitmap contains a pointer to the main list of zone
+ * bitmap objects, a struct bm_position used for browsing the bitmap,
+ * and a pointer to the list of pages used for allocating all of the
+ * zone bitmap objects and bitmap block objects.
  *
- *     NOTE: It has to be possible to lay out the bitmap in memory
- *     using only allocations of order 0.  Additionally, the bitmap is
- *     designed to work with arbitrary number of zones (this is over the
- *     top for now, but let's avoid making unnecessary assumptions ;-).
+ * NOTE: It has to be possible to lay out the bitmap in memory
+ * using only allocations of order 0.  Additionally, the bitmap is
+ * designed to work with arbitrary number of zones (this is over the
+ * top for now, but let's avoid making unnecessary assumptions ;-).
  *
- *     struct zone_bitmap contains a pointer to a list of bitmap block
- *     objects and a pointer to the bitmap block object that has been
- *     most recently used for setting bits.  Additionally, it contains the
- *     pfns that correspond to the start and end of the represented zone.
+ * struct zone_bitmap contains a pointer to a list of bitmap block
+ * objects and a pointer to the bitmap block object that has been
+ * most recently used for setting bits.  Additionally, it contains the
+ * PFNs that correspond to the start and end of the represented zone.
  *
- *     struct bm_block contains a pointer to the memory page in which
- *     information is stored (in the form of a block of bitmap)
- *     It also contains the pfns that correspond to the start and end of
- *     the represented memory area.
+ * struct bm_block contains a pointer to the memory page in which
+ * information is stored (in the form of a block of bitmap)
+ * It also contains the pfns that correspond to the start and end of
+ * the represented memory area.
  *
- *     The memory bitmap is organized as a radix tree to guarantee fast random
- *     access to the bits. There is one radix tree for each zone (as returned
- *     from create_mem_extents).
+ * The memory bitmap is organized as a radix tree to guarantee fast random
+ * access to the bits. There is one radix tree for each zone (as returned
+ * from create_mem_extents).
  *
- *     One radix tree is represented by one struct mem_zone_bm_rtree. There are
- *     two linked lists for the nodes of the tree, one for the inner nodes and
- *     one for the leave nodes. The linked leave nodes are used for fast linear
- *     access of the memory bitmap.
+ * One radix tree is represented by one struct mem_zone_bm_rtree. There are
+ * two linked lists for the nodes of the tree, one for the inner nodes and
+ * one for the leave nodes. The linked leave nodes are used for fast linear
+ * access of the memory bitmap.
  *
- *     The struct rtree_node represents one node of the radix tree.
+ * The struct rtree_node represents one node of the radix tree.
  */
 
 #define BM_END_OF_MAP  (~0UL)
@@ -305,9 +375,8 @@ struct bm_position {
 struct memory_bitmap {
        struct list_head zones;
        struct linked_page *p_list;     /* list of pages used to store zone
-                                        * bitmap objects and bitmap block
-                                        * objects
-                                        */
+                                          bitmap objects and bitmap block
+                                          objects */
        struct bm_position cur; /* most recently used bit position */
 };
 
@@ -321,12 +390,12 @@ struct memory_bitmap {
 #endif
 #define BM_RTREE_LEVEL_MASK    ((1UL << BM_RTREE_LEVEL_SHIFT) - 1)
 
-/*
- *     alloc_rtree_node - Allocate a new node and add it to the radix tree.
+/**
+ * alloc_rtree_node - Allocate a new node and add it to the radix tree.
  *
- *     This function is used to allocate inner nodes as well as the
- *     leave nodes of the radix tree. It also adds the node to the
- *     corresponding linked list passed in by the *list parameter.
+ * This function is used to allocate inner nodes as well as the
+ * leave nodes of the radix tree. It also adds the node to the
+ * corresponding linked list passed in by the *list parameter.
  */
 static struct rtree_node *alloc_rtree_node(gfp_t gfp_mask, int safe_needed,
                                           struct chain_allocator *ca,
@@ -347,12 +416,12 @@ static struct rtree_node *alloc_rtree_node(gfp_t gfp_mask, int safe_needed,
        return node;
 }
 
-/*
- *     add_rtree_block - Add a new leave node to the radix tree
+/**
+ * add_rtree_block - Add a new leave node to the radix tree.
  *
- *     The leave nodes need to be allocated in order to keep the leaves
- *     linked list in order. This is guaranteed by the zone->blocks
- *     counter.
+ * The leave nodes need to be allocated in order to keep the leaves
+ * linked list in order. This is guaranteed by the zone->blocks
+ * counter.
  */
 static int add_rtree_block(struct mem_zone_bm_rtree *zone, gfp_t gfp_mask,
                           int safe_needed, struct chain_allocator *ca)
@@ -417,17 +486,18 @@ static int add_rtree_block(struct mem_zone_bm_rtree *zone, gfp_t gfp_mask,
 static void free_zone_bm_rtree(struct mem_zone_bm_rtree *zone,
                               int clear_nosave_free);
 
-/*
- *     create_zone_bm_rtree - create a radix tree for one zone
+/**
+ * create_zone_bm_rtree - Create a radix tree for one zone.
  *
- *     Allocated the mem_zone_bm_rtree structure and initializes it.
- *     This function also allocated and builds the radix tree for the
- *     zone.
+ * Allocated the mem_zone_bm_rtree structure and initializes it.
+ * This function also allocated and builds the radix tree for the
+ * zone.
  */
-static struct mem_zone_bm_rtree *
-create_zone_bm_rtree(gfp_t gfp_mask, int safe_needed,
-                    struct chain_allocator *ca,
-                    unsigned long start, unsigned long end)
+static struct mem_zone_bm_rtree *create_zone_bm_rtree(gfp_t gfp_mask,
+                                                     int safe_needed,
+                                                     struct chain_allocator *ca,
+                                                     unsigned long start,
+                                                     unsigned long end)
 {
        struct mem_zone_bm_rtree *zone;
        unsigned int i, nr_blocks;
@@ -454,12 +524,12 @@ create_zone_bm_rtree(gfp_t gfp_mask, int safe_needed,
        return zone;
 }
 
-/*
- *     free_zone_bm_rtree - Free the memory of the radix tree
+/**
+ * free_zone_bm_rtree - Free the memory of the radix tree.
  *
- *     Free all node pages of the radix tree. The mem_zone_bm_rtree
- *     structure itself is not freed here nor are the rtree_node
- *     structs.
+ * Free all node pages of the radix tree. The mem_zone_bm_rtree
+ * structure itself is not freed here nor are the rtree_node
+ * structs.
  */
 static void free_zone_bm_rtree(struct mem_zone_bm_rtree *zone,
                               int clear_nosave_free)
@@ -492,8 +562,8 @@ struct mem_extent {
 };
 
 /**
- *     free_mem_extents - free a list of memory extents
- *     @list - list of extents to empty
+ * free_mem_extents - Free a list of memory extents.
+ * @list: List of extents to free.
  */
 static void free_mem_extents(struct list_head *list)
 {
@@ -506,10 +576,11 @@ static void free_mem_extents(struct list_head *list)
 }
 
 /**
- *     create_mem_extents - create a list of memory extents representing
- *                          contiguous ranges of PFNs
- *     @list - list to put the extents into
- *     @gfp_mask - mask to use for memory allocations
+ * create_mem_extents - Create a list of memory extents.
+ * @list: List to put the extents into.
+ * @gfp_mask: Mask to use for memory allocations.
+ *
+ * The extents represent contiguous ranges of PFNs.
  */
 static int create_mem_extents(struct list_head *list, gfp_t gfp_mask)
 {
@@ -565,10 +636,10 @@ static int create_mem_extents(struct list_head *list, gfp_t gfp_mask)
 }
 
 /**
-  *    memory_bm_create - allocate memory for a memory bitmap
 */
-static int
-memory_bm_create(struct memory_bitmap *bm, gfp_t gfp_mask, int safe_needed)
+ * memory_bm_create - Allocate memory for a memory bitmap.
+ */
+static int memory_bm_create(struct memory_bitmap *bm, gfp_t gfp_mask,
+                           int safe_needed)
 {
        struct chain_allocator ca;
        struct list_head mem_extents;
@@ -607,8 +678,9 @@ memory_bm_create(struct memory_bitmap *bm, gfp_t gfp_mask, int safe_needed)
 }
 
 /**
-  *    memory_bm_free - free memory occupied by the memory bitmap @bm
-  */
+ * memory_bm_free - Free memory occupied by the memory bitmap.
+ * @bm: Memory bitmap.
+ */
 static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free)
 {
        struct mem_zone_bm_rtree *zone;
@@ -622,14 +694,13 @@ static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free)
 }
 
 /**
- *     memory_bm_find_bit - Find the bit for pfn in the memory
- *                          bitmap
+ * memory_bm_find_bit - Find the bit for a given PFN in a memory bitmap.
  *
- *     Find the bit in the bitmap @bm that corresponds to given pfn.
- *     The cur.zone, cur.block and cur.node_pfn member of @bm are
- *     updated.
- *     It walks the radix tree to find the page which contains the bit for
- *     pfn and returns the bit position in **addr and *bit_nr.
+ * Find the bit in memory bitmap @bm that corresponds to the given PFN.
+ * The cur.zone, cur.block and cur.node_pfn members of @bm are updated.
+ *
+ * Walk the radix tree to find the page containing the bit that represents @pfn
+ * and return the position of the bit in @addr and @bit_nr.
  */
 static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn,
                              void **addr, unsigned int *bit_nr)
@@ -658,10 +729,9 @@ static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn,
 
 zone_found:
        /*
-        * We have a zone. Now walk the radix tree to find the leave
-        * node for our pfn.
+        * We have found the zone. Now walk the radix tree to find the leaf node
+        * for our PFN.
         */
-
        node = bm->cur.node;
        if (((pfn - zone->start_pfn) & ~BM_BLOCK_MASK) == bm->cur.node_pfn)
                goto node_found;
@@ -754,14 +824,14 @@ static bool memory_bm_pfn_present(struct memory_bitmap *bm, unsigned long pfn)
 }
 
 /*
- *     rtree_next_node - Jumps to the next leave node
+ * rtree_next_node - Jump to the next leaf node.
  *
- *     Sets the position to the beginning of the next node in the
- *     memory bitmap. This is either the next node in the current
- *     zone's radix tree or the first node in the radix tree of the
- *     next zone.
+ * Set the position to the beginning of the next node in the
+ * memory bitmap. This is either the next node in the current
+ * zone's radix tree or the first node in the radix tree of the
+ * next zone.
  *
- *     Returns true if there is a next node, false otherwise.
+ * Return true if there is a next node, false otherwise.
  */
 static bool rtree_next_node(struct memory_bitmap *bm)
 {
@@ -790,14 +860,15 @@ static bool rtree_next_node(struct memory_bitmap *bm)
 }
 
 /**
- *     memory_bm_rtree_next_pfn - Find the next set bit in the bitmap @bm
+ * memory_bm_rtree_next_pfn - Find the next set bit in a memory bitmap.
+ * @bm: Memory bitmap.
  *
- *     Starting from the last returned position this function searches
- *     for the next set bit in the memory bitmap and returns its
- *     number. If no more bit is set BM_END_OF_MAP is returned.
+ * Starting from the last returned position this function searches for the next
+ * set bit in @bm and returns the PFN represented by it.  If no more bits are
+ * set, BM_END_OF_MAP is returned.
  *
- *     It is required to run memory_bm_position_reset() before the
- *     first call to this function.
+ * It is required to run memory_bm_position_reset() before the first call to
+ * this function for the given memory bitmap.
  */
 static unsigned long memory_bm_next_pfn(struct memory_bitmap *bm)
 {
@@ -819,11 +890,10 @@ static unsigned long memory_bm_next_pfn(struct memory_bitmap *bm)
        return BM_END_OF_MAP;
 }
 
-/**
- *     This structure represents a range of page frames the contents of which
- *     should not be saved during the suspend.
+/*
+ * This structure represents a range of page frames the contents of which
+ * should not be saved during hibernation.
  */
-
 struct nosave_region {
        struct list_head list;
        unsigned long start_pfn;
@@ -832,15 +902,42 @@ struct nosave_region {
 
 static LIST_HEAD(nosave_regions);
 
+static void recycle_zone_bm_rtree(struct mem_zone_bm_rtree *zone)
+{
+       struct rtree_node *node;
+
+       list_for_each_entry(node, &zone->nodes, list)
+               recycle_safe_page(node->data);
+
+       list_for_each_entry(node, &zone->leaves, list)
+               recycle_safe_page(node->data);
+}
+
+static void memory_bm_recycle(struct memory_bitmap *bm)
+{
+       struct mem_zone_bm_rtree *zone;
+       struct linked_page *p_list;
+
+       list_for_each_entry(zone, &bm->zones, list)
+               recycle_zone_bm_rtree(zone);
+
+       p_list = bm->p_list;
+       while (p_list) {
+               struct linked_page *lp = p_list;
+
+               p_list = lp->next;
+               recycle_safe_page(lp);
+       }
+}
+
 /**
- *     register_nosave_region - register a range of page frames the contents
- *     of which should not be saved during the suspend (to be used in the early
- *     initialization code)
+ * register_nosave_region - Register a region of unsaveable memory.
+ *
+ * Register a range of page frames the contents of which should not be saved
+ * during hibernation (to be used in the early initialization code).
  */
-
-void __init
-__register_nosave_region(unsigned long start_pfn, unsigned long end_pfn,
-                        int use_kmalloc)
+void __init __register_nosave_region(unsigned long start_pfn,
+                                    unsigned long end_pfn, int use_kmalloc)
 {
        struct nosave_region *region;
 
@@ -857,12 +954,13 @@ __register_nosave_region(unsigned long start_pfn, unsigned long end_pfn,
                }
        }
        if (use_kmalloc) {
-               /* during init, this shouldn't fail */
+               /* During init, this shouldn't fail */
                region = kmalloc(sizeof(struct nosave_region), GFP_KERNEL);
                BUG_ON(!region);
-       } else
+       } else {
                /* This allocation cannot fail */
                region = memblock_virt_alloc(sizeof(struct nosave_region), 0);
+       }
        region->start_pfn = start_pfn;
        region->end_pfn = end_pfn;
        list_add_tail(&region->list, &nosave_regions);
@@ -923,10 +1021,12 @@ static void swsusp_unset_page_forbidden(struct page *page)
 }
 
 /**
- *     mark_nosave_pages - set bits corresponding to the page frames the
- *     contents of which should not be saved in a given bitmap.
+ * mark_nosave_pages - Mark pages that should not be saved.
+ * @bm: Memory bitmap.
+ *
+ * Set the bits in @bm that correspond to the page frames the contents of which
+ * should not be saved.
  */
-
 static void mark_nosave_pages(struct memory_bitmap *bm)
 {
        struct nosave_region *region;
@@ -956,13 +1056,13 @@ static void mark_nosave_pages(struct memory_bitmap *bm)
 }
 
 /**
- *     create_basic_memory_bitmaps - create bitmaps needed for marking page
- *     frames that should not be saved and free page frames.  The pointers
- *     forbidden_pages_map and free_pages_map are only modified if everything
- *     goes well, because we don't want the bits to be used before both bitmaps
- *     are set up.
+ * create_basic_memory_bitmaps - Create bitmaps to hold basic page information.
+ *
+ * Create bitmaps needed for marking page frames that should not be saved and
+ * free page frames.  The forbidden_pages_map and free_pages_map pointers are
+ * only modified if everything goes well, because we don't want the bits to be
+ * touched before both bitmaps are set up.
  */
-
 int create_basic_memory_bitmaps(void)
 {
        struct memory_bitmap *bm1, *bm2;
@@ -1007,12 +1107,12 @@ int create_basic_memory_bitmaps(void)
 }
 
 /**
- *     free_basic_memory_bitmaps - free memory bitmaps allocated by
- *     create_basic_memory_bitmaps().  The auxiliary pointers are necessary
- *     so that the bitmaps themselves are not referred to while they are being
- *     freed.
+ * free_basic_memory_bitmaps - Free memory bitmaps holding basic information.
+ *
+ * Free memory bitmaps allocated by create_basic_memory_bitmaps().  The
+ * auxiliary pointers are necessary so that the bitmaps themselves are not
+ * referred to while they are being freed.
  */
-
 void free_basic_memory_bitmaps(void)
 {
        struct memory_bitmap *bm1, *bm2;
@@ -1033,11 +1133,13 @@ void free_basic_memory_bitmaps(void)
 }
 
 /**
- *     snapshot_additional_pages - estimate the number of additional pages
- *     be needed for setting up the suspend image data structures for given
- *     zone (usually the returned value is greater than the exact number)
+ * snapshot_additional_pages - Estimate the number of extra pages needed.
+ * @zone: Memory zone to carry out the computation for.
+ *
+ * Estimate the number of additional pages needed for setting up a hibernation
+ * image data structures for @zone (usually, the returned value is greater than
+ * the exact number).
  */
-
 unsigned int snapshot_additional_pages(struct zone *zone)
 {
        unsigned int rtree, nodes;
@@ -1055,10 +1157,10 @@ unsigned int snapshot_additional_pages(struct zone *zone)
 
 #ifdef CONFIG_HIGHMEM
 /**
- *     count_free_highmem_pages - compute the total number of free highmem
- *     pages, system-wide.
+ * count_free_highmem_pages - Compute the total number of free highmem pages.
+ *
+ * The returned number is system-wide.
  */
-
 static unsigned int count_free_highmem_pages(void)
 {
        struct zone *zone;
@@ -1072,11 +1174,12 @@ static unsigned int count_free_highmem_pages(void)
 }
 
 /**
- *     saveable_highmem_page - Determine whether a highmem page should be
- *     included in the suspend image.
+ * saveable_highmem_page - Check if a highmem page is saveable.
  *
- *     We should save the page if it isn't Nosave or NosaveFree, or Reserved,
- *     and it isn't a part of a free chunk of pages.
+ * Determine whether a highmem page should be included in a hibernation image.
+ *
+ * We should save the page if it isn't Nosave or NosaveFree, or Reserved,
+ * and it isn't part of a free chunk of pages.
  */
 static struct page *saveable_highmem_page(struct zone *zone, unsigned long pfn)
 {
@@ -1102,10 +1205,8 @@ static struct page *saveable_highmem_page(struct zone *zone, unsigned long pfn)
 }
 
 /**
- *     count_highmem_pages - compute the total number of saveable highmem
- *     pages.
+ * count_highmem_pages - Compute the total number of saveable highmem pages.
  */
-
 static unsigned int count_highmem_pages(void)
 {
        struct zone *zone;
@@ -1133,12 +1234,14 @@ static inline void *saveable_highmem_page(struct zone *z, unsigned long p)
 #endif /* CONFIG_HIGHMEM */
 
 /**
- *     saveable_page - Determine whether a non-highmem page should be included
- *     in the suspend image.
+ * saveable_page - Check if the given page is saveable.
  *
- *     We should save the page if it isn't Nosave, and is not in the range
- *     of pages statically defined as 'unsaveable', and it isn't a part of
- *     a free chunk of pages.
+ * Determine whether a non-highmem page should be included in a hibernation
+ * image.
+ *
+ * We should save the page if it isn't Nosave, and is not in the range
+ * of pages statically defined as 'unsaveable', and it isn't part of
+ * a free chunk of pages.
  */
 static struct page *saveable_page(struct zone *zone, unsigned long pfn)
 {
@@ -1167,10 +1270,8 @@ static struct page *saveable_page(struct zone *zone, unsigned long pfn)
 }
 
 /**
- *     count_data_pages - compute the total number of saveable non-highmem
- *     pages.
+ * count_data_pages - Compute the total number of saveable non-highmem pages.
  */
-
 static unsigned int count_data_pages(void)
 {
        struct zone *zone;
@@ -1190,7 +1291,8 @@ static unsigned int count_data_pages(void)
        return n;
 }
 
-/* This is needed, because copy_page and memcpy are not usable for copying
+/*
+ * This is needed, because copy_page and memcpy are not usable for copying
  * task structs.
  */
 static inline void do_copy_page(long *dst, long *src)
@@ -1201,12 +1303,12 @@ static inline void do_copy_page(long *dst, long *src)
                *dst++ = *src++;
 }
 
-
 /**
- *     safe_copy_page - check if the page we are going to copy is marked as
- *             present in the kernel page tables (this always is the case if
- *             CONFIG_DEBUG_PAGEALLOC is not set and in that case
- *             kernel_page_present() always returns 'true').
+ * safe_copy_page - Copy a page in a safe way.
+ *
+ * Check if the page we are going to copy is marked as present in the kernel
+ * page tables (this always is the case if CONFIG_DEBUG_PAGEALLOC is not set
+ * and in that case kernel_page_present() always returns 'true').
  */
 static void safe_copy_page(void *dst, struct page *s_page)
 {
@@ -1219,10 +1321,8 @@ static void safe_copy_page(void *dst, struct page *s_page)
        }
 }
 
-
 #ifdef CONFIG_HIGHMEM
-static inline struct page *
-page_is_saveable(struct zone *zone, unsigned long pfn)
+static inline struct page *page_is_saveable(struct zone *zone, unsigned long pfn)
 {
        return is_highmem(zone) ?
                saveable_highmem_page(zone, pfn) : saveable_page(zone, pfn);
@@ -1243,7 +1343,8 @@ static void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
                kunmap_atomic(src);
        } else {
                if (PageHighMem(d_page)) {
-                       /* Page pointed to by src may contain some kernel
+                       /*
+                        * The page pointed to by src may contain some kernel
                         * data modified by kmap_atomic()
                         */
                        safe_copy_page(buffer, s_page);
@@ -1265,8 +1366,8 @@ static inline void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
 }
 #endif /* CONFIG_HIGHMEM */
 
-static void
-copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
+static void copy_data_pages(struct memory_bitmap *copy_bm,
+                           struct memory_bitmap *orig_bm)
 {
        struct zone *zone;
        unsigned long pfn;
@@ -1315,12 +1416,11 @@ static struct memory_bitmap orig_bm;
 static struct memory_bitmap copy_bm;
 
 /**
- *     swsusp_free - free pages allocated for the suspend.
+ * swsusp_free - Free pages allocated for hibernation image.
  *
- *     Suspend pages are alocated before the atomic copy is made, so we
- *     need to release them after the resume.
+ * Image pages are alocated before snapshot creation, so they need to be
+ * released after resume.
  */
-
 void swsusp_free(void)
 {
        unsigned long fb_pfn, fr_pfn;
@@ -1351,6 +1451,7 @@ loop:
 
                memory_bm_clear_current(forbidden_pages_map);
                memory_bm_clear_current(free_pages_map);
+               hibernate_restore_unprotect_page(page_address(page));
                __free_page(page);
                goto loop;
        }
@@ -1362,6 +1463,7 @@ out:
        buffer = NULL;
        alloc_normal = 0;
        alloc_highmem = 0;
+       hibernate_restore_protection_end();
 }
 
 /* Helper functions used for the shrinking of memory. */
@@ -1369,7 +1471,7 @@ out:
 #define GFP_IMAGE      (GFP_KERNEL | __GFP_NOWARN)
 
 /**
- * preallocate_image_pages - Allocate a number of pages for hibernation image
+ * preallocate_image_pages - Allocate a number of pages for hibernation image.
  * @nr_pages: Number of page frames to allocate.
  * @mask: GFP flags to use for the allocation.
  *
@@ -1419,7 +1521,7 @@ static unsigned long preallocate_image_highmem(unsigned long nr_pages)
 }
 
 /**
- *  __fraction - Compute (an approximation of) x * (multiplier / base)
+ *  __fraction - Compute (an approximation of) x * (multiplier / base).
  */
 static unsigned long __fraction(u64 x, u64 multiplier, u64 base)
 {
@@ -1429,8 +1531,8 @@ static unsigned long __fraction(u64 x, u64 multiplier, u64 base)
 }
 
 static unsigned long preallocate_highmem_fraction(unsigned long nr_pages,
-                                               unsigned long highmem,
-                                               unsigned long total)
+                                                 unsigned long highmem,
+                                                 unsigned long total)
 {
        unsigned long alloc = __fraction(nr_pages, highmem, total);
 
@@ -1443,15 +1545,15 @@ static inline unsigned long preallocate_image_highmem(unsigned long nr_pages)
 }
 
 static inline unsigned long preallocate_highmem_fraction(unsigned long nr_pages,
-                                               unsigned long highmem,
-                                               unsigned long total)
+                                                        unsigned long highmem,
+                                                        unsigned long total)
 {
        return 0;
 }
 #endif /* CONFIG_HIGHMEM */
 
 /**
- * free_unnecessary_pages - Release preallocated pages not needed for the image
+ * free_unnecessary_pages - Release preallocated pages not needed for the image.
  */
 static unsigned long free_unnecessary_pages(void)
 {
@@ -1505,7 +1607,7 @@ static unsigned long free_unnecessary_pages(void)
 }
 
 /**
- * minimum_image_size - Estimate the minimum acceptable size of an image
+ * minimum_image_size - Estimate the minimum acceptable size of an image.
  * @saveable: Number of saveable pages in the system.
  *
  * We want to avoid attempting to free too much memory too hard, so estimate the
@@ -1535,7 +1637,7 @@ static unsigned long minimum_image_size(unsigned long saveable)
 }
 
 /**
- * hibernate_preallocate_memory - Preallocate memory for hibernation image
+ * hibernate_preallocate_memory - Preallocate memory for hibernation image.
  *
  * To create a hibernation image it is necessary to make a copy of every page
  * frame in use.  We also need a number of page frames to be free during
@@ -1708,10 +1810,11 @@ int hibernate_preallocate_memory(void)
 
 #ifdef CONFIG_HIGHMEM
 /**
-  *    count_pages_for_highmem - compute the number of non-highmem pages
-  *    that will be necessary for creating copies of highmem pages.
-  */
-
+ * count_pages_for_highmem - Count non-highmem pages needed for copying highmem.
+ *
+ * Compute the number of non-highmem pages that will be necessary for creating
+ * copies of highmem pages.
+ */
 static unsigned int count_pages_for_highmem(unsigned int nr_highmem)
 {
        unsigned int free_highmem = count_free_highmem_pages() + alloc_highmem;
@@ -1724,15 +1827,12 @@ static unsigned int count_pages_for_highmem(unsigned int nr_highmem)
        return nr_highmem;
 }
 #else
-static unsigned int
-count_pages_for_highmem(unsigned int nr_highmem) { return 0; }
+static unsigned int count_pages_for_highmem(unsigned int nr_highmem) { return 0; }
 #endif /* CONFIG_HIGHMEM */
 
 /**
- *     enough_free_mem - Make sure we have enough free memory for the
- *     snapshot image.
+ * enough_free_mem - Check if there is enough free memory for the image.
  */
-
 static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem)
 {
        struct zone *zone;
@@ -1751,10 +1851,11 @@ static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem)
 
 #ifdef CONFIG_HIGHMEM
 /**
- *     get_highmem_buffer - if there are some highmem pages in the suspend
- *     image, we may need the buffer to copy them and/or load their data.
+ * get_highmem_buffer - Allocate a buffer for highmem pages.
+ *
+ * If there are some highmem pages in the hibernation image, we may need a
+ * buffer to copy them and/or load their data.
  */
-
 static inline int get_highmem_buffer(int safe_needed)
 {
        buffer = get_image_page(GFP_ATOMIC | __GFP_COLD, safe_needed);
@@ -1762,13 +1863,13 @@ static inline int get_highmem_buffer(int safe_needed)
 }
 
 /**
- *     alloc_highmem_image_pages - allocate some highmem pages for the image.
- *     Try to allocate as many pages as needed, but if the number of free
- *     highmem pages is lesser than that, allocate them all.
+ * alloc_highmem_image_pages - Allocate some highmem pages for the image.
+ *
+ * Try to allocate as many pages as needed, but if the number of free highmem
+ * pages is less than that, allocate them all.
  */
-
-static inline unsigned int
-alloc_highmem_pages(struct memory_bitmap *bm, unsigned int nr_highmem)
+static inline unsigned int alloc_highmem_pages(struct memory_bitmap *bm,
+                                              unsigned int nr_highmem)
 {
        unsigned int to_alloc = count_free_highmem_pages();
 
@@ -1787,25 +1888,24 @@ alloc_highmem_pages(struct memory_bitmap *bm, unsigned int nr_highmem)
 #else
 static inline int get_highmem_buffer(int safe_needed) { return 0; }
 
-static inline unsigned int
-alloc_highmem_pages(struct memory_bitmap *bm, unsigned int n) { return 0; }
+static inline unsigned int alloc_highmem_pages(struct memory_bitmap *bm,
+                                              unsigned int n) { return 0; }
 #endif /* CONFIG_HIGHMEM */
 
 /**
- *     swsusp_alloc - allocate memory for the suspend image
+ * swsusp_alloc - Allocate memory for hibernation image.
  *
- *     We first try to allocate as many highmem pages as there are
- *     saveable highmem pages in the system.  If that fails, we allocate
- *     non-highmem pages for the copies of the remaining highmem ones.
+ * We first try to allocate as many highmem pages as there are
+ * saveable highmem pages in the system.  If that fails, we allocate
+ * non-highmem pages for the copies of the remaining highmem ones.
  *
- *     In this approach it is likely that the copies of highmem pages will
- *     also be located in the high memory, because of the way in which
- *     copy_data_pages() works.
+ * In this approach it is likely that the copies of highmem pages will
+ * also be located in the high memory, because of the way in which
+ * copy_data_pages() works.
  */
-
-static int
-swsusp_alloc(struct memory_bitmap *orig_bm, struct memory_bitmap *copy_bm,
-               unsigned int nr_pages, unsigned int nr_highmem)
+static int swsusp_alloc(struct memory_bitmap *orig_bm,
+                       struct memory_bitmap *copy_bm,
+                       unsigned int nr_pages, unsigned int nr_highmem)
 {
        if (nr_highmem > 0) {
                if (get_highmem_buffer(PG_ANY))
@@ -1855,7 +1955,8 @@ asmlinkage __visible int swsusp_save(void)
                return -ENOMEM;
        }
 
-       /* During allocating of suspend pagedir, new cold pages may appear.
+       /*
+        * During allocating of suspend pagedir, new cold pages may appear.
         * Kill them.
         */
        drain_local_pages(NULL);
@@ -1918,12 +2019,14 @@ static int init_header(struct swsusp_info *info)
 }
 
 /**
- *     pack_pfns - pfns corresponding to the set bits found in the bitmap @bm
- *     are stored in the array @buf[] (1 page at a time)
+ * pack_pfns - Prepare PFNs for saving.
+ * @bm: Memory bitmap.
+ * @buf: Memory buffer to store the PFNs in.
+ *
+ * PFNs corresponding to set bits in @bm are stored in the area of memory
+ * pointed to by @buf (1 page at a time).
  */
-
-static inline void
-pack_pfns(unsigned long *buf, struct memory_bitmap *bm)
+static inline void pack_pfns(unsigned long *buf, struct memory_bitmap *bm)
 {
        int j;
 
@@ -1937,22 +2040,21 @@ pack_pfns(unsigned long *buf, struct memory_bitmap *bm)
 }
 
 /**
- *     snapshot_read_next - used for reading the system memory snapshot.
+ * snapshot_read_next - Get the address to read the next image page from.
+ * @handle: Snapshot handle to be used for the reading.
  *
- *     On the first call to it @handle should point to a zeroed
- *     snapshot_handle structure.  The structure gets updated and a pointer
- *     to it should be passed to this function every next time.
+ * On the first call, @handle should point to a zeroed snapshot_handle
+ * structure.  The structure gets populated then and a pointer to it should be
+ * passed to this function every next time.
  *
- *     On success the function returns a positive number.  Then, the caller
- *     is allowed to read up to the returned number of bytes from the memory
- *     location computed by the data_of() macro.
+ * On success, the function returns a positive number.  Then, the caller
+ * is allowed to read up to the returned number of bytes from the memory
+ * location computed by the data_of() macro.
  *
- *     The function returns 0 to indicate the end of data stream condition,
- *     and a negative number is returned on error.  In such cases the
- *     structure pointed to by @handle is not updated and should not be used
- *     any more.
+ * The function returns 0 to indicate the end of the data stream condition,
+ * and negative numbers are returned on errors.  If that happens, the structure
+ * pointed to by @handle is not updated and should not be used any more.
  */
-
 int snapshot_read_next(struct snapshot_handle *handle)
 {
        if (handle->cur > nr_meta_pages + nr_copy_pages)
@@ -1981,7 +2083,8 @@ int snapshot_read_next(struct snapshot_handle *handle)
 
                page = pfn_to_page(memory_bm_next_pfn(&copy_bm));
                if (PageHighMem(page)) {
-                       /* Highmem pages are copied to the buffer,
+                       /*
+                        * Highmem pages are copied to the buffer,
                         * because we can't return with a kmapped
                         * highmem page (we may not be called again).
                         */
@@ -1999,53 +2102,41 @@ int snapshot_read_next(struct snapshot_handle *handle)
        return PAGE_SIZE;
 }
 
-/**
- *     mark_unsafe_pages - mark the pages that cannot be used for storing
- *     the image during resume, because they conflict with the pages that
- *     had been used before suspend
- */
-
-static int mark_unsafe_pages(struct memory_bitmap *bm)
+static void duplicate_memory_bitmap(struct memory_bitmap *dst,
+                                   struct memory_bitmap *src)
 {
-       struct zone *zone;
-       unsigned long pfn, max_zone_pfn;
+       unsigned long pfn;
 
-       /* Clear page flags */
-       for_each_populated_zone(zone) {
-               max_zone_pfn = zone_end_pfn(zone);
-               for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
-                       if (pfn_valid(pfn))
-                               swsusp_unset_page_free(pfn_to_page(pfn));
+       memory_bm_position_reset(src);
+       pfn = memory_bm_next_pfn(src);
+       while (pfn != BM_END_OF_MAP) {
+               memory_bm_set_bit(dst, pfn);
+               pfn = memory_bm_next_pfn(src);
        }
-
-       /* Mark pages that correspond to the "original" pfns as "unsafe" */
-       memory_bm_position_reset(bm);
-       do {
-               pfn = memory_bm_next_pfn(bm);
-               if (likely(pfn != BM_END_OF_MAP)) {
-                       if (likely(pfn_valid(pfn)))
-                               swsusp_set_page_free(pfn_to_page(pfn));
-                       else
-                               return -EFAULT;
-               }
-       } while (pfn != BM_END_OF_MAP);
-
-       allocated_unsafe_pages = 0;
-
-       return 0;
 }
 
-static void
-duplicate_memory_bitmap(struct memory_bitmap *dst, struct memory_bitmap *src)
+/**
+ * mark_unsafe_pages - Mark pages that were used before hibernation.
+ *
+ * Mark the pages that cannot be used for storing the image during restoration,
+ * because they conflict with the pages that had been used before hibernation.
+ */
+static void mark_unsafe_pages(struct memory_bitmap *bm)
 {
        unsigned long pfn;
 
-       memory_bm_position_reset(src);
-       pfn = memory_bm_next_pfn(src);
+       /* Clear the "free"/"unsafe" bit for all PFNs */
+       memory_bm_position_reset(free_pages_map);
+       pfn = memory_bm_next_pfn(free_pages_map);
        while (pfn != BM_END_OF_MAP) {
-               memory_bm_set_bit(dst, pfn);
-               pfn = memory_bm_next_pfn(src);
+               memory_bm_clear_current(free_pages_map);
+               pfn = memory_bm_next_pfn(free_pages_map);
        }
+
+       /* Mark pages that correspond to the "original" PFNs as "unsafe" */
+       duplicate_memory_bitmap(free_pages_map, bm);
+
+       allocated_unsafe_pages = 0;
 }
 
 static int check_header(struct swsusp_info *info)
@@ -2063,11 +2154,9 @@ static int check_header(struct swsusp_info *info)
 }
 
 /**
- *     load header - check the image header and copy data from it
+ * load header - Check the image header and copy the data from it.
  */
-
-static int
-load_header(struct swsusp_info *info)
+static int load_header(struct swsusp_info *info)
 {
        int error;
 
@@ -2081,8 +2170,12 @@ load_header(struct swsusp_info *info)
 }
 
 /**
- *     unpack_orig_pfns - for each element of @buf[] (1 page at a time) set
- *     the corresponding bit in the memory bitmap @bm
+ * unpack_orig_pfns - Set bits corresponding to given PFNs in a memory bitmap.
+ * @bm: Memory bitmap.
+ * @buf: Area of memory containing the PFNs.
+ *
+ * For each element of the array pointed to by @buf (1 page at a time), set the
+ * corresponding bit in @bm.
  */
 static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm)
 {
@@ -2095,7 +2188,7 @@ static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm)
                /* Extract and buffer page key for data page (s390 only). */
                page_key_memorize(buf + j);
 
-               if (memory_bm_pfn_present(bm, buf[j]))
+               if (pfn_valid(buf[j]) && memory_bm_pfn_present(bm, buf[j]))
                        memory_bm_set_bit(bm, buf[j]);
                else
                        return -EFAULT;
@@ -2104,13 +2197,9 @@ static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm)
        return 0;
 }
 
-/* List of "safe" pages that may be used to store data loaded from the suspend
- * image
- */
-static struct linked_page *safe_pages_list;
-
 #ifdef CONFIG_HIGHMEM
-/* struct highmem_pbe is used for creating the list of highmem pages that
+/*
+ * struct highmem_pbe is used for creating the list of highmem pages that
  * should be restored atomically during the resume from disk, because the page
  * frames they have occupied before the suspend are in use.
  */
@@ -2120,7 +2209,8 @@ struct highmem_pbe {
        struct highmem_pbe *next;
 };
 
-/* List of highmem PBEs needed for restoring the highmem pages that were
+/*
+ * List of highmem PBEs needed for restoring the highmem pages that were
  * allocated before the suspend and included in the suspend image, but have
  * also been allocated by the "resume" kernel, so their contents cannot be
  * written directly to their "original" page frames.
@@ -2128,11 +2218,11 @@ struct highmem_pbe {
 static struct highmem_pbe *highmem_pblist;
 
 /**
- *     count_highmem_image_pages - compute the number of highmem pages in the
- *     suspend image.  The bits in the memory bitmap @bm that correspond to the
- *     image pages are assumed to be set.
+ * count_highmem_image_pages - Compute the number of highmem pages in the image.
+ * @bm: Memory bitmap.
+ *
+ * The bits in @bm that correspond to image pages are assumed to be set.
  */
-
 static unsigned int count_highmem_image_pages(struct memory_bitmap *bm)
 {
        unsigned long pfn;
@@ -2149,24 +2239,25 @@ static unsigned int count_highmem_image_pages(struct memory_bitmap *bm)
        return cnt;
 }
 
-/**
- *     prepare_highmem_image - try to allocate as many highmem pages as
- *     there are highmem image pages (@nr_highmem_p points to the variable
- *     containing the number of highmem image pages).  The pages that are
- *     "safe" (ie. will not be overwritten when the suspend image is
- *     restored) have the corresponding bits set in @bm (it must be
- *     unitialized).
- *
- *     NOTE: This function should not be called if there are no highmem
- *     image pages.
- */
-
 static unsigned int safe_highmem_pages;
 
 static struct memory_bitmap *safe_highmem_bm;
 
-static int
-prepare_highmem_image(struct memory_bitmap *bm, unsigned int *nr_highmem_p)
+/**
+ * prepare_highmem_image - Allocate memory for loading highmem data from image.
+ * @bm: Pointer to an uninitialized memory bitmap structure.
+ * @nr_highmem_p: Pointer to the number of highmem image pages.
+ *
+ * Try to allocate as many highmem pages as there are highmem image pages
+ * (@nr_highmem_p points to the variable containing the number of highmem image
+ * pages).  The pages that are "safe" (ie. will not be overwritten when the
+ * hibernation image is restored entirely) have the corresponding bits set in
+ * @bm (it must be unitialized).
+ *
+ * NOTE: This function should not be called if there are no highmem image pages.
+ */
+static int prepare_highmem_image(struct memory_bitmap *bm,
+                                unsigned int *nr_highmem_p)
 {
        unsigned int to_alloc;
 
@@ -2201,39 +2292,42 @@ prepare_highmem_image(struct memory_bitmap *bm, unsigned int *nr_highmem_p)
        return 0;
 }
 
+static struct page *last_highmem_page;
+
 /**
- *     get_highmem_page_buffer - for given highmem image page find the buffer
- *     that suspend_write_next() should set for its caller to write to.
+ * get_highmem_page_buffer - Prepare a buffer to store a highmem image page.
  *
- *     If the page is to be saved to its "original" page frame or a copy of
- *     the page is to be made in the highmem, @buffer is returned.  Otherwise,
- *     the copy of the page is to be made in normal memory, so the address of
- *     the copy is returned.
+ * For a given highmem image page get a buffer that suspend_write_next() should
+ * return to its caller to write to.
  *
- *     If @buffer is returned, the caller of suspend_write_next() will write
- *     the page's contents to @buffer, so they will have to be copied to the
- *     right location on the next call to suspend_write_next() and it is done
- *     with the help of copy_last_highmem_page().  For this purpose, if
- *     @buffer is returned, @last_highmem page is set to the page to which
- *     the data will have to be copied from @buffer.
+ * If the page is to be saved to its "original" page frame or a copy of
+ * the page is to be made in the highmem, @buffer is returned.  Otherwise,
+ * the copy of the page is to be made in normal memory, so the address of
+ * the copy is returned.
+ *
+ * If @buffer is returned, the caller of suspend_write_next() will write
+ * the page's contents to @buffer, so they will have to be copied to the
+ * right location on the next call to suspend_write_next() and it is done
+ * with the help of copy_last_highmem_page().  For this purpose, if
+ * @buffer is returned, @last_highmem_page is set to the page to which
+ * the data will have to be copied from @buffer.
  */
-
-static struct page *last_highmem_page;
-
-static void *
-get_highmem_page_buffer(struct page *page, struct chain_allocator *ca)
+static void *get_highmem_page_buffer(struct page *page,
+                                    struct chain_allocator *ca)
 {
        struct highmem_pbe *pbe;
        void *kaddr;
 
        if (swsusp_page_is_forbidden(page) && swsusp_page_is_free(page)) {
-               /* We have allocated the "original" page frame and we can
+               /*
+                * We have allocated the "original" page frame and we can
                 * use it directly to store the loaded page.
                 */
                last_highmem_page = page;
                return buffer;
        }
-       /* The "original" page frame has not been allocated and we have to
+       /*
+        * The "original" page frame has not been allocated and we have to
         * use a "safe" page frame to store the loaded page.
         */
        pbe = chain_alloc(ca, sizeof(struct highmem_pbe));
@@ -2263,11 +2357,12 @@ get_highmem_page_buffer(struct page *page, struct chain_allocator *ca)
 }
 
 /**
- *     copy_last_highmem_page - copy the contents of a highmem image from
- *     @buffer, where the caller of snapshot_write_next() has place them,
- *     to the right location represented by @last_highmem_page .
+ * copy_last_highmem_page - Copy most the most recent highmem image page.
+ *
+ * Copy the contents of a highmem image from @buffer, where the caller of
+ * snapshot_write_next() has stored them, to the right location represented by
+ * @last_highmem_page .
  */
-
 static void copy_last_highmem_page(void)
 {
        if (last_highmem_page) {
@@ -2294,17 +2389,13 @@ static inline void free_highmem_data(void)
                free_image_page(buffer, PG_UNSAFE_CLEAR);
 }
 #else
-static unsigned int
-count_highmem_image_pages(struct memory_bitmap *bm) { return 0; }
+static unsigned int count_highmem_image_pages(struct memory_bitmap *bm) { return 0; }
 
-static inline int
-prepare_highmem_image(struct memory_bitmap *bm, unsigned int *nr_highmem_p)
-{
-       return 0;
-}
+static inline int prepare_highmem_image(struct memory_bitmap *bm,
+                                       unsigned int *nr_highmem_p) { return 0; }
 
-static inline void *
-get_highmem_page_buffer(struct page *page, struct chain_allocator *ca)
+static inline void *get_highmem_page_buffer(struct page *page,
+                                           struct chain_allocator *ca)
 {
        return ERR_PTR(-EINVAL);
 }
@@ -2314,27 +2405,27 @@ static inline int last_highmem_page_copied(void) { return 1; }
 static inline void free_highmem_data(void) {}
 #endif /* CONFIG_HIGHMEM */
 
+#define PBES_PER_LINKED_PAGE   (LINKED_PAGE_DATA_SIZE / sizeof(struct pbe))
+
 /**
- *     prepare_image - use the memory bitmap @bm to mark the pages that will
- *     be overwritten in the process of restoring the system memory state
- *     from the suspend image ("unsafe" pages) and allocate memory for the
- *     image.
+ * prepare_image - Make room for loading hibernation image.
+ * @new_bm: Unitialized memory bitmap structure.
+ * @bm: Memory bitmap with unsafe pages marked.
+ *
+ * Use @bm to mark the pages that will be overwritten in the process of
+ * restoring the system memory state from the suspend image ("unsafe" pages)
+ * and allocate memory for the image.
  *
- *     The idea is to allocate a new memory bitmap first and then allocate
- *     as many pages as needed for the image data, but not to assign these
- *     pages to specific tasks initially.  Instead, we just mark them as
- *     allocated and create a lists of "safe" pages that will be used
- *     later.  On systems with high memory a list of "safe" highmem pages is
- *     also created.
+ * The idea is to allocate a new memory bitmap first and then allocate
+ * as many pages as needed for image data, but without specifying what those
+ * pages will be used for just yet.  Instead, we mark them all as allocated and
+ * create a lists of "safe" pages to be used later.  On systems with high
+ * memory a list of "safe" highmem pages is created too.
  */
-
-#define PBES_PER_LINKED_PAGE   (LINKED_PAGE_DATA_SIZE / sizeof(struct pbe))
-
-static int
-prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
+static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
 {
        unsigned int nr_pages, nr_highmem;
-       struct linked_page *sp_list, *lp;
+       struct linked_page *lp;
        int error;
 
        /* If there is no highmem, the buffer will not be necessary */
@@ -2342,9 +2433,7 @@ prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
        buffer = NULL;
 
        nr_highmem = count_highmem_image_pages(bm);
-       error = mark_unsafe_pages(bm);
-       if (error)
-               goto Free;
+       mark_unsafe_pages(bm);
 
        error = memory_bm_create(new_bm, GFP_ATOMIC, PG_SAFE);
        if (error)
@@ -2357,14 +2446,15 @@ prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
                if (error)
                        goto Free;
        }
-       /* Reserve some safe pages for potential later use.
+       /*
+        * Reserve some safe pages for potential later use.
         *
         * NOTE: This way we make sure there will be enough safe pages for the
         * chain_alloc() in get_buffer().  It is a bit wasteful, but
         * nr_copy_pages cannot be greater than 50% of the memory anyway.
+        *
+        * nr_copy_pages cannot be less than allocated_unsafe_pages too.
         */
-       sp_list = NULL;
-       /* nr_copy_pages cannot be lesser than allocated_unsafe_pages */
        nr_pages = nr_copy_pages - nr_highmem - allocated_unsafe_pages;
        nr_pages = DIV_ROUND_UP(nr_pages, PBES_PER_LINKED_PAGE);
        while (nr_pages > 0) {
@@ -2373,12 +2463,11 @@ prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
                        error = -ENOMEM;
                        goto Free;
                }
-               lp->next = sp_list;
-               sp_list = lp;
+               lp->next = safe_pages_list;
+               safe_pages_list = lp;
                nr_pages--;
        }
        /* Preallocate memory for the image */
-       safe_pages_list = NULL;
        nr_pages = nr_copy_pages - nr_highmem - allocated_unsafe_pages;
        while (nr_pages > 0) {
                lp = (struct linked_page *)get_zeroed_page(GFP_ATOMIC);
@@ -2396,12 +2485,6 @@ prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
                swsusp_set_page_free(virt_to_page(lp));
                nr_pages--;
        }
-       /* Free the reserved safe pages so that chain_alloc() can use them */
-       while (sp_list) {
-               lp = sp_list->next;
-               free_image_page(sp_list, PG_UNSAFE_CLEAR);
-               sp_list = lp;
-       }
        return 0;
 
  Free:
@@ -2410,10 +2493,11 @@ prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
 }
 
 /**
- *     get_buffer - compute the address that snapshot_write_next() should
- *     set for its caller to write to.
+ * get_buffer - Get the address to store the next image data page.
+ *
+ * Get the address that snapshot_write_next() should return to its caller to
+ * write to.
  */
-
 static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
 {
        struct pbe *pbe;
@@ -2428,12 +2512,14 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
                return get_highmem_page_buffer(page, ca);
 
        if (swsusp_page_is_forbidden(page) && swsusp_page_is_free(page))
-               /* We have allocated the "original" page frame and we can
+               /*
+                * We have allocated the "original" page frame and we can
                 * use it directly to store the loaded page.
                 */
                return page_address(page);
 
-       /* The "original" page frame has not been allocated and we have to
+       /*
+        * The "original" page frame has not been allocated and we have to
         * use a "safe" page frame to store the loaded page.
         */
        pbe = chain_alloc(ca, sizeof(struct pbe));
@@ -2450,22 +2536,21 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
 }
 
 /**
- *     snapshot_write_next - used for writing the system memory snapshot.
+ * snapshot_write_next - Get the address to store the next image page.
+ * @handle: Snapshot handle structure to guide the writing.
  *
- *     On the first call to it @handle should point to a zeroed
- *     snapshot_handle structure.  The structure gets updated and a pointer
- *     to it should be passed to this function every next time.
+ * On the first call, @handle should point to a zeroed snapshot_handle
+ * structure.  The structure gets populated then and a pointer to it should be
+ * passed to this function every next time.
  *
- *     On success the function returns a positive number.  Then, the caller
- *     is allowed to write up to the returned number of bytes to the memory
- *     location computed by the data_of() macro.
+ * On success, the function returns a positive number.  Then, the caller
+ * is allowed to write up to the returned number of bytes to the memory
+ * location computed by the data_of() macro.
  *
- *     The function returns 0 to indicate the "end of file" condition,
- *     and a negative number is returned on error.  In such cases the
- *     structure pointed to by @handle is not updated and should not be used
- *     any more.
+ * The function returns 0 to indicate the "end of file" condition.  Negative
+ * numbers are returned on errors, in which cases the structure pointed to by
+ * @handle is not updated and should not be used any more.
  */
-
 int snapshot_write_next(struct snapshot_handle *handle)
 {
        static struct chain_allocator ca;
@@ -2491,6 +2576,8 @@ int snapshot_write_next(struct snapshot_handle *handle)
                if (error)
                        return error;
 
+               safe_pages_list = NULL;
+
                error = memory_bm_create(&copy_bm, GFP_ATOMIC, PG_ANY);
                if (error)
                        return error;
@@ -2500,6 +2587,7 @@ int snapshot_write_next(struct snapshot_handle *handle)
                if (error)
                        return error;
 
+               hibernate_restore_protection_begin();
        } else if (handle->cur <= nr_meta_pages + 1) {
                error = unpack_orig_pfns(buffer, &copy_bm);
                if (error)
@@ -2522,6 +2610,7 @@ int snapshot_write_next(struct snapshot_handle *handle)
                copy_last_highmem_page();
                /* Restore page key for data page (s390 only). */
                page_key_write(handle->buffer);
+               hibernate_restore_protect_page(handle->buffer);
                handle->buffer = get_buffer(&orig_bm, &ca);
                if (IS_ERR(handle->buffer))
                        return PTR_ERR(handle->buffer);
@@ -2533,22 +2622,23 @@ int snapshot_write_next(struct snapshot_handle *handle)
 }
 
 /**
- *     snapshot_write_finalize - must be called after the last call to
- *     snapshot_write_next() in case the last page in the image happens
- *     to be a highmem page and its contents should be stored in the
- *     highmem.  Additionally, it releases the memory that will not be
- *     used any more.
+ * snapshot_write_finalize - Complete the loading of a hibernation image.
+ *
+ * Must be called after the last call to snapshot_write_next() in case the last
+ * page in the image happens to be a highmem page and its contents should be
+ * stored in highmem.  Additionally, it recycles bitmap memory that's not
+ * necessary any more.
  */
-
 void snapshot_write_finalize(struct snapshot_handle *handle)
 {
        copy_last_highmem_page();
        /* Restore page key for data page (s390 only). */
        page_key_write(handle->buffer);
        page_key_free();
-       /* Free only if we have loaded the image entirely */
+       hibernate_restore_protect_page(handle->buffer);
+       /* Do that only if we have loaded the image entirely */
        if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) {
-               memory_bm_free(&orig_bm, PG_UNSAFE_CLEAR);
+               memory_bm_recycle(&orig_bm);
                free_highmem_data();
        }
 }
@@ -2561,8 +2651,8 @@ int snapshot_image_loaded(struct snapshot_handle *handle)
 
 #ifdef CONFIG_HIGHMEM
 /* Assumes that @buf is ready and points to a "safe" page */
-static inline void
-swap_two_pages_data(struct page *p1, struct page *p2, void *buf)
+static inline void swap_two_pages_data(struct page *p1, struct page *p2,
+                                      void *buf)
 {
        void *kaddr1, *kaddr2;
 
@@ -2576,15 +2666,15 @@ swap_two_pages_data(struct page *p1, struct page *p2, void *buf)
 }
 
 /**
- *     restore_highmem - for each highmem page that was allocated before
- *     the suspend and included in the suspend image, and also has been
- *     allocated by the "resume" kernel swap its current (ie. "before
- *     resume") contents with the previous (ie. "before suspend") one.
+ * restore_highmem - Put highmem image pages into their original locations.
+ *
+ * For each highmem page that was in use before hibernation and is included in
+ * the image, and also has been allocated by the "restore" kernel, swap its
+ * current contents with the previous (ie. "before hibernation") ones.
  *
- *     If the resume eventually fails, we can call this function once
- *     again and restore the "before resume" highmem state.
+ * If the restore eventually fails, we can call this function once again and
+ * restore the highmem state as seen by the restore kernel.
  */
-
 int restore_highmem(void)
 {
        struct highmem_pbe *pbe = highmem_pblist;
index 5b70d64..0acab9d 100644 (file)
@@ -266,16 +266,18 @@ static int suspend_test(int level)
  */
 static int suspend_prepare(suspend_state_t state)
 {
-       int error;
+       int error, nr_calls = 0;
 
        if (!sleep_state_supported(state))
                return -EPERM;
 
        pm_prepare_console();
 
-       error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
-       if (error)
+       error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls);
+       if (error) {
+               nr_calls--;
                goto Finish;
+       }
 
        trace_suspend_resume(TPS("freeze_processes"), 0, true);
        error = suspend_freeze_processes();
@@ -286,7 +288,7 @@ static int suspend_prepare(suspend_state_t state)
        suspend_stats.failed_freeze++;
        dpm_save_failed_step(SUSPEND_FREEZE);
  Finish:
-       pm_notifier_call_chain(PM_POST_SUSPEND);
+       __pm_notifier_call_chain(PM_POST_SUSPEND, nr_calls, NULL);
        pm_restore_console();
        return error;
 }
index 160e100..51cef84 100644 (file)
@@ -348,6 +348,12 @@ static int swsusp_swap_check(void)
        if (res < 0)
                blkdev_put(hib_resume_bdev, FMODE_WRITE);
 
+       /*
+        * Update the resume device to the one actually used,
+        * so the test_resume mode can use it in case it is
+        * invoked from hibernate() to test the snapshot.
+        */
+       swsusp_resume_device = hib_resume_bdev->bd_dev;
        return res;
 }
 
index 526e891..35310b6 100644 (file)
@@ -47,7 +47,7 @@ atomic_t snapshot_device_available = ATOMIC_INIT(1);
 static int snapshot_open(struct inode *inode, struct file *filp)
 {
        struct snapshot_data *data;
-       int error;
+       int error, nr_calls = 0;
 
        if (!hibernation_available())
                return -EPERM;
@@ -74,9 +74,9 @@ static int snapshot_open(struct inode *inode, struct file *filp)
                        swap_type_of(swsusp_resume_device, 0, NULL) : -1;
                data->mode = O_RDONLY;
                data->free_bitmaps = false;
-               error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
+               error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
                if (error)
-                       pm_notifier_call_chain(PM_POST_HIBERNATION);
+                       __pm_notifier_call_chain(PM_POST_HIBERNATION, --nr_calls, NULL);
        } else {
                /*
                 * Resuming.  We may need to wait for the image device to
@@ -86,13 +86,15 @@ static int snapshot_open(struct inode *inode, struct file *filp)
 
                data->swap = -1;
                data->mode = O_WRONLY;
-               error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
+               error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls);
                if (!error) {
                        error = create_basic_memory_bitmaps();
                        data->free_bitmaps = !error;
-               }
+               } else
+                       nr_calls--;
+
                if (error)
-                       pm_notifier_call_chain(PM_POST_RESTORE);
+                       __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
        }
        if (error)
                atomic_inc(&snapshot_device_available);
index 51d7105..97ee9ac 100644 (file)
@@ -5394,13 +5394,15 @@ void idle_task_exit(void)
 /*
  * Since this CPU is going 'away' for a while, fold any nr_active delta
  * we might have. Assumes we're called after migrate_tasks() so that the
- * nr_active count is stable.
+ * nr_active count is stable. We need to take the teardown thread which
+ * is calling this into account, so we hand in adjust = 1 to the load
+ * calculation.
  *
  * Also see the comment "Global load-average calculations".
  */
 static void calc_load_migrate(struct rq *rq)
 {
-       long delta = calc_load_fold_active(rq);
+       long delta = calc_load_fold_active(rq, 1);
        if (delta)
                atomic_long_add(delta, &calc_load_tasks);
 }
index b0b93fd..a2d6eb7 100644 (file)
@@ -78,11 +78,11 @@ void get_avenrun(unsigned long *loads, unsigned long offset, int shift)
        loads[2] = (avenrun[2] + offset) << shift;
 }
 
-long calc_load_fold_active(struct rq *this_rq)
+long calc_load_fold_active(struct rq *this_rq, long adjust)
 {
        long nr_active, delta = 0;
 
-       nr_active = this_rq->nr_running;
+       nr_active = this_rq->nr_running - adjust;
        nr_active += (long)this_rq->nr_uninterruptible;
 
        if (nr_active != this_rq->calc_load_active) {
@@ -188,7 +188,7 @@ void calc_load_enter_idle(void)
         * We're going into NOHZ mode, if there's any pending delta, fold it
         * into the pending idle delta.
         */
-       delta = calc_load_fold_active(this_rq);
+       delta = calc_load_fold_active(this_rq, 0);
        if (delta) {
                int idx = calc_load_write_idx();
 
@@ -389,7 +389,7 @@ void calc_global_load_tick(struct rq *this_rq)
        if (time_before(jiffies, this_rq->calc_load_update))
                return;
 
-       delta  = calc_load_fold_active(this_rq);
+       delta  = calc_load_fold_active(this_rq, 0);
        if (delta)
                atomic_long_add(delta, &calc_load_tasks);
 
index 7cbeb92..898c0d2 100644 (file)
@@ -28,7 +28,7 @@ extern unsigned long calc_load_update;
 extern atomic_long_t calc_load_tasks;
 
 extern void calc_global_load_tick(struct rq *this_rq);
-extern long calc_load_fold_active(struct rq *this_rq);
+extern long calc_load_fold_active(struct rq *this_rq, long adjust);
 
 #ifdef CONFIG_SMP
 extern void cpu_load_update_active(struct rq *this_rq);
index 1cafba8..39008d7 100644 (file)
@@ -777,6 +777,7 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp)
                        timer->it.cpu.expires = 0;
                        sample_to_timespec(timer->it_clock, timer->it.cpu.expires,
                                           &itp->it_value);
+                       return;
                } else {
                        cpu_timer_sample_group(timer->it_clock, p, &now);
                        unlock_task_sighand(p, &flags);
index e1c0e99..d12bd95 100644 (file)
@@ -4369,8 +4369,8 @@ static void show_pwq(struct pool_workqueue *pwq)
 /**
  * show_workqueue_state - dump workqueue state
  *
- * Called from a sysrq handler and prints out all busy workqueues and
- * pools.
+ * Called from a sysrq handler or try_to_freeze_tasks() and prints out
+ * all busy workqueues and pools.
  */
 void show_workqueue_state(void)
 {
@@ -4600,15 +4600,11 @@ static void restore_unbound_workers_cpumask(struct worker_pool *pool, int cpu)
        if (!cpumask_test_cpu(cpu, pool->attrs->cpumask))
                return;
 
-       /* is @cpu the only online CPU? */
        cpumask_and(&cpumask, pool->attrs->cpumask, cpu_online_mask);
-       if (cpumask_weight(&cpumask) != 1)
-               return;
 
        /* as we're called from CPU_ONLINE, the following shouldn't fail */
        for_each_pool_worker(worker, pool)
-               WARN_ON_ONCE(set_cpus_allowed_ptr(worker->task,
-                                                 pool->attrs->cpumask) < 0);
+               WARN_ON_ONCE(set_cpus_allowed_ptr(worker->task, &cpumask) < 0);
 }
 
 /*
index 79bfe0e..7bc0477 100644 (file)
@@ -1009,8 +1009,6 @@ static void isolate_freepages(struct compact_control *cc)
                                block_end_pfn = block_start_pfn,
                                block_start_pfn -= pageblock_nr_pages,
                                isolate_start_pfn = block_start_pfn) {
-               unsigned long isolated;
-
                /*
                 * This can iterate a massively long zone without finding any
                 * suitable migration targets, so periodically check if we need
@@ -1034,36 +1032,30 @@ static void isolate_freepages(struct compact_control *cc)
                        continue;
 
                /* Found a block suitable for isolating free pages from. */
-               isolated = isolate_freepages_block(cc, &isolate_start_pfn,
-                                               block_end_pfn, freelist, false);
-               /* If isolation failed early, do not continue needlessly */
-               if (!isolated && isolate_start_pfn < block_end_pfn &&
-                   cc->nr_migratepages > cc->nr_freepages)
-                       break;
+               isolate_freepages_block(cc, &isolate_start_pfn, block_end_pfn,
+                                       freelist, false);
 
                /*
-                * If we isolated enough freepages, or aborted due to async
-                * compaction being contended, terminate the loop.
-                * Remember where the free scanner should restart next time,
-                * which is where isolate_freepages_block() left off.
-                * But if it scanned the whole pageblock, isolate_start_pfn
-                * now points at block_end_pfn, which is the start of the next
-                * pageblock.
-                * In that case we will however want to restart at the start
-                * of the previous pageblock.
+                * If we isolated enough freepages, or aborted due to lock
+                * contention, terminate.
                 */
                if ((cc->nr_freepages >= cc->nr_migratepages)
                                                        || cc->contended) {
-                       if (isolate_start_pfn >= block_end_pfn)
+                       if (isolate_start_pfn >= block_end_pfn) {
+                               /*
+                                * Restart at previous pageblock if more
+                                * freepages can be isolated next time.
+                                */
                                isolate_start_pfn =
                                        block_start_pfn - pageblock_nr_pages;
+                       }
                        break;
-               } else {
+               } else if (isolate_start_pfn < block_end_pfn) {
                        /*
-                        * isolate_freepages_block() should not terminate
-                        * prematurely unless contended, or isolated enough
+                        * If isolation failed early, do not continue
+                        * needlessly.
                         */
-                       VM_BUG_ON(isolate_start_pfn < block_end_pfn);
+                       break;
                }
        }
 
index 9ed5853..343a2b7 100644 (file)
@@ -1624,14 +1624,9 @@ int madvise_free_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
        if (next - addr != HPAGE_PMD_SIZE) {
                get_page(page);
                spin_unlock(ptl);
-               if (split_huge_page(page)) {
-                       put_page(page);
-                       unlock_page(page);
-                       goto out_unlocked;
-               }
+               split_huge_page(page);
                put_page(page);
                unlock_page(page);
-               ret = 1;
                goto out_unlocked;
        }
 
@@ -2989,7 +2984,7 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd,
 }
 
 void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
-               unsigned long address, bool freeze)
+               unsigned long address, bool freeze, struct page *page)
 {
        spinlock_t *ptl;
        struct mm_struct *mm = vma->vm_mm;
@@ -2997,8 +2992,17 @@ void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
 
        mmu_notifier_invalidate_range_start(mm, haddr, haddr + HPAGE_PMD_SIZE);
        ptl = pmd_lock(mm, pmd);
+
+       /*
+        * If caller asks to setup a migration entries, we need a page to check
+        * pmd against. Otherwise we can end up replacing wrong page.
+        */
+       VM_BUG_ON(freeze && !page);
+       if (page && page != pmd_page(*pmd))
+               goto out;
+
        if (pmd_trans_huge(*pmd)) {
-               struct page *page = pmd_page(*pmd);
+               page = pmd_page(*pmd);
                if (PageMlocked(page))
                        clear_page_mlock(page);
        } else if (!pmd_devmap(*pmd))
@@ -3025,24 +3029,8 @@ void split_huge_pmd_address(struct vm_area_struct *vma, unsigned long address,
                return;
 
        pmd = pmd_offset(pud, address);
-       if (!pmd_present(*pmd) || (!pmd_trans_huge(*pmd) && !pmd_devmap(*pmd)))
-               return;
 
-       /*
-        * If caller asks to setup a migration entries, we need a page to check
-        * pmd against. Otherwise we can end up replacing wrong page.
-        */
-       VM_BUG_ON(freeze && !page);
-       if (page && page != pmd_page(*pmd))
-               return;
-
-       /*
-        * Caller holds the mmap_sem write mode or the anon_vma lock,
-        * so a huge pmd cannot materialize from under us (khugepaged
-        * holds both the mmap_sem write mode and the anon_vma lock
-        * write mode).
-        */
-       __split_huge_pmd(vma, pmd, address, freeze);
+       __split_huge_pmd(vma, pmd, address, freeze, page);
 }
 
 void vma_adjust_trans_huge(struct vm_area_struct *vma,
index c1f3c0b..addfe4a 100644 (file)
@@ -3383,7 +3383,7 @@ retry_avoidcopy:
        /* If no-one else is actually using this page, avoid the copy
         * and just make the page writable */
        if (page_mapcount(old_page) == 1 && PageAnon(old_page)) {
-               page_move_anon_rmap(old_page, vma, address);
+               page_move_anon_rmap(old_page, vma);
                set_huge_ptep_writable(vma, address, ptep);
                return 0;
        }
index 4973505..65793f1 100644 (file)
@@ -238,30 +238,23 @@ static void qlist_move_cache(struct qlist_head *from,
                                   struct qlist_head *to,
                                   struct kmem_cache *cache)
 {
-       struct qlist_node *prev = NULL, *curr;
+       struct qlist_node *curr;
 
        if (unlikely(qlist_empty(from)))
                return;
 
        curr = from->head;
+       qlist_init(from);
        while (curr) {
-               struct qlist_node *qlink = curr;
-               struct kmem_cache *obj_cache = qlink_to_cache(qlink);
-
-               if (obj_cache == cache) {
-                       if (unlikely(from->head == qlink)) {
-                               from->head = curr->next;
-                               prev = curr;
-                       } else
-                               prev->next = curr->next;
-                       if (unlikely(from->tail == qlink))
-                               from->tail = curr->next;
-                       from->bytes -= cache->size;
-                       qlist_put(to, qlink, cache->size);
-               } else {
-                       prev = curr;
-               }
-               curr = curr->next;
+               struct qlist_node *next = curr->next;
+               struct kmem_cache *obj_cache = qlink_to_cache(curr);
+
+               if (obj_cache == cache)
+                       qlist_put(to, curr, obj_cache->size);
+               else
+                       qlist_put(from, curr, obj_cache->size);
+
+               curr = next;
        }
 }
 
index ac8664d..5339c89 100644 (file)
@@ -4057,6 +4057,60 @@ static struct cftype mem_cgroup_legacy_files[] = {
        { },    /* terminate */
 };
 
+/*
+ * Private memory cgroup IDR
+ *
+ * Swap-out records and page cache shadow entries need to store memcg
+ * references in constrained space, so we maintain an ID space that is
+ * limited to 16 bit (MEM_CGROUP_ID_MAX), limiting the total number of
+ * memory-controlled cgroups to 64k.
+ *
+ * However, there usually are many references to the oflline CSS after
+ * the cgroup has been destroyed, such as page cache or reclaimable
+ * slab objects, that don't need to hang on to the ID. We want to keep
+ * those dead CSS from occupying IDs, or we might quickly exhaust the
+ * relatively small ID space and prevent the creation of new cgroups
+ * even when there are much fewer than 64k cgroups - possibly none.
+ *
+ * Maintain a private 16-bit ID space for memcg, and allow the ID to
+ * be freed and recycled when it's no longer needed, which is usually
+ * when the CSS is offlined.
+ *
+ * The only exception to that are records of swapped out tmpfs/shmem
+ * pages that need to be attributed to live ancestors on swapin. But
+ * those references are manageable from userspace.
+ */
+
+static DEFINE_IDR(mem_cgroup_idr);
+
+static void mem_cgroup_id_get(struct mem_cgroup *memcg)
+{
+       atomic_inc(&memcg->id.ref);
+}
+
+static void mem_cgroup_id_put(struct mem_cgroup *memcg)
+{
+       if (atomic_dec_and_test(&memcg->id.ref)) {
+               idr_remove(&mem_cgroup_idr, memcg->id.id);
+               memcg->id.id = 0;
+
+               /* Memcg ID pins CSS */
+               css_put(&memcg->css);
+       }
+}
+
+/**
+ * mem_cgroup_from_id - look up a memcg from a memcg id
+ * @id: the memcg id to look up
+ *
+ * Caller must hold rcu_read_lock().
+ */
+struct mem_cgroup *mem_cgroup_from_id(unsigned short id)
+{
+       WARN_ON_ONCE(!rcu_read_lock_held());
+       return idr_find(&mem_cgroup_idr, id);
+}
+
 static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *memcg, int node)
 {
        struct mem_cgroup_per_node *pn;
@@ -4116,6 +4170,12 @@ static struct mem_cgroup *mem_cgroup_alloc(void)
        if (!memcg)
                return NULL;
 
+       memcg->id.id = idr_alloc(&mem_cgroup_idr, NULL,
+                                1, MEM_CGROUP_ID_MAX,
+                                GFP_KERNEL);
+       if (memcg->id.id < 0)
+               goto fail;
+
        memcg->stat = alloc_percpu(struct mem_cgroup_stat_cpu);
        if (!memcg->stat)
                goto fail;
@@ -4142,8 +4202,11 @@ static struct mem_cgroup *mem_cgroup_alloc(void)
 #ifdef CONFIG_CGROUP_WRITEBACK
        INIT_LIST_HEAD(&memcg->cgwb_list);
 #endif
+       idr_replace(&mem_cgroup_idr, memcg, memcg->id.id);
        return memcg;
 fail:
+       if (memcg->id.id > 0)
+               idr_remove(&mem_cgroup_idr, memcg->id.id);
        mem_cgroup_free(memcg);
        return NULL;
 }
@@ -4206,12 +4269,11 @@ fail:
        return ERR_PTR(-ENOMEM);
 }
 
-static int
-mem_cgroup_css_online(struct cgroup_subsys_state *css)
+static int mem_cgroup_css_online(struct cgroup_subsys_state *css)
 {
-       if (css->id > MEM_CGROUP_ID_MAX)
-               return -ENOSPC;
-
+       /* Online state pins memcg ID, memcg ID pins CSS */
+       mem_cgroup_id_get(mem_cgroup_from_css(css));
+       css_get(css);
        return 0;
 }
 
@@ -4234,6 +4296,8 @@ static void mem_cgroup_css_offline(struct cgroup_subsys_state *css)
 
        memcg_offline_kmem(memcg);
        wb_memcg_offline(memcg);
+
+       mem_cgroup_id_put(memcg);
 }
 
 static void mem_cgroup_css_released(struct cgroup_subsys_state *css)
@@ -5756,6 +5820,7 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
        if (!memcg)
                return;
 
+       mem_cgroup_id_get(memcg);
        oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg));
        VM_BUG_ON_PAGE(oldid, page);
        mem_cgroup_swap_statistics(memcg, true);
@@ -5774,6 +5839,9 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
        VM_BUG_ON(!irqs_disabled());
        mem_cgroup_charge_statistics(memcg, page, false, -1);
        memcg_check_events(memcg, page);
+
+       if (!mem_cgroup_is_root(memcg))
+               css_put(&memcg->css);
 }
 
 /*
@@ -5804,11 +5872,11 @@ int mem_cgroup_try_charge_swap(struct page *page, swp_entry_t entry)
            !page_counter_try_charge(&memcg->swap, 1, &counter))
                return -ENOMEM;
 
+       mem_cgroup_id_get(memcg);
        oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg));
        VM_BUG_ON_PAGE(oldid, page);
        mem_cgroup_swap_statistics(memcg, true);
 
-       css_get(&memcg->css);
        return 0;
 }
 
@@ -5837,7 +5905,7 @@ void mem_cgroup_uncharge_swap(swp_entry_t entry)
                                page_counter_uncharge(&memcg->memsw, 1);
                }
                mem_cgroup_swap_statistics(memcg, false);
-               css_put(&memcg->css);
+               mem_cgroup_id_put(memcg);
        }
        rcu_read_unlock();
 }
index cd1f29e..9e04681 100644 (file)
@@ -2399,8 +2399,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
                                 * Protected against the rmap code by
                                 * the page lock.
                                 */
-                               page_move_anon_rmap(compound_head(old_page),
-                                                   vma, address);
+                               page_move_anon_rmap(old_page, vma);
                        }
                        unlock_page(old_page);
                        return wp_page_reuse(mm, vma, address, page_table, ptl,
index 6903b69..8b3e134 100644 (file)
@@ -286,7 +286,9 @@ static inline void reset_deferred_meminit(pg_data_t *pgdat)
 /* Returns true if the struct page for the pfn is uninitialised */
 static inline bool __meminit early_page_uninitialised(unsigned long pfn)
 {
-       if (pfn >= NODE_DATA(early_pfn_to_nid(pfn))->first_deferred_pfn)
+       int nid = early_pfn_to_nid(pfn);
+
+       if (node_online(nid) && pfn >= NODE_DATA(nid)->first_deferred_pfn)
                return true;
 
        return false;
@@ -1273,7 +1275,7 @@ int __meminit early_pfn_to_nid(unsigned long pfn)
        spin_lock(&early_pfn_lock);
        nid = __early_pfn_to_nid(pfn, &early_pfnnid_cache);
        if (nid < 0)
-               nid = 0;
+               nid = first_online_node;
        spin_unlock(&early_pfn_lock);
 
        return nid;
index 0ea5d90..701b93f 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1084,23 +1084,20 @@ EXPORT_SYMBOL_GPL(page_mkclean);
  * page_move_anon_rmap - move a page to our anon_vma
  * @page:      the page to move to our anon_vma
  * @vma:       the vma the page belongs to
- * @address:   the user virtual address mapped
  *
  * When a page belongs exclusively to one process after a COW event,
  * that page can be moved into the anon_vma that belongs to just that
  * process, so the rmap code will not search the parent or sibling
  * processes.
  */
-void page_move_anon_rmap(struct page *page,
-       struct vm_area_struct *vma, unsigned long address)
+void page_move_anon_rmap(struct page *page, struct vm_area_struct *vma)
 {
        struct anon_vma *anon_vma = vma->anon_vma;
 
+       page = compound_head(page);
+
        VM_BUG_ON_PAGE(!PageLocked(page), page);
        VM_BUG_ON_VMA(!anon_vma, vma);
-       if (IS_ENABLED(CONFIG_DEBUG_VM) && PageTransHuge(page))
-               address &= HPAGE_PMD_MASK;
-       VM_BUG_ON_PAGE(page->index != linear_page_index(vma, address), page);
 
        anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON;
        /*
@@ -1427,7 +1424,8 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
                        goto out;
        }
 
-       pte = page_check_address(page, mm, address, &ptl, 0);
+       pte = page_check_address(page, mm, address, &ptl,
+                                PageTransCompound(page));
        if (!pte)
                goto out;
 
index a65dad7..82317ab 100644 (file)
@@ -526,8 +526,8 @@ void memcg_create_kmem_cache(struct mem_cgroup *memcg,
                goto out_unlock;
 
        cgroup_name(css->cgroup, memcg_name_buf, sizeof(memcg_name_buf));
-       cache_name = kasprintf(GFP_KERNEL, "%s(%d:%s)", root_cache->name,
-                              css->id, memcg_name_buf);
+       cache_name = kasprintf(GFP_KERNEL, "%s(%llu:%s)", root_cache->name,
+                              css->serial_nr, memcg_name_buf);
        if (!cache_name)
                goto out_unlock;
 
index 8a75f8d..5772775 100644 (file)
@@ -491,7 +491,7 @@ static int __init workingset_init(void)
        max_order = fls_long(totalram_pages - 1);
        if (max_order > timestamp_bits)
                bucket_order = max_order - timestamp_bits;
-       printk("workingset: timestamp_bits=%d max_order=%d bucket_order=%u\n",
+       pr_info("workingset: timestamp_bits=%d max_order=%d bucket_order=%u\n",
               timestamp_bits, max_order, bucket_order);
 
        ret = list_lru_init_key(&workingset_shadow_nodes, &shadow_nodes_key);
index 86ae75b..516b0e7 100644 (file)
@@ -146,10 +146,12 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
 
 static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu)
 {
-       /* TODO: gotta make sure the underlying layer can handle it,
-        * maybe an IFF_VLAN_CAPABLE flag for devices?
-        */
-       if (vlan_dev_priv(dev)->real_dev->mtu < new_mtu)
+       struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
+       unsigned int max_mtu = real_dev->mtu;
+
+       if (netif_reduces_vlan_mtu(real_dev))
+               max_mtu -= VLAN_HLEN;
+       if (max_mtu < new_mtu)
                return -ERANGE;
 
        dev->mtu = new_mtu;
index c92b52f..1270207 100644 (file)
@@ -118,6 +118,7 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
 {
        struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
        struct net_device *real_dev;
+       unsigned int max_mtu;
        __be16 proto;
        int err;
 
@@ -144,9 +145,11 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
        if (err < 0)
                return err;
 
+       max_mtu = netif_reduces_vlan_mtu(real_dev) ? real_dev->mtu - VLAN_HLEN :
+                                                    real_dev->mtu;
        if (!tb[IFLA_MTU])
-               dev->mtu = real_dev->mtu;
-       else if (dev->mtu > real_dev->mtu)
+               dev->mtu = max_mtu;
+       else if (dev->mtu > max_mtu)
                return -EINVAL;
 
        err = vlan_changelink(dev, tb, data);
index 748a9ea..825a5cd 100644 (file)
@@ -177,10 +177,21 @@ static void batadv_backbone_gw_put(struct batadv_bla_backbone_gw *backbone_gw)
 static void batadv_claim_release(struct kref *ref)
 {
        struct batadv_bla_claim *claim;
+       struct batadv_bla_backbone_gw *old_backbone_gw;
 
        claim = container_of(ref, struct batadv_bla_claim, refcount);
 
-       batadv_backbone_gw_put(claim->backbone_gw);
+       spin_lock_bh(&claim->backbone_lock);
+       old_backbone_gw = claim->backbone_gw;
+       claim->backbone_gw = NULL;
+       spin_unlock_bh(&claim->backbone_lock);
+
+       spin_lock_bh(&old_backbone_gw->crc_lock);
+       old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+       spin_unlock_bh(&old_backbone_gw->crc_lock);
+
+       batadv_backbone_gw_put(old_backbone_gw);
+
        kfree_rcu(claim, rcu);
 }
 
@@ -418,9 +429,12 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac,
                break;
        }
 
-       if (vid & BATADV_VLAN_HAS_TAG)
+       if (vid & BATADV_VLAN_HAS_TAG) {
                skb = vlan_insert_tag(skb, htons(ETH_P_8021Q),
                                      vid & VLAN_VID_MASK);
+               if (!skb)
+                       goto out;
+       }
 
        skb_reset_mac_header(skb);
        skb->protocol = eth_type_trans(skb, soft_iface);
@@ -674,8 +688,10 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
                                 const u8 *mac, const unsigned short vid,
                                 struct batadv_bla_backbone_gw *backbone_gw)
 {
+       struct batadv_bla_backbone_gw *old_backbone_gw;
        struct batadv_bla_claim *claim;
        struct batadv_bla_claim search_claim;
+       bool remove_crc = false;
        int hash_added;
 
        ether_addr_copy(search_claim.addr, mac);
@@ -689,8 +705,10 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
                        return;
 
                ether_addr_copy(claim->addr, mac);
+               spin_lock_init(&claim->backbone_lock);
                claim->vid = vid;
                claim->lasttime = jiffies;
+               kref_get(&backbone_gw->refcount);
                claim->backbone_gw = backbone_gw;
 
                kref_init(&claim->refcount);
@@ -718,15 +736,26 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
                           "bla_add_claim(): changing ownership for %pM, vid %d\n",
                           mac, BATADV_PRINT_VID(vid));
 
-               spin_lock_bh(&claim->backbone_gw->crc_lock);
-               claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
-               spin_unlock_bh(&claim->backbone_gw->crc_lock);
-               batadv_backbone_gw_put(claim->backbone_gw);
+               remove_crc = true;
        }
-       /* set (new) backbone gw */
+
+       /* replace backbone_gw atomically and adjust reference counters */
+       spin_lock_bh(&claim->backbone_lock);
+       old_backbone_gw = claim->backbone_gw;
        kref_get(&backbone_gw->refcount);
        claim->backbone_gw = backbone_gw;
+       spin_unlock_bh(&claim->backbone_lock);
 
+       if (remove_crc) {
+               /* remove claim address from old backbone_gw */
+               spin_lock_bh(&old_backbone_gw->crc_lock);
+               old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+               spin_unlock_bh(&old_backbone_gw->crc_lock);
+       }
+
+       batadv_backbone_gw_put(old_backbone_gw);
+
+       /* add claim address to new backbone_gw */
        spin_lock_bh(&backbone_gw->crc_lock);
        backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
        spin_unlock_bh(&backbone_gw->crc_lock);
@@ -736,6 +765,26 @@ claim_free_ref:
        batadv_claim_put(claim);
 }
 
+/**
+ * batadv_bla_claim_get_backbone_gw - Get valid reference for backbone_gw of
+ *  claim
+ * @claim: claim whose backbone_gw should be returned
+ *
+ * Return: valid reference to claim::backbone_gw
+ */
+static struct batadv_bla_backbone_gw *
+batadv_bla_claim_get_backbone_gw(struct batadv_bla_claim *claim)
+{
+       struct batadv_bla_backbone_gw *backbone_gw;
+
+       spin_lock_bh(&claim->backbone_lock);
+       backbone_gw = claim->backbone_gw;
+       kref_get(&backbone_gw->refcount);
+       spin_unlock_bh(&claim->backbone_lock);
+
+       return backbone_gw;
+}
+
 /**
  * batadv_bla_del_claim - delete a claim from the claim hash
  * @bat_priv: the bat priv with all the soft interface information
@@ -760,10 +809,6 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv,
                           batadv_choose_claim, claim);
        batadv_claim_put(claim); /* reference from the hash is gone */
 
-       spin_lock_bh(&claim->backbone_gw->crc_lock);
-       claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
-       spin_unlock_bh(&claim->backbone_gw->crc_lock);
-
        /* don't need the reference from hash_find() anymore */
        batadv_claim_put(claim);
 }
@@ -1216,6 +1261,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
                                    struct batadv_hard_iface *primary_if,
                                    int now)
 {
+       struct batadv_bla_backbone_gw *backbone_gw;
        struct batadv_bla_claim *claim;
        struct hlist_head *head;
        struct batadv_hashtable *hash;
@@ -1230,14 +1276,17 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(claim, head, hash_entry) {
+                       backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
                        if (now)
                                goto purge_now;
-                       if (!batadv_compare_eth(claim->backbone_gw->orig,
+
+                       if (!batadv_compare_eth(backbone_gw->orig,
                                                primary_if->net_dev->dev_addr))
-                               continue;
+                               goto skip;
+
                        if (!batadv_has_timed_out(claim->lasttime,
                                                  BATADV_BLA_CLAIM_TIMEOUT))
-                               continue;
+                               goto skip;
 
                        batadv_dbg(BATADV_DBG_BLA, bat_priv,
                                   "bla_purge_claims(): %pM, vid %d, time out\n",
@@ -1245,8 +1294,10 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
 
 purge_now:
                        batadv_handle_unclaim(bat_priv, primary_if,
-                                             claim->backbone_gw->orig,
+                                             backbone_gw->orig,
                                              claim->addr, claim->vid);
+skip:
+                       batadv_backbone_gw_put(backbone_gw);
                }
                rcu_read_unlock();
        }
@@ -1757,9 +1808,11 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb,
 bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
                   unsigned short vid, bool is_bcast)
 {
+       struct batadv_bla_backbone_gw *backbone_gw;
        struct ethhdr *ethhdr;
        struct batadv_bla_claim search_claim, *claim = NULL;
        struct batadv_hard_iface *primary_if;
+       bool own_claim;
        bool ret;
 
        ethhdr = eth_hdr(skb);
@@ -1794,8 +1847,12 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
        }
 
        /* if it is our own claim ... */
-       if (batadv_compare_eth(claim->backbone_gw->orig,
-                              primary_if->net_dev->dev_addr)) {
+       backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
+       own_claim = batadv_compare_eth(backbone_gw->orig,
+                                      primary_if->net_dev->dev_addr);
+       batadv_backbone_gw_put(backbone_gw);
+
+       if (own_claim) {
                /* ... allow it in any case */
                claim->lasttime = jiffies;
                goto allow;
@@ -1859,7 +1916,9 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
 {
        struct ethhdr *ethhdr;
        struct batadv_bla_claim search_claim, *claim = NULL;
+       struct batadv_bla_backbone_gw *backbone_gw;
        struct batadv_hard_iface *primary_if;
+       bool client_roamed;
        bool ret = false;
 
        primary_if = batadv_primary_if_get_selected(bat_priv);
@@ -1889,8 +1948,12 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
                goto allow;
 
        /* check if we are responsible. */
-       if (batadv_compare_eth(claim->backbone_gw->orig,
-                              primary_if->net_dev->dev_addr)) {
+       backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
+       client_roamed = batadv_compare_eth(backbone_gw->orig,
+                                          primary_if->net_dev->dev_addr);
+       batadv_backbone_gw_put(backbone_gw);
+
+       if (client_roamed) {
                /* if yes, the client has roamed and we have
                 * to unclaim it.
                 */
@@ -1938,6 +2001,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
        struct net_device *net_dev = (struct net_device *)seq->private;
        struct batadv_priv *bat_priv = netdev_priv(net_dev);
        struct batadv_hashtable *hash = bat_priv->bla.claim_hash;
+       struct batadv_bla_backbone_gw *backbone_gw;
        struct batadv_bla_claim *claim;
        struct batadv_hard_iface *primary_if;
        struct hlist_head *head;
@@ -1962,17 +2026,21 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(claim, head, hash_entry) {
-                       is_own = batadv_compare_eth(claim->backbone_gw->orig,
+                       backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
+
+                       is_own = batadv_compare_eth(backbone_gw->orig,
                                                    primary_addr);
 
-                       spin_lock_bh(&claim->backbone_gw->crc_lock);
-                       backbone_crc = claim->backbone_gw->crc;
-                       spin_unlock_bh(&claim->backbone_gw->crc_lock);
+                       spin_lock_bh(&backbone_gw->crc_lock);
+                       backbone_crc = backbone_gw->crc;
+                       spin_unlock_bh(&backbone_gw->crc_lock);
                        seq_printf(seq, " * %pM on %5d by %pM [%c] (%#.4x)\n",
                                   claim->addr, BATADV_PRINT_VID(claim->vid),
-                                  claim->backbone_gw->orig,
+                                  backbone_gw->orig,
                                   (is_own ? 'x' : ' '),
                                   backbone_crc);
+
+                       batadv_backbone_gw_put(backbone_gw);
                }
                rcu_read_unlock();
        }
index 278800a..aee3b39 100644 (file)
@@ -1009,9 +1009,12 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
                if (!skb_new)
                        goto out;
 
-               if (vid & BATADV_VLAN_HAS_TAG)
+               if (vid & BATADV_VLAN_HAS_TAG) {
                        skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
                                                  vid & VLAN_VID_MASK);
+                       if (!skb_new)
+                               goto out;
+               }
 
                skb_reset_mac_header(skb_new);
                skb_new->protocol = eth_type_trans(skb_new,
@@ -1089,9 +1092,12 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
         */
        skb_reset_mac_header(skb_new);
 
-       if (vid & BATADV_VLAN_HAS_TAG)
+       if (vid & BATADV_VLAN_HAS_TAG) {
                skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
                                          vid & VLAN_VID_MASK);
+               if (!skb_new)
+                       goto out;
+       }
 
        /* To preserve backwards compatibility, the node has choose the outgoing
         * format based on the incoming request packet type. The assumption is
index 7f51bc2..ab8c4f9 100644 (file)
@@ -765,6 +765,8 @@ static void batadv_orig_node_release(struct kref *ref)
        struct batadv_neigh_node *neigh_node;
        struct batadv_orig_node *orig_node;
        struct batadv_orig_ifinfo *orig_ifinfo;
+       struct batadv_orig_node_vlan *vlan;
+       struct batadv_orig_ifinfo *last_candidate;
 
        orig_node = container_of(ref, struct batadv_orig_node, refcount);
 
@@ -782,8 +784,21 @@ static void batadv_orig_node_release(struct kref *ref)
                hlist_del_rcu(&orig_ifinfo->list);
                batadv_orig_ifinfo_put(orig_ifinfo);
        }
+
+       last_candidate = orig_node->last_bonding_candidate;
+       orig_node->last_bonding_candidate = NULL;
        spin_unlock_bh(&orig_node->neigh_list_lock);
 
+       if (last_candidate)
+               batadv_orig_ifinfo_put(last_candidate);
+
+       spin_lock_bh(&orig_node->vlan_list_lock);
+       hlist_for_each_entry_safe(vlan, node_tmp, &orig_node->vlan_list, list) {
+               hlist_del_rcu(&vlan->list);
+               batadv_orig_node_vlan_put(vlan);
+       }
+       spin_unlock_bh(&orig_node->vlan_list_lock);
+
        /* Free nc_nodes */
        batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL);
 
index 6c2901a..bfac086 100644 (file)
@@ -455,6 +455,29 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
        return 0;
 }
 
+/**
+ * batadv_last_bonding_replace - Replace last_bonding_candidate of orig_node
+ * @orig_node: originator node whose bonding candidates should be replaced
+ * @new_candidate: new bonding candidate or NULL
+ */
+static void
+batadv_last_bonding_replace(struct batadv_orig_node *orig_node,
+                           struct batadv_orig_ifinfo *new_candidate)
+{
+       struct batadv_orig_ifinfo *old_candidate;
+
+       spin_lock_bh(&orig_node->neigh_list_lock);
+       old_candidate = orig_node->last_bonding_candidate;
+
+       if (new_candidate)
+               kref_get(&new_candidate->refcount);
+       orig_node->last_bonding_candidate = new_candidate;
+       spin_unlock_bh(&orig_node->neigh_list_lock);
+
+       if (old_candidate)
+               batadv_orig_ifinfo_put(old_candidate);
+}
+
 /**
  * batadv_find_router - find a suitable router for this originator
  * @bat_priv: the bat priv with all the soft interface information
@@ -562,10 +585,6 @@ next:
        }
        rcu_read_unlock();
 
-       /* last_bonding_candidate is reset below, remove the old reference. */
-       if (orig_node->last_bonding_candidate)
-               batadv_orig_ifinfo_put(orig_node->last_bonding_candidate);
-
        /* After finding candidates, handle the three cases:
         * 1) there is a next candidate, use that
         * 2) there is no next candidate, use the first of the list
@@ -574,21 +593,28 @@ next:
        if (next_candidate) {
                batadv_neigh_node_put(router);
 
-               /* remove references to first candidate, we don't need it. */
-               if (first_candidate) {
-                       batadv_neigh_node_put(first_candidate_router);
-                       batadv_orig_ifinfo_put(first_candidate);
-               }
+               kref_get(&next_candidate_router->refcount);
                router = next_candidate_router;
-               orig_node->last_bonding_candidate = next_candidate;
+               batadv_last_bonding_replace(orig_node, next_candidate);
        } else if (first_candidate) {
                batadv_neigh_node_put(router);
 
-               /* refcounting has already been done in the loop above. */
+               kref_get(&first_candidate_router->refcount);
                router = first_candidate_router;
-               orig_node->last_bonding_candidate = first_candidate;
+               batadv_last_bonding_replace(orig_node, first_candidate);
        } else {
-               orig_node->last_bonding_candidate = NULL;
+               batadv_last_bonding_replace(orig_node, NULL);
+       }
+
+       /* cleanup of candidates */
+       if (first_candidate) {
+               batadv_neigh_node_put(first_candidate_router);
+               batadv_orig_ifinfo_put(first_candidate);
+       }
+
+       if (next_candidate) {
+               batadv_neigh_node_put(next_candidate_router);
+               batadv_orig_ifinfo_put(next_candidate);
        }
 
        return router;
index f2f1256..0103976 100644 (file)
@@ -424,8 +424,8 @@ int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
        struct batadv_orig_node *orig_node;
 
        orig_node = batadv_gw_get_selected_orig(bat_priv);
-       return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0,
-                                      orig_node, vid);
+       return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST_4ADDR,
+                                      BATADV_P_DATA, orig_node, vid);
 }
 
 void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface)
index ba846b0..74d865a 100644 (file)
@@ -330,7 +330,9 @@ struct batadv_orig_node {
        DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
        u32 last_bcast_seqno;
        struct hlist_head neigh_list;
-       /* neigh_list_lock protects: neigh_list and router */
+       /* neigh_list_lock protects: neigh_list, ifinfo_list,
+        * last_bonding_candidate and router
+        */
        spinlock_t neigh_list_lock;
        struct hlist_node hash_entry;
        struct batadv_priv *bat_priv;
@@ -1042,6 +1044,7 @@ struct batadv_bla_backbone_gw {
  * @addr: mac address of claimed non-mesh client
  * @vid: vlan id this client was detected on
  * @backbone_gw: pointer to backbone gw claiming this client
+ * @backbone_lock: lock protecting backbone_gw pointer
  * @lasttime: last time we heard of claim (locals only)
  * @hash_entry: hlist node for batadv_priv_bla::claim_hash
  * @refcount: number of contexts the object is used
@@ -1051,6 +1054,7 @@ struct batadv_bla_claim {
        u8 addr[ETH_ALEN];
        unsigned short vid;
        struct batadv_bla_backbone_gw *backbone_gw;
+       spinlock_t backbone_lock; /* protects backbone_gw */
        unsigned long lasttime;
        struct hlist_node hash_entry;
        struct rcu_head rcu;
index 03062bb..7e480bf 100644 (file)
@@ -1260,6 +1260,115 @@ struct ceph_osdmap *ceph_osdmap_decode(void **p, void *end)
        return map;
 }
 
+/*
+ * Encoding order is (new_up_client, new_state, new_weight).  Need to
+ * apply in the (new_weight, new_state, new_up_client) order, because
+ * an incremental map may look like e.g.
+ *
+ *     new_up_client: { osd=6, addr=... } # set osd_state and addr
+ *     new_state: { osd=6, xorstate=EXISTS } # clear osd_state
+ */
+static int decode_new_up_state_weight(void **p, void *end,
+                                     struct ceph_osdmap *map)
+{
+       void *new_up_client;
+       void *new_state;
+       void *new_weight_end;
+       u32 len;
+
+       new_up_client = *p;
+       ceph_decode_32_safe(p, end, len, e_inval);
+       len *= sizeof(u32) + sizeof(struct ceph_entity_addr);
+       ceph_decode_need(p, end, len, e_inval);
+       *p += len;
+
+       new_state = *p;
+       ceph_decode_32_safe(p, end, len, e_inval);
+       len *= sizeof(u32) + sizeof(u8);
+       ceph_decode_need(p, end, len, e_inval);
+       *p += len;
+
+       /* new_weight */
+       ceph_decode_32_safe(p, end, len, e_inval);
+       while (len--) {
+               s32 osd;
+               u32 w;
+
+               ceph_decode_need(p, end, 2*sizeof(u32), e_inval);
+               osd = ceph_decode_32(p);
+               w = ceph_decode_32(p);
+               BUG_ON(osd >= map->max_osd);
+               pr_info("osd%d weight 0x%x %s\n", osd, w,
+                    w == CEPH_OSD_IN ? "(in)" :
+                    (w == CEPH_OSD_OUT ? "(out)" : ""));
+               map->osd_weight[osd] = w;
+
+               /*
+                * If we are marking in, set the EXISTS, and clear the
+                * AUTOOUT and NEW bits.
+                */
+               if (w) {
+                       map->osd_state[osd] |= CEPH_OSD_EXISTS;
+                       map->osd_state[osd] &= ~(CEPH_OSD_AUTOOUT |
+                                                CEPH_OSD_NEW);
+               }
+       }
+       new_weight_end = *p;
+
+       /* new_state (up/down) */
+       *p = new_state;
+       len = ceph_decode_32(p);
+       while (len--) {
+               s32 osd;
+               u8 xorstate;
+               int ret;
+
+               osd = ceph_decode_32(p);
+               xorstate = ceph_decode_8(p);
+               if (xorstate == 0)
+                       xorstate = CEPH_OSD_UP;
+               BUG_ON(osd >= map->max_osd);
+               if ((map->osd_state[osd] & CEPH_OSD_UP) &&
+                   (xorstate & CEPH_OSD_UP))
+                       pr_info("osd%d down\n", osd);
+               if ((map->osd_state[osd] & CEPH_OSD_EXISTS) &&
+                   (xorstate & CEPH_OSD_EXISTS)) {
+                       pr_info("osd%d does not exist\n", osd);
+                       map->osd_weight[osd] = CEPH_OSD_IN;
+                       ret = set_primary_affinity(map, osd,
+                                                  CEPH_OSD_DEFAULT_PRIMARY_AFFINITY);
+                       if (ret)
+                               return ret;
+                       memset(map->osd_addr + osd, 0, sizeof(*map->osd_addr));
+                       map->osd_state[osd] = 0;
+               } else {
+                       map->osd_state[osd] ^= xorstate;
+               }
+       }
+
+       /* new_up_client */
+       *p = new_up_client;
+       len = ceph_decode_32(p);
+       while (len--) {
+               s32 osd;
+               struct ceph_entity_addr addr;
+
+               osd = ceph_decode_32(p);
+               ceph_decode_copy(p, &addr, sizeof(addr));
+               ceph_decode_addr(&addr);
+               BUG_ON(osd >= map->max_osd);
+               pr_info("osd%d up\n", osd);
+               map->osd_state[osd] |= CEPH_OSD_EXISTS | CEPH_OSD_UP;
+               map->osd_addr[osd] = addr;
+       }
+
+       *p = new_weight_end;
+       return 0;
+
+e_inval:
+       return -EINVAL;
+}
+
 /*
  * decode and apply an incremental map update.
  */
@@ -1358,49 +1467,10 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
                        __remove_pg_pool(&map->pg_pools, pi);
        }
 
-       /* new_up */
-       ceph_decode_32_safe(p, end, len, e_inval);
-       while (len--) {
-               u32 osd;
-               struct ceph_entity_addr addr;
-               ceph_decode_32_safe(p, end, osd, e_inval);
-               ceph_decode_copy_safe(p, end, &addr, sizeof(addr), e_inval);
-               ceph_decode_addr(&addr);
-               pr_info("osd%d up\n", osd);
-               BUG_ON(osd >= map->max_osd);
-               map->osd_state[osd] |= CEPH_OSD_UP | CEPH_OSD_EXISTS;
-               map->osd_addr[osd] = addr;
-       }
-
-       /* new_state */
-       ceph_decode_32_safe(p, end, len, e_inval);
-       while (len--) {
-               u32 osd;
-               u8 xorstate;
-               ceph_decode_32_safe(p, end, osd, e_inval);
-               xorstate = **(u8 **)p;
-               (*p)++;  /* clean flag */
-               if (xorstate == 0)
-                       xorstate = CEPH_OSD_UP;
-               if (xorstate & CEPH_OSD_UP)
-                       pr_info("osd%d down\n", osd);
-               if (osd < map->max_osd)
-                       map->osd_state[osd] ^= xorstate;
-       }
-
-       /* new_weight */
-       ceph_decode_32_safe(p, end, len, e_inval);
-       while (len--) {
-               u32 osd, off;
-               ceph_decode_need(p, end, sizeof(u32)*2, e_inval);
-               osd = ceph_decode_32(p);
-               off = ceph_decode_32(p);
-               pr_info("osd%d weight 0x%x %s\n", osd, off,
-                    off == CEPH_OSD_IN ? "(in)" :
-                    (off == CEPH_OSD_OUT ? "(out)" : ""));
-               if (osd < map->max_osd)
-                       map->osd_weight[osd] = off;
-       }
+       /* new_up_client, new_state, new_weight */
+       err = decode_new_up_state_weight(p, end, map);
+       if (err)
+               goto bad;
 
        /* new_pg_temp */
        err = decode_new_pg_temp(p, end, map);
index c4b330c..e759d90 100644 (file)
 #include <net/sock_reuseport.h>
 
 /**
- *     sk_filter - run a packet through a socket filter
+ *     sk_filter_trim_cap - run a packet through a socket filter
  *     @sk: sock associated with &sk_buff
  *     @skb: buffer to filter
+ *     @cap: limit on how short the eBPF program may trim the packet
  *
  * Run the eBPF program and then cut skb->data to correct size returned by
  * the program. If pkt_len is 0 we toss packet. If skb->len is smaller
@@ -64,7 +65,7 @@
  * be accepted or -EPERM if the packet should be tossed.
  *
  */
-int sk_filter(struct sock *sk, struct sk_buff *skb)
+int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap)
 {
        int err;
        struct sk_filter *filter;
@@ -85,14 +86,13 @@ int sk_filter(struct sock *sk, struct sk_buff *skb)
        filter = rcu_dereference(sk->sk_filter);
        if (filter) {
                unsigned int pkt_len = bpf_prog_run_save_cb(filter->prog, skb);
-
-               err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM;
+               err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM;
        }
        rcu_read_unlock();
 
        return err;
 }
-EXPORT_SYMBOL(sk_filter);
+EXPORT_SYMBOL(sk_filter_trim_cap);
 
 static u64 __skb_get_pay_offset(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
 {
index 08bf97e..25dab8b 100644 (file)
@@ -452,11 +452,12 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(sock_queue_rcv_skb);
 
-int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
+int __sk_receive_skb(struct sock *sk, struct sk_buff *skb,
+                    const int nested, unsigned int trim_cap)
 {
        int rc = NET_RX_SUCCESS;
 
-       if (sk_filter(sk, skb))
+       if (sk_filter_trim_cap(sk, skb, trim_cap))
                goto discard_and_relse;
 
        skb->dev = NULL;
@@ -492,7 +493,7 @@ discard_and_relse:
        kfree_skb(skb);
        goto out;
 }
-EXPORT_SYMBOL(sk_receive_skb);
+EXPORT_SYMBOL(__sk_receive_skb);
 
 struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie)
 {
@@ -1938,6 +1939,10 @@ int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg,
                sockc->tsflags &= ~SOF_TIMESTAMPING_TX_RECORD_MASK;
                sockc->tsflags |= tsflags;
                break;
+       /* SCM_RIGHTS and SCM_CREDENTIALS are semantically in SOL_UNIX. */
+       case SCM_RIGHTS:
+       case SCM_CREDENTIALS:
+               break;
        default:
                return -EINVAL;
        }
index 5c7e413..345a3ae 100644 (file)
@@ -462,7 +462,7 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk,
        security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
        rt = ip_route_output_flow(net, &fl4, sk);
        if (IS_ERR(rt)) {
-               __IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
+               IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
                return NULL;
        }
 
@@ -527,17 +527,19 @@ static void dccp_v4_ctl_send_reset(const struct sock *sk, struct sk_buff *rxskb)
                                                                 rxiph->daddr);
        skb_dst_set(skb, dst_clone(dst));
 
+       local_bh_disable();
        bh_lock_sock(ctl_sk);
        err = ip_build_and_send_pkt(skb, ctl_sk,
                                    rxiph->daddr, rxiph->saddr, NULL);
        bh_unlock_sock(ctl_sk);
 
        if (net_xmit_eval(err) == 0) {
-               DCCP_INC_STATS(DCCP_MIB_OUTSEGS);
-               DCCP_INC_STATS(DCCP_MIB_OUTRSTS);
+               __DCCP_INC_STATS(DCCP_MIB_OUTSEGS);
+               __DCCP_INC_STATS(DCCP_MIB_OUTRSTS);
        }
+       local_bh_enable();
 out:
-        dst_release(dst);
+       dst_release(dst);
 }
 
 static void dccp_v4_reqsk_destructor(struct request_sock *req)
@@ -866,7 +868,7 @@ lookup:
                goto discard_and_relse;
        nf_reset(skb);
 
-       return sk_receive_skb(sk, skb, 1);
+       return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4);
 
 no_dccp_socket:
        if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
index d176f4e..3ff137d 100644 (file)
@@ -732,7 +732,7 @@ lookup:
        if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
                goto discard_and_relse;
 
-       return sk_receive_skb(sk, skb, 1) ? -1 : 0;
+       return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4) ? -1 : 0;
 
 no_dccp_socket:
        if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
index d09173b..539fa26 100644 (file)
@@ -479,6 +479,9 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
                if (!rtnh_ok(rtnh, remaining))
                        return -EINVAL;
 
+               if (rtnh->rtnh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+                       return -EINVAL;
+
                nexthop_nh->nh_flags =
                        (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags;
                nexthop_nh->nh_oif = rtnh->rtnh_ifindex;
@@ -1003,6 +1006,9 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
        if (fib_props[cfg->fc_type].scope > cfg->fc_scope)
                goto err_inval;
 
+       if (cfg->fc_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+               goto err_inval;
+
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
        if (cfg->fc_mp) {
                nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len);
index d6c8f4c..42bf89a 100644 (file)
@@ -87,7 +87,7 @@ int sysctl_tcp_adv_win_scale __read_mostly = 1;
 EXPORT_SYMBOL(sysctl_tcp_adv_win_scale);
 
 /* rfc5961 challenge ack rate limiting */
-int sysctl_tcp_challenge_ack_limit = 100;
+int sysctl_tcp_challenge_ack_limit = 1000;
 
 int sysctl_tcp_stdurg __read_mostly;
 int sysctl_tcp_rfc1337 __read_mostly;
@@ -3421,6 +3421,23 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32
        return flag;
 }
 
+static bool __tcp_oow_rate_limited(struct net *net, int mib_idx,
+                                  u32 *last_oow_ack_time)
+{
+       if (*last_oow_ack_time) {
+               s32 elapsed = (s32)(tcp_time_stamp - *last_oow_ack_time);
+
+               if (0 <= elapsed && elapsed < sysctl_tcp_invalid_ratelimit) {
+                       NET_INC_STATS(net, mib_idx);
+                       return true;    /* rate-limited: don't send yet! */
+               }
+       }
+
+       *last_oow_ack_time = tcp_time_stamp;
+
+       return false;   /* not rate-limited: go ahead, send dupack now! */
+}
+
 /* Return true if we're currently rate-limiting out-of-window ACKs and
  * thus shouldn't send a dupack right now. We rate-limit dupacks in
  * response to out-of-window SYNs or ACKs to mitigate ACK loops or DoS
@@ -3434,21 +3451,9 @@ bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb,
        /* Data packets without SYNs are not likely part of an ACK loop. */
        if ((TCP_SKB_CB(skb)->seq != TCP_SKB_CB(skb)->end_seq) &&
            !tcp_hdr(skb)->syn)
-               goto not_rate_limited;
-
-       if (*last_oow_ack_time) {
-               s32 elapsed = (s32)(tcp_time_stamp - *last_oow_ack_time);
-
-               if (0 <= elapsed && elapsed < sysctl_tcp_invalid_ratelimit) {
-                       NET_INC_STATS(net, mib_idx);
-                       return true;    /* rate-limited: don't send yet! */
-               }
-       }
-
-       *last_oow_ack_time = tcp_time_stamp;
+               return false;
 
-not_rate_limited:
-       return false;   /* not rate-limited: go ahead, send dupack now! */
+       return __tcp_oow_rate_limited(net, mib_idx, last_oow_ack_time);
 }
 
 /* RFC 5961 7 [ACK Throttling] */
@@ -3458,21 +3463,26 @@ static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb)
        static u32 challenge_timestamp;
        static unsigned int challenge_count;
        struct tcp_sock *tp = tcp_sk(sk);
-       u32 now;
+       u32 count, now;
 
        /* First check our per-socket dupack rate limit. */
-       if (tcp_oow_rate_limited(sock_net(sk), skb,
-                                LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
-                                &tp->last_oow_ack_time))
+       if (__tcp_oow_rate_limited(sock_net(sk),
+                                  LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
+                                  &tp->last_oow_ack_time))
                return;
 
-       /* Then check the check host-wide RFC 5961 rate limit. */
+       /* Then check host-wide RFC 5961 rate limit. */
        now = jiffies / HZ;
        if (now != challenge_timestamp) {
+               u32 half = (sysctl_tcp_challenge_ack_limit + 1) >> 1;
+
                challenge_timestamp = now;
-               challenge_count = 0;
+               WRITE_ONCE(challenge_count, half +
+                          prandom_u32_max(sysctl_tcp_challenge_ack_limit));
        }
-       if (++challenge_count <= sysctl_tcp_challenge_ack_limit) {
+       count = READ_ONCE(challenge_count);
+       if (count > 0) {
+               WRITE_ONCE(challenge_count, count - 1);
                NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK);
                tcp_send_ack(sk);
        }
index ca5e8ea..4aed8fc 100644 (file)
@@ -1583,6 +1583,8 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 
        if (sk_filter(sk, skb))
                goto drop;
+       if (unlikely(skb->len < sizeof(struct udphdr)))
+               goto drop;
 
        udp_csum_pull_header(skb);
        if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) {
index 005dc82..acc0970 100644 (file)
@@ -620,6 +620,8 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 
        if (sk_filter(sk, skb))
                goto drop;
+       if (unlikely(skb->len < sizeof(struct udphdr)))
+               goto drop;
 
        udp_csum_pull_header(skb);
        if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) {
index 803001a..1b07578 100644 (file)
@@ -1545,7 +1545,8 @@ error:
 /*
  *      Set up receiving multicast socket over UDP
  */
-static struct socket *make_receive_sock(struct netns_ipvs *ipvs, int id)
+static struct socket *make_receive_sock(struct netns_ipvs *ipvs, int id,
+                                       int ifindex)
 {
        /* multicast addr */
        union ipvs_sockaddr mcast_addr;
@@ -1566,6 +1567,7 @@ static struct socket *make_receive_sock(struct netns_ipvs *ipvs, int id)
                set_sock_size(sock->sk, 0, result);
 
        get_mcast_sockaddr(&mcast_addr, &salen, &ipvs->bcfg, id);
+       sock->sk->sk_bound_dev_if = ifindex;
        result = sock->ops->bind(sock, (struct sockaddr *)&mcast_addr, salen);
        if (result < 0) {
                pr_err("Error binding to the multicast addr\n");
@@ -1868,7 +1870,7 @@ int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c,
                if (state == IP_VS_STATE_MASTER)
                        sock = make_send_sock(ipvs, id);
                else
-                       sock = make_receive_sock(ipvs, id);
+                       sock = make_receive_sock(ipvs, id, dev->ifindex);
                if (IS_ERR(sock)) {
                        result = PTR_ERR(sock);
                        goto outtinfo;
index f204274..9f530ad 100644 (file)
@@ -646,6 +646,7 @@ static int nf_ct_resolve_clash(struct net *net, struct sk_buff *skb,
 
        l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
        if (l4proto->allow_clash &&
+           !nfct_nat(ct) &&
            !nf_ct_is_dying(ct) &&
            atomic_inc_not_zero(&ct->ct_general.use)) {
                nf_ct_acct_merge(ct, ctinfo, (struct nf_conn *)skb->nfct);
@@ -1601,8 +1602,15 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
        unsigned int nr_slots, i;
        size_t sz;
 
+       if (*sizep > (UINT_MAX / sizeof(struct hlist_nulls_head)))
+               return NULL;
+
        BUILD_BUG_ON(sizeof(struct hlist_nulls_head) != sizeof(struct hlist_head));
        nr_slots = *sizep = roundup(*sizep, PAGE_SIZE / sizeof(struct hlist_nulls_head));
+
+       if (nr_slots > (UINT_MAX / sizeof(struct hlist_nulls_head)))
+               return NULL;
+
        sz = nr_slots * sizeof(struct hlist_nulls_head);
        hash = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO,
                                        get_order(sz));
index 2c88187..cf7c745 100644 (file)
@@ -1724,9 +1724,11 @@ struct nft_expr *nft_expr_init(const struct nft_ctx *ctx,
 
        err = nf_tables_newexpr(ctx, &info, expr);
        if (err < 0)
-               goto err2;
+               goto err3;
 
        return expr;
+err3:
+       kfree(expr);
 err2:
        module_put(info.ops->type->owner);
 err1:
index 137e308..81fbb45 100644 (file)
@@ -54,7 +54,6 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
        const struct nf_conn_help *help;
        const struct nf_conntrack_tuple *tuple;
        const struct nf_conntrack_helper *helper;
-       long diff;
        unsigned int state;
 
        ct = nf_ct_get(pkt->skb, &ctinfo);
@@ -94,10 +93,7 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
                return;
 #endif
        case NFT_CT_EXPIRATION:
-               diff = (long)jiffies - (long)ct->timeout.expires;
-               if (diff < 0)
-                       diff = 0;
-               *dest = jiffies_to_msecs(diff);
+               *dest = jiffies_to_msecs(nf_ct_expires(ct));
                return;
        case NFT_CT_HELPER:
                if (ct->master == NULL)
index 16c50b0..f4bad9d 100644 (file)
@@ -227,7 +227,7 @@ void nft_meta_set_eval(const struct nft_expr *expr,
                        skb->pkt_type = value;
                break;
        case NFT_META_NFTRACE:
-               skb->nf_trace = 1;
+               skb->nf_trace = !!value;
                break;
        default:
                WARN_ON(1);
index 9f0983f..b43c401 100644 (file)
@@ -1927,13 +1927,11 @@ retry:
                goto out_unlock;
        }
 
-       sockc.tsflags = 0;
+       sockc.tsflags = sk->sk_tsflags;
        if (msg->msg_controllen) {
                err = sock_cmsg_send(sk, msg, &sockc);
-               if (unlikely(err)) {
-                       err = -EINVAL;
+               if (unlikely(err))
                        goto out_unlock;
-               }
        }
 
        skb->protocol = proto;
@@ -2678,7 +2676,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
                dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
        }
 
-       sockc.tsflags = 0;
+       sockc.tsflags = po->sk.sk_tsflags;
        if (msg->msg_controllen) {
                err = sock_cmsg_send(&po->sk, msg, &sockc);
                if (unlikely(err))
@@ -2881,7 +2879,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
        if (unlikely(!(dev->flags & IFF_UP)))
                goto out_unlock;
 
-       sockc.tsflags = 0;
+       sockc.tsflags = sk->sk_tsflags;
        sockc.mark = sk->sk_mark;
        if (msg->msg_controllen) {
                err = sock_cmsg_send(sk, msg, &sockc);
index 79c4abc..0a63947 100644 (file)
@@ -164,7 +164,8 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
                rose_frames_acked(sk, nr);
                if (ns == rose->vr) {
                        rose_start_idletimer(sk);
-                       if (sock_queue_rcv_skb(sk, skb) == 0) {
+                       if (sk_filter_trim_cap(sk, skb, ROSE_MIN_LEN) == 0 &&
+                           __sock_queue_rcv_skb(sk, skb) == 0) {
                                rose->vr = (rose->vr + 1) % ROSE_MODULUS;
                                queued = 1;
                        } else {
index 62f9d81..052f84d 100644 (file)
@@ -1140,8 +1140,10 @@ htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d)
 
        if (!cl->level && cl->un.leaf.q)
                qlen = cl->un.leaf.q->q.qlen;
-       cl->xstats.tokens = PSCHED_NS2TICKS(cl->tokens);
-       cl->xstats.ctokens = PSCHED_NS2TICKS(cl->ctokens);
+       cl->xstats.tokens = clamp_t(s64, PSCHED_NS2TICKS(cl->tokens),
+                                   INT_MIN, INT_MAX);
+       cl->xstats.ctokens = clamp_t(s64, PSCHED_NS2TICKS(cl->ctokens),
+                                    INT_MIN, INT_MAX);
 
        if (gnet_stats_copy_basic(d, NULL, &cl->bstats) < 0 ||
            gnet_stats_copy_rate_est(d, NULL, &cl->rate_est) < 0 ||
index a701527..47cf460 100644 (file)
@@ -112,7 +112,6 @@ int sctp_rcv(struct sk_buff *skb)
        struct sctp_ep_common *rcvr;
        struct sctp_transport *transport = NULL;
        struct sctp_chunk *chunk;
-       struct sctphdr *sh;
        union sctp_addr src;
        union sctp_addr dest;
        int family;
@@ -127,8 +126,6 @@ int sctp_rcv(struct sk_buff *skb)
        if (skb_linearize(skb))
                goto discard_it;
 
-       sh = sctp_hdr(skb);
-
        /* Pull up the IP and SCTP headers. */
        __skb_pull(skb, skb_transport_offset(skb));
        if (skb->len < sizeof(struct sctphdr))
@@ -230,7 +227,7 @@ int sctp_rcv(struct sk_buff *skb)
        chunk->rcvr = rcvr;
 
        /* Remember the SCTP header. */
-       chunk->sctp_hdr = sh;
+       chunk->sctp_hdr = sctp_hdr(skb);
 
        /* Set the source and destination addresses of the incoming chunk.  */
        sctp_init_addrs(chunk, &src, &dest);
index bf8f05c..a597708 100644 (file)
@@ -330,6 +330,21 @@ static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b)
        return 0;
 }
 
+/* tipc_bearer_reset_all - reset all links on all bearers
+ */
+void tipc_bearer_reset_all(struct net *net)
+{
+       struct tipc_net *tn = tipc_net(net);
+       struct tipc_bearer *b;
+       int i;
+
+       for (i = 0; i < MAX_BEARERS; i++) {
+               b = rcu_dereference_rtnl(tn->bearer_list[i]);
+               if (b)
+                       tipc_reset_bearer(net, b);
+       }
+}
+
 /**
  * bearer_disable
  *
index f686e41..60e49c3 100644 (file)
@@ -198,6 +198,7 @@ void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest);
 void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest);
 struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name);
 struct tipc_media *tipc_media_find(const char *name);
+void tipc_bearer_reset_all(struct net *net);
 int tipc_bearer_setup(void);
 void tipc_bearer_cleanup(void);
 void tipc_bearer_stop(struct net *net);
index 67b6ab9..7d89f87 100644 (file)
@@ -349,6 +349,8 @@ void tipc_link_remove_bc_peer(struct tipc_link *snd_l,
        u16 ack = snd_l->snd_nxt - 1;
 
        snd_l->ackers--;
+       rcv_l->bc_peer_is_up = true;
+       rcv_l->state = LINK_ESTABLISHED;
        tipc_link_bc_ack_rcv(rcv_l, ack, xmitq);
        tipc_link_reset(rcv_l);
        rcv_l->state = LINK_RESET;
@@ -1559,7 +1561,12 @@ void tipc_link_bc_sync_rcv(struct tipc_link *l, struct tipc_msg *hdr,
        if (!msg_peer_node_is_up(hdr))
                return;
 
-       l->bc_peer_is_up = true;
+       /* Open when peer ackowledges our bcast init msg (pkt #1) */
+       if (msg_ack(hdr))
+               l->bc_peer_is_up = true;
+
+       if (!l->bc_peer_is_up)
+               return;
 
        /* Ignore if peers_snd_nxt goes beyond receive window */
        if (more(peers_snd_nxt, l->rcv_nxt + l->window))
index e01e2c7..23d4761 100644 (file)
@@ -1297,10 +1297,6 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id
 
        rc = tipc_bcast_rcv(net, be->link, skb);
 
-       /* Broadcast link reset may happen at reassembly failure */
-       if (rc & TIPC_LINK_DOWN_EVT)
-               tipc_node_reset_links(n);
-
        /* Broadcast ACKs are sent on a unicast link */
        if (rc & TIPC_LINK_SND_BC_ACK) {
                tipc_node_read_lock(n);
@@ -1320,6 +1316,17 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id
                spin_unlock_bh(&be->inputq2.lock);
                tipc_sk_mcast_rcv(net, &be->arrvq, &be->inputq2);
        }
+
+       if (rc & TIPC_LINK_DOWN_EVT) {
+               /* Reception reassembly failure => reset all links to peer */
+               if (!tipc_link_is_up(be->link))
+                       tipc_node_reset_links(n);
+
+               /* Retransmission failure => reset all links to all peers */
+               if (!tipc_link_is_up(tipc_bc_sndlink(net)))
+                       tipc_bearer_reset_all(net);
+       }
+
        tipc_node_put(n);
 }
 
index d759901..7d72283 100644 (file)
@@ -3487,16 +3487,16 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                params.smps_mode = NL80211_SMPS_OFF;
        }
 
+       params.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
+       if (params.pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ])
+               return -EOPNOTSUPP;
+
        if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
                params.acl = parse_acl_data(&rdev->wiphy, info);
                if (IS_ERR(params.acl))
                        return PTR_ERR(params.acl);
        }
 
-       params.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
-       if (params.pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ])
-               return -EOPNOTSUPP;
-
        wdev_lock(wdev);
        err = rdev_start_ap(rdev, dev, &params);
        if (!err) {
index 2443ee3..b7d1592 100644 (file)
@@ -721,6 +721,8 @@ __ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
         * alignment since sizeof(struct ethhdr) is 14.
         */
        frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + cur_len);
+       if (!frame)
+               return NULL;
 
        skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
        skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);
index 93e1fd4..a0ba48f 100755 (executable)
 # Authors:
 #       Todd Brandt <todd.e.brandt@linux.intel.com>
 #
+# Links:
+#       Home Page
+#         https://01.org/suspendresume
+#       Source repo
+#         https://github.com/01org/suspendresume
+#       Documentation
+#         Getting Started
+#           https://01.org/suspendresume/documentation/getting-started
+#         Command List:
+#           https://01.org/suspendresume/documentation/command-list
+#
 # Description:
 #       This tool is designed to assist kernel and OS developers in optimizing
 #       their linux stack's suspend/resume time. Using a kernel image built
@@ -35,6 +46,8 @@
 #               CONFIG_FTRACE=y
 #               CONFIG_FUNCTION_TRACER=y
 #               CONFIG_FUNCTION_GRAPH_TRACER=y
+#               CONFIG_KPROBES=y
+#               CONFIG_KPROBES_ON_FTRACE=y
 #
 #       For kernel versions older than 3.15:
 #       The following additional kernel parameters are required:
@@ -52,6 +65,7 @@ import re
 import platform
 from datetime import datetime
 import struct
+import ConfigParser
 
 # ----------------- CLASSES --------------------
 
@@ -60,8 +74,15 @@ import struct
 #       A global, single-instance container used to
 #       store system values and test parameters
 class SystemValues:
-       version = 3.0
+       ansi = False
+       version = '4.2'
        verbose = False
+       addlogs = False
+       mindevlen = 0.001
+       mincglen = 1.0
+       srgap = 0
+       cgexp = False
+       outdir = ''
        testdir = '.'
        tpath = '/sys/kernel/debug/tracing/'
        fpdtpath = '/sys/firmware/acpi/tables/FPDT'
@@ -71,26 +92,21 @@ class SystemValues:
                'device_pm_callback_end',
                'device_pm_callback_start'
        ]
-       modename = {
-               'freeze': 'Suspend-To-Idle (S0)',
-               'standby': 'Power-On Suspend (S1)',
-               'mem': 'Suspend-to-RAM (S3)',
-               'disk': 'Suspend-to-disk (S4)'
-       }
+       testcommand = ''
        mempath = '/dev/mem'
        powerfile = '/sys/power/state'
        suspendmode = 'mem'
        hostname = 'localhost'
        prefix = 'test'
        teststamp = ''
+       dmesgstart = 0.0
        dmesgfile = ''
        ftracefile = ''
        htmlfile = ''
+       embedded = False
        rtcwake = False
        rtcwaketime = 10
        rtcpath = ''
-       android = False
-       adb = 'adb'
        devicefilter = []
        stamp = 0
        execcount = 1
@@ -98,16 +114,90 @@ class SystemValues:
        usecallgraph = False
        usetraceevents = False
        usetraceeventsonly = False
+       usetracemarkers = True
+       usekprobes = True
+       usedevsrc = False
        notestrun = False
-       altdevname = dict()
+       devprops = dict()
        postresumetime = 0
+       devpropfmt = '# Device Properties: .*'
        tracertypefmt = '# tracer: (?P<t>.*)'
        firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
        postresumefmt = '# post resume time (?P<t>[0-9]*)$'
        stampfmt = '# suspend-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\
                                '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
                                ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
+       kprobecolor = 'rgba(204,204,204,0.5)'
+       synccolor = 'rgba(204,204,204,0.5)'
+       debugfuncs = []
+       tracefuncs = {
+               'sys_sync': dict(),
+               'pm_prepare_console': dict(),
+               'pm_notifier_call_chain': dict(),
+               'freeze_processes': dict(),
+               'freeze_kernel_threads': dict(),
+               'pm_restrict_gfp_mask': dict(),
+               'acpi_suspend_begin': dict(),
+               'suspend_console': dict(),
+               'acpi_pm_prepare': dict(),
+               'syscore_suspend': dict(),
+               'arch_enable_nonboot_cpus_end': dict(),
+               'syscore_resume': dict(),
+               'acpi_pm_finish': dict(),
+               'resume_console': dict(),
+               'acpi_pm_end': dict(),
+               'pm_restore_gfp_mask': dict(),
+               'thaw_processes': dict(),
+               'pm_restore_console': dict(),
+               'CPU_OFF': {
+                       'func':'_cpu_down',
+                       'args_x86_64': {'cpu':'%di:s32'},
+                       'format': 'CPU_OFF[{cpu}]',
+                       'mask': 'CPU_.*_DOWN'
+               },
+               'CPU_ON': {
+                       'func':'_cpu_up',
+                       'args_x86_64': {'cpu':'%di:s32'},
+                       'format': 'CPU_ON[{cpu}]',
+                       'mask': 'CPU_.*_UP'
+               },
+       }
+       dev_tracefuncs = {
+               # general wait/delay/sleep
+               'msleep': { 'args_x86_64': {'time':'%di:s32'} },
+               'udelay': { 'func':'__const_udelay', 'args_x86_64': {'loops':'%di:s32'} },
+               'acpi_os_stall': dict(),
+               # ACPI
+               'acpi_resume_power_resources': dict(),
+               'acpi_ps_parse_aml': dict(),
+               # filesystem
+               'ext4_sync_fs': dict(),
+               # ATA
+               'ata_eh_recover': { 'args_x86_64': {'port':'+36(%di):s32'} },
+               # i915
+               'i915_gem_restore_gtt_mappings': dict(),
+               'intel_opregion_setup': dict(),
+               'intel_dp_detect': dict(),
+               'intel_hdmi_detect': dict(),
+               'intel_opregion_init': dict(),
+       }
+       kprobes_postresume = [
+               {
+                       'name': 'ataportrst',
+                       'func': 'ata_eh_recover',
+                       'args': {'port':'+36(%di):s32'},
+                       'format': 'ata{port}_port_reset',
+                       'mask': 'ata.*_port_reset'
+               }
+       ]
+       kprobes = dict()
+       timeformat = '%.3f'
        def __init__(self):
+               # if this is a phoronix test run, set some default options
+               if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ):
+                       self.embedded = True
+                       self.addlogs = True
+                       self.htmlfile = os.environ['LOG_FILE']
                self.hostname = platform.node()
                if(self.hostname == ''):
                        self.hostname = 'localhost'
@@ -118,6 +208,12 @@ class SystemValues:
                if os.path.exists(rtc) and os.path.exists(rtc+'/date') and \
                        os.path.exists(rtc+'/time') and os.path.exists(rtc+'/wakealarm'):
                        self.rtcpath = rtc
+               if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()):
+                       self.ansi = True
+       def setPrecision(self, num):
+               if num < 0 or num > 6:
+                       return
+               self.timeformat = '%.{0}f'.format(num)
        def setOutputFile(self):
                if((self.htmlfile == '') and (self.dmesgfile != '')):
                        m = re.match('(?P<name>.*)_dmesg\.txt$', self.dmesgfile)
@@ -129,32 +225,37 @@ class SystemValues:
                                self.htmlfile = m.group('name')+'.html'
                if(self.htmlfile == ''):
                        self.htmlfile = 'output.html'
-       def initTestOutput(self, subdir):
-               if(not self.android):
-                       self.prefix = self.hostname
-                       v = open('/proc/version', 'r').read().strip()
-                       kver = string.split(v)[2]
-               else:
-                       self.prefix = 'android'
-                       v = os.popen(self.adb+' shell cat /proc/version').read().strip()
-                       kver = string.split(v)[2]
-               testtime = datetime.now().strftime('suspend-%m%d%y-%H%M%S')
+       def initTestOutput(self, subdir, testpath=''):
+               self.prefix = self.hostname
+               v = open('/proc/version', 'r').read().strip()
+               kver = string.split(v)[2]
+               n = datetime.now()
+               testtime = n.strftime('suspend-%m%d%y-%H%M%S')
+               if not testpath:
+                       testpath = n.strftime('suspend-%y%m%d-%H%M%S')
                if(subdir != "."):
-                       self.testdir = subdir+"/"+testtime
+                       self.testdir = subdir+"/"+testpath
                else:
-                       self.testdir = testtime
+                       self.testdir = testpath
                self.teststamp = \
                        '# '+testtime+' '+self.prefix+' '+self.suspendmode+' '+kver
+               if(self.embedded):
+                       self.dmesgfile = \
+                               '/tmp/'+testtime+'_'+self.suspendmode+'_dmesg.txt'
+                       self.ftracefile = \
+                               '/tmp/'+testtime+'_'+self.suspendmode+'_ftrace.txt'
+                       return
                self.dmesgfile = \
                        self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_dmesg.txt'
                self.ftracefile = \
                        self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_ftrace.txt'
                self.htmlfile = \
                        self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
-               os.mkdir(self.testdir)
+               if not os.path.isdir(self.testdir):
+                       os.mkdir(self.testdir)
        def setDeviceFilter(self, devnames):
                self.devicefilter = string.split(devnames)
-       def rtcWakeAlarm(self):
+       def rtcWakeAlarmOn(self):
                os.system('echo 0 > '+self.rtcpath+'/wakealarm')
                outD = open(self.rtcpath+'/date', 'r').read().strip()
                outT = open(self.rtcpath+'/time', 'r').read().strip()
@@ -172,9 +273,361 @@ class SystemValues:
                        nowtime = int(datetime.now().strftime('%s'))
                alarm = nowtime + self.rtcwaketime
                os.system('echo %d > %s/wakealarm' % (alarm, self.rtcpath))
+       def rtcWakeAlarmOff(self):
+               os.system('echo 0 > %s/wakealarm' % self.rtcpath)
+       def initdmesg(self):
+               # get the latest time stamp from the dmesg log
+               fp = os.popen('dmesg')
+               ktime = '0'
+               for line in fp:
+                       line = line.replace('\r\n', '')
+                       idx = line.find('[')
+                       if idx > 1:
+                               line = line[idx:]
+                       m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
+                       if(m):
+                               ktime = m.group('ktime')
+               fp.close()
+               self.dmesgstart = float(ktime)
+       def getdmesg(self):
+               # store all new dmesg lines since initdmesg was called
+               fp = os.popen('dmesg')
+               op = open(self.dmesgfile, 'a')
+               for line in fp:
+                       line = line.replace('\r\n', '')
+                       idx = line.find('[')
+                       if idx > 1:
+                               line = line[idx:]
+                       m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
+                       if(not m):
+                               continue
+                       ktime = float(m.group('ktime'))
+                       if ktime > self.dmesgstart:
+                               op.write(line)
+               fp.close()
+               op.close()
+       def addFtraceFilterFunctions(self, file):
+               fp = open(file)
+               list = fp.read().split('\n')
+               fp.close()
+               for i in list:
+                       if len(i) < 2:
+                               continue
+                       self.tracefuncs[i] = dict()
+       def getFtraceFilterFunctions(self, current):
+               rootCheck(True)
+               if not current:
+                       os.system('cat '+self.tpath+'available_filter_functions')
+                       return
+               fp = open(self.tpath+'available_filter_functions')
+               master = fp.read().split('\n')
+               fp.close()
+               if len(self.debugfuncs) > 0:
+                       for i in self.debugfuncs:
+                               if i in master:
+                                       print i
+                               else:
+                                       print self.colorText(i)
+               else:
+                       for i in self.tracefuncs:
+                               if 'func' in self.tracefuncs[i]:
+                                       i = self.tracefuncs[i]['func']
+                               if i in master:
+                                       print i
+                               else:
+                                       print self.colorText(i)
+       def setFtraceFilterFunctions(self, list):
+               fp = open(self.tpath+'available_filter_functions')
+               master = fp.read().split('\n')
+               fp.close()
+               flist = ''
+               for i in list:
+                       if i not in master:
+                               continue
+                       if ' [' in i:
+                               flist += i.split(' ')[0]+'\n'
+                       else:
+                               flist += i+'\n'
+               fp = open(self.tpath+'set_graph_function', 'w')
+               fp.write(flist)
+               fp.close()
+       def kprobeMatch(self, name, target):
+               if name not in self.kprobes:
+                       return False
+               if re.match(self.kprobes[name]['mask'], target):
+                       return True
+               return False
+       def basicKprobe(self, name):
+               self.kprobes[name] = {'name': name,'func': name,'args': dict(),'format': name,'mask': name}
+       def defaultKprobe(self, name, kdata):
+               k = kdata
+               for field in ['name', 'format', 'mask', 'func']:
+                       if field not in k:
+                               k[field] = name
+               archargs = 'args_'+platform.machine()
+               if archargs in k:
+                       k['args'] = k[archargs]
+               else:
+                       k['args'] = dict()
+                       k['format'] = name
+               self.kprobes[name] = k
+       def kprobeColor(self, name):
+               if name not in self.kprobes or 'color' not in self.kprobes[name]:
+                       return ''
+               return self.kprobes[name]['color']
+       def kprobeDisplayName(self, name, dataraw):
+               if name not in self.kprobes:
+                       self.basicKprobe(name)
+               data = ''
+               quote=0
+               # first remvoe any spaces inside quotes, and the quotes
+               for c in dataraw:
+                       if c == '"':
+                               quote = (quote + 1) % 2
+                       if quote and c == ' ':
+                               data += '_'
+                       elif c != '"':
+                               data += c
+               fmt, args = self.kprobes[name]['format'], self.kprobes[name]['args']
+               arglist = dict()
+               # now process the args
+               for arg in sorted(args):
+                       arglist[arg] = ''
+                       m = re.match('.* '+arg+'=(?P<arg>.*) ', data);
+                       if m:
+                               arglist[arg] = m.group('arg')
+                       else:
+                               m = re.match('.* '+arg+'=(?P<arg>.*)', data);
+                               if m:
+                                       arglist[arg] = m.group('arg')
+               out = fmt.format(**arglist)
+               out = out.replace(' ', '_').replace('"', '')
+               return out
+       def kprobeText(self, kprobe):
+               name, fmt, func, args = kprobe['name'], kprobe['format'], kprobe['func'], kprobe['args']
+               if re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', func):
+                       doError('Kprobe "%s" has format info in the function name "%s"' % (name, func), False)
+               for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', fmt):
+                       if arg not in args:
+                               doError('Kprobe "%s" is missing argument "%s"' % (name, arg), False)
+               val = 'p:%s_cal %s' % (name, func)
+               for i in sorted(args):
+                       val += ' %s=%s' % (i, args[i])
+               val += '\nr:%s_ret %s $retval\n' % (name, func)
+               return val
+       def addKprobes(self):
+               # first test each kprobe
+               print('INITIALIZING KPROBES...')
+               rejects = []
+               for name in sorted(self.kprobes):
+                       if not self.testKprobe(self.kprobes[name]):
+                               rejects.append(name)
+               # remove all failed ones from the list
+               for name in rejects:
+                       vprint('Skipping KPROBE: %s' % name)
+                       self.kprobes.pop(name)
+               self.fsetVal('', 'kprobe_events')
+               kprobeevents = ''
+               # set the kprobes all at once
+               for kp in self.kprobes:
+                       val = self.kprobeText(self.kprobes[kp])
+                       vprint('Adding KPROBE: %s\n%s' % (kp, val.strip()))
+                       kprobeevents += self.kprobeText(self.kprobes[kp])
+               self.fsetVal(kprobeevents, 'kprobe_events')
+               # verify that the kprobes were set as ordered
+               check = self.fgetVal('kprobe_events')
+               linesout = len(kprobeevents.split('\n'))
+               linesack = len(check.split('\n'))
+               if linesack < linesout:
+                       # if not, try appending the kprobes 1 by 1
+                       for kp in self.kprobes:
+                               kprobeevents = self.kprobeText(self.kprobes[kp])
+                               self.fsetVal(kprobeevents, 'kprobe_events', 'a')
+               self.fsetVal('1', 'events/kprobes/enable')
+       def testKprobe(self, kprobe):
+               kprobeevents = self.kprobeText(kprobe)
+               if not kprobeevents:
+                       return False
+               try:
+                       self.fsetVal(kprobeevents, 'kprobe_events')
+                       check = self.fgetVal('kprobe_events')
+               except:
+                       return False
+               linesout = len(kprobeevents.split('\n'))
+               linesack = len(check.split('\n'))
+               if linesack < linesout:
+                       return False
+               return True
+       def fsetVal(self, val, path, mode='w'):
+               file = self.tpath+path
+               if not os.path.exists(file):
+                       return False
+               try:
+                       fp = open(file, mode)
+                       fp.write(val)
+                       fp.close()
+               except:
+                       pass
+               return True
+       def fgetVal(self, path):
+               file = self.tpath+path
+               res = ''
+               if not os.path.exists(file):
+                       return res
+               try:
+                       fp = open(file, 'r')
+                       res = fp.read()
+                       fp.close()
+               except:
+                       pass
+               return res
+       def cleanupFtrace(self):
+               if(self.usecallgraph or self.usetraceevents):
+                       self.fsetVal('0', 'events/kprobes/enable')
+                       self.fsetVal('', 'kprobe_events')
+       def setupAllKprobes(self):
+               for name in self.tracefuncs:
+                       self.defaultKprobe(name, self.tracefuncs[name])
+               for name in self.dev_tracefuncs:
+                       self.defaultKprobe(name, self.dev_tracefuncs[name])
+       def isCallgraphFunc(self, name):
+               if len(self.debugfuncs) < 1 and self.suspendmode == 'command':
+                       return True
+               if name in self.debugfuncs:
+                       return True
+               funclist = []
+               for i in self.tracefuncs:
+                       if 'func' in self.tracefuncs[i]:
+                               funclist.append(self.tracefuncs[i]['func'])
+                       else:
+                               funclist.append(i)
+               if name in funclist:
+                       return True
+               return False
+       def initFtrace(self, testing=False):
+               tp = self.tpath
+               print('INITIALIZING FTRACE...')
+               # turn trace off
+               self.fsetVal('0', 'tracing_on')
+               self.cleanupFtrace()
+               # set the trace clock to global
+               self.fsetVal('global', 'trace_clock')
+               # set trace buffer to a huge value
+               self.fsetVal('nop', 'current_tracer')
+               self.fsetVal('100000', 'buffer_size_kb')
+               # go no further if this is just a status check
+               if testing:
+                       return
+               if self.usekprobes:
+                       # add tracefunc kprobes so long as were not using full callgraph
+                       if(not self.usecallgraph or len(self.debugfuncs) > 0):
+                               for name in self.tracefuncs:
+                                       self.defaultKprobe(name, self.tracefuncs[name])
+                               if self.usedevsrc:
+                                       for name in self.dev_tracefuncs:
+                                               self.defaultKprobe(name, self.dev_tracefuncs[name])
+                       else:
+                               self.usedevsrc = False
+                       self.addKprobes()
+               # initialize the callgraph trace, unless this is an x2 run
+               if(self.usecallgraph):
+                       # set trace type
+                       self.fsetVal('function_graph', 'current_tracer')
+                       self.fsetVal('', 'set_ftrace_filter')
+                       # set trace format options
+                       self.fsetVal('print-parent', 'trace_options')
+                       self.fsetVal('funcgraph-abstime', 'trace_options')
+                       self.fsetVal('funcgraph-cpu', 'trace_options')
+                       self.fsetVal('funcgraph-duration', 'trace_options')
+                       self.fsetVal('funcgraph-proc', 'trace_options')
+                       self.fsetVal('funcgraph-tail', 'trace_options')
+                       self.fsetVal('nofuncgraph-overhead', 'trace_options')
+                       self.fsetVal('context-info', 'trace_options')
+                       self.fsetVal('graph-time', 'trace_options')
+                       self.fsetVal('0', 'max_graph_depth')
+                       if len(self.debugfuncs) > 0:
+                               self.setFtraceFilterFunctions(self.debugfuncs)
+                       elif self.suspendmode == 'command':
+                               self.fsetVal('', 'set_graph_function')
+                       else:
+                               cf = ['dpm_run_callback']
+                               if(self.usetraceeventsonly):
+                                       cf += ['dpm_prepare', 'dpm_complete']
+                               for fn in self.tracefuncs:
+                                       if 'func' in self.tracefuncs[fn]:
+                                               cf.append(self.tracefuncs[fn]['func'])
+                                       else:
+                                               cf.append(fn)
+                               self.setFtraceFilterFunctions(cf)
+               if(self.usetraceevents):
+                       # turn trace events on
+                       events = iter(self.traceevents)
+                       for e in events:
+                               self.fsetVal('1', 'events/power/'+e+'/enable')
+               # clear the trace buffer
+               self.fsetVal('', 'trace')
+       def verifyFtrace(self):
+               # files needed for any trace data
+               files = ['buffer_size_kb', 'current_tracer', 'trace', 'trace_clock',
+                                'trace_marker', 'trace_options', 'tracing_on']
+               # files needed for callgraph trace data
+               tp = self.tpath
+               if(self.usecallgraph):
+                       files += [
+                               'available_filter_functions',
+                               'set_ftrace_filter',
+                               'set_graph_function'
+                       ]
+               for f in files:
+                       if(os.path.exists(tp+f) == False):
+                               return False
+               return True
+       def verifyKprobes(self):
+               # files needed for kprobes to work
+               files = ['kprobe_events', 'events']
+               tp = self.tpath
+               for f in files:
+                       if(os.path.exists(tp+f) == False):
+                               return False
+               return True
+       def colorText(self, str):
+               if not self.ansi:
+                       return str
+               return '\x1B[31;40m'+str+'\x1B[m'
 
 sysvals = SystemValues()
 
+# Class: DevProps
+# Description:
+#       Simple class which holds property values collected
+#       for all the devices used in the timeline.
+class DevProps:
+       syspath = ''
+       altname = ''
+       async = True
+       xtraclass = ''
+       xtrainfo = ''
+       def out(self, dev):
+               return '%s,%s,%d;' % (dev, self.altname, self.async)
+       def debug(self, dev):
+               print '%s:\n\taltname = %s\n\t  async = %s' % (dev, self.altname, self.async)
+       def altName(self, dev):
+               if not self.altname or self.altname == dev:
+                       return dev
+               return '%s [%s]' % (self.altname, dev)
+       def xtraClass(self):
+               if self.xtraclass:
+                       return ' '+self.xtraclass
+               if not self.async:
+                       return ' sync'
+               return ''
+       def xtraInfo(self):
+               if self.xtraclass:
+                       return ' '+self.xtraclass
+               if self.async:
+                       return ' async'
+               return ' sync'
+
 # Class: DeviceNode
 # Description:
 #       A container used to create a device hierachy, with a single root node
@@ -228,6 +681,7 @@ class Data:
        html_device_id = 0
        stamp = 0
        outfile = ''
+       dev_ubiquitous = ['msleep', 'udelay']
        def __init__(self, num):
                idchar = 'abcdefghijklmnopqrstuvwxyz'
                self.testnumber = num
@@ -257,6 +711,9 @@ class Data:
                                                                'row': 0, 'color': '#FFFFCC', 'order': 9}
                }
                self.phases = self.sortedPhases()
+               self.devicegroups = []
+               for phase in self.phases:
+                       self.devicegroups.append([phase])
        def getStart(self):
                return self.dmesg[self.phases[0]]['start']
        def setStart(self, time):
@@ -273,51 +730,61 @@ class Data:
                        for dev in list:
                                d = list[dev]
                                if(d['pid'] == pid and time >= d['start'] and
-                                       time <= d['end']):
+                                       time < d['end']):
                                        return False
                return True
-       def addIntraDevTraceEvent(self, action, name, pid, time):
-               if(action == 'mutex_lock_try'):
-                       color = 'red'
-               elif(action == 'mutex_lock_pass'):
-                       color = 'green'
-               elif(action == 'mutex_unlock'):
-                       color = 'blue'
-               else:
-                       # create separate colors based on the name
-                       v1 = len(name)*10 % 256
-                       v2 = string.count(name, 'e')*100 % 256
-                       v3 = ord(name[0])*20 % 256
-                       color = '#%06X' % ((v1*0x10000) + (v2*0x100) + v3)
-               for phase in self.phases:
+       def targetDevice(self, phaselist, start, end, pid=-1):
+               tgtdev = ''
+               for phase in phaselist:
                        list = self.dmesg[phase]['list']
-                       for dev in list:
-                               d = list[dev]
-                               if(d['pid'] == pid and time >= d['start'] and
-                                       time <= d['end']):
-                                       e = TraceEvent(action, name, color, time)
-                                       if('traceevents' not in d):
-                                               d['traceevents'] = []
-                                       d['traceevents'].append(e)
-                                       return d
-                                       break
-               return 0
-       def capIntraDevTraceEvent(self, action, name, pid, time):
-               for phase in self.phases:
-                       list = self.dmesg[phase]['list']
-                       for dev in list:
-                               d = list[dev]
-                               if(d['pid'] == pid and time >= d['start'] and
-                                       time <= d['end']):
-                                       if('traceevents' not in d):
-                                               return
-                                       for e in d['traceevents']:
-                                               if(e.action == action and
-                                                       e.name == name and not e.ready):
-                                                       e.length = time - e.time
-                                                       e.ready = True
-                                                       break
-                                       return
+                       for devname in list:
+                               dev = list[devname]
+                               if(pid >= 0 and dev['pid'] != pid):
+                                       continue
+                               devS = dev['start']
+                               devE = dev['end']
+                               if(start < devS or start >= devE or end <= devS or end > devE):
+                                       continue
+                               tgtdev = dev
+                               break
+               return tgtdev
+       def addDeviceFunctionCall(self, displayname, kprobename, proc, pid, start, end, cdata, rdata):
+               machstart = self.dmesg['suspend_machine']['start']
+               machend = self.dmesg['resume_machine']['end']
+               tgtdev = self.targetDevice(self.phases, start, end, pid)
+               if not tgtdev and start >= machstart and end < machend:
+                       # device calls in machine phases should be serial
+                       tgtdev = self.targetDevice(['suspend_machine', 'resume_machine'], start, end)
+               if not tgtdev:
+                       if 'scsi_eh' in proc:
+                               self.newActionGlobal(proc, start, end, pid)
+                               self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata)
+                       else:
+                               vprint('IGNORE: %s[%s](%d) [%f - %f] | %s | %s | %s' % (displayname, kprobename,
+                                       pid, start, end, cdata, rdata, proc))
+                       return False
+               # detail block fits within tgtdev
+               if('src' not in tgtdev):
+                       tgtdev['src'] = []
+               title = cdata+' '+rdata
+               mstr = '\(.*\) *(?P<args>.*) *\((?P<caller>.*)\+.* arg1=(?P<ret>.*)'
+               m = re.match(mstr, title)
+               if m:
+                       c = m.group('caller')
+                       a = m.group('args').strip()
+                       r = m.group('ret')
+                       if len(r) > 6:
+                               r = ''
+                       else:
+                               r = 'ret=%s ' % r
+                       l = '%0.3fms' % ((end - start) * 1000)
+                       if kprobename in self.dev_ubiquitous:
+                               title = '%s(%s) <- %s, %s(%s)' % (displayname, a, c, r, l)
+                       else:
+                               title = '%s(%s) %s(%s)' % (displayname, a, r, l)
+               e = TraceEvent(title, kprobename, start, end - start)
+               tgtdev['src'].append(e)
+               return True
        def trimTimeVal(self, t, t0, dT, left):
                if left:
                        if(t > t0):
@@ -353,11 +820,11 @@ class Data:
                                        cg.end = self.trimTimeVal(cg.end, t0, dT, left)
                                        for line in cg.list:
                                                line.time = self.trimTimeVal(line.time, t0, dT, left)
-                               if('traceevents' in d):
-                                       for e in d['traceevents']:
+                               if('src' in d):
+                                       for e in d['src']:
                                                e.time = self.trimTimeVal(e.time, t0, dT, left)
        def normalizeTime(self, tZero):
-               # first trim out any standby or freeze clock time
+               # trim out any standby or freeze clock time
                if(self.tSuspended != self.tResumed):
                        if(self.tResumed > tZero):
                                self.trimTime(self.tSuspended, \
@@ -365,29 +832,6 @@ class Data:
                        else:
                                self.trimTime(self.tSuspended, \
                                        self.tResumed-self.tSuspended, False)
-               # shift the timeline so that tZero is the new 0
-               self.tSuspended -= tZero
-               self.tResumed -= tZero
-               self.start -= tZero
-               self.end -= tZero
-               for phase in self.phases:
-                       p = self.dmesg[phase]
-                       p['start'] -= tZero
-                       p['end'] -= tZero
-                       list = p['list']
-                       for name in list:
-                               d = list[name]
-                               d['start'] -= tZero
-                               d['end'] -= tZero
-                               if('ftrace' in d):
-                                       cg = d['ftrace']
-                                       cg.start -= tZero
-                                       cg.end -= tZero
-                                       for line in cg.list:
-                                               line.time -= tZero
-                               if('traceevents' in d):
-                                       for e in d['traceevents']:
-                                               e.time -= tZero
        def newPhaseWithSingleAction(self, phasename, devname, start, end, color):
                for phase in self.phases:
                        self.dmesg[phase]['order'] += 1
@@ -417,6 +861,7 @@ class Data:
                        {'list': list, 'start': start, 'end': end,
                        'row': 0, 'color': color, 'order': order}
                self.phases = self.sortedPhases()
+               self.devicegroups.append([phasename])
        def setPhase(self, phase, ktime, isbegin):
                if(isbegin):
                        self.dmesg[phase]['start'] = ktime
@@ -442,7 +887,10 @@ class Data:
                for devname in phaselist:
                        dev = phaselist[devname]
                        if(dev['end'] < 0):
-                               dev['end'] = end
+                               for p in self.phases:
+                                       if self.dmesg[p]['end'] > dev['start']:
+                                               dev['end'] = self.dmesg[p]['end']
+                                               break
                                vprint('%s (%s): callback didnt return' % (devname, phase))
        def deviceFilter(self, devicefilter):
                # remove all by the relatives of the filter devnames
@@ -472,22 +920,58 @@ class Data:
                # if any calls never returned, clip them at system resume end
                for phase in self.phases:
                        self.fixupInitcalls(phase, self.getEnd())
-       def newActionGlobal(self, name, start, end):
+       def isInsideTimeline(self, start, end):
+               if(self.start <= start and self.end > start):
+                       return True
+               return False
+       def phaseOverlap(self, phases):
+               rmgroups = []
+               newgroup = []
+               for group in self.devicegroups:
+                       for phase in phases:
+                               if phase not in group:
+                                       continue
+                               for p in group:
+                                       if p not in newgroup:
+                                               newgroup.append(p)
+                               if group not in rmgroups:
+                                       rmgroups.append(group)
+               for group in rmgroups:
+                       self.devicegroups.remove(group)
+               self.devicegroups.append(newgroup)
+       def newActionGlobal(self, name, start, end, pid=-1, color=''):
+               # if event starts before timeline start, expand timeline
+               if(start < self.start):
+                       self.setStart(start)
+               # if event ends after timeline end, expand the timeline
+               if(end > self.end):
+                       self.setEnd(end)
                # which phase is this device callback or action "in"
                targetphase = "none"
+               htmlclass = ''
                overlap = 0.0
+               phases = []
                for phase in self.phases:
                        pstart = self.dmesg[phase]['start']
                        pend = self.dmesg[phase]['end']
                        o = max(0, min(end, pend) - max(start, pstart))
-                       if(o > overlap):
+                       if o > 0:
+                               phases.append(phase)
+                       if o > overlap:
+                               if overlap > 0 and phase == 'post_resume':
+                                       continue
                                targetphase = phase
                                overlap = o
+               if pid == -2:
+                       htmlclass = ' bg'
+               if len(phases) > 1:
+                       htmlclass = ' bg'
+                       self.phaseOverlap(phases)
                if targetphase in self.phases:
-                       self.newAction(targetphase, name, -1, '', start, end, '')
-                       return True
+                       newname = self.newAction(targetphase, name, pid, '', start, end, '', htmlclass, color)
+                       return (targetphase, newname)
                return False
-       def newAction(self, phase, name, pid, parent, start, end, drv):
+       def newAction(self, phase, name, pid, parent, start, end, drv, htmlclass='', color=''):
                # new device callback for a specific phase
                self.html_device_id += 1
                devid = '%s%d' % (self.idstr, self.html_device_id)
@@ -495,8 +979,19 @@ class Data:
                length = -1.0
                if(start >= 0 and end >= 0):
                        length = end - start
+               if pid == -2:
+                       i = 2
+                       origname = name
+                       while(name in list):
+                               name = '%s[%d]' % (origname, i)
+                               i += 1
                list[name] = {'start': start, 'end': end, 'pid': pid, 'par': parent,
                                          'length': length, 'row': 0, 'id': devid, 'drv': drv }
+               if htmlclass:
+                       list[name]['htmlclass'] = htmlclass
+               if color:
+                       list[name]['color'] = color
+               return name
        def deviceIDs(self, devlist, phase):
                idlist = []
                list = self.dmesg[phase]['list']
@@ -536,10 +1031,21 @@ class Data:
                        vprint('    %16s: %f - %f (%d devices)' % (phase, \
                                self.dmesg[phase]['start'], self.dmesg[phase]['end'], dc))
                vprint('            test end: %f' % self.end)
+       def deviceChildrenAllPhases(self, devname):
+               devlist = []
+               for phase in self.phases:
+                       list = self.deviceChildren(devname, phase)
+                       for dev in list:
+                               if dev not in devlist:
+                                       devlist.append(dev)
+               return devlist
        def masterTopology(self, name, list, depth):
                node = DeviceNode(name, depth)
                for cname in list:
-                       clist = self.deviceChildren(cname, 'resume')
+                       # avoid recursions
+                       if name == cname:
+                               continue
+                       clist = self.deviceChildrenAllPhases(cname)
                        cnode = self.masterTopology(cname, clist, depth+1)
                        node.children.append(cnode)
                return node
@@ -580,7 +1086,8 @@ class Data:
                        list = self.dmesg[phase]['list']
                        for dev in list:
                                pdev = list[dev]['par']
-                               if(re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
+                               pid = list[dev]['pid']
+                               if(pid < 0 or re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
                                        continue
                                if pdev and pdev not in real and pdev not in rootlist:
                                        rootlist.append(pdev)
@@ -589,22 +1096,33 @@ class Data:
                rootlist = self.rootDeviceList()
                master = self.masterTopology('', rootlist, 0)
                return self.printTopology(master)
+       def selectTimelineDevices(self, widfmt, tTotal, mindevlen):
+               # only select devices that will actually show up in html
+               self.tdevlist = dict()
+               for phase in self.dmesg:
+                       devlist = []
+                       list = self.dmesg[phase]['list']
+                       for dev in list:
+                               length = (list[dev]['end'] - list[dev]['start']) * 1000
+                               width = widfmt % (((list[dev]['end']-list[dev]['start'])*100)/tTotal)
+                               if width != '0.000000' and length >= mindevlen:
+                                       devlist.append(dev)
+                       self.tdevlist[phase] = devlist
 
 # Class: TraceEvent
 # Description:
 #       A container for trace event data found in the ftrace file
 class TraceEvent:
-       ready = False
-       name = ''
+       text = ''
        time = 0.0
-       color = '#FFFFFF'
        length = 0.0
-       action = ''
-       def __init__(self, a, n, c, t):
-               self.action = a
-               self.name = n
-               self.color = c
+       title = ''
+       row = 0
+       def __init__(self, a, n, t, l):
+               self.title = a
+               self.text = n
                self.time = t
+               self.length = l
 
 # Class: FTraceLine
 # Description:
@@ -623,11 +1141,14 @@ class FTraceLine:
        fcall = False
        freturn = False
        fevent = False
+       fkprobe = False
        depth = 0
        name = ''
        type = ''
-       def __init__(self, t, m, d):
+       def __init__(self, t, m='', d=''):
                self.time = float(t)
+               if not m and not d:
+                       return
                # is this a trace event
                if(d == 'traceevent' or re.match('^ *\/\* *(?P<msg>.*) \*\/ *$', m)):
                        if(d == 'traceevent'):
@@ -644,6 +1165,18 @@ class FTraceLine:
                                self.type = emm.group('call')
                        else:
                                self.name = msg
+                       km = re.match('^(?P<n>.*)_cal$', self.type)
+                       if km:
+                               self.fcall = True
+                               self.fkprobe = True
+                               self.type = km.group('n')
+                               return
+                       km = re.match('^(?P<n>.*)_ret$', self.type)
+                       if km:
+                               self.freturn = True
+                               self.fkprobe = True
+                               self.type = km.group('n')
+                               return
                        self.fevent = True
                        return
                # convert the duration to seconds
@@ -662,7 +1195,7 @@ class FTraceLine:
                                # includes comment with function name
                                match = re.match('^} *\/\* *(?P<n>.*) *\*\/$', m)
                                if(match):
-                                       self.name = match.group('n')
+                                       self.name = match.group('n').strip()
                # function call
                else:
                        self.fcall = True
@@ -670,19 +1203,19 @@ class FTraceLine:
                        if(m[-1] == '{'):
                                match = re.match('^(?P<n>.*) *\(.*', m)
                                if(match):
-                                       self.name = match.group('n')
+                                       self.name = match.group('n').strip()
                        # function call with no children (leaf)
                        elif(m[-1] == ';'):
                                self.freturn = True
                                match = re.match('^(?P<n>.*) *\(.*', m)
                                if(match):
-                                       self.name = match.group('n')
+                                       self.name = match.group('n').strip()
                        # something else (possibly a trace marker)
                        else:
                                self.name = m
        def getDepth(self, str):
                return len(str)/2
-       def debugPrint(self, dev):
+       def debugPrint(self, dev=''):
                if(self.freturn and self.fcall):
                        print('%s -- %f (%02d): %s(); (%.3f us)' % (dev, self.time, \
                                self.depth, self.name, self.length*1000000))
@@ -692,6 +1225,33 @@ class FTraceLine:
                else:
                        print('%s -- %f (%02d): %s() { (%.3f us)' % (dev, self.time, \
                                self.depth, self.name, self.length*1000000))
+       def startMarker(self):
+               global sysvals
+               # Is this the starting line of a suspend?
+               if not self.fevent:
+                       return False
+               if sysvals.usetracemarkers:
+                       if(self.name == 'SUSPEND START'):
+                               return True
+                       return False
+               else:
+                       if(self.type == 'suspend_resume' and
+                               re.match('suspend_enter\[.*\] begin', self.name)):
+                               return True
+                       return False
+       def endMarker(self):
+               # Is this the ending line of a resume?
+               if not self.fevent:
+                       return False
+               if sysvals.usetracemarkers:
+                       if(self.name == 'RESUME COMPLETE'):
+                               return True
+                       return False
+               else:
+                       if(self.type == 'suspend_resume' and
+                               re.match('thaw_processes\[.*\] end', self.name)):
+                               return True
+                       return False
 
 # Class: FTraceCallGraph
 # Description:
@@ -705,54 +1265,124 @@ class FTraceCallGraph:
        list = []
        invalid = False
        depth = 0
-       def __init__(self):
+       pid = 0
+       def __init__(self, pid):
                self.start = -1.0
                self.end = -1.0
                self.list = []
                self.depth = 0
-       def setDepth(self, line):
+               self.pid = pid
+       def addLine(self, line, debug=False):
+               # if this is already invalid, just leave
+               if(self.invalid):
+                       return False
+               # invalidate on too much data or bad depth
+               if(len(self.list) >= 1000000 or self.depth < 0):
+                       self.invalidate(line)
+                       return False
+               # compare current depth with this lines pre-call depth
+               prelinedep = line.depth
+               if(line.freturn and not line.fcall):
+                       prelinedep += 1
+               last = 0
+               lasttime = line.time
+               virtualfname = 'execution_misalignment'
+               if len(self.list) > 0:
+                       last = self.list[-1]
+                       lasttime = last.time
+               # handle low misalignments by inserting returns
+               if prelinedep < self.depth:
+                       if debug and last:
+                               print '-------- task %d --------' % self.pid
+                               last.debugPrint()
+                       idx = 0
+                       # add return calls to get the depth down
+                       while prelinedep < self.depth:
+                               if debug:
+                                       print 'MISALIGN LOW (add returns): C%d - eC%d' % (self.depth, prelinedep)
+                               self.depth -= 1
+                               if idx == 0 and last and last.fcall and not last.freturn:
+                                       # special case, turn last call into a leaf
+                                       last.depth = self.depth
+                                       last.freturn = True
+                                       last.length = line.time - last.time
+                                       if debug:
+                                               last.debugPrint()
+                               else:
+                                       vline = FTraceLine(lasttime)
+                                       vline.depth = self.depth
+                                       vline.name = virtualfname
+                                       vline.freturn = True
+                                       self.list.append(vline)
+                                       if debug:
+                                               vline.debugPrint()
+                               idx += 1
+                       if debug:
+                               line.debugPrint()
+                               print ''
+               # handle high misalignments by inserting calls
+               elif prelinedep > self.depth:
+                       if debug and last:
+                               print '-------- task %d --------' % self.pid
+                               last.debugPrint()
+                       idx = 0
+                       # add calls to get the depth up
+                       while prelinedep > self.depth:
+                               if debug:
+                                       print 'MISALIGN HIGH (add calls): C%d - eC%d' % (self.depth, prelinedep)
+                               if idx == 0 and line.freturn and not line.fcall:
+                                       # special case, turn this return into a leaf
+                                       line.fcall = True
+                                       prelinedep -= 1
+                               else:
+                                       vline = FTraceLine(lasttime)
+                                       vline.depth = self.depth
+                                       vline.name = virtualfname
+                                       vline.fcall = True
+                                       if debug:
+                                               vline.debugPrint()
+                                       self.list.append(vline)
+                                       self.depth += 1
+                                       if not last:
+                                               self.start = vline.time
+                               idx += 1
+                       if debug:
+                               line.debugPrint()
+                               print ''
+               # process the call and set the new depth
                if(line.fcall and not line.freturn):
-                       line.depth = self.depth
                        self.depth += 1
                elif(line.freturn and not line.fcall):
                        self.depth -= 1
-                       line.depth = self.depth
-               else:
-                       line.depth = self.depth
-       def addLine(self, line, match):
-               if(not self.invalid):
-                       self.setDepth(line)
+               if len(self.list) < 1:
+                       self.start = line.time
+               self.list.append(line)
                if(line.depth == 0 and line.freturn):
                        if(self.start < 0):
                                self.start = line.time
                        self.end = line.time
-                       self.list.append(line)
+                       if line.fcall:
+                               self.end += line.length
+                       if self.list[0].name == virtualfname:
+                               self.invalid = True
                        return True
-               if(self.invalid):
-                       return False
-               if(len(self.list) >= 1000000 or self.depth < 0):
-                       if(len(self.list) > 0):
-                               first = self.list[0]
-                               self.list = []
-                               self.list.append(first)
-                       self.invalid = True
-                       if(not match):
-                               return False
-                       id = 'task %s cpu %s' % (match.group('pid'), match.group('cpu'))
-                       window = '(%f - %f)' % (self.start, line.time)
-                       if(self.depth < 0):
-                               print('Too much data for '+id+\
-                                       ' (buffer overflow), ignoring this callback')
-                       else:
-                               print('Too much data for '+id+\
-                                       ' '+window+', ignoring this callback')
-                       return False
-               self.list.append(line)
-               if(self.start < 0):
-                       self.start = line.time
                return False
+       def invalidate(self, line):
+               if(len(self.list) > 0):
+                       first = self.list[0]
+                       self.list = []
+                       self.list.append(first)
+               self.invalid = True
+               id = 'task %s' % (self.pid)
+               window = '(%f - %f)' % (self.start, line.time)
+               if(self.depth < 0):
+                       vprint('Too much data for '+id+\
+                               ' (buffer overflow), ignoring this callback')
+               else:
+                       vprint('Too much data for '+id+\
+                               ' '+window+', ignoring this callback')
        def slice(self, t0, tN):
-               minicg = FTraceCallGraph()
+               minicg = FTraceCallGraph(0)
                count = -1
                firstdepth = 0
                for l in self.list:
@@ -764,13 +1394,26 @@ class FTraceCallGraph:
                                firstdepth = l.depth
                                count = 0
                        l.depth -= firstdepth
-                       minicg.addLine(l, 0)
+                       minicg.addLine(l)
                        if((count == 0 and l.freturn and l.fcall) or
                                (count > 0 and l.depth <= 0)):
                                break
                        count += 1
                return minicg
-       def sanityCheck(self):
+       def repair(self, enddepth):
+               # bring the depth back to 0 with additional returns
+               fixed = False
+               last = self.list[-1]
+               for i in reversed(range(enddepth)):
+                       t = FTraceLine(last.time)
+                       t.depth = i
+                       t.freturn = True
+                       fixed = self.addLine(t)
+                       if fixed:
+                               self.end = last.time
+                               return True
+               return False
+       def postProcess(self, debug=False):
                stack = dict()
                cnt = 0
                for l in self.list:
@@ -779,98 +1422,317 @@ class FTraceCallGraph:
                                cnt += 1
                        elif(l.freturn and not l.fcall):
                                if(l.depth not in stack):
+                                       if debug:
+                                               print 'Post Process Error: Depth missing'
+                                               l.debugPrint()
                                        return False
+                               # transfer total time from return line to call line
                                stack[l.depth].length = l.length
-                               stack[l.depth] = 0
+                               stack.pop(l.depth)
                                l.length = 0
                                cnt -= 1
                if(cnt == 0):
+                       # trace caught the whole call tree
                        return True
-               return False
-       def debugPrint(self, filename):
-               if(filename == 'stdout'):
-                       print('[%f - %f]') % (self.start, self.end)
-                       for l in self.list:
-                               if(l.freturn and l.fcall):
-                                       print('%f (%02d): %s(); (%.3f us)' % (l.time, \
-                                               l.depth, l.name, l.length*1000000))
-                               elif(l.freturn):
-                                       print('%f (%02d): %s} (%.3f us)' % (l.time, \
-                                               l.depth, l.name, l.length*1000000))
-                               else:
-                                       print('%f (%02d): %s() { (%.3f us)' % (l.time, \
-                                               l.depth, l.name, l.length*1000000))
-                       print(' ')
-               else:
-                       fp = open(filename, 'w')
-                       print(filename)
-                       for l in self.list:
-                               if(l.freturn and l.fcall):
-                                       fp.write('%f (%02d): %s(); (%.3f us)\n' % (l.time, \
-                                               l.depth, l.name, l.length*1000000))
-                               elif(l.freturn):
-                                       fp.write('%f (%02d): %s} (%.3f us)\n' % (l.time, \
-                                               l.depth, l.name, l.length*1000000))
-                               else:
-                                       fp.write('%f (%02d): %s() { (%.3f us)\n' % (l.time, \
-                                               l.depth, l.name, l.length*1000000))
-                       fp.close()
+               elif(cnt < 0):
+                       if debug:
+                               print 'Post Process Error: Depth is less than 0'
+                       return False
+               # trace ended before call tree finished
+               return self.repair(cnt)
+       def deviceMatch(self, pid, data):
+               found = False
+               # add the callgraph data to the device hierarchy
+               borderphase = {
+                       'dpm_prepare': 'suspend_prepare',
+                       'dpm_complete': 'resume_complete'
+               }
+               if(self.list[0].name in borderphase):
+                       p = borderphase[self.list[0].name]
+                       list = data.dmesg[p]['list']
+                       for devname in list:
+                               dev = list[devname]
+                               if(pid == dev['pid'] and
+                                       self.start <= dev['start'] and
+                                       self.end >= dev['end']):
+                                       dev['ftrace'] = self.slice(dev['start'], dev['end'])
+                                       found = True
+                       return found
+               for p in data.phases:
+                       if(data.dmesg[p]['start'] <= self.start and
+                               self.start <= data.dmesg[p]['end']):
+                               list = data.dmesg[p]['list']
+                               for devname in list:
+                                       dev = list[devname]
+                                       if(pid == dev['pid'] and
+                                               self.start <= dev['start'] and
+                                               self.end >= dev['end']):
+                                               dev['ftrace'] = self
+                                               found = True
+                                               break
+                               break
+               return found
+       def newActionFromFunction(self, data):
+               name = self.list[0].name
+               if name in ['dpm_run_callback', 'dpm_prepare', 'dpm_complete']:
+                       return
+               fs = self.start
+               fe = self.end
+               if fs < data.start or fe > data.end:
+                       return
+               phase = ''
+               for p in data.phases:
+                       if(data.dmesg[p]['start'] <= self.start and
+                               self.start < data.dmesg[p]['end']):
+                               phase = p
+                               break
+               if not phase:
+                       return
+               out = data.newActionGlobal(name, fs, fe, -2)
+               if out:
+                       phase, myname = out
+                       data.dmesg[phase]['list'][myname]['ftrace'] = self
+       def debugPrint(self):
+               print('[%f - %f] %s (%d)') % (self.start, self.end, self.list[0].name, self.pid)
+               for l in self.list:
+                       if(l.freturn and l.fcall):
+                               print('%f (%02d): %s(); (%.3f us)' % (l.time, \
+                                       l.depth, l.name, l.length*1000000))
+                       elif(l.freturn):
+                               print('%f (%02d): %s} (%.3f us)' % (l.time, \
+                                       l.depth, l.name, l.length*1000000))
+                       else:
+                               print('%f (%02d): %s() { (%.3f us)' % (l.time, \
+                                       l.depth, l.name, l.length*1000000))
+               print(' ')
 
 # Class: Timeline
 # Description:
-#       A container for a suspend/resume html timeline. In older versions
-#       of the script there were multiple timelines, but in the latest
-#       there is only one.
+#       A container for a device timeline which calculates
+#       all the html properties to display it correctly
 class Timeline:
        html = {}
-       scaleH = 0.0 # height of the row as a percent of the timeline height
-       rowH = 0.0 # height of each row in percent of the timeline height
-       row_height_pixels = 30
-       maxrows = 0
-       height = 0
-       def __init__(self):
+       height = 0      # total timeline height
+       scaleH = 20     # timescale (top) row height
+       rowH = 30       # device row height
+       bodyH = 0       # body height
+       rows = 0        # total timeline rows
+       phases = []
+       rowmaxlines = dict()
+       rowcount = dict()
+       rowheight = dict()
+       def __init__(self, rowheight):
+               self.rowH = rowheight
                self.html = {
+                       'header': '',
                        'timeline': '',
                        'legend': '',
-                       'scale': ''
                }
-       def setRows(self, rows):
-               self.maxrows = int(rows)
-               self.scaleH = 100.0/float(self.maxrows)
-               self.height = self.maxrows*self.row_height_pixels
-               r = float(self.maxrows - 1)
-               if(r < 1.0):
-                       r = 1.0
-               self.rowH = (100.0 - self.scaleH)/r
+       # Function: getDeviceRows
+       # Description:
+       #    determine how may rows the device funcs will take
+       # Arguments:
+       #        rawlist: the list of devices/actions for a single phase
+       # Output:
+       #        The total number of rows needed to display this phase of the timeline
+       def getDeviceRows(self, rawlist):
+               # clear all rows and set them to undefined
+               lendict = dict()
+               for item in rawlist:
+                       item.row = -1
+                       lendict[item] = item.length
+               list = []
+               for i in sorted(lendict, key=lendict.get, reverse=True):
+                       list.append(i)
+               remaining = len(list)
+               rowdata = dict()
+               row = 1
+               # try to pack each row with as many ranges as possible
+               while(remaining > 0):
+                       if(row not in rowdata):
+                               rowdata[row] = []
+                       for i in list:
+                               if(i.row >= 0):
+                                       continue
+                               s = i.time
+                               e = i.time + i.length
+                               valid = True
+                               for ritem in rowdata[row]:
+                                       rs = ritem.time
+                                       re = ritem.time + ritem.length
+                                       if(not (((s <= rs) and (e <= rs)) or
+                                               ((s >= re) and (e >= re)))):
+                                               valid = False
+                                               break
+                               if(valid):
+                                       rowdata[row].append(i)
+                                       i.row = row
+                                       remaining -= 1
+                       row += 1
+               return row
+       # Function: getPhaseRows
+       # Description:
+       #        Organize the timeline entries into the smallest
+       #        number of rows possible, with no entry overlapping
+       # Arguments:
+       #        list: the list of devices/actions for a single phase
+       #        devlist: string list of device names to use
+       # Output:
+       #        The total number of rows needed to display this phase of the timeline
+       def getPhaseRows(self, dmesg, devlist):
+               # clear all rows and set them to undefined
+               remaining = len(devlist)
+               rowdata = dict()
+               row = 0
+               lendict = dict()
+               myphases = []
+               for item in devlist:
+                       if item[0] not in self.phases:
+                               self.phases.append(item[0])
+                       if item[0] not in myphases:
+                               myphases.append(item[0])
+                               self.rowmaxlines[item[0]] = dict()
+                               self.rowheight[item[0]] = dict()
+                       dev = dmesg[item[0]]['list'][item[1]]
+                       dev['row'] = -1
+                       lendict[item] = float(dev['end']) - float(dev['start'])
+                       if 'src' in dev:
+                               dev['devrows'] = self.getDeviceRows(dev['src'])
+               lenlist = []
+               for i in sorted(lendict, key=lendict.get, reverse=True):
+                       lenlist.append(i)
+               orderedlist = []
+               for item in lenlist:
+                       dev = dmesg[item[0]]['list'][item[1]]
+                       if dev['pid'] == -2:
+                               orderedlist.append(item)
+               for item in lenlist:
+                       if item not in orderedlist:
+                               orderedlist.append(item)
+               # try to pack each row with as many ranges as possible
+               while(remaining > 0):
+                       rowheight = 1
+                       if(row not in rowdata):
+                               rowdata[row] = []
+                       for item in orderedlist:
+                               dev = dmesg[item[0]]['list'][item[1]]
+                               if(dev['row'] < 0):
+                                       s = dev['start']
+                                       e = dev['end']
+                                       valid = True
+                                       for ritem in rowdata[row]:
+                                               rs = ritem['start']
+                                               re = ritem['end']
+                                               if(not (((s <= rs) and (e <= rs)) or
+                                                       ((s >= re) and (e >= re)))):
+                                                       valid = False
+                                                       break
+                                       if(valid):
+                                               rowdata[row].append(dev)
+                                               dev['row'] = row
+                                               remaining -= 1
+                                               if 'devrows' in dev and dev['devrows'] > rowheight:
+                                                       rowheight = dev['devrows']
+                       for phase in myphases:
+                               self.rowmaxlines[phase][row] = rowheight
+                               self.rowheight[phase][row] = rowheight * self.rowH
+                       row += 1
+               if(row > self.rows):
+                       self.rows = int(row)
+               for phase in myphases:
+                       self.rowcount[phase] = row
+               return row
+       def phaseRowHeight(self, phase, row):
+               return self.rowheight[phase][row]
+       def phaseRowTop(self, phase, row):
+               top = 0
+               for i in sorted(self.rowheight[phase]):
+                       if i >= row:
+                               break
+                       top += self.rowheight[phase][i]
+               return top
+       # Function: calcTotalRows
+       # Description:
+       #        Calculate the heights and offsets for the header and rows
+       def calcTotalRows(self):
+               maxrows = 0
+               standardphases = []
+               for phase in self.phases:
+                       total = 0
+                       for i in sorted(self.rowmaxlines[phase]):
+                               total += self.rowmaxlines[phase][i]
+                       if total > maxrows:
+                               maxrows = total
+                       if total == self.rowcount[phase]:
+                               standardphases.append(phase)
+               self.height = self.scaleH + (maxrows*self.rowH)
+               self.bodyH = self.height - self.scaleH
+               for phase in standardphases:
+                       for i in sorted(self.rowheight[phase]):
+                               self.rowheight[phase][i] = self.bodyH/self.rowcount[phase]
+       # Function: createTimeScale
+       # Description:
+       #        Create the timescale for a timeline block
+       # Arguments:
+       #        m0: start time (mode begin)
+       #        mMax: end time (mode end)
+       #        tTotal: total timeline time
+       #        mode: suspend or resume
+       # Output:
+       #        The html code needed to display the time scale
+       def createTimeScale(self, m0, mMax, tTotal, mode):
+               timescale = '<div class="t" style="right:{0}%">{1}</div>\n'
+               rline = '<div class="t" style="left:0;border-left:1px solid black;border-right:0;">Resume</div>\n'
+               output = '<div class="timescale">\n'
+               # set scale for timeline
+               mTotal = mMax - m0
+               tS = 0.1
+               if(tTotal <= 0):
+                       return output+'</div>\n'
+               if(tTotal > 4):
+                       tS = 1
+               divTotal = int(mTotal/tS) + 1
+               divEdge = (mTotal - tS*(divTotal-1))*100/mTotal
+               for i in range(divTotal):
+                       htmlline = ''
+                       if(mode == 'resume'):
+                               pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal))
+                               val = '%0.fms' % (float(i)*tS*1000)
+                               htmlline = timescale.format(pos, val)
+                               if(i == 0):
+                                       htmlline = rline
+                       else:
+                               pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal) - divEdge)
+                               val = '%0.fms' % (float(i-divTotal+1)*tS*1000)
+                               if(i == divTotal - 1):
+                                       val = 'Suspend'
+                               htmlline = timescale.format(pos, val)
+                       output += htmlline
+               output += '</div>\n'
+               return output
 
-# Class: TestRun
+# Class: TestProps
 # Description:
-#       A container for a suspend/resume test run. This is necessary as
-#       there could be more than one, and they need to be separate.
-class TestRun:
+#       A list of values describing the properties of these test runs
+class TestProps:
+       stamp = ''
+       tracertype = ''
+       S0i3 = False
+       fwdata = []
        ftrace_line_fmt_fg = \
                '^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\
                ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\
-               '[ +!]*(?P<dur>[0-9\.]*) .*\|  (?P<msg>.*)'
+               '[ +!#\*@$]*(?P<dur>[0-9\.]*) .*\|  (?P<msg>.*)'
        ftrace_line_fmt_nop = \
                ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\[(?P<cpu>[0-9]*)\] *'+\
                '(?P<flags>.{4}) *(?P<time>[0-9\.]*): *'+\
                '(?P<msg>.*)'
        ftrace_line_fmt = ftrace_line_fmt_nop
        cgformat = False
-       ftemp = dict()
-       ttemp = dict()
-       inthepipe = False
-       tracertype = ''
        data = 0
-       def __init__(self, dataobj):
-               self.data = dataobj
-               self.ftemp = dict()
-               self.ttemp = dict()
-       def isReady(self):
-               if(tracertype == '' or not data):
-                       return False
-               return True
+       ktemp = dict()
+       def __init__(self):
+               self.ktemp = dict()
        def setTracerType(self, tracer):
                self.tracertype = tracer
                if(tracer == 'function_graph'):
@@ -881,6 +1743,19 @@ class TestRun:
                else:
                        doError('Invalid tracer format: [%s]' % tracer, False)
 
+# Class: TestRun
+# Description:
+#       A container for a suspend/resume test run. This is necessary as
+#       there could be more than one, and they need to be separate.
+class TestRun:
+       ftemp = dict()
+       ttemp = dict()
+       data = 0
+       def __init__(self, dataobj):
+               self.data = dataobj
+               self.ftemp = dict()
+               self.ttemp = dict()
+
 # ----------------- FUNCTIONS --------------------
 
 # Function: vprint
@@ -893,104 +1768,16 @@ def vprint(msg):
        if(sysvals.verbose):
                print(msg)
 
-# Function: initFtrace
-# Description:
-#       Configure ftrace to use trace events and/or a callgraph
-def initFtrace():
-       global sysvals
-
-       tp = sysvals.tpath
-       cf = 'dpm_run_callback'
-       if(sysvals.usetraceeventsonly):
-               cf = '-e dpm_prepare -e dpm_complete -e dpm_run_callback'
-       if(sysvals.usecallgraph or sysvals.usetraceevents):
-               print('INITIALIZING FTRACE...')
-               # turn trace off
-               os.system('echo 0 > '+tp+'tracing_on')
-               # set the trace clock to global
-               os.system('echo global > '+tp+'trace_clock')
-               # set trace buffer to a huge value
-               os.system('echo nop > '+tp+'current_tracer')
-               os.system('echo 100000 > '+tp+'buffer_size_kb')
-               # initialize the callgraph trace, unless this is an x2 run
-               if(sysvals.usecallgraph and sysvals.execcount == 1):
-                       # set trace type
-                       os.system('echo function_graph > '+tp+'current_tracer')
-                       os.system('echo "" > '+tp+'set_ftrace_filter')
-                       # set trace format options
-                       os.system('echo funcgraph-abstime > '+tp+'trace_options')
-                       os.system('echo funcgraph-proc > '+tp+'trace_options')
-                       # focus only on device suspend and resume
-                       os.system('cat '+tp+'available_filter_functions | grep '+\
-                               cf+' > '+tp+'set_graph_function')
-               if(sysvals.usetraceevents):
-                       # turn trace events on
-                       events = iter(sysvals.traceevents)
-                       for e in events:
-                               os.system('echo 1 > '+sysvals.epath+e+'/enable')
-               # clear the trace buffer
-               os.system('echo "" > '+tp+'trace')
-
-# Function: initFtraceAndroid
-# Description:
-#       Configure ftrace to capture trace events
-def initFtraceAndroid():
-       global sysvals
-
-       tp = sysvals.tpath
-       if(sysvals.usetraceevents):
-               print('INITIALIZING FTRACE...')
-               # turn trace off
-               os.system(sysvals.adb+" shell 'echo 0 > "+tp+"tracing_on'")
-               # set the trace clock to global
-               os.system(sysvals.adb+" shell 'echo global > "+tp+"trace_clock'")
-               # set trace buffer to a huge value
-               os.system(sysvals.adb+" shell 'echo nop > "+tp+"current_tracer'")
-               os.system(sysvals.adb+" shell 'echo 10000 > "+tp+"buffer_size_kb'")
-               # turn trace events on
-               events = iter(sysvals.traceevents)
-               for e in events:
-                       os.system(sysvals.adb+" shell 'echo 1 > "+\
-                               sysvals.epath+e+"/enable'")
-               # clear the trace buffer
-               os.system(sysvals.adb+" shell 'echo \"\" > "+tp+"trace'")
-
-# Function: verifyFtrace
-# Description:
-#       Check that ftrace is working on the system
-# Output:
-#       True or False
-def verifyFtrace():
-       global sysvals
-       # files needed for any trace data
-       files = ['buffer_size_kb', 'current_tracer', 'trace', 'trace_clock',
-                        'trace_marker', 'trace_options', 'tracing_on']
-       # files needed for callgraph trace data
-       tp = sysvals.tpath
-       if(sysvals.usecallgraph):
-               files += [
-                       'available_filter_functions',
-                       'set_ftrace_filter',
-                       'set_graph_function'
-               ]
-       for f in files:
-               if(sysvals.android):
-                       out = os.popen(sysvals.adb+' shell ls '+tp+f).read().strip()
-                       if(out != tp+f):
-                               return False
-               else:
-                       if(os.path.exists(tp+f) == False):
-                               return False
-       return True
-
 # Function: parseStamp
 # Description:
 #       Pull in the stamp comment line from the data file(s),
 #       create the stamp, and add it to the global sysvals object
 # Arguments:
 #       m: the valid re.match output for the stamp line
-def parseStamp(m, data):
+def parseStamp(line, data):
        global sysvals
+
+       m = re.match(sysvals.stampfmt, line)
        data.stamp = {'time': '', 'host': '', 'mode': ''}
        dt = datetime(int(m.group('y'))+2000, int(m.group('m')),
                int(m.group('d')), int(m.group('H')), int(m.group('M')),
@@ -999,6 +1786,7 @@ def parseStamp(m, data):
        data.stamp['host'] = m.group('host')
        data.stamp['mode'] = m.group('mode')
        data.stamp['kernel'] = m.group('kernel')
+       sysvals.hostname = data.stamp['host']
        sysvals.suspendmode = data.stamp['mode']
        if not sysvals.stamp:
                sysvals.stamp = data.stamp
@@ -1031,14 +1819,35 @@ def diffStamp(stamp1, stamp2):
 def doesTraceLogHaveTraceEvents():
        global sysvals
 
+       # check for kprobes
+       sysvals.usekprobes = False
+       out = os.system('grep -q "_cal: (" '+sysvals.ftracefile)
+       if(out == 0):
+               sysvals.usekprobes = True
+       # check for callgraph data on trace event blocks
+       out = os.system('grep -q "_cpu_down()" '+sysvals.ftracefile)
+       if(out == 0):
+               sysvals.usekprobes = True
+       out = os.popen('head -1 '+sysvals.ftracefile).read().replace('\n', '')
+       m = re.match(sysvals.stampfmt, out)
+       if m and m.group('mode') == 'command':
+               sysvals.usetraceeventsonly = True
+               sysvals.usetraceevents = True
+               return
+       # figure out what level of trace events are supported
        sysvals.usetraceeventsonly = True
        sysvals.usetraceevents = False
        for e in sysvals.traceevents:
-               out = os.popen('cat '+sysvals.ftracefile+' | grep "'+e+': "').read()
-               if(not out):
+               out = os.system('grep -q "'+e+': " '+sysvals.ftracefile)
+               if(out != 0):
                        sysvals.usetraceeventsonly = False
-               if(e == 'suspend_resume' and out):
+               if(e == 'suspend_resume' and out == 0):
                        sysvals.usetraceevents = True
+       # determine is this log is properly formatted
+       for e in ['SUSPEND START', 'RESUME COMPLETE']:
+               out = os.system('grep -q "'+e+'" '+sysvals.ftracefile)
+               if(out != 0):
+                       sysvals.usetracemarkers = False
 
 # Function: appendIncompleteTraceLog
 # Description:
@@ -1055,44 +1864,42 @@ def appendIncompleteTraceLog(testruns):
 
        # create TestRun vessels for ftrace parsing
        testcnt = len(testruns)
-       testidx = -1
+       testidx = 0
        testrun = []
        for data in testruns:
                testrun.append(TestRun(data))
 
        # extract the callgraph and traceevent data
        vprint('Analyzing the ftrace data...')
+       tp = TestProps()
        tf = open(sysvals.ftracefile, 'r')
+       data = 0
        for line in tf:
                # remove any latent carriage returns
                line = line.replace('\r\n', '')
-               # grab the time stamp first (signifies the start of the test run)
+               # grab the time stamp
                m = re.match(sysvals.stampfmt, line)
                if(m):
-                       testidx += 1
-                       parseStamp(m, testrun[testidx].data)
-                       continue
-               # pull out any firmware data
-               if(re.match(sysvals.firmwarefmt, line)):
-                       continue
-               # if we havent found a test time stamp yet keep spinning til we do
-               if(testidx < 0):
+                       tp.stamp = line
                        continue
                # determine the trace data type (required for further parsing)
                m = re.match(sysvals.tracertypefmt, line)
                if(m):
-                       tracer = m.group('t')
-                       testrun[testidx].setTracerType(tracer)
+                       tp.setTracerType(m.group('t'))
+                       continue
+               # device properties line
+               if(re.match(sysvals.devpropfmt, line)):
+                       devProps(line)
                        continue
-               # parse only valid lines, if this isnt one move on
-               m = re.match(testrun[testidx].ftrace_line_fmt, line)
+               # parse only valid lines, if this is not one move on
+               m = re.match(tp.ftrace_line_fmt, line)
                if(not m):
                        continue
                # gather the basic message data from the line
                m_time = m.group('time')
                m_pid = m.group('pid')
                m_msg = m.group('msg')
-               if(testrun[testidx].cgformat):
+               if(tp.cgformat):
                        m_param3 = m.group('dur')
                else:
                        m_param3 = 'traceevent'
@@ -1104,119 +1911,114 @@ def appendIncompleteTraceLog(testruns):
                # the line should be a call, return, or event
                if(not t.fcall and not t.freturn and not t.fevent):
                        continue
-               # only parse the ftrace data during suspend/resume
-               data = testrun[testidx].data
-               if(not testrun[testidx].inthepipe):
-                       # look for the suspend start marker
-                       if(t.fevent):
-                               if(t.name == 'SUSPEND START'):
-                                       testrun[testidx].inthepipe = True
-                                       data.setStart(t.time)
+               # look for the suspend start marker
+               if(t.startMarker()):
+                       data = testrun[testidx].data
+                       parseStamp(tp.stamp, data)
+                       data.setStart(t.time)
+                       continue
+               if(not data):
+                       continue
+               # find the end of resume
+               if(t.endMarker()):
+                       data.setEnd(t.time)
+                       testidx += 1
+                       if(testidx >= testcnt):
+                               break
+                       continue
+               # trace event processing
+               if(t.fevent):
+                       # general trace events have two types, begin and end
+                       if(re.match('(?P<name>.*) begin$', t.name)):
+                               isbegin = True
+                       elif(re.match('(?P<name>.*) end$', t.name)):
+                               isbegin = False
+                       else:
                                continue
-               else:
-                       # trace event processing
-                       if(t.fevent):
-                               if(t.name == 'RESUME COMPLETE'):
-                                       testrun[testidx].inthepipe = False
-                                       data.setEnd(t.time)
-                                       if(testidx == testcnt - 1):
-                                               break
-                                       continue
-                               # general trace events have two types, begin and end
-                               if(re.match('(?P<name>.*) begin$', t.name)):
-                                       isbegin = True
-                               elif(re.match('(?P<name>.*) end$', t.name)):
-                                       isbegin = False
+                       m = re.match('(?P<name>.*)\[(?P<val>[0-9]*)\] .*', t.name)
+                       if(m):
+                               val = m.group('val')
+                               if val == '0':
+                                       name = m.group('name')
                                else:
-                                       continue
-                               m = re.match('(?P<name>.*)\[(?P<val>[0-9]*)\] .*', t.name)
-                               if(m):
-                                       val = m.group('val')
-                                       if val == '0':
-                                               name = m.group('name')
-                                       else:
-                                               name = m.group('name')+'['+val+']'
+                                       name = m.group('name')+'['+val+']'
+                       else:
+                               m = re.match('(?P<name>.*) .*', t.name)
+                               name = m.group('name')
+                       # special processing for trace events
+                       if re.match('dpm_prepare\[.*', name):
+                               continue
+                       elif re.match('machine_suspend.*', name):
+                               continue
+                       elif re.match('suspend_enter\[.*', name):
+                               if(not isbegin):
+                                       data.dmesg['suspend_prepare']['end'] = t.time
+                               continue
+                       elif re.match('dpm_suspend\[.*', name):
+                               if(not isbegin):
+                                       data.dmesg['suspend']['end'] = t.time
+                               continue
+                       elif re.match('dpm_suspend_late\[.*', name):
+                               if(isbegin):
+                                       data.dmesg['suspend_late']['start'] = t.time
                                else:
-                                       m = re.match('(?P<name>.*) .*', t.name)
-                                       name = m.group('name')
-                               # special processing for trace events
-                               if re.match('dpm_prepare\[.*', name):
-                                       continue
-                               elif re.match('machine_suspend.*', name):
-                                       continue
-                               elif re.match('suspend_enter\[.*', name):
-                                       if(not isbegin):
-                                               data.dmesg['suspend_prepare']['end'] = t.time
-                                       continue
-                               elif re.match('dpm_suspend\[.*', name):
-                                       if(not isbegin):
-                                               data.dmesg['suspend']['end'] = t.time
-                                       continue
-                               elif re.match('dpm_suspend_late\[.*', name):
-                                       if(isbegin):
-                                               data.dmesg['suspend_late']['start'] = t.time
-                                       else:
-                                               data.dmesg['suspend_late']['end'] = t.time
-                                       continue
-                               elif re.match('dpm_suspend_noirq\[.*', name):
-                                       if(isbegin):
-                                               data.dmesg['suspend_noirq']['start'] = t.time
-                                       else:
-                                               data.dmesg['suspend_noirq']['end'] = t.time
-                                       continue
-                               elif re.match('dpm_resume_noirq\[.*', name):
-                                       if(isbegin):
-                                               data.dmesg['resume_machine']['end'] = t.time
-                                               data.dmesg['resume_noirq']['start'] = t.time
-                                       else:
-                                               data.dmesg['resume_noirq']['end'] = t.time
-                                       continue
-                               elif re.match('dpm_resume_early\[.*', name):
-                                       if(isbegin):
-                                               data.dmesg['resume_early']['start'] = t.time
-                                       else:
-                                               data.dmesg['resume_early']['end'] = t.time
-                                       continue
-                               elif re.match('dpm_resume\[.*', name):
-                                       if(isbegin):
-                                               data.dmesg['resume']['start'] = t.time
-                                       else:
-                                               data.dmesg['resume']['end'] = t.time
-                                       continue
-                               elif re.match('dpm_complete\[.*', name):
-                                       if(isbegin):
-                                               data.dmesg['resume_complete']['start'] = t.time
-                                       else:
-                                               data.dmesg['resume_complete']['end'] = t.time
-                                       continue
-                               # is this trace event outside of the devices calls
-                               if(data.isTraceEventOutsideDeviceCalls(pid, t.time)):
-                                       # global events (outside device calls) are simply graphed
-                                       if(isbegin):
-                                               # store each trace event in ttemp
-                                               if(name not in testrun[testidx].ttemp):
-                                                       testrun[testidx].ttemp[name] = []
-                                               testrun[testidx].ttemp[name].append(\
-                                                       {'begin': t.time, 'end': t.time})
-                                       else:
-                                               # finish off matching trace event in ttemp
-                                               if(name in testrun[testidx].ttemp):
-                                                       testrun[testidx].ttemp[name][-1]['end'] = t.time
+                                       data.dmesg['suspend_late']['end'] = t.time
+                               continue
+                       elif re.match('dpm_suspend_noirq\[.*', name):
+                               if(isbegin):
+                                       data.dmesg['suspend_noirq']['start'] = t.time
                                else:
-                                       if(isbegin):
-                                               data.addIntraDevTraceEvent('', name, pid, t.time)
-                                       else:
-                                               data.capIntraDevTraceEvent('', name, pid, t.time)
-                       # call/return processing
-                       elif sysvals.usecallgraph:
-                               # create a callgraph object for the data
-                               if(pid not in testrun[testidx].ftemp):
-                                       testrun[testidx].ftemp[pid] = []
-                                       testrun[testidx].ftemp[pid].append(FTraceCallGraph())
-                               # when the call is finished, see which device matches it
-                               cg = testrun[testidx].ftemp[pid][-1]
-                               if(cg.addLine(t, m)):
-                                       testrun[testidx].ftemp[pid].append(FTraceCallGraph())
+                                       data.dmesg['suspend_noirq']['end'] = t.time
+                               continue
+                       elif re.match('dpm_resume_noirq\[.*', name):
+                               if(isbegin):
+                                       data.dmesg['resume_machine']['end'] = t.time
+                                       data.dmesg['resume_noirq']['start'] = t.time
+                               else:
+                                       data.dmesg['resume_noirq']['end'] = t.time
+                               continue
+                       elif re.match('dpm_resume_early\[.*', name):
+                               if(isbegin):
+                                       data.dmesg['resume_early']['start'] = t.time
+                               else:
+                                       data.dmesg['resume_early']['end'] = t.time
+                               continue
+                       elif re.match('dpm_resume\[.*', name):
+                               if(isbegin):
+                                       data.dmesg['resume']['start'] = t.time
+                               else:
+                                       data.dmesg['resume']['end'] = t.time
+                               continue
+                       elif re.match('dpm_complete\[.*', name):
+                               if(isbegin):
+                                       data.dmesg['resume_complete']['start'] = t.time
+                               else:
+                                       data.dmesg['resume_complete']['end'] = t.time
+                               continue
+                       # skip trace events inside devices calls
+                       if(not data.isTraceEventOutsideDeviceCalls(pid, t.time)):
+                               continue
+                       # global events (outside device calls) are simply graphed
+                       if(isbegin):
+                               # store each trace event in ttemp
+                               if(name not in testrun[testidx].ttemp):
+                                       testrun[testidx].ttemp[name] = []
+                               testrun[testidx].ttemp[name].append(\
+                                       {'begin': t.time, 'end': t.time})
+                       else:
+                               # finish off matching trace event in ttemp
+                               if(name in testrun[testidx].ttemp):
+                                       testrun[testidx].ttemp[name][-1]['end'] = t.time
+               # call/return processing
+               elif sysvals.usecallgraph:
+                       # create a callgraph object for the data
+                       if(pid not in testrun[testidx].ftemp):
+                               testrun[testidx].ftemp[pid] = []
+                               testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid))
+                       # when the call is finished, see which device matches it
+                       cg = testrun[testidx].ftemp[pid][-1]
+                       if(cg.addLine(t)):
+                               testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid))
        tf.close()
 
        for test in testrun:
@@ -1224,20 +2026,14 @@ def appendIncompleteTraceLog(testruns):
                if(sysvals.usetraceevents):
                        for name in test.ttemp:
                                for event in test.ttemp[name]:
-                                       begin = event['begin']
-                                       end = event['end']
-                                       # if event starts before timeline start, expand timeline
-                                       if(begin < test.data.start):
-                                               test.data.setStart(begin)
-                                       # if event ends after timeline end, expand the timeline
-                                       if(end > test.data.end):
-                                               test.data.setEnd(end)
-                                       test.data.newActionGlobal(name, begin, end)
+                                       test.data.newActionGlobal(name, event['begin'], event['end'])
 
                # add the callgraph data to the device hierarchy
                for pid in test.ftemp:
                        for cg in test.ftemp[pid]:
-                               if(not cg.sanityCheck()):
+                               if len(cg.list) < 1 or cg.invalid:
+                                       continue
+                               if(not cg.postProcess()):
                                        id = 'task %s cpu %s' % (pid, m.group('cpu'))
                                        vprint('Sanity check failed for '+\
                                                id+', ignoring this callback')
@@ -1259,14 +2055,6 @@ def appendIncompleteTraceLog(testruns):
                if(sysvals.verbose):
                        test.data.printDetails()
 
-
-       # add the time in between the tests as a new phase so we can see it
-       if(len(testruns) > 1):
-               t1e = testruns[0].getEnd()
-               t2s = testruns[-1].getStart()
-               testruns[-1].newPhaseWithSingleAction('user mode', \
-                       'user mode', t1e, t2s, '#FF9966')
-
 # Function: parseTraceLog
 # Description:
 #       Analyze an ftrace log output file generated from this app during
@@ -1280,9 +2068,16 @@ def parseTraceLog():
 
        vprint('Analyzing the ftrace data...')
        if(os.path.exists(sysvals.ftracefile) == False):
-               doError('%s doesnt exist' % sysvals.ftracefile, False)
+               doError('%s does not exist' % sysvals.ftracefile, False)
+
+       sysvals.setupAllKprobes()
+       tracewatch = ['suspend_enter']
+       if sysvals.usekprobes:
+               tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend',
+                       'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON', 'CPU_OFF']
 
        # extract the callgraph and traceevent data
+       tp = TestProps()
        testruns = []
        testdata = []
        testrun = 0
@@ -1295,27 +2090,17 @@ def parseTraceLog():
                # stamp line: each stamp means a new test run
                m = re.match(sysvals.stampfmt, line)
                if(m):
-                       data = Data(len(testdata))
-                       testdata.append(data)
-                       testrun = TestRun(data)
-                       testruns.append(testrun)
-                       parseStamp(m, data)
-                       continue
-               if(not data):
+                       tp.stamp = line
                        continue
                # firmware line: pull out any firmware data
                m = re.match(sysvals.firmwarefmt, line)
                if(m):
-                       data.fwSuspend = int(m.group('s'))
-                       data.fwResume = int(m.group('r'))
-                       if(data.fwSuspend > 0 or data.fwResume > 0):
-                               data.fwValid = True
+                       tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
                        continue
                # tracer type line: determine the trace data type
                m = re.match(sysvals.tracertypefmt, line)
                if(m):
-                       tracer = m.group('t')
-                       testrun.setTracerType(tracer)
+                       tp.setTracerType(m.group('t'))
                        continue
                # post resume time line: did this test run include post-resume data
                m = re.match(sysvals.postresumefmt, line)
@@ -1324,15 +2109,20 @@ def parseTraceLog():
                        if(t > 0):
                                sysvals.postresumetime = t
                        continue
+               # device properties line
+               if(re.match(sysvals.devpropfmt, line)):
+                       devProps(line)
+                       continue
                # ftrace line: parse only valid lines
-               m = re.match(testrun.ftrace_line_fmt, line)
+               m = re.match(tp.ftrace_line_fmt, line)
                if(not m):
                        continue
                # gather the basic message data from the line
                m_time = m.group('time')
+               m_proc = m.group('proc')
                m_pid = m.group('pid')
                m_msg = m.group('msg')
-               if(testrun.cgformat):
+               if(tp.cgformat):
                        m_param3 = m.group('dur')
                else:
                        m_param3 = 'traceevent'
@@ -1344,24 +2134,38 @@ def parseTraceLog():
                # the line should be a call, return, or event
                if(not t.fcall and not t.freturn and not t.fevent):
                        continue
-               # only parse the ftrace data during suspend/resume
-               if(not testrun.inthepipe):
-                       # look for the suspend start marker
-                       if(t.fevent):
-                               if(t.name == 'SUSPEND START'):
-                                       testrun.inthepipe = True
-                                       data.setStart(t.time)
+               # find the start of suspend
+               if(t.startMarker()):
+                       phase = 'suspend_prepare'
+                       data = Data(len(testdata))
+                       testdata.append(data)
+                       testrun = TestRun(data)
+                       testruns.append(testrun)
+                       parseStamp(tp.stamp, data)
+                       if len(tp.fwdata) > data.testnumber:
+                               data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
+                               if(data.fwSuspend > 0 or data.fwResume > 0):
+                                       data.fwValid = True
+                       data.setStart(t.time)
+                       continue
+               if(not data):
+                       continue
+               # find the end of resume
+               if(t.endMarker()):
+                       if(sysvals.usetracemarkers and sysvals.postresumetime > 0):
+                               phase = 'post_resume'
+                               data.newPhase(phase, t.time, t.time, '#F0F0F0', -1)
+                       data.setEnd(t.time)
+                       if(not sysvals.usetracemarkers):
+                               # no trace markers? then quit and be sure to finish recording
+                               # the event we used to trigger resume end
+                               if(len(testrun.ttemp['thaw_processes']) > 0):
+                                       # if an entry exists, assume this is its end
+                                       testrun.ttemp['thaw_processes'][-1]['end'] = t.time
+                               break
                        continue
                # trace event processing
                if(t.fevent):
-                       if(t.name == 'RESUME COMPLETE'):
-                               if(sysvals.postresumetime > 0):
-                                       phase = 'post_resume'
-                                       data.newPhase(phase, t.time, t.time, '#FF9966', -1)
-                               else:
-                                       testrun.inthepipe = False
-                               data.setEnd(t.time)
-                               continue
                        if(phase == 'post_resume'):
                                data.setEnd(t.time)
                        if(t.type == 'suspend_resume'):
@@ -1383,8 +2187,7 @@ def parseTraceLog():
                                        m = re.match('(?P<name>.*) .*', t.name)
                                        name = m.group('name')
                                # ignore these events
-                               if(re.match('acpi_suspend\[.*', t.name) or
-                                       re.match('suspend_enter\[.*', name)):
+                               if(name.split('[')[0] in tracewatch):
                                        continue
                                # -- phase changes --
                                # suspend_prepare start
@@ -1418,7 +2221,7 @@ def parseTraceLog():
                                                data.dmesg[phase]['end'] = t.time
                                                data.tSuspended = t.time
                                        else:
-                                               if(sysvals.suspendmode in ['mem', 'disk']):
+                                               if(sysvals.suspendmode in ['mem', 'disk'] and not tp.S0i3):
                                                        data.dmesg['suspend_machine']['end'] = t.time
                                                        data.tSuspended = t.time
                                                phase = 'resume_machine'
@@ -1426,6 +2229,15 @@ def parseTraceLog():
                                                data.tResumed = t.time
                                                data.tLow = data.tResumed - data.tSuspended
                                        continue
+                               # acpi_suspend
+                               elif(re.match('acpi_suspend\[.*', t.name)):
+                                       # acpi_suspend[0] S0i3
+                                       if(re.match('acpi_suspend\[0\] begin', t.name)):
+                                               if(sysvals.suspendmode == 'mem'):
+                                                       tp.S0i3 = True
+                                                       data.dmesg['suspend_machine']['end'] = t.time
+                                                       data.tSuspended = t.time
+                                       continue
                                # resume_noirq start
                                elif(re.match('dpm_resume_noirq\[.*', t.name)):
                                        phase = 'resume_noirq'
@@ -1449,30 +2261,25 @@ def parseTraceLog():
                                        if(isbegin):
                                                data.dmesg[phase]['start'] = t.time
                                        continue
-
-                               # is this trace event outside of the devices calls
-                               if(data.isTraceEventOutsideDeviceCalls(pid, t.time)):
-                                       # global events (outside device calls) are simply graphed
-                                       if(name not in testrun.ttemp):
-                                               testrun.ttemp[name] = []
-                                       if(isbegin):
-                                               # create a new list entry
-                                               testrun.ttemp[name].append(\
-                                                       {'begin': t.time, 'end': t.time})
-                                       else:
-                                               if(len(testrun.ttemp[name]) > 0):
-                                                       # if an antry exists, assume this is its end
-                                                       testrun.ttemp[name][-1]['end'] = t.time
-                                               elif(phase == 'post_resume'):
-                                                       # post resume events can just have ends
-                                                       testrun.ttemp[name].append({
-                                                               'begin': data.dmesg[phase]['start'],
-                                                               'end': t.time})
+                               # skip trace events inside devices calls
+                               if(not data.isTraceEventOutsideDeviceCalls(pid, t.time)):
+                                       continue
+                               # global events (outside device calls) are graphed
+                               if(name not in testrun.ttemp):
+                                       testrun.ttemp[name] = []
+                               if(isbegin):
+                                       # create a new list entry
+                                       testrun.ttemp[name].append(\
+                                               {'begin': t.time, 'end': t.time, 'pid': pid})
                                else:
-                                       if(isbegin):
-                                               data.addIntraDevTraceEvent('', name, pid, t.time)
-                                       else:
-                                               data.capIntraDevTraceEvent('', name, pid, t.time)
+                                       if(len(testrun.ttemp[name]) > 0):
+                                               # if an entry exists, assume this is its end
+                                               testrun.ttemp[name][-1]['end'] = t.time
+                                       elif(phase == 'post_resume'):
+                                               # post resume events can just have ends
+                                               testrun.ttemp[name].append({
+                                                       'begin': data.dmesg[phase]['start'],
+                                                       'end': t.time})
                        # device callback start
                        elif(t.type == 'device_pm_callback_start'):
                                m = re.match('(?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*',\
@@ -1495,75 +2302,127 @@ def parseTraceLog():
                                        dev = list[n]
                                        dev['length'] = t.time - dev['start']
                                        dev['end'] = t.time
+               # kprobe event processing
+               elif(t.fkprobe):
+                       kprobename = t.type
+                       kprobedata = t.name
+                       key = (kprobename, pid)
+                       # displayname is generated from kprobe data
+                       displayname = ''
+                       if(t.fcall):
+                               displayname = sysvals.kprobeDisplayName(kprobename, kprobedata)
+                               if not displayname:
+                                       continue
+                               if(key not in tp.ktemp):
+                                       tp.ktemp[key] = []
+                               tp.ktemp[key].append({
+                                       'pid': pid,
+                                       'begin': t.time,
+                                       'end': t.time,
+                                       'name': displayname,
+                                       'cdata': kprobedata,
+                                       'proc': m_proc,
+                               })
+                       elif(t.freturn):
+                               if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
+                                       continue
+                               e = tp.ktemp[key][-1]
+                               if e['begin'] < 0.0 or t.time - e['begin'] < 0.000001:
+                                       tp.ktemp[key].pop()
+                               else:
+                                       e['end'] = t.time
+                                       e['rdata'] = kprobedata
                # callgraph processing
                elif sysvals.usecallgraph:
-                       # this shouldn't happen, but JIC, ignore callgraph data post-res
-                       if(phase == 'post_resume'):
-                               continue
                        # create a callgraph object for the data
-                       if(pid not in testrun.ftemp):
-                               testrun.ftemp[pid] = []
-                               testrun.ftemp[pid].append(FTraceCallGraph())
+                       key = (m_proc, pid)
+                       if(key not in testrun.ftemp):
+                               testrun.ftemp[key] = []
+                               testrun.ftemp[key].append(FTraceCallGraph(pid))
                        # when the call is finished, see which device matches it
-                       cg = testrun.ftemp[pid][-1]
-                       if(cg.addLine(t, m)):
-                               testrun.ftemp[pid].append(FTraceCallGraph())
+                       cg = testrun.ftemp[key][-1]
+                       if(cg.addLine(t)):
+                               testrun.ftemp[key].append(FTraceCallGraph(pid))
        tf.close()
 
+       if sysvals.suspendmode == 'command':
+               for test in testruns:
+                       for p in test.data.phases:
+                               if p == 'resume_complete':
+                                       test.data.dmesg[p]['start'] = test.data.start
+                                       test.data.dmesg[p]['end'] = test.data.end
+                               else:
+                                       test.data.dmesg[p]['start'] = test.data.start
+                                       test.data.dmesg[p]['end'] = test.data.start
+                       test.data.tSuspended = test.data.start
+                       test.data.tResumed = test.data.start
+                       test.data.tLow = 0
+                       test.data.fwValid = False
+
        for test in testruns:
                # add the traceevent data to the device hierarchy
                if(sysvals.usetraceevents):
+                       # add actual trace funcs
                        for name in test.ttemp:
                                for event in test.ttemp[name]:
-                                       begin = event['begin']
-                                       end = event['end']
-                                       # if event starts before timeline start, expand timeline
-                                       if(begin < test.data.start):
-                                               test.data.setStart(begin)
-                                       # if event ends after timeline end, expand the timeline
-                                       if(end > test.data.end):
-                                               test.data.setEnd(end)
-                                       test.data.newActionGlobal(name, begin, end)
-
-               # add the callgraph data to the device hierarchy
-               borderphase = {
-                       'dpm_prepare': 'suspend_prepare',
-                       'dpm_complete': 'resume_complete'
-               }
-               for pid in test.ftemp:
-                       for cg in test.ftemp[pid]:
-                               if len(cg.list) < 2:
+                                       test.data.newActionGlobal(name, event['begin'], event['end'], event['pid'])
+                       # add the kprobe based virtual tracefuncs as actual devices
+                       for key in tp.ktemp:
+                               name, pid = key
+                               if name not in sysvals.tracefuncs:
                                        continue
-                               if(not cg.sanityCheck()):
-                                       id = 'task %s cpu %s' % (pid, m.group('cpu'))
-                                       vprint('Sanity check failed for '+\
-                                               id+', ignoring this callback')
-                                       continue
-                               callstart = cg.start
-                               callend = cg.end
-                               if(cg.list[0].name in borderphase):
-                                       p = borderphase[cg.list[0].name]
-                                       list = test.data.dmesg[p]['list']
-                                       for devname in list:
-                                               dev = list[devname]
-                                               if(pid == dev['pid'] and
-                                                       callstart <= dev['start'] and
-                                                       callend >= dev['end']):
-                                                       dev['ftrace'] = cg.slice(dev['start'], dev['end'])
-                                       continue
-                               if(cg.list[0].name != 'dpm_run_callback'):
+                               for e in tp.ktemp[key]:
+                                       kb, ke = e['begin'], e['end']
+                                       if kb == ke or not test.data.isInsideTimeline(kb, ke):
+                                               continue
+                                       test.data.newActionGlobal(e['name'], kb, ke, pid)
+                       # add config base kprobes and dev kprobes
+                       for key in tp.ktemp:
+                               name, pid = key
+                               if name in sysvals.tracefuncs:
                                        continue
-                               for p in test.data.phases:
-                                       if(test.data.dmesg[p]['start'] <= callstart and
-                                               callstart <= test.data.dmesg[p]['end']):
-                                               list = test.data.dmesg[p]['list']
-                                               for devname in list:
-                                                       dev = list[devname]
-                                                       if(pid == dev['pid'] and
-                                                               callstart <= dev['start'] and
-                                                               callend >= dev['end']):
-                                                               dev['ftrace'] = cg
-                                               break
+                               for e in tp.ktemp[key]:
+                                       kb, ke = e['begin'], e['end']
+                                       if kb == ke or not test.data.isInsideTimeline(kb, ke):
+                                               continue
+                                       color = sysvals.kprobeColor(e['name'])
+                                       if name not in sysvals.dev_tracefuncs:
+                                               # config base kprobe
+                                               test.data.newActionGlobal(e['name'], kb, ke, -2, color)
+                                       elif sysvals.usedevsrc:
+                                               # dev kprobe
+                                               data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
+                                                       ke, e['cdata'], e['rdata'])
+               if sysvals.usecallgraph:
+                       # add the callgraph data to the device hierarchy
+                       sortlist = dict()
+                       for key in test.ftemp:
+                               proc, pid = key
+                               for cg in test.ftemp[key]:
+                                       if len(cg.list) < 1 or cg.invalid:
+                                               continue
+                                       if(not cg.postProcess()):
+                                               id = 'task %s' % (pid)
+                                               vprint('Sanity check failed for '+\
+                                                       id+', ignoring this callback')
+                                               continue
+                                       # match cg data to devices
+                                       if sysvals.suspendmode == 'command' or not cg.deviceMatch(pid, test.data):
+                                               sortkey = '%f%f%d' % (cg.start, cg.end, pid)
+                                               sortlist[sortkey] = cg
+                       # create blocks for orphan cg data
+                       for sortkey in sorted(sortlist):
+                               cg = sortlist[sortkey]
+                               name = cg.list[0].name
+                               if sysvals.isCallgraphFunc(name):
+                                       vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name))
+                                       cg.newActionFromFunction(test.data)
+
+       if sysvals.suspendmode == 'command':
+               if(sysvals.verbose):
+                       for data in testdata:
+                               data.printDetails()
+               return testdata
 
        # fill in any missing phases
        for data in testdata:
@@ -1587,14 +2446,52 @@ def parseTraceLog():
                if(sysvals.verbose):
                        data.printDetails()
 
-       # add the time in between the tests as a new phase so we can see it
-       if(len(testdata) > 1):
-               t1e = testdata[0].getEnd()
-               t2s = testdata[-1].getStart()
-               testdata[-1].newPhaseWithSingleAction('user mode', \
-                       'user mode', t1e, t2s, '#FF9966')
        return testdata
 
+# Function: loadRawKernelLog
+# Description:
+#       Load a raw kernel log that wasn't created by this tool, it might be
+#       possible to extract a valid suspend/resume log
+def loadRawKernelLog(dmesgfile):
+       global sysvals
+
+       stamp = {'time': '', 'host': '', 'mode': 'mem', 'kernel': ''}
+       stamp['time'] = datetime.now().strftime('%B %d %Y, %I:%M:%S %p')
+       stamp['host'] = sysvals.hostname
+
+       testruns = []
+       data = 0
+       lf = open(dmesgfile, 'r')
+       for line in lf:
+               line = line.replace('\r\n', '')
+               idx = line.find('[')
+               if idx > 1:
+                       line = line[idx:]
+               m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
+               if(not m):
+                       continue
+               msg = m.group("msg")
+               m = re.match('PM: Syncing filesystems.*', msg)
+               if(m):
+                       if(data):
+                               testruns.append(data)
+                       data = Data(len(testruns))
+                       data.stamp = stamp
+               if(data):
+                       m = re.match('.* *(?P<k>[0-9]\.[0-9]{2}\.[0-9]-.*) .*', msg)
+                       if(m):
+                               stamp['kernel'] = m.group('k')
+                       m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
+                       if(m):
+                               stamp['mode'] = m.group('m')
+                       data.dmesgtext.append(line)
+       if(data):
+               testruns.append(data)
+               sysvals.stamp = stamp
+               sysvals.suspendmode = stamp['mode']
+       lf.close()
+       return testruns
+
 # Function: loadKernelLog
 # Description:
 #       [deprecated for kernel 3.15.0 or newer]
@@ -1607,9 +2504,10 @@ def loadKernelLog():
 
        vprint('Analyzing the dmesg data...')
        if(os.path.exists(sysvals.dmesgfile) == False):
-               doError('%s doesnt exist' % sysvals.dmesgfile, False)
+               doError('%s does not exist' % sysvals.dmesgfile, False)
 
-       # there can be multiple test runs in a single file delineated by stamps
+       # there can be multiple test runs in a single file
+       tp = TestProps()
        testruns = []
        data = 0
        lf = open(sysvals.dmesgfile, 'r')
@@ -1620,35 +2518,43 @@ def loadKernelLog():
                        line = line[idx:]
                m = re.match(sysvals.stampfmt, line)
                if(m):
-                       if(data):
-                               testruns.append(data)
-                       data = Data(len(testruns))
-                       parseStamp(m, data)
-                       continue
-               if(not data):
+                       tp.stamp = line
                        continue
                m = re.match(sysvals.firmwarefmt, line)
                if(m):
-                       data.fwSuspend = int(m.group('s'))
-                       data.fwResume = int(m.group('r'))
-                       if(data.fwSuspend > 0 or data.fwResume > 0):
-                               data.fwValid = True
+                       tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
                        continue
                m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
-               if(m):
-                       data.dmesgtext.append(line)
-                       if(re.match('ACPI: resume from mwait', m.group('msg'))):
-                               print('NOTE: This suspend appears to be freeze rather than'+\
-                                       ' %s, it will be treated as such' % sysvals.suspendmode)
-                               sysvals.suspendmode = 'freeze'
-               else:
-                       vprint('ignoring dmesg line: %s' % line.replace('\n', ''))
-       testruns.append(data)
+               if(not m):
+                       continue
+               msg = m.group("msg")
+               if(re.match('PM: Syncing filesystems.*', msg)):
+                       if(data):
+                               testruns.append(data)
+                       data = Data(len(testruns))
+                       parseStamp(tp.stamp, data)
+                       if len(tp.fwdata) > data.testnumber:
+                               data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
+                               if(data.fwSuspend > 0 or data.fwResume > 0):
+                                       data.fwValid = True
+               if(re.match('ACPI: resume from mwait', msg)):
+                       print('NOTE: This suspend appears to be freeze rather than'+\
+                               ' %s, it will be treated as such' % sysvals.suspendmode)
+                       sysvals.suspendmode = 'freeze'
+               if(not data):
+                       continue
+               data.dmesgtext.append(line)
+       if(data):
+               testruns.append(data)
        lf.close()
 
-       if(not data):
-               print('ERROR: analyze_suspend header missing from dmesg log')
-               sys.exit()
+       if(len(testruns) < 1):
+               # bad log, but see if you can extract something meaningful anyway
+               testruns = loadRawKernelLog(sysvals.dmesgfile)
+
+       if(len(testruns) < 1):
+               doError(' dmesg log is completely unreadable: %s' \
+                       % sysvals.dmesgfile, False)
 
        # fix lines with same timestamp/function with the call and return swapped
        for data in testruns:
@@ -1865,7 +2771,8 @@ def parseKernelLog(data):
                                                actions[a] = []
                                        actions[a].append({'begin': ktime, 'end': ktime})
                                if(re.match(at[a]['emsg'], msg)):
-                                       actions[a][-1]['end'] = ktime
+                                       if(a in actions):
+                                               actions[a][-1]['end'] = ktime
                        # now look for CPU on/off events
                        if(re.match('Disabling non-boot CPUs .*', msg)):
                                # start of first cpu suspend
@@ -1912,15 +2819,7 @@ def parseKernelLog(data):
        # fill in any actions we've found
        for name in actions:
                for event in actions[name]:
-                       begin = event['begin']
-                       end = event['end']
-                       # if event starts before timeline start, expand timeline
-                       if(begin < data.start):
-                               data.setStart(begin)
-                       # if event ends after timeline end, expand the timeline
-                       if(end > data.end):
-                               data.setEnd(end)
-                       data.newActionGlobal(name, begin, end)
+                       data.newActionGlobal(name, event['begin'], event['end'])
 
        if(sysvals.verbose):
                data.printDetails()
@@ -1929,92 +2828,6 @@ def parseKernelLog(data):
        data.fixupInitcallsThatDidntReturn()
        return True
 
-# Function: setTimelineRows
-# Description:
-#       Organize the timeline entries into the smallest
-#       number of rows possible, with no entry overlapping
-# Arguments:
-#       list: the list of devices/actions for a single phase
-#       sortedkeys: cronologically sorted key list to use
-# Output:
-#       The total number of rows needed to display this phase of the timeline
-def setTimelineRows(list, sortedkeys):
-
-       # clear all rows and set them to undefined
-       remaining = len(list)
-       rowdata = dict()
-       row = 0
-       for item in list:
-               list[item]['row'] = -1
-
-       # try to pack each row with as many ranges as possible
-       while(remaining > 0):
-               if(row not in rowdata):
-                       rowdata[row] = []
-               for item in sortedkeys:
-                       if(list[item]['row'] < 0):
-                               s = list[item]['start']
-                               e = list[item]['end']
-                               valid = True
-                               for ritem in rowdata[row]:
-                                       rs = ritem['start']
-                                       re = ritem['end']
-                                       if(not (((s <= rs) and (e <= rs)) or
-                                               ((s >= re) and (e >= re)))):
-                                               valid = False
-                                               break
-                               if(valid):
-                                       rowdata[row].append(list[item])
-                                       list[item]['row'] = row
-                                       remaining -= 1
-               row += 1
-       return row
-
-# Function: createTimeScale
-# Description:
-#       Create the timescale header for the html timeline
-# Arguments:
-#       t0: start time (suspend begin)
-#       tMax: end time (resume end)
-#       tSuspend: time when suspend occurs, i.e. the zero time
-# Output:
-#       The html code needed to display the time scale
-def createTimeScale(t0, tMax, tSuspended):
-       timescale = '<div class="t" style="right:{0}%">{1}</div>\n'
-       output = '<div id="timescale">\n'
-
-       # set scale for timeline
-       tTotal = tMax - t0
-       tS = 0.1
-       if(tTotal <= 0):
-               return output
-       if(tTotal > 4):
-               tS = 1
-       if(tSuspended < 0):
-               for i in range(int(tTotal/tS)+1):
-                       pos = '%0.3f' % (100 - ((float(i)*tS*100)/tTotal))
-                       if(i > 0):
-                               val = '%0.fms' % (float(i)*tS*1000)
-                       else:
-                               val = ''
-                       output += timescale.format(pos, val)
-       else:
-               tSuspend = tSuspended - t0
-               divTotal = int(tTotal/tS) + 1
-               divSuspend = int(tSuspend/tS)
-               s0 = (tSuspend - tS*divSuspend)*100/tTotal
-               for i in range(divTotal):
-                       pos = '%0.3f' % (100 - ((float(i)*tS*100)/tTotal) - s0)
-                       if((i == 0) and (s0 < 3)):
-                               val = ''
-                       elif(i == divSuspend):
-                               val = 'S/R'
-                       else:
-                               val = '%0.fms' % (float(i-divSuspend)*tS*1000)
-                       output += timescale.format(pos, val)
-       output += '</div>\n'
-       return output
-
 # Function: createHTMLSummarySimple
 # Description:
 #       Create summary html file for a series of tests
@@ -2146,6 +2959,32 @@ def createHTMLSummarySimple(testruns, htmlfile):
        hf.write('</body>\n</html>\n')
        hf.close()
 
+def htmlTitle():
+       global sysvals
+       modename = {
+               'freeze': 'Freeze (S0)',
+               'standby': 'Standby (S1)',
+               'mem': 'Suspend (S3)',
+               'disk': 'Hibernate (S4)'
+       }
+       kernel = sysvals.stamp['kernel']
+       host = sysvals.hostname[0].upper()+sysvals.hostname[1:]
+       mode = sysvals.suspendmode
+       if sysvals.suspendmode in modename:
+               mode = modename[sysvals.suspendmode]
+       return host+' '+mode+' '+kernel
+
+def ordinal(value):
+       suffix = 'th'
+       if value < 10 or value > 19:
+               if value % 10 == 1:
+                       suffix = 'st'
+               elif value % 10 == 2:
+                       suffix = 'nd'
+               elif value % 10 == 3:
+                       suffix = 'rd'
+       return '%d%s' % (value, suffix)
+
 # Function: createHTML
 # Description:
 #       Create the output html file from the resident test data
@@ -2156,6 +2995,10 @@ def createHTMLSummarySimple(testruns, htmlfile):
 def createHTML(testruns):
        global sysvals
 
+       if len(testruns) < 1:
+               print('ERROR: Not enough test data to build a timeline')
+               return
+
        for data in testruns:
                data.normalizeTime(testruns[-1].tSuspended)
 
@@ -2163,16 +3006,18 @@ def createHTML(testruns):
        if len(testruns) > 1:
                x2changes = ['1', 'relative']
        # html function templates
+       headline_version = '<div class="version"><a href="https://01.org/suspendresume">AnalyzeSuspend v%s</a></div>' % sysvals.version
        headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n'
        html_devlist1 = '<button id="devlist1" class="devlist" style="float:left;">Device Detail%s</button>' % x2changes[0]
        html_zoombox = '<center><button id="zoomin">ZOOM IN</button><button id="zoomout">ZOOM OUT</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
        html_devlist2 = '<button id="devlist2" class="devlist" style="float:right;">Device Detail2</button>\n'
        html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n'
-       html_device = '<div id="{0}" title="{1}" class="thread" style="left:{2}%;top:{3}%;height:{4}%;width:{5}%;">{6}</div>\n'
-       html_traceevent = '<div title="{0}" class="traceevent" style="left:{1}%;top:{2}%;height:{3}%;width:{4}%;border:1px solid {5};background-color:{5}">{6}</div>\n'
-       html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}%;height:{3}%;background-color:{4}">{5}</div>\n'
+       html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;">\n'
+       html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n'
+       html_traceevent = '<div title="{0}" class="traceevent" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;">{5}</div>\n'
+       html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background-color:{4}">{5}</div>\n'
        html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background-color:{3}"></div>\n'
-       html_legend = '<div class="square" style="left:{0}%;background-color:{1}">&nbsp;{2}</div>\n'
+       html_legend = '<div id="p{3}" class="square" style="left:{0}%;background-color:{1}">&nbsp;{2}</div>\n'
        html_timetotal = '<table class="time1">\n<tr>'\
                '<td class="green">{2} Suspend Time: <b>{0} ms</b></td>'\
                '<td class="yellow">{2} Resume Time: <b>{1} ms</b></td>'\
@@ -2182,6 +3027,10 @@ def createHTML(testruns):
                '<td class="gray">'+sysvals.suspendmode+' time: <b>{1} ms</b></td>'\
                '<td class="yellow">{3} Resume Time: <b>{2} ms</b></td>'\
                '</tr>\n</table>\n'
+       html_timetotal3 = '<table class="time1">\n<tr>'\
+               '<td class="green">Execution Time: <b>{0} ms</b></td>'\
+               '<td class="yellow">Command: <b>{1}</b></td>'\
+               '</tr>\n</table>\n'
        html_timegroups = '<table class="time2">\n<tr>'\
                '<td class="green">{4}Kernel Suspend: {0} ms</td>'\
                '<td class="purple">{4}Firmware Suspend: {1} ms</td>'\
@@ -2189,12 +3038,21 @@ def createHTML(testruns):
                '<td class="yellow">{4}Kernel Resume: {3} ms</td>'\
                '</tr>\n</table>\n'
 
+       # html format variables
+       rowheight = 30
+       devtextS = '14px'
+       devtextH = '30px'
+       hoverZ = 'z-index:10;'
+
+       if sysvals.usedevsrc:
+               hoverZ = ''
+
        # device timeline
        vprint('Creating Device Timeline...')
-       devtl = Timeline()
+
+       devtl = Timeline(rowheight)
 
        # Generate the header for this timeline
-       textnum = ['First', 'Second']
        for data in testruns:
                tTotal = data.end - data.start
                tEnd = data.dmesg['resume_complete']['end']
@@ -2203,7 +3061,17 @@ def createHTML(testruns):
                        sys.exit()
                if(data.tLow > 0):
                        low_time = '%.0f'%(data.tLow*1000)
-               if data.fwValid:
+               if sysvals.suspendmode == 'command':
+                       run_time = '%.0f'%((data.end-data.start)*1000)
+                       if sysvals.testcommand:
+                               testdesc = sysvals.testcommand
+                       else:
+                               testdesc = 'unknown'
+                       if(len(testruns) > 1):
+                               testdesc = ordinal(data.testnumber+1)+' '+testdesc
+                       thtml = html_timetotal3.format(run_time, testdesc)
+                       devtl.html['header'] += thtml
+               elif data.fwValid:
                        suspend_time = '%.0f'%((data.tSuspended-data.start)*1000 + \
                                (data.fwSuspend/1000000.0))
                        resume_time = '%.0f'%((tEnd-data.tSuspended)*1000 + \
@@ -2211,7 +3079,7 @@ def createHTML(testruns):
                        testdesc1 = 'Total'
                        testdesc2 = ''
                        if(len(testruns) > 1):
-                               testdesc1 = testdesc2 = textnum[data.testnumber]
+                               testdesc1 = testdesc2 = ordinal(data.testnumber+1)
                                testdesc2 += ' '
                        if(data.tLow == 0):
                                thtml = html_timetotal.format(suspend_time, \
@@ -2219,28 +3087,28 @@ def createHTML(testruns):
                        else:
                                thtml = html_timetotal2.format(suspend_time, low_time, \
                                        resume_time, testdesc1)
-                       devtl.html['timeline'] += thtml
+                       devtl.html['header'] += thtml
                        sktime = '%.3f'%((data.dmesg['suspend_machine']['end'] - \
                                data.getStart())*1000)
                        sftime = '%.3f'%(data.fwSuspend / 1000000.0)
                        rftime = '%.3f'%(data.fwResume / 1000000.0)
-                       rktime = '%.3f'%((data.getEnd() - \
+                       rktime = '%.3f'%((data.dmesg['resume_complete']['end'] - \
                                data.dmesg['resume_machine']['start'])*1000)
-                       devtl.html['timeline'] += html_timegroups.format(sktime, \
+                       devtl.html['header'] += html_timegroups.format(sktime, \
                                sftime, rftime, rktime, testdesc2)
                else:
                        suspend_time = '%.0f'%((data.tSuspended-data.start)*1000)
                        resume_time = '%.0f'%((tEnd-data.tSuspended)*1000)
                        testdesc = 'Kernel'
                        if(len(testruns) > 1):
-                               testdesc = textnum[data.testnumber]+' '+testdesc
+                               testdesc = ordinal(data.testnumber+1)+' '+testdesc
                        if(data.tLow == 0):
                                thtml = html_timetotal.format(suspend_time, \
                                        resume_time, testdesc)
                        else:
                                thtml = html_timetotal2.format(suspend_time, low_time, \
                                        resume_time, testdesc)
-                       devtl.html['timeline'] += thtml
+                       devtl.html['header'] += thtml
 
        # time scale for potentially multiple datasets
        t0 = testruns[0].start
@@ -2249,153 +3117,241 @@ def createHTML(testruns):
        tTotal = tMax - t0
 
        # determine the maximum number of rows we need to draw
-       timelinerows = 0
        for data in testruns:
-               for phase in data.dmesg:
-                       list = data.dmesg[phase]['list']
-                       rows = setTimelineRows(list, list)
-                       data.dmesg[phase]['row'] = rows
-                       if(rows > timelinerows):
-                               timelinerows = rows
-
-       # calculate the timeline height and create bounding box, add buttons
-       devtl.setRows(timelinerows + 1)
-       devtl.html['timeline'] += html_devlist1
-       if len(testruns) > 1:
-               devtl.html['timeline'] += html_devlist2
+               data.selectTimelineDevices('%f', tTotal, sysvals.mindevlen)
+               for group in data.devicegroups:
+                       devlist = []
+                       for phase in group:
+                               for devname in data.tdevlist[phase]:
+                                       devlist.append((phase,devname))
+                       devtl.getPhaseRows(data.dmesg, devlist)
+       devtl.calcTotalRows()
+
+       # create bounding box, add buttons
+       if sysvals.suspendmode != 'command':
+               devtl.html['timeline'] += html_devlist1
+               if len(testruns) > 1:
+                       devtl.html['timeline'] += html_devlist2
        devtl.html['timeline'] += html_zoombox
        devtl.html['timeline'] += html_timeline.format('dmesg', devtl.height)
 
-       # draw the colored boxes for each of the phases
-       for data in testruns:
-               for b in data.dmesg:
-                       phase = data.dmesg[b]
-                       length = phase['end']-phase['start']
-                       left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal)
-                       width = '%.3f' % ((length*100.0)/tTotal)
-                       devtl.html['timeline'] += html_phase.format(left, width, \
-                               '%.3f'%devtl.scaleH, '%.3f'%(100-devtl.scaleH), \
-                               data.dmesg[b]['color'], '')
+       # draw the full timeline
+       phases = {'suspend':[],'resume':[]}
+       for phase in data.dmesg:
+               if 'resume' in phase:
+                       phases['resume'].append(phase)
+               else:
+                       phases['suspend'].append(phase)
 
-       # draw the time scale, try to make the number of labels readable
-       devtl.html['scale'] = createTimeScale(t0, tMax, tSuspended)
-       devtl.html['timeline'] += devtl.html['scale']
-       for data in testruns:
-               for b in data.dmesg:
-                       phaselist = data.dmesg[b]['list']
-                       for d in phaselist:
-                               name = d
-                               drv = ''
-                               dev = phaselist[d]
-                               if(d in sysvals.altdevname):
-                                       name = sysvals.altdevname[d]
-                               if('drv' in dev and dev['drv']):
-                                       drv = ' {%s}' % dev['drv']
-                               height = (100.0 - devtl.scaleH)/data.dmesg[b]['row']
-                               top = '%.3f' % ((dev['row']*height) + devtl.scaleH)
-                               left = '%.3f' % (((dev['start']-t0)*100)/tTotal)
-                               width = '%.3f' % (((dev['end']-dev['start'])*100)/tTotal)
-                               length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
-                               color = 'rgba(204,204,204,0.5)'
-                               devtl.html['timeline'] += html_device.format(dev['id'], \
-                                       d+drv+length+b, left, top, '%.3f'%height, width, name+drv)
-
-       # draw any trace events found
+       # draw each test run chronologically
        for data in testruns:
-               for b in data.dmesg:
-                       phaselist = data.dmesg[b]['list']
-                       for name in phaselist:
-                               dev = phaselist[name]
-                               if('traceevents' in dev):
-                                       vprint('Debug trace events found for device %s' % name)
-                                       vprint('%20s %20s %10s %8s' % ('action', \
+               # if nore than one test, draw a block to represent user mode
+               if(data.testnumber > 0):
+                       m0 = testruns[data.testnumber-1].end
+                       mMax = testruns[data.testnumber].start
+                       mTotal = mMax - m0
+                       name = 'usermode%d' % data.testnumber
+                       top = '%d' % devtl.scaleH
+                       left = '%f' % (((m0-t0)*100.0)/tTotal)
+                       width = '%f' % ((mTotal*100.0)/tTotal)
+                       title = 'user mode (%0.3f ms) ' % (mTotal*1000)
+                       devtl.html['timeline'] += html_device.format(name, \
+                               title, left, top, '%d'%devtl.bodyH, width, '', '', '')
+               # now draw the actual timeline blocks
+               for dir in phases:
+                       # draw suspend and resume blocks separately
+                       bname = '%s%d' % (dir[0], data.testnumber)
+                       if dir == 'suspend':
+                               m0 = testruns[data.testnumber].start
+                               mMax = testruns[data.testnumber].tSuspended
+                               mTotal = mMax - m0
+                               left = '%f' % (((m0-t0)*100.0)/tTotal)
+                       else:
+                               m0 = testruns[data.testnumber].tSuspended
+                               mMax = testruns[data.testnumber].end
+                               mTotal = mMax - m0
+                               left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal)
+                       # if a timeline block is 0 length, skip altogether
+                       if mTotal == 0:
+                               continue
+                       width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal)
+                       devtl.html['timeline'] += html_tblock.format(bname, left, width)
+                       for b in sorted(phases[dir]):
+                               # draw the phase color background
+                               phase = data.dmesg[b]
+                               length = phase['end']-phase['start']
+                               left = '%f' % (((phase['start']-m0)*100.0)/mTotal)
+                               width = '%f' % ((length*100.0)/mTotal)
+                               devtl.html['timeline'] += html_phase.format(left, width, \
+                                       '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
+                                       data.dmesg[b]['color'], '')
+                               # draw the devices for this phase
+                               phaselist = data.dmesg[b]['list']
+                               for d in data.tdevlist[b]:
+                                       name = d
+                                       drv = ''
+                                       dev = phaselist[d]
+                                       xtraclass = ''
+                                       xtrainfo = ''
+                                       xtrastyle = ''
+                                       if 'htmlclass' in dev:
+                                               xtraclass = dev['htmlclass']
+                                               xtrainfo = dev['htmlclass']
+                                       if 'color' in dev:
+                                               xtrastyle = 'background-color:%s;' % dev['color']
+                                       if(d in sysvals.devprops):
+                                               name = sysvals.devprops[d].altName(d)
+                                               xtraclass = sysvals.devprops[d].xtraClass()
+                                               xtrainfo = sysvals.devprops[d].xtraInfo()
+                                       if('drv' in dev and dev['drv']):
+                                               drv = ' {%s}' % dev['drv']
+                                       rowheight = devtl.phaseRowHeight(b, dev['row'])
+                                       rowtop = devtl.phaseRowTop(b, dev['row'])
+                                       top = '%.3f' % (rowtop + devtl.scaleH)
+                                       left = '%f' % (((dev['start']-m0)*100)/mTotal)
+                                       width = '%f' % (((dev['end']-dev['start'])*100)/mTotal)
+                                       length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
+                                       if sysvals.suspendmode == 'command':
+                                               title = name+drv+xtrainfo+length+'cmdexec'
+                                       else:
+                                               title = name+drv+xtrainfo+length+b
+                                       devtl.html['timeline'] += html_device.format(dev['id'], \
+                                               title, left, top, '%.3f'%rowheight, width, \
+                                               d+drv, xtraclass, xtrastyle)
+                                       if('src' not in dev):
+                                               continue
+                                       # draw any trace events for this device
+                                       vprint('Debug trace events found for device %s' % d)
+                                       vprint('%20s %20s %10s %8s' % ('title', \
                                                'name', 'time(ms)', 'length(ms)'))
-                                       for e in dev['traceevents']:
-                                               vprint('%20s %20s %10.3f %8.3f' % (e.action, \
-                                                       e.name, e.time*1000, e.length*1000))
-                                               height = (100.0 - devtl.scaleH)/data.dmesg[b]['row']
-                                               top = '%.3f' % ((dev['row']*height) + devtl.scaleH)
-                                               left = '%.3f' % (((e.time-t0)*100)/tTotal)
-                                               width = '%.3f' % (e.length*100/tTotal)
+                                       for e in dev['src']:
+                                               vprint('%20s %20s %10.3f %8.3f' % (e.title, \
+                                                       e.text, e.time*1000, e.length*1000))
+                                               height = devtl.rowH
+                                               top = '%.3f' % (rowtop + devtl.scaleH + (e.row*devtl.rowH))
+                                               left = '%f' % (((e.time-m0)*100)/mTotal)
+                                               width = '%f' % (e.length*100/mTotal)
                                                color = 'rgba(204,204,204,0.5)'
                                                devtl.html['timeline'] += \
-                                                       html_traceevent.format(e.action+' '+e.name, \
+                                                       html_traceevent.format(e.title, \
                                                                left, top, '%.3f'%height, \
-                                                               width, e.color, '')
+                                                               width, e.text)
+                       # draw the time scale, try to make the number of labels readable
+                       devtl.html['timeline'] += devtl.createTimeScale(m0, mMax, tTotal, dir)
+                       devtl.html['timeline'] += '</div>\n'
 
        # timeline is finished
        devtl.html['timeline'] += '</div>\n</div>\n'
 
        # draw a legend which describes the phases by color
-       data = testruns[-1]
-       devtl.html['legend'] = '<div class="legend">\n'
-       pdelta = 100.0/len(data.phases)
-       pmargin = pdelta / 4.0
-       for phase in data.phases:
-               order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin)
-               name = string.replace(phase, '_', ' &nbsp;')
-               devtl.html['legend'] += html_legend.format(order, \
-                       data.dmesg[phase]['color'], name)
-       devtl.html['legend'] += '</div>\n'
+       if sysvals.suspendmode != 'command':
+               data = testruns[-1]
+               devtl.html['legend'] = '<div class="legend">\n'
+               pdelta = 100.0/len(data.phases)
+               pmargin = pdelta / 4.0
+               for phase in data.phases:
+                       tmp = phase.split('_')
+                       id = tmp[0][0]
+                       if(len(tmp) > 1):
+                               id += tmp[1][0]
+                       order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin)
+                       name = string.replace(phase, '_', ' &nbsp;')
+                       devtl.html['legend'] += html_legend.format(order, \
+                               data.dmesg[phase]['color'], name, id)
+               devtl.html['legend'] += '</div>\n'
 
        hf = open(sysvals.htmlfile, 'w')
-       thread_height = 0
+
+       if not sysvals.cgexp:
+               cgchk = 'checked'
+               cgnchk = 'not(:checked)'
+       else:
+               cgchk = 'not(:checked)'
+               cgnchk = 'checked'
 
        # write the html header first (html head, css code, up to body start)
        html_header = '<!DOCTYPE html>\n<html>\n<head>\n\
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
-       <title>AnalyzeSuspend</title>\n\
+       <title>'+htmlTitle()+'</title>\n\
        <style type=\'text/css\'>\n\
-               body {overflow-y: scroll;}\n\
-               .stamp {width: 100%;text-align:center;background-color:gray;line-height:30px;color:white;font: 25px Arial;}\n\
-               .callgraph {margin-top: 30px;box-shadow: 5px 5px 20px black;}\n\
-               .callgraph article * {padding-left: 28px;}\n\
-               h1 {color:black;font: bold 30px Times;}\n\
-               t0 {color:black;font: bold 30px Times;}\n\
-               t1 {color:black;font: 30px Times;}\n\
-               t2 {color:black;font: 25px Times;}\n\
-               t3 {color:black;font: 20px Times;white-space:nowrap;}\n\
-               t4 {color:black;font: bold 30px Times;line-height:60px;white-space:nowrap;}\n\
+               body {overflow-y:scroll;}\n\
+               .stamp {width:100%;text-align:center;background-color:gray;line-height:30px;color:white;font:25px Arial;}\n\
+               .callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\
+               .callgraph article * {padding-left:28px;}\n\
+               h1 {color:black;font:bold 30px Times;}\n\
+               t0 {color:black;font:bold 30px Times;}\n\
+               t1 {color:black;font:30px Times;}\n\
+               t2 {color:black;font:25px Times;}\n\
+               t3 {color:black;font:20px Times;white-space:nowrap;}\n\
+               t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\
+               cS {color:blue;font:bold 11px Times;}\n\
+               cR {color:red;font:bold 11px Times;}\n\
                table {width:100%;}\n\
                .gray {background-color:rgba(80,80,80,0.1);}\n\
                .green {background-color:rgba(204,255,204,0.4);}\n\
                .purple {background-color:rgba(128,0,128,0.2);}\n\
                .yellow {background-color:rgba(255,255,204,0.4);}\n\
-               .time1 {font: 22px Arial;border:1px solid;}\n\
-               .time2 {font: 15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\
-               td {text-align: center;}\n\
+               .time1 {font:22px Arial;border:1px solid;}\n\
+               .time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\
+               td {text-align:center;}\n\
                r {color:#500000;font:15px Tahoma;}\n\
                n {color:#505050;font:15px Tahoma;}\n\
-               .tdhl {color: red;}\n\
-               .hide {display: none;}\n\
-               .pf {display: none;}\n\
-               .pf:checked + label {background: url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
-               .pf:not(:checked) ~ label {background: url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
-               .pf:checked ~ *:not(:nth-child(2)) {display: none;}\n\
-               .zoombox {position: relative; width: 100%; overflow-x: scroll;}\n\
-               .timeline {position: relative; font-size: 14px;cursor: pointer;width: 100%; overflow: hidden; background-color:#dddddd;}\n\
-               .thread {position: absolute; height: '+'%.3f'%thread_height+'%; overflow: hidden; line-height: 30px; border:1px solid;text-align:center;white-space:nowrap;background-color:rgba(204,204,204,0.5);}\n\
-               .thread:hover {background-color:white;border:1px solid red;z-index:10;}\n\
-               .hover {background-color:white;border:1px solid red;z-index:10;}\n\
-               .traceevent {position: absolute;opacity: 0.3;height: '+'%.3f'%thread_height+'%;width:0;overflow:hidden;line-height:30px;text-align:center;white-space:nowrap;}\n\
-               .phase {position: absolute;overflow: hidden;border:0px;text-align:center;}\n\
+               .tdhl {color:red;}\n\
+               .hide {display:none;}\n\
+               .pf {display:none;}\n\
+               .pf:'+cgchk+' + label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
+               .pf:'+cgnchk+' ~ label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
+               .pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\
+               .zoombox {position:relative;width:100%;overflow-x:scroll;}\n\
+               .timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\
+               .thread {position:absolute;height:0%;overflow:hidden;line-height:'+devtextH+';font-size:'+devtextS+';border:1px solid;text-align:center;white-space:nowrap;background-color:rgba(204,204,204,0.5);}\n\
+               .thread.sync {background-color:'+sysvals.synccolor+';}\n\
+               .thread.bg {background-color:'+sysvals.kprobecolor+';}\n\
+               .thread:hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
+               .hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
+               .hover.sync {background-color:white;}\n\
+               .hover.bg {background-color:white;}\n\
+               .traceevent {position:absolute;font-size:10px;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,rgba(204,204,204,1),rgba(150,150,150,1));}\n\
+               .traceevent:hover {background:white;}\n\
+               .phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\
                .phaselet {position:absolute;overflow:hidden;border:0px;text-align:center;height:100px;font-size:24px;}\n\
-               .t {position:absolute;top:0%;height:100%;border-right:1px solid black;}\n\
-               .legend {position: relative; width: 100%; height: 40px; text-align: center;margin-bottom:20px}\n\
-               .legend .square {position:absolute;top:10px; width: 0px;height: 20px;border:1px solid;padding-left:20px;}\n\
+               .t {z-index:2;position:absolute;pointer-events:none;top:0%;height:100%;border-right:1px solid black;}\n\
+               .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\
+               .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\
                button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\
+               .logbtn {position:relative;float:right;height:25px;width:50px;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\
                .devlist {position:'+x2changes[1]+';width:190px;}\n\
-               #devicedetail {height:100px;box-shadow: 5px 5px 20px black;}\n\
+               a:link {color:white;text-decoration:none;}\n\
+               a:visited {color:white;}\n\
+               a:hover {color:white;}\n\
+               a:active {color:white;}\n\
+               .version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\
+               #devicedetail {height:100px;box-shadow:5px 5px 20px black;}\n\
+               .tblock {position:absolute;height:100%;}\n\
+               .bg {z-index:1;}\n\
        </style>\n</head>\n<body>\n'
-       hf.write(html_header)
+
+       # no header or css if its embedded
+       if(sysvals.embedded):
+               hf.write('pass True tSus %.3f tRes %.3f tLow %.3f fwvalid %s tSus %.3f tRes %.3f\n' %
+                       (data.tSuspended-data.start, data.end-data.tSuspended, data.tLow, data.fwValid, \
+                               data.fwSuspend/1000000, data.fwResume/1000000))
+       else:
+               hf.write(html_header)
 
        # write the test title and general info header
        if(sysvals.stamp['time'] != ""):
+               hf.write(headline_version)
+               if sysvals.addlogs and sysvals.dmesgfile:
+                       hf.write('<button id="showdmesg" class="logbtn">dmesg</button>')
+               if sysvals.addlogs and sysvals.ftracefile:
+                       hf.write('<button id="showftrace" class="logbtn">ftrace</button>')
                hf.write(headline_stamp.format(sysvals.stamp['host'],
                        sysvals.stamp['kernel'], sysvals.stamp['mode'], \
                                sysvals.stamp['time']))
 
        # write the device timeline
+       hf.write(devtl.html['header'])
        hf.write(devtl.html['timeline'])
        hf.write(devtl.html['legend'])
        hf.write('<div id="devicedetailtitle"></div>\n')
@@ -2410,12 +3366,15 @@ def createHTML(testruns):
                        width = '%.3f' % ((length*100.0)/tTotal)
                        hf.write(html_phaselet.format(b, left, width, \
                                data.dmesg[b]['color']))
+               if sysvals.suspendmode == 'command':
+                       hf.write(html_phaselet.format('cmdexec', '0', '0', \
+                               data.dmesg['resume_complete']['color']))
                hf.write('</div>\n')
        hf.write('</div>\n')
 
        # write the ftrace data (callgraph)
        data = testruns[-1]
-       if(sysvals.usecallgraph):
+       if(sysvals.usecallgraph and not sysvals.embedded):
                hf.write('<section id="callgraphs" class="callgraph">\n')
                # write out the ftrace data converted to html
                html_func_top = '<article id="{0}" class="atop" style="background-color:{1}">\n<input type="checkbox" class="pf" id="f{2}" checked/><label for="f{2}">{3} {4}</label>\n'
@@ -2428,22 +3387,29 @@ def createHTML(testruns):
                        for devname in data.sortedDevices(p):
                                if('ftrace' not in list[devname]):
                                        continue
-                               name = devname
-                               if(devname in sysvals.altdevname):
-                                       name = sysvals.altdevname[devname]
                                devid = list[devname]['id']
                                cg = list[devname]['ftrace']
-                               flen = '<r>(%.3f ms @ %.3f to %.3f)</r>' % \
-                                       ((cg.end - cg.start)*1000, cg.start*1000, cg.end*1000)
+                               clen = (cg.end - cg.start) * 1000
+                               if clen < sysvals.mincglen:
+                                       continue
+                               fmt = '<r>(%.3f ms @ '+sysvals.timeformat+' to '+sysvals.timeformat+')</r>'
+                               flen = fmt % (clen, cg.start, cg.end)
+                               name = devname
+                               if(devname in sysvals.devprops):
+                                       name = sysvals.devprops[devname].altName(devname)
+                               if sysvals.suspendmode == 'command':
+                                       ftitle = name
+                               else:
+                                       ftitle = name+' '+p
                                hf.write(html_func_top.format(devid, data.dmesg[p]['color'], \
-                                       num, name+' '+p, flen))
+                                       num, ftitle, flen))
                                num += 1
                                for line in cg.list:
                                        if(line.length < 0.000000001):
                                                flen = ''
                                        else:
-                                               flen = '<n>(%.3f ms @ %.3f)</n>' % (line.length*1000, \
-                                                       line.time*1000)
+                                               fmt = '<n>(%.3f ms @ '+sysvals.timeformat+')</n>'
+                                               flen = fmt % (line.length*1000, line.time)
                                        if(line.freturn and line.fcall):
                                                hf.write(html_func_leaf.format(line.name, flen))
                                        elif(line.freturn):
@@ -2453,9 +3419,40 @@ def createHTML(testruns):
                                                num += 1
                                hf.write(html_func_end)
                hf.write('\n\n    </section>\n')
-       # write the footer and close
-       addScriptCode(hf, testruns)
-       hf.write('</body>\n</html>\n')
+
+       # add the dmesg log as a hidden div
+       if sysvals.addlogs and sysvals.dmesgfile:
+               hf.write('<div id="dmesglog" style="display:none;">\n')
+               lf = open(sysvals.dmesgfile, 'r')
+               for line in lf:
+                       hf.write(line)
+               lf.close()
+               hf.write('</div>\n')
+       # add the ftrace log as a hidden div
+       if sysvals.addlogs and sysvals.ftracefile:
+               hf.write('<div id="ftracelog" style="display:none;">\n')
+               lf = open(sysvals.ftracefile, 'r')
+               for line in lf:
+                       hf.write(line)
+               lf.close()
+               hf.write('</div>\n')
+
+       if(not sysvals.embedded):
+               # write the footer and close
+               addScriptCode(hf, testruns)
+               hf.write('</body>\n</html>\n')
+       else:
+               # embedded out will be loaded in a page, skip the js
+               t0 = (testruns[0].start - testruns[-1].tSuspended) * 1000
+               tMax = (testruns[-1].end - testruns[-1].tSuspended) * 1000
+               # add js code in a div entry for later evaluation
+               detail = 'var bounds = [%f,%f];\n' % (t0, tMax)
+               detail += 'var devtable = [\n'
+               for data in testruns:
+                       topo = data.deviceTopology()
+                       detail += '\t"%s",\n' % (topo)
+               detail += '];\n'
+               hf.write('<div id=customcode style=display:none>\n'+detail+'</div>\n')
        hf.close()
        return True
 
@@ -2466,8 +3463,8 @@ def createHTML(testruns):
 #       hf: the open html file pointer
 #       testruns: array of Data objects from parseKernelLog or parseTraceLog
 def addScriptCode(hf, testruns):
-       t0 = (testruns[0].start - testruns[-1].tSuspended) * 1000
-       tMax = (testruns[-1].end - testruns[-1].tSuspended) * 1000
+       t0 = testruns[0].start * 1000
+       tMax = testruns[-1].end * 1000
        # create an array in javascript memory with the device details
        detail = '      var devtable = [];\n'
        for data in testruns:
@@ -2477,8 +3474,43 @@ def addScriptCode(hf, testruns):
        # add the code which will manipulate the data in the browser
        script_code = \
        '<script type="text/javascript">\n'+detail+\
+       '       var resolution = -1;\n'\
+       '       function redrawTimescale(t0, tMax, tS) {\n'\
+       '               var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;"><cR><-R</cR></div>\';\n'\
+       '               var tTotal = tMax - t0;\n'\
+       '               var list = document.getElementsByClassName("tblock");\n'\
+       '               for (var i = 0; i < list.length; i++) {\n'\
+       '                       var timescale = list[i].getElementsByClassName("timescale")[0];\n'\
+       '                       var m0 = t0 + (tTotal*parseFloat(list[i].style.left)/100);\n'\
+       '                       var mTotal = tTotal*parseFloat(list[i].style.width)/100;\n'\
+       '                       var mMax = m0 + mTotal;\n'\
+       '                       var html = "";\n'\
+       '                       var divTotal = Math.floor(mTotal/tS) + 1;\n'\
+       '                       if(divTotal > 1000) continue;\n'\
+       '                       var divEdge = (mTotal - tS*(divTotal-1))*100/mTotal;\n'\
+       '                       var pos = 0.0, val = 0.0;\n'\
+       '                       for (var j = 0; j < divTotal; j++) {\n'\
+       '                               var htmlline = "";\n'\
+       '                               if(list[i].id[5] == "r") {\n'\
+       '                                       pos = 100 - (((j)*tS*100)/mTotal);\n'\
+       '                                       val = (j)*tS;\n'\
+       '                                       htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
+       '                                       if(j == 0)\n'\
+       '                                               htmlline = rline;\n'\
+       '                               } else {\n'\
+       '                                       pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\
+       '                                       val = (j-divTotal+1)*tS;\n'\
+       '                                       if(j == divTotal - 1)\n'\
+       '                                               htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S-></cS></div>\';\n'\
+       '                                       else\n'\
+       '                                               htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
+       '                               }\n'\
+       '                               html += htmlline;\n'\
+       '                       }\n'\
+       '                       timescale.innerHTML = html;\n'\
+       '               }\n'\
+       '       }\n'\
        '       function zoomTimeline() {\n'\
-       '               var timescale = document.getElementById("timescale");\n'\
        '               var dmesg = document.getElementById("dmesg");\n'\
        '               var zoombox = document.getElementById("dmesgzoombox");\n'\
        '               var val = parseFloat(dmesg.style.width);\n'\
@@ -2486,7 +3518,7 @@ def addScriptCode(hf, testruns):
        '               var sh = window.outerWidth / 2;\n'\
        '               if(this.id == "zoomin") {\n'\
        '                       newval = val * 1.2;\n'\
-       '                       if(newval > 40000) newval = 40000;\n'\
+       '                       if(newval > 910034) newval = 910034;\n'\
        '                       dmesg.style.width = newval+"%";\n'\
        '                       zoombox.scrollLeft = ((zoombox.scrollLeft + sh) * newval / val) - sh;\n'\
        '               } else if (this.id == "zoomout") {\n'\
@@ -2498,19 +3530,17 @@ def addScriptCode(hf, testruns):
        '                       zoombox.scrollLeft = 0;\n'\
        '                       dmesg.style.width = "100%";\n'\
        '               }\n'\
-       '               var html = "";\n'\
+       '               var tS = [10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1];\n'\
        '               var t0 = bounds[0];\n'\
        '               var tMax = bounds[1];\n'\
        '               var tTotal = tMax - t0;\n'\
        '               var wTotal = tTotal * 100.0 / newval;\n'\
-       '               for(var tS = 1000; (wTotal / tS) < 3; tS /= 10);\n'\
-       '               if(tS < 1) tS = 1;\n'\
-       '               for(var s = ((t0 / tS)|0) * tS; s < tMax; s += tS) {\n'\
-       '                       var pos = (tMax - s) * 100.0 / tTotal;\n'\
-       '                       var name = (s == 0)?"S/R":(s+"ms");\n'\
-       '                       html += "<div class=\\"t\\" style=\\"right:"+pos+"%\\">"+name+"</div>";\n'\
-       '               }\n'\
-       '               timescale.innerHTML = html;\n'\
+       '               var idx = 7*window.innerWidth/1100;\n'\
+       '               for(var i = 0; (i < tS.length)&&((wTotal / tS[i]) < idx); i++);\n'\
+       '               if(i >= tS.length) i = tS.length - 1;\n'\
+       '               if(tS[i] == resolution) return;\n'\
+       '               resolution = tS[i];\n'\
+       '               redrawTimescale(t0, tMax, tS[i]);\n'\
        '       }\n'\
        '       function deviceHover() {\n'\
        '               var name = this.title.slice(0, this.title.indexOf(" ("));\n'\
@@ -2523,12 +3553,13 @@ def addScriptCode(hf, testruns):
        '                       cpu = parseInt(name.slice(8));\n'\
        '               for (var i = 0; i < dev.length; i++) {\n'\
        '                       dname = dev[i].title.slice(0, dev[i].title.indexOf(" ("));\n'\
+       '                       var cname = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
        '                       if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
        '                               (name == dname))\n'\
        '                       {\n'\
-       '                               dev[i].className = "thread hover";\n'\
+       '                               dev[i].className = "hover "+cname;\n'\
        '                       } else {\n'\
-       '                               dev[i].className = "thread";\n'\
+       '                               dev[i].className = cname;\n'\
        '                       }\n'\
        '               }\n'\
        '       }\n'\
@@ -2536,7 +3567,7 @@ def addScriptCode(hf, testruns):
        '               var dmesg = document.getElementById("dmesg");\n'\
        '               var dev = dmesg.getElementsByClassName("thread");\n'\
        '               for (var i = 0; i < dev.length; i++) {\n'\
-       '                       dev[i].className = "thread";\n'\
+       '                       dev[i].className = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
        '               }\n'\
        '       }\n'\
        '       function deviceTitle(title, total, cpu) {\n'\
@@ -2547,7 +3578,7 @@ def addScriptCode(hf, testruns):
        '                       total[2] = (total[2]+total[4])/2;\n'\
        '               }\n'\
        '               var devtitle = document.getElementById("devicedetailtitle");\n'\
-       '               var name = title.slice(0, title.indexOf(" "));\n'\
+       '               var name = title.slice(0, title.indexOf(" ("));\n'\
        '               if(cpu >= 0) name = "CPU"+cpu;\n'\
        '               var driver = "";\n'\
        '               var tS = "<t2>(</t2>";\n'\
@@ -2579,6 +3610,8 @@ def addScriptCode(hf, testruns):
        '               var dev = dmesg.getElementsByClassName("thread");\n'\
        '               var idlist = [];\n'\
        '               var pdata = [[]];\n'\
+       '               if(document.getElementById("devicedetail1"))\n'\
+       '                       pdata = [[], []];\n'\
        '               var pd = pdata[0];\n'\
        '               var total = [0.0, 0.0, 0.0];\n'\
        '               for (var i = 0; i < dev.length; i++) {\n'\
@@ -2634,6 +3667,7 @@ def addScriptCode(hf, testruns):
        '               var cglist = document.getElementById("callgraphs");\n'\
        '               if(!cglist) return;\n'\
        '               var cg = cglist.getElementsByClassName("atop");\n'\
+       '               if(cg.length < 10) return;\n'\
        '               for (var i = 0; i < cg.length; i++) {\n'\
        '                       if(idlist.indexOf(cg[i].id) >= 0) {\n'\
        '                               cg[i].style.display = "block";\n'\
@@ -2658,15 +3692,32 @@ def addScriptCode(hf, testruns):
        '                       dt = devtable[1];\n'\
        '               win.document.write(html+dt);\n'\
        '       }\n'\
+       '       function logWindow(e) {\n'\
+       '               var name = e.target.id.slice(4);\n'\
+       '               var win = window.open();\n'\
+       '               var log = document.getElementById(name+"log");\n'\
+       '               var title = "<title>"+document.title.split(" ")[0]+" "+name+" log</title>";\n'\
+       '               win.document.write(title+"<pre>"+log.innerHTML+"</pre>");\n'\
+       '               win.document.close();\n'\
+       '       }\n'\
+       '       function onClickPhase(e) {\n'\
+       '       }\n'\
+       '       window.addEventListener("resize", function () {zoomTimeline();});\n'\
        '       window.addEventListener("load", function () {\n'\
        '               var dmesg = document.getElementById("dmesg");\n'\
        '               dmesg.style.width = "100%"\n'\
        '               document.getElementById("zoomin").onclick = zoomTimeline;\n'\
        '               document.getElementById("zoomout").onclick = zoomTimeline;\n'\
        '               document.getElementById("zoomdef").onclick = zoomTimeline;\n'\
-       '               var devlist = document.getElementsByClassName("devlist");\n'\
-       '               for (var i = 0; i < devlist.length; i++)\n'\
-       '                       devlist[i].onclick = devListWindow;\n'\
+       '               var list = document.getElementsByClassName("square");\n'\
+       '               for (var i = 0; i < list.length; i++)\n'\
+       '                       list[i].onclick = onClickPhase;\n'\
+       '               var list = document.getElementsByClassName("logbtn");\n'\
+       '               for (var i = 0; i < list.length; i++)\n'\
+       '                       list[i].onclick = logWindow;\n'\
+       '               list = document.getElementsByClassName("devlist");\n'\
+       '               for (var i = 0; i < list.length; i++)\n'\
+       '                       list[i].onclick = devListWindow;\n'\
        '               var dev = dmesg.getElementsByClassName("thread");\n'\
        '               for (var i = 0; i < dev.length; i++) {\n'\
        '                       dev[i].onclick = deviceDetail;\n'\
@@ -2685,141 +3736,87 @@ def addScriptCode(hf, testruns):
 def executeSuspend():
        global sysvals
 
-       detectUSB(False)
        t0 = time.time()*1000
        tp = sysvals.tpath
+       fwdata = []
+       # mark the start point in the kernel ring buffer just as we start
+       sysvals.initdmesg()
+       # start ftrace
+       if(sysvals.usecallgraph or sysvals.usetraceevents):
+               print('START TRACING')
+               sysvals.fsetVal('1', 'tracing_on')
        # execute however many s/r runs requested
        for count in range(1,sysvals.execcount+1):
-               # clear the kernel ring buffer just as we start
-               os.system('dmesg -C')
-               # enable callgraph ftrace only for the second run
-               if(sysvals.usecallgraph and count == 2):
-                       # set trace type
-                       os.system('echo function_graph > '+tp+'current_tracer')
-                       os.system('echo "" > '+tp+'set_ftrace_filter')
-                       # set trace format options
-                       os.system('echo funcgraph-abstime > '+tp+'trace_options')
-                       os.system('echo funcgraph-proc > '+tp+'trace_options')
-                       # focus only on device suspend and resume
-                       os.system('cat '+tp+'available_filter_functions | '+\
-                               'grep dpm_run_callback > '+tp+'set_graph_function')
                # if this is test2 and there's a delay, start here
                if(count > 1 and sysvals.x2delay > 0):
                        tN = time.time()*1000
                        while (tN - t0) < sysvals.x2delay:
                                tN = time.time()*1000
                                time.sleep(0.001)
-               # start ftrace
-               if(sysvals.usecallgraph or sysvals.usetraceevents):
-                       print('START TRACING')
-                       os.system('echo 1 > '+tp+'tracing_on')
                # initiate suspend
                if(sysvals.usecallgraph or sysvals.usetraceevents):
-                       os.system('echo SUSPEND START > '+tp+'trace_marker')
-               if(sysvals.rtcwake):
-                       print('SUSPEND START')
-                       print('will autoresume in %d seconds' % sysvals.rtcwaketime)
-                       sysvals.rtcWakeAlarm()
+                       sysvals.fsetVal('SUSPEND START', 'trace_marker')
+               if sysvals.suspendmode == 'command':
+                       print('COMMAND START')
+                       if(sysvals.rtcwake):
+                               print('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
+                               sysvals.rtcWakeAlarmOn()
+                       os.system(sysvals.testcommand)
                else:
-                       print('SUSPEND START (press a key to resume)')
-               pf = open(sysvals.powerfile, 'w')
-               pf.write(sysvals.suspendmode)
-               # execution will pause here
-               pf.close()
+                       if(sysvals.rtcwake):
+                               print('SUSPEND START')
+                               print('will autoresume in %d seconds' % sysvals.rtcwaketime)
+                               sysvals.rtcWakeAlarmOn()
+                       else:
+                               print('SUSPEND START (press a key to resume)')
+                       pf = open(sysvals.powerfile, 'w')
+                       pf.write(sysvals.suspendmode)
+                       # execution will pause here
+                       try:
+                               pf.close()
+                       except:
+                               pass
                t0 = time.time()*1000
+               if(sysvals.rtcwake):
+                       sysvals.rtcWakeAlarmOff()
                # return from suspend
                print('RESUME COMPLETE')
                if(sysvals.usecallgraph or sysvals.usetraceevents):
-                       os.system('echo RESUME COMPLETE > '+tp+'trace_marker')
-               # see if there's firmware timing data to be had
-               t = sysvals.postresumetime
-               if(t > 0):
-                       print('Waiting %d seconds for POST-RESUME trace events...' % t)
-                       time.sleep(t)
-               # stop ftrace
-               if(sysvals.usecallgraph or sysvals.usetraceevents):
-                       os.system('echo 0 > '+tp+'tracing_on')
-                       print('CAPTURING TRACE')
-                       writeDatafileHeader(sysvals.ftracefile)
-                       os.system('cat '+tp+'trace >> '+sysvals.ftracefile)
-                       os.system('echo "" > '+tp+'trace')
-               # grab a copy of the dmesg output
-               print('CAPTURING DMESG')
-               writeDatafileHeader(sysvals.dmesgfile)
-               os.system('dmesg -c >> '+sysvals.dmesgfile)
-
-def writeDatafileHeader(filename):
+                       sysvals.fsetVal('RESUME COMPLETE', 'trace_marker')
+               if(sysvals.suspendmode == 'mem'):
+                       fwdata.append(getFPDT(False))
+       # look for post resume events after the last test run
+       t = sysvals.postresumetime
+       if(t > 0):
+               print('Waiting %d seconds for POST-RESUME trace events...' % t)
+               time.sleep(t)
+       # stop ftrace
+       if(sysvals.usecallgraph or sysvals.usetraceevents):
+               sysvals.fsetVal('0', 'tracing_on')
+               print('CAPTURING TRACE')
+               writeDatafileHeader(sysvals.ftracefile, fwdata)
+               os.system('cat '+tp+'trace >> '+sysvals.ftracefile)
+               sysvals.fsetVal('', 'trace')
+               devProps()
+       # grab a copy of the dmesg output
+       print('CAPTURING DMESG')
+       writeDatafileHeader(sysvals.dmesgfile, fwdata)
+       sysvals.getdmesg()
+
+def writeDatafileHeader(filename, fwdata):
        global sysvals
 
-       fw = getFPDT(False)
        prt = sysvals.postresumetime
        fp = open(filename, 'a')
        fp.write(sysvals.teststamp+'\n')
-       if(fw):
-               fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
+       if(sysvals.suspendmode == 'mem'):
+               for fw in fwdata:
+                       if(fw):
+                               fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
        if(prt > 0):
                fp.write('# post resume time %u\n' % prt)
        fp.close()
 
-# Function: executeAndroidSuspend
-# Description:
-#       Execute system suspend through the sysfs interface
-#       on a remote android device, then transfer the output
-#       dmesg and ftrace files to the local output directory.
-def executeAndroidSuspend():
-       global sysvals
-
-       # check to see if the display is currently off
-       tp = sysvals.tpath
-       out = os.popen(sysvals.adb+\
-               ' shell dumpsys power | grep mScreenOn').read().strip()
-       # if so we need to turn it on so we can issue a new suspend
-       if(out.endswith('false')):
-               print('Waking the device up for the test...')
-               # send the KEYPAD_POWER keyevent to wake it up
-               os.system(sysvals.adb+' shell input keyevent 26')
-               # wait a few seconds so the user can see the device wake up
-               time.sleep(3)
-       # execute however many s/r runs requested
-       for count in range(1,sysvals.execcount+1):
-               # clear the kernel ring buffer just as we start
-               os.system(sysvals.adb+' shell dmesg -c > /dev/null 2>&1')
-               # start ftrace
-               if(sysvals.usetraceevents):
-                       print('START TRACING')
-                       os.system(sysvals.adb+" shell 'echo 1 > "+tp+"tracing_on'")
-               # initiate suspend
-               for count in range(1,sysvals.execcount+1):
-                       if(sysvals.usetraceevents):
-                               os.system(sysvals.adb+\
-                                       " shell 'echo SUSPEND START > "+tp+"trace_marker'")
-                       print('SUSPEND START (press a key on the device to resume)')
-                       os.system(sysvals.adb+" shell 'echo "+sysvals.suspendmode+\
-                               " > "+sysvals.powerfile+"'")
-                       # execution will pause here, then adb will exit
-                       while(True):
-                               check = os.popen(sysvals.adb+\
-                                       ' shell pwd 2>/dev/null').read().strip()
-                               if(len(check) > 0):
-                                       break
-                               time.sleep(1)
-                       if(sysvals.usetraceevents):
-                               os.system(sysvals.adb+" shell 'echo RESUME COMPLETE > "+tp+\
-                                       "trace_marker'")
-               # return from suspend
-               print('RESUME COMPLETE')
-               # stop ftrace
-               if(sysvals.usetraceevents):
-                       os.system(sysvals.adb+" shell 'echo 0 > "+tp+"tracing_on'")
-                       print('CAPTURING TRACE')
-                       os.system('echo "'+sysvals.teststamp+'" > '+sysvals.ftracefile)
-                       os.system(sysvals.adb+' shell cat '+tp+\
-                               'trace >> '+sysvals.ftracefile)
-               # grab a copy of the dmesg output
-               print('CAPTURING DMESG')
-               os.system('echo "'+sysvals.teststamp+'" > '+sysvals.dmesgfile)
-               os.system(sysvals.adb+' shell dmesg >> '+sysvals.dmesgfile)
-
 # Function: setUSBDevicesAuto
 # Description:
 #       Set the autosuspend control parameter of all USB devices to auto
@@ -2829,7 +3826,7 @@ def executeAndroidSuspend():
 def setUSBDevicesAuto():
        global sysvals
 
-       rootCheck()
+       rootCheck(True)
        for dirname, dirnames, filenames in os.walk('/sys/devices'):
                if(re.match('.*/usb[0-9]*.*', dirname) and
                        'idVendor' in filenames and 'idProduct' in filenames):
@@ -2874,9 +3871,7 @@ def ms2nice(val):
 # Description:
 #       Detect all the USB hosts and devices currently connected and add
 #       a list of USB device names to sysvals for better timeline readability
-# Arguments:
-#       output: True to output the info to stdout, False otherwise
-def detectUSB(output):
+def detectUSB():
        global sysvals
 
        field = {'idVendor':'', 'idProduct':'', 'product':'', 'speed':''}
@@ -2887,18 +3882,18 @@ def detectUSB(output):
                        'runtime_suspended_time':'',
                        'active_duration':'',
                        'connected_duration':''}
-       if(output):
-               print('LEGEND')
-               print('---------------------------------------------------------------------------------------------')
-               print('  A = async/sync PM queue Y/N                       D = autosuspend delay (seconds)')
-               print('  S = autosuspend Y/N                         rACTIVE = runtime active (min/sec)')
-               print('  P = persist across suspend Y/N              rSUSPEN = runtime suspend (min/sec)')
-               print('  E = runtime suspend enabled/forbidden Y/N    ACTIVE = active duration (min/sec)')
-               print('  R = runtime status active/suspended Y/N     CONNECT = connected duration (min/sec)')
-               print('  U = runtime usage count')
-               print('---------------------------------------------------------------------------------------------')
-               print('  NAME       ID      DESCRIPTION         SPEED A S P E R U D rACTIVE rSUSPEN  ACTIVE CONNECT')
-               print('---------------------------------------------------------------------------------------------')
+
+       print('LEGEND')
+       print('---------------------------------------------------------------------------------------------')
+       print('  A = async/sync PM queue Y/N                       D = autosuspend delay (seconds)')
+       print('  S = autosuspend Y/N                         rACTIVE = runtime active (min/sec)')
+       print('  P = persist across suspend Y/N              rSUSPEN = runtime suspend (min/sec)')
+       print('  E = runtime suspend enabled/forbidden Y/N    ACTIVE = active duration (min/sec)')
+       print('  R = runtime status active/suspended Y/N     CONNECT = connected duration (min/sec)')
+       print('  U = runtime usage count')
+       print('---------------------------------------------------------------------------------------------')
+       print('  NAME       ID      DESCRIPTION         SPEED A S P E R U D rACTIVE rSUSPEN  ACTIVE CONNECT')
+       print('---------------------------------------------------------------------------------------------')
 
        for dirname, dirnames, filenames in os.walk('/sys/devices'):
                if(re.match('.*/usb[0-9]*.*', dirname) and
@@ -2907,35 +3902,149 @@ def detectUSB(output):
                                field[i] = os.popen('cat %s/%s 2>/dev/null' % \
                                        (dirname, i)).read().replace('\n', '')
                        name = dirname.split('/')[-1]
-                       if(len(field['product']) > 0):
-                               sysvals.altdevname[name] = \
-                                       '%s [%s]' % (field['product'], name)
+                       for i in power:
+                               power[i] = os.popen('cat %s/power/%s 2>/dev/null' % \
+                                       (dirname, i)).read().replace('\n', '')
+                       if(re.match('usb[0-9]*', name)):
+                               first = '%-8s' % name
                        else:
-                               sysvals.altdevname[name] = \
-                                       '%s:%s [%s]' % (field['idVendor'], \
-                                               field['idProduct'], name)
-                       if(output):
-                               for i in power:
-                                       power[i] = os.popen('cat %s/power/%s 2>/dev/null' % \
-                                               (dirname, i)).read().replace('\n', '')
-                               if(re.match('usb[0-9]*', name)):
-                                       first = '%-8s' % name
-                               else:
-                                       first = '%8s' % name
-                               print('%s [%s:%s] %-20s %-4s %1s %1s %1s %1s %1s %1s %1s %s %s %s %s' % \
-                                       (first, field['idVendor'], field['idProduct'], \
-                                       field['product'][0:20], field['speed'], \
-                                       yesno(power['async']), \
-                                       yesno(power['control']), \
-                                       yesno(power['persist']), \
-                                       yesno(power['runtime_enabled']), \
-                                       yesno(power['runtime_status']), \
-                                       power['runtime_usage'], \
-                                       power['autosuspend'], \
-                                       ms2nice(power['runtime_active_time']), \
-                                       ms2nice(power['runtime_suspended_time']), \
-                                       ms2nice(power['active_duration']), \
-                                       ms2nice(power['connected_duration'])))
+                               first = '%8s' % name
+                       print('%s [%s:%s] %-20s %-4s %1s %1s %1s %1s %1s %1s %1s %s %s %s %s' % \
+                               (first, field['idVendor'], field['idProduct'], \
+                               field['product'][0:20], field['speed'], \
+                               yesno(power['async']), \
+                               yesno(power['control']), \
+                               yesno(power['persist']), \
+                               yesno(power['runtime_enabled']), \
+                               yesno(power['runtime_status']), \
+                               power['runtime_usage'], \
+                               power['autosuspend'], \
+                               ms2nice(power['runtime_active_time']), \
+                               ms2nice(power['runtime_suspended_time']), \
+                               ms2nice(power['active_duration']), \
+                               ms2nice(power['connected_duration'])))
+
+# Function: devProps
+# Description:
+#       Retrieve a list of properties for all devices in the trace log
+def devProps(data=0):
+       global sysvals
+       props = dict()
+
+       if data:
+               idx = data.index(': ') + 2
+               if idx >= len(data):
+                       return
+               devlist = data[idx:].split(';')
+               for dev in devlist:
+                       f = dev.split(',')
+                       if len(f) < 3:
+                               continue
+                       dev = f[0]
+                       props[dev] = DevProps()
+                       props[dev].altname = f[1]
+                       if int(f[2]):
+                               props[dev].async = True
+                       else:
+                               props[dev].async = False
+                       sysvals.devprops = props
+               if sysvals.suspendmode == 'command' and 'testcommandstring' in props:
+                       sysvals.testcommand = props['testcommandstring'].altname
+               return
+
+       if(os.path.exists(sysvals.ftracefile) == False):
+               doError('%s does not exist' % sysvals.ftracefile, False)
+
+       # first get the list of devices we need properties for
+       msghead = 'Additional data added by AnalyzeSuspend'
+       alreadystamped = False
+       tp = TestProps()
+       tf = open(sysvals.ftracefile, 'r')
+       for line in tf:
+               if msghead in line:
+                       alreadystamped = True
+                       continue
+               # determine the trace data type (required for further parsing)
+               m = re.match(sysvals.tracertypefmt, line)
+               if(m):
+                       tp.setTracerType(m.group('t'))
+                       continue
+               # parse only valid lines, if this is not one move on
+               m = re.match(tp.ftrace_line_fmt, line)
+               if(not m or 'device_pm_callback_start' not in line):
+                       continue
+               m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
+               if(not m):
+                       continue
+               drv, dev, par = m.group('drv'), m.group('d'), m.group('p')
+               if dev not in props:
+                       props[dev] = DevProps()
+       tf.close()
+
+       if not alreadystamped and sysvals.suspendmode == 'command':
+               out = '#\n# '+msghead+'\n# Device Properties: '
+               out += 'testcommandstring,%s,0;' % (sysvals.testcommand)
+               with open(sysvals.ftracefile, 'a') as fp:
+                       fp.write(out+'\n')
+               sysvals.devprops = props
+               return
+
+       # now get the syspath for each of our target devices
+       for dirname, dirnames, filenames in os.walk('/sys/devices'):
+               if(re.match('.*/power', dirname) and 'async' in filenames):
+                       dev = dirname.split('/')[-2]
+                       if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
+                               props[dev].syspath = dirname[:-6]
+
+       # now fill in the properties for our target devices
+       for dev in props:
+               dirname = props[dev].syspath
+               if not dirname or not os.path.exists(dirname):
+                       continue
+               with open(dirname+'/power/async') as fp:
+                       text = fp.read()
+                       props[dev].async = False
+                       if 'enabled' in text:
+                               props[dev].async = True
+               fields = os.listdir(dirname)
+               if 'product' in fields:
+                       with open(dirname+'/product') as fp:
+                               props[dev].altname = fp.read()
+               elif 'name' in fields:
+                       with open(dirname+'/name') as fp:
+                               props[dev].altname = fp.read()
+               elif 'model' in fields:
+                       with open(dirname+'/model') as fp:
+                               props[dev].altname = fp.read()
+               elif 'description' in fields:
+                       with open(dirname+'/description') as fp:
+                               props[dev].altname = fp.read()
+               elif 'id' in fields:
+                       with open(dirname+'/id') as fp:
+                               props[dev].altname = fp.read()
+               elif 'idVendor' in fields and 'idProduct' in fields:
+                       idv, idp = '', ''
+                       with open(dirname+'/idVendor') as fp:
+                               idv = fp.read().strip()
+                       with open(dirname+'/idProduct') as fp:
+                               idp = fp.read().strip()
+                       props[dev].altname = '%s:%s' % (idv, idp)
+
+               if props[dev].altname:
+                       out = props[dev].altname.strip().replace('\n', ' ')
+                       out = out.replace(',', ' ')
+                       out = out.replace(';', ' ')
+                       props[dev].altname = out
+
+       # and now write the data to the ftrace file
+       if not alreadystamped:
+               out = '#\n# '+msghead+'\n# Device Properties: '
+               for dev in sorted(props):
+                       out += props[dev].out(dev)
+               with open(sysvals.ftracefile, 'a') as fp:
+                       fp.write(out+'\n')
+
+       sysvals.devprops = props
 
 # Function: getModes
 # Description:
@@ -2945,15 +4054,10 @@ def detectUSB(output):
 def getModes():
        global sysvals
        modes = ''
-       if(not sysvals.android):
-               if(os.path.exists(sysvals.powerfile)):
-                       fp = open(sysvals.powerfile, 'r')
-                       modes = string.split(fp.read())
-                       fp.close()
-       else:
-               line = os.popen(sysvals.adb+' shell cat '+\
-                       sysvals.powerfile).read().strip()
-               modes = string.split(line)
+       if(os.path.exists(sysvals.powerfile)):
+               fp = open(sysvals.powerfile, 'r')
+               modes = string.split(fp.read())
+               fp.close()
        return modes
 
 # Function: getFPDT
@@ -2971,22 +4075,22 @@ def getFPDT(output):
        prectype[0] = 'Basic S3 Resume Performance Record'
        prectype[1] = 'Basic S3 Suspend Performance Record'
 
-       rootCheck()
+       rootCheck(True)
        if(not os.path.exists(sysvals.fpdtpath)):
                if(output):
-                       doError('file doesnt exist: %s' % sysvals.fpdtpath, False)
+                       doError('file does not exist: %s' % sysvals.fpdtpath, False)
                return False
        if(not os.access(sysvals.fpdtpath, os.R_OK)):
                if(output):
-                       doError('file isnt readable: %s' % sysvals.fpdtpath, False)
+                       doError('file is not readable: %s' % sysvals.fpdtpath, False)
                return False
        if(not os.path.exists(sysvals.mempath)):
                if(output):
-                       doError('file doesnt exist: %s' % sysvals.mempath, False)
+                       doError('file does not exist: %s' % sysvals.mempath, False)
                return False
        if(not os.access(sysvals.mempath, os.R_OK)):
                if(output):
-                       doError('file isnt readable: %s' % sysvals.mempath, False)
+                       doError('file is not readable: %s' % sysvals.mempath, False)
                return False
 
        fp = open(sysvals.fpdtpath, 'rb')
@@ -3027,15 +4131,19 @@ def getFPDT(output):
        while(i < len(records)):
                header = struct.unpack('HBB', records[i:i+4])
                if(header[0] not in rectype):
+                       i += header[1]
                        continue
                if(header[1] != 16):
+                       i += header[1]
                        continue
                addr = struct.unpack('Q', records[i+8:i+16])[0]
                try:
                        fp.seek(addr)
                        first = fp.read(8)
                except:
-                       doError('Bad address 0x%x in %s' % (addr, sysvals.mempath), False)
+                       if(output):
+                               print('Bad address 0x%x in %s' % (addr, sysvals.mempath))
+                       return [0, 0]
                rechead = struct.unpack('4sI', first)
                recdata = fp.read(rechead[1]-8)
                if(rechead[0] == 'FBPT'):
@@ -3090,89 +4198,60 @@ def getFPDT(output):
 #       print the results to the terminal
 # Output:
 #       True if the test will work, False if not
-def statusCheck():
+def statusCheck(probecheck=False):
        global sysvals
        status = True
 
-       if(sysvals.android):
-               print('Checking the android system ...')
-       else:
-               print('Checking this system (%s)...' % platform.node())
-
-       # check if adb is connected to a device
-       if(sysvals.android):
-               res = 'NO'
-               out = os.popen(sysvals.adb+' get-state').read().strip()
-               if(out == 'device'):
-                       res = 'YES'
-               print('    is android device connected: %s' % res)
-               if(res != 'YES'):
-                       print('    Please connect the device before using this tool')
-                       return False
+       print('Checking this system (%s)...' % platform.node())
 
        # check we have root access
-       res = 'NO (No features of this tool will work!)'
-       if(sysvals.android):
-               out = os.popen(sysvals.adb+' shell id').read().strip()
-               if('root' in out):
-                       res = 'YES'
-       else:
-               if(os.environ['USER'] == 'root'):
-                       res = 'YES'
+       res = sysvals.colorText('NO (No features of this tool will work!)')
+       if(rootCheck(False)):
+               res = 'YES'
        print('    have root access: %s' % res)
        if(res != 'YES'):
-               if(sysvals.android):
-                       print('    Try running "adb root" to restart the daemon as root')
-               else:
-                       print('    Try running this script with sudo')
+               print('    Try running this script with sudo')
                return False
 
        # check sysfs is mounted
-       res = 'NO (No features of this tool will work!)'
-       if(sysvals.android):
-               out = os.popen(sysvals.adb+' shell ls '+\
-                       sysvals.powerfile).read().strip()
-               if(out == sysvals.powerfile):
-                       res = 'YES'
-       else:
-               if(os.path.exists(sysvals.powerfile)):
-                       res = 'YES'
+       res = sysvals.colorText('NO (No features of this tool will work!)')
+       if(os.path.exists(sysvals.powerfile)):
+               res = 'YES'
        print('    is sysfs mounted: %s' % res)
        if(res != 'YES'):
                return False
 
        # check target mode is a valid mode
-       res = 'NO'
-       modes = getModes()
-       if(sysvals.suspendmode in modes):
-               res = 'YES'
-       else:
-               status = False
-       print('    is "%s" a valid power mode: %s' % (sysvals.suspendmode, res))
-       if(res == 'NO'):
-               print('      valid power modes are: %s' % modes)
-               print('      please choose one with -m')
-
-       # check if the tool can unlock the device
-       if(sysvals.android):
-               res = 'YES'
-               out1 = os.popen(sysvals.adb+\
-                       ' shell dumpsys power | grep mScreenOn').read().strip()
-               out2 = os.popen(sysvals.adb+\
-                       ' shell input').read().strip()
-               if(not out1.startswith('mScreenOn') or not out2.startswith('usage')):
-                       res = 'NO (wake the android device up before running the test)'
-               print('    can I unlock the screen: %s' % res)
+       if sysvals.suspendmode != 'command':
+               res = sysvals.colorText('NO')
+               modes = getModes()
+               if(sysvals.suspendmode in modes):
+                       res = 'YES'
+               else:
+                       status = False
+               print('    is "%s" a valid power mode: %s' % (sysvals.suspendmode, res))
+               if(res == 'NO'):
+                       print('      valid power modes are: %s' % modes)
+                       print('      please choose one with -m')
 
        # check if ftrace is available
-       res = 'NO'
-       ftgood = verifyFtrace()
+       res = sysvals.colorText('NO')
+       ftgood = sysvals.verifyFtrace()
        if(ftgood):
                res = 'YES'
        elif(sysvals.usecallgraph):
                status = False
        print('    is ftrace supported: %s' % res)
 
+       # check if kprobes are available
+       res = sysvals.colorText('NO')
+       sysvals.usekprobes = sysvals.verifyKprobes()
+       if(sysvals.usekprobes):
+               res = 'YES'
+       else:
+               sysvals.usedevsrc = False
+       print('    are kprobes supported: %s' % res)
+
        # what data source are we using
        res = 'DMESG'
        if(ftgood):
@@ -3180,14 +4259,8 @@ def statusCheck():
                sysvals.usetraceevents = False
                for e in sysvals.traceevents:
                        check = False
-                       if(sysvals.android):
-                               out = os.popen(sysvals.adb+' shell ls -d '+\
-                                       sysvals.epath+e).read().strip()
-                               if(out == sysvals.epath+e):
-                                       check = True
-                       else:
-                               if(os.path.exists(sysvals.epath+e)):
-                                       check = True
+                       if(os.path.exists(sysvals.epath+e)):
+                               check = True
                        if(not check):
                                sysvals.usetraceeventsonly = False
                        if(e == 'suspend_resume' and check):
@@ -3199,13 +4272,48 @@ def statusCheck():
        print('    timeline data source: %s' % res)
 
        # check if rtcwake
-       res = 'NO'
+       res = sysvals.colorText('NO')
        if(sysvals.rtcpath != ''):
                res = 'YES'
        elif(sysvals.rtcwake):
                status = False
        print('    is rtcwake supported: %s' % res)
 
+       if not probecheck:
+               return status
+
+       if (sysvals.usecallgraph and len(sysvals.debugfuncs) > 0) or len(sysvals.kprobes) > 0:
+               sysvals.initFtrace(True)
+
+       # verify callgraph debugfuncs
+       if sysvals.usecallgraph and len(sysvals.debugfuncs) > 0:
+               print('    verifying these ftrace callgraph functions work:')
+               sysvals.setFtraceFilterFunctions(sysvals.debugfuncs)
+               fp = open(sysvals.tpath+'set_graph_function', 'r')
+               flist = fp.read().split('\n')
+               fp.close()
+               for func in sysvals.debugfuncs:
+                       res = sysvals.colorText('NO')
+                       if func in flist:
+                               res = 'YES'
+                       else:
+                               for i in flist:
+                                       if ' [' in i and func == i.split(' ')[0]:
+                                               res = 'YES'
+                                               break
+                       print('         %s: %s' % (func, res))
+
+       # verify kprobes
+       if len(sysvals.kprobes) > 0:
+               print('    verifying these kprobes work:')
+               for name in sorted(sysvals.kprobes):
+                       if name in sysvals.tracefuncs:
+                               continue
+                       res = sysvals.colorText('NO')
+                       if sysvals.testKprobe(sysvals.kprobes[name]):
+                               res = 'YES'
+                       print('         %s: %s' % (name, res))
+
        return status
 
 # Function: doError
@@ -3226,7 +4334,7 @@ def doError(msg, help):
 # Arguments:
 #       msg: the warning message to print
 #       file: If not empty, a filename to request be sent to the owner for debug
-def doWarning(msg, file):
+def doWarning(msg, file=''):
        print('/* %s */') % msg
        if(file):
                print('/* For a fix, please send this'+\
@@ -3235,18 +4343,25 @@ def doWarning(msg, file):
 # Function: rootCheck
 # Description:
 #       quick check to see if we have root access
-def rootCheck():
-       if(os.environ['USER'] != 'root'):
-               doError('This script must be run as root', False)
+def rootCheck(fatal):
+       global sysvals
+       if(os.access(sysvals.powerfile, os.W_OK)):
+               return True
+       if fatal:
+               doError('This command must be run as root', False)
+       return False
 
 # Function: getArgInt
 # Description:
 #       pull out an integer argument from the command line with checks
-def getArgInt(name, args, min, max):
-       try:
-               arg = args.next()
-       except:
-               doError(name+': no argument supplied', True)
+def getArgInt(name, args, min, max, main=True):
+       if main:
+               try:
+                       arg = args.next()
+               except:
+                       doError(name+': no argument supplied', True)
+       else:
+               arg = args
        try:
                val = int(arg)
        except:
@@ -3255,6 +4370,25 @@ def getArgInt(name, args, min, max):
                doError(name+': value should be between %d and %d' % (min, max), True)
        return val
 
+# Function: getArgFloat
+# Description:
+#       pull out a float argument from the command line with checks
+def getArgFloat(name, args, min, max, main=True):
+       if main:
+               try:
+                       arg = args.next()
+               except:
+                       doError(name+': no argument supplied', True)
+       else:
+               arg = args
+       try:
+               val = float(arg)
+       except:
+               doError(name+': non-numerical value given', True)
+       if(val < min or val > max):
+               doError(name+': value should be between %f and %f' % (min, max), True)
+       return val
+
 # Function: rerunTest
 # Description:
 #       generate an output from an existing set of ftrace/dmesg logs
@@ -3282,15 +4416,12 @@ def rerunTest():
 # Function: runTest
 # Description:
 #       execute a suspend/resume, gather the logs, and generate the output
-def runTest(subdir):
+def runTest(subdir, testpath=''):
        global sysvals
 
        # prepare for the test
-       if(not sysvals.android):
-               initFtrace()
-       else:
-               initFtraceAndroid()
-       sysvals.initTestOutput(subdir)
+       sysvals.initFtrace()
+       sysvals.initTestOutput(subdir, testpath)
 
        vprint('Output files:\n    %s' % sysvals.dmesgfile)
        if(sysvals.usecallgraph or
@@ -3300,10 +4431,8 @@ def runTest(subdir):
        vprint('    %s' % sysvals.htmlfile)
 
        # execute the test
-       if(not sysvals.android):
-               executeSuspend()
-       else:
-               executeAndroidSuspend()
+       executeSuspend()
+       sysvals.cleanupFtrace()
 
        # analyze the data and create the html output
        print('PROCESSING DATA')
@@ -3367,6 +4496,153 @@ def runSummary(subdir, output):
 
        createHTMLSummarySimple(testruns, subdir+'/summary.html')
 
+# Function: checkArgBool
+# Description:
+#       check if a boolean string value is true or false
+def checkArgBool(value):
+       yes = ['1', 'true', 'yes', 'on']
+       if value.lower() in yes:
+               return True
+       return False
+
+# Function: configFromFile
+# Description:
+#       Configure the script via the info in a config file
+def configFromFile(file):
+       global sysvals
+       Config = ConfigParser.ConfigParser()
+
+       ignorekprobes = False
+       Config.read(file)
+       sections = Config.sections()
+       if 'Settings' in sections:
+               for opt in Config.options('Settings'):
+                       value = Config.get('Settings', opt).lower()
+                       if(opt.lower() == 'verbose'):
+                               sysvals.verbose = checkArgBool(value)
+                       elif(opt.lower() == 'addlogs'):
+                               sysvals.addlogs = checkArgBool(value)
+                       elif(opt.lower() == 'dev'):
+                               sysvals.usedevsrc = checkArgBool(value)
+                       elif(opt.lower() == 'ignorekprobes'):
+                               ignorekprobes = checkArgBool(value)
+                       elif(opt.lower() == 'x2'):
+                               if checkArgBool(value):
+                                       sysvals.execcount = 2
+                       elif(opt.lower() == 'callgraph'):
+                               sysvals.usecallgraph = checkArgBool(value)
+                       elif(opt.lower() == 'callgraphfunc'):
+                               sysvals.debugfuncs = []
+                               if value:
+                                       value = value.split(',')
+                               for i in value:
+                                       sysvals.debugfuncs.append(i.strip())
+                       elif(opt.lower() == 'expandcg'):
+                               sysvals.cgexp = checkArgBool(value)
+                       elif(opt.lower() == 'srgap'):
+                               if checkArgBool(value):
+                                       sysvals.srgap = 5
+                       elif(opt.lower() == 'mode'):
+                               sysvals.suspendmode = value
+                       elif(opt.lower() == 'command'):
+                               sysvals.testcommand = value
+                       elif(opt.lower() == 'x2delay'):
+                               sysvals.x2delay = getArgInt('-x2delay', value, 0, 60000, False)
+                       elif(opt.lower() == 'postres'):
+                               sysvals.postresumetime = getArgInt('-postres', value, 0, 3600, False)
+                       elif(opt.lower() == 'rtcwake'):
+                               sysvals.rtcwake = True
+                               sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False)
+                       elif(opt.lower() == 'timeprec'):
+                               sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False))
+                       elif(opt.lower() == 'mindev'):
+                               sysvals.mindevlen = getArgFloat('-mindev', value, 0.0, 10000.0, False)
+                       elif(opt.lower() == 'mincg'):
+                               sysvals.mincglen = getArgFloat('-mincg', value, 0.0, 10000.0, False)
+                       elif(opt.lower() == 'kprobecolor'):
+                               try:
+                                       val = int(value, 16)
+                                       sysvals.kprobecolor = '#'+value
+                               except:
+                                       sysvals.kprobecolor = value
+                       elif(opt.lower() == 'synccolor'):
+                               try:
+                                       val = int(value, 16)
+                                       sysvals.synccolor = '#'+value
+                               except:
+                                       sysvals.synccolor = value
+                       elif(opt.lower() == 'output-dir'):
+                               args = dict()
+                               n = datetime.now()
+                               args['date'] = n.strftime('%y%m%d')
+                               args['time'] = n.strftime('%H%M%S')
+                               args['hostname'] = sysvals.hostname
+                               sysvals.outdir = value.format(**args)
+
+       if sysvals.suspendmode == 'command' and not sysvals.testcommand:
+               doError('No command supplied for mode "command"', False)
+       if sysvals.usedevsrc and sysvals.usecallgraph:
+               doError('dev and callgraph cannot both be true', False)
+       if sysvals.usecallgraph and sysvals.execcount > 1:
+               doError('-x2 is not compatible with -f', False)
+
+       if ignorekprobes:
+               return
+
+       kprobes = dict()
+       archkprobe = 'Kprobe_'+platform.machine()
+       if archkprobe in sections:
+               for name in Config.options(archkprobe):
+                       kprobes[name] = Config.get(archkprobe, name)
+       if 'Kprobe' in sections:
+               for name in Config.options('Kprobe'):
+                       kprobes[name] = Config.get('Kprobe', name)
+
+       for name in kprobes:
+               function = name
+               format = name
+               color = ''
+               args = dict()
+               data = kprobes[name].split()
+               i = 0
+               for val in data:
+                       # bracketted strings are special formatting, read them separately
+                       if val[0] == '[' and val[-1] == ']':
+                               for prop in val[1:-1].split(','):
+                                       p = prop.split('=')
+                                       if p[0] == 'color':
+                                               try:
+                                                       color = int(p[1], 16)
+                                                       color = '#'+p[1]
+                                               except:
+                                                       color = p[1]
+                               continue
+                       # first real arg should be the format string
+                       if i == 0:
+                               format = val
+                       # all other args are actual function args
+                       else:
+                               d = val.split('=')
+                               args[d[0]] = d[1]
+                       i += 1
+               if not function or not format:
+                       doError('Invalid kprobe: %s' % name, False)
+               for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', format):
+                       if arg not in args:
+                               doError('Kprobe "%s" is missing argument "%s"' % (name, arg), False)
+               if name in sysvals.kprobes:
+                       doError('Duplicate kprobe found "%s"' % (name), False)
+               vprint('Adding KPROBE: %s %s %s %s' % (name, function, format, args))
+               sysvals.kprobes[name] = {
+                       'name': name,
+                       'func': function,
+                       'format': format,
+                       'args': args,
+                       'mask': re.sub('{(?P<n>[a-z,A-Z,0-9]*)}', '.*', format)
+               }
+               if color:
+                       sysvals.kprobes[name]['color'] = color
+
 # Function: printHelp
 # Description:
 #       print out the help text
@@ -3375,7 +4651,7 @@ def printHelp():
        modes = getModes()
 
        print('')
-       print('AnalyzeSuspend v%.1f' % sysvals.version)
+       print('AnalyzeSuspend v%s' % sysvals.version)
        print('Usage: sudo analyze_suspend.py <options>')
        print('')
        print('Description:')
@@ -3396,27 +4672,38 @@ def printHelp():
        print('  [general]')
        print('    -h          Print this help text')
        print('    -v          Print the current tool version')
+       print('    -config file Pull arguments and config options from a file')
        print('    -verbose    Print extra information during execution and analysis')
        print('    -status     Test to see if the system is enabled to run this tool')
        print('    -modes      List available suspend modes')
        print('    -m mode     Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode)
-       print('    -rtcwake t  Use rtcwake to autoresume after <t> seconds (default: disabled)')
+       print('    -o subdir   Override the output subdirectory')
        print('  [advanced]')
+       print('    -rtcwake t  Use rtcwake to autoresume after <t> seconds (default: disabled)')
+       print('    -addlogs    Add the dmesg and ftrace logs to the html output')
+       print('    -multi n d  Execute <n> consecutive tests at <d> seconds intervals. The outputs will')
+       print('                be created in a new subdirectory with a summary page.')
+       print('    -srgap      Add a visible gap in the timeline between sus/res (default: disabled)')
+       print('    -cmd {s}    Instead of suspend/resume, run a command, e.g. "sync -d"')
+       print('    -mindev ms  Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)')
+       print('    -mincg  ms  Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)')
+       print('    -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)')
+       print('  [debug]')
        print('    -f          Use ftrace to create device callgraphs (default: disabled)')
-       print('    -filter "d1 d2 ..." Filter out all but this list of dev names')
+       print('    -expandcg   pre-expand the callgraph data in the html output (default: disabled)')
+       print('    -flist      Print the list of functions currently being captured in ftrace')
+       print('    -flistall   Print all functions capable of being captured in ftrace')
+       print('    -fadd file  Add functions to be graphed in the timeline from a list in a text file')
+       print('    -filter "d1 d2 ..." Filter out all but this list of device names')
+       print('    -dev        Display common low level functions in the timeline')
+       print('  [post-resume task analysis]')
        print('    -x2         Run two suspend/resumes back to back (default: disabled)')
        print('    -x2delay t  Minimum millisecond delay <t> between the two test runs (default: 0 ms)')
        print('    -postres t  Time after resume completion to wait for post-resume events (default: 0 S)')
-       print('    -multi n d  Execute <n> consecutive tests at <d> seconds intervals. The outputs will')
-       print('                be created in a new subdirectory with a summary page.')
        print('  [utilities]')
        print('    -fpdt       Print out the contents of the ACPI Firmware Performance Data Table')
        print('    -usbtopo    Print out the current USB topology with power info')
        print('    -usbauto    Enable autosuspend for all connected USB devices')
-       print('  [android testing]')
-       print('    -adb binary Use the given adb binary to run the test on an android device.')
-       print('                The device should already be connected and with root access.')
-       print('                Commands will be executed on the device using "adb shell"')
        print('  [re-analyze data from previous runs]')
        print('    -ftrace ftracefile  Create HTML output using ftrace input')
        print('    -dmesg dmesgfile    Create HTML output using dmesg (not needed for kernel >= 3.15)')
@@ -3430,6 +4717,7 @@ if __name__ == '__main__':
        cmd = ''
        cmdarg = ''
        multitest = {'run': False, 'count': 0, 'delay': 0}
+       simplecmds = ['-modes', '-fpdt', '-flist', '-flistall', '-usbtopo', '-usbauto', '-status']
        # loop through the command line arguments
        args = iter(sys.argv[1:])
        for arg in args:
@@ -3438,58 +4726,85 @@ if __name__ == '__main__':
                                val = args.next()
                        except:
                                doError('No mode supplied', True)
+                       if val == 'command' and not sysvals.testcommand:
+                               doError('No command supplied for mode "command"', True)
                        sysvals.suspendmode = val
-               elif(arg == '-adb'):
-                       try:
-                               val = args.next()
-                       except:
-                               doError('No adb binary supplied', True)
-                       if(not os.path.exists(val)):
-                               doError('file doesnt exist: %s' % val, False)
-                       if(not os.access(val, os.X_OK)):
-                               doError('file isnt executable: %s' % val, False)
-                       try:
-                               check = os.popen(val+' version').read().strip()
-                       except:
-                               doError('adb version failed to execute', False)
-                       if(not re.match('Android Debug Bridge .*', check)):
-                               doError('adb version failed to execute', False)
-                       sysvals.adb = val
-                       sysvals.android = True
+               elif(arg in simplecmds):
+                       cmd = arg[1:]
+               elif(arg == '-h'):
+                       printHelp()
+                       sys.exit()
+               elif(arg == '-v'):
+                       print("Version %s" % sysvals.version)
+                       sys.exit()
                elif(arg == '-x2'):
-                       if(sysvals.postresumetime > 0):
-                               doError('-x2 is not compatible with -postres', False)
                        sysvals.execcount = 2
+                       if(sysvals.usecallgraph):
+                               doError('-x2 is not compatible with -f', False)
                elif(arg == '-x2delay'):
                        sysvals.x2delay = getArgInt('-x2delay', args, 0, 60000)
                elif(arg == '-postres'):
-                       if(sysvals.execcount != 1):
-                               doError('-x2 is not compatible with -postres', False)
                        sysvals.postresumetime = getArgInt('-postres', args, 0, 3600)
                elif(arg == '-f'):
                        sysvals.usecallgraph = True
-               elif(arg == '-modes'):
-                       cmd = 'modes'
-               elif(arg == '-fpdt'):
-                       cmd = 'fpdt'
-               elif(arg == '-usbtopo'):
-                       cmd = 'usbtopo'
-               elif(arg == '-usbauto'):
-                       cmd = 'usbauto'
-               elif(arg == '-status'):
-                       cmd = 'status'
+                       if(sysvals.execcount > 1):
+                               doError('-x2 is not compatible with -f', False)
+                       if(sysvals.usedevsrc):
+                               doError('-dev is not compatible with -f', False)
+               elif(arg == '-addlogs'):
+                       sysvals.addlogs = True
                elif(arg == '-verbose'):
                        sysvals.verbose = True
-               elif(arg == '-v'):
-                       print("Version %.1f" % sysvals.version)
-                       sys.exit()
+               elif(arg == '-dev'):
+                       sysvals.usedevsrc = True
+                       if(sysvals.usecallgraph):
+                               doError('-dev is not compatible with -f', False)
                elif(arg == '-rtcwake'):
                        sysvals.rtcwake = True
                        sysvals.rtcwaketime = getArgInt('-rtcwake', args, 0, 3600)
+               elif(arg == '-timeprec'):
+                       sysvals.setPrecision(getArgInt('-timeprec', args, 0, 6))
+               elif(arg == '-mindev'):
+                       sysvals.mindevlen = getArgFloat('-mindev', args, 0.0, 10000.0)
+               elif(arg == '-mincg'):
+                       sysvals.mincglen = getArgFloat('-mincg', args, 0.0, 10000.0)
+               elif(arg == '-cmd'):
+                       try:
+                               val = args.next()
+                       except:
+                               doError('No command string supplied', True)
+                       sysvals.testcommand = val
+                       sysvals.suspendmode = 'command'
+               elif(arg == '-expandcg'):
+                       sysvals.cgexp = True
+               elif(arg == '-srgap'):
+                       sysvals.srgap = 5
                elif(arg == '-multi'):
                        multitest['run'] = True
                        multitest['count'] = getArgInt('-multi n (exec count)', args, 2, 1000000)
                        multitest['delay'] = getArgInt('-multi d (delay between tests)', args, 0, 3600)
+               elif(arg == '-o'):
+                       try:
+                               val = args.next()
+                       except:
+                               doError('No subdirectory name supplied', True)
+                       sysvals.outdir = val
+               elif(arg == '-config'):
+                       try:
+                               val = args.next()
+                       except:
+                               doError('No text file supplied', True)
+                       if(os.path.exists(val) == False):
+                               doError('%s does not exist' % val, False)
+                       configFromFile(val)
+               elif(arg == '-fadd'):
+                       try:
+                               val = args.next()
+                       except:
+                               doError('No text file supplied', True)
+                       if(os.path.exists(val) == False):
+                               doError('%s does not exist' % val, False)
+                       sysvals.addFtraceFilterFunctions(val)
                elif(arg == '-dmesg'):
                        try:
                                val = args.next()
@@ -3498,17 +4813,16 @@ if __name__ == '__main__':
                        sysvals.notestrun = True
                        sysvals.dmesgfile = val
                        if(os.path.exists(sysvals.dmesgfile) == False):
-                               doError('%s doesnt exist' % sysvals.dmesgfile, False)
+                               doError('%s does not exist' % sysvals.dmesgfile, False)
                elif(arg == '-ftrace'):
                        try:
                                val = args.next()
                        except:
                                doError('No ftrace file supplied', True)
                        sysvals.notestrun = True
-                       sysvals.usecallgraph = True
                        sysvals.ftracefile = val
                        if(os.path.exists(sysvals.ftracefile) == False):
-                               doError('%s doesnt exist' % sysvals.ftracefile, False)
+                               doError('%s does not exist' % sysvals.ftracefile, False)
                elif(arg == '-summary'):
                        try:
                                val = args.next()
@@ -3518,35 +4832,35 @@ if __name__ == '__main__':
                        cmdarg = val
                        sysvals.notestrun = True
                        if(os.path.isdir(val) == False):
-                               doError('%s isnt accesible' % val, False)
+                               doError('%s is not accesible' % val, False)
                elif(arg == '-filter'):
                        try:
                                val = args.next()
                        except:
                                doError('No devnames supplied', True)
                        sysvals.setDeviceFilter(val)
-               elif(arg == '-h'):
-                       printHelp()
-                       sys.exit()
                else:
                        doError('Invalid argument: '+arg, True)
 
+       # callgraph size cannot exceed device size
+       if sysvals.mincglen < sysvals.mindevlen:
+               sysvals.mincglen = sysvals.mindevlen
+
        # just run a utility command and exit
        if(cmd != ''):
                if(cmd == 'status'):
-                       statusCheck()
+                       statusCheck(True)
                elif(cmd == 'fpdt'):
-                       if(sysvals.android):
-                               doError('cannot read FPDT on android device', False)
                        getFPDT(True)
                elif(cmd == 'usbtopo'):
-                       if(sysvals.android):
-                               doError('cannot read USB topology '+\
-                                       'on an android device', False)
-                       detectUSB(True)
+                       detectUSB()
                elif(cmd == 'modes'):
                        modes = getModes()
                        print modes
+               elif(cmd == 'flist'):
+                       sysvals.getFtraceFilterFunctions(True)
+               elif(cmd == 'flistall'):
+                       sysvals.getFtraceFilterFunctions(False)
                elif(cmd == 'usbauto'):
                        setUSBDevicesAuto()
                elif(cmd == 'summary'):
@@ -3554,15 +4868,6 @@ if __name__ == '__main__':
                        runSummary(cmdarg, True)
                sys.exit()
 
-       # run test on android device
-       if(sysvals.android):
-               if(sysvals.usecallgraph):
-                       doError('ftrace (-f) is not yet supported '+\
-                               'in the android kernel', False)
-               if(sysvals.notestrun):
-                       doError('cannot analyze test files on the '+\
-                               'android device', False)
-
        # if instructed, re-analyze existing data files
        if(sysvals.notestrun):
                rerunTest()
@@ -3574,18 +4879,20 @@ if __name__ == '__main__':
                sys.exit()
 
        if multitest['run']:
-               # run multiple tests in a separte subdirectory
+               # run multiple tests in a separate subdirectory
                s = 'x%d' % multitest['count']
-               subdir = datetime.now().strftime('suspend-'+s+'-%m%d%y-%H%M%S')
-               os.mkdir(subdir)
+               if not sysvals.outdir:
+                       sysvals.outdir = datetime.now().strftime('suspend-'+s+'-%m%d%y-%H%M%S')
+               if not os.path.isdir(sysvals.outdir):
+                       os.mkdir(sysvals.outdir)
                for i in range(multitest['count']):
                        if(i != 0):
                                print('Waiting %d seconds...' % (multitest['delay']))
                                time.sleep(multitest['delay'])
                        print('TEST (%d/%d) START' % (i+1, multitest['count']))
-                       runTest(subdir)
+                       runTest(sysvals.outdir)
                        print('TEST (%d/%d) COMPLETE' % (i+1, multitest['count']))
-               runSummary(subdir, False)
+               runSummary(sysvals.outdir, False)
        else:
                # run the test in the current directory
-               runTest(".")
+               runTest('.', sysvals.outdir)
index cd129e6..8b00031 100644 (file)
@@ -13,9 +13,11 @@ quiet_cmd_gen_constants_py = GEN     $@
        $(CPP) -E -x c -P $(c_flags) $< > $@ ;\
        sed -i '1,/<!-- end-c-headers -->/d;' $@
 
-$(obj)/constants.py: $(SRCTREE)/$(obj)/constants.py.in
-       $(call if_changed,gen_constants_py)
+targets += constants.py
+$(obj)/constants.py: $(SRCTREE)/$(obj)/constants.py.in FORCE
+       $(call if_changed_dep,gen_constants_py)
 
 build_constants_py: $(obj)/constants.py
+       @:
 
 clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py) $(obj)/constants.py
index 07e6c2b..7986f4e 100644 (file)
@@ -14,7 +14,6 @@
 
 #include <linux/fs.h>
 #include <linux/mount.h>
-#include <linux/radix-tree.h>
 
 /* We need to stringify expanded macros so that they can be parsed */
 
@@ -51,9 +50,3 @@ LX_VALUE(MNT_NOEXEC)
 LX_VALUE(MNT_NOATIME)
 LX_VALUE(MNT_NODIRATIME)
 LX_VALUE(MNT_RELATIME)
-
-/* linux/radix-tree.h */
-LX_VALUE(RADIX_TREE_INDIRECT_PTR)
-LX_GDBPARSED(RADIX_TREE_HEIGHT_MASK)
-LX_GDBPARSED(RADIX_TREE_MAP_SHIFT)
-LX_GDBPARSED(RADIX_TREE_MAP_MASK)
diff --git a/scripts/gdb/linux/radixtree.py b/scripts/gdb/linux/radixtree.py
deleted file mode 100644 (file)
index 0fdef4e..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-#
-# gdb helper commands and functions for Linux kernel debugging
-#
-#  Radix Tree Parser
-#
-# Copyright (c) 2016 Linaro Ltd
-#
-# Authors:
-#  Kieran Bingham <kieran.bingham@linaro.org>
-#
-# This work is licensed under the terms of the GNU GPL version 2.
-#
-
-import gdb
-
-from linux import utils
-from linux import constants
-
-radix_tree_root_type = utils.CachedType("struct radix_tree_root")
-radix_tree_node_type = utils.CachedType("struct radix_tree_node")
-
-
-def is_indirect_ptr(node):
-    long_type = utils.get_long_type()
-    return (node.cast(long_type) & constants.LX_RADIX_TREE_INDIRECT_PTR)
-
-
-def indirect_to_ptr(node):
-    long_type = utils.get_long_type()
-    node_type = node.type
-    indirect_ptr = node.cast(long_type) & ~constants.LX_RADIX_TREE_INDIRECT_PTR
-    return indirect_ptr.cast(node_type)
-
-
-def maxindex(height):
-    height = height & constants.LX_RADIX_TREE_HEIGHT_MASK
-    return gdb.parse_and_eval("height_to_maxindex["+str(height)+"]")
-
-
-def lookup(root, index):
-    if root.type == radix_tree_root_type.get_type().pointer():
-        root = root.dereference()
-    elif root.type != radix_tree_root_type.get_type():
-        raise gdb.GdbError("Must be struct radix_tree_root not {}"
-                           .format(root.type))
-
-    node = root['rnode']
-    if node is 0:
-        return None
-
-    if not (is_indirect_ptr(node)):
-        if (index > 0):
-            return None
-        return node
-
-    node = indirect_to_ptr(node)
-
-    height = node['path'] & constants.LX_RADIX_TREE_HEIGHT_MASK
-    if (index > maxindex(height)):
-        return None
-
-    shift = (height-1) * constants.LX_RADIX_TREE_MAP_SHIFT
-
-    while True:
-        new_index = (index >> shift) & constants.LX_RADIX_TREE_MAP_MASK
-        slot = node['slots'][new_index]
-
-        node = slot.cast(node.type.pointer()).dereference()
-        if node is 0:
-            return None
-
-        shift -= constants.LX_RADIX_TREE_MAP_SHIFT
-        height -= 1
-
-        if (height <= 0):
-            break
-
-    return node
-
-
-class LxRadixTree(gdb.Function):
-    """ Lookup and return a node from a RadixTree.
-
-$lx_radix_tree_lookup(root_node [, index]): Return the node at the given index.
-If index is omitted, the root node is dereferenced and returned."""
-
-    def __init__(self):
-        super(LxRadixTree, self).__init__("lx_radix_tree_lookup")
-
-    def invoke(self, root, index=0):
-        result = lookup(root, index)
-        if result is None:
-            raise gdb.GdbError("No entry in tree at index {}".format(index))
-
-        return result
-
-LxRadixTree()
index 9a0f892..004b0ac 100644 (file)
@@ -153,7 +153,7 @@ lx-symbols command."""
             saved_state['breakpoint'].enabled = saved_state['enabled']
 
     def invoke(self, arg, from_tty):
-        self.module_paths = arg.split()
+        self.module_paths = [os.path.expanduser(p) for p in arg.split()]
         self.module_paths.append(os.getcwd())
 
         # enforce update
index 3a80ad6..6e0b0af 100644 (file)
@@ -31,4 +31,3 @@ else:
     import linux.lists
     import linux.proc
     import linux.constants
-    import linux.radixtree
index a85d455..b4fe9b0 100644 (file)
@@ -160,6 +160,8 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
        
        if (snd_BUG_ON(!card || !id))
                return;
+       if (card->shutdown)
+               return;
        read_lock(&card->ctl_files_rwlock);
 #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
        card->mixer_oss_change_count++;
index 308c9ec..8e980aa 100644 (file)
@@ -849,6 +849,14 @@ int snd_pcm_new_internal(struct snd_card *card, const char *id, int device,
 }
 EXPORT_SYMBOL(snd_pcm_new_internal);
 
+static void free_chmap(struct snd_pcm_str *pstr)
+{
+       if (pstr->chmap_kctl) {
+               snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl);
+               pstr->chmap_kctl = NULL;
+       }
+}
+
 static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
 {
        struct snd_pcm_substream *substream, *substream_next;
@@ -871,6 +879,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
                kfree(setup);
        }
 #endif
+       free_chmap(pstr);
        if (pstr->substream_count)
                put_device(&pstr->dev);
 }
@@ -1135,10 +1144,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
        for (cidx = 0; cidx < 2; cidx++) {
                if (!pcm->internal)
                        snd_unregister_device(&pcm->streams[cidx].dev);
-               if (pcm->streams[cidx].chmap_kctl) {
-                       snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl);
-                       pcm->streams[cidx].chmap_kctl = NULL;
-               }
+               free_chmap(&pcm->streams[cidx]);
        }
        mutex_unlock(&pcm->open_mutex);
        mutex_unlock(&register_mutex);
index e320c44..6f8ea13 100644 (file)
@@ -1218,8 +1218,10 @@ static int azx_free(struct azx *chip)
        if (use_vga_switcheroo(hda)) {
                if (chip->disabled && hda->probe_continued)
                        snd_hda_unlock_devices(&chip->bus);
-               if (hda->vga_switcheroo_registered)
+               if (hda->vga_switcheroo_registered) {
                        vga_switcheroo_unregister_client(chip->pci);
+                       vga_switcheroo_fini_domain_pm_ops(chip->card->dev);
+               }
        }
 
        if (bus->chip_init) {
@@ -2267,6 +2269,8 @@ static const struct pci_device_id azx_ids[] = {
          .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
        { PCI_DEVICE(0x1002, 0x157a),
          .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+       { PCI_DEVICE(0x1002, 0x15b3),
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
        { PCI_DEVICE(0x1002, 0x793b),
          .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
        { PCI_DEVICE(0x1002, 0x7919),
index 5fac786..abcb5a6 100644 (file)
@@ -5738,7 +5738,6 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
        {}
 };
 #define ALC225_STANDARD_PINS \
-       {0x12, 0xb7a60130}, \
        {0x21, 0x04211020}
 
 #define ALC256_STANDARD_PINS \
@@ -5763,10 +5762,24 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
 static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
        SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
                ALC225_STANDARD_PINS,
+               {0x12, 0xb7a60130},
                {0x14, 0x901701a0}),
        SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
                ALC225_STANDARD_PINS,
+               {0x12, 0xb7a60130},
                {0x14, 0x901701b0}),
+       SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC225_STANDARD_PINS,
+               {0x12, 0xb7a60150},
+               {0x14, 0x901701a0}),
+       SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC225_STANDARD_PINS,
+               {0x12, 0xb7a60150},
+               {0x14, 0x901701b0}),
+       SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC225_STANDARD_PINS,
+               {0x12, 0xb7a60130},
+               {0x1b, 0x90170110}),
        SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
                {0x14, 0x90170110},
                {0x21, 0x02211020}),
index 69860da..9e5276d 100644 (file)
@@ -556,7 +556,6 @@ static int usb_audio_probe(struct usb_interface *intf,
                                goto __error;
                        }
                        chip = usb_chip[i];
-                       dev_set_drvdata(&dev->dev, chip);
                        atomic_inc(&chip->active); /* avoid autopm */
                        break;
                }
@@ -582,6 +581,7 @@ static int usb_audio_probe(struct usb_interface *intf,
                        goto __error;
                }
        }
+       dev_set_drvdata(&dev->dev, chip);
 
        /*
         * For devices with more than one control interface, we assume the
index e8a1e69..25d8031 100644 (file)
@@ -122,10 +122,14 @@ static bool ignore_func(struct objtool_file *file, struct symbol *func)
 
        /* check for STACK_FRAME_NON_STANDARD */
        if (file->whitelist && file->whitelist->rela)
-               list_for_each_entry(rela, &file->whitelist->rela->rela_list, list)
-                       if (rela->sym->sec == func->sec &&
+               list_for_each_entry(rela, &file->whitelist->rela->rela_list, list) {
+                       if (rela->sym->type == STT_SECTION &&
+                           rela->sym->sec == func->sec &&
                            rela->addend == func->offset)
                                return true;
+                       if (rela->sym->type == STT_FUNC && rela->sym == func)
+                               return true;
+               }
 
        /* check if it has a context switching instruction */
        func_for_each_insn(file, func, insn)
index e367b1a..8561e7d 100644 (file)
@@ -1,7 +1,7 @@
 CC             = $(CROSS_COMPILE)gcc
 BUILD_OUTPUT   := $(CURDIR)
-PREFIX         := /usr
-DESTDIR                :=
+PREFIX         ?= /usr
+DESTDIR                ?=
 
 ifeq ("$(origin O)", "command line")
        BUILD_OUTPUT := $(O)
index b7447ce..b0ac057 100644 (file)
@@ -122,7 +122,7 @@ enum {
        NODE_TAGGED = 2,
 };
 
-#define THRASH_SIZE            1000 * 1000
+#define THRASH_SIZE            (1000 * 1000)
 #define N 127
 #define BATCH  33
 
index 7cf6e17..b9d34b3 100644 (file)
@@ -510,10 +510,11 @@ static void slab_stats(struct slabinfo *s)
                        s->alloc_node_mismatch, (s->alloc_node_mismatch * 100) / total);
        }
 
-       if (s->cmpxchg_double_fail || s->cmpxchg_double_cpu_fail)
+       if (s->cmpxchg_double_fail || s->cmpxchg_double_cpu_fail) {
                printf("\nCmpxchg_double Looping\n------------------------\n");
                printf("Locked Cmpxchg Double redos   %lu\nUnlocked Cmpxchg Double redos %lu\n",
                        s->cmpxchg_double_fail, s->cmpxchg_double_cpu_fail);
+       }
 }
 
 static void report(struct slabinfo *s)