aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuodong Xu <guodong.xu@linaro.org>2013-12-24 15:33:58 +0800
committerGuodong Xu <guodong.xu@linaro.org>2013-12-24 15:33:58 +0800
commitb5f77390e00d2f05c52efd4e6fd1f83d145eb9dc (patch)
tree045d8d19fa25a878e83b45ec2c0dadd9235c5aa1
parent85e32c21a194794f6bf1bc1c0f8e89914e06cda1 (diff)
parentf41a52ee6767653f23794ccc7f342d5628c2f6f7 (diff)
Merge commit 'lsk-android-13.11' into integration-hilt-lsk-android
* commit 'lsk-android-13.11': (626 commits) sched: hmp: Fix build breakage when not using CONFIG_SCHED_HMP Documentation: HMP: Small Task Packing explanation sched: hmp: add read-only hmp domain sysfs file HMP: Avoid using the cpu stopper to stop runnable tasks Linux 3.10.20 media: sh_vou: almost forever loop in sh_vou_try_fmt_vid_out() usbcore: set lpm_capable field for LPM capable root hubs usb: fail on usb_hub_create_port_device() errors usb: fix cleanup after failure in hub_configure() backlight: atmel-pwm-bl: fix deferred probe from __init misc: atmel_pwm: add deferred-probing support iwlwifi: pcie: add new SKUs for 7000 & 3160 NIC series iwlwifi: add new 7260 and 3160 series device IDs perf: Fix perf ring buffer memory ordering tracing: Fix potential out-of-bounds in trace_get_user() ALSA: hda - hdmi: Fix reported channel map on common default layouts USB: add new zte 3g-dongle's pid to option.c hyperv-fb: add pci stub batman-adv: set up network coding packet handlers during module init xen-netback: transition to CLOSED when removing a VIF ...
-rw-r--r--Documentation/DocBook/media_api.tmpl4
-rw-r--r--Documentation/arm/small_task_packing.txt136
-rw-r--r--Documentation/filesystems/proc.txt6
-rw-r--r--Documentation/networking/ip-sysctl.txt9
-rw-r--r--Documentation/sysctl/vm.txt16
-rw-r--r--Makefile2
-rw-r--r--android/configs/android-recommended.cfg1
-rw-r--r--arch/arc/include/asm/delay.h5
-rw-r--r--arch/arc/include/asm/sections.h1
-rw-r--r--arch/arc/include/asm/spinlock.h9
-rw-r--r--arch/arc/include/asm/uaccess.h4
-rw-r--r--arch/arc/kernel/head.S7
-rw-r--r--arch/arc/kernel/irq.c3
-rw-r--r--arch/arc/kernel/ptrace.c2
-rw-r--r--arch/arc/kernel/setup.c3
-rw-r--r--arch/arc/kernel/signal.c25
-rw-r--r--arch/arc/kernel/unaligned.c6
-rw-r--r--arch/arc/mm/fault.c6
-rw-r--r--arch/arm/Kconfig43
-rw-r--r--arch/arm/boot/dts/at91rm9200.dtsi6
-rw-r--r--arch/arm/boot/dts/at91sam9260.dtsi8
-rw-r--r--arch/arm/boot/dts/at91sam9263.dtsi8
-rw-r--r--arch/arm/boot/dts/at91sam9g45.dtsi8
-rw-r--r--arch/arm/boot/dts/at91sam9n12.dtsi8
-rw-r--r--arch/arm/boot/dts/at91sam9x5.dtsi8
-rw-r--r--arch/arm/boot/dts/integratorcp.dts9
-rw-r--r--arch/arm/boot/dts/sama5d3.dtsi4
-rw-r--r--arch/arm/boot/dts/sun4i-a10.dtsi4
-rw-r--r--arch/arm/boot/dts/sun5i-a13.dtsi4
-rw-r--r--arch/arm/crypto/aes-armv4.S6
-rw-r--r--arch/arm/include/asm/jump_label.h2
-rw-r--r--arch/arm/include/asm/syscall.h6
-rw-r--r--arch/arm/kernel/kgdb.c4
-rw-r--r--arch/arm/kernel/process.c2
-rw-r--r--arch/arm/kernel/smp.c5
-rw-r--r--arch/arm/kvm/coproc.c26
-rw-r--r--arch/arm/kvm/coproc.h3
-rw-r--r--arch/arm/kvm/coproc_a15.c6
-rw-r--r--arch/arm/mach-mxs/pm.h4
-rw-r--r--arch/arm/mach-versatile/include/mach/platform.h2
-rw-r--r--arch/arm/mach-versatile/pci.c47
-rw-r--r--arch/arm/xen/enlighten.c5
-rw-r--r--arch/arm64/kernel/perf_event.c7
-rw-r--r--arch/arm64/kernel/smp.c3
-rw-r--r--arch/avr32/kernel/time.c9
-rw-r--r--arch/mips/ath79/clock.c2
-rw-r--r--arch/mips/include/asm/jump_label.h2
-rw-r--r--arch/parisc/kernel/head.S4
-rw-r--r--arch/parisc/kernel/traps.c6
-rw-r--r--arch/powerpc/include/asm/jump_label.h2
-rw-r--r--arch/powerpc/kernel/align.c10
-rw-r--r--arch/powerpc/kernel/iommu.c2
-rw-r--r--arch/powerpc/kernel/sysfs.c18
-rw-r--r--arch/powerpc/kernel/tm.S94
-rw-r--r--arch/powerpc/kernel/vio.c12
-rw-r--r--arch/powerpc/kvm/book3s_hv_rmhandlers.S2
-rw-r--r--arch/powerpc/kvm/book3s_xics.c1
-rw-r--r--arch/powerpc/lib/checksum_64.S58
-rw-r--r--arch/powerpc/perf/power8-pmu.c5
-rw-r--r--arch/powerpc/platforms/pseries/setup.c31
-rw-r--r--arch/s390/include/asm/jump_label.h2
-rw-r--r--arch/s390/kernel/entry.S1
-rw-r--r--arch/s390/kernel/entry64.S1
-rw-r--r--arch/sparc/include/asm/jump_label.h2
-rw-r--r--arch/sparc/kernel/ds.c5
-rw-r--r--arch/sparc/kernel/entry.S2
-rw-r--r--arch/sparc/kernel/ktlb.S3
-rw-r--r--arch/sparc/kernel/syscalls.S8
-rw-r--r--arch/sparc/kernel/trampoline_64.S2
-rw-r--r--arch/sparc/lib/ksyms.c9
-rw-r--r--arch/tile/include/asm/percpu.h34
-rw-r--r--arch/um/include/shared/os.h1
-rw-r--r--arch/um/kernel/Makefile2
-rw-r--r--arch/um/kernel/exitcode.c4
-rw-r--r--arch/um/kernel/maccess.c24
-rw-r--r--arch/um/os-Linux/process.c52
-rw-r--r--arch/x86/ia32/ia32_signal.c2
-rw-r--r--arch/x86/include/asm/checksum_32.h22
-rw-r--r--arch/x86/include/asm/cpufeature.h2
-rw-r--r--arch/x86/include/asm/e820.h2
-rw-r--r--arch/x86/include/asm/jump_label.h2
-rw-r--r--arch/x86/include/asm/mce.h13
-rw-r--r--arch/x86/include/asm/mmu_context.h20
-rw-r--r--arch/x86/kernel/amd_nb.c13
-rw-r--r--arch/x86/kernel/apic/x2apic_uv_x.c2
-rw-r--r--arch/x86/kernel/e820.c5
-rw-r--r--arch/x86/kernel/reboot.c16
-rw-r--r--arch/x86/kernel/setup.c19
-rw-r--r--arch/x86/kernel/signal.c6
-rw-r--r--arch/x86/lib/csum-wrappers_64.c12
-rw-r--r--arch/x86/platform/efi/efi.c11
-rw-r--r--arch/xtensa/kernel/signal.c2
-rw-r--r--block/cfq-iosched.c2
-rw-r--r--crypto/api.c7
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/acpi/acpi_ipmi.c24
-rw-r--r--drivers/acpi/acpi_lpss.c3
-rw-r--r--drivers/ata/libata-eh.c6
-rw-r--r--drivers/base/core.c14
-rw-r--r--drivers/block/cciss.c1
-rw-r--r--drivers/block/cpqarray.c1
-rw-r--r--drivers/block/rbd.c14
-rw-r--r--drivers/bluetooth/ath3k.c2
-rw-r--r--drivers/bluetooth/btusb.c2
-rw-r--r--drivers/char/random.c3
-rw-r--r--drivers/clk/clk-wm831x.c2
-rw-r--r--drivers/clk/versatile/clk-icst.c2
-rw-r--r--drivers/connector/cn_proc.c18
-rw-r--r--drivers/connector/connector.c7
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c72
-rw-r--r--drivers/cpufreq/intel_pstate.c7
-rw-r--r--drivers/cpuidle/coupled.c127
-rw-r--r--drivers/dma/imx-dma.c31
-rw-r--r--drivers/edac/amd64_edac.c9
-rw-r--r--drivers/gator/Kconfig33
-rw-r--r--drivers/gator/LICENSE339
-rw-r--r--drivers/gator/Makefile76
-rw-r--r--drivers/gator/gator.h146
-rw-r--r--drivers/gator/gator_annotate.c186
-rw-r--r--drivers/gator/gator_annotate_kernel.c157
-rw-r--r--drivers/gator/gator_backtrace.c160
-rw-r--r--drivers/gator/gator_cookies.c397
-rwxr-xr-xdrivers/gator/gator_events.sh19
-rw-r--r--drivers/gator/gator_events_armv6.c244
-rw-r--r--drivers/gator/gator_events_armv7.c319
-rw-r--r--drivers/gator/gator_events_block.c155
-rw-r--r--drivers/gator/gator_events_ccn-504.c306
-rw-r--r--drivers/gator/gator_events_irq.c167
-rw-r--r--drivers/gator/gator_events_l2c-310.c210
-rw-r--r--drivers/gator/gator_events_mali_4xx.c751
-rw-r--r--drivers/gator/gator_events_mali_4xx.h18
-rw-r--r--drivers/gator/gator_events_mali_common.c74
-rw-r--r--drivers/gator/gator_events_mali_common.h88
-rw-r--r--drivers/gator/gator_events_mali_t6xx.c512
-rw-r--r--drivers/gator/gator_events_mali_t6xx_hw.c758
-rw-r--r--drivers/gator/gator_events_mali_t6xx_hw_test.c55
-rw-r--r--drivers/gator/gator_events_meminfo.c240
-rw-r--r--drivers/gator/gator_events_mmaped.c229
-rw-r--r--drivers/gator/gator_events_net.c171
-rw-r--r--drivers/gator/gator_events_perf_pmu.c516
-rw-r--r--drivers/gator/gator_events_sched.c115
-rw-r--r--drivers/gator/gator_events_scorpion.c676
-rw-r--r--drivers/gator/gator_fs.c382
-rw-r--r--drivers/gator/gator_hrtimer_gator.c86
-rw-r--r--drivers/gator/gator_hrtimer_perf.c113
-rw-r--r--drivers/gator/gator_iks.c195
-rw-r--r--drivers/gator/gator_main.c1440
-rw-r--r--drivers/gator/gator_marshaling.c413
-rw-r--r--drivers/gator/gator_pack.c58
-rw-r--r--drivers/gator/gator_trace_gpu.c294
-rw-r--r--drivers/gator/gator_trace_gpu.h79
-rw-r--r--drivers/gator/gator_trace_power.c203
-rw-r--r--drivers/gator/gator_trace_sched.c256
-rw-r--r--drivers/gator/mali/mali_mjollnir_profiling_gator_api.h164
-rw-r--r--drivers/gator/mali/mali_utgard_profiling_gator_api.h202
-rw-r--r--drivers/gator/mali_t6xx.mk25
-rw-r--r--drivers/gpio/gpio-lynxpoint.c5
-rw-r--r--drivers/gpio/gpio-omap.c158
-rw-r--r--drivers/gpu/drm/ast/ast_drv.h2
-rw-r--r--drivers/gpu/drm/drm_crtc.c18
-rw-r--r--drivers/gpu/drm/drm_drv.c9
-rw-r--r--drivers/gpu/drm/drm_edid.c3
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c76
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h12
-rw-r--r--drivers/gpu/drm/i915/intel_display.c12
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c13
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c15
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/init.c19
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c6
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c8
-rw-r--r--drivers/gpu/drm/radeon/atombios_i2c.c4
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c27
-rw-r--r--drivers/gpu/drm/radeon/evergreend.h8
-rw-r--r--drivers/gpu/drm/radeon/r100.c8
-rw-r--r--drivers/gpu/drm/radeon/r600_hdmi.c16
-rw-r--r--drivers/gpu/drm/radeon/r600d.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c13
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c34
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c15
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon_test.c4
-rw-r--r--drivers/gpu/drm/radeon/rs400.c9
-rw-r--r--drivers/gpu/drm/radeon/si.c129
-rw-r--r--drivers/gpu/drm/radeon/sid.h8
-rw-r--r--drivers/gpu/drm/ttm/ttm_tt.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c17
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c2
-rw-r--r--drivers/gpu/ion/Makefile5
-rw-r--r--drivers/gpu/ion/compat_ion.c172
-rw-r--r--drivers/gpu/ion/compat_ion.h30
-rw-r--r--drivers/gpu/ion/ion.c260
-rw-r--r--drivers/gpu/ion/ion_carveout_heap.c7
-rw-r--r--drivers/gpu/ion/ion_chunk_heap.c15
-rw-r--r--drivers/gpu/ion/ion_cma_heap.c205
-rw-r--r--drivers/gpu/ion/ion_heap.c27
-rw-r--r--drivers/gpu/ion/ion_priv.h26
-rw-r--r--drivers/gpu/ion/ion_system_heap.c25
-rw-r--r--drivers/hid/hid-core.c111
-rw-r--r--drivers/hid/hid-ids.h8
-rw-r--r--drivers/hid/hid-input.c23
-rw-r--r--drivers/hid/hid-lenovo-tpkbd.c25
-rw-r--r--drivers/hid/hid-lg2ff.c19
-rw-r--r--drivers/hid/hid-lg3ff.c29
-rw-r--r--drivers/hid/hid-lg4ff.c20
-rw-r--r--drivers/hid/hid-lgff.c17
-rw-r--r--drivers/hid/hid-logitech-dj.c22
-rw-r--r--drivers/hid/hid-multitouch.c26
-rw-r--r--drivers/hid/hid-ntrig.c3
-rw-r--r--drivers/hid/hid-picolcd_cir.c3
-rw-r--r--drivers/hid/hid-picolcd_core.c2
-rw-r--r--drivers/hid/hid-picolcd_debugfs.c23
-rw-r--r--drivers/hid/hid-picolcd_fb.c6
-rw-r--r--drivers/hid/hid-pl.c10
-rw-r--r--drivers/hid/hid-roccat-konepure.c3
-rw-r--r--drivers/hid/hid-sensor-hub.c3
-rw-r--r--drivers/hid/hid-speedlink.c11
-rw-r--r--drivers/hid/hid-steelseries.c5
-rw-r--r--drivers/hid/hid-zpff.c18
-rw-r--r--drivers/hid/hidraw.c60
-rw-r--r--drivers/hid/uhid.c4
-rw-r--r--drivers/hid/usbhid/hid-core.c5
-rw-r--r--drivers/hid/usbhid/hid-quirks.c2
-rw-r--r--drivers/hwmon/applesmc.c24
-rw-r--r--drivers/i2c/busses/i2c-ismt.c3
-rw-r--r--drivers/i2c/busses/i2c-omap.c3
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.c14
-rw-r--r--drivers/input/mouse/bcm5974.c6
-rw-r--r--drivers/iommu/intel-iommu.c72
-rw-r--r--drivers/irqchip/irq-gic.c5
-rw-r--r--drivers/leds/leds-wm831x-status.c4
-rw-r--r--drivers/md/bcache/bcache.h7
-rw-r--r--drivers/md/bcache/bset.c39
-rw-r--r--drivers/md/bcache/btree.c2
-rw-r--r--drivers/md/bcache/journal.c33
-rw-r--r--drivers/md/bcache/request.c20
-rw-r--r--drivers/md/bcache/sysfs.c8
-rw-r--r--drivers/md/bcache/util.c11
-rw-r--r--drivers/md/bcache/util.h12
-rw-r--r--drivers/md/bcache/writeback.c42
-rw-r--r--drivers/md/dm-mpath.c11
-rw-r--r--drivers/md/dm-raid.c2
-rw-r--r--drivers/md/dm-snap-persistent.c20
-rw-r--r--drivers/md/dm-snap.c5
-rw-r--r--drivers/md/dm.c11
-rw-r--r--drivers/md/md.c5
-rw-r--r--drivers/md/raid1.c1
-rw-r--r--drivers/md/raid10.c1
-rw-r--r--drivers/md/raid5.c20
-rw-r--r--drivers/media/common/siano/smsdvb-main.c3
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.c16
-rw-r--r--drivers/media/pci/Kconfig4
-rw-r--r--drivers/media/platform/coda.c2
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.c9
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.h1
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-m2m.c1
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c2
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.c1
-rw-r--r--drivers/media/platform/sh_vou.c2
-rw-r--r--drivers/media/usb/Kconfig4
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-core.c11
-rw-r--r--drivers/misc/atmel_pwm.c6
-rw-r--r--drivers/misc/mei/amthif.c1
-rw-r--r--drivers/misc/mei/bus.c5
-rw-r--r--drivers/misc/mei/client.h6
-rw-r--r--drivers/misc/mei/hbm.c10
-rw-r--r--drivers/misc/mei/init.c3
-rw-r--r--drivers/misc/mei/main.c11
-rw-r--r--drivers/misc/mei/mei_dev.h6
-rw-r--r--drivers/mmc/host/tmio_mmc_dma.c4
-rw-r--r--drivers/mtd/nand/nand_base.c8
-rw-r--r--drivers/mtd/ubi/wl.c3
-rw-r--r--drivers/net/bonding/bond_main.c13
-rw-r--r--drivers/net/can/at91_can.c4
-rw-r--r--drivers/net/can/dev.c10
-rw-r--r--drivers/net/can/flexcan.c14
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c2
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/sge.c3
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c3
-rw-r--r--drivers/net/ethernet/marvell/mv643xx_eth.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cmd.c2
-rw-r--r--drivers/net/ethernet/realtek/r8169.c1
-rw-r--r--drivers/net/ethernet/ti/davinci_emac.c3
-rw-r--r--drivers/net/ethernet/via/via-rhine.c9
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_main.c6
-rw-r--r--drivers/net/ppp/pptp.c2
-rw-r--r--drivers/net/tun.c11
-rw-r--r--drivers/net/usb/cdc_ether.c5
-rw-r--r--drivers/net/usb/dm9601.c2
-rw-r--r--drivers/net/usb/qmi_wwan.c130
-rw-r--r--drivers/net/virtio_net.c16
-rw-r--r--drivers/net/wan/farsync.c1
-rw-r--r--drivers/net/wan/wanxl.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h5
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c23
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c17
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c1
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c28
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h3
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c14
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/usb.c2
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/dma.c15
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c6
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-7000.c42
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-config.h7
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/drv.c89
-rw-r--r--drivers/net/wireless/mwifiex/11n_aggr.c3
-rw-r--r--drivers/net/wireless/mwifiex/11n_aggr.h2
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c5
-rw-r--r--drivers/net/wireless/mwifiex/main.c11
-rw-r--r--drivers/net/wireless/mwifiex/sta_ioctl.c18
-rw-r--r--drivers/net/wireless/mwifiex/usb.c7
-rw-r--r--drivers/net/wireless/mwifiex/wmm.c3
-rw-r--r--drivers/net/wireless/p54/p54usb.c1
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c22
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/trx.c3
-rw-r--r--drivers/net/wireless/rtlwifi/wifi.h2
-rw-r--r--drivers/net/xen-netback/common.h2
-rw-r--r--drivers/net/xen-netback/interface.c15
-rw-r--r--drivers/net/xen-netback/netback.c104
-rw-r--r--drivers/net/xen-netback/xenbus.c156
-rw-r--r--drivers/ntb/ntb_hw.c41
-rw-r--r--drivers/ntb/ntb_hw.h16
-rw-r--r--drivers/ntb/ntb_regs.h4
-rw-r--r--drivers/ntb/ntb_transport.c17
-rw-r--r--drivers/of/base.c1
-rw-r--r--drivers/pci/pci-acpi.c6
-rw-r--r--drivers/pinctrl/pinctrl-at91.c4
-rw-r--r--drivers/power/Kconfig10
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/android_battery.c692
-rw-r--r--drivers/scsi/aacraid/linit.c2
-rw-r--r--drivers/scsi/esp_scsi.c14
-rw-r--r--drivers/scsi/esp_scsi.h1
-rw-r--r--drivers/scsi/mpt3sas/Makefile2
-rw-r--r--drivers/scsi/sd.c13
-rw-r--r--drivers/staging/android/sw_sync.h18
-rw-r--r--drivers/staging/android/sync.c14
-rw-r--r--drivers/staging/bcm/Bcmchar.c1
-rw-r--r--drivers/staging/comedi/drivers/dt282x.c3
-rw-r--r--drivers/staging/comedi/drivers/ni_65xx.c25
-rw-r--r--drivers/staging/iio/adc/mxs-lradc.c18
-rw-r--r--drivers/staging/ozwpan/ozcdev.c3
-rw-r--r--drivers/staging/sb105x/sb_pci_mp.c2
-rw-r--r--drivers/staging/vt6656/iwctl.c3
-rw-r--r--drivers/staging/vt6656/main_usb.c3
-rw-r--r--drivers/staging/wlags49_h2/wl_priv.c9
-rw-r--r--drivers/switch/switch_class.c4
-rw-r--r--drivers/target/iscsi/iscsi_target_util.c4
-rw-r--r--drivers/target/target_core_alua.c2
-rw-r--r--drivers/target/target_core_pr.c2
-rw-r--r--drivers/target/target_core_pscsi.c8
-rw-r--r--drivers/tty/hvc/hvc_xen.c1
-rw-r--r--drivers/tty/serial/pch_uart.c5
-rw-r--r--drivers/tty/serial/serial-tegra.c4
-rw-r--r--drivers/tty/serial/vt8500_serial.c5
-rw-r--r--drivers/tty/tty_io.c3
-rw-r--r--drivers/tty/tty_ioctl.c3
-rw-r--r--drivers/uio/uio.c41
-rw-r--r--drivers/usb/class/cdc-wdm.c13
-rw-r--r--drivers/usb/core/config.c3
-rw-r--r--drivers/usb/core/devio.c16
-rw-r--r--drivers/usb/core/hcd.c1
-rw-r--r--drivers/usb/core/hub.c83
-rw-r--r--drivers/usb/core/port.c13
-rw-r--r--drivers/usb/core/quirks.c6
-rw-r--r--drivers/usb/core/usb.h1
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c4
-rw-r--r--drivers/usb/dwc3/gadget.c39
-rw-r--r--drivers/usb/gadget/android.c4
-rw-r--r--drivers/usb/gadget/dummy_hcd.c7
-rw-r--r--drivers/usb/gadget/f_fs.c60
-rw-r--r--drivers/usb/gadget/f_mtp.c9
-rw-r--r--drivers/usb/gadget/uvc_queue.c6
-rw-r--r--drivers/usb/host/ehci-fsl.c12
-rw-r--r--drivers/usb/host/ehci-mxc.c2
-rw-r--r--drivers/usb/host/ehci-pci.c2
-rw-r--r--drivers/usb/host/ohci-hcd.c22
-rw-r--r--drivers/usb/host/ohci-pci.c2
-rw-r--r--drivers/usb/host/ohci-q.c24
-rw-r--r--drivers/usb/host/uhci-pci.c2
-rw-r--r--drivers/usb/host/uhci-q.c12
-rw-r--r--drivers/usb/host/xhci-hub.c2
-rw-r--r--drivers/usb/host/xhci-pci.c2
-rw-r--r--drivers/usb/host/xhci-plat.c2
-rw-r--r--drivers/usb/host/xhci-ring.c24
-rw-r--r--drivers/usb/host/xhci.c54
-rw-r--r--drivers/usb/host/xhci.h2
-rw-r--r--drivers/usb/serial/ftdi_sio.c1
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h6
-rw-r--r--drivers/usb/serial/mos7720.c6
-rw-r--r--drivers/usb/serial/option.c245
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c1
-rw-r--r--drivers/usb/storage/scsiglue.c5
-rw-r--r--drivers/usb/storage/unusual_devs.h7
-rw-r--r--drivers/vhost/scsi.c2
-rw-r--r--drivers/video/Kconfig1
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/adf/Kconfig14
-rw-r--r--drivers/video/adf/Makefile15
-rw-r--r--drivers/video/adf/adf.c1168
-rw-r--r--drivers/video/adf/adf.h71
-rw-r--r--drivers/video/adf/adf_client.c807
-rw-r--r--drivers/video/adf/adf_fbdev.c651
-rw-r--r--drivers/video/adf/adf_fops.c957
-rw-r--r--drivers/video/adf/adf_fops.h37
-rw-r--r--drivers/video/adf/adf_fops32.c217
-rw-r--r--drivers/video/adf/adf_fops32.h78
-rw-r--r--drivers/video/adf/adf_format.c280
-rw-r--r--drivers/video/adf/adf_memblock.c149
-rw-r--r--drivers/video/adf/adf_sysfs.c302
-rw-r--r--drivers/video/adf/adf_sysfs.h33
-rw-r--r--drivers/video/adf/adf_trace.h93
-rw-r--r--drivers/video/au1100fb.c28
-rw-r--r--drivers/video/au1200fb.c27
-rw-r--r--drivers/video/backlight/atmel-pwm-bl.c9
-rw-r--r--drivers/video/hyperv_fb.c40
-rw-r--r--drivers/watchdog/ts72xx_wdt.c3
-rw-r--r--drivers/xen/grant-table.c13
-rw-r--r--fs/binfmt_elf.c30
-rw-r--r--fs/bio-integrity.c2
-rw-r--r--fs/bio.c4
-rw-r--r--fs/btrfs/extent-tree.c3
-rw-r--r--fs/btrfs/inode.c2
-rw-r--r--fs/btrfs/ioctl.c3
-rw-r--r--fs/btrfs/relocation.c14
-rw-r--r--fs/btrfs/send.c3
-rw-r--r--fs/ceph/ioctl.c4
-rw-r--r--fs/cifs/connect.c2
-rw-r--r--fs/cifs/dir.c1
-rw-r--r--fs/cifs/smb2misc.c148
-rw-r--r--fs/ecryptfs/keystore.c3
-rw-r--r--fs/ext4/inode.c109
-rw-r--r--fs/ext4/xattr.c2
-rw-r--r--fs/fuse/dir.c15
-rw-r--r--fs/fuse/file.c34
-rw-r--r--fs/fuse/fuse_i.h2
-rw-r--r--fs/fuse/inode.c3
-rw-r--r--fs/isofs/inode.c16
-rw-r--r--fs/jfs/jfs_inode.c3
-rw-r--r--fs/nfs/nfs4filelayoutdev.c18
-rw-r--r--fs/nilfs2/page.c2
-rw-r--r--fs/nilfs2/segment.c11
-rw-r--r--fs/ocfs2/extent_map.c1
-rw-r--r--fs/proc/root.c3
-rw-r--r--fs/proc/task_mmu.c62
-rw-r--r--fs/seq_file.c2
-rw-r--r--fs/statfs.c2
-rw-r--r--fs/sysv/super.c1
-rw-r--r--fs/udf/super.c342
-rw-r--r--fs/xfs/xfs_da_btree.c5
-rw-r--r--include/asm-generic/hugetlb.h4
-rw-r--r--include/linux/balloon_compaction.h25
-rw-r--r--include/linux/compat.h7
-rw-r--r--include/linux/compiler-gcc4.h15
-rw-r--r--include/linux/device-mapper.h3
-rw-r--r--include/linux/hid.h9
-rw-r--r--include/linux/ion.h15
-rw-r--r--include/linux/ipc_namespace.h2
-rw-r--r--include/linux/miscdevice.h1
-rw-r--r--include/linux/mm.h2
-rw-r--r--include/linux/mm_types.h15
-rw-r--r--include/linux/pci_ids.h2
-rw-r--r--include/linux/platform_data/android_battery.h47
-rw-r--r--include/linux/random.h1
-rw-r--r--include/linux/rculist.h5
-rw-r--r--include/linux/sem.h6
-rw-r--r--include/linux/signal.h8
-rw-r--r--include/linux/skbuff.h5
-rw-r--r--include/linux/timex.h1
-rw-r--r--include/linux/usb/hcd.h2
-rw-r--r--include/linux/usb_usual.h4
-rw-r--r--include/media/v4l2-ctrls.h1
-rw-r--r--include/net/addrconf.h4
-rw-r--r--include/net/bluetooth/hci.h1
-rw-r--r--include/net/cipso_ipv4.h6
-rw-r--r--include/net/dst.h12
-rw-r--r--include/net/ip.h12
-rw-r--r--include/net/ip6_fib.h1
-rw-r--r--include/net/ip6_route.h6
-rw-r--r--include/net/ip_tunnels.h2
-rw-r--r--include/net/secure_seq.h1
-rw-r--r--include/net/sock.h2
-rw-r--r--include/net/tcp.h1
-rw-r--r--include/trace/events/arm-ipi.h100
-rw-r--r--include/trace/events/sched.h121
-rw-r--r--include/trace/events/smp.h90
-rw-r--r--include/uapi/drm/drm_mode.h2
-rw-r--r--include/uapi/drm/radeon_drm.h2
-rw-r--r--include/uapi/linux/perf_event.h12
-rw-r--r--include/uapi/linux/prctl.h3
-rw-r--r--include/uapi/video/adf.h311
-rw-r--r--include/video/adf.h484
-rw-r--r--include/video/adf_client.h61
-rw-r--r--include/video/adf_fbdev.h59
-rw-r--r--include/video/adf_format.h26
-rw-r--r--include/video/adf_memblock.h20
-rw-r--r--init/main.c2
-rw-r--r--ipc/msg.c281
-rw-r--r--ipc/namespace.c7
-rw-r--r--ipc/sem.c660
-rw-r--r--ipc/shm.c288
-rw-r--r--ipc/util.c140
-rw-r--r--ipc/util.h34
-rw-r--r--kernel/audit.c5
-rw-r--r--kernel/cgroup.c6
-rw-r--r--kernel/events/ring_buffer.c31
-rw-r--r--kernel/events/uprobes.c4
-rw-r--r--kernel/fork.c7
-rw-r--r--kernel/pid.c1
-rw-r--r--kernel/sched/cputime.c19
-rw-r--r--kernel/sched/fair.c531
-rw-r--r--kernel/smp.c12
-rw-r--r--kernel/softirq.c15
-rw-r--r--kernel/sys.c145
-rw-r--r--kernel/sysctl.c9
-rw-r--r--kernel/time/clockevents.c65
-rw-r--r--kernel/time/ntp.c6
-rw-r--r--kernel/time/timekeeping.c2
-rw-r--r--kernel/trace/trace.c5
-rw-r--r--lib/scatterlist.c3
-rw-r--r--mm/bounce.c2
-rw-r--r--mm/huge_memory.c82
-rw-r--r--mm/madvise.c3
-rw-r--r--mm/memcontrol.c8
-rw-r--r--mm/memory.c54
-rw-r--r--mm/mempolicy.c2
-rw-r--r--mm/migrate.c21
-rw-r--r--mm/mlock.c3
-rw-r--r--mm/mmap.c44
-rw-r--r--mm/mprotect.c5
-rw-r--r--mm/page-writeback.c10
-rw-r--r--mm/page_alloc.c32
-rw-r--r--mm/page_io.c50
-rw-r--r--mm/swap.c77
-rw-r--r--mm/vmalloc.c6
-rw-r--r--mm/vmscan.c4
-rw-r--r--net/8021q/vlan_netlink.c2
-rw-r--r--net/batman-adv/main.c5
-rw-r--r--net/batman-adv/network-coding.c28
-rw-r--r--net/batman-adv/network-coding.h14
-rw-r--r--net/bluetooth/hci_core.c26
-rwxr-xr-xnet/bluetooth/hci_event.c6
-rw-r--r--net/bluetooth/hidp/core.c15
-rw-r--r--net/bridge/br_netlink.c4
-rw-r--r--net/bridge/br_private.h8
-rw-r--r--net/bridge/br_stp.c23
-rw-r--r--net/bridge/br_stp_if.c12
-rw-r--r--net/caif/cfctrl.c3
-rw-r--r--net/ceph/osd_client.c2
-rw-r--r--net/ceph/osdmap.c2
-rw-r--r--net/compat.c2
-rw-r--r--net/core/flow_dissector.c8
-rw-r--r--net/core/netpoll.c11
-rw-r--r--net/core/scm.c2
-rw-r--r--net/core/secure_seq.c29
-rw-r--r--net/core/sock.c1
-rw-r--r--net/dccp/ipv6.c1
-rw-r--r--net/ipv4/af_inet.c4
-rw-r--r--net/ipv4/igmp.c8
-rw-r--r--net/ipv4/inet_hashtables.c2
-rw-r--r--net/ipv4/inetpeer.c4
-rw-r--r--net/ipv4/ip_gre.c2
-rw-r--r--net/ipv4/ip_output.c10
-rw-r--r--net/ipv4/ip_tunnel.c16
-rw-r--r--net/ipv4/ip_vti.c14
-rw-r--r--net/ipv4/ipip.c2
-rw-r--r--net/ipv4/ipmr.c2
-rw-r--r--net/ipv4/raw.c2
-rw-r--r--net/ipv4/route.c2
-rw-r--r--net/ipv4/sysctl_net_ipv4.c10
-rw-r--r--net/ipv4/tcp.c31
-rw-r--r--net/ipv4/tcp_input.c41
-rw-r--r--net/ipv4/tcp_output.c28
-rw-r--r--net/ipv4/xfrm4_mode_tunnel.c2
-rw-r--r--net/ipv6/addrconf.c27
-rw-r--r--net/ipv6/exthdrs.c6
-rw-r--r--net/ipv6/inet6_hashtables.c2
-rw-r--r--net/ipv6/ip6_fib.c2
-rw-r--r--net/ipv6/ip6_gre.c4
-rw-r--r--net/ipv6/ip6_output.c57
-rw-r--r--net/ipv6/ip6_tunnel.c5
-rw-r--r--net/ipv6/mcast.c4
-rw-r--r--net/ipv6/route.c55
-rw-r--r--net/ipv6/sit.c87
-rw-r--r--net/l2tp/l2tp_core.c36
-rw-r--r--net/l2tp/l2tp_core.h3
-rw-r--r--net/l2tp/l2tp_ppp.c4
-rw-r--r--net/mac80211/cfg.c2
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/rx.c3
-rw-r--r--net/mac80211/scan.c19
-rw-r--r--net/mac80211/status.c3
-rw-r--r--net/mac80211/tx.c3
-rw-r--r--net/mac80211/util.c4
-rw-r--r--net/netfilter/ipset/ip_set_hash_gen.h28
-rw-r--r--net/netfilter/ipvs/ip_vs_xmit.c2
-rw-r--r--net/netfilter/nf_conntrack_h323_main.c4
-rw-r--r--net/sched/sch_htb.c2
-rw-r--r--net/sctp/input.c3
-rw-r--r--net/sctp/ipv6.c44
-rw-r--r--net/sctp/output.c3
-rw-r--r--net/sctp/socket.c5
-rw-r--r--net/socket.c24
-rw-r--r--net/sunrpc/auth_gss/gss_rpc_upcall.c26
-rw-r--r--net/sunrpc/auth_gss/gss_rpc_xdr.c41
-rw-r--r--net/sunrpc/auth_gss/gss_rpc_xdr.h5
-rw-r--r--net/sysctl_net.c4
-rw-r--r--net/unix/af_unix.c10
-rw-r--r--net/unix/diag.c1
-rw-r--r--net/wireless/ibss.c3
-rw-r--r--net/wireless/radiotap.c7
-rw-r--r--scripts/kallsyms.c12
-rw-r--r--scripts/link-vmlinux.sh2
-rw-r--r--sound/core/compress_offload.c3
-rw-r--r--sound/core/pcm.c4
-rw-r--r--sound/pci/hda/hda_codec.c4
-rw-r--r--sound/pci/hda/hda_generic.c6
-rw-r--r--sound/pci/hda/hda_intel.c1
-rw-r--r--sound/pci/hda/patch_conexant.c11
-rw-r--r--sound/pci/hda/patch_hdmi.c75
-rw-r--r--sound/pci/hda/patch_realtek.c38
-rw-r--r--sound/soc/codecs/88pm860x-codec.c3
-rw-r--r--sound/soc/codecs/ab8500-codec.c7
-rw-r--r--sound/soc/codecs/max98095.c4
-rw-r--r--sound/soc/codecs/mc13783.c4
-rw-r--r--sound/soc/codecs/wm8960.c6
-rw-r--r--sound/soc/codecs/wm_hubs.c1
-rw-r--r--sound/soc/soc-dapm.c2
-rw-r--r--sound/usb/usx2y/us122l.c4
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c22
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c7
-rw-r--r--tools/gator/daemon/Android.mk52
-rw-r--r--tools/gator/daemon/Buffer.cpp217
-rw-r--r--tools/gator/daemon/Buffer.h61
-rw-r--r--tools/gator/daemon/CapturedXML.cpp134
-rw-r--r--tools/gator/daemon/CapturedXML.h26
-rw-r--r--tools/gator/daemon/Child.cpp408
-rw-r--r--tools/gator/daemon/Child.h31
-rw-r--r--tools/gator/daemon/Collector.cpp221
-rw-r--r--tools/gator/daemon/Collector.h38
-rw-r--r--tools/gator/daemon/ConfigurationXML.cpp209
-rw-r--r--tools/gator/daemon/ConfigurationXML.h34
-rw-r--r--tools/gator/daemon/Counter.h61
-rw-r--r--tools/gator/daemon/Driver.cpp15
-rw-r--r--tools/gator/daemon/Driver.h44
-rw-r--r--tools/gator/daemon/EventsXML.cpp70
-rw-r--r--tools/gator/daemon/EventsXML.h18
-rw-r--r--tools/gator/daemon/Fifo.cpp130
-rw-r--r--tools/gator/daemon/Fifo.h44
-rw-r--r--tools/gator/daemon/Hwmon.cpp326
-rw-r--r--tools/gator/daemon/Hwmon.h39
-rw-r--r--tools/gator/daemon/KMod.cpp102
-rw-r--r--tools/gator/daemon/KMod.h27
-rw-r--r--tools/gator/daemon/LocalCapture.cpp129
-rw-r--r--tools/gator/daemon/LocalCapture.h26
-rw-r--r--tools/gator/daemon/Logging.cpp78
-rw-r--r--tools/gator/daemon/Logging.h47
-rw-r--r--tools/gator/daemon/Makefile25
-rw-r--r--tools/gator/daemon/Makefile_aarch6415
-rw-r--r--tools/gator/daemon/OlySocket.cpp261
-rw-r--r--tools/gator/daemon/OlySocket.h36
-rw-r--r--tools/gator/daemon/OlyUtility.cpp227
-rw-r--r--tools/gator/daemon/OlyUtility.h42
-rw-r--r--tools/gator/daemon/Sender.cpp135
-rw-r--r--tools/gator/daemon/Sender.h38
-rw-r--r--tools/gator/daemon/SessionData.cpp148
-rw-r--r--tools/gator/daemon/SessionData.h71
-rw-r--r--tools/gator/daemon/SessionXML.cpp109
-rw-r--r--tools/gator/daemon/SessionXML.h38
-rw-r--r--tools/gator/daemon/StreamlineSetup.cpp272
-rw-r--r--tools/gator/daemon/StreamlineSetup.h43
-rw-r--r--tools/gator/daemon/common.mk50
-rw-r--r--tools/gator/daemon/configuration.xml57
-rw-r--r--tools/gator/daemon/escape.c75
-rw-r--r--tools/gator/daemon/events-ARM11.xml39
-rw-r--r--tools/gator/daemon/events-ARM11MPCore.xml26
-rw-r--r--tools/gator/daemon/events-CCI-400.xml47
-rw-r--r--tools/gator/daemon/events-CCN-504.xml122
-rw-r--r--tools/gator/daemon/events-Cortex-A12.xml86
-rw-r--r--tools/gator/daemon/events-Cortex-A15.xml68
-rw-r--r--tools/gator/daemon/events-Cortex-A5.xml36
-rw-r--r--tools/gator/daemon/events-Cortex-A53.xml171
-rw-r--r--tools/gator/daemon/events-Cortex-A57.xml171
-rw-r--r--tools/gator/daemon/events-Cortex-A7.xml43
-rw-r--r--tools/gator/daemon/events-Cortex-A8.xml52
-rw-r--r--tools/gator/daemon/events-Cortex-A9.xml65
-rw-r--r--tools/gator/daemon/events-Krait-architected.xml22
-rw-r--r--tools/gator/daemon/events-L2C-310.xml18
-rw-r--r--tools/gator/daemon/events-Linux.xml17
-rw-r--r--tools/gator/daemon/events-Mali-4xx.xml251
-rw-r--r--tools/gator/daemon/events-Mali-T6xx.xml38
-rw-r--r--tools/gator/daemon/events-Mali-T6xx_hw.xml113
-rw-r--r--tools/gator/daemon/events-Scorpion.xml107
-rw-r--r--tools/gator/daemon/events-ScorpionMP.xml90
-rw-r--r--tools/gator/daemon/events_footer.xml1
-rw-r--r--tools/gator/daemon/events_header.xml2
-rw-r--r--tools/gator/daemon/libsensors/COPYING.LGPL502
-rw-r--r--tools/gator/daemon/libsensors/access.c561
-rw-r--r--tools/gator/daemon/libsensors/access.h33
-rw-r--r--tools/gator/daemon/libsensors/conf-lex.c2881
-rw-r--r--tools/gator/daemon/libsensors/conf-lex.l372
-rw-r--r--tools/gator/daemon/libsensors/conf-parse.c2042
-rw-r--r--tools/gator/daemon/libsensors/conf-parse.h84
-rw-r--r--tools/gator/daemon/libsensors/conf-parse.y347
-rw-r--r--tools/gator/daemon/libsensors/conf.h34
-rw-r--r--tools/gator/daemon/libsensors/data.c278
-rw-r--r--tools/gator/daemon/libsensors/data.h184
-rw-r--r--tools/gator/daemon/libsensors/error.c92
-rw-r--r--tools/gator/daemon/libsensors/error.h74
-rw-r--r--tools/gator/daemon/libsensors/general.c85
-rw-r--r--tools/gator/daemon/libsensors/general.h39
-rw-r--r--tools/gator/daemon/libsensors/init.c341
-rw-r--r--tools/gator/daemon/libsensors/init.h28
-rw-r--r--tools/gator/daemon/libsensors/scanner.h32
-rw-r--r--tools/gator/daemon/libsensors/sensors.h311
-rw-r--r--tools/gator/daemon/libsensors/sysfs.c926
-rw-r--r--tools/gator/daemon/libsensors/sysfs.h43
-rw-r--r--tools/gator/daemon/libsensors/version.h1
-rw-r--r--tools/gator/daemon/main.cpp480
-rw-r--r--tools/gator/daemon/mxml/COPYING507
-rw-r--r--tools/gator/daemon/mxml/config.h96
-rw-r--r--tools/gator/daemon/mxml/mxml-attr.c319
-rw-r--r--tools/gator/daemon/mxml/mxml-entity.c460
-rw-r--r--tools/gator/daemon/mxml/mxml-file.c3082
-rw-r--r--tools/gator/daemon/mxml/mxml-get.c471
-rw-r--r--tools/gator/daemon/mxml/mxml-index.c662
-rw-r--r--tools/gator/daemon/mxml/mxml-node.c807
-rw-r--r--tools/gator/daemon/mxml/mxml-private.c331
-rw-r--r--tools/gator/daemon/mxml/mxml-private.h50
-rw-r--r--tools/gator/daemon/mxml/mxml-search.c287
-rw-r--r--tools/gator/daemon/mxml/mxml-set.c349
-rw-r--r--tools/gator/daemon/mxml/mxml-string.c476
-rw-r--r--tools/gator/daemon/mxml/mxml.h329
-rw-r--r--tools/lib/lk/debugfs.c1
-rw-r--r--tools/perf/util/map.c1
742 files changed, 48522 insertions, 3879 deletions
diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl
index 6a8b7158697f..9c92bb879b6d 100644
--- a/Documentation/DocBook/media_api.tmpl
+++ b/Documentation/DocBook/media_api.tmpl
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
- "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY % media-entities SYSTEM "./media-entities.tmpl"> %media-entities;
<!ENTITY media-indices SYSTEM "./media-indices.tmpl">
diff --git a/Documentation/arm/small_task_packing.txt b/Documentation/arm/small_task_packing.txt
new file mode 100644
index 000000000000..43f0a8b80234
--- /dev/null
+++ b/Documentation/arm/small_task_packing.txt
@@ -0,0 +1,136 @@
+Small Task Packing in the big.LITTLE MP Reference Patch Set
+
+What is small task packing?
+----
+Simply that the scheduler will fit as many small tasks on a single CPU
+as possible before using other CPUs. A small task is defined as one
+whose tracked load is less than 90% of a NICE_0 task. This is a change
+from the usual behavior since the scheduler will normally use an idle
+CPU for a waking task unless that task is considered cache hot.
+
+
+How is it implemented?
+----
+Since all small tasks must wake up relatively frequently, the main
+requirement for packing small tasks is to select a partly-busy CPU when
+waking rather than looking for an idle CPU. We use the tracked load of
+the CPU runqueue to determine how heavily loaded each CPU is and the
+tracked load of the task to determine if it will fit on the CPU. We
+always start with the lowest-numbered CPU in a sched domain and stop
+looking when we find a CPU with enough space for the task.
+
+Some further tweaks are necessary to suppress load balancing when the
+CPU is not fully loaded, otherwise the scheduler attempts to spread
+tasks evenly across the domain.
+
+
+How does it interact with the HMP patches?
+----
+Firstly, we only enable packing on the little domain. The intent is that
+the big domain is intended to spread tasks amongst the available CPUs
+one-task-per-CPU. The little domain however is attempting to use as
+little power as possible while servicing its tasks.
+
+Secondly, since we offload big tasks onto little CPUs in order to try
+to devote one CPU to each task, we have a threshold above which we do
+not try to pack a task and instead will select an idle CPU if possible.
+This maintains maximum forward progress for busy tasks temporarily
+demoted from big CPUs.
+
+
+Can the behaviour be tuned?
+----
+Yes, the load level of a 'full' CPU can be easily modified in the source
+and is exposed through sysfs as /sys/kernel/hmp/packing_limit to be
+changed at runtime. The presence of the packing behaviour is controlled
+by CONFIG_SCHED_HMP_LITTLE_PACKING and can be disabled at run-time
+using /sys/kernel/hmp/packing_enable.
+The definition of a small task is hard coded as 90% of NICE_0_LOAD
+and cannot be modified at run time.
+
+
+Why do I need to tune it?
+----
+The optimal configuration is likely to be different depending upon the
+design and manufacturing of your SoC.
+
+In the main, there are two system effects from enabling small task
+packing.
+
+1. CPU operating point may increase
+2. wakeup latency of tasks may be increased
+
+There are also likely to be secondary effects from loading one CPU
+rather than spreading tasks.
+
+Note that all of these system effects are dependent upon the workload
+under consideration.
+
+
+CPU Operating Point
+----
+The primary impact of loading one CPU with a number of light tasks is to
+increase the compute requirement of that CPU since it is no longer idle
+as often. Increased compute requirement causes an increase in the
+frequency of the CPU through CPUfreq.
+
+Consider this example:
+We have a system with 3 CPUs which can operate at any frequency between
+350MHz and 1GHz. The system has 6 tasks which would each produce 10%
+load at 1GHz. The scheduler has frequency-invariant load scaling
+enabled. Our DVFS governor aims for 80% utilization at the chosen
+frequency.
+
+Without task packing, these tasks will be spread out amongst all CPUs
+such that each has 2. This will produce roughly 20% system load, and
+the frequency of the package will remain at 350MHz.
+
+With task packing set to the default packing_limit, all of these tasks
+will sit on one CPU and require a package frequency of ~750MHz to reach
+80% utilization. (0.75 = 0.6 * 0.8).
+
+When a package operates on a single frequency domain, all CPUs in that
+package share frequency and voltage.
+
+Depending upon the SoC implementation there can be a significant amount
+of energy lost to leakage from idle CPUs. The decision about how
+loaded a CPU must be to be considered 'full' is therefore controllable
+through sysfs (sys/kernel/hmp/packing_limit) and directly in the code.
+
+Continuing the example, lets set packing_limit to 450 which means we
+will pack tasks until the total load of all running tasks >= 450. In
+practise, this is very similar to a 55% idle 1Ghz CPU.
+
+Now we are only able to place 4 tasks on CPU0, and two will overflow
+onto CPU1. CPU0 will have a load of 40% and CPU1 will have a load of
+20%. In order to still hit 80% utilization, CPU0 now only needs to
+operate at (0.4*0.8=0.32) 320MHz, which means that the lowest operating
+point will be selected, the same as in the non-packing case, except that
+now CPU2 is no longer needed and can be power-gated.
+
+In order to use less energy, the saving from power-gating CPU2 must be
+more than the energy spent running CPU0 for the extra cycles. This
+depends upon the SoC implementation.
+
+This is obviously a contrived example requiring all the tasks to
+be runnable at the same time, but it illustrates the point.
+
+
+Wakeup Latency
+----
+This is an unavoidable consequence of trying to pack tasks together
+rather than giving them a CPU each. If you cannot find an acceptable
+level of wakeup latency, you should turn packing off.
+
+Cyclictest is a good test application for determining the added latency
+when configuring packing.
+
+
+Why is it turned off for the VersatileExpress V2P_CA15A7 CoreTile?
+----
+Simply, this core tile only has power gating for the whole A7 package.
+When small task packing is enabled, all our low-energy use cases
+normally fit onto one A7 CPU. We therefore end up with 2 mostly-idle
+CPUs and one mostly-busy CPU. This decreases the amount of time
+available where the whole package is idle and can be turned off.
+
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt
index fd8d0d594fc7..e0eb9d287312 100644
--- a/Documentation/filesystems/proc.txt
+++ b/Documentation/filesystems/proc.txt
@@ -369,6 +369,8 @@ is not associated with a file:
[stack:1001] = the stack of the thread with tid 1001
[vdso] = the "virtual dynamic shared object",
the kernel system call handler
+ [anon:<name>] = an anonymous mapping that has been
+ named by userspace
or if empty, the mapping is anonymous.
@@ -419,6 +421,7 @@ KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 374 kB
VmFlags: rd ex mr mw me de
+Name: name from userspace
the first of these lines shows the same information as is displayed for the
mapping in /proc/PID/maps. The remaining lines show the size of the mapping
@@ -469,6 +472,9 @@ Note that there is no guarantee that every flag and associated mnemonic will
be present in all further kernel releases. Things get changed, the flags may
be vanished or the reverse -- new added.
+The "Name" field will only be present on a mapping that has been named by
+userspace, and will show the name passed in by userspace.
+
This file is only present if the CONFIG_MMU kernel configuration option is
enabled.
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 3458d6343e01..3994f0bbeeb6 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -478,6 +478,15 @@ tcp_syn_retries - INTEGER
tcp_timestamps - BOOLEAN
Enable timestamps as defined in RFC1323.
+tcp_min_tso_segs - INTEGER
+ Minimal number of segments per TSO frame.
+ Since linux-3.12, TCP does an automatic sizing of TSO frames,
+ depending on flow rate, instead of filling 64Kbytes packets.
+ For specific usages, it's possible to force TCP to build big
+ TSO frames. Note that TCP stack might split too big TSO packets
+ if available window is too small.
+ Default: 2
+
tcp_tso_win_divisor - INTEGER
This allows control over what percentage of the congestion window
can be consumed by a single TSO frame.
diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt
index dcc75a9ed919..b81fca90f7fe 100644
--- a/Documentation/sysctl/vm.txt
+++ b/Documentation/sysctl/vm.txt
@@ -29,6 +29,7 @@ Currently, these files are in /proc/sys/vm:
- dirty_writeback_centisecs
- drop_caches
- extfrag_threshold
+- extra_free_kbytes
- hugepages_treat_as_movable
- hugetlb_shm_group
- laptop_mode
@@ -198,6 +199,21 @@ fragmentation index is <= extfrag_threshold. The default value is 500.
==============================================================
+extra_free_kbytes
+
+This parameter tells the VM to keep extra free memory between the threshold
+where background reclaim (kswapd) kicks in, and the threshold where direct
+reclaim (by allocating processes) kicks in.
+
+This is useful for workloads that require low latency memory allocations
+and have a bounded burstiness in memory allocations, for example a
+realtime application that receives and transmits network traffic
+(causing in-kernel memory allocations) with a maximum total message burst
+size of 200MB may need 200MB of extra free memory to avoid direct reclaim
+related latencies.
+
+==============================================================
+
hugepages_treat_as_movable
This parameter is only useful when kernelcore= is specified at boot time to
diff --git a/Makefile b/Makefile
index afe001e3b2d6..ba784b7b0f51 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 3
PATCHLEVEL = 10
-SUBLEVEL = 12
+SUBLEVEL = 20
EXTRAVERSION =
NAME = TOSSUG Baby Fish
diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg
index ca3cc8613366..546c37f552bc 100644
--- a/android/configs/android-recommended.cfg
+++ b/android/configs/android-recommended.cfg
@@ -11,7 +11,6 @@ CONFIG_COMPACTION=y
CONFIG_ANDROID_RAM_CONSOLE=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_BATTERY_ANDROID=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
diff --git a/arch/arc/include/asm/delay.h b/arch/arc/include/asm/delay.h
index 442ce5d0f709..43de30256981 100644
--- a/arch/arc/include/asm/delay.h
+++ b/arch/arc/include/asm/delay.h
@@ -53,11 +53,10 @@ static inline void __udelay(unsigned long usecs)
{
unsigned long loops;
- /* (long long) cast ensures 64 bit MPY - real or emulated
+ /* (u64) cast ensures 64 bit MPY - real or emulated
* HZ * 4295 is pre-evaluated by gcc - hence only 2 mpy ops
*/
- loops = ((long long)(usecs * 4295 * HZ) *
- (long long)(loops_per_jiffy)) >> 32;
+ loops = ((u64) usecs * 4295 * HZ * loops_per_jiffy) >> 32;
__delay(loops);
}
diff --git a/arch/arc/include/asm/sections.h b/arch/arc/include/asm/sections.h
index 6fc1159dfefe..764f1e3ba752 100644
--- a/arch/arc/include/asm/sections.h
+++ b/arch/arc/include/asm/sections.h
@@ -11,7 +11,6 @@
#include <asm-generic/sections.h>
-extern char _int_vec_base_lds[];
extern char __arc_dccm_base[];
extern char __dtb_start[];
diff --git a/arch/arc/include/asm/spinlock.h b/arch/arc/include/asm/spinlock.h
index f158197ac5b0..b6a8c2dfbe6e 100644
--- a/arch/arc/include/asm/spinlock.h
+++ b/arch/arc/include/asm/spinlock.h
@@ -45,7 +45,14 @@ static inline int arch_spin_trylock(arch_spinlock_t *lock)
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
- lock->slock = __ARCH_SPIN_LOCK_UNLOCKED__;
+ unsigned int tmp = __ARCH_SPIN_LOCK_UNLOCKED__;
+
+ __asm__ __volatile__(
+ " ex %0, [%1] \n"
+ : "+r" (tmp)
+ : "r"(&(lock->slock))
+ : "memory");
+
smp_mb();
}
diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h
index 32420824375b..30c9baffa96f 100644
--- a/arch/arc/include/asm/uaccess.h
+++ b/arch/arc/include/asm/uaccess.h
@@ -43,7 +43,7 @@
* Because it essentially checks if buffer end is within limit and @len is
* non-ngeative, which implies that buffer start will be within limit too.
*
- * The reason for rewriting being, for majorit yof cases, @len is generally
+ * The reason for rewriting being, for majority of cases, @len is generally
* compile time constant, causing first sub-expression to be compile time
* subsumed.
*
@@ -53,7 +53,7 @@
*
*/
#define __user_ok(addr, sz) (((sz) <= TASK_SIZE) && \
- (((addr)+(sz)) <= get_fs()))
+ ((addr) <= (get_fs() - (sz))))
#define __access_ok(addr, sz) (unlikely(__kernel_ok) || \
likely(__user_ok((addr), (sz))))
diff --git a/arch/arc/kernel/head.S b/arch/arc/kernel/head.S
index 006dec3fc353..0f944f024513 100644
--- a/arch/arc/kernel/head.S
+++ b/arch/arc/kernel/head.S
@@ -27,11 +27,16 @@ stext:
; Don't clobber r0-r4 yet. It might have bootloader provided info
;-------------------------------------------------------------------
+ sr @_int_vec_base_lds, [AUX_INTR_VEC_BASE]
+
#ifdef CONFIG_SMP
; Only Boot (Master) proceeds. Others wait in platform dependent way
; IDENTITY Reg [ 3 2 1 0 ]
; (cpu-id) ^^^ => Zero for UP ARC700
; => #Core-ID if SMP (Master 0)
+ ; Note that non-boot CPUs might not land here if halt-on-reset and
+ ; instead breath life from @first_lines_of_secondary, but we still
+ ; need to make sure only boot cpu takes this path.
GET_CPU_ID r5
cmp r5, 0
jnz arc_platform_smp_wait_to_boot
@@ -96,6 +101,8 @@ stext:
first_lines_of_secondary:
+ sr @_int_vec_base_lds, [AUX_INTR_VEC_BASE]
+
; setup per-cpu idle task as "current" on this CPU
ld r0, [@secondary_idle_tsk]
SET_CURR_TASK_ON_CPU r0, r1
diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c
index 8115fa531575..a199471ce01e 100644
--- a/arch/arc/kernel/irq.c
+++ b/arch/arc/kernel/irq.c
@@ -24,7 +24,6 @@
* -Needed for each CPU (hence not foldable into init_IRQ)
*
* what it does ?
- * -setup Vector Table Base Reg - in case Linux not linked at 0x8000_0000
* -Disable all IRQs (on CPU side)
* -Optionally, setup the High priority Interrupts as Level 2 IRQs
*/
@@ -32,8 +31,6 @@ void __cpuinit arc_init_IRQ(void)
{
int level_mask = 0;
- write_aux_reg(AUX_INTR_VEC_BASE, _int_vec_base_lds);
-
/* Disable all IRQs: enable them as devices request */
write_aux_reg(AUX_IENABLE, 0);
diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c
index c6a81c58d0f3..0851604bb9cd 100644
--- a/arch/arc/kernel/ptrace.c
+++ b/arch/arc/kernel/ptrace.c
@@ -92,7 +92,7 @@ static int genregs_set(struct task_struct *target,
REG_IN_CHUNK(scratch, callee, ptregs); /* pt_regs[bta..orig_r8] */
REG_IN_CHUNK(callee, efa, cregs); /* callee_regs[r25..r13] */
REG_IGNORE_ONE(efa); /* efa update invalid */
- REG_IN_ONE(stop_pc, &ptregs->ret); /* stop_pc: PC update */
+ REG_IGNORE_ONE(stop_pc); /* PC updated via @ret */
return ret;
}
diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c
index b2b3731dd1e9..2d7786b69a8a 100644
--- a/arch/arc/kernel/setup.c
+++ b/arch/arc/kernel/setup.c
@@ -47,10 +47,7 @@ void __cpuinit read_arc_build_cfg_regs(void)
READ_BCR(AUX_IDENTITY, cpu->core);
cpu->timers = read_aux_reg(ARC_REG_TIMERS_BCR);
-
cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE);
- if (cpu->vec_base == 0)
- cpu->vec_base = (unsigned int)_int_vec_base_lds;
READ_BCR(ARC_REG_D_UNCACH_BCR, uncached_space);
cpu->uncached_base = uncached_space.start << 24;
diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c
index ee6ef2f60a28..7e95e1a86510 100644
--- a/arch/arc/kernel/signal.c
+++ b/arch/arc/kernel/signal.c
@@ -101,7 +101,6 @@ SYSCALL_DEFINE0(rt_sigreturn)
{
struct rt_sigframe __user *sf;
unsigned int magic;
- int err;
struct pt_regs *regs = current_pt_regs();
/* Always make any pending restarted system calls return -EINTR */
@@ -119,15 +118,16 @@ SYSCALL_DEFINE0(rt_sigreturn)
if (!access_ok(VERIFY_READ, sf, sizeof(*sf)))
goto badframe;
- err = restore_usr_regs(regs, sf);
- err |= __get_user(magic, &sf->sigret_magic);
- if (err)
+ if (__get_user(magic, &sf->sigret_magic))
goto badframe;
if (unlikely(is_do_ss_needed(magic)))
if (restore_altstack(&sf->uc.uc_stack))
goto badframe;
+ if (restore_usr_regs(regs, sf))
+ goto badframe;
+
/* Don't restart from sigreturn */
syscall_wont_restart(regs);
@@ -191,6 +191,15 @@ setup_rt_frame(int signo, struct k_sigaction *ka, siginfo_t *info,
return 1;
/*
+ * w/o SA_SIGINFO, struct ucontext is partially populated (only
+ * uc_mcontext/uc_sigmask) for kernel's normal user state preservation
+ * during signal handler execution. This works for SA_SIGINFO as well
+ * although the semantics are now overloaded (the same reg state can be
+ * inspected by userland: but are they allowed to fiddle with it ?
+ */
+ err |= stash_usr_regs(sf, regs, set);
+
+ /*
* SA_SIGINFO requires 3 args to signal handler:
* #1: sig-no (common to any handler)
* #2: struct siginfo
@@ -213,14 +222,6 @@ setup_rt_frame(int signo, struct k_sigaction *ka, siginfo_t *info,
magic = MAGIC_SIGALTSTK;
}
- /*
- * w/o SA_SIGINFO, struct ucontext is partially populated (only
- * uc_mcontext/uc_sigmask) for kernel's normal user state preservation
- * during signal handler execution. This works for SA_SIGINFO as well
- * although the semantics are now overloaded (the same reg state can be
- * inspected by userland: but are they allowed to fiddle with it ?
- */
- err |= stash_usr_regs(sf, regs, set);
err |= __put_user(magic, &sf->sigret_magic);
if (err)
return err;
diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c
index 4cd81633febd..116d3e09b5b5 100644
--- a/arch/arc/kernel/unaligned.c
+++ b/arch/arc/kernel/unaligned.c
@@ -233,6 +233,12 @@ int misaligned_fixup(unsigned long address, struct pt_regs *regs,
regs->status32 &= ~STATUS_DE_MASK;
} else {
regs->ret += state.instr_len;
+
+ /* handle zero-overhead-loop */
+ if ((regs->ret == regs->lp_end) && (regs->lp_count)) {
+ regs->ret = regs->lp_start;
+ regs->lp_count--;
+ }
}
return 0;
diff --git a/arch/arc/mm/fault.c b/arch/arc/mm/fault.c
index 689ffd86d5e9..331a0846628e 100644
--- a/arch/arc/mm/fault.c
+++ b/arch/arc/mm/fault.c
@@ -16,7 +16,7 @@
#include <linux/kdebug.h>
#include <asm/pgalloc.h>
-static int handle_vmalloc_fault(struct mm_struct *mm, unsigned long address)
+static int handle_vmalloc_fault(unsigned long address)
{
/*
* Synchronize this task's top level page-table
@@ -26,7 +26,7 @@ static int handle_vmalloc_fault(struct mm_struct *mm, unsigned long address)
pud_t *pud, *pud_k;
pmd_t *pmd, *pmd_k;
- pgd = pgd_offset_fast(mm, address);
+ pgd = pgd_offset_fast(current->active_mm, address);
pgd_k = pgd_offset_k(address);
if (!pgd_present(*pgd_k))
@@ -72,7 +72,7 @@ void do_page_fault(struct pt_regs *regs, int write, unsigned long address,
* nothing more.
*/
if (address >= VMALLOC_START && address <= VMALLOC_END) {
- ret = handle_vmalloc_fault(mm, address);
+ ret = handle_vmalloc_fault(address);
if (unlikely(ret))
goto bad_area_nosemaphore;
else
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1aa9f46d22c4..ec94c57ef942 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1512,6 +1512,17 @@ config SCHED_HMP
There is currently no support for migration of task groups, hence
!SCHED_AUTOGROUP. Furthermore, normal load-balancing must be disabled
between cpus of different type (DISABLE_CPU_SCHED_DOMAIN_BALANCE).
+ When turned on, this option adds sys/kernel/hmp directory which
+ contains the following files:
+ up_threshold - the load average threshold used for up migration
+ (0 - 1023)
+ down_threshold - the load average threshold used for down migration
+ (0 - 1023)
+ hmp_domains - a list of cpumasks for the present HMP domains,
+ starting with the 'biggest' and ending with the
+ 'smallest'.
+ Note that both the threshold files can be written at runtime to
+ control scheduler behaviour.
config SCHED_HMP_PRIO_FILTER
bool "(EXPERIMENTAL) Filter HMP migrations by task priority"
@@ -1546,28 +1557,24 @@ config HMP_VARIABLE_SCALE
bool "Allows changing the load tracking scale through sysfs"
depends on SCHED_HMP
help
- When turned on, this option exports the thresholds and load average
- period value for the load tracking patches through sysfs.
+ When turned on, this option exports the load average period value
+ for the load tracking patches through sysfs.
The values can be modified to change the rate of load accumulation
- and the thresholds used for HMP migration.
- The load_avg_period_ms is the time in ms to reach a load average of
- 0.5 for an idle task of 0 load average ratio that start a busy loop.
- The up_threshold and down_threshold is the value to go to a faster
- CPU or to go back to a slower cpu.
- The {up,down}_threshold are devided by 1024 before being compared
- to the load average.
- For examples, with load_avg_period_ms = 128 and up_threshold = 512,
+ used for HMP migration. 'load_avg_period_ms' is the time in ms to
+ reach a load average of 0.5 for an idle task of 0 load average
+ ratio which becomes 100% busy.
+ For example, with load_avg_period_ms = 128 and up_threshold = 512,
a running task with a load of 0 will be migrated to a bigger CPU after
128ms, because after 128ms its load_avg_ratio is 0.5 and the real
up_threshold is 0.5.
This patch has the same behavior as changing the Y of the load
average computation to
(1002/1024)^(LOAD_AVG_PERIOD/load_avg_period_ms)
- but it remove intermadiate overflows in computation.
+ but removes intermediate overflows in computation.
config HMP_FREQUENCY_INVARIANT_SCALE
bool "(EXPERIMENTAL) Frequency-Invariant Tracked Load for HMP"
- depends on HMP_VARIABLE_SCALE && CPU_FREQ
+ depends on SCHED_HMP && CPU_FREQ
help
Scales the current load contribution in line with the frequency
of the CPU that the task was executed on.
@@ -1580,6 +1587,18 @@ config HMP_FREQUENCY_INVARIANT_SCALE
migration strategy to interact more predictably with CPUFreq's
asynchronous compute capacity changes.
+config SCHED_HMP_LITTLE_PACKING
+ bool "Small task packing for HMP"
+ depends on SCHED_HMP
+ default n
+ help
+ Allows the HMP Scheduler to pack small tasks into CPUs in the
+ smallest HMP domain.
+ Controlled by two sysfs files in sys/kernel/hmp.
+ packing_enable: 1 to enable, 0 to disable packing. Default 1.
+ packing_limit: runqueue load ratio where a RQ is considered
+ to be full. Default is NICE_0_LOAD * 9/8.
+
config HAVE_ARM_SCU
bool
help
diff --git a/arch/arm/boot/dts/at91rm9200.dtsi b/arch/arm/boot/dts/at91rm9200.dtsi
index 5d3ed5aafc69..0af879a4eafa 100644
--- a/arch/arm/boot/dts/at91rm9200.dtsi
+++ b/arch/arm/boot/dts/at91rm9200.dtsi
@@ -35,8 +35,12 @@
ssc2 = &ssc2;
};
cpus {
- cpu@0 {
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ cpu {
compatible = "arm,arm920t";
+ device_type = "cpu";
};
};
diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi
index 43a18f74cddb..0dbdb846f90a 100644
--- a/arch/arm/boot/dts/at91sam9260.dtsi
+++ b/arch/arm/boot/dts/at91sam9260.dtsi
@@ -32,8 +32,12 @@
ssc0 = &ssc0;
};
cpus {
- cpu@0 {
- compatible = "arm,arm926ejs";
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ cpu {
+ compatible = "arm,arm926ej-s";
+ device_type = "cpu";
};
};
diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi
index 94b58ab2cc08..fcd38f89904e 100644
--- a/arch/arm/boot/dts/at91sam9263.dtsi
+++ b/arch/arm/boot/dts/at91sam9263.dtsi
@@ -29,8 +29,12 @@
ssc1 = &ssc1;
};
cpus {
- cpu@0 {
- compatible = "arm,arm926ejs";
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ cpu {
+ compatible = "arm,arm926ej-s";
+ device_type = "cpu";
};
};
diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi
index bf18a735c37d..479a0622cdb8 100644
--- a/arch/arm/boot/dts/at91sam9g45.dtsi
+++ b/arch/arm/boot/dts/at91sam9g45.dtsi
@@ -35,8 +35,12 @@
ssc1 = &ssc1;
};
cpus {
- cpu@0 {
- compatible = "arm,arm926ejs";
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ cpu {
+ compatible = "arm,arm926ej-s";
+ device_type = "cpu";
};
};
diff --git a/arch/arm/boot/dts/at91sam9n12.dtsi b/arch/arm/boot/dts/at91sam9n12.dtsi
index 8d25f889928e..a92ec78349a2 100644
--- a/arch/arm/boot/dts/at91sam9n12.dtsi
+++ b/arch/arm/boot/dts/at91sam9n12.dtsi
@@ -31,8 +31,12 @@
ssc0 = &ssc0;
};
cpus {
- cpu@0 {
- compatible = "arm,arm926ejs";
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ cpu {
+ compatible = "arm,arm926ej-s";
+ device_type = "cpu";
};
};
diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi
index b5833d1f7cff..2b2b6923d16b 100644
--- a/arch/arm/boot/dts/at91sam9x5.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5.dtsi
@@ -33,8 +33,12 @@
ssc0 = &ssc0;
};
cpus {
- cpu@0 {
- compatible = "arm,arm926ejs";
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ cpu {
+ compatible = "arm,arm926ej-s";
+ device_type = "cpu";
};
};
diff --git a/arch/arm/boot/dts/integratorcp.dts b/arch/arm/boot/dts/integratorcp.dts
index ff1aea0ee043..72693a69f830 100644
--- a/arch/arm/boot/dts/integratorcp.dts
+++ b/arch/arm/boot/dts/integratorcp.dts
@@ -9,11 +9,6 @@
model = "ARM Integrator/CP";
compatible = "arm,integrator-cp";
- aliases {
- arm,timer-primary = &timer2;
- arm,timer-secondary = &timer1;
- };
-
chosen {
bootargs = "root=/dev/ram0 console=ttyAMA0,38400n8 earlyprintk";
};
@@ -24,14 +19,18 @@
};
timer0: timer@13000000 {
+ /* TIMER0 runs @ 25MHz */
compatible = "arm,integrator-cp-timer";
+ status = "disabled";
};
timer1: timer@13000100 {
+ /* TIMER1 runs @ 1MHz */
compatible = "arm,integrator-cp-timer";
};
timer2: timer@13000200 {
+ /* TIMER2 runs @ 1MHz */
compatible = "arm,integrator-cp-timer";
};
diff --git a/arch/arm/boot/dts/sama5d3.dtsi b/arch/arm/boot/dts/sama5d3.dtsi
index 5000e0d42849..642775d7ca67 100644
--- a/arch/arm/boot/dts/sama5d3.dtsi
+++ b/arch/arm/boot/dts/sama5d3.dtsi
@@ -35,8 +35,12 @@
ssc1 = &ssc1;
};
cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
cpu@0 {
+ device_type = "cpu";
compatible = "arm,cortex-a5";
+ reg = <0x0>;
};
};
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index e7ef619a70a2..06ef8b625dba 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -16,8 +16,12 @@
interrupt-parent = <&intc>;
cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
cpu@0 {
+ device_type = "cpu";
compatible = "arm,cortex-a8";
+ reg = <0x0>;
};
};
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index 31fa38f8cc98..d2852547b572 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -17,8 +17,12 @@
interrupt-parent = <&intc>;
cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
cpu@0 {
+ device_type = "cpu";
compatible = "arm,cortex-a8";
+ reg = <0x0>;
};
};
diff --git a/arch/arm/crypto/aes-armv4.S b/arch/arm/crypto/aes-armv4.S
index 19d6cd6f29f9..3a14ea8fe97e 100644
--- a/arch/arm/crypto/aes-armv4.S
+++ b/arch/arm/crypto/aes-armv4.S
@@ -148,7 +148,7 @@ AES_Te:
@ const AES_KEY *key) {
.align 5
ENTRY(AES_encrypt)
- sub r3,pc,#8 @ AES_encrypt
+ adr r3,AES_encrypt
stmdb sp!,{r1,r4-r12,lr}
mov r12,r0 @ inp
mov r11,r2
@@ -381,7 +381,7 @@ _armv4_AES_encrypt:
.align 5
ENTRY(private_AES_set_encrypt_key)
_armv4_AES_set_encrypt_key:
- sub r3,pc,#8 @ AES_set_encrypt_key
+ adr r3,_armv4_AES_set_encrypt_key
teq r0,#0
moveq r0,#-1
beq .Labrt
@@ -843,7 +843,7 @@ AES_Td:
@ const AES_KEY *key) {
.align 5
ENTRY(AES_decrypt)
- sub r3,pc,#8 @ AES_decrypt
+ adr r3,AES_decrypt
stmdb sp!,{r1,r4-r12,lr}
mov r12,r0 @ inp
mov r11,r2
diff --git a/arch/arm/include/asm/jump_label.h b/arch/arm/include/asm/jump_label.h
index bfc198c75913..863c892b4aaa 100644
--- a/arch/arm/include/asm/jump_label.h
+++ b/arch/arm/include/asm/jump_label.h
@@ -16,7 +16,7 @@
static __always_inline bool arch_static_branch(struct static_key *key)
{
- asm goto("1:\n\t"
+ asm_volatile_goto("1:\n\t"
JUMP_LABEL_NOP "\n\t"
".pushsection __jump_table, \"aw\"\n\t"
".word 1b, %l[l_yes], %c0\n\t"
diff --git a/arch/arm/include/asm/syscall.h b/arch/arm/include/asm/syscall.h
index f1d96d4e8092..73ddd7239b33 100644
--- a/arch/arm/include/asm/syscall.h
+++ b/arch/arm/include/asm/syscall.h
@@ -57,6 +57,9 @@ static inline void syscall_get_arguments(struct task_struct *task,
unsigned int i, unsigned int n,
unsigned long *args)
{
+ if (n == 0)
+ return;
+
if (i + n > SYSCALL_MAX_ARGS) {
unsigned long *args_bad = args + SYSCALL_MAX_ARGS - i;
unsigned int n_bad = n + i - SYSCALL_MAX_ARGS;
@@ -81,6 +84,9 @@ static inline void syscall_set_arguments(struct task_struct *task,
unsigned int i, unsigned int n,
const unsigned long *args)
{
+ if (n == 0)
+ return;
+
if (i + n > SYSCALL_MAX_ARGS) {
pr_warning("%s called with max args %d, handling only %d\n",
__func__, i + n, SYSCALL_MAX_ARGS);
diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c
index 778c2f7024ff..b321c8fbb87d 100644
--- a/arch/arm/kernel/kgdb.c
+++ b/arch/arm/kernel/kgdb.c
@@ -144,6 +144,8 @@ int kgdb_arch_handle_exception(int exception_vector, int signo,
static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr)
{
+ if (user_mode(regs))
+ return -1;
kgdb_handle_exception(1, SIGTRAP, 0, regs);
return 0;
@@ -151,6 +153,8 @@ static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr)
static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr)
{
+ if (user_mode(regs))
+ return -1;
compiled_break = 1;
kgdb_handle_exception(1, SIGTRAP, 0, regs);
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index c86ad0d5a495..49a9b7cda24a 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -593,7 +593,7 @@ int in_gate_area_no_mm(unsigned long addr)
{
return in_gate_area(NULL, addr);
}
-#define is_gate_vma(vma) ((vma) = &gate_vma)
+#define is_gate_vma(vma) ((vma) == &gate_vma)
#else
#define is_gate_vma(vma) 0
#endif
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index a666911fbe20..ed3243bb6c07 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -46,6 +46,9 @@
#include <asm/virt.h>
#include <asm/mach/arch.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/arm-ipi.h>
+
/*
* as from 2.5, kernels no longer have an init_tasks structure
* so we need some other way of telling a new secondary core
@@ -673,6 +676,7 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
if (ipinr < NR_IPI)
__inc_irq_stat(cpu, ipi_irqs[ipinr]);
+ trace_arm_ipi_entry(ipinr);
switch (ipinr) {
case IPI_WAKEUP:
break;
@@ -722,6 +726,7 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
cpu, ipinr);
break;
}
+ trace_arm_ipi_exit(ipinr);
set_irq_regs(old_regs);
}
diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
index 4a5199070430..db9cf692d4dd 100644
--- a/arch/arm/kvm/coproc.c
+++ b/arch/arm/kvm/coproc.c
@@ -146,7 +146,11 @@ static bool pm_fake(struct kvm_vcpu *vcpu,
#define access_pmintenclr pm_fake
/* Architected CP15 registers.
- * Important: Must be sorted ascending by CRn, CRM, Op1, Op2
+ * CRn denotes the primary register number, but is copied to the CRm in the
+ * user space API for 64-bit register access in line with the terminology used
+ * in the ARM ARM.
+ * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit
+ * registers preceding 32-bit ones.
*/
static const struct coproc_reg cp15_regs[] = {
/* CSSELR: swapped by interrupt.S. */
@@ -154,8 +158,8 @@ static const struct coproc_reg cp15_regs[] = {
NULL, reset_unknown, c0_CSSELR },
/* TTBR0/TTBR1: swapped by interrupt.S. */
- { CRm( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 },
- { CRm( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 },
+ { CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 },
+ { CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 },
/* TTBCR: swapped by interrupt.S. */
{ CRn( 2), CRm( 0), Op1( 0), Op2( 2), is32,
@@ -182,7 +186,7 @@ static const struct coproc_reg cp15_regs[] = {
NULL, reset_unknown, c6_IFAR },
/* PAR swapped by interrupt.S */
- { CRn( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR },
+ { CRm64( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR },
/*
* DC{C,I,CI}SW operations:
@@ -399,12 +403,13 @@ static bool index_to_params(u64 id, struct coproc_params *params)
| KVM_REG_ARM_OPC1_MASK))
return false;
params->is_64bit = true;
- params->CRm = ((id & KVM_REG_ARM_CRM_MASK)
+ /* CRm to CRn: see cp15_to_index for details */
+ params->CRn = ((id & KVM_REG_ARM_CRM_MASK)
>> KVM_REG_ARM_CRM_SHIFT);
params->Op1 = ((id & KVM_REG_ARM_OPC1_MASK)
>> KVM_REG_ARM_OPC1_SHIFT);
params->Op2 = 0;
- params->CRn = 0;
+ params->CRm = 0;
return true;
default:
return false;
@@ -898,7 +903,14 @@ static u64 cp15_to_index(const struct coproc_reg *reg)
if (reg->is_64) {
val |= KVM_REG_SIZE_U64;
val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT);
- val |= (reg->CRm << KVM_REG_ARM_CRM_SHIFT);
+ /*
+ * CRn always denotes the primary coproc. reg. nr. for the
+ * in-kernel representation, but the user space API uses the
+ * CRm for the encoding, because it is modelled after the
+ * MRRC/MCRR instructions: see the ARM ARM rev. c page
+ * B3-1445
+ */
+ val |= (reg->CRn << KVM_REG_ARM_CRM_SHIFT);
} else {
val |= KVM_REG_SIZE_U32;
val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT);
diff --git a/arch/arm/kvm/coproc.h b/arch/arm/kvm/coproc.h
index b7301d3e4799..0461d5c8d3de 100644
--- a/arch/arm/kvm/coproc.h
+++ b/arch/arm/kvm/coproc.h
@@ -135,6 +135,8 @@ static inline int cmp_reg(const struct coproc_reg *i1,
return -1;
if (i1->CRn != i2->CRn)
return i1->CRn - i2->CRn;
+ if (i1->is_64 != i2->is_64)
+ return i2->is_64 - i1->is_64;
if (i1->CRm != i2->CRm)
return i1->CRm - i2->CRm;
if (i1->Op1 != i2->Op1)
@@ -145,6 +147,7 @@ static inline int cmp_reg(const struct coproc_reg *i1,
#define CRn(_x) .CRn = _x
#define CRm(_x) .CRm = _x
+#define CRm64(_x) .CRn = _x, .CRm = 0
#define Op1(_x) .Op1 = _x
#define Op2(_x) .Op2 = _x
#define is64 .is_64 = true
diff --git a/arch/arm/kvm/coproc_a15.c b/arch/arm/kvm/coproc_a15.c
index 685063a6d0cf..cf93472b9dd6 100644
--- a/arch/arm/kvm/coproc_a15.c
+++ b/arch/arm/kvm/coproc_a15.c
@@ -114,7 +114,11 @@ static bool access_l2ectlr(struct kvm_vcpu *vcpu,
/*
* A15-specific CP15 registers.
- * Important: Must be sorted ascending by CRn, CRM, Op1, Op2
+ * CRn denotes the primary register number, but is copied to the CRm in the
+ * user space API for 64-bit register access in line with the terminology used
+ * in the ARM ARM.
+ * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit
+ * registers preceding 32-bit ones.
*/
static const struct coproc_reg a15_regs[] = {
/* MPIDR: we use VMPIDR for guest access. */
diff --git a/arch/arm/mach-mxs/pm.h b/arch/arm/mach-mxs/pm.h
index f57e7cdece2e..09d77b00a96b 100644
--- a/arch/arm/mach-mxs/pm.h
+++ b/arch/arm/mach-mxs/pm.h
@@ -9,6 +9,10 @@
#ifndef __ARCH_MXS_PM_H
#define __ARCH_MXS_PM_H
+#ifdef CONFIG_PM
void mxs_pm_init(void);
+#else
+#define mxs_pm_init NULL
+#endif
#endif
diff --git a/arch/arm/mach-versatile/include/mach/platform.h b/arch/arm/mach-versatile/include/mach/platform.h
index ec087407b163..6f938ccb0c54 100644
--- a/arch/arm/mach-versatile/include/mach/platform.h
+++ b/arch/arm/mach-versatile/include/mach/platform.h
@@ -231,12 +231,14 @@
/* PCI space */
#define VERSATILE_PCI_BASE 0x41000000 /* PCI Interface */
#define VERSATILE_PCI_CFG_BASE 0x42000000
+#define VERSATILE_PCI_IO_BASE 0x43000000
#define VERSATILE_PCI_MEM_BASE0 0x44000000
#define VERSATILE_PCI_MEM_BASE1 0x50000000
#define VERSATILE_PCI_MEM_BASE2 0x60000000
/* Sizes of above maps */
#define VERSATILE_PCI_BASE_SIZE 0x01000000
#define VERSATILE_PCI_CFG_BASE_SIZE 0x02000000
+#define VERSATILE_PCI_IO_BASE_SIZE 0x01000000
#define VERSATILE_PCI_MEM_BASE0_SIZE 0x0c000000 /* 32Mb */
#define VERSATILE_PCI_MEM_BASE1_SIZE 0x10000000 /* 256Mb */
#define VERSATILE_PCI_MEM_BASE2_SIZE 0x10000000 /* 256Mb */
diff --git a/arch/arm/mach-versatile/pci.c b/arch/arm/mach-versatile/pci.c
index e92e5e0705bc..c97be4ea76d2 100644
--- a/arch/arm/mach-versatile/pci.c
+++ b/arch/arm/mach-versatile/pci.c
@@ -43,9 +43,9 @@
#define PCI_IMAP0 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x0)
#define PCI_IMAP1 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x4)
#define PCI_IMAP2 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x8)
-#define PCI_SMAP0 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x10)
-#define PCI_SMAP1 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x14)
-#define PCI_SMAP2 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x18)
+#define PCI_SMAP0 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x14)
+#define PCI_SMAP1 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x18)
+#define PCI_SMAP2 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x1c)
#define PCI_SELFID __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0xc)
#define DEVICE_ID_OFFSET 0x00
@@ -170,8 +170,8 @@ static struct pci_ops pci_versatile_ops = {
.write = versatile_write_config,
};
-static struct resource io_mem = {
- .name = "PCI I/O space",
+static struct resource unused_mem = {
+ .name = "PCI unused",
.start = VERSATILE_PCI_MEM_BASE0,
.end = VERSATILE_PCI_MEM_BASE0+VERSATILE_PCI_MEM_BASE0_SIZE-1,
.flags = IORESOURCE_MEM,
@@ -195,9 +195,9 @@ static int __init pci_versatile_setup_resources(struct pci_sys_data *sys)
{
int ret = 0;
- ret = request_resource(&iomem_resource, &io_mem);
+ ret = request_resource(&iomem_resource, &unused_mem);
if (ret) {
- printk(KERN_ERR "PCI: unable to allocate I/O "
+ printk(KERN_ERR "PCI: unable to allocate unused "
"memory region (%d)\n", ret);
goto out;
}
@@ -205,7 +205,7 @@ static int __init pci_versatile_setup_resources(struct pci_sys_data *sys)
if (ret) {
printk(KERN_ERR "PCI: unable to allocate non-prefetchable "
"memory region (%d)\n", ret);
- goto release_io_mem;
+ goto release_unused_mem;
}
ret = request_resource(&iomem_resource, &pre_mem);
if (ret) {
@@ -225,8 +225,8 @@ static int __init pci_versatile_setup_resources(struct pci_sys_data *sys)
release_non_mem:
release_resource(&non_mem);
- release_io_mem:
- release_resource(&io_mem);
+ release_unused_mem:
+ release_resource(&unused_mem);
out:
return ret;
}
@@ -246,7 +246,7 @@ int __init pci_versatile_setup(int nr, struct pci_sys_data *sys)
goto out;
}
- ret = pci_ioremap_io(0, VERSATILE_PCI_MEM_BASE0);
+ ret = pci_ioremap_io(0, VERSATILE_PCI_IO_BASE);
if (ret)
goto out;
@@ -295,6 +295,19 @@ int __init pci_versatile_setup(int nr, struct pci_sys_data *sys)
__raw_writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_2);
/*
+ * For many years the kernel and QEMU were symbiotically buggy
+ * in that they both assumed the same broken IRQ mapping.
+ * QEMU therefore attempts to auto-detect old broken kernels
+ * so that they still work on newer QEMU as they did on old
+ * QEMU. Since we now use the correct (ie matching-hardware)
+ * IRQ mapping we write a definitely different value to a
+ * PCI_INTERRUPT_LINE register to tell QEMU that we expect
+ * real hardware behaviour and it need not be backwards
+ * compatible for us. This write is harmless on real hardware.
+ */
+ __raw_writel(0, VERSATILE_PCI_VIRT_BASE+PCI_INTERRUPT_LINE);
+
+ /*
* Do not to map Versatile FPGA PCI device into memory space
*/
pci_slot_ignore |= (1 << myslot);
@@ -327,13 +340,13 @@ static int __init versatile_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int irq;
- /* slot, pin, irq
- * 24 1 IRQ_SIC_PCI0
- * 25 1 IRQ_SIC_PCI1
- * 26 1 IRQ_SIC_PCI2
- * 27 1 IRQ_SIC_PCI3
+ /*
+ * Slot INTA INTB INTC INTD
+ * 31 PCI1 PCI2 PCI3 PCI0
+ * 30 PCI0 PCI1 PCI2 PCI3
+ * 29 PCI3 PCI0 PCI1 PCI2
*/
- irq = IRQ_SIC_PCI0 + ((slot - 24 + pin - 1) & 3);
+ irq = IRQ_SIC_PCI0 + ((slot + 2 + pin - 1) & 3);
return irq;
}
diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
index 05db95d010ae..81edd31bb4ac 100644
--- a/arch/arm/xen/enlighten.c
+++ b/arch/arm/xen/enlighten.c
@@ -273,12 +273,15 @@ core_initcall(xen_guest_init);
static int __init xen_pm_init(void)
{
+ if (!xen_domain())
+ return -ENODEV;
+
pm_power_off = xen_power_off;
arm_pm_restart = xen_restart;
return 0;
}
-subsys_initcall(xen_pm_init);
+late_initcall(xen_pm_init);
static irqreturn_t xen_arm_callback(int irq, void *arg)
{
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 12e6ccb88691..cea1594ff933 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -325,7 +325,10 @@ validate_event(struct pmu_hw_events *hw_events,
if (is_software_event(event))
return 1;
- if (event->pmu != leader_pmu || event->state <= PERF_EVENT_STATE_OFF)
+ if (event->pmu != leader_pmu || event->state < PERF_EVENT_STATE_OFF)
+ return 1;
+
+ if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec)
return 1;
return armpmu->get_event_idx(hw_events, &fake_event) >= 0;
@@ -781,7 +784,7 @@ static const unsigned armv8_pmuv3_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
/*
* PMXEVTYPER: Event selection reg
*/
-#define ARMV8_EVTYPE_MASK 0xc00000ff /* Mask for writable bits */
+#define ARMV8_EVTYPE_MASK 0xc80000ff /* Mask for writable bits */
#define ARMV8_EVTYPE_EVENT 0xff /* Mask for EVENT bits */
/*
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 5d54e3717bf8..a28d2859932e 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -48,6 +48,9 @@
#include <asm/tlbflush.h>
#include <asm/ptrace.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/arm-ipi.h>
+
/*
* as from 2.5, kernels no longer have an init_tasks structure
* so we need some other way of telling a new secondary core
diff --git a/arch/avr32/kernel/time.c b/arch/avr32/kernel/time.c
index 869a1c6ffeee..12f828ad5058 100644
--- a/arch/avr32/kernel/time.c
+++ b/arch/avr32/kernel/time.c
@@ -98,7 +98,14 @@ static void comparator_mode(enum clock_event_mode mode,
case CLOCK_EVT_MODE_SHUTDOWN:
sysreg_write(COMPARE, 0);
pr_debug("%s: stop\n", evdev->name);
- cpu_idle_poll_ctrl(false);
+ if (evdev->mode == CLOCK_EVT_MODE_ONESHOT ||
+ evdev->mode == CLOCK_EVT_MODE_RESUME) {
+ /*
+ * Only disable idle poll if we have forced that
+ * in a previous call.
+ */
+ cpu_idle_poll_ctrl(false);
+ }
break;
default:
BUG();
diff --git a/arch/mips/ath79/clock.c b/arch/mips/ath79/clock.c
index 765ef30e3e1c..733017b3dfe7 100644
--- a/arch/mips/ath79/clock.c
+++ b/arch/mips/ath79/clock.c
@@ -164,7 +164,7 @@ static void __init ar933x_clocks_init(void)
ath79_ahb_clk.rate = freq / t;
}
- ath79_wdt_clk.rate = ath79_ref_clk.rate;
+ ath79_wdt_clk.rate = ath79_ahb_clk.rate;
ath79_uart_clk.rate = ath79_ref_clk.rate;
}
diff --git a/arch/mips/include/asm/jump_label.h b/arch/mips/include/asm/jump_label.h
index 4d6d77ed9b9d..e194f957ca8c 100644
--- a/arch/mips/include/asm/jump_label.h
+++ b/arch/mips/include/asm/jump_label.h
@@ -22,7 +22,7 @@
static __always_inline bool arch_static_branch(struct static_key *key)
{
- asm goto("1:\tnop\n\t"
+ asm_volatile_goto("1:\tnop\n\t"
"nop\n\t"
".pushsection __jump_table, \"aw\"\n\t"
WORD_INSN " 1b, %l[l_yes], %0\n\t"
diff --git a/arch/parisc/kernel/head.S b/arch/parisc/kernel/head.S
index 37aabd772fbb..d2d58258aea6 100644
--- a/arch/parisc/kernel/head.S
+++ b/arch/parisc/kernel/head.S
@@ -195,6 +195,8 @@ common_stext:
ldw MEM_PDC_HI(%r0),%r6
depd %r6, 31, 32, %r3 /* move to upper word */
+ mfctl %cr30,%r6 /* PCX-W2 firmware bug */
+
ldo PDC_PSW(%r0),%arg0 /* 21 */
ldo PDC_PSW_SET_DEFAULTS(%r0),%arg1 /* 2 */
ldo PDC_PSW_WIDE_BIT(%r0),%arg2 /* 2 */
@@ -203,6 +205,8 @@ common_stext:
copy %r0,%arg3
stext_pdc_ret:
+ mtctl %r6,%cr30 /* restore task thread info */
+
/* restore rfi target address*/
ldd TI_TASK-THREAD_SZ_ALGN(%sp), %r10
tophys_r1 %r10
diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c
index 04e47c6a4562..b3f87a3b4bce 100644
--- a/arch/parisc/kernel/traps.c
+++ b/arch/parisc/kernel/traps.c
@@ -805,14 +805,14 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
else {
/*
- * The kernel should never fault on its own address space.
+ * The kernel should never fault on its own address space,
+ * unless pagefault_disable() was called before.
*/
- if (fault_space == 0)
+ if (fault_space == 0 && !in_atomic())
{
pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC);
parisc_terminate("Kernel Fault", regs, code, fault_address);
-
}
}
diff --git a/arch/powerpc/include/asm/jump_label.h b/arch/powerpc/include/asm/jump_label.h
index ae098c438f00..f016bb699b5f 100644
--- a/arch/powerpc/include/asm/jump_label.h
+++ b/arch/powerpc/include/asm/jump_label.h
@@ -19,7 +19,7 @@
static __always_inline bool arch_static_branch(struct static_key *key)
{
- asm goto("1:\n\t"
+ asm_volatile_goto("1:\n\t"
"nop\n\t"
".pushsection __jump_table, \"aw\"\n\t"
JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t"
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
index ee5b690a0bed..52e5758ea368 100644
--- a/arch/powerpc/kernel/align.c
+++ b/arch/powerpc/kernel/align.c
@@ -764,6 +764,16 @@ int fix_alignment(struct pt_regs *regs)
nb = aligninfo[instr].len;
flags = aligninfo[instr].flags;
+ /* ldbrx/stdbrx overlap lfs/stfs in the DSISR unfortunately */
+ if (IS_XFORM(instruction) && ((instruction >> 1) & 0x3ff) == 532) {
+ nb = 8;
+ flags = LD+SW;
+ } else if (IS_XFORM(instruction) &&
+ ((instruction >> 1) & 0x3ff) == 660) {
+ nb = 8;
+ flags = ST+SW;
+ }
+
/* Byteswap little endian loads and stores */
swiz = 0;
if (regs->msr & MSR_LE) {
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index c0d0dbddfba1..93d8d96840b5 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -658,7 +658,7 @@ struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid)
/* number of bytes needed for the bitmap */
sz = BITS_TO_LONGS(tbl->it_size) * sizeof(unsigned long);
- page = alloc_pages_node(nid, GFP_ATOMIC, get_order(sz));
+ page = alloc_pages_node(nid, GFP_KERNEL, get_order(sz));
if (!page)
panic("iommu_init_table: Can't allocate %ld bytes\n", sz);
tbl->it_map = page_address(page);
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index e68a84568b8b..a15fd1a0690c 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -17,6 +17,7 @@
#include <asm/machdep.h>
#include <asm/smp.h>
#include <asm/pmc.h>
+#include <asm/firmware.h>
#include "cacheinfo.h"
@@ -179,15 +180,25 @@ SYSFS_PMCSETUP(spurr, SPRN_SPURR);
SYSFS_PMCSETUP(dscr, SPRN_DSCR);
SYSFS_PMCSETUP(pir, SPRN_PIR);
+/*
+ Lets only enable read for phyp resources and
+ enable write when needed with a separate function.
+ Lets be conservative and default to pseries.
+*/
static DEVICE_ATTR(mmcra, 0600, show_mmcra, store_mmcra);
static DEVICE_ATTR(spurr, 0400, show_spurr, NULL);
static DEVICE_ATTR(dscr, 0600, show_dscr, store_dscr);
-static DEVICE_ATTR(purr, 0600, show_purr, store_purr);
+static DEVICE_ATTR(purr, 0400, show_purr, store_purr);
static DEVICE_ATTR(pir, 0400, show_pir, NULL);
unsigned long dscr_default = 0;
EXPORT_SYMBOL(dscr_default);
+static void add_write_permission_dev_attr(struct device_attribute *attr)
+{
+ attr->attr.mode |= 0200;
+}
+
static ssize_t show_dscr_default(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -394,8 +405,11 @@ static void __cpuinit register_cpu_online(unsigned int cpu)
if (cpu_has_feature(CPU_FTR_MMCRA))
device_create_file(s, &dev_attr_mmcra);
- if (cpu_has_feature(CPU_FTR_PURR))
+ if (cpu_has_feature(CPU_FTR_PURR)) {
+ if (!firmware_has_feature(FW_FEATURE_LPAR))
+ add_write_permission_dev_attr(&dev_attr_purr);
device_create_file(s, &dev_attr_purr);
+ }
if (cpu_has_feature(CPU_FTR_SPURR))
device_create_file(s, &dev_attr_spurr);
diff --git a/arch/powerpc/kernel/tm.S b/arch/powerpc/kernel/tm.S
index 1edd6c2a168d..f2abb219a17b 100644
--- a/arch/powerpc/kernel/tm.S
+++ b/arch/powerpc/kernel/tm.S
@@ -79,6 +79,11 @@ _GLOBAL(tm_abort)
TABORT(R3)
blr
+ .section ".toc","aw"
+DSCR_DEFAULT:
+ .tc dscr_default[TC],dscr_default
+
+ .section ".text"
/* void tm_reclaim(struct thread_struct *thread,
* unsigned long orig_msr,
@@ -178,11 +183,18 @@ dont_backup_fp:
std r1, PACATMSCRATCH(r13)
ld r1, PACAR1(r13)
+ /* Store the PPR in r11 and reset to decent value */
+ std r11, GPR11(r1) /* Temporary stash */
+ mfspr r11, SPRN_PPR
+ HMT_MEDIUM
+
/* Now get some more GPRS free */
std r7, GPR7(r1) /* Temporary stash */
std r12, GPR12(r1) /* '' '' '' */
ld r12, STACK_PARAM(0)(r1) /* Param 0, thread_struct * */
+ std r11, THREAD_TM_PPR(r12) /* Store PPR and free r11 */
+
addi r7, r12, PT_CKPT_REGS /* Thread's ckpt_regs */
/* Make r7 look like an exception frame so that we
@@ -194,15 +206,19 @@ dont_backup_fp:
SAVE_GPR(0, r7) /* user r0 */
SAVE_GPR(2, r7) /* user r2 */
SAVE_4GPRS(3, r7) /* user r3-r6 */
- SAVE_4GPRS(8, r7) /* user r8-r11 */
+ SAVE_GPR(8, r7) /* user r8 */
+ SAVE_GPR(9, r7) /* user r9 */
+ SAVE_GPR(10, r7) /* user r10 */
ld r3, PACATMSCRATCH(r13) /* user r1 */
ld r4, GPR7(r1) /* user r7 */
- ld r5, GPR12(r1) /* user r12 */
- GET_SCRATCH0(6) /* user r13 */
+ ld r5, GPR11(r1) /* user r11 */
+ ld r6, GPR12(r1) /* user r12 */
+ GET_SCRATCH0(8) /* user r13 */
std r3, GPR1(r7)
std r4, GPR7(r7)
- std r5, GPR12(r7)
- std r6, GPR13(r7)
+ std r5, GPR11(r7)
+ std r6, GPR12(r7)
+ std r8, GPR13(r7)
SAVE_NVGPRS(r7) /* user r14-r31 */
@@ -225,14 +241,12 @@ dont_backup_fp:
std r6, _XER(r7)
- /* ******************** TAR, PPR, DSCR ********** */
+ /* ******************** TAR, DSCR ********** */
mfspr r3, SPRN_TAR
- mfspr r4, SPRN_PPR
- mfspr r5, SPRN_DSCR
+ mfspr r4, SPRN_DSCR
std r3, THREAD_TM_TAR(r12)
- std r4, THREAD_TM_PPR(r12)
- std r5, THREAD_TM_DSCR(r12)
+ std r4, THREAD_TM_DSCR(r12)
/* MSR and flags: We don't change CRs, and we don't need to alter
* MSR.
@@ -249,7 +263,7 @@ dont_backup_fp:
std r3, THREAD_TM_TFHAR(r12)
std r4, THREAD_TM_TFIAR(r12)
- /* AMR and PPR are checkpointed too, but are unsupported by Linux. */
+ /* AMR is checkpointed too, but is unsupported by Linux. */
/* Restore original MSR/IRQ state & clear TM mode */
ld r14, TM_FRAME_L0(r1) /* Orig MSR */
@@ -265,6 +279,12 @@ dont_backup_fp:
mtcr r4
mtlr r0
ld r2, 40(r1)
+
+ /* Load system default DSCR */
+ ld r4, DSCR_DEFAULT@toc(r2)
+ ld r0, 0(r4)
+ mtspr SPRN_DSCR, r0
+
blr
@@ -349,44 +369,50 @@ dont_restore_fp:
restore_gprs:
- /* ******************** TAR, PPR, DSCR ********** */
- ld r4, THREAD_TM_TAR(r3)
- ld r5, THREAD_TM_PPR(r3)
- ld r6, THREAD_TM_DSCR(r3)
+ /* ******************** CR,LR,CCR,MSR ********** */
+ ld r4, _CTR(r7)
+ ld r5, _LINK(r7)
+ ld r6, _CCR(r7)
+ ld r8, _XER(r7)
- mtspr SPRN_TAR, r4
- mtspr SPRN_PPR, r5
- mtspr SPRN_DSCR, r6
+ mtctr r4
+ mtlr r5
+ mtcr r6
+ mtxer r8
- /* ******************** CR,LR,CCR,MSR ********** */
- ld r3, _CTR(r7)
- ld r4, _LINK(r7)
- ld r5, _CCR(r7)
- ld r6, _XER(r7)
+ /* ******************** TAR ******************** */
+ ld r4, THREAD_TM_TAR(r3)
+ mtspr SPRN_TAR, r4
- mtctr r3
- mtlr r4
- mtcr r5
- mtxer r6
+ /* Load up the PPR and DSCR in GPRs only at this stage */
+ ld r5, THREAD_TM_DSCR(r3)
+ ld r6, THREAD_TM_PPR(r3)
/* MSR and flags: We don't change CRs, and we don't need to alter
* MSR.
*/
REST_4GPRS(0, r7) /* GPR0-3 */
- REST_GPR(4, r7) /* GPR4-6 */
- REST_GPR(5, r7)
- REST_GPR(6, r7)
+ REST_GPR(4, r7) /* GPR4 */
REST_4GPRS(8, r7) /* GPR8-11 */
REST_2GPRS(12, r7) /* GPR12-13 */
REST_NVGPRS(r7) /* GPR14-31 */
- ld r7, GPR7(r7) /* GPR7 */
+ /* Load up PPR and DSCR here so we don't run with user values for long
+ */
+ mtspr SPRN_DSCR, r5
+ mtspr SPRN_PPR, r6
+
+ REST_GPR(5, r7) /* GPR5-7 */
+ REST_GPR(6, r7)
+ ld r7, GPR7(r7)
/* Commit register state as checkpointed state: */
TRECHKPT
+ HMT_MEDIUM
+
/* Our transactional state has now changed.
*
* Now just get out of here. Transactional (current) state will be
@@ -405,6 +431,12 @@ restore_gprs:
mtcr r4
mtlr r0
ld r2, 40(r1)
+
+ /* Load system default DSCR */
+ ld r4, DSCR_DEFAULT@toc(r2)
+ ld r0, 0(r4)
+ mtspr SPRN_DSCR, r0
+
blr
/* ****************************************************************** */
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
index 536016d792ba..2d845d8199fc 100644
--- a/arch/powerpc/kernel/vio.c
+++ b/arch/powerpc/kernel/vio.c
@@ -1529,11 +1529,15 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
const char *cp;
dn = dev->of_node;
- if (!dn)
- return -ENODEV;
+ if (!dn) {
+ strcat(buf, "\n");
+ return strlen(buf);
+ }
cp = of_get_property(dn, "compatible", NULL);
- if (!cp)
- return -ENODEV;
+ if (!cp) {
+ strcat(buf, "\n");
+ return strlen(buf);
+ }
return sprintf(buf, "vio:T%sS%s\n", vio_dev->type, cp);
}
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index b02f91e4c70d..7bcd4d6e177b 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -1054,7 +1054,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
BEGIN_FTR_SECTION
mfspr r8, SPRN_DSCR
ld r7, HSTATE_DSCR(r13)
- std r8, VCPU_DSCR(r7)
+ std r8, VCPU_DSCR(r9)
mtspr SPRN_DSCR, r7
END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
diff --git a/arch/powerpc/kvm/book3s_xics.c b/arch/powerpc/kvm/book3s_xics.c
index 94c1dd46b83d..a3a5cb8ee7ea 100644
--- a/arch/powerpc/kvm/book3s_xics.c
+++ b/arch/powerpc/kvm/book3s_xics.c
@@ -19,6 +19,7 @@
#include <asm/hvcall.h>
#include <asm/xics.h>
#include <asm/debug.h>
+#include <asm/time.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
diff --git a/arch/powerpc/lib/checksum_64.S b/arch/powerpc/lib/checksum_64.S
index 167f72555d60..57a072065057 100644
--- a/arch/powerpc/lib/checksum_64.S
+++ b/arch/powerpc/lib/checksum_64.S
@@ -226,19 +226,35 @@ _GLOBAL(csum_partial)
blr
- .macro source
+ .macro srcnr
100:
.section __ex_table,"a"
.align 3
- .llong 100b,.Lsrc_error
+ .llong 100b,.Lsrc_error_nr
.previous
.endm
- .macro dest
+ .macro source
+150:
+ .section __ex_table,"a"
+ .align 3
+ .llong 150b,.Lsrc_error
+ .previous
+ .endm
+
+ .macro dstnr
200:
.section __ex_table,"a"
.align 3
- .llong 200b,.Ldest_error
+ .llong 200b,.Ldest_error_nr
+ .previous
+ .endm
+
+ .macro dest
+250:
+ .section __ex_table,"a"
+ .align 3
+ .llong 250b,.Ldest_error
.previous
.endm
@@ -269,16 +285,16 @@ _GLOBAL(csum_partial_copy_generic)
rldicl. r6,r3,64-1,64-2 /* r6 = (r3 & 0x3) >> 1 */
beq .Lcopy_aligned
- li r7,4
- sub r6,r7,r6
+ li r9,4
+ sub r6,r9,r6
mtctr r6
1:
-source; lhz r6,0(r3) /* align to doubleword */
+srcnr; lhz r6,0(r3) /* align to doubleword */
subi r5,r5,2
addi r3,r3,2
adde r0,r0,r6
-dest; sth r6,0(r4)
+dstnr; sth r6,0(r4)
addi r4,r4,2
bdnz 1b
@@ -392,10 +408,10 @@ dest; std r16,56(r4)
mtctr r6
3:
-source; ld r6,0(r3)
+srcnr; ld r6,0(r3)
addi r3,r3,8
adde r0,r0,r6
-dest; std r6,0(r4)
+dstnr; std r6,0(r4)
addi r4,r4,8
bdnz 3b
@@ -405,10 +421,10 @@ dest; std r6,0(r4)
srdi. r6,r5,2
beq .Lcopy_tail_halfword
-source; lwz r6,0(r3)
+srcnr; lwz r6,0(r3)
addi r3,r3,4
adde r0,r0,r6
-dest; stw r6,0(r4)
+dstnr; stw r6,0(r4)
addi r4,r4,4
subi r5,r5,4
@@ -416,10 +432,10 @@ dest; stw r6,0(r4)
srdi. r6,r5,1
beq .Lcopy_tail_byte
-source; lhz r6,0(r3)
+srcnr; lhz r6,0(r3)
addi r3,r3,2
adde r0,r0,r6
-dest; sth r6,0(r4)
+dstnr; sth r6,0(r4)
addi r4,r4,2
subi r5,r5,2
@@ -427,10 +443,10 @@ dest; sth r6,0(r4)
andi. r6,r5,1
beq .Lcopy_finish
-source; lbz r6,0(r3)
+srcnr; lbz r6,0(r3)
sldi r9,r6,8 /* Pad the byte out to 16 bits */
adde r0,r0,r9
-dest; stb r6,0(r4)
+dstnr; stb r6,0(r4)
.Lcopy_finish:
addze r0,r0 /* add in final carry */
@@ -440,6 +456,11 @@ dest; stb r6,0(r4)
blr
.Lsrc_error:
+ ld r14,STK_REG(R14)(r1)
+ ld r15,STK_REG(R15)(r1)
+ ld r16,STK_REG(R16)(r1)
+ addi r1,r1,STACKFRAMESIZE
+.Lsrc_error_nr:
cmpdi 0,r7,0
beqlr
li r6,-EFAULT
@@ -447,6 +468,11 @@ dest; stb r6,0(r4)
blr
.Ldest_error:
+ ld r14,STK_REG(R14)(r1)
+ ld r15,STK_REG(R15)(r1)
+ ld r16,STK_REG(R16)(r1)
+ addi r1,r1,STACKFRAMESIZE
+.Ldest_error_nr:
cmpdi 0,r8,0
beqlr
li r6,-EFAULT
diff --git a/arch/powerpc/perf/power8-pmu.c b/arch/powerpc/perf/power8-pmu.c
index d59f5b2d4c2f..9aefaebedef1 100644
--- a/arch/powerpc/perf/power8-pmu.c
+++ b/arch/powerpc/perf/power8-pmu.c
@@ -194,6 +194,7 @@
#define MMCR1_UNIT_SHIFT(pmc) (60 - (4 * ((pmc) - 1)))
#define MMCR1_COMBINE_SHIFT(pmc) (35 - ((pmc) - 1))
#define MMCR1_PMCSEL_SHIFT(pmc) (24 - (((pmc) - 1)) * 8)
+#define MMCR1_FAB_SHIFT 36
#define MMCR1_DC_QUAL_SHIFT 47
#define MMCR1_IC_QUAL_SHIFT 46
@@ -367,8 +368,8 @@ static int power8_compute_mmcr(u64 event[], int n_ev,
* the threshold bits are used for the match value.
*/
if (event_is_fab_match(event[i])) {
- mmcr1 |= (event[i] >> EVENT_THR_CTL_SHIFT) &
- EVENT_THR_CTL_MASK;
+ mmcr1 |= ((event[i] >> EVENT_THR_CTL_SHIFT) &
+ EVENT_THR_CTL_MASK) << MMCR1_FAB_SHIFT;
} else {
val = (event[i] >> EVENT_THR_CTL_SHIFT) & EVENT_THR_CTL_MASK;
mmcra |= val << MMCRA_THR_CTL_SHIFT;
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index c11c8238797c..54b998f2750d 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -354,7 +354,7 @@ static int alloc_dispatch_log_kmem_cache(void)
}
early_initcall(alloc_dispatch_log_kmem_cache);
-static void pSeries_idle(void)
+static void pseries_lpar_idle(void)
{
/* This would call on the cpuidle framework, and the back-end pseries
* driver to go to idle states
@@ -362,10 +362,22 @@ static void pSeries_idle(void)
if (cpuidle_idle_call()) {
/* On error, execute default handler
* to go into low thread priority and possibly
- * low power mode.
+ * low power mode by cedeing processor to hypervisor
*/
- HMT_low();
- HMT_very_low();
+
+ /* Indicate to hypervisor that we are idle. */
+ get_lppaca()->idle = 1;
+
+ /*
+ * Yield the processor to the hypervisor. We return if
+ * an external interrupt occurs (which are driven prior
+ * to returning here) or if a prod occurs from another
+ * processor. When returning here, external interrupts
+ * are enabled.
+ */
+ cede_processor();
+
+ get_lppaca()->idle = 0;
}
}
@@ -456,15 +468,14 @@ static void __init pSeries_setup_arch(void)
pSeries_nvram_init();
- if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
+ if (firmware_has_feature(FW_FEATURE_LPAR)) {
vpa_init(boot_cpuid);
- ppc_md.power_save = pSeries_idle;
- }
-
- if (firmware_has_feature(FW_FEATURE_LPAR))
+ ppc_md.power_save = pseries_lpar_idle;
ppc_md.enable_pmcs = pseries_lpar_enable_pmcs;
- else
+ } else {
+ /* No special idle routine */
ppc_md.enable_pmcs = power4_enable_pmcs;
+ }
ppc_md.pcibios_root_bridge_prepare = pseries_root_bridge_prepare;
diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h
index 6c32190dc73e..346b1c85ffb4 100644
--- a/arch/s390/include/asm/jump_label.h
+++ b/arch/s390/include/asm/jump_label.h
@@ -15,7 +15,7 @@
static __always_inline bool arch_static_branch(struct static_key *key)
{
- asm goto("0: brcl 0,0\n"
+ asm_volatile_goto("0: brcl 0,0\n"
".pushsection __jump_table, \"aw\"\n"
ASM_ALIGN "\n"
ASM_PTR " 0b, %l[label], %0\n"
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index 4d5e6f8a7978..32bb7bf29397 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -265,6 +265,7 @@ sysc_sigpending:
tm __TI_flags+3(%r12),_TIF_SYSCALL
jno sysc_return
lm %r2,%r7,__PT_R2(%r11) # load svc arguments
+ l %r10,__TI_sysc_table(%r12) # 31 bit system call table
xr %r8,%r8 # svc 0 returns -ENOSYS
clc __PT_INT_CODE+2(2,%r11),BASED(.Lnr_syscalls+2)
jnl sysc_nr_ok # invalid svc number -> do svc 0
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S
index 4c17eece707e..2e3befddecee 100644
--- a/arch/s390/kernel/entry64.S
+++ b/arch/s390/kernel/entry64.S
@@ -293,6 +293,7 @@ sysc_sigpending:
tm __TI_flags+7(%r12),_TIF_SYSCALL
jno sysc_return
lmg %r2,%r7,__PT_R2(%r11) # load svc arguments
+ lg %r10,__TI_sysc_table(%r12) # address of system call table
lghi %r8,0 # svc 0 returns -ENOSYS
llgh %r1,__PT_INT_CODE+2(%r11) # load new svc number
cghi %r1,NR_syscalls
diff --git a/arch/sparc/include/asm/jump_label.h b/arch/sparc/include/asm/jump_label.h
index 5080d16a832f..ec2e2e2aba7d 100644
--- a/arch/sparc/include/asm/jump_label.h
+++ b/arch/sparc/include/asm/jump_label.h
@@ -9,7 +9,7 @@
static __always_inline bool arch_static_branch(struct static_key *key)
{
- asm goto("1:\n\t"
+ asm_volatile_goto("1:\n\t"
"nop\n\t"
"nop\n\t"
".pushsection __jump_table, \"aw\"\n\t"
diff --git a/arch/sparc/kernel/ds.c b/arch/sparc/kernel/ds.c
index 5ef48dab5636..252f8768aac6 100644
--- a/arch/sparc/kernel/ds.c
+++ b/arch/sparc/kernel/ds.c
@@ -842,9 +842,8 @@ void ldom_reboot(const char *boot_command)
if (boot_command && strlen(boot_command)) {
unsigned long len;
- strcpy(full_boot_str, "boot ");
- strlcpy(full_boot_str + strlen("boot "), boot_command,
- sizeof(full_boot_str + strlen("boot ")));
+ snprintf(full_boot_str, sizeof(full_boot_str), "boot %s",
+ boot_command);
len = strlen(full_boot_str);
if (reboot_data_supported) {
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
index e2a030045089..33c02b15f478 100644
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -839,7 +839,7 @@ sys_sigreturn:
nop
call syscall_trace
- nop
+ mov 1, %o1
1:
/* We don't want to muck with user registers like a
diff --git a/arch/sparc/kernel/ktlb.S b/arch/sparc/kernel/ktlb.S
index 0746e5e32b37..fde5a419cf27 100644
--- a/arch/sparc/kernel/ktlb.S
+++ b/arch/sparc/kernel/ktlb.S
@@ -25,11 +25,10 @@ kvmap_itlb:
*/
kvmap_itlb_4v:
-kvmap_itlb_nonlinear:
/* Catch kernel NULL pointer calls. */
sethi %hi(PAGE_SIZE), %g5
cmp %g4, %g5
- bleu,pn %xcc, kvmap_dtlb_longpath
+ blu,pn %xcc, kvmap_itlb_longpath
nop
KERN_TSB_LOOKUP_TL1(%g4, %g6, %g5, %g1, %g2, %g3, kvmap_itlb_load)
diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
index 22a1098961f5..73ec8a798d95 100644
--- a/arch/sparc/kernel/syscalls.S
+++ b/arch/sparc/kernel/syscalls.S
@@ -152,7 +152,7 @@ linux_syscall_trace32:
srl %i4, 0, %o4
srl %i1, 0, %o1
srl %i2, 0, %o2
- ba,pt %xcc, 2f
+ ba,pt %xcc, 5f
srl %i3, 0, %o3
linux_syscall_trace:
@@ -182,13 +182,13 @@ linux_sparc_syscall32:
srl %i1, 0, %o1 ! IEU0 Group
ldx [%g6 + TI_FLAGS], %l0 ! Load
- srl %i5, 0, %o5 ! IEU1
+ srl %i3, 0, %o3 ! IEU0
srl %i2, 0, %o2 ! IEU0 Group
andcc %l0, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT|_TIF_SYSCALL_TRACEPOINT), %g0
bne,pn %icc, linux_syscall_trace32 ! CTI
mov %i0, %l5 ! IEU1
- call %l7 ! CTI Group brk forced
- srl %i3, 0, %o3 ! IEU0
+5: call %l7 ! CTI Group brk forced
+ srl %i5, 0, %o5 ! IEU1
ba,a,pt %xcc, 3f
/* Linux native system calls enter here... */
diff --git a/arch/sparc/kernel/trampoline_64.S b/arch/sparc/kernel/trampoline_64.S
index 2e973a26fbda..3a43edb46aa3 100644
--- a/arch/sparc/kernel/trampoline_64.S
+++ b/arch/sparc/kernel/trampoline_64.S
@@ -131,7 +131,6 @@ startup_continue:
clr %l5
sethi %hi(num_kernel_image_mappings), %l6
lduw [%l6 + %lo(num_kernel_image_mappings)], %l6
- add %l6, 1, %l6
mov 15, %l7
BRANCH_IF_ANY_CHEETAH(g1,g5,2f)
@@ -224,7 +223,6 @@ niagara_lock_tlb:
clr %l5
sethi %hi(num_kernel_image_mappings), %l6
lduw [%l6 + %lo(num_kernel_image_mappings)], %l6
- add %l6, 1, %l6
1:
mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
diff --git a/arch/sparc/lib/ksyms.c b/arch/sparc/lib/ksyms.c
index 0c4e35e522fa..323335b9cd2b 100644
--- a/arch/sparc/lib/ksyms.c
+++ b/arch/sparc/lib/ksyms.c
@@ -98,15 +98,6 @@ EXPORT_SYMBOL(___copy_from_user);
EXPORT_SYMBOL(___copy_in_user);
EXPORT_SYMBOL(__clear_user);
-/* RW semaphores */
-EXPORT_SYMBOL(__down_read);
-EXPORT_SYMBOL(__down_read_trylock);
-EXPORT_SYMBOL(__down_write);
-EXPORT_SYMBOL(__down_write_trylock);
-EXPORT_SYMBOL(__up_read);
-EXPORT_SYMBOL(__up_write);
-EXPORT_SYMBOL(__downgrade_write);
-
/* Atomic counter implementation. */
EXPORT_SYMBOL(atomic_add);
EXPORT_SYMBOL(atomic_add_ret);
diff --git a/arch/tile/include/asm/percpu.h b/arch/tile/include/asm/percpu.h
index 63294f5a8efb..4f7ae39fa202 100644
--- a/arch/tile/include/asm/percpu.h
+++ b/arch/tile/include/asm/percpu.h
@@ -15,9 +15,37 @@
#ifndef _ASM_TILE_PERCPU_H
#define _ASM_TILE_PERCPU_H
-register unsigned long __my_cpu_offset __asm__("tp");
-#define __my_cpu_offset __my_cpu_offset
-#define set_my_cpu_offset(tp) (__my_cpu_offset = (tp))
+register unsigned long my_cpu_offset_reg asm("tp");
+
+#ifdef CONFIG_PREEMPT
+/*
+ * For full preemption, we can't just use the register variable
+ * directly, since we need barrier() to hazard against it, causing the
+ * compiler to reload anything computed from a previous "tp" value.
+ * But we also don't want to use volatile asm, since we'd like the
+ * compiler to be able to cache the value across multiple percpu reads.
+ * So we use a fake stack read as a hazard against barrier().
+ * The 'U' constraint is like 'm' but disallows postincrement.
+ */
+static inline unsigned long __my_cpu_offset(void)
+{
+ unsigned long tp;
+ register unsigned long *sp asm("sp");
+ asm("move %0, tp" : "=r" (tp) : "U" (*sp));
+ return tp;
+}
+#define __my_cpu_offset __my_cpu_offset()
+#else
+/*
+ * We don't need to hazard against barrier() since "tp" doesn't ever
+ * change with PREEMPT_NONE, and with PREEMPT_VOLUNTARY it only
+ * changes at function call points, at which we are already re-reading
+ * the value of "tp" due to "my_cpu_offset_reg" being a global variable.
+ */
+#define __my_cpu_offset my_cpu_offset_reg
+#endif
+
+#define set_my_cpu_offset(tp) (my_cpu_offset_reg = (tp))
#include <asm-generic/percpu.h>
diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h
index 95feaa47a2fb..c70a234a3f8c 100644
--- a/arch/um/include/shared/os.h
+++ b/arch/um/include/shared/os.h
@@ -200,6 +200,7 @@ extern int os_unmap_memory(void *addr, int len);
extern int os_drop_memory(void *addr, int length);
extern int can_drop_memory(void);
extern void os_flush_stdout(void);
+extern int os_mincore(void *addr, unsigned long len);
/* execvp.c */
extern int execvp_noalloc(char *buf, const char *file, char *const argv[]);
diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile
index babe21826e3e..d8b78a03855c 100644
--- a/arch/um/kernel/Makefile
+++ b/arch/um/kernel/Makefile
@@ -13,7 +13,7 @@ clean-files :=
obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \
physmem.o process.o ptrace.o reboot.o sigio.o \
signal.o smp.o syscall.o sysrq.o time.o tlb.o trap.o \
- um_arch.o umid.o skas/
+ um_arch.o umid.o maccess.o skas/
obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o
obj-$(CONFIG_GPROF) += gprof_syms.o
diff --git a/arch/um/kernel/exitcode.c b/arch/um/kernel/exitcode.c
index 829df49dee99..41ebbfebb333 100644
--- a/arch/um/kernel/exitcode.c
+++ b/arch/um/kernel/exitcode.c
@@ -40,9 +40,11 @@ static ssize_t exitcode_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *pos)
{
char *end, buf[sizeof("nnnnn\0")];
+ size_t size;
int tmp;
- if (copy_from_user(buf, buffer, count))
+ size = min(count, sizeof(buf));
+ if (copy_from_user(buf, buffer, size))
return -EFAULT;
tmp = simple_strtol(buf, &end, 0);
diff --git a/arch/um/kernel/maccess.c b/arch/um/kernel/maccess.c
new file mode 100644
index 000000000000..1f3d5c4910d1
--- /dev/null
+++ b/arch/um/kernel/maccess.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 Richard Weinberger <richrd@nod.at>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <os.h>
+
+long probe_kernel_read(void *dst, const void *src, size_t size)
+{
+ void *psrc = (void *)rounddown((unsigned long)src, PAGE_SIZE);
+
+ if ((unsigned long)src < PAGE_SIZE || size <= 0)
+ return -EFAULT;
+
+ if (os_mincore(psrc, size + src - psrc) <= 0)
+ return -EFAULT;
+
+ return __probe_kernel_read(dst, src, size);
+}
diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c
index b8f34c9e53ae..67b9c8f5a89e 100644
--- a/arch/um/os-Linux/process.c
+++ b/arch/um/os-Linux/process.c
@@ -4,6 +4,7 @@
*/
#include <stdio.h>
+#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
@@ -232,6 +233,57 @@ out:
return ok;
}
+static int os_page_mincore(void *addr)
+{
+ char vec[2];
+ int ret;
+
+ ret = mincore(addr, UM_KERN_PAGE_SIZE, vec);
+ if (ret < 0) {
+ if (errno == ENOMEM || errno == EINVAL)
+ return 0;
+ else
+ return -errno;
+ }
+
+ return vec[0] & 1;
+}
+
+int os_mincore(void *addr, unsigned long len)
+{
+ char *vec;
+ int ret, i;
+
+ if (len <= UM_KERN_PAGE_SIZE)
+ return os_page_mincore(addr);
+
+ vec = calloc(1, (len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE);
+ if (!vec)
+ return -ENOMEM;
+
+ ret = mincore(addr, UM_KERN_PAGE_SIZE, vec);
+ if (ret < 0) {
+ if (errno == ENOMEM || errno == EINVAL)
+ ret = 0;
+ else
+ ret = -errno;
+
+ goto out;
+ }
+
+ for (i = 0; i < ((len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); i++) {
+ if (!(vec[i] & 1)) {
+ ret = 0;
+ goto out;
+ }
+ }
+
+ ret = 1;
+out:
+ free(vec);
+ return ret;
+}
+
void init_new_thread_signals(void)
{
set_handler(SIGSEGV);
diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c
index cf1a471a18a2..10adb41f162e 100644
--- a/arch/x86/ia32/ia32_signal.c
+++ b/arch/x86/ia32/ia32_signal.c
@@ -459,7 +459,7 @@ int ia32_setup_rt_frame(int sig, struct ksignal *ksig,
else
put_user_ex(0, &frame->uc.uc_flags);
put_user_ex(0, &frame->uc.uc_link);
- err |= __compat_save_altstack(&frame->uc.uc_stack, regs->sp);
+ compat_save_altstack_ex(&frame->uc.uc_stack, regs->sp);
if (ksig->ka.sa.sa_flags & SA_RESTORER)
restorer = ksig->ka.sa.sa_restorer;
diff --git a/arch/x86/include/asm/checksum_32.h b/arch/x86/include/asm/checksum_32.h
index 46fc474fd819..f50de6951738 100644
--- a/arch/x86/include/asm/checksum_32.h
+++ b/arch/x86/include/asm/checksum_32.h
@@ -49,9 +49,15 @@ static inline __wsum csum_partial_copy_from_user(const void __user *src,
int len, __wsum sum,
int *err_ptr)
{
+ __wsum ret;
+
might_sleep();
- return csum_partial_copy_generic((__force void *)src, dst,
- len, sum, err_ptr, NULL);
+ stac();
+ ret = csum_partial_copy_generic((__force void *)src, dst,
+ len, sum, err_ptr, NULL);
+ clac();
+
+ return ret;
}
/*
@@ -176,10 +182,16 @@ static inline __wsum csum_and_copy_to_user(const void *src,
int len, __wsum sum,
int *err_ptr)
{
+ __wsum ret;
+
might_sleep();
- if (access_ok(VERIFY_WRITE, dst, len))
- return csum_partial_copy_generic(src, (__force void *)dst,
- len, sum, NULL, err_ptr);
+ if (access_ok(VERIFY_WRITE, dst, len)) {
+ stac();
+ ret = csum_partial_copy_generic(src, (__force void *)dst,
+ len, sum, NULL, err_ptr);
+ clac();
+ return ret;
+ }
if (len)
*err_ptr = -EFAULT;
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index e99ac27f95b2..4af181dacf9e 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -365,7 +365,7 @@ extern const char * const x86_power_flags[32];
static __always_inline __pure bool __static_cpu_has(u16 bit)
{
#if __GNUC__ > 4 || __GNUC_MINOR__ >= 5
- asm goto("1: jmp %l[t_no]\n"
+ asm_volatile_goto("1: jmp %l[t_no]\n"
"2:\n"
".section .altinstructions,\"a\"\n"
" .long 1b - .\n"
diff --git a/arch/x86/include/asm/e820.h b/arch/x86/include/asm/e820.h
index cccd07fa5e3a..779c2efe2e97 100644
--- a/arch/x86/include/asm/e820.h
+++ b/arch/x86/include/asm/e820.h
@@ -29,7 +29,7 @@ extern void e820_setup_gap(void);
extern int e820_search_gap(unsigned long *gapstart, unsigned long *gapsize,
unsigned long start_addr, unsigned long long end_addr);
struct setup_data;
-extern void parse_e820_ext(struct setup_data *data);
+extern void parse_e820_ext(u64 phys_addr, u32 data_len);
#if defined(CONFIG_X86_64) || \
(defined(CONFIG_X86_32) && defined(CONFIG_HIBERNATION))
diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h
index 3a16c1483b45..029766958e69 100644
--- a/arch/x86/include/asm/jump_label.h
+++ b/arch/x86/include/asm/jump_label.h
@@ -13,7 +13,7 @@
static __always_inline bool arch_static_branch(struct static_key *key)
{
- asm goto("1:"
+ asm_volatile_goto("1:"
STATIC_KEY_INITIAL_NOP
".pushsection __jump_table, \"aw\" \n\t"
_ASM_ALIGN "\n\t"
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h
index fa5f71e021d5..e6833c655e59 100644
--- a/arch/x86/include/asm/mce.h
+++ b/arch/x86/include/asm/mce.h
@@ -32,11 +32,20 @@
#define MCI_STATUS_PCC (1ULL<<57) /* processor context corrupt */
#define MCI_STATUS_S (1ULL<<56) /* Signaled machine check */
#define MCI_STATUS_AR (1ULL<<55) /* Action required */
-#define MCACOD 0xffff /* MCA Error Code */
+
+/*
+ * Note that the full MCACOD field of IA32_MCi_STATUS MSR is
+ * bits 15:0. But bit 12 is the 'F' bit, defined for corrected
+ * errors to indicate that errors are being filtered by hardware.
+ * We should mask out bit 12 when looking for specific signatures
+ * of uncorrected errors - so the F bit is deliberately skipped
+ * in this #define.
+ */
+#define MCACOD 0xefff /* MCA Error Code */
/* Architecturally defined codes from SDM Vol. 3B Chapter 15 */
#define MCACOD_SCRUB 0x00C0 /* 0xC0-0xCF Memory Scrubbing */
-#define MCACOD_SCRUBMSK 0xfff0
+#define MCACOD_SCRUBMSK 0xeff0 /* Skip bit 12 ('F' bit) */
#define MCACOD_L3WB 0x017A /* L3 Explicit Writeback */
#define MCACOD_DATA 0x0134 /* Data Load */
#define MCACOD_INSTR 0x0150 /* Instruction Fetch */
diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
index cdbf36776106..be12c534fd59 100644
--- a/arch/x86/include/asm/mmu_context.h
+++ b/arch/x86/include/asm/mmu_context.h
@@ -45,22 +45,28 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
/* Re-load page tables */
load_cr3(next->pgd);
- /* stop flush ipis for the previous mm */
+ /* Stop flush ipis for the previous mm */
cpumask_clear_cpu(cpu, mm_cpumask(prev));
- /*
- * load the LDT, if the LDT is different:
- */
+ /* Load the LDT, if the LDT is different: */
if (unlikely(prev->context.ldt != next->context.ldt))
load_LDT_nolock(&next->context);
}
#ifdef CONFIG_SMP
- else {
+ else {
this_cpu_write(cpu_tlbstate.state, TLBSTATE_OK);
BUG_ON(this_cpu_read(cpu_tlbstate.active_mm) != next);
- if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next))) {
- /* We were in lazy tlb mode and leave_mm disabled
+ if (!cpumask_test_cpu(cpu, mm_cpumask(next))) {
+ /*
+ * On established mms, the mm_cpumask is only changed
+ * from irq context, from ptep_clear_flush() while in
+ * lazy tlb mode, and here. Irqs are blocked during
+ * schedule, protecting us from simultaneous changes.
+ */
+ cpumask_set_cpu(cpu, mm_cpumask(next));
+ /*
+ * We were in lazy tlb mode and leave_mm disabled
* tlb flush IPI delivery. We must reload CR3
* to make sure to use no freed page tables.
*/
diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c
index 3048ded1b598..59554dca96ec 100644
--- a/arch/x86/kernel/amd_nb.c
+++ b/arch/x86/kernel/amd_nb.c
@@ -20,6 +20,7 @@ const struct pci_device_id amd_nb_misc_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
{}
};
@@ -27,6 +28,7 @@ EXPORT_SYMBOL(amd_nb_misc_ids);
static const struct pci_device_id amd_nb_link_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) },
{}
};
@@ -81,13 +83,20 @@ int amd_cache_northbridges(void)
next_northbridge(misc, amd_nb_misc_ids);
node_to_amd_nb(i)->link = link =
next_northbridge(link, amd_nb_link_ids);
- }
+ }
+ /* GART present only on Fam15h upto model 0fh */
if (boot_cpu_data.x86 == 0xf || boot_cpu_data.x86 == 0x10 ||
- boot_cpu_data.x86 == 0x15)
+ (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model < 0x10))
amd_northbridges.flags |= AMD_NB_GART;
/*
+ * Check for L3 cache presence.
+ */
+ if (!cpuid_edx(0x80000006))
+ return 0;
+
+ /*
* Some CPU families support L3 Cache Index Disable. There are some
* limitations because of E382 and E388 on family 0x10.
*/
diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c
index 794f6eb54cd3..b32dbb411a9a 100644
--- a/arch/x86/kernel/apic/x2apic_uv_x.c
+++ b/arch/x86/kernel/apic/x2apic_uv_x.c
@@ -98,7 +98,7 @@ static int __init early_get_pnodeid(void)
break;
case UV3_HUB_PART_NUMBER:
case UV3_HUB_PART_NUMBER_X:
- uv_min_hub_revision_id += UV3_HUB_REVISION_BASE - 1;
+ uv_min_hub_revision_id += UV3_HUB_REVISION_BASE;
break;
}
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
index d32abeabbda5..174da5fc5a7b 100644
--- a/arch/x86/kernel/e820.c
+++ b/arch/x86/kernel/e820.c
@@ -658,15 +658,18 @@ __init void e820_setup_gap(void)
* boot_params.e820_map, others are passed via SETUP_E820_EXT node of
* linked list of struct setup_data, which is parsed here.
*/
-void __init parse_e820_ext(struct setup_data *sdata)
+void __init parse_e820_ext(u64 phys_addr, u32 data_len)
{
int entries;
struct e820entry *extmap;
+ struct setup_data *sdata;
+ sdata = early_memremap(phys_addr, data_len);
entries = sdata->len / sizeof(struct e820entry);
extmap = (struct e820entry *)(sdata->data);
__append_e820_map(extmap, entries);
sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map);
+ early_iounmap(sdata, data_len);
printk(KERN_INFO "e820: extended physical RAM map:\n");
e820_print_map("extended");
}
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index 76fa1e9a2b39..90fd1195f276 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -447,6 +447,22 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Precision M6600"),
},
},
+ { /* Handle problems with rebooting on the Dell PowerEdge C6100. */
+ .callback = set_pci_reboot,
+ .ident = "Dell PowerEdge C6100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "C6100"),
+ },
+ },
+ { /* Some C6100 machines were shipped with vendor being 'Dell'. */
+ .callback = set_pci_reboot,
+ .ident = "Dell PowerEdge C6100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "C6100"),
+ },
+ },
{ }
};
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 56f7fcfe7fa2..91964c663f0d 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -426,25 +426,23 @@ static void __init reserve_initrd(void)
static void __init parse_setup_data(void)
{
struct setup_data *data;
- u64 pa_data;
+ u64 pa_data, pa_next;
pa_data = boot_params.hdr.setup_data;
while (pa_data) {
- u32 data_len, map_len;
+ u32 data_len, map_len, data_type;
map_len = max(PAGE_SIZE - (pa_data & ~PAGE_MASK),
(u64)sizeof(struct setup_data));
data = early_memremap(pa_data, map_len);
data_len = data->len + sizeof(struct setup_data);
- if (data_len > map_len) {
- early_iounmap(data, map_len);
- data = early_memremap(pa_data, data_len);
- map_len = data_len;
- }
+ data_type = data->type;
+ pa_next = data->next;
+ early_iounmap(data, map_len);
- switch (data->type) {
+ switch (data_type) {
case SETUP_E820_EXT:
- parse_e820_ext(data);
+ parse_e820_ext(pa_data, data_len);
break;
case SETUP_DTB:
add_dtb(pa_data);
@@ -452,8 +450,7 @@ static void __init parse_setup_data(void)
default:
break;
}
- pa_data = data->next;
- early_iounmap(data, map_len);
+ pa_data = pa_next;
}
}
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 69562992e457..087ab2af381a 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -364,7 +364,7 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig,
else
put_user_ex(0, &frame->uc.uc_flags);
put_user_ex(0, &frame->uc.uc_link);
- err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
+ save_altstack_ex(&frame->uc.uc_stack, regs->sp);
/* Set up to return from userspace. */
restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn);
@@ -429,7 +429,7 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig,
else
put_user_ex(0, &frame->uc.uc_flags);
put_user_ex(0, &frame->uc.uc_link);
- err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
+ save_altstack_ex(&frame->uc.uc_stack, regs->sp);
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
@@ -496,7 +496,7 @@ static int x32_setup_rt_frame(struct ksignal *ksig,
else
put_user_ex(0, &frame->uc.uc_flags);
put_user_ex(0, &frame->uc.uc_link);
- err |= __compat_save_altstack(&frame->uc.uc_stack, regs->sp);
+ compat_save_altstack_ex(&frame->uc.uc_stack, regs->sp);
put_user_ex(0, &frame->uc.uc__pad0);
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
diff --git a/arch/x86/lib/csum-wrappers_64.c b/arch/x86/lib/csum-wrappers_64.c
index 25b7ae8d058a..7609e0e421ec 100644
--- a/arch/x86/lib/csum-wrappers_64.c
+++ b/arch/x86/lib/csum-wrappers_64.c
@@ -6,6 +6,7 @@
*/
#include <asm/checksum.h>
#include <linux/module.h>
+#include <asm/smap.h>
/**
* csum_partial_copy_from_user - Copy and checksum from user space.
@@ -52,8 +53,10 @@ csum_partial_copy_from_user(const void __user *src, void *dst,
len -= 2;
}
}
+ stac();
isum = csum_partial_copy_generic((__force const void *)src,
dst, len, isum, errp, NULL);
+ clac();
if (unlikely(*errp))
goto out_err;
@@ -82,6 +85,8 @@ __wsum
csum_partial_copy_to_user(const void *src, void __user *dst,
int len, __wsum isum, int *errp)
{
+ __wsum ret;
+
might_sleep();
if (unlikely(!access_ok(VERIFY_WRITE, dst, len))) {
@@ -105,8 +110,11 @@ csum_partial_copy_to_user(const void *src, void __user *dst,
}
*errp = 0;
- return csum_partial_copy_generic(src, (void __force *)dst,
- len, isum, NULL, errp);
+ stac();
+ ret = csum_partial_copy_generic(src, (void __force *)dst,
+ len, isum, NULL, errp);
+ clac();
+ return ret;
}
EXPORT_SYMBOL(csum_partial_copy_to_user);
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index d2fbcedcf6ea..91ac654ff4ea 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -910,10 +910,13 @@ void __init efi_enter_virtual_mode(void)
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
md = p;
- if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
- md->type != EFI_BOOT_SERVICES_CODE &&
- md->type != EFI_BOOT_SERVICES_DATA)
- continue;
+ if (!(md->attribute & EFI_MEMORY_RUNTIME)) {
+#ifdef CONFIG_X86_64
+ if (md->type != EFI_BOOT_SERVICES_CODE &&
+ md->type != EFI_BOOT_SERVICES_DATA)
+#endif
+ continue;
+ }
size = md->num_pages << EFI_PAGE_SHIFT;
end = md->phys_addr + size;
diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c
index 718eca1850bd..98b67d5f1514 100644
--- a/arch/xtensa/kernel/signal.c
+++ b/arch/xtensa/kernel/signal.c
@@ -341,7 +341,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
sp = regs->areg[1];
- if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) {
+ if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && sas_ss_flags(sp) == 0) {
sp = current->sas_ss_sp + current->sas_ss_size;
}
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index d5bbdcfd0dab..c410752c5c65 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -1803,7 +1803,7 @@ static u64 cfqg_prfill_avg_queue_size(struct seq_file *sf,
if (samples) {
v = blkg_stat_read(&cfqg->stats.avg_queue_size_sum);
- do_div(v, samples);
+ v = div64_u64(v, samples);
}
__blkg_prfill_u64(sf, pd, v);
return 0;
diff --git a/crypto/api.c b/crypto/api.c
index 3b6180336d3d..37c4c7213de0 100644
--- a/crypto/api.c
+++ b/crypto/api.c
@@ -34,6 +34,8 @@ EXPORT_SYMBOL_GPL(crypto_alg_sem);
BLOCKING_NOTIFIER_HEAD(crypto_chain);
EXPORT_SYMBOL_GPL(crypto_chain);
+static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg);
+
struct crypto_alg *crypto_mod_get(struct crypto_alg *alg)
{
return try_module_get(alg->cra_module) ? crypto_alg_get(alg) : NULL;
@@ -144,8 +146,11 @@ static struct crypto_alg *crypto_larval_add(const char *name, u32 type,
}
up_write(&crypto_alg_sem);
- if (alg != &larval->alg)
+ if (alg != &larval->alg) {
kfree(larval);
+ if (crypto_is_larval(alg))
+ alg = crypto_larval_wait(alg);
+ }
return alg;
}
diff --git a/drivers/Kconfig b/drivers/Kconfig
index e40c5e6d541e..ba3c789ad9b8 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -168,4 +168,6 @@ source "drivers/ipack/Kconfig"
source "drivers/reset/Kconfig"
+source "drivers/gator/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 2bb844b79a69..1c92047b81bd 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -153,3 +153,5 @@ obj-$(CONFIG_IIO) += iio/
obj-$(CONFIG_VME_BUS) += vme/
obj-$(CONFIG_IPACK_BUS) += ipack/
obj-$(CONFIG_NTB) += ntb/
+
+obj-$(CONFIG_GATOR) += gator/
diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c
index f40acef80269..a6977e12d574 100644
--- a/drivers/acpi/acpi_ipmi.c
+++ b/drivers/acpi/acpi_ipmi.c
@@ -39,6 +39,7 @@
#include <linux/ipmi.h>
#include <linux/device.h>
#include <linux/pnp.h>
+#include <linux/spinlock.h>
MODULE_AUTHOR("Zhao Yakui");
MODULE_DESCRIPTION("ACPI IPMI Opregion driver");
@@ -57,7 +58,7 @@ struct acpi_ipmi_device {
struct list_head head;
/* the IPMI request message list */
struct list_head tx_msg_list;
- struct mutex tx_msg_lock;
+ spinlock_t tx_msg_lock;
acpi_handle handle;
struct pnp_dev *pnp_dev;
ipmi_user_t user_interface;
@@ -147,6 +148,7 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg,
struct kernel_ipmi_msg *msg;
struct acpi_ipmi_buffer *buffer;
struct acpi_ipmi_device *device;
+ unsigned long flags;
msg = &tx_msg->tx_message;
/*
@@ -177,10 +179,10 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg,
/* Get the msgid */
device = tx_msg->device;
- mutex_lock(&device->tx_msg_lock);
+ spin_lock_irqsave(&device->tx_msg_lock, flags);
device->curr_msgid++;
tx_msg->tx_msgid = device->curr_msgid;
- mutex_unlock(&device->tx_msg_lock);
+ spin_unlock_irqrestore(&device->tx_msg_lock, flags);
}
static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
@@ -242,6 +244,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
int msg_found = 0;
struct acpi_ipmi_msg *tx_msg;
struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
+ unsigned long flags;
if (msg->user != ipmi_device->user_interface) {
dev_warn(&pnp_dev->dev, "Unexpected response is returned. "
@@ -250,7 +253,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
ipmi_free_recv_msg(msg);
return;
}
- mutex_lock(&ipmi_device->tx_msg_lock);
+ spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) {
if (msg->msgid == tx_msg->tx_msgid) {
msg_found = 1;
@@ -258,7 +261,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
}
}
- mutex_unlock(&ipmi_device->tx_msg_lock);
+ spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
if (!msg_found) {
dev_warn(&pnp_dev->dev, "Unexpected response (msg id %ld) is "
"returned.\n", msg->msgid);
@@ -378,6 +381,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
struct acpi_ipmi_device *ipmi_device = handler_context;
int err, rem_time;
acpi_status status;
+ unsigned long flags;
/*
* IPMI opregion message.
* IPMI message is firstly written to the BMC and system software
@@ -395,9 +399,9 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
return AE_NO_MEMORY;
acpi_format_ipmi_msg(tx_msg, address, value);
- mutex_lock(&ipmi_device->tx_msg_lock);
+ spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
- mutex_unlock(&ipmi_device->tx_msg_lock);
+ spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
err = ipmi_request_settime(ipmi_device->user_interface,
&tx_msg->addr,
tx_msg->tx_msgid,
@@ -413,9 +417,9 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
status = AE_OK;
end_label:
- mutex_lock(&ipmi_device->tx_msg_lock);
+ spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
list_del(&tx_msg->head);
- mutex_unlock(&ipmi_device->tx_msg_lock);
+ spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
kfree(tx_msg);
return status;
}
@@ -457,7 +461,7 @@ static void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device)
INIT_LIST_HEAD(&ipmi_device->head);
- mutex_init(&ipmi_device->tx_msg_lock);
+ spin_lock_init(&ipmi_device->tx_msg_lock);
INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
ipmi_install_space_handler(ipmi_device);
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index cab13f2fc28e..7c451cb26254 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -155,12 +155,13 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
pdata->mmio_size = resource_size(&rentry->res);
pdata->mmio_base = ioremap(rentry->res.start,
pdata->mmio_size);
- pdata->dev_desc = dev_desc;
break;
}
acpi_dev_free_resource_list(&resource_list);
+ pdata->dev_desc = dev_desc;
+
if (dev_desc->clk_required) {
ret = register_device_clock(adev, pdata);
if (ret) {
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index c69fcce505c0..370462fa8e01 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1322,14 +1322,14 @@ void ata_eh_qc_complete(struct ata_queued_cmd *qc)
* should be retried. To be used from EH.
*
* SCSI midlayer limits the number of retries to scmd->allowed.
- * scmd->retries is decremented for commands which get retried
+ * scmd->allowed is incremented for commands which get retried
* due to unrelated failures (qc->err_mask is zero).
*/
void ata_eh_qc_retry(struct ata_queued_cmd *qc)
{
struct scsi_cmnd *scmd = qc->scsicmd;
- if (!qc->err_mask && scmd->retries)
- scmd->retries--;
+ if (!qc->err_mask)
+ scmd->allowed++;
__ata_eh_qc_complete(qc);
}
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 2499cefdcdf2..ca4bcb8b3938 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1839,7 +1839,7 @@ EXPORT_SYMBOL_GPL(device_move);
*/
void device_shutdown(void)
{
- struct device *dev;
+ struct device *dev, *parent;
spin_lock(&devices_kset->list_lock);
/*
@@ -1856,7 +1856,7 @@ void device_shutdown(void)
* prevent it from being freed because parent's
* lock is to be held
*/
- get_device(dev->parent);
+ parent = get_device(dev->parent);
get_device(dev);
/*
* Make sure the device is off the kset list, in the
@@ -1866,8 +1866,8 @@ void device_shutdown(void)
spin_unlock(&devices_kset->list_lock);
/* hold lock to avoid race with probe/release */
- if (dev->parent)
- device_lock(dev->parent);
+ if (parent)
+ device_lock(parent);
device_lock(dev);
/* Don't allow any more runtime suspends */
@@ -1885,11 +1885,11 @@ void device_shutdown(void)
}
device_unlock(dev);
- if (dev->parent)
- device_unlock(dev->parent);
+ if (parent)
+ device_unlock(parent);
put_device(dev);
- put_device(dev->parent);
+ put_device(parent);
spin_lock(&devices_kset->list_lock);
}
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 62b6c2cc80b5..90a4e6b940ac 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -1189,6 +1189,7 @@ static int cciss_ioctl32_passthru(struct block_device *bdev, fmode_t mode,
int err;
u32 cp;
+ memset(&arg64, 0, sizeof(arg64));
err = 0;
err |=
copy_from_user(&arg64.LUN_info, &arg32->LUN_info,
diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
index 639d26b90b91..2b9440384536 100644
--- a/drivers/block/cpqarray.c
+++ b/drivers/block/cpqarray.c
@@ -1193,6 +1193,7 @@ out_passthru:
ida_pci_info_struct pciinfo;
if (!arg) return -EINVAL;
+ memset(&pciinfo, 0, sizeof(pciinfo));
pciinfo.bus = host->pci_dev->bus->number;
pciinfo.dev_fn = host->pci_dev->devfn;
pciinfo.board_id = host->board_id;
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index aff789d6fccd..8c7421af8f15 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -1565,11 +1565,12 @@ rbd_img_obj_request_read_callback(struct rbd_obj_request *obj_request)
obj_request, obj_request->img_request, obj_request->result,
xferred, length);
/*
- * ENOENT means a hole in the image. We zero-fill the
- * entire length of the request. A short read also implies
- * zero-fill to the end of the request. Either way we
- * update the xferred count to indicate the whole request
- * was satisfied.
+ * ENOENT means a hole in the image. We zero-fill the entire
+ * length of the request. A short read also implies zero-fill
+ * to the end of the request. An error requires the whole
+ * length of the request to be reported finished with an error
+ * to the block layer. In each case we update the xferred
+ * count to indicate the whole request was satisfied.
*/
rbd_assert(obj_request->type != OBJ_REQUEST_NODATA);
if (obj_request->result == -ENOENT) {
@@ -1578,14 +1579,13 @@ rbd_img_obj_request_read_callback(struct rbd_obj_request *obj_request)
else
zero_pages(obj_request->pages, 0, length);
obj_request->result = 0;
- obj_request->xferred = length;
} else if (xferred < length && !obj_request->result) {
if (obj_request->type == OBJ_REQUEST_BIO)
zero_bio_chain(obj_request->bio_list, xferred);
else
zero_pages(obj_request->pages, xferred, length);
- obj_request->xferred = length;
}
+ obj_request->xferred = length;
obj_request_done_set(obj_request);
}
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index a12b923bbaca..0a327f4154a2 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -85,6 +85,7 @@ static struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x04CA, 0x3008) },
{ USB_DEVICE(0x13d3, 0x3362) },
{ USB_DEVICE(0x0CF3, 0xE004) },
+ { USB_DEVICE(0x0CF3, 0xE005) },
{ USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0489, 0xe057) },
{ USB_DEVICE(0x13d3, 0x3393) },
@@ -126,6 +127,7 @@ static struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index d0b3d900d452..58491f1b2799 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -102,6 +102,7 @@ static struct usb_device_id btusb_table[] = {
/* Broadcom BCM20702A0 */
{ USB_DEVICE(0x0b05, 0x17b5) },
+ { USB_DEVICE(0x0b05, 0x17cb) },
{ USB_DEVICE(0x04ca, 0x2003) },
{ USB_DEVICE(0x0489, 0xe042) },
{ USB_DEVICE(0x413c, 0x8197) },
@@ -148,6 +149,7 @@ static struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 35487e8ded59..81eefa1c0d3f 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1462,12 +1462,11 @@ ctl_table random_table[] = {
static u32 random_int_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned;
-static int __init random_int_secret_init(void)
+int random_int_secret_init(void)
{
get_random_bytes(random_int_secret, sizeof(random_int_secret));
return 0;
}
-late_initcall(random_int_secret_init);
/*
* Get a random word for internal kernel use only. Similar to urandom but
diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c
index 16ed06808554..917a3ab482f9 100644
--- a/drivers/clk/clk-wm831x.c
+++ b/drivers/clk/clk-wm831x.c
@@ -360,6 +360,8 @@ static int wm831x_clk_probe(struct platform_device *pdev)
if (!clkdata)
return -ENOMEM;
+ clkdata->wm831x = wm831x;
+
/* XTAL_ENA can only be set via OTP/InstantConfig so just read once */
ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
if (ret < 0) {
diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c
index 67ccf4aa7277..f5e4c21b301f 100644
--- a/drivers/clk/versatile/clk-icst.c
+++ b/drivers/clk/versatile/clk-icst.c
@@ -107,7 +107,7 @@ static int icst_set_rate(struct clk_hw *hw, unsigned long rate,
vco = icst_hz_to_vco(icst->params, rate);
icst->rate = icst_hz(icst->params, vco);
- vco_set(icst->vcoreg, icst->lockreg, vco);
+ vco_set(icst->lockreg, icst->vcoreg, vco);
return 0;
}
diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c
index 08ae128cce9b..c73fc2b74de2 100644
--- a/drivers/connector/cn_proc.c
+++ b/drivers/connector/cn_proc.c
@@ -65,6 +65,7 @@ void proc_fork_connector(struct task_struct *task)
msg = (struct cn_msg *)buffer;
ev = (struct proc_event *)msg->data;
+ memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ktime_get_ts(&ts); /* get high res monotonic timestamp */
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -80,6 +81,7 @@ void proc_fork_connector(struct task_struct *task)
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */
msg->len = sizeof(*ev);
+ msg->flags = 0; /* not used */
/* If cn_netlink_send() failed, the data is not sent */
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
}
@@ -96,6 +98,7 @@ void proc_exec_connector(struct task_struct *task)
msg = (struct cn_msg *)buffer;
ev = (struct proc_event *)msg->data;
+ memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ktime_get_ts(&ts); /* get high res monotonic timestamp */
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -106,6 +109,7 @@ void proc_exec_connector(struct task_struct *task)
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */
msg->len = sizeof(*ev);
+ msg->flags = 0; /* not used */
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
}
@@ -122,6 +126,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
msg = (struct cn_msg *)buffer;
ev = (struct proc_event *)msg->data;
+ memset(&ev->event_data, 0, sizeof(ev->event_data));
ev->what = which_id;
ev->event_data.id.process_pid = task->pid;
ev->event_data.id.process_tgid = task->tgid;
@@ -145,6 +150,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */
msg->len = sizeof(*ev);
+ msg->flags = 0; /* not used */
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
}
@@ -160,6 +166,7 @@ void proc_sid_connector(struct task_struct *task)
msg = (struct cn_msg *)buffer;
ev = (struct proc_event *)msg->data;
+ memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ktime_get_ts(&ts); /* get high res monotonic timestamp */
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -170,6 +177,7 @@ void proc_sid_connector(struct task_struct *task)
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */
msg->len = sizeof(*ev);
+ msg->flags = 0; /* not used */
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
}
@@ -185,6 +193,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
msg = (struct cn_msg *)buffer;
ev = (struct proc_event *)msg->data;
+ memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ktime_get_ts(&ts); /* get high res monotonic timestamp */
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -203,6 +212,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */
msg->len = sizeof(*ev);
+ msg->flags = 0; /* not used */
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
}
@@ -218,6 +228,7 @@ void proc_comm_connector(struct task_struct *task)
msg = (struct cn_msg *)buffer;
ev = (struct proc_event *)msg->data;
+ memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ktime_get_ts(&ts); /* get high res monotonic timestamp */
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -229,6 +240,7 @@ void proc_comm_connector(struct task_struct *task)
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */
msg->len = sizeof(*ev);
+ msg->flags = 0; /* not used */
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
}
@@ -244,6 +256,7 @@ void proc_coredump_connector(struct task_struct *task)
msg = (struct cn_msg *)buffer;
ev = (struct proc_event *)msg->data;
+ memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ktime_get_ts(&ts); /* get high res monotonic timestamp */
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -254,6 +267,7 @@ void proc_coredump_connector(struct task_struct *task)
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */
msg->len = sizeof(*ev);
+ msg->flags = 0; /* not used */
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
}
@@ -269,6 +283,7 @@ void proc_exit_connector(struct task_struct *task)
msg = (struct cn_msg *)buffer;
ev = (struct proc_event *)msg->data;
+ memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ktime_get_ts(&ts); /* get high res monotonic timestamp */
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -281,6 +296,7 @@ void proc_exit_connector(struct task_struct *task)
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */
msg->len = sizeof(*ev);
+ msg->flags = 0; /* not used */
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
}
@@ -304,6 +320,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
msg = (struct cn_msg *)buffer;
ev = (struct proc_event *)msg->data;
+ memset(&ev->event_data, 0, sizeof(ev->event_data));
msg->seq = rcvd_seq;
ktime_get_ts(&ts); /* get high res monotonic timestamp */
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -313,6 +330,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = rcvd_ack + 1;
msg->len = sizeof(*ev);
+ msg->flags = 0; /* not used */
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
}
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index 6ecfa758942c..0daa11e418b1 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -157,17 +157,18 @@ static int cn_call_callback(struct sk_buff *skb)
static void cn_rx_skb(struct sk_buff *__skb)
{
struct nlmsghdr *nlh;
- int err;
struct sk_buff *skb;
+ int len, err;
skb = skb_get(__skb);
if (skb->len >= NLMSG_HDRLEN) {
nlh = nlmsg_hdr(skb);
+ len = nlmsg_len(nlh);
- if (nlh->nlmsg_len < sizeof(struct cn_msg) ||
+ if (len < (int)sizeof(struct cn_msg) ||
skb->len < nlh->nlmsg_len ||
- nlh->nlmsg_len > CONNECTOR_MAX_MSG_SIZE) {
+ len > CONNECTOR_MAX_MSG_SIZE) {
kfree_skb(skb);
return;
}
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index a494fbd7dbe8..12376ae07de8 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -61,6 +61,7 @@ static cpumask_t speedchange_cpumask;
static spinlock_t speedchange_cpumask_lock;
static struct mutex gov_lock;
+/* Target load. Lower values result in higher CPU speeds. */
#define DEFAULT_TARGET_LOAD 90
static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD};
@@ -71,31 +72,25 @@ static unsigned int default_above_hispeed_delay[] = {
struct cpufreq_interactive_tunables {
int usage_count;
-
/* Hi speed to bump to from lo speed when load burst (default max) */
unsigned int hispeed_freq;
-
/* Go to hi speed when CPU load at or above this value. */
#define DEFAULT_GO_HISPEED_LOAD 99
unsigned long go_hispeed_load;
-
/* Target load. Lower values result in higher CPU speeds. */
spinlock_t target_loads_lock;
unsigned int *target_loads;
int ntarget_loads;
-
/*
* The minimum amount of time to spend at a frequency before we can ramp
* down.
*/
#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC)
unsigned long min_sample_time;
-
/*
* The sample rate of the timer used to increase frequency
*/
unsigned long timer_rate;
-
/*
* Wait this long before raising speed above hispeed, by default a
* single timer interval.
@@ -103,21 +98,18 @@ struct cpufreq_interactive_tunables {
spinlock_t above_hispeed_delay_lock;
unsigned int *above_hispeed_delay;
int nabove_hispeed_delay;
-
/* Non-zero means indefinite speed boost active */
int boost_val;
- /* Duration of a boost pulse in usecs */
+ /* Duration of a boot pulse in usecs */
int boostpulse_duration_val;
/* End time of boost pulse in ktime converted to usecs */
u64 boostpulse_endtime;
-
/*
* Max additional time to wait in idle, beyond timer_rate, at speeds
* above minimum before wakeup to reduce speed, or -1 if unnecessary.
*/
#define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE)
int timer_slack_val;
-
bool io_is_busy;
};
@@ -137,15 +129,15 @@ static void cpufreq_interactive_timer_resched(
spin_lock_irqsave(&pcpu->load_lock, flags);
pcpu->time_in_idle =
get_cpu_idle_time(smp_processor_id(),
- &pcpu->time_in_idle_timestamp,
- tunables->io_is_busy);
+ &pcpu->time_in_idle_timestamp,
+ tunables->io_is_busy);
pcpu->cputime_speedadj = 0;
pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp;
expires = jiffies + usecs_to_jiffies(tunables->timer_rate);
mod_timer_pinned(&pcpu->cpu_timer, expires);
if (tunables->timer_slack_val >= 0 &&
- pcpu->target_freq > pcpu->policy->min) {
+ pcpu->target_freq > pcpu->policy->min) {
expires += usecs_to_jiffies(tunables->timer_slack_val);
mod_timer_pinned(&pcpu->cpu_slack_timer, expires);
}
@@ -158,16 +150,17 @@ static void cpufreq_interactive_timer_resched(
* function.
*/
static void cpufreq_interactive_timer_start(
- struct cpufreq_interactive_tunables *tunables, int cpu)
+ struct cpufreq_interactive_tunables *tunables, int cpu)
{
struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
- unsigned long expires = jiffies + usecs_to_jiffies(tunables->timer_rate);
+ unsigned long expires = jiffies +
+ usecs_to_jiffies(tunables->timer_rate);
unsigned long flags;
pcpu->cpu_timer.expires = expires;
add_timer_on(&pcpu->cpu_timer, cpu);
if (tunables->timer_slack_val >= 0 &&
- pcpu->target_freq > pcpu->policy->min) {
+ pcpu->target_freq > pcpu->policy->min) {
expires += usecs_to_jiffies(tunables->timer_slack_val);
pcpu->cpu_slack_timer.expires = expires;
add_timer_on(&pcpu->cpu_slack_timer, cpu);
@@ -183,8 +176,8 @@ static void cpufreq_interactive_timer_start(
}
static unsigned int freq_to_above_hispeed_delay(
- struct cpufreq_interactive_tunables *tunables,
- unsigned int freq)
+ struct cpufreq_interactive_tunables *tunables,
+ unsigned int freq)
{
int i;
unsigned int ret;
@@ -201,8 +194,8 @@ static unsigned int freq_to_above_hispeed_delay(
return ret;
}
-static unsigned int freq_to_targetload(struct cpufreq_interactive_tunables
- *tunables, unsigned int freq)
+static unsigned int freq_to_targetload(
+ struct cpufreq_interactive_tunables *tunables, unsigned int freq)
{
int i;
unsigned int ret;
@@ -211,7 +204,7 @@ static unsigned int freq_to_targetload(struct cpufreq_interactive_tunables
spin_lock_irqsave(&tunables->target_loads_lock, flags);
for (i = 0; i < tunables->ntarget_loads - 1 &&
- freq >= tunables->target_loads[i+1]; i += 2)
+ freq >= tunables->target_loads[i+1]; i += 2)
;
ret = tunables->target_loads[i];
@@ -706,8 +699,9 @@ err:
return ERR_PTR(err);
}
-static ssize_t show_target_loads(struct cpufreq_interactive_tunables *tunables,
- char *buf)
+static ssize_t show_target_loads(
+ struct cpufreq_interactive_tunables *tunables,
+ char *buf)
{
int i;
ssize_t ret = 0;
@@ -719,13 +713,14 @@ static ssize_t show_target_loads(struct cpufreq_interactive_tunables *tunables,
ret += sprintf(buf + ret, "%u%s", tunables->target_loads[i],
i & 0x1 ? ":" : " ");
- ret += sprintf(buf + ret, "\n");
+ ret += sprintf(buf + --ret, "\n");
spin_unlock_irqrestore(&tunables->target_loads_lock, flags);
return ret;
}
-static ssize_t store_target_loads(struct cpufreq_interactive_tunables *tunables,
- const char *buf, size_t count)
+static ssize_t store_target_loads(
+ struct cpufreq_interactive_tunables *tunables,
+ const char *buf, size_t count)
{
int ntokens;
unsigned int *new_target_loads = NULL;
@@ -744,8 +739,8 @@ static ssize_t store_target_loads(struct cpufreq_interactive_tunables *tunables,
return count;
}
-static ssize_t show_above_hispeed_delay(struct cpufreq_interactive_tunables
- *tunables, char *buf)
+static ssize_t show_above_hispeed_delay(
+ struct cpufreq_interactive_tunables *tunables, char *buf)
{
int i;
ssize_t ret = 0;
@@ -758,13 +753,14 @@ static ssize_t show_above_hispeed_delay(struct cpufreq_interactive_tunables
tunables->above_hispeed_delay[i],
i & 0x1 ? ":" : " ");
- ret += sprintf(buf + ret, "\n");
+ ret += sprintf(buf + --ret, "\n");
spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags);
return ret;
}
-static ssize_t store_above_hispeed_delay(struct cpufreq_interactive_tunables
- *tunables, const char *buf, size_t count)
+static ssize_t store_above_hispeed_delay(
+ struct cpufreq_interactive_tunables *tunables,
+ const char *buf, size_t count)
{
int ntokens;
unsigned int *new_above_hispeed_delay = NULL;
@@ -1012,6 +1008,18 @@ store_gov_pol_sys(boostpulse);
show_store_gov_pol_sys(boostpulse_duration);
show_store_gov_pol_sys(io_is_busy);
+#define gov_sys_attr_rw(_name) \
+static struct global_attr _name##_gov_sys = \
+__ATTR(_name, 0644, show_##_name##_gov_sys, store_##_name##_gov_sys)
+
+#define gov_pol_attr_rw(_name) \
+static struct freq_attr _name##_gov_pol = \
+__ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol)
+
+#define gov_sys_pol_attr_rw(_name) \
+ gov_sys_attr_rw(_name); \
+ gov_pol_attr_rw(_name)
+
gov_sys_pol_attr_rw(target_loads);
gov_sys_pol_attr_rw(above_hispeed_delay);
gov_sys_pol_attr_rw(hispeed_freq);
@@ -1200,6 +1208,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
pcpu->hispeed_validate_time =
pcpu->floor_validate_time;
down_write(&pcpu->enable_sem);
+ del_timer_sync(&pcpu->cpu_timer);
+ del_timer_sync(&pcpu->cpu_slack_timer);
cpufreq_interactive_timer_start(tunables, j);
pcpu->governor_enabled = 1;
up_write(&pcpu->enable_sem);
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 6d6a0b48eb75..9520e3b90bde 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -629,8 +629,8 @@ static int __cpuinit intel_pstate_cpu_exit(struct cpufreq_policy *policy)
static int __cpuinit intel_pstate_cpu_init(struct cpufreq_policy *policy)
{
- int rc, min_pstate, max_pstate;
struct cpudata *cpu;
+ int rc;
rc = intel_pstate_init_cpu(policy->cpu);
if (rc)
@@ -644,9 +644,8 @@ static int __cpuinit intel_pstate_cpu_init(struct cpufreq_policy *policy)
else
policy->policy = CPUFREQ_POLICY_POWERSAVE;
- intel_pstate_get_min_max(cpu, &min_pstate, &max_pstate);
- policy->min = min_pstate * 100000;
- policy->max = max_pstate * 100000;
+ policy->min = cpu->pstate.min_pstate * 100000;
+ policy->max = cpu->pstate.turbo_pstate * 100000;
/* cpuinfo and default policy values */
policy->cpuinfo.min_freq = cpu->pstate.min_pstate * 100000;
diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c
index 2a297f86dbad..fe853903fe10 100644
--- a/drivers/cpuidle/coupled.c
+++ b/drivers/cpuidle/coupled.c
@@ -106,6 +106,7 @@ struct cpuidle_coupled {
cpumask_t coupled_cpus;
int requested_state[NR_CPUS];
atomic_t ready_waiting_counts;
+ atomic_t abort_barrier;
int online_count;
int refcnt;
int prevent;
@@ -122,12 +123,19 @@ static DEFINE_MUTEX(cpuidle_coupled_lock);
static DEFINE_PER_CPU(struct call_single_data, cpuidle_coupled_poke_cb);
/*
- * The cpuidle_coupled_poked_mask mask is used to avoid calling
+ * The cpuidle_coupled_poke_pending mask is used to avoid calling
* __smp_call_function_single with the per cpu call_single_data struct already
* in use. This prevents a deadlock where two cpus are waiting for each others
* call_single_data struct to be available
*/
-static cpumask_t cpuidle_coupled_poked_mask;
+static cpumask_t cpuidle_coupled_poke_pending;
+
+/*
+ * The cpuidle_coupled_poked mask is used to ensure that each cpu has been poked
+ * once to minimize entering the ready loop with a poke pending, which would
+ * require aborting and retrying.
+ */
+static cpumask_t cpuidle_coupled_poked;
/**
* cpuidle_coupled_parallel_barrier - synchronize all online coupled cpus
@@ -291,10 +299,11 @@ static inline int cpuidle_coupled_get_state(struct cpuidle_device *dev,
return state;
}
-static void cpuidle_coupled_poked(void *info)
+static void cpuidle_coupled_handle_poke(void *info)
{
int cpu = (unsigned long)info;
- cpumask_clear_cpu(cpu, &cpuidle_coupled_poked_mask);
+ cpumask_set_cpu(cpu, &cpuidle_coupled_poked);
+ cpumask_clear_cpu(cpu, &cpuidle_coupled_poke_pending);
}
/**
@@ -313,7 +322,7 @@ static void cpuidle_coupled_poke(int cpu)
{
struct call_single_data *csd = &per_cpu(cpuidle_coupled_poke_cb, cpu);
- if (!cpumask_test_and_set_cpu(cpu, &cpuidle_coupled_poked_mask))
+ if (!cpumask_test_and_set_cpu(cpu, &cpuidle_coupled_poke_pending))
__smp_call_function_single(cpu, csd, 0);
}
@@ -340,30 +349,19 @@ static void cpuidle_coupled_poke_others(int this_cpu,
* @coupled: the struct coupled that contains the current cpu
* @next_state: the index in drv->states of the requested state for this cpu
*
- * Updates the requested idle state for the specified cpuidle device,
- * poking all coupled cpus out of idle if necessary to let them see the new
- * state.
+ * Updates the requested idle state for the specified cpuidle device.
+ * Returns the number of waiting cpus.
*/
-static void cpuidle_coupled_set_waiting(int cpu,
+static int cpuidle_coupled_set_waiting(int cpu,
struct cpuidle_coupled *coupled, int next_state)
{
- int w;
-
coupled->requested_state[cpu] = next_state;
/*
- * If this is the last cpu to enter the waiting state, poke
- * all the other cpus out of their waiting state so they can
- * enter a deeper state. This can race with one of the cpus
- * exiting the waiting state due to an interrupt and
- * decrementing waiting_count, see comment below.
- *
* The atomic_inc_return provides a write barrier to order the write
* to requested_state with the later write that increments ready_count.
*/
- w = atomic_inc_return(&coupled->ready_waiting_counts) & WAITING_MASK;
- if (w == coupled->online_count)
- cpuidle_coupled_poke_others(cpu, coupled);
+ return atomic_inc_return(&coupled->ready_waiting_counts) & WAITING_MASK;
}
/**
@@ -410,19 +408,33 @@ static void cpuidle_coupled_set_done(int cpu, struct cpuidle_coupled *coupled)
* been processed and the poke bit has been cleared.
*
* Other interrupts may also be processed while interrupts are enabled, so
- * need_resched() must be tested after turning interrupts off again to make sure
+ * need_resched() must be tested after this function returns to make sure
* the interrupt didn't schedule work that should take the cpu out of idle.
*
- * Returns 0 if need_resched was false, -EINTR if need_resched was true.
+ * Returns 0 if no poke was pending, 1 if a poke was cleared.
*/
static int cpuidle_coupled_clear_pokes(int cpu)
{
+ if (!cpumask_test_cpu(cpu, &cpuidle_coupled_poke_pending))
+ return 0;
+
local_irq_enable();
- while (cpumask_test_cpu(cpu, &cpuidle_coupled_poked_mask))
+ while (cpumask_test_cpu(cpu, &cpuidle_coupled_poke_pending))
cpu_relax();
local_irq_disable();
- return need_resched() ? -EINTR : 0;
+ return 1;
+}
+
+static bool cpuidle_coupled_any_pokes_pending(struct cpuidle_coupled *coupled)
+{
+ cpumask_t cpus;
+ int ret;
+
+ cpumask_and(&cpus, cpu_online_mask, &coupled->coupled_cpus);
+ ret = cpumask_and(&cpus, &cpuidle_coupled_poke_pending, &cpus);
+
+ return ret;
}
/**
@@ -449,12 +461,14 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
{
int entered_state = -1;
struct cpuidle_coupled *coupled = dev->coupled;
+ int w;
if (!coupled)
return -EINVAL;
while (coupled->prevent) {
- if (cpuidle_coupled_clear_pokes(dev->cpu)) {
+ cpuidle_coupled_clear_pokes(dev->cpu);
+ if (need_resched()) {
local_irq_enable();
return entered_state;
}
@@ -465,15 +479,37 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
/* Read barrier ensures online_count is read after prevent is cleared */
smp_rmb();
- cpuidle_coupled_set_waiting(dev->cpu, coupled, next_state);
+reset:
+ cpumask_clear_cpu(dev->cpu, &cpuidle_coupled_poked);
+
+ w = cpuidle_coupled_set_waiting(dev->cpu, coupled, next_state);
+ /*
+ * If this is the last cpu to enter the waiting state, poke
+ * all the other cpus out of their waiting state so they can
+ * enter a deeper state. This can race with one of the cpus
+ * exiting the waiting state due to an interrupt and
+ * decrementing waiting_count, see comment below.
+ */
+ if (w == coupled->online_count) {
+ cpumask_set_cpu(dev->cpu, &cpuidle_coupled_poked);
+ cpuidle_coupled_poke_others(dev->cpu, coupled);
+ }
retry:
/*
* Wait for all coupled cpus to be idle, using the deepest state
- * allowed for a single cpu.
+ * allowed for a single cpu. If this was not the poking cpu, wait
+ * for at least one poke before leaving to avoid a race where
+ * two cpus could arrive at the waiting loop at the same time,
+ * but the first of the two to arrive could skip the loop without
+ * processing the pokes from the last to arrive.
*/
- while (!cpuidle_coupled_cpus_waiting(coupled)) {
- if (cpuidle_coupled_clear_pokes(dev->cpu)) {
+ while (!cpuidle_coupled_cpus_waiting(coupled) ||
+ !cpumask_test_cpu(dev->cpu, &cpuidle_coupled_poked)) {
+ if (cpuidle_coupled_clear_pokes(dev->cpu))
+ continue;
+
+ if (need_resched()) {
cpuidle_coupled_set_not_waiting(dev->cpu, coupled);
goto out;
}
@@ -487,12 +523,19 @@ retry:
dev->safe_state_index);
}
- if (cpuidle_coupled_clear_pokes(dev->cpu)) {
+ cpuidle_coupled_clear_pokes(dev->cpu);
+ if (need_resched()) {
cpuidle_coupled_set_not_waiting(dev->cpu, coupled);
goto out;
}
/*
+ * Make sure final poke status for this cpu is visible before setting
+ * cpu as ready.
+ */
+ smp_wmb();
+
+ /*
* All coupled cpus are probably idle. There is a small chance that
* one of the other cpus just became active. Increment the ready count,
* and spin until all coupled cpus have incremented the counter. Once a
@@ -511,6 +554,28 @@ retry:
cpu_relax();
}
+ /*
+ * Make sure read of all cpus ready is done before reading pending pokes
+ */
+ smp_rmb();
+
+ /*
+ * There is a small chance that a cpu left and reentered idle after this
+ * cpu saw that all cpus were waiting. The cpu that reentered idle will
+ * have sent this cpu a poke, which will still be pending after the
+ * ready loop. The pending interrupt may be lost by the interrupt
+ * controller when entering the deep idle state. It's not possible to
+ * clear a pending interrupt without turning interrupts on and handling
+ * it, and it's too late to turn on interrupts here, so reset the
+ * coupled idle state of all cpus and retry.
+ */
+ if (cpuidle_coupled_any_pokes_pending(coupled)) {
+ cpuidle_coupled_set_done(dev->cpu, coupled);
+ /* Wait for all cpus to see the pending pokes */
+ cpuidle_coupled_parallel_barrier(dev, &coupled->abort_barrier);
+ goto reset;
+ }
+
/* all cpus have acked the coupled state */
next_state = cpuidle_coupled_get_state(dev, coupled);
@@ -596,7 +661,7 @@ have_coupled:
coupled->refcnt++;
csd = &per_cpu(cpuidle_coupled_poke_cb, dev->cpu);
- csd->func = cpuidle_coupled_poked;
+ csd->func = cpuidle_coupled_handle_poke;
csd->info = (void *)(unsigned long)dev->cpu;
return 0;
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index f28583370d00..617d17029065 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -414,17 +414,18 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac)
struct imxdma_engine *imxdma = imxdmac->imxdma;
int chno = imxdmac->channel;
struct imxdma_desc *desc;
+ unsigned long flags;
- spin_lock(&imxdma->lock);
+ spin_lock_irqsave(&imxdma->lock, flags);
if (list_empty(&imxdmac->ld_active)) {
- spin_unlock(&imxdma->lock);
+ spin_unlock_irqrestore(&imxdma->lock, flags);
goto out;
}
desc = list_first_entry(&imxdmac->ld_active,
struct imxdma_desc,
node);
- spin_unlock(&imxdma->lock);
+ spin_unlock_irqrestore(&imxdma->lock, flags);
if (desc->sg) {
u32 tmp;
@@ -496,7 +497,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan);
struct imxdma_engine *imxdma = imxdmac->imxdma;
- unsigned long flags;
int slot = -1;
int i;
@@ -504,7 +504,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
switch (d->type) {
case IMXDMA_DESC_INTERLEAVED:
/* Try to get a free 2D slot */
- spin_lock_irqsave(&imxdma->lock, flags);
for (i = 0; i < IMX_DMA_2D_SLOTS; i++) {
if ((imxdma->slots_2d[i].count > 0) &&
((imxdma->slots_2d[i].xsr != d->x) ||
@@ -514,10 +513,8 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
slot = i;
break;
}
- if (slot < 0) {
- spin_unlock_irqrestore(&imxdma->lock, flags);
+ if (slot < 0)
return -EBUSY;
- }
imxdma->slots_2d[slot].xsr = d->x;
imxdma->slots_2d[slot].ysr = d->y;
@@ -526,7 +523,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
imxdmac->slot_2d = slot;
imxdmac->enabled_2d = true;
- spin_unlock_irqrestore(&imxdma->lock, flags);
if (slot == IMX_DMA_2D_SLOT_A) {
d->config_mem &= ~CCR_MSEL_B;
@@ -602,18 +598,17 @@ static void imxdma_tasklet(unsigned long data)
struct imxdma_channel *imxdmac = (void *)data;
struct imxdma_engine *imxdma = imxdmac->imxdma;
struct imxdma_desc *desc;
+ unsigned long flags;
- spin_lock(&imxdma->lock);
+ spin_lock_irqsave(&imxdma->lock, flags);
if (list_empty(&imxdmac->ld_active)) {
/* Someone might have called terminate all */
- goto out;
+ spin_unlock_irqrestore(&imxdma->lock, flags);
+ return;
}
desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc, node);
- if (desc->desc.callback)
- desc->desc.callback(desc->desc.callback_param);
-
/* If we are dealing with a cyclic descriptor, keep it on ld_active
* and dont mark the descriptor as complete.
* Only in non-cyclic cases it would be marked as complete
@@ -640,7 +635,11 @@ static void imxdma_tasklet(unsigned long data)
__func__, imxdmac->channel);
}
out:
- spin_unlock(&imxdma->lock);
+ spin_unlock_irqrestore(&imxdma->lock, flags);
+
+ if (desc->desc.callback)
+ desc->desc.callback(desc->desc.callback_param);
+
}
static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -862,7 +861,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
kfree(imxdmac->sg_list);
imxdmac->sg_list = kcalloc(periods + 1,
- sizeof(struct scatterlist), GFP_KERNEL);
+ sizeof(struct scatterlist), GFP_ATOMIC);
if (!imxdmac->sg_list)
return NULL;
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 8b6a0343c220..8b3d90143514 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -2470,8 +2470,15 @@ static int amd64_init_one_instance(struct pci_dev *F2)
layers[0].size = pvt->csels[0].b_cnt;
layers[0].is_virt_csrow = true;
layers[1].type = EDAC_MC_LAYER_CHANNEL;
- layers[1].size = pvt->channel_count;
+
+ /*
+ * Always allocate two channels since we can have setups with DIMMs on
+ * only one channel. Also, this simplifies handling later for the price
+ * of a couple of KBs tops.
+ */
+ layers[1].size = 2;
layers[1].is_virt_csrow = false;
+
mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0);
if (!mci)
goto err_siblings;
diff --git a/drivers/gator/Kconfig b/drivers/gator/Kconfig
new file mode 100644
index 000000000000..7ea0fcc3d01d
--- /dev/null
+++ b/drivers/gator/Kconfig
@@ -0,0 +1,33 @@
+config GATOR
+ tristate "Gator module for ARM's Streamline Performance Analyzer"
+ default m if (ARM || ARM64)
+ depends on PROFILING
+ depends on HIGH_RES_TIMERS
+ depends on LOCAL_TIMERS || !(ARM && SMP)
+ select TRACING
+
+config GATOR_WITH_MALI_SUPPORT
+ bool
+
+choice
+ prompt "Enable Mali GPU support in Gator"
+ depends on GATOR
+ optional
+
+config GATOR_MALI_400MP
+ bool "Mali-400MP"
+ select GATOR_WITH_MALI_SUPPORT
+
+config GATOR_MALI_T6XX
+ bool "Mali-T604 or Mali-T658"
+ select GATOR_WITH_MALI_SUPPORT
+
+endchoice
+
+config GATOR_MALI_PATH
+ string "Path to Mali driver"
+ depends on GATOR_WITH_MALI_SUPPORT
+ default "drivers/gpu/arm/mali400mp"
+ help
+ The gator code adds this to its include path so it can get the Mali
+ trace headers with: #include "linux/mali_linux_trace.h"
diff --git a/drivers/gator/LICENSE b/drivers/gator/LICENSE
new file mode 100644
index 000000000000..d159169d1050
--- /dev/null
+++ b/drivers/gator/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/drivers/gator/Makefile b/drivers/gator/Makefile
new file mode 100644
index 000000000000..942e415677bd
--- /dev/null
+++ b/drivers/gator/Makefile
@@ -0,0 +1,76 @@
+ifneq ($(KERNELRELEASE),)
+
+# Uncomment the following line to enable kernel stack unwinding within gator, or update gator_backtrace.c
+# EXTRA_CFLAGS += -DGATOR_KERNEL_STACK_UNWINDING
+
+CONFIG_GATOR ?= m
+obj-$(CONFIG_GATOR) := gator.o
+
+gator-y := gator_main.o \
+ gator_events_irq.o \
+ gator_events_sched.o \
+ gator_events_net.o \
+ gator_events_block.o \
+ gator_events_meminfo.o \
+ gator_events_perf_pmu.o
+
+gator-y += gator_events_mmaped.o
+
+ifeq ($(CONFIG_GATOR_WITH_MALI_SUPPORT),y)
+
+ifeq ($(CONFIG_GATOR_MALI_T6XX),y)
+gator-y += gator_events_mali_t6xx.o \
+ gator_events_mali_t6xx_hw.o
+include $(M)/mali_t6xx.mk
+else
+gator-y += gator_events_mali_4xx.o
+endif
+gator-y += gator_events_mali_common.o
+
+ccflags-y += -I$(CONFIG_GATOR_MALI_PATH)
+ccflags-$(CONFIG_GATOR_MALI_400MP) += -DMALI_SUPPORT=MALI_400
+ccflags-$(CONFIG_GATOR_MALI_T6XX) += -DMALI_SUPPORT=MALI_T6xx
+endif
+
+# GATOR_TEST controls whether to include (=1) or exclude (=0) test code.
+GATOR_TEST ?= 0
+EXTRA_CFLAGS += -DGATOR_TEST=$(GATOR_TEST)
+
+gator-$(CONFIG_ARM) += gator_events_armv6.o \
+ gator_events_armv7.o \
+ gator_events_ccn-504.o \
+ gator_events_l2c-310.o \
+ gator_events_scorpion.o
+
+gator-$(CONFIG_ARM64) += gator_events_ccn-504.o
+
+$(obj)/gator_main.o: $(obj)/gator_events.h
+
+clean-files := gator_events.h
+
+# Note, in the recipe below we use "cd $(srctree) && cd $(src)" rather than
+# "cd $(srctree)/$(src)" because under DKMS $(src) is an absolute path, and we
+# can't just use $(src) because for normal kernel builds this is relative to
+# $(srctree)
+
+ chk_events.h = :
+ quiet_chk_events.h = echo ' CHK $@'
+silent_chk_events.h = :
+$(obj)/gator_events.h: FORCE
+ @$($(quiet)chk_events.h)
+ $(Q)cd $(srctree) && cd $(src) ; $(CONFIG_SHELL) gator_events.sh $(abspath $@)
+
+else
+
+all:
+ @echo
+ @echo "usage:"
+ @echo " make -C <kernel_build_dir> M=\`pwd\` ARCH=arm CROSS_COMPILE=<...> modules"
+ @echo
+ $(error)
+
+clean:
+ rm -f *.o .*.cmd gator_events.h modules.order Module.symvers gator.ko gator.mod.c
+ rm -rf .tmp_versions
+
+endif
diff --git a/drivers/gator/gator.h b/drivers/gator/gator.h
new file mode 100644
index 000000000000..2e122da767d8
--- /dev/null
+++ b/drivers/gator/gator.h
@@ -0,0 +1,146 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef GATOR_H_
+#define GATOR_H_
+
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+
+#define GATOR_PERF_SUPPORT LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+#define GATOR_PERF_PMU_SUPPORT GATOR_PERF_SUPPORT && defined(CONFIG_PERF_EVENTS) && (!(defined(__arm__) || defined(__aarch64__)) || defined(CONFIG_HW_PERF_EVENTS))
+#define GATOR_NO_PERF_SUPPORT (!(GATOR_PERF_SUPPORT))
+#define GATOR_CPU_FREQ_SUPPORT (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) && defined(CONFIG_CPU_FREQ)
+#define GATOR_IKS_SUPPORT defined(CONFIG_BL_SWITCHER)
+
+// cpu ids
+#define ARM1136 0xb36
+#define ARM1156 0xb56
+#define ARM1176 0xb76
+#define ARM11MPCORE 0xb02
+#define CORTEX_A5 0xc05
+#define CORTEX_A7 0xc07
+#define CORTEX_A8 0xc08
+#define CORTEX_A9 0xc09
+#define CORTEX_A12 0xc0d
+#define CORTEX_A15 0xc0f
+#define SCORPION 0x00f
+#define SCORPIONMP 0x02d
+#define KRAITSIM 0x049
+#define KRAIT 0x04d
+#define KRAIT_S4_PRO 0x06f
+#define CORTEX_A53 0xd03
+#define CORTEX_A57 0xd07
+#define AARCH64 0xd0f
+#define OTHER 0xfff
+
+#define MAXSIZE_CORE_NAME 32
+
+struct gator_cpu {
+ const int cpuid;
+ // Human readable name
+ const char core_name[MAXSIZE_CORE_NAME];
+ // Perf PMU name
+ const char * const pmu_name;
+ // gatorfs event name
+ const char * const pmnc_name;
+ // compatible from Documentation/devicetree/bindings/arm/cpus.txt
+ const char * const dt_name;
+ const int pmnc_counters;
+};
+
+const struct gator_cpu *gator_find_cpu_by_cpuid(const u32 cpuid);
+const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name);
+
+/******************************************************************************
+ * Filesystem
+ ******************************************************************************/
+int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root,
+ char const *name,
+ const struct file_operations *fops, int perm);
+
+struct dentry *gatorfs_mkdir(struct super_block *sb, struct dentry *root,
+ char const *name);
+
+int gatorfs_create_ulong(struct super_block *sb, struct dentry *root,
+ char const *name, unsigned long *val);
+
+int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root,
+ char const *name, unsigned long *val);
+
+void gator_op_create_files(struct super_block *sb, struct dentry *root);
+
+/******************************************************************************
+ * Tracepoints
+ ******************************************************************************/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+# error Kernels prior to 2.6.32 not supported
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+# define GATOR_DEFINE_PROBE(probe_name, proto) \
+ static void probe_##probe_name(PARAMS(proto))
+# define GATOR_REGISTER_TRACE(probe_name) \
+ register_trace_##probe_name(probe_##probe_name)
+# define GATOR_UNREGISTER_TRACE(probe_name) \
+ unregister_trace_##probe_name(probe_##probe_name)
+#else
+# define GATOR_DEFINE_PROBE(probe_name, proto) \
+ static void probe_##probe_name(void *data, PARAMS(proto))
+# define GATOR_REGISTER_TRACE(probe_name) \
+ register_trace_##probe_name(probe_##probe_name, NULL)
+# define GATOR_UNREGISTER_TRACE(probe_name) \
+ unregister_trace_##probe_name(probe_##probe_name, NULL)
+#endif
+
+/******************************************************************************
+ * Events
+ ******************************************************************************/
+struct gator_interface {
+ void (*shutdown)(void); // Complementary function to init
+ int (*create_files)(struct super_block *sb, struct dentry *root);
+ int (*start)(void);
+ void (*stop)(void); // Complementary function to start
+ int (*online)(int **buffer, bool migrate);
+ int (*offline)(int **buffer, bool migrate);
+ void (*online_dispatch)(int cpu, bool migrate); // called in process context but may not be running on core 'cpu'
+ void (*offline_dispatch)(int cpu, bool migrate); // called in process context but may not be running on core 'cpu'
+ int (*read)(int **buffer);
+ int (*read64)(long long **buffer);
+ struct list_head list;
+};
+
+// gator_events_init is used as a search term in gator_events.sh
+#define gator_events_init(initfn) \
+ static inline int __gator_events_init_test(void) \
+ { return initfn(); }
+
+int gator_events_install(struct gator_interface *interface);
+int gator_events_get_key(void);
+u32 gator_cpuid(void);
+
+void gator_backtrace_handler(struct pt_regs *const regs);
+
+#if !GATOR_IKS_SUPPORT
+
+#define get_physical_cpu() smp_processor_id()
+#define lcpu_to_pcpu(lcpu) lcpu
+#define pcpu_to_lcpu(pcpu) pcpu
+
+#else
+
+#define get_physical_cpu() lcpu_to_pcpu(get_logical_cpu())
+int lcpu_to_pcpu(const int lcpu);
+int pcpu_to_lcpu(const int pcpu);
+
+#endif
+
+#define get_logical_cpu() smp_processor_id()
+#define on_primary_core() (get_logical_cpu() == 0)
+
+#endif // GATOR_H_
diff --git a/drivers/gator/gator_annotate.c b/drivers/gator/gator_annotate.c
new file mode 100644
index 000000000000..5b9399bea230
--- /dev/null
+++ b/drivers/gator/gator_annotate.c
@@ -0,0 +1,186 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/current.h>
+#include <linux/spinlock.h>
+
+static DEFINE_SPINLOCK(annotate_lock);
+static bool collect_annotations = false;
+
+static int annotate_copy(struct file *file, char const __user *buf, size_t count)
+{
+ int cpu = 0;
+ int write = per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF];
+
+ if (file == NULL) {
+ // copy from kernel
+ memcpy(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count);
+ } else {
+ // copy from user space
+ if (copy_from_user(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count) != 0)
+ return -1;
+ }
+ per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF] = (write + count) & gator_buffer_mask[ANNOTATE_BUF];
+
+ return 0;
+}
+
+static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count_orig, loff_t *offset)
+{
+ int pid, cpu, header_size, available, contiguous, length1, length2, size, count = count_orig & 0x7fffffff;
+ bool interrupt_context;
+
+ if (*offset) {
+ return -EINVAL;
+ }
+
+ interrupt_context = in_interrupt();
+ // Annotations are not supported in interrupt context, but may work if you comment out the the next four lines of code.
+ // By doing so, annotations in interrupt context can result in deadlocks and lost data.
+ if (interrupt_context) {
+ printk(KERN_WARNING "gator: Annotations are not supported in interrupt context. Edit gator_annotate.c in the gator driver to enable annotations in interrupt context.\n");
+ return -EINVAL;
+ }
+
+ retry:
+ // synchronize between cores and with collect_annotations
+ spin_lock(&annotate_lock);
+
+ if (!collect_annotations) {
+ // Not collecting annotations, tell the caller everything was written
+ size = count_orig;
+ goto annotate_write_out;
+ }
+
+ // Annotation only uses a single per-cpu buffer as the data must be in order to the engine
+ cpu = 0;
+
+ if (current == NULL) {
+ pid = 0;
+ } else {
+ pid = current->pid;
+ }
+
+ // determine total size of the payload
+ header_size = MAXSIZE_PACK32 * 3 + MAXSIZE_PACK64;
+ available = buffer_bytes_available(cpu, ANNOTATE_BUF) - header_size;
+ size = count < available ? count : available;
+
+ if (size <= 0) {
+ // Buffer is full, wait until space is available
+ spin_unlock(&annotate_lock);
+
+ // Drop the annotation as blocking is not allowed in interrupt context
+ if (interrupt_context) {
+ return -EINVAL;
+ }
+
+ wait_event_interruptible(gator_annotate_wait, buffer_bytes_available(cpu, ANNOTATE_BUF) > header_size || !collect_annotations);
+
+ // Check to see if a signal is pending
+ if (signal_pending(current)) {
+ return -EINTR;
+ }
+
+ goto retry;
+ }
+
+ // synchronize shared variables annotateBuf and annotatePos
+ if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF]) {
+ u64 time = gator_get_time();
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, get_physical_cpu());
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, pid);
+ gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, time);
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, size);
+
+ // determine the sizes to capture, length1 + length2 will equal size
+ contiguous = contiguous_space_available(cpu, ANNOTATE_BUF);
+ if (size < contiguous) {
+ length1 = size;
+ length2 = 0;
+ } else {
+ length1 = contiguous;
+ length2 = size - contiguous;
+ }
+
+ if (annotate_copy(file, buf, length1) != 0) {
+ size = -EINVAL;
+ goto annotate_write_out;
+ }
+
+ if (length2 > 0 && annotate_copy(file, &buf[length1], length2) != 0) {
+ size = -EINVAL;
+ goto annotate_write_out;
+ }
+
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, ANNOTATE_BUF, time);
+ }
+
+annotate_write_out:
+ spin_unlock(&annotate_lock);
+
+ // return the number of bytes written
+ return size;
+}
+
+#include "gator_annotate_kernel.c"
+
+static int annotate_release(struct inode *inode, struct file *file)
+{
+ int cpu = 0;
+
+ // synchronize between cores
+ spin_lock(&annotate_lock);
+
+ if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF] && buffer_check_space(cpu, ANNOTATE_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) {
+ uint32_t pid = current->pid;
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, get_physical_cpu());
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, pid);
+ gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, 0); // time
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, 0); // size
+ }
+
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, ANNOTATE_BUF, gator_get_time());
+
+ spin_unlock(&annotate_lock);
+
+ return 0;
+}
+
+static const struct file_operations annotate_fops = {
+ .write = annotate_write,
+ .release = annotate_release
+};
+
+static int gator_annotate_create_files(struct super_block *sb, struct dentry *root)
+{
+ return gatorfs_create_file_perm(sb, root, "annotate", &annotate_fops, 0666);
+}
+
+static int gator_annotate_start(void)
+{
+ collect_annotations = true;
+ return 0;
+}
+
+static void gator_annotate_stop(void)
+{
+ // the spinlock here will ensure that when this function exits, we are not in the middle of an annotation
+ spin_lock(&annotate_lock);
+ collect_annotations = false;
+ wake_up(&gator_annotate_wait);
+ spin_unlock(&annotate_lock);
+}
diff --git a/drivers/gator/gator_annotate_kernel.c b/drivers/gator/gator_annotate_kernel.c
new file mode 100644
index 000000000000..4715f64a1865
--- /dev/null
+++ b/drivers/gator/gator_annotate_kernel.c
@@ -0,0 +1,157 @@
+/**
+ * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define ESCAPE_CODE 0x1c
+#define STRING_ANNOTATION 0x06
+#define NAME_CHANNEL_ANNOTATION 0x07
+#define NAME_GROUP_ANNOTATION 0x08
+#define VISUAL_ANNOTATION 0x04
+#define MARKER_ANNOTATION 0x05
+
+static void kannotate_write(const char *ptr, unsigned int size)
+{
+ int retval;
+ int pos = 0;
+ loff_t offset = 0;
+ while (pos < size) {
+ retval = annotate_write(NULL, &ptr[pos], size - pos, &offset);
+ if (retval < 0) {
+ printk(KERN_WARNING "gator: kannotate_write failed with return value %d\n", retval);
+ return;
+ }
+ pos += retval;
+ }
+}
+
+void gator_annotate_channel(int channel, const char *str)
+{
+ int str_size = strlen(str) & 0xffff;
+ long long header = ESCAPE_CODE | (STRING_ANNOTATION << 8) | (channel << 16) | ((long long)str_size << 48);
+ kannotate_write((char *)&header, sizeof(header));
+ kannotate_write(str, str_size);
+}
+
+EXPORT_SYMBOL(gator_annotate_channel);
+
+void gator_annotate(const char *str)
+{
+ gator_annotate_channel(0, str);
+}
+
+EXPORT_SYMBOL(gator_annotate);
+
+void gator_annotate_channel_color(int channel, int color, const char *str)
+{
+ int str_size = (strlen(str) + 4) & 0xffff;
+ char header[12];
+ header[0] = ESCAPE_CODE;
+ header[1] = STRING_ANNOTATION;
+ *(u32 *)(&header[2]) = channel;
+ *(u16 *)(&header[6]) = str_size;
+ *(u32 *)(&header[8]) = color;
+ kannotate_write((char *)&header, sizeof(header));
+ kannotate_write(str, str_size - 4);
+}
+
+EXPORT_SYMBOL(gator_annotate_channel_color);
+
+void gator_annotate_color(int color, const char *str)
+{
+ gator_annotate_channel_color(0, color, str);
+}
+
+EXPORT_SYMBOL(gator_annotate_color);
+
+void gator_annotate_channel_end(int channel)
+{
+ long long header = ESCAPE_CODE | (STRING_ANNOTATION << 8) | (channel << 16);
+ kannotate_write((char *)&header, sizeof(header));
+}
+
+EXPORT_SYMBOL(gator_annotate_channel_end);
+
+void gator_annotate_end(void)
+{
+ gator_annotate_channel_end(0);
+}
+
+EXPORT_SYMBOL(gator_annotate_end);
+
+void gator_annotate_name_channel(int channel, int group, const char* str)
+{
+ int str_size = strlen(str) & 0xffff;
+ char header[12];
+ header[0] = ESCAPE_CODE;
+ header[1] = NAME_CHANNEL_ANNOTATION;
+ *(u32 *)(&header[2]) = channel;
+ *(u32 *)(&header[6]) = group;
+ *(u16 *)(&header[10]) = str_size;
+ kannotate_write((char *)&header, sizeof(header));
+ kannotate_write(str, str_size);
+}
+
+EXPORT_SYMBOL(gator_annotate_name_channel);
+
+void gator_annotate_name_group(int group, const char* str)
+{
+ int str_size = strlen(str) & 0xffff;
+ long long header = ESCAPE_CODE | (NAME_GROUP_ANNOTATION << 8) | (group << 16) | ((long long)str_size << 48);
+ kannotate_write((char *)&header, sizeof(header));
+ kannotate_write(str, str_size);
+}
+
+EXPORT_SYMBOL(gator_annotate_name_group);
+
+void gator_annotate_visual(const char *data, unsigned int length, const char *str)
+{
+ int str_size = strlen(str) & 0xffff;
+ int visual_annotation = ESCAPE_CODE | (VISUAL_ANNOTATION << 8) | (str_size << 16);
+ kannotate_write((char *)&visual_annotation, sizeof(visual_annotation));
+ kannotate_write(str, str_size);
+ kannotate_write((char *)&length, sizeof(length));
+ kannotate_write(data, length);
+}
+
+EXPORT_SYMBOL(gator_annotate_visual);
+
+void gator_annotate_marker(void)
+{
+ int header = ESCAPE_CODE | (MARKER_ANNOTATION << 8);
+ kannotate_write((char *)&header, sizeof(header));
+}
+
+EXPORT_SYMBOL(gator_annotate_marker);
+
+void gator_annotate_marker_str(const char *str)
+{
+ int str_size = strlen(str) & 0xffff;
+ int header = ESCAPE_CODE | (MARKER_ANNOTATION << 8) | (str_size << 16);
+ kannotate_write((char *)&header, sizeof(header));
+ kannotate_write(str, str_size);
+}
+
+EXPORT_SYMBOL(gator_annotate_marker_str);
+
+void gator_annotate_marker_color(int color)
+{
+ long long header = (ESCAPE_CODE | (MARKER_ANNOTATION << 8) | 0x00040000 | ((long long)color << 32));
+ kannotate_write((char *)&header, sizeof(header));
+}
+
+EXPORT_SYMBOL(gator_annotate_marker_color);
+
+void gator_annotate_marker_color_str(int color, const char *str)
+{
+ int str_size = (strlen(str) + 4) & 0xffff;
+ long long header = ESCAPE_CODE | (MARKER_ANNOTATION << 8) | (str_size << 16) | ((long long)color << 32);
+ kannotate_write((char *)&header, sizeof(header));
+ kannotate_write(str, str_size - 4);
+}
+
+EXPORT_SYMBOL(gator_annotate_marker_color_str);
diff --git a/drivers/gator/gator_backtrace.c b/drivers/gator/gator_backtrace.c
new file mode 100644
index 000000000000..0670d6cea9bd
--- /dev/null
+++ b/drivers/gator/gator_backtrace.c
@@ -0,0 +1,160 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/*
+ * EABI backtrace stores {fp,lr} on the stack.
+ */
+struct stack_frame_eabi {
+ union {
+ struct {
+ unsigned long fp;
+ // May be the fp in the case of a leaf function or clang
+ unsigned long lr;
+ // If lr is really the fp, lr2 is the corresponding lr
+ unsigned long lr2;
+ };
+ // Used to read 32 bit fp/lr from a 64 bit kernel
+ struct {
+ u32 fp_32;
+ // same as lr above
+ u32 lr_32;
+ // same as lr2 above
+ u32 lr2_32;
+ };
+ };
+};
+
+static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int depth)
+{
+#if defined(__arm__) || defined(__aarch64__)
+ struct stack_frame_eabi *curr;
+ struct stack_frame_eabi bufcurr;
+#if defined(__arm__)
+ const bool is_compat = false;
+ unsigned long fp = regs->ARM_fp;
+ unsigned long sp = regs->ARM_sp;
+ unsigned long lr = regs->ARM_lr;
+ const int gcc_frame_offset = sizeof(unsigned long);
+#else
+ // Is userspace aarch32 (32 bit)
+ const bool is_compat = compat_user_mode(regs);
+ unsigned long fp = (is_compat ? regs->regs[11] : regs->regs[29]);
+ unsigned long sp = (is_compat ? regs->compat_sp : regs->sp);
+ unsigned long lr = (is_compat ? regs->compat_lr : regs->regs[30]);
+ const int gcc_frame_offset = (is_compat ? sizeof(u32) : 0);
+#endif
+ // clang frame offset is always zero
+ int is_user_mode = user_mode(regs);
+
+ // pc (current function) has already been added
+
+ if (!is_user_mode) {
+ return;
+ }
+
+ // Add the lr (parent function)
+ // entry preamble may not have executed
+ gator_add_trace(cpu, lr);
+
+ // check fp is valid
+ if (fp == 0 || fp < sp) {
+ return;
+ }
+
+ // Get the current stack frame
+ curr = (struct stack_frame_eabi *)(fp - gcc_frame_offset);
+ if ((unsigned long)curr & 3) {
+ return;
+ }
+
+ while (depth-- && curr) {
+ if (!access_ok(VERIFY_READ, curr, sizeof(struct stack_frame_eabi)) ||
+ __copy_from_user_inatomic(&bufcurr, curr, sizeof(struct stack_frame_eabi))) {
+ return;
+ }
+
+ fp = (is_compat ? bufcurr.fp_32 : bufcurr.fp);
+ lr = (is_compat ? bufcurr.lr_32 : bufcurr.lr);
+
+#define calc_next(reg) ((reg) - gcc_frame_offset)
+ // Returns true if reg is a valid fp
+#define validate_next(reg, curr) \
+ ((reg) != 0 && (calc_next(reg) & 3) == 0 && (unsigned long)(curr) < calc_next(reg))
+
+ // Try lr from the stack as the fp because gcc leaf functions do not push lr
+ // If gcc_frame_offset is non-zero, the lr will also be the clang fp
+ // This assumes code is at a lower address than the stack
+ if (validate_next(lr, curr)) {
+ fp = lr;
+ lr = (is_compat ? bufcurr.lr2_32 : bufcurr.lr2);
+ }
+
+ gator_add_trace(cpu, lr);
+
+ if (!validate_next(fp, curr)) {
+ return;
+ }
+
+ // Move to the next stack frame
+ curr = (struct stack_frame_eabi *)calc_next(fp);
+ }
+#endif
+}
+
+#if defined(__arm__) || defined(__aarch64__)
+static int report_trace(struct stackframe *frame, void *d)
+{
+ unsigned int *depth = d, cookie = NO_COOKIE;
+ unsigned long addr = frame->pc;
+
+ if (*depth) {
+#if defined(MODULE)
+ unsigned int cpu = get_physical_cpu();
+ struct module *mod = __module_address(addr);
+ if (mod) {
+ cookie = get_cookie(cpu, current, mod->name, false);
+ addr = addr - (unsigned long)mod->module_core;
+ }
+#endif
+ marshal_backtrace(addr & ~1, cookie);
+ (*depth)--;
+ }
+
+ return *depth == 0;
+}
+#endif
+
+// Uncomment the following line to enable kernel stack unwinding within gator, note it can also be defined from the Makefile
+// #define GATOR_KERNEL_STACK_UNWINDING
+static void kernel_backtrace(int cpu, struct pt_regs *const regs)
+{
+#if defined(__arm__) || defined(__aarch64__)
+#ifdef GATOR_KERNEL_STACK_UNWINDING
+ int depth = gator_backtrace_depth;
+#else
+ int depth = 1;
+#endif
+ struct stackframe frame;
+ if (depth == 0)
+ depth = 1;
+#if defined(__arm__)
+ frame.fp = regs->ARM_fp;
+ frame.sp = regs->ARM_sp;
+ frame.lr = regs->ARM_lr;
+ frame.pc = regs->ARM_pc;
+#else
+ frame.fp = regs->regs[29];
+ frame.sp = regs->sp;
+ frame.pc = regs->pc;
+#endif
+ walk_stackframe(&frame, report_trace, &depth);
+#else
+ marshal_backtrace(PC_REG & ~1, NO_COOKIE);
+#endif
+}
diff --git a/drivers/gator/gator_cookies.c b/drivers/gator/gator_cookies.c
new file mode 100644
index 000000000000..5f98a1cc309c
--- /dev/null
+++ b/drivers/gator/gator_cookies.c
@@ -0,0 +1,397 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define COOKIEMAP_ENTRIES 1024 /* must be power of 2 */
+#define TRANSLATE_SIZE 256
+#define MAX_COLLISIONS 2
+
+static uint32_t *gator_crc32_table;
+static unsigned int translate_buffer_mask;
+
+static DEFINE_PER_CPU(char *, translate_text);
+static DEFINE_PER_CPU(uint32_t, cookie_next_key);
+static DEFINE_PER_CPU(uint64_t *, cookie_keys);
+static DEFINE_PER_CPU(uint32_t *, cookie_values);
+static DEFINE_PER_CPU(int, translate_buffer_read);
+static DEFINE_PER_CPU(int, translate_buffer_write);
+static DEFINE_PER_CPU(void **, translate_buffer);
+
+static inline uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq);
+static void wq_cookie_handler(struct work_struct *unused);
+DECLARE_WORK(cookie_work, wq_cookie_handler);
+static struct timer_list app_process_wake_up_timer;
+static void app_process_wake_up_handler(unsigned long unused_data);
+
+static uint32_t cookiemap_code(uint64_t value64)
+{
+ uint32_t value = (uint32_t)((value64 >> 32) + value64);
+ uint32_t cookiecode = (value >> 24) & 0xff;
+ cookiecode = cookiecode * 31 + ((value >> 16) & 0xff);
+ cookiecode = cookiecode * 31 + ((value >> 8) & 0xff);
+ cookiecode = cookiecode * 31 + ((value >> 0) & 0xff);
+ cookiecode &= (COOKIEMAP_ENTRIES - 1);
+ return cookiecode * MAX_COLLISIONS;
+}
+
+static uint32_t gator_chksum_crc32(const char *data)
+{
+ register unsigned long crc;
+ const unsigned char *block = data;
+ int i, length = strlen(data);
+
+ crc = 0xFFFFFFFF;
+ for (i = 0; i < length; i++) {
+ crc = ((crc >> 8) & 0x00FFFFFF) ^ gator_crc32_table[(crc ^ *block++) & 0xFF];
+ }
+
+ return (crc ^ 0xFFFFFFFF);
+}
+
+/*
+ * Exists
+ * Pre: [0][1][v][3]..[n-1]
+ * Post: [v][0][1][3]..[n-1]
+ */
+static uint32_t cookiemap_exists(uint64_t key)
+{
+ unsigned long x, flags, retval = 0;
+ int cpu = get_physical_cpu();
+ uint32_t cookiecode = cookiemap_code(key);
+ uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]);
+ uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]);
+
+ // Can be called from interrupt handler or from work queue
+ local_irq_save(flags);
+ for (x = 0; x < MAX_COLLISIONS; x++) {
+ if (keys[x] == key) {
+ uint32_t value = values[x];
+ for (; x > 0; x--) {
+ keys[x] = keys[x - 1];
+ values[x] = values[x - 1];
+ }
+ keys[0] = key;
+ values[0] = value;
+ retval = value;
+ break;
+ }
+ }
+ local_irq_restore(flags);
+
+ return retval;
+}
+
+/*
+ * Add
+ * Pre: [0][1][2][3]..[n-1]
+ * Post: [v][0][1][2]..[n-2]
+ */
+static void cookiemap_add(uint64_t key, uint32_t value)
+{
+ int cpu = get_physical_cpu();
+ int cookiecode = cookiemap_code(key);
+ uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]);
+ uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]);
+ int x;
+
+ for (x = MAX_COLLISIONS - 1; x > 0; x--) {
+ keys[x] = keys[x - 1];
+ values[x] = values[x - 1];
+ }
+ keys[0] = key;
+ values[0] = value;
+}
+
+static void translate_buffer_write_ptr(int cpu, void *x)
+{
+ per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_write, cpu)++] = x;
+ per_cpu(translate_buffer_write, cpu) &= translate_buffer_mask;
+}
+
+static void *translate_buffer_read_ptr(int cpu)
+{
+ void *value = per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_read, cpu)++];
+ per_cpu(translate_buffer_read, cpu) &= translate_buffer_mask;
+ return value;
+}
+
+static void wq_cookie_handler(struct work_struct *unused)
+{
+ struct task_struct *task;
+ char *text;
+ int cpu = get_physical_cpu();
+ unsigned int commit;
+
+ mutex_lock(&start_mutex);
+
+ if (gator_started != 0) {
+ commit = per_cpu(translate_buffer_write, cpu);
+ while (per_cpu(translate_buffer_read, cpu) != commit) {
+ task = (struct task_struct *)translate_buffer_read_ptr(cpu);
+ text = (char *)translate_buffer_read_ptr(cpu);
+ get_cookie(cpu, task, text, true);
+ }
+ }
+
+ mutex_unlock(&start_mutex);
+}
+
+static void app_process_wake_up_handler(unsigned long unused_data)
+{
+ // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater
+ schedule_work(&cookie_work);
+}
+
+// Retrieve full name from proc/pid/cmdline for java processes on Android
+static int translate_app_process(const char **text, int cpu, struct task_struct *task, bool from_wq)
+{
+ void *maddr;
+ unsigned int len;
+ unsigned long addr;
+ struct mm_struct *mm;
+ struct page *page = NULL;
+ struct vm_area_struct *page_vma;
+ int bytes, offset, retval = 0, ptr;
+ char *buf = per_cpu(translate_text, cpu);
+
+ // Push work into a work queue if in atomic context as the kernel functions below might sleep
+ // Rely on the in_interrupt variable rather than in_irq() or in_interrupt() kernel functions, as the value of these functions seems
+ // inconsistent during a context switch between android/linux versions
+ if (!from_wq) {
+ // Check if already in buffer
+ ptr = per_cpu(translate_buffer_read, cpu);
+ while (ptr != per_cpu(translate_buffer_write, cpu)) {
+ if (per_cpu(translate_buffer, cpu)[ptr] == (void *)task)
+ goto out;
+ ptr = (ptr + 2) & translate_buffer_mask;
+ }
+
+ translate_buffer_write_ptr(cpu, (void *)task);
+ translate_buffer_write_ptr(cpu, (void *)*text);
+
+ mod_timer(&app_process_wake_up_timer, jiffies + 1);
+ goto out;
+ }
+
+ mm = get_task_mm(task);
+ if (!mm)
+ goto out;
+ if (!mm->arg_end)
+ goto outmm;
+ addr = mm->arg_start;
+ len = mm->arg_end - mm->arg_start;
+
+ if (len > TRANSLATE_SIZE)
+ len = TRANSLATE_SIZE;
+
+ down_read(&mm->mmap_sem);
+ while (len) {
+ if (get_user_pages(task, mm, addr, 1, 0, 1, &page, &page_vma) <= 0)
+ goto outsem;
+
+ maddr = kmap(page);
+ offset = addr & (PAGE_SIZE - 1);
+ bytes = len;
+ if (bytes > PAGE_SIZE - offset)
+ bytes = PAGE_SIZE - offset;
+
+ copy_from_user_page(page_vma, page, addr, buf, maddr + offset, bytes);
+
+ kunmap(page); // release page allocated by get_user_pages()
+ page_cache_release(page);
+
+ len -= bytes;
+ buf += bytes;
+ addr += bytes;
+
+ *text = per_cpu(translate_text, cpu);
+ retval = 1;
+ }
+
+ // On app_process startup, /proc/pid/cmdline is initially "zygote" then "<pre-initialized>" but changes after an initial startup period
+ if (strcmp(*text, "zygote") == 0 || strcmp(*text, "<pre-initialized>") == 0)
+ retval = 0;
+
+outsem:
+ up_read(&mm->mmap_sem);
+outmm:
+ mmput(mm);
+out:
+ return retval;
+}
+
+static inline uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq)
+{
+ unsigned long flags, cookie;
+ uint64_t key;
+
+ key = gator_chksum_crc32(text);
+ key = (key << 32) | (uint32_t)task->tgid;
+
+ cookie = cookiemap_exists(key);
+ if (cookie) {
+ return cookie;
+ }
+
+ if (strcmp(text, "app_process") == 0) {
+ if (!translate_app_process(&text, cpu, task, from_wq))
+ return UNRESOLVED_COOKIE;
+ }
+
+ // Can be called from interrupt handler or from work queue or from scheduler trace
+ local_irq_save(flags);
+
+ cookie = UNRESOLVED_COOKIE;
+ if (marshal_cookie_header(text)) {
+ cookie = per_cpu(cookie_next_key, cpu) += nr_cpu_ids;
+ cookiemap_add(key, cookie);
+ marshal_cookie(cookie, text);
+ }
+
+ local_irq_restore(flags);
+
+ return cookie;
+}
+
+static int get_exec_cookie(int cpu, struct task_struct *task)
+{
+ struct mm_struct *mm = task->mm;
+ const char *text;
+
+ // kernel threads have no address space
+ if (!mm)
+ return NO_COOKIE;
+
+ if (task && task->mm && task->mm->exe_file) {
+ text = task->mm->exe_file->f_path.dentry->d_name.name;
+ return get_cookie(cpu, task, text, false);
+ }
+
+ return UNRESOLVED_COOKIE;
+}
+
+static unsigned long get_address_cookie(int cpu, struct task_struct *task, unsigned long addr, off_t *offset)
+{
+ unsigned long cookie = NO_COOKIE;
+ struct mm_struct *mm = task->mm;
+ struct vm_area_struct *vma;
+ const char *text;
+
+ if (!mm)
+ return cookie;
+
+ for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) {
+ if (addr < vma->vm_start || addr >= vma->vm_end)
+ continue;
+
+ if (vma->vm_file) {
+ text = vma->vm_file->f_path.dentry->d_name.name;
+ cookie = get_cookie(cpu, task, text, false);
+ *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start;
+ } else {
+ /* must be an anonymous map */
+ *offset = addr;
+ }
+
+ break;
+ }
+
+ if (!vma)
+ cookie = UNRESOLVED_COOKIE;
+
+ return cookie;
+}
+
+static int cookies_initialize(void)
+{
+ uint32_t crc, poly;
+ int i, j, cpu, size, err = 0;
+
+ int translate_buffer_size = 512; // must be a power of 2
+ translate_buffer_mask = translate_buffer_size / sizeof(per_cpu(translate_buffer, 0)[0]) - 1;
+
+ for_each_present_cpu(cpu) {
+ per_cpu(cookie_next_key, cpu) = nr_cpu_ids + cpu;
+
+ size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint64_t);
+ per_cpu(cookie_keys, cpu) = (uint64_t *)kmalloc(size, GFP_KERNEL);
+ if (!per_cpu(cookie_keys, cpu)) {
+ err = -ENOMEM;
+ goto cookie_setup_error;
+ }
+ memset(per_cpu(cookie_keys, cpu), 0, size);
+
+ size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint32_t);
+ per_cpu(cookie_values, cpu) = (uint32_t *)kmalloc(size, GFP_KERNEL);
+ if (!per_cpu(cookie_values, cpu)) {
+ err = -ENOMEM;
+ goto cookie_setup_error;
+ }
+ memset(per_cpu(cookie_values, cpu), 0, size);
+
+ per_cpu(translate_buffer, cpu) = (void **)kmalloc(translate_buffer_size, GFP_KERNEL);
+ if (!per_cpu(translate_buffer, cpu)) {
+ err = -ENOMEM;
+ goto cookie_setup_error;
+ }
+
+ per_cpu(translate_buffer_write, cpu) = 0;
+ per_cpu(translate_buffer_read, cpu) = 0;
+
+ per_cpu(translate_text, cpu) = (char *)kmalloc(TRANSLATE_SIZE, GFP_KERNEL);
+ if (!per_cpu(translate_text, cpu)) {
+ err = -ENOMEM;
+ goto cookie_setup_error;
+ }
+ }
+
+ // build CRC32 table
+ poly = 0x04c11db7;
+ gator_crc32_table = (uint32_t *)kmalloc(256 * sizeof(uint32_t), GFP_KERNEL);
+ for (i = 0; i < 256; i++) {
+ crc = i;
+ for (j = 8; j > 0; j--) {
+ if (crc & 1) {
+ crc = (crc >> 1) ^ poly;
+ } else {
+ crc >>= 1;
+ }
+ }
+ gator_crc32_table[i] = crc;
+ }
+
+ setup_timer(&app_process_wake_up_timer, app_process_wake_up_handler, 0);
+
+cookie_setup_error:
+ return err;
+}
+
+static void cookies_release(void)
+{
+ int cpu;
+
+ for_each_present_cpu(cpu) {
+ kfree(per_cpu(cookie_keys, cpu));
+ per_cpu(cookie_keys, cpu) = NULL;
+
+ kfree(per_cpu(cookie_values, cpu));
+ per_cpu(cookie_values, cpu) = NULL;
+
+ kfree(per_cpu(translate_buffer, cpu));
+ per_cpu(translate_buffer, cpu) = NULL;
+ per_cpu(translate_buffer_read, cpu) = 0;
+ per_cpu(translate_buffer_write, cpu) = 0;
+
+ kfree(per_cpu(translate_text, cpu));
+ per_cpu(translate_text, cpu) = NULL;
+ }
+
+ del_timer_sync(&app_process_wake_up_timer);
+ kfree(gator_crc32_table);
+ gator_crc32_table = NULL;
+}
diff --git a/drivers/gator/gator_events.sh b/drivers/gator/gator_events.sh
new file mode 100755
index 000000000000..5467dd6d17d6
--- /dev/null
+++ b/drivers/gator/gator_events.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+EVENTS=`grep gator_events_init *.c | sed 's/.\+gator_events_init(\(.\+\)).\+/\1/'`
+
+(
+ echo /\* This file is auto generated \*/
+ echo
+ for EVENT in $EVENTS; do
+ echo __weak int $EVENT\(void\)\;
+ done
+ echo
+ echo static int \(*gator_events_list[]\)\(void\) = {
+ for EVENT in $EVENTS; do
+ echo \ $EVENT,
+ done
+ echo }\;
+) > $1.tmp
+
+cmp -s $1 $1.tmp && rm $1.tmp || mv $1.tmp $1
diff --git a/drivers/gator/gator_events_armv6.c b/drivers/gator/gator_events_armv6.c
new file mode 100644
index 000000000000..4f1bca6e2dbe
--- /dev/null
+++ b/drivers/gator/gator_events_armv6.c
@@ -0,0 +1,244 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "gator.h"
+
+// gator_events_perf_pmu.c is used if perf is supported
+#if GATOR_NO_PERF_SUPPORT
+
+static const char *pmnc_name;
+
+/*
+ * Per-CPU PMCR
+ */
+#define PMCR_E (1 << 0) /* Enable */
+#define PMCR_P (1 << 1) /* Count reset */
+#define PMCR_C (1 << 2) /* Cycle counter reset */
+#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */
+#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */
+#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */
+
+#define PMN0 0
+#define PMN1 1
+#define CCNT 2
+#define CNTMAX (CCNT+1)
+
+static int pmnc_counters = 0;
+static unsigned long pmnc_enabled[CNTMAX];
+static unsigned long pmnc_event[CNTMAX];
+static unsigned long pmnc_key[CNTMAX];
+
+static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
+
+static inline void armv6_pmnc_write(u32 val)
+{
+ /* upper 4bits and 7, 11 are write-as-0 */
+ val &= 0x0ffff77f;
+ asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val));
+}
+
+static inline u32 armv6_pmnc_read(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val));
+ return val;
+}
+
+static void armv6_pmnc_reset_counter(unsigned int cnt)
+{
+ u32 val = 0;
+ switch (cnt) {
+ case CCNT:
+ asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val));
+ break;
+ case PMN0:
+ asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val));
+ break;
+ case PMN1:
+ asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val));
+ break;
+ }
+}
+
+int gator_events_armv6_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int i;
+
+ pmnc_counters = 3;
+
+ for (i = PMN0; i <= CCNT; i++) {
+ char buf[40];
+ if (i == CCNT) {
+ snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
+ } else {
+ snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i);
+ }
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
+ if (i != CCNT) {
+ gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int gator_events_armv6_online(int **buffer, bool migrate)
+{
+ unsigned int cnt, len = 0, cpu = smp_processor_id();
+ u32 pmnc;
+
+ if (armv6_pmnc_read() & PMCR_E) {
+ armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E);
+ }
+
+ /* initialize PMNC, reset overflow, D bit, C bit and P bit. */
+ armv6_pmnc_write(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT |
+ PMCR_C | PMCR_P);
+
+ /* configure control register */
+ for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) {
+ unsigned long event;
+
+ if (!pmnc_enabled[cnt])
+ continue;
+
+ event = pmnc_event[cnt] & 255;
+
+ // Set event (if destined for PMNx counters)
+ if (cnt == PMN0) {
+ pmnc |= event << 20;
+ } else if (cnt == PMN1) {
+ pmnc |= event << 12;
+ }
+
+ // Reset counter
+ armv6_pmnc_reset_counter(cnt);
+ }
+ armv6_pmnc_write(pmnc | PMCR_E);
+
+ // return zero values, no need to read as the counters were just reset
+ for (cnt = PMN0; cnt <= CCNT; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = 0;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static int gator_events_armv6_offline(int **buffer, bool migrate)
+{
+ unsigned int cnt;
+
+ armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E);
+ for (cnt = PMN0; cnt <= CCNT; cnt++) {
+ armv6_pmnc_reset_counter(cnt);
+ }
+
+ return 0;
+}
+
+static void gator_events_armv6_stop(void)
+{
+ unsigned int cnt;
+
+ for (cnt = PMN0; cnt <= CCNT; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ }
+}
+
+static int gator_events_armv6_read(int **buffer)
+{
+ int cnt, len = 0;
+ int cpu = smp_processor_id();
+
+ // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled
+ if (!(armv6_pmnc_read() & PMCR_E)) {
+ return 0;
+ }
+
+ for (cnt = PMN0; cnt <= CCNT; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ u32 value = 0;
+ switch (cnt) {
+ case CCNT:
+ asm volatile("mrc p15, 0, %0, c15, c12, 1" : "=r" (value));
+ break;
+ case PMN0:
+ asm volatile("mrc p15, 0, %0, c15, c12, 2" : "=r" (value));
+ break;
+ case PMN1:
+ asm volatile("mrc p15, 0, %0, c15, c12, 3" : "=r" (value));
+ break;
+ }
+ armv6_pmnc_reset_counter(cnt);
+
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = value;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static struct gator_interface gator_events_armv6_interface = {
+ .create_files = gator_events_armv6_create_files,
+ .stop = gator_events_armv6_stop,
+ .online = gator_events_armv6_online,
+ .offline = gator_events_armv6_offline,
+ .read = gator_events_armv6_read,
+};
+
+int gator_events_armv6_init(void)
+{
+ unsigned int cnt;
+
+ switch (gator_cpuid()) {
+ case ARM1136:
+ case ARM1156:
+ case ARM1176:
+ pmnc_name = "ARM11";
+ break;
+ case ARM11MPCORE:
+ pmnc_name = "ARM11MPCore";
+ break;
+ default:
+ return -1;
+ }
+
+ for (cnt = PMN0; cnt <= CCNT; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ pmnc_key[cnt] = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_armv6_interface);
+}
+
+gator_events_init(gator_events_armv6_init);
+
+#else
+int gator_events_armv6_init(void)
+{
+ return -1;
+}
+#endif
diff --git a/drivers/gator/gator_events_armv7.c b/drivers/gator/gator_events_armv7.c
new file mode 100644
index 000000000000..58f29566eeab
--- /dev/null
+++ b/drivers/gator/gator_events_armv7.c
@@ -0,0 +1,319 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* Disabling interrupts
+ * Many of the functions below disable interrupts via local_irq_save(). This disabling of interrupts is done to prevent any race conditions
+ * between multiple entities (e.g. hrtimer interrupts and event based interrupts) calling the same functions. As accessing the pmu involves
+ * several steps (disable, select, read, enable), these steps must be performed atomically. Normal synchronization routines cannot be used
+ * as these functions are being called from interrupt context.
+ */
+
+#include "gator.h"
+
+// gator_events_perf_pmu.c is used if perf is supported
+#if GATOR_NO_PERF_SUPPORT
+
+// Per-CPU PMNC: config reg
+#define PMNC_E (1 << 0) /* Enable all counters */
+#define PMNC_P (1 << 1) /* Reset all counters */
+#define PMNC_C (1 << 2) /* Cycle counter reset */
+#define PMNC_MASK 0x3f /* Mask for writable bits */
+
+// ccnt reg
+#define CCNT_REG (1 << 31)
+
+#define CCNT 0
+#define CNT0 1
+#define CNTMAX (6+1)
+
+static const char *pmnc_name;
+static int pmnc_counters;
+
+static unsigned long pmnc_enabled[CNTMAX];
+static unsigned long pmnc_event[CNTMAX];
+static unsigned long pmnc_key[CNTMAX];
+
+static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
+
+inline void armv7_pmnc_write(u32 val)
+{
+ val &= PMNC_MASK;
+ asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
+}
+
+inline u32 armv7_pmnc_read(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
+ return val;
+}
+
+inline u32 armv7_ccnt_read(u32 reset_value)
+{
+ unsigned long flags;
+ u32 newval = -reset_value;
+ u32 den = CCNT_REG;
+ u32 val;
+
+ local_irq_save(flags);
+ asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable
+ asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); // read
+ asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (newval)); // new value
+ asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable
+ local_irq_restore(flags);
+
+ return val;
+}
+
+inline u32 armv7_cntn_read(unsigned int cnt, u32 reset_value)
+{
+ unsigned long flags;
+ u32 newval = -reset_value;
+ u32 sel = (cnt - CNT0);
+ u32 den = 1 << sel;
+ u32 oldval;
+
+ local_irq_save(flags);
+ asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable
+ asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (sel)); // select
+ asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (oldval)); // read
+ asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (newval)); // new value
+ asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable
+ local_irq_restore(flags);
+
+ return oldval;
+}
+
+static inline void armv7_pmnc_disable_interrupt(unsigned int cnt)
+{
+ u32 val = cnt ? (1 << (cnt - CNT0)) : (1 << 31);
+ asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val));
+}
+
+inline u32 armv7_pmnc_reset_interrupt(void)
+{
+ // Get and reset overflow status flags
+ u32 flags;
+ asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (flags));
+ flags &= 0x8000003f;
+ asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (flags));
+ return flags;
+}
+
+static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
+{
+ u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG;
+ asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
+ return cnt;
+}
+
+static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
+{
+ u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG;
+ asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
+ return cnt;
+}
+
+static inline int armv7_pmnc_select_counter(unsigned int cnt)
+{
+ u32 val = (cnt - CNT0);
+ asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
+ return cnt;
+}
+
+static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
+{
+ if (armv7_pmnc_select_counter(cnt) == cnt) {
+ asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
+ }
+}
+
+static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int i;
+
+ for (i = 0; i < pmnc_counters; i++) {
+ char buf[40];
+ if (i == 0) {
+ snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
+ } else {
+ snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i - 1);
+ }
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
+ if (i > 0) {
+ gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int gator_events_armv7_online(int **buffer, bool migrate)
+{
+ unsigned int cnt, len = 0, cpu = smp_processor_id();
+
+ if (armv7_pmnc_read() & PMNC_E) {
+ armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
+ }
+
+ // Initialize & Reset PMNC: C bit and P bit
+ armv7_pmnc_write(PMNC_P | PMNC_C);
+
+ // Reset overflow flags
+ armv7_pmnc_reset_interrupt();
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ unsigned long event;
+
+ if (!pmnc_enabled[cnt])
+ continue;
+
+ // Disable counter
+ armv7_pmnc_disable_counter(cnt);
+
+ event = pmnc_event[cnt] & 255;
+
+ // Set event (if destined for PMNx counters), we don't need to set the event if it's a cycle count
+ if (cnt != CCNT)
+ armv7_pmnc_write_evtsel(cnt, event);
+
+ armv7_pmnc_disable_interrupt(cnt);
+
+ // Reset counter
+ cnt ? armv7_cntn_read(cnt, 0) : armv7_ccnt_read(0);
+
+ // Enable counter
+ armv7_pmnc_enable_counter(cnt);
+ }
+
+ // enable
+ armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
+
+ // return zero values, no need to read as the counters were just reset
+ for (cnt = 0; cnt < pmnc_counters; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = 0;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static int gator_events_armv7_offline(int **buffer, bool migrate)
+{
+ // disable all counters, including PMCCNTR; overflow IRQs will not be signaled
+ armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
+
+ return 0;
+}
+
+static void gator_events_armv7_stop(void)
+{
+ unsigned int cnt;
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ }
+}
+
+static int gator_events_armv7_read(int **buffer)
+{
+ int cnt, len = 0;
+ int cpu = smp_processor_id();
+
+ // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled
+ if (!(armv7_pmnc_read() & PMNC_E)) {
+ return 0;
+ }
+
+ for (cnt = 0; cnt < pmnc_counters; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ int value;
+ if (cnt == CCNT) {
+ value = armv7_ccnt_read(0);
+ } else {
+ value = armv7_cntn_read(cnt, 0);
+ }
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = value;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static struct gator_interface gator_events_armv7_interface = {
+ .create_files = gator_events_armv7_create_files,
+ .stop = gator_events_armv7_stop,
+ .online = gator_events_armv7_online,
+ .offline = gator_events_armv7_offline,
+ .read = gator_events_armv7_read,
+};
+
+int gator_events_armv7_init(void)
+{
+ unsigned int cnt;
+
+ switch (gator_cpuid()) {
+ case CORTEX_A5:
+ pmnc_name = "Cortex-A5";
+ pmnc_counters = 2;
+ break;
+ case CORTEX_A7:
+ pmnc_name = "Cortex-A7";
+ pmnc_counters = 4;
+ break;
+ case CORTEX_A8:
+ pmnc_name = "Cortex-A8";
+ pmnc_counters = 4;
+ break;
+ case CORTEX_A9:
+ pmnc_name = "Cortex-A9";
+ pmnc_counters = 6;
+ break;
+ case CORTEX_A15:
+ pmnc_name = "Cortex-A15";
+ pmnc_counters = 6;
+ break;
+ default:
+ return -1;
+ }
+
+ pmnc_counters++; // CNT[n] + CCNT
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ pmnc_key[cnt] = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_armv7_interface);
+}
+
+gator_events_init(gator_events_armv7_init);
+
+#else
+int gator_events_armv7_init(void)
+{
+ return -1;
+}
+#endif
diff --git a/drivers/gator/gator_events_block.c b/drivers/gator/gator_events_block.c
new file mode 100644
index 000000000000..56c6a6736529
--- /dev/null
+++ b/drivers/gator/gator_events_block.c
@@ -0,0 +1,155 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "gator.h"
+#include <trace/events/block.h>
+
+#define BLOCK_RQ_WR 0
+#define BLOCK_RQ_RD 1
+
+#define BLOCK_TOTAL (BLOCK_RQ_RD+1)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+#define EVENTWRITE REQ_RW
+#else
+#define EVENTWRITE REQ_WRITE
+#endif
+
+static ulong block_rq_wr_enabled;
+static ulong block_rq_rd_enabled;
+static ulong block_rq_wr_key;
+static ulong block_rq_rd_key;
+static atomic_t blockCnt[BLOCK_TOTAL];
+static int blockGet[BLOCK_TOTAL * 4];
+
+GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq))
+{
+ int write, size;
+
+ if (!rq)
+ return;
+
+ write = rq->cmd_flags & EVENTWRITE;
+ size = rq->resid_len;
+
+ if (!size)
+ return;
+
+ if (write) {
+ if (block_rq_wr_enabled) {
+ atomic_add(size, &blockCnt[BLOCK_RQ_WR]);
+ }
+ } else {
+ if (block_rq_rd_enabled) {
+ atomic_add(size, &blockCnt[BLOCK_RQ_RD]);
+ }
+ }
+}
+
+static int gator_events_block_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+
+ /* block_complete_wr */
+ dir = gatorfs_mkdir(sb, root, "Linux_block_rq_wr");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &block_rq_wr_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_wr_key);
+
+ /* block_complete_rd */
+ dir = gatorfs_mkdir(sb, root, "Linux_block_rq_rd");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &block_rq_rd_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_rd_key);
+
+ return 0;
+}
+
+static int gator_events_block_start(void)
+{
+ // register tracepoints
+ if (block_rq_wr_enabled || block_rq_rd_enabled)
+ if (GATOR_REGISTER_TRACE(block_rq_complete))
+ goto fail_block_rq_exit;
+ pr_debug("gator: registered block event tracepoints\n");
+
+ return 0;
+
+ // unregister tracepoints on error
+fail_block_rq_exit:
+ pr_err("gator: block event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
+
+ return -1;
+}
+
+static void gator_events_block_stop(void)
+{
+ if (block_rq_wr_enabled || block_rq_rd_enabled)
+ GATOR_UNREGISTER_TRACE(block_rq_complete);
+ pr_debug("gator: unregistered block event tracepoints\n");
+
+ block_rq_wr_enabled = 0;
+ block_rq_rd_enabled = 0;
+}
+
+static int gator_events_block_read(int **buffer)
+{
+ int len, value, data = 0;
+
+ if (!on_primary_core()) {
+ return 0;
+ }
+
+ len = 0;
+ if (block_rq_wr_enabled && (value = atomic_read(&blockCnt[BLOCK_RQ_WR])) > 0) {
+ atomic_sub(value, &blockCnt[BLOCK_RQ_WR]);
+ blockGet[len++] = block_rq_wr_key;
+ blockGet[len++] = 0; // indicates to Streamline that value bytes were written now, not since the last message
+ blockGet[len++] = block_rq_wr_key;
+ blockGet[len++] = value;
+ data += value;
+ }
+ if (block_rq_rd_enabled && (value = atomic_read(&blockCnt[BLOCK_RQ_RD])) > 0) {
+ atomic_sub(value, &blockCnt[BLOCK_RQ_RD]);
+ blockGet[len++] = block_rq_rd_key;
+ blockGet[len++] = 0; // indicates to Streamline that value bytes were read now, not since the last message
+ blockGet[len++] = block_rq_rd_key;
+ blockGet[len++] = value;
+ data += value;
+ }
+
+ if (buffer)
+ *buffer = blockGet;
+
+ return len;
+}
+
+static struct gator_interface gator_events_block_interface = {
+ .create_files = gator_events_block_create_files,
+ .start = gator_events_block_start,
+ .stop = gator_events_block_stop,
+ .read = gator_events_block_read,
+};
+
+int gator_events_block_init(void)
+{
+ block_rq_wr_enabled = 0;
+ block_rq_rd_enabled = 0;
+
+ block_rq_wr_key = gator_events_get_key();
+ block_rq_rd_key = gator_events_get_key();
+
+ return gator_events_install(&gator_events_block_interface);
+}
+
+gator_events_init(gator_events_block_init);
diff --git a/drivers/gator/gator_events_ccn-504.c b/drivers/gator/gator_events_ccn-504.c
new file mode 100644
index 000000000000..b91a9a149b90
--- /dev/null
+++ b/drivers/gator/gator_events_ccn-504.c
@@ -0,0 +1,306 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*******************************************************************************
+ * WARNING: This code is an experimental implementation of the CCN-504 hardware
+ * counters which has not been tested on the hardware. Commented debug
+ * statements are present and can be uncommented for diagnostic purposes.
+ ******************************************************************************/
+
+#include <linux/io.h>
+#include <linux/module.h>
+
+#include "gator.h"
+
+#define PERIPHBASE 0x2E000000
+
+#define NUM_REGIONS 256
+#define REGION_SIZE (64*1024)
+#define REGION_DEBUG 1
+#define REGION_XP 64
+
+// DT (Debug) region
+#define PMEVCNTSR0 0x0150
+#define PMCCNTRSR 0x0190
+#define PMCR 0x01A8
+#define PMSR 0x01B0
+#define PMSR_REQ 0x01B8
+#define PMSR_CLR 0x01C0
+
+// XP region
+#define DT_CONFIG 0x0300
+
+// Multiple
+#define PMU_EVENT_SEL 0x0600
+#define OLY_ID 0xFF00
+
+#define CCNT 4
+#define CNTMAX (4 + 1)
+
+#define get_pmu_event_id(event) (((event) >> 0) & 0xFF)
+#define get_node_type(event) (((event) >> 8) & 0xFF)
+#define get_region(event) (((event) >> 16) & 0xFF)
+
+MODULE_PARM_DESC(ccn504_addr, "CCN-504 physical base address");
+static unsigned long ccn504_addr = 0;
+module_param(ccn504_addr, ulong, 0444);
+
+static void __iomem *gator_events_ccn504_base;
+static unsigned long gator_events_ccn504_enabled[CNTMAX];
+static unsigned long gator_events_ccn504_event[CNTMAX];
+static unsigned long gator_events_ccn504_key[CNTMAX];
+static int gator_events_ccn504_buffer[2*CNTMAX];
+
+static void gator_events_ccn504_create_shutdown(void)
+{
+ if (gator_events_ccn504_base != NULL) {
+ iounmap(gator_events_ccn504_base);
+ }
+}
+
+static int gator_events_ccn504_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int i;
+ char buf[32];
+
+ for (i = 0; i < CNTMAX; ++i) {
+ if (i == CCNT) {
+ snprintf(buf, sizeof(buf), "CCN-504_ccnt");
+ } else {
+ snprintf(buf, sizeof(buf), "CCN-504_cnt%i", i);
+ }
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (!dir) {
+ return -1;
+ }
+
+ gatorfs_create_ulong(sb, dir, "enabled", &gator_events_ccn504_enabled[i]);
+ if (i != CCNT) {
+ gatorfs_create_ulong(sb, dir, "event", &gator_events_ccn504_event[i]);
+ }
+ gatorfs_create_ro_ulong(sb, dir, "key", &gator_events_ccn504_key[i]);
+ }
+
+ return 0;
+}
+
+static void gator_events_ccn504_set_dt_config(int xp_node_id, int event_num, int value)
+{
+ u32 dt_config;
+
+ dt_config = readl(gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG);
+ dt_config |= (value + event_num) << (4*event_num);
+ //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, dt_config, (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG);
+ writel(dt_config, gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG);
+}
+
+static int gator_events_ccn504_start(void)
+{
+ int i;
+
+ // Disable INTREQ on overflow
+ // [6] ovfl_intr_en = 0
+ // perhaps set to 1?
+ // [5] cntr_rst = 0
+ // No register paring
+ // [4:1] cntcfg = 0
+ // Enable PMU features
+ // [0] pmu_en = 1
+ //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, 0x1, REGION_DEBUG*REGION_SIZE + PMCR);
+ writel(0x1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMCR);
+
+ // Assume no other pmu_event_sel registers are set
+
+ // cycle counter does not need to be enabled
+ for (i = 0; i < CCNT; ++i) {
+ int pmu_event_id;
+ int node_type;
+ int region;
+ u32 pmu_event_sel;
+ u32 oly_id_whole;
+ u32 oly_id;
+ u32 node_id;
+
+ if (!gator_events_ccn504_enabled[i]) {
+ continue;
+ }
+
+ pmu_event_id = get_pmu_event_id(gator_events_ccn504_event[i]);
+ node_type = get_node_type(gator_events_ccn504_event[i]);
+ region = get_region(gator_events_ccn504_event[i]);
+ //printk(KERN_ERR "%s(%s:%i) pmu_event_id: %x node_type: %x region: %x\n", __FUNCTION__, __FILE__, __LINE__, pmu_event_id, node_type, region);
+
+ // Verify the node_type
+ oly_id_whole = readl(gator_events_ccn504_base + region*REGION_SIZE + OLY_ID);
+ oly_id = oly_id_whole & 0x1F;
+ node_id = (oly_id_whole >> 8) & 0x7F;
+ if ((oly_id != node_type) ||
+ ((node_type == 0x16) && ((oly_id == 0x14) || (oly_id == 0x15) || (oly_id == 0x16) || (oly_id == 0x18) || (oly_id == 0x19) || (oly_id == 0x1A)))) {
+ printk(KERN_ERR "%s(%s:%i) oly_id is %x expected %x\n", __FUNCTION__, __FILE__, __LINE__, oly_id, node_type);
+ return -1;
+ }
+
+ // Set the control register
+ pmu_event_sel = readl(gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL);
+ switch (node_type) {
+ case 0x08: // XP
+ pmu_event_sel |= pmu_event_id << (7*i);
+ gator_events_ccn504_set_dt_config(node_id, i, 0x4);
+ break;
+ case 0x04: // HN-F
+ case 0x16: // RN-I
+ case 0x10: // SBAS
+ pmu_event_sel |= pmu_event_id << (4*i);
+ gator_events_ccn504_set_dt_config(node_id/2, i, (node_id & 1) == 0 ? 0x8 : 0xC);
+ break;
+ }
+ //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, pmu_event_sel, region*REGION_SIZE + PMU_EVENT_SEL);
+ writel(pmu_event_sel, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL);
+ }
+
+ return 0;
+}
+
+static void gator_events_ccn504_stop(void)
+{
+ int i;
+
+ // cycle counter does not need to be disabled
+ for (i = 0; i < CCNT; ++i) {
+ int node_type;
+ int region;
+
+ node_type = get_node_type(gator_events_ccn504_event[i]);
+ region = get_region(gator_events_ccn504_event[i]);
+
+ //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, 0, region*REGION_SIZE + PMU_EVENT_SEL);
+ writel(0, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL);
+ }
+
+ // Clear dt_config
+ for (i = 0; i < 11; ++i) {
+ //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, 0, (REGION_XP + i)*REGION_SIZE + DT_CONFIG);
+ writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG);
+ }
+}
+
+static int gator_events_ccn504_read(int **buffer)
+{
+ int i;
+ int len = 0;
+
+ if (!on_primary_core()) {
+ return 0;
+ }
+
+ // Verify the pmsr register is zero
+ //i = 0;
+ while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) != 0) {
+ //++i;
+ }
+ //printk(KERN_ERR "%s(%s:%i) %i\n", __FUNCTION__, __FILE__, __LINE__, i);
+
+ // Request a PMU snapshot
+ writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_REQ);
+
+ // Wait for the snapshot
+ //i = 0;
+ while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) == 0) {
+ //++i;
+ }
+ //printk(KERN_ERR "%s(%s:%i) %i\n", __FUNCTION__, __FILE__, __LINE__, i);
+
+ // Read the shadow registers
+ for (i = 0; i < CNTMAX; ++i) {
+ if (!gator_events_ccn504_enabled[i]) {
+ continue;
+ }
+
+ gator_events_ccn504_buffer[len++] = gator_events_ccn504_key[i];
+ gator_events_ccn504_buffer[len++] = readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + (i == CCNT ? PMCCNTRSR : PMEVCNTSR0 + 8*i));
+
+ // Are the counters registers cleared when read? Is that what the cntr_rst bit on the pmcr register does?
+ }
+
+ // Clear the PMU snapshot status
+ writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_CLR);
+
+ return len;
+}
+
+static void __maybe_unused gator_events_ccn504_enumerate(int pos, int size)
+{
+ int i;
+ u32 oly_id;
+
+ for (i = pos; i < pos + size; ++i) {
+ oly_id = readl(gator_events_ccn504_base + i*REGION_SIZE + OLY_ID);
+ printk(KERN_ERR "%s(%s:%i) %i %08x\n", __FUNCTION__, __FILE__, __LINE__, i, oly_id);
+ }
+}
+
+static struct gator_interface gator_events_ccn504_interface = {
+ .shutdown = gator_events_ccn504_create_shutdown,
+ .create_files = gator_events_ccn504_create_files,
+ .start = gator_events_ccn504_start,
+ .stop = gator_events_ccn504_stop,
+ .read = gator_events_ccn504_read,
+};
+
+int gator_events_ccn504_init(void)
+{
+ int i;
+
+ if (ccn504_addr == 0) {
+ return -1;
+ }
+
+ gator_events_ccn504_base = ioremap(ccn504_addr, NUM_REGIONS*REGION_SIZE);
+ if (gator_events_ccn504_base == NULL) {
+ printk(KERN_ERR "%s(%s:%i) ioremap returned NULL\n", __FUNCTION__, __FILE__, __LINE__);
+ return -1;
+ }
+ //printk(KERN_ERR "%s(%s:%i)\n", __FUNCTION__, __FILE__, __LINE__);
+
+ // Test - can memory be read
+ {
+ //gator_events_ccn504_enumerate(0, NUM_REGIONS);
+
+#if 0
+ // DT
+ gator_events_ccn504_enumerate(1, 1);
+ // HN-F
+ gator_events_ccn504_enumerate(32, 8);
+ // XP
+ gator_events_ccn504_enumerate(64, 11);
+ // RN-I
+ gator_events_ccn504_enumerate(128, 1);
+ gator_events_ccn504_enumerate(130, 1);
+ gator_events_ccn504_enumerate(134, 1);
+ gator_events_ccn504_enumerate(140, 1);
+ gator_events_ccn504_enumerate(144, 1);
+ gator_events_ccn504_enumerate(148, 1);
+ // SBAS
+ gator_events_ccn504_enumerate(129, 1);
+ gator_events_ccn504_enumerate(137, 1);
+ gator_events_ccn504_enumerate(139, 1);
+ gator_events_ccn504_enumerate(147, 1);
+#endif
+ }
+
+ for (i = 0; i < CNTMAX; ++i) {
+ gator_events_ccn504_enabled[i] = 0;
+ gator_events_ccn504_event[i] = 0;
+ gator_events_ccn504_key[i] = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_ccn504_interface);
+}
+
+gator_events_init(gator_events_ccn504_init);
diff --git a/drivers/gator/gator_events_irq.c b/drivers/gator/gator_events_irq.c
new file mode 100644
index 000000000000..b4df7faefff8
--- /dev/null
+++ b/drivers/gator/gator_events_irq.c
@@ -0,0 +1,167 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "gator.h"
+#include <trace/events/irq.h>
+
+#define HARDIRQ 0
+#define SOFTIRQ 1
+#define TOTALIRQ (SOFTIRQ+1)
+
+static ulong hardirq_enabled;
+static ulong softirq_enabled;
+static ulong hardirq_key;
+static ulong softirq_key;
+static DEFINE_PER_CPU(atomic_t[TOTALIRQ], irqCnt);
+static DEFINE_PER_CPU(int[TOTALIRQ * 2], irqGet);
+
+GATOR_DEFINE_PROBE(irq_handler_exit,
+ TP_PROTO(int irq, struct irqaction *action, int ret))
+{
+ atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[HARDIRQ]);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(struct softirq_action *h, struct softirq_action *vec))
+#else
+GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(unsigned int vec_nr))
+#endif
+{
+ atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[SOFTIRQ]);
+}
+
+static int gator_events_irq_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+
+ /* irq */
+ dir = gatorfs_mkdir(sb, root, "Linux_irq_irq");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &hardirq_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &hardirq_key);
+
+ /* soft irq */
+ dir = gatorfs_mkdir(sb, root, "Linux_irq_softirq");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &softirq_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &softirq_key);
+
+ return 0;
+}
+
+static int gator_events_irq_online(int **buffer, bool migrate)
+{
+ int len = 0, cpu = get_physical_cpu();
+
+ // synchronization with the irq_exit functions is not necessary as the values are being reset
+ if (hardirq_enabled) {
+ atomic_set(&per_cpu(irqCnt, cpu)[HARDIRQ], 0);
+ per_cpu(irqGet, cpu)[len++] = hardirq_key;
+ per_cpu(irqGet, cpu)[len++] = 0;
+ }
+
+ if (softirq_enabled) {
+ atomic_set(&per_cpu(irqCnt, cpu)[SOFTIRQ], 0);
+ per_cpu(irqGet, cpu)[len++] = softirq_key;
+ per_cpu(irqGet, cpu)[len++] = 0;
+ }
+
+ if (buffer)
+ *buffer = per_cpu(irqGet, cpu);
+
+ return len;
+}
+
+static int gator_events_irq_start(void)
+{
+ // register tracepoints
+ if (hardirq_enabled)
+ if (GATOR_REGISTER_TRACE(irq_handler_exit))
+ goto fail_hardirq_exit;
+ if (softirq_enabled)
+ if (GATOR_REGISTER_TRACE(softirq_exit))
+ goto fail_softirq_exit;
+ pr_debug("gator: registered irq tracepoints\n");
+
+ return 0;
+
+ // unregister tracepoints on error
+fail_softirq_exit:
+ if (hardirq_enabled)
+ GATOR_UNREGISTER_TRACE(irq_handler_exit);
+fail_hardirq_exit:
+ pr_err("gator: irq tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
+
+ return -1;
+}
+
+static void gator_events_irq_stop(void)
+{
+ if (hardirq_enabled)
+ GATOR_UNREGISTER_TRACE(irq_handler_exit);
+ if (softirq_enabled)
+ GATOR_UNREGISTER_TRACE(softirq_exit);
+ pr_debug("gator: unregistered irq tracepoints\n");
+
+ hardirq_enabled = 0;
+ softirq_enabled = 0;
+}
+
+static int gator_events_irq_read(int **buffer)
+{
+ int len, value;
+ int cpu = get_physical_cpu();
+
+ len = 0;
+ if (hardirq_enabled) {
+ value = atomic_read(&per_cpu(irqCnt, cpu)[HARDIRQ]);
+ atomic_sub(value, &per_cpu(irqCnt, cpu)[HARDIRQ]);
+
+ per_cpu(irqGet, cpu)[len++] = hardirq_key;
+ per_cpu(irqGet, cpu)[len++] = value;
+ }
+
+ if (softirq_enabled) {
+ value = atomic_read(&per_cpu(irqCnt, cpu)[SOFTIRQ]);
+ atomic_sub(value, &per_cpu(irqCnt, cpu)[SOFTIRQ]);
+
+ per_cpu(irqGet, cpu)[len++] = softirq_key;
+ per_cpu(irqGet, cpu)[len++] = value;
+ }
+
+ if (buffer)
+ *buffer = per_cpu(irqGet, cpu);
+
+ return len;
+}
+
+static struct gator_interface gator_events_irq_interface = {
+ .create_files = gator_events_irq_create_files,
+ .online = gator_events_irq_online,
+ .start = gator_events_irq_start,
+ .stop = gator_events_irq_stop,
+ .read = gator_events_irq_read,
+};
+
+int gator_events_irq_init(void)
+{
+ hardirq_key = gator_events_get_key();
+ softirq_key = gator_events_get_key();
+
+ hardirq_enabled = 0;
+ softirq_enabled = 0;
+
+ return gator_events_install(&gator_events_irq_interface);
+}
+
+gator_events_init(gator_events_irq_init);
diff --git a/drivers/gator/gator_events_l2c-310.c b/drivers/gator/gator_events_l2c-310.c
new file mode 100644
index 000000000000..21aa4a214d97
--- /dev/null
+++ b/drivers/gator/gator_events_l2c-310.c
@@ -0,0 +1,210 @@
+/**
+ * l2c310 (L2 Cache Controller) event counters for gator
+ *
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#if defined(CONFIG_OF)
+#include <linux/of.h>
+#include <linux/of_address.h>
+#endif
+#include <asm/hardware/cache-l2x0.h>
+
+#include "gator.h"
+
+#define L2C310_COUNTERS_NUM 2
+
+static struct {
+ unsigned long enabled;
+ unsigned long event;
+ unsigned long key;
+} l2c310_counters[L2C310_COUNTERS_NUM];
+
+static int l2c310_buffer[L2C310_COUNTERS_NUM * 2];
+
+static void __iomem *l2c310_base;
+
+static void gator_events_l2c310_reset_counters(void)
+{
+ u32 val = readl(l2c310_base + L2X0_EVENT_CNT_CTRL);
+
+ val |= ((1 << L2C310_COUNTERS_NUM) - 1) << 1;
+
+ writel(val, l2c310_base + L2X0_EVENT_CNT_CTRL);
+}
+
+static int gator_events_l2c310_create_files(struct super_block *sb,
+ struct dentry *root)
+{
+ int i;
+
+ for (i = 0; i < L2C310_COUNTERS_NUM; i++) {
+ char buf[16];
+ struct dentry *dir;
+
+ snprintf(buf, sizeof(buf), "L2C-310_cnt%d", i);
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (WARN_ON(!dir))
+ return -1;
+ gatorfs_create_ulong(sb, dir, "enabled",
+ &l2c310_counters[i].enabled);
+ gatorfs_create_ulong(sb, dir, "event",
+ &l2c310_counters[i].event);
+ gatorfs_create_ro_ulong(sb, dir, "key",
+ &l2c310_counters[i].key);
+ }
+
+ return 0;
+}
+
+static int gator_events_l2c310_start(void)
+{
+ static const unsigned long l2x0_event_cntx_cfg[L2C310_COUNTERS_NUM] = {
+ L2X0_EVENT_CNT0_CFG,
+ L2X0_EVENT_CNT1_CFG,
+ };
+ int i;
+
+ /* Counter event sources */
+ for (i = 0; i < L2C310_COUNTERS_NUM; i++)
+ writel((l2c310_counters[i].event & 0xf) << 2,
+ l2c310_base + l2x0_event_cntx_cfg[i]);
+
+ gator_events_l2c310_reset_counters();
+
+ /* Event counter enable */
+ writel(1, l2c310_base + L2X0_EVENT_CNT_CTRL);
+
+ return 0;
+}
+
+static void gator_events_l2c310_stop(void)
+{
+ /* Event counter disable */
+ writel(0, l2c310_base + L2X0_EVENT_CNT_CTRL);
+}
+
+static int gator_events_l2c310_read(int **buffer)
+{
+ static const unsigned long l2x0_event_cntx_val[L2C310_COUNTERS_NUM] = {
+ L2X0_EVENT_CNT0_VAL,
+ L2X0_EVENT_CNT1_VAL,
+ };
+ int i;
+ int len = 0;
+
+ if (!on_primary_core())
+ return 0;
+
+ for (i = 0; i < L2C310_COUNTERS_NUM; i++) {
+ if (l2c310_counters[i].enabled) {
+ l2c310_buffer[len++] = l2c310_counters[i].key;
+ l2c310_buffer[len++] = readl(l2c310_base +
+ l2x0_event_cntx_val[i]);
+ }
+ }
+
+ /* l2c310 counters are saturating, not wrapping in case of overflow */
+ gator_events_l2c310_reset_counters();
+
+ if (buffer)
+ *buffer = l2c310_buffer;
+
+ return len;
+}
+
+static struct gator_interface gator_events_l2c310_interface = {
+ .create_files = gator_events_l2c310_create_files,
+ .start = gator_events_l2c310_start,
+ .stop = gator_events_l2c310_stop,
+ .read = gator_events_l2c310_read,
+};
+
+#define L2C310_ADDR_PROBE (~0)
+
+MODULE_PARM_DESC(l2c310_addr, "L2C310 physical base address (0 to disable)");
+static unsigned long l2c310_addr = L2C310_ADDR_PROBE;
+module_param(l2c310_addr, ulong, 0444);
+
+static void __iomem *gator_events_l2c310_probe(void)
+{
+ phys_addr_t variants[] = {
+#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_S5PV310)
+ 0x10502000,
+#endif
+#if defined(CONFIG_ARCH_OMAP4)
+ 0x48242000,
+#endif
+#if defined(CONFIG_ARCH_TEGRA)
+ 0x50043000,
+#endif
+#if defined(CONFIG_ARCH_U8500)
+ 0xa0412000,
+#endif
+#if defined(CONFIG_ARCH_VEXPRESS)
+ 0x1e00a000, // A9x4 core tile (HBI-0191)
+ 0x2c0f0000, // New memory map tiles
+#endif
+ };
+ int i;
+ void __iomem *base;
+#if defined(CONFIG_OF)
+ struct device_node *node = of_find_all_nodes(NULL);
+
+ if (node) {
+ of_node_put(node);
+
+ node = of_find_compatible_node(NULL, NULL, "arm,pl310-cache");
+ base = of_iomap(node, 0);
+ of_node_put(node);
+
+ return base;
+ }
+#endif
+
+ for (i = 0; i < ARRAY_SIZE(variants); i++) {
+ base = ioremap(variants[i], SZ_4K);
+ if (base) {
+ u32 cache_id = readl(base + L2X0_CACHE_ID);
+
+ if ((cache_id & 0xff0003c0) == 0x410000c0)
+ return base;
+
+ iounmap(base);
+ }
+ }
+
+ return NULL;
+}
+
+int gator_events_l2c310_init(void)
+{
+ int i;
+
+ if (gator_cpuid() != CORTEX_A5 && gator_cpuid() != CORTEX_A9)
+ return -1;
+
+ if (l2c310_addr == L2C310_ADDR_PROBE)
+ l2c310_base = gator_events_l2c310_probe();
+ else if (l2c310_addr)
+ l2c310_base = ioremap(l2c310_addr, SZ_4K);
+
+ if (!l2c310_base)
+ return -1;
+
+ for (i = 0; i < L2C310_COUNTERS_NUM; i++) {
+ l2c310_counters[i].enabled = 0;
+ l2c310_counters[i].key = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_l2c310_interface);
+}
+
+gator_events_init(gator_events_l2c310_init);
diff --git a/drivers/gator/gator_events_mali_4xx.c b/drivers/gator/gator_events_mali_4xx.c
new file mode 100644
index 000000000000..dd275f70b700
--- /dev/null
+++ b/drivers/gator/gator_events_mali_4xx.c
@@ -0,0 +1,751 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "gator.h"
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+
+#include "linux/mali_linux_trace.h"
+
+#include "gator_events_mali_common.h"
+#include "gator_events_mali_4xx.h"
+
+/*
+ * There are (currently) four different variants of the comms between gator and Mali:
+ * 1 (deprecated): No software counter support
+ * 2 (deprecated): Tracepoint called for each separate s/w counter value as it appears
+ * 3 (default): Single tracepoint for all s/w counters in a bundle.
+ * Interface style 3 is the default if no other is specified. 1 and 2 will be eliminated when
+ * existing Mali DDKs are upgraded.
+ * 4. As above, but for the Utgard (Mali-450) driver.
+ */
+
+#if !defined(GATOR_MALI_INTERFACE_STYLE)
+#define GATOR_MALI_INTERFACE_STYLE (3)
+#endif
+
+#if GATOR_MALI_INTERFACE_STYLE < 4
+#include "mali/mali_mjollnir_profiling_gator_api.h"
+#else
+#include "mali/mali_utgard_profiling_gator_api.h"
+#endif
+
+/*
+ * Check that the MALI_SUPPORT define is set to one of the allowable device codes.
+ */
+#if (MALI_SUPPORT != MALI_4xx)
+#error MALI_SUPPORT set to an invalid device code: expecting MALI_4xx
+#endif
+
+/* gatorfs variables for counter enable state,
+ * the event the counter should count and the
+ * 'key' (a unique id set by gatord and returned
+ * by gator.ko)
+ */
+static unsigned long counter_enabled[NUMBER_OF_EVENTS];
+static unsigned long counter_event[NUMBER_OF_EVENTS];
+static unsigned long counter_key[NUMBER_OF_EVENTS];
+
+/* The data we have recorded */
+static u32 counter_data[NUMBER_OF_EVENTS];
+/* The address to sample (or 0 if samples are sent to us) */
+static u32 *counter_address[NUMBER_OF_EVENTS];
+
+/* An array used to return the data we recorded
+ * as key,value pairs hence the *2
+ */
+static unsigned long counter_dump[NUMBER_OF_EVENTS * 2];
+static unsigned long counter_prev[NUMBER_OF_EVENTS];
+
+/* Note whether tracepoints have been registered */
+static int trace_registered;
+
+/*
+ * These numbers define the actual numbers of each block type that exist in the system. Initially
+ * these are set to the maxima defined above; if the driver is capable of being queried (newer
+ * drivers only) then the values may be revised.
+ */
+static unsigned int n_vp_cores = MAX_NUM_VP_CORES;
+static unsigned int n_l2_cores = MAX_NUM_L2_CACHE_CORES;
+static unsigned int n_fp_cores = MAX_NUM_FP_CORES;
+
+/**
+ * Calculate the difference and handle the overflow.
+ */
+static u32 get_difference(u32 start, u32 end)
+{
+ if (start - end >= 0) {
+ return start - end;
+ }
+
+ // Mali counters are unsigned 32 bit values that wrap.
+ return (4294967295u - end) + start;
+}
+
+/**
+ * Returns non-zero if the given counter ID is an activity counter.
+ */
+static inline int is_activity_counter(unsigned int event_id)
+{
+ return (event_id >= FIRST_ACTIVITY_EVENT &&
+ event_id <= LAST_ACTIVITY_EVENT);
+}
+
+/**
+ * Returns non-zero if the given counter ID is a hardware counter.
+ */
+static inline int is_hw_counter(unsigned int event_id)
+{
+ return (event_id >= FIRST_HW_COUNTER && event_id <= LAST_HW_COUNTER);
+}
+
+/*
+ * These are provided for utgard compatibility.
+ */
+typedef void _mali_profiling_get_mali_version_type(struct _mali_profiling_mali_version *values);
+typedef u32 _mali_profiling_get_l2_counters_type(_mali_profiling_l2_counter_values *values);
+
+#if GATOR_MALI_INTERFACE_STYLE == 2
+/**
+ * Returns non-zero if the given counter ID is a software counter.
+ */
+static inline int is_sw_counter(unsigned int event_id)
+{
+ return (event_id >= FIRST_SW_COUNTER && event_id <= LAST_SW_COUNTER);
+}
+#endif
+
+#if GATOR_MALI_INTERFACE_STYLE == 2
+/*
+ * The Mali DDK uses s64 types to contain software counter values, but gator
+ * can only use a maximum of 32 bits. This function scales a software counter
+ * to an appropriate range.
+ */
+static u32 scale_sw_counter_value(unsigned int event_id, signed long long value)
+{
+ u32 scaled_value;
+
+ switch (event_id) {
+ case COUNTER_GLES_UPLOAD_TEXTURE_TIME:
+ case COUNTER_GLES_UPLOAD_VBO_TIME:
+ scaled_value = (u32)div_s64(value, 1000000);
+ break;
+ default:
+ scaled_value = (u32)value;
+ break;
+ }
+
+ return scaled_value;
+}
+#endif
+
+/* Probe for continuously sampled counter */
+#if 0 //WE_DONT_CURRENTLY_USE_THIS_SO_SUPPRESS_WARNING
+GATOR_DEFINE_PROBE(mali_sample_address, TP_PROTO(unsigned int event_id, u32 *addr))
+{
+ /* Turning on too many pr_debug statements in frequently called functions
+ * can cause stability and/or performance problems
+ */
+ //pr_debug("gator: mali_sample_address %d %d\n", event_id, addr);
+ if (event_id >= ACTIVITY_VP && event_id <= COUNTER_FP3_C1) {
+ counter_address[event_id] = addr;
+ }
+}
+#endif
+
+/* Probe for hardware counter events */
+GATOR_DEFINE_PROBE(mali_hw_counter, TP_PROTO(unsigned int event_id, unsigned int value))
+{
+ /* Turning on too many pr_debug statements in frequently called functions
+ * can cause stability and/or performance problems
+ */
+ //pr_debug("gator: mali_hw_counter %d %d\n", event_id, value);
+ if (is_hw_counter(event_id)) {
+ counter_data[event_id] = value;
+ }
+}
+
+#if GATOR_MALI_INTERFACE_STYLE == 2
+GATOR_DEFINE_PROBE(mali_sw_counter, TP_PROTO(unsigned int event_id, signed long long value))
+{
+ if (is_sw_counter(event_id)) {
+ counter_data[event_id] = scale_sw_counter_value(event_id, value);
+ }
+}
+#endif /* GATOR_MALI_INTERFACE_STYLE == 2 */
+
+#if GATOR_MALI_INTERFACE_STYLE >= 3
+GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void *surface_id, unsigned int *counters))
+{
+ u32 i;
+
+ /* Copy over the values for those counters which are enabled. */
+ for (i = FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) {
+ if (counter_enabled[i]) {
+ counter_data[i] = (u32)(counters[i - FIRST_SW_COUNTER]);
+ }
+ }
+}
+#endif /* GATOR_MALI_INTERFACE_STYLE >= 3 */
+
+/**
+ * Create a single filesystem entry for a specified event.
+ * @param sb the superblock
+ * @param root Filesystem root
+ * @param name The name of the entry to create
+ * @param event The ID of the event
+ * @param create_event_item boolean indicating whether to create an 'event' filesystem entry. True to create.
+ *
+ * @return 0 if ok, non-zero if the create failed.
+ */
+static int create_fs_entry(struct super_block *sb, struct dentry *root, const char *name, int event, int create_event_item)
+{
+ struct dentry *dir;
+
+ dir = gatorfs_mkdir(sb, root, name);
+
+ if (!dir) {
+ return -1;
+ }
+
+ if (create_event_item) {
+ gatorfs_create_ulong(sb, dir, "event", &counter_event[event]);
+ }
+
+ gatorfs_create_ulong(sb, dir, "enabled", &counter_enabled[event]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]);
+
+ return 0;
+}
+
+#if GATOR_MALI_INTERFACE_STYLE > 3
+/*
+ * Read the version info structure if available
+ */
+static void initialise_version_info(void)
+{
+ _mali_profiling_get_mali_version_type *mali_profiling_get_mali_version_symbol;
+
+ mali_profiling_get_mali_version_symbol = symbol_get(_mali_profiling_get_mali_version);
+
+ if (mali_profiling_get_mali_version_symbol) {
+ struct _mali_profiling_mali_version version_info;
+
+ pr_debug("gator: mali online _mali_profiling_get_mali_version symbol @ %p\n",
+ mali_profiling_get_mali_version_symbol);
+
+ /*
+ * Revise the number of each different core type using information derived from the DDK.
+ */
+ mali_profiling_get_mali_version_symbol(&version_info);
+
+ n_fp_cores = version_info.num_of_fp_cores;
+ n_vp_cores = version_info.num_of_vp_cores;
+ n_l2_cores = version_info.num_of_l2_cores;
+
+ /* Release the function - we're done with it. */
+ symbol_put(_mali_profiling_get_mali_version);
+ } else {
+ printk("gator: mali online _mali_profiling_get_mali_version symbol not found\n");
+ }
+}
+#endif
+
+static int create_files(struct super_block *sb, struct dentry *root)
+{
+ int event;
+ const char *mali_name = gator_mali_get_mali_name();
+
+ char buf[40];
+ int core_id;
+ int counter_number;
+
+ pr_debug("gator: Initialising counters with style = %d\n", GATOR_MALI_INTERFACE_STYLE);
+
+#if GATOR_MALI_INTERFACE_STYLE > 3
+ /*
+ * Initialise first: this sets up the number of cores available (on compatible DDK versions).
+ * Ideally this would not need guarding but other parts of the code depend on the interface style being set
+ * correctly; if it is not then the system can enter an inconsistent state.
+ */
+ initialise_version_info();
+#endif
+
+ /* Vertex processor counters */
+ for (core_id = 0; core_id < n_vp_cores; core_id++) {
+ int activity_counter_id = ACTIVITY_VP_0;
+ snprintf(buf, sizeof buf, "ARM_%s_VP_%d_active", mali_name, core_id);
+ if (create_fs_entry(sb, root, buf, activity_counter_id, 0) != 0) {
+ return -1;
+ }
+
+ for (counter_number = 0; counter_number < 2; counter_number++) {
+ int counter_id = COUNTER_VP_0_C0 + (2 * core_id) + counter_number;
+
+ snprintf(buf, sizeof buf, "ARM_%s_VP_%d_cnt%d", mali_name, core_id, counter_number);
+ if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) {
+ return -1;
+ }
+ }
+ }
+
+ /* Fragment processors' counters */
+ for (core_id = 0; core_id < n_fp_cores; core_id++) {
+ int activity_counter_id = ACTIVITY_FP_0 + core_id;
+
+ snprintf(buf, sizeof buf, "ARM_%s_FP_%d_active", mali_name, core_id);
+ if (create_fs_entry(sb, root, buf, activity_counter_id, 0) != 0) {
+ return -1;
+ }
+
+ for (counter_number = 0; counter_number < 2; counter_number++) {
+ int counter_id = COUNTER_FP_0_C0 + (2 * core_id) + counter_number;
+
+ snprintf(buf, sizeof buf, "ARM_%s_FP_%d_cnt%d", mali_name, core_id, counter_number);
+ if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) {
+ return -1;
+ }
+ }
+ }
+
+ /* L2 Cache counters */
+ for (core_id = 0; core_id < n_l2_cores; core_id++) {
+ for (counter_number = 0; counter_number < 2; counter_number++) {
+ int counter_id = COUNTER_L2_0_C0 + (2 * core_id) + counter_number;
+
+ snprintf(buf, sizeof buf, "ARM_%s_L2_%d_cnt%d", mali_name, core_id, counter_number);
+ if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) {
+ return -1;
+ }
+ }
+ }
+
+ /* Now set up the software counter entries */
+ for (event = FIRST_SW_COUNTER; event <= LAST_SW_COUNTER; event++) {
+ snprintf(buf, sizeof(buf), "ARM_%s_SW_%d", mali_name, event - FIRST_SW_COUNTER);
+
+ if (create_fs_entry(sb, root, buf, event, 0) != 0) {
+ return -1;
+ }
+ }
+
+ /* Now set up the special counter entries */
+ snprintf(buf, sizeof(buf), "ARM_%s_Filmstrip_cnt0", mali_name);
+ if (create_fs_entry(sb, root, buf, COUNTER_FILMSTRIP, 1) != 0) {
+ return -1;
+ }
+
+#ifdef DVFS_REPORTED_BY_DDK
+ snprintf(buf, sizeof(buf), "ARM_%s_Frequency", mali_name);
+ if (create_fs_entry(sb, root, buf, COUNTER_FREQUENCY, 1) != 0) {
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "ARM_%s_Voltage", mali_name);
+ if (create_fs_entry(sb, root, buf, COUNTER_VOLTAGE, 1) != 0) {
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+/*
+ * Local store for the get_counters entry point into the DDK.
+ * This is stored here since it is used very regularly.
+ */
+static mali_profiling_get_counters_type *mali_get_counters = NULL;
+static _mali_profiling_get_l2_counters_type *mali_get_l2_counters = NULL;
+
+/*
+ * Examine list of counters between two index limits and determine if any one is enabled.
+ * Returns 1 if any counter is enabled, 0 if none is.
+ */
+static int is_any_counter_enabled(unsigned int first_counter, unsigned int last_counter)
+{
+ unsigned int i;
+
+ for (i = first_counter; i <= last_counter; i++) {
+ if (counter_enabled[i]) {
+ return 1; /* At least one counter is enabled */
+ }
+ }
+
+ return 0; /* No s/w counters enabled */
+}
+
+static void init_counters(unsigned int from_counter, unsigned int to_counter)
+{
+ unsigned int counter_id;
+
+ /* If a Mali driver is present and exporting the appropriate symbol
+ * then we can request the HW counters (of which there are only 2)
+ * be configured to count the desired events
+ */
+ mali_profiling_set_event_type *mali_set_hw_event;
+
+ mali_set_hw_event = symbol_get(_mali_profiling_set_event);
+
+ if (mali_set_hw_event) {
+ pr_debug("gator: mali online _mali_profiling_set_event symbol @ %p\n", mali_set_hw_event);
+
+ for (counter_id = from_counter; counter_id <= to_counter; counter_id++) {
+ if (counter_enabled[counter_id]) {
+ mali_set_hw_event(counter_id, counter_event[counter_id]);
+ } else {
+ mali_set_hw_event(counter_id, 0xFFFFFFFF);
+ }
+ }
+
+ symbol_put(_mali_profiling_set_event);
+ } else {
+ printk("gator: mali online _mali_profiling_set_event symbol not found\n");
+ }
+}
+
+static void mali_counter_initialize(void)
+{
+ int i;
+ int core_id;
+
+ mali_osk_fb_control_set_type *mali_set_fb_event;
+ mali_profiling_control_type *mali_control;
+
+ init_counters(COUNTER_L2_0_C0, COUNTER_L2_0_C0 + (2 * n_l2_cores) - 1);
+ init_counters(COUNTER_VP_0_C0, COUNTER_VP_0_C0 + (2 * n_vp_cores) - 1);
+ init_counters(COUNTER_FP_0_C0, COUNTER_FP_0_C0 + (2 * n_fp_cores) - 1);
+
+ mali_set_fb_event = symbol_get(_mali_osk_fb_control_set);
+
+ if (mali_set_fb_event) {
+ pr_debug("gator: mali online _mali_osk_fb_control_set symbol @ %p\n", mali_set_fb_event);
+
+ mali_set_fb_event(0, (counter_enabled[COUNTER_FILMSTRIP] ? 1 : 0));
+
+ symbol_put(_mali_osk_fb_control_set);
+ } else {
+ printk("gator: mali online _mali_osk_fb_control_set symbol not found\n");
+ }
+
+ /* Generic control interface for Mali DDK. */
+ mali_control = symbol_get(_mali_profiling_control);
+ if (mali_control) {
+ /* The event attribute in the XML file keeps the actual frame rate. */
+ unsigned int rate = counter_event[COUNTER_FILMSTRIP] & 0xff;
+ unsigned int resize_factor = (counter_event[COUNTER_FILMSTRIP] >> 8) & 0xff;
+
+ pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control);
+
+ mali_control(SW_COUNTER_ENABLE, (is_any_counter_enabled(FIRST_SW_COUNTER, LAST_SW_COUNTER) ? 1 : 0));
+ mali_control(FBDUMP_CONTROL_ENABLE, (counter_enabled[COUNTER_FILMSTRIP] ? 1 : 0));
+ mali_control(FBDUMP_CONTROL_RATE, rate);
+ mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor);
+
+ pr_debug("gator: sent mali_control enabled=%d, rate=%d\n", (counter_enabled[COUNTER_FILMSTRIP] ? 1 : 0), rate);
+
+ symbol_put(_mali_profiling_control);
+ } else {
+ printk("gator: mali online _mali_profiling_control symbol not found\n");
+ }
+
+ mali_get_counters = symbol_get(_mali_profiling_get_counters);
+ if (mali_get_counters) {
+ pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", mali_get_counters);
+
+ } else {
+ pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined");
+ }
+
+ mali_get_l2_counters = symbol_get(_mali_profiling_get_l2_counters);
+ if (mali_get_l2_counters) {
+ pr_debug("gator: mali online _mali_profiling_get_l2_counters symbol @ %p\n", mali_get_l2_counters);
+
+ } else {
+ pr_debug("gator WARNING: mali _mali_profiling_get_l2_counters symbol not defined");
+ }
+
+ if (!mali_get_counters && !mali_get_l2_counters) {
+ pr_debug("gator: WARNING: no L2 counters available");
+ n_l2_cores = 0;
+ }
+
+ for (core_id = 0; core_id < n_l2_cores; core_id++) {
+ int counter_id = COUNTER_L2_0_C0 + (2 * core_id);
+ counter_prev[counter_id] = 0;
+ counter_prev[counter_id + 1] = 0;
+ }
+
+ /* Clear counters in the start */
+ for (i = 0; i < NUMBER_OF_EVENTS; i++) {
+ counter_data[i] = 0;
+ }
+}
+
+static void mali_counter_deinitialize(void)
+{
+ mali_profiling_set_event_type *mali_set_hw_event;
+ mali_osk_fb_control_set_type *mali_set_fb_event;
+ mali_profiling_control_type *mali_control;
+
+ mali_set_hw_event = symbol_get(_mali_profiling_set_event);
+
+ if (mali_set_hw_event) {
+ int i;
+
+ pr_debug("gator: mali offline _mali_profiling_set_event symbol @ %p\n", mali_set_hw_event);
+ for (i = FIRST_HW_COUNTER; i <= LAST_HW_COUNTER; i++) {
+ mali_set_hw_event(i, 0xFFFFFFFF);
+ }
+
+ symbol_put(_mali_profiling_set_event);
+ } else {
+ printk("gator: mali offline _mali_profiling_set_event symbol not found\n");
+ }
+
+ mali_set_fb_event = symbol_get(_mali_osk_fb_control_set);
+
+ if (mali_set_fb_event) {
+ pr_debug("gator: mali offline _mali_osk_fb_control_set symbol @ %p\n", mali_set_fb_event);
+
+ mali_set_fb_event(0, 0);
+
+ symbol_put(_mali_osk_fb_control_set);
+ } else {
+ printk("gator: mali offline _mali_osk_fb_control_set symbol not found\n");
+ }
+
+ /* Generic control interface for Mali DDK. */
+ mali_control = symbol_get(_mali_profiling_control);
+
+ if (mali_control) {
+ pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", mali_set_fb_event);
+
+ /* Reset the DDK state - disable counter collection */
+ mali_control(SW_COUNTER_ENABLE, 0);
+
+ mali_control(FBDUMP_CONTROL_ENABLE, 0);
+
+ symbol_put(_mali_profiling_control);
+ } else {
+ printk("gator: mali offline _mali_profiling_control symbol not found\n");
+ }
+
+ if (mali_get_counters) {
+ symbol_put(_mali_profiling_get_counters);
+ }
+
+ if (mali_get_l2_counters) {
+ symbol_put(_mali_profiling_get_l2_counters);
+ }
+}
+
+static int start(void)
+{
+ // register tracepoints
+ if (GATOR_REGISTER_TRACE(mali_hw_counter)) {
+ printk("gator: mali_hw_counter tracepoint failed to activate\n");
+ return -1;
+ }
+
+#if GATOR_MALI_INTERFACE_STYLE == 1
+ /* None. */
+#elif GATOR_MALI_INTERFACE_STYLE == 2
+ /* For patched Mali driver. */
+ if (GATOR_REGISTER_TRACE(mali_sw_counter)) {
+ printk("gator: mali_sw_counter tracepoint failed to activate\n");
+ return -1;
+ }
+#elif GATOR_MALI_INTERFACE_STYLE >= 3
+ /* For Mali drivers with built-in support. */
+ if (GATOR_REGISTER_TRACE(mali_sw_counters)) {
+ printk("gator: mali_sw_counters tracepoint failed to activate\n");
+ return -1;
+ }
+#else
+#error Unknown GATOR_MALI_INTERFACE_STYLE option.
+#endif
+
+ trace_registered = 1;
+
+ mali_counter_initialize();
+ return 0;
+}
+
+static void stop(void)
+{
+ unsigned int cnt;
+
+ pr_debug("gator: mali stop\n");
+
+ if (trace_registered) {
+ GATOR_UNREGISTER_TRACE(mali_hw_counter);
+
+#if GATOR_MALI_INTERFACE_STYLE == 1
+ /* None. */
+#elif GATOR_MALI_INTERFACE_STYLE == 2
+ /* For patched Mali driver. */
+ GATOR_UNREGISTER_TRACE(mali_sw_counter);
+#elif GATOR_MALI_INTERFACE_STYLE >= 3
+ /* For Mali drivers with built-in support. */
+ GATOR_UNREGISTER_TRACE(mali_sw_counters);
+#else
+#error Unknown GATOR_MALI_INTERFACE_STYLE option.
+#endif
+
+ pr_debug("gator: mali timeline tracepoint deactivated\n");
+
+ trace_registered = 0;
+ }
+
+ for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) {
+ counter_enabled[cnt] = 0;
+ counter_event[cnt] = 0;
+ counter_address[cnt] = NULL;
+ }
+
+ mali_counter_deinitialize();
+}
+
+static void dump_counters(unsigned int from_counter, unsigned int to_counter, unsigned int *len)
+{
+ unsigned int counter_id;
+
+ for (counter_id = from_counter; counter_id <= to_counter; counter_id++) {
+ if (counter_enabled[counter_id]) {
+ counter_dump[(*len)++] = counter_key[counter_id];
+ counter_dump[(*len)++] = counter_data[counter_id];
+
+ counter_data[counter_id] = 0;
+ }
+ }
+}
+
+static int read(int **buffer)
+{
+ int len = 0;
+
+ if (!on_primary_core())
+ return 0;
+
+ // Read the L2 C0 and C1 here.
+ if (n_l2_cores > 0 && is_any_counter_enabled(COUNTER_L2_0_C0, COUNTER_L2_0_C0 + (2 * n_l2_cores))) {
+ unsigned int unavailable_l2_caches = 0;
+ _mali_profiling_l2_counter_values cache_values;
+ unsigned int cache_id;
+ struct _mali_profiling_core_counters *per_core;
+
+ /* Poke the driver to get the counter values - older style; only one L2 cache */
+ if (mali_get_l2_counters) {
+ unavailable_l2_caches = mali_get_l2_counters(&cache_values);
+ } else if (mali_get_counters) {
+ per_core = &cache_values.cores[0];
+ mali_get_counters(&per_core->source0, &per_core->value0, &per_core->source1, &per_core->value1);
+ } else {
+ /* This should never happen, as n_l2_caches is only set > 0 if one of the above functions is found. */
+ }
+
+ /* Fill in the two cache counter values for each cache block. */
+ for (cache_id = 0; cache_id < n_l2_cores; cache_id++) {
+ unsigned int counter_id_0 = COUNTER_L2_0_C0 + (2 * cache_id);
+ unsigned int counter_id_1 = counter_id_0 + 1;
+
+ if ((1 << cache_id) & unavailable_l2_caches) {
+ continue; /* This cache is unavailable (powered-off, possibly). */
+ }
+
+ per_core = &cache_values.cores[cache_id];
+
+ if (counter_enabled[counter_id_0]) {
+ // Calculate and save src0's counter val0
+ counter_dump[len++] = counter_key[counter_id_0];
+ counter_dump[len++] = get_difference(per_core->value0, counter_prev[counter_id_0]);
+ }
+
+ if (counter_enabled[counter_id_1]) {
+ // Calculate and save src1's counter val1
+ counter_dump[len++] = counter_key[counter_id_1];
+ counter_dump[len++] = get_difference(per_core->value1, counter_prev[counter_id_1]);
+ }
+
+ // Save the previous values for the counters.
+ counter_prev[counter_id_0] = per_core->value0;
+ counter_prev[counter_id_1] = per_core->value1;
+ }
+ }
+
+ /* Process other (non-timeline) counters. */
+ dump_counters(COUNTER_VP_0_C0, COUNTER_VP_0_C0 + (2 * n_vp_cores) - 1, &len);
+ dump_counters(COUNTER_FP_0_C0, COUNTER_FP_0_C0 + (2 * n_fp_cores) - 1, &len);
+
+ dump_counters(FIRST_SW_COUNTER, LAST_SW_COUNTER, &len);
+
+#ifdef DVFS_REPORTED_BY_DDK
+ {
+ int cnt;
+ /*
+ * Add in the voltage and frequency counters if enabled. Note that, since these are
+ * actually passed as events, the counter value should not be cleared.
+ */
+ cnt = COUNTER_FREQUENCY;
+ if (counter_enabled[cnt]) {
+ counter_dump[len++] = counter_key[cnt];
+ counter_dump[len++] = counter_data[cnt];
+ }
+
+ cnt = COUNTER_VOLTAGE;
+ if (counter_enabled[cnt]) {
+ counter_dump[len++] = counter_key[cnt];
+ counter_dump[len++] = counter_data[cnt];
+ }
+ }
+#endif
+
+ if (buffer) {
+ *buffer = (int *)counter_dump;
+ }
+
+ return len;
+}
+
+static struct gator_interface gator_events_mali_interface = {
+ .create_files = create_files,
+ .start = start,
+ .stop = stop,
+ .read = read,
+};
+
+extern void gator_events_mali_log_dvfs_event(unsigned int frequency_mhz, unsigned int voltage_mv)
+{
+#ifdef DVFS_REPORTED_BY_DDK
+ counter_data[COUNTER_FREQUENCY] = frequency_mhz;
+ counter_data[COUNTER_VOLTAGE] = voltage_mv;
+#endif
+}
+
+int gator_events_mali_init(void)
+{
+ unsigned int cnt;
+
+ pr_debug("gator: mali init\n");
+
+ for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) {
+ counter_enabled[cnt] = 0;
+ counter_event[cnt] = 0;
+ counter_key[cnt] = gator_events_get_key();
+ counter_address[cnt] = NULL;
+ counter_data[cnt] = 0;
+ }
+
+ trace_registered = 0;
+
+ return gator_events_install(&gator_events_mali_interface);
+}
+
+gator_events_init(gator_events_mali_init);
diff --git a/drivers/gator/gator_events_mali_4xx.h b/drivers/gator/gator_events_mali_4xx.h
new file mode 100644
index 000000000000..413ad0ffe794
--- /dev/null
+++ b/drivers/gator/gator_events_mali_4xx.h
@@ -0,0 +1,18 @@
+/**
+ * Copyright (C) ARM Limited 2011-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/*
+ * Header contains common definitions for the Mali-4xx processors.
+ */
+#if !defined(GATOR_EVENTS_MALI_4xx_H)
+#define GATOR_EVENTS_MALI_4xx_H
+
+extern void gator_events_mali_log_dvfs_event(unsigned int d0, unsigned int d1);
+
+#endif /* GATOR_EVENTS_MALI_4xx_H */
diff --git a/drivers/gator/gator_events_mali_common.c b/drivers/gator/gator_events_mali_common.c
new file mode 100644
index 000000000000..5a98b3745fc2
--- /dev/null
+++ b/drivers/gator/gator_events_mali_common.c
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include "gator_events_mali_common.h"
+
+static u32 gator_mali_get_id(void)
+{
+ return MALI_SUPPORT;
+}
+
+extern const char *gator_mali_get_mali_name(void)
+{
+ u32 id = gator_mali_get_id();
+
+ switch (id) {
+ case MALI_T6xx:
+ return "Mali-T6xx";
+ case MALI_4xx:
+ return "Mali-4xx";
+ default:
+ pr_debug("gator: Mali-T6xx: unknown Mali ID (%d)\n", id);
+ return "Mali-Unknown";
+ }
+}
+
+extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter)
+{
+ int err;
+ char buf[255];
+ struct dentry *dir;
+
+ /* If the counter name is empty ignore it */
+ if (strlen(event_name) != 0) {
+ /* Set up the filesystem entry for this event. */
+ snprintf(buf, sizeof(buf), "ARM_%s_%s", mali_name, event_name);
+
+ dir = gatorfs_mkdir(sb, root, buf);
+
+ if (dir == NULL) {
+ pr_debug("gator: Mali-T6xx: error creating file system for: %s (%s)", event_name, buf);
+ return -1;
+ }
+
+ err = gatorfs_create_ulong(sb, dir, "enabled", &counter->enabled);
+ if (err != 0) {
+ pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ulong for: %s (%s)", event_name, buf);
+ return -1;
+ }
+ err = gatorfs_create_ro_ulong(sb, dir, "key", &counter->key);
+ if (err != 0) {
+ pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ro_ulong for: %s (%s)", event_name, buf);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+extern void gator_mali_initialise_counters(mali_counter counters[], unsigned int n_counters)
+{
+ unsigned int cnt;
+
+ for (cnt = 0; cnt < n_counters; cnt++) {
+ mali_counter *counter = &counters[cnt];
+
+ counter->key = gator_events_get_key();
+ counter->enabled = 0;
+ }
+}
diff --git a/drivers/gator/gator_events_mali_common.h b/drivers/gator/gator_events_mali_common.h
new file mode 100644
index 000000000000..d67ee2d245ad
--- /dev/null
+++ b/drivers/gator/gator_events_mali_common.h
@@ -0,0 +1,88 @@
+/**
+ * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#if !defined(GATOR_EVENTS_MALI_COMMON_H)
+#define GATOR_EVENTS_MALI_COMMON_H
+
+#include "gator.h"
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+/* Device codes for each known GPU */
+#define MALI_4xx (0x0b07)
+#define MALI_T6xx (0x0056)
+
+/* Ensure that MALI_SUPPORT has been defined to something. */
+#ifndef MALI_SUPPORT
+#error MALI_SUPPORT not defined!
+#endif
+
+/* Values for the supported activity event types */
+#define ACTIVITY_START (1)
+#define ACTIVITY_STOP (2)
+
+/*
+ * Runtime state information for a counter.
+ */
+typedef struct {
+ unsigned long key; /* 'key' (a unique id set by gatord and returned by gator.ko) */
+ unsigned long enabled; /* counter enable state */
+} mali_counter;
+
+/*
+ * Mali-4xx
+ */
+typedef int mali_profiling_set_event_type(unsigned int, int);
+typedef void mali_osk_fb_control_set_type(unsigned int, unsigned int);
+typedef void mali_profiling_control_type(unsigned int, unsigned int);
+typedef void mali_profiling_get_counters_type(unsigned int *, unsigned int *, unsigned int *, unsigned int *);
+
+/*
+ * Driver entry points for functions called directly by gator.
+ */
+extern int _mali_profiling_set_event(unsigned int, int);
+extern void _mali_osk_fb_control_set(unsigned int, unsigned int);
+extern void _mali_profiling_control(unsigned int, unsigned int);
+extern void _mali_profiling_get_counters(unsigned int *, unsigned int *, unsigned int *, unsigned int *);
+
+/**
+ * Returns a name which identifies the GPU type (eg Mali-4xx, Mali-T6xx).
+ *
+ * @return The name as a constant string.
+ */
+extern const char *gator_mali_get_mali_name(void);
+
+/**
+ * Creates a filesystem entry under /dev/gator relating to the specified event name and key, and
+ * associate the key/enable values with this entry point.
+ *
+ * @param mali_name A name related to the type of GPU, obtained from a call to gator_mali_get_mali_name()
+ * @param event_name The name of the event.
+ * @param sb Linux super block
+ * @param root Directory under which the entry will be created.
+ * @param counter_key Ptr to location which will be associated with the counter key.
+ * @param counter_enabled Ptr to location which will be associated with the counter enable state.
+ *
+ * @return 0 if entry point was created, non-zero if not.
+ */
+extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter);
+
+/**
+ * Initializes the counter array.
+ *
+ * @param keys The array of counters
+ * @param n_counters The number of entries in each of the arrays.
+ */
+extern void gator_mali_initialise_counters(mali_counter counters[], unsigned int n_counters);
+
+#endif /* GATOR_EVENTS_MALI_COMMON_H */
diff --git a/drivers/gator/gator_events_mali_t6xx.c b/drivers/gator/gator_events_mali_t6xx.c
new file mode 100644
index 000000000000..2576a99a126a
--- /dev/null
+++ b/drivers/gator/gator_events_mali_t6xx.c
@@ -0,0 +1,512 @@
+/**
+ * Copyright (C) ARM Limited 2011-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "gator.h"
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+#include "linux/mali_linux_trace.h"
+
+#include "gator_events_mali_common.h"
+
+/*
+ * Check that the MALI_SUPPORT define is set to one of the allowable device codes.
+ */
+#if (MALI_SUPPORT != MALI_T6xx)
+#error MALI_SUPPORT set to an invalid device code: expecting MALI_T6xx
+#endif
+
+/* Counters for Mali-T6xx:
+ *
+ * - Timeline events
+ * They are tracepoints, but instead of reporting a number they report a START/STOP event.
+ * They are reported in Streamline as number of microseconds while that particular counter was active.
+ *
+ * - SW counters
+ * They are tracepoints reporting a particular number.
+ * They are accumulated in sw_counter_data array until they are passed to Streamline, then they are zeroed.
+ *
+ * - Accumulators
+ * They are the same as software counters but their value is not zeroed.
+ */
+
+/* Timeline (start/stop) activity */
+static const char *timeline_event_names[] = {
+ "PM_SHADER_0",
+ "PM_SHADER_1",
+ "PM_SHADER_2",
+ "PM_SHADER_3",
+ "PM_SHADER_4",
+ "PM_SHADER_5",
+ "PM_SHADER_6",
+ "PM_SHADER_7",
+ "PM_TILER_0",
+ "PM_L2_0",
+ "PM_L2_1",
+ "MMU_AS_0",
+ "MMU_AS_1",
+ "MMU_AS_2",
+ "MMU_AS_3"
+};
+
+enum {
+ PM_SHADER_0 = 0,
+ PM_SHADER_1,
+ PM_SHADER_2,
+ PM_SHADER_3,
+ PM_SHADER_4,
+ PM_SHADER_5,
+ PM_SHADER_6,
+ PM_SHADER_7,
+ PM_TILER_0,
+ PM_L2_0,
+ PM_L2_1,
+ MMU_AS_0,
+ MMU_AS_1,
+ MMU_AS_2,
+ MMU_AS_3
+};
+/* The number of shader blocks in the enum above */
+#define NUM_PM_SHADER (8)
+
+/* Software Counters */
+static const char *software_counter_names[] = {
+ "MMU_PAGE_FAULT_0",
+ "MMU_PAGE_FAULT_1",
+ "MMU_PAGE_FAULT_2",
+ "MMU_PAGE_FAULT_3"
+};
+
+enum {
+ MMU_PAGE_FAULT_0 = 0,
+ MMU_PAGE_FAULT_1,
+ MMU_PAGE_FAULT_2,
+ MMU_PAGE_FAULT_3
+};
+
+/* Software Counters */
+static const char *accumulators_names[] = {
+ "TOTAL_ALLOC_PAGES"
+};
+
+enum {
+ TOTAL_ALLOC_PAGES = 0
+};
+
+#define FIRST_TIMELINE_EVENT (0)
+#define NUMBER_OF_TIMELINE_EVENTS (sizeof(timeline_event_names) / sizeof(timeline_event_names[0]))
+#define FIRST_SOFTWARE_COUNTER (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS)
+#define NUMBER_OF_SOFTWARE_COUNTERS (sizeof(software_counter_names) / sizeof(software_counter_names[0]))
+#define FIRST_ACCUMULATOR (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS)
+#define NUMBER_OF_ACCUMULATORS (sizeof(accumulators_names) / sizeof(accumulators_names[0]))
+#define NUMBER_OF_EVENTS (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS)
+
+/*
+ * gatorfs variables for counter enable state
+ */
+static mali_counter counters[NUMBER_OF_EVENTS];
+
+/* An array used to return the data we recorded
+ * as key,value pairs hence the *2
+ */
+static unsigned long counter_dump[NUMBER_OF_EVENTS * 2];
+
+/*
+ * Array holding counter start times (in ns) for each counter. A zero here
+ * indicates that the activity monitored by this counter is not running.
+ */
+static struct timespec timeline_event_starttime[NUMBER_OF_TIMELINE_EVENTS];
+
+/* The data we have recorded */
+static unsigned int timeline_data[NUMBER_OF_TIMELINE_EVENTS];
+static unsigned int sw_counter_data[NUMBER_OF_SOFTWARE_COUNTERS];
+static unsigned int accumulators_data[NUMBER_OF_ACCUMULATORS];
+
+/* Hold the previous timestamp, used to calculate the sample interval. */
+static struct timespec prev_timestamp;
+
+/**
+ * Returns the timespan (in microseconds) between the two specified timestamps.
+ *
+ * @param start Ptr to the start timestamp
+ * @param end Ptr to the end timestamp
+ *
+ * @return Number of microseconds between the two timestamps (can be negative if start follows end).
+ */
+static inline long get_duration_us(const struct timespec *start, const struct timespec *end)
+{
+ long event_duration_us = (end->tv_nsec - start->tv_nsec) / 1000;
+ event_duration_us += (end->tv_sec - start->tv_sec) * 1000000;
+
+ return event_duration_us;
+}
+
+static void record_timeline_event(unsigned int timeline_index, unsigned int type)
+{
+ struct timespec event_timestamp;
+ struct timespec *event_start = &timeline_event_starttime[timeline_index];
+
+ switch (type) {
+ case ACTIVITY_START:
+ /* Get the event time... */
+ getnstimeofday(&event_timestamp);
+
+ /* Remember the start time if the activity is not already started */
+ if (event_start->tv_sec == 0) {
+ *event_start = event_timestamp; /* Structure copy */
+ }
+ break;
+
+ case ACTIVITY_STOP:
+ /* if the counter was started... */
+ if (event_start->tv_sec != 0) {
+ /* Get the event time... */
+ getnstimeofday(&event_timestamp);
+
+ /* Accumulate the duration in us */
+ timeline_data[timeline_index] += get_duration_us(event_start, &event_timestamp);
+
+ /* Reset the start time to indicate the activity is stopped. */
+ event_start->tv_sec = 0;
+ }
+ break;
+
+ default:
+ /* Other activity events are ignored. */
+ break;
+ }
+}
+
+/*
+ * Documentation about the following tracepoints is in mali_linux_trace.h
+ */
+
+GATOR_DEFINE_PROBE(mali_pm_status, TP_PROTO(unsigned int event_id, unsigned long long value))
+{
+#define SHADER_PRESENT_LO 0x100 /* (RO) Shader core present bitmap, low word */
+#define TILER_PRESENT_LO 0x110 /* (RO) Tiler core present bitmap, low word */
+#define L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */
+#define BIT_AT(value, pos) ((value >> pos) & 1)
+
+ static unsigned long long previous_shader_bitmask = 0;
+ static unsigned long long previous_tiler_bitmask = 0;
+ static unsigned long long previous_l2_bitmask = 0;
+
+ switch (event_id) {
+ case SHADER_PRESENT_LO:
+ {
+ unsigned long long changed_bitmask = previous_shader_bitmask ^ value;
+ int pos;
+
+ for (pos = 0; pos < NUM_PM_SHADER; ++pos) {
+ if (BIT_AT(changed_bitmask, pos)) {
+ record_timeline_event(PM_SHADER_0 + pos, BIT_AT(value, pos) ? ACTIVITY_START : ACTIVITY_STOP);
+ }
+ }
+
+ previous_shader_bitmask = value;
+ break;
+ }
+
+ case TILER_PRESENT_LO:
+ {
+ unsigned long long changed = previous_tiler_bitmask ^ value;
+
+ if (BIT_AT(changed, 0)) {
+ record_timeline_event(PM_TILER_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP);
+ }
+
+ previous_tiler_bitmask = value;
+ break;
+ }
+
+ case L2_PRESENT_LO:
+ {
+ unsigned long long changed = previous_l2_bitmask ^ value;
+
+ if (BIT_AT(changed, 0)) {
+ record_timeline_event(PM_L2_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP);
+ }
+ if (BIT_AT(changed, 4)) {
+ record_timeline_event(PM_L2_1, BIT_AT(value, 4) ? ACTIVITY_START : ACTIVITY_STOP);
+ }
+
+ previous_l2_bitmask = value;
+ break;
+ }
+
+ default:
+ /* No other blocks are supported at present */
+ break;
+ }
+
+#undef SHADER_PRESENT_LO
+#undef TILER_PRESENT_LO
+#undef L2_PRESENT_LO
+#undef BIT_AT
+}
+
+GATOR_DEFINE_PROBE(mali_page_fault_insert_pages, TP_PROTO(int event_id, unsigned long value))
+{
+ /* We add to the previous since we may receive many tracepoints in one sample period */
+ sw_counter_data[MMU_PAGE_FAULT_0 + event_id] += value;
+}
+
+GATOR_DEFINE_PROBE(mali_mmu_as_in_use, TP_PROTO(int event_id))
+{
+ record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_START);
+}
+
+GATOR_DEFINE_PROBE(mali_mmu_as_released, TP_PROTO(int event_id))
+{
+ record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_STOP);
+}
+
+GATOR_DEFINE_PROBE(mali_total_alloc_pages_change, TP_PROTO(long long int event_id))
+{
+ accumulators_data[TOTAL_ALLOC_PAGES] = event_id;
+}
+
+static int create_files(struct super_block *sb, struct dentry *root)
+{
+ int event;
+ /*
+ * Create the filesystem for all events
+ */
+ int counter_index = 0;
+ const char *mali_name = gator_mali_get_mali_name();
+
+ for (event = FIRST_TIMELINE_EVENT; event < FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS; event++) {
+ if (gator_mali_create_file_system(mali_name, timeline_event_names[counter_index], sb, root, &counters[event]) != 0) {
+ return -1;
+ }
+ counter_index++;
+ }
+ counter_index = 0;
+ for (event = FIRST_SOFTWARE_COUNTER; event < FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS; event++) {
+ if (gator_mali_create_file_system(mali_name, software_counter_names[counter_index], sb, root, &counters[event]) != 0) {
+ return -1;
+ }
+ counter_index++;
+ }
+ counter_index = 0;
+ for (event = FIRST_ACCUMULATOR; event < FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS; event++) {
+ if (gator_mali_create_file_system(mali_name, accumulators_names[counter_index], sb, root, &counters[event]) != 0) {
+ return -1;
+ }
+ counter_index++;
+ }
+
+ return 0;
+}
+
+static int register_tracepoints(void)
+{
+ if (GATOR_REGISTER_TRACE(mali_pm_status)) {
+ pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint failed to activate\n");
+ return 0;
+ }
+
+ if (GATOR_REGISTER_TRACE(mali_page_fault_insert_pages)) {
+ pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint failed to activate\n");
+ return 0;
+ }
+
+ if (GATOR_REGISTER_TRACE(mali_mmu_as_in_use)) {
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint failed to activate\n");
+ return 0;
+ }
+
+ if (GATOR_REGISTER_TRACE(mali_mmu_as_released)) {
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint failed to activate\n");
+ return 0;
+ }
+
+ if (GATOR_REGISTER_TRACE(mali_total_alloc_pages_change)) {
+ pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint failed to activate\n");
+ return 0;
+ }
+
+ pr_debug("gator: Mali-T6xx: start\n");
+ pr_debug("gator: Mali-T6xx: mali_pm_status probe is at %p\n", &probe_mali_pm_status);
+ pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages probe is at %p\n", &probe_mali_page_fault_insert_pages);
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use probe is at %p\n", &probe_mali_mmu_as_in_use);
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_released probe is at %p\n", &probe_mali_mmu_as_released);
+ pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change probe is at %p\n", &probe_mali_total_alloc_pages_change);
+
+ return 1;
+}
+
+static int start(void)
+{
+ unsigned int cnt;
+
+ /* Clean all data for the next capture */
+ for (cnt = 0; cnt < NUMBER_OF_TIMELINE_EVENTS; cnt++) {
+ timeline_event_starttime[cnt].tv_sec = timeline_event_starttime[cnt].tv_nsec = 0;
+ timeline_data[cnt] = 0;
+ }
+
+ for (cnt = 0; cnt < NUMBER_OF_SOFTWARE_COUNTERS; cnt++) {
+ sw_counter_data[cnt] = 0;
+ }
+
+ for (cnt = 0; cnt < NUMBER_OF_ACCUMULATORS; cnt++) {
+ accumulators_data[cnt] = 0;
+ }
+
+ /* Register tracepoints */
+ if (register_tracepoints() == 0) {
+ return -1;
+ }
+
+ /*
+ * Set the first timestamp for calculating the sample interval. The first interval could be quite long,
+ * since it will be the time between 'start' and the first 'read'.
+ * This means that timeline values will be divided by a big number for the first sample.
+ */
+ getnstimeofday(&prev_timestamp);
+
+ return 0;
+}
+
+static void stop(void)
+{
+ pr_debug("gator: Mali-T6xx: stop\n");
+
+ /*
+ * It is safe to unregister traces even if they were not successfully
+ * registered, so no need to check.
+ */
+ GATOR_UNREGISTER_TRACE(mali_pm_status);
+ pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint deactivated\n");
+
+ GATOR_UNREGISTER_TRACE(mali_page_fault_insert_pages);
+ pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint deactivated\n");
+
+ GATOR_UNREGISTER_TRACE(mali_mmu_as_in_use);
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint deactivated\n");
+
+ GATOR_UNREGISTER_TRACE(mali_mmu_as_released);
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint deactivated\n");
+
+ GATOR_UNREGISTER_TRACE(mali_total_alloc_pages_change);
+ pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint deactivated\n");
+}
+
+static int read(int **buffer)
+{
+ int cnt;
+ int len = 0;
+ long sample_interval_us = 0;
+ struct timespec read_timestamp;
+
+ if (!on_primary_core()) {
+ return 0;
+ }
+
+ /* Get the start of this sample period. */
+ getnstimeofday(&read_timestamp);
+
+ /*
+ * Calculate the sample interval if the previous sample time is valid.
+ * We use tv_sec since it will not be 0.
+ */
+ if (prev_timestamp.tv_sec != 0) {
+ sample_interval_us = get_duration_us(&prev_timestamp, &read_timestamp);
+ }
+
+ /* Structure copy. Update the previous timestamp. */
+ prev_timestamp = read_timestamp;
+
+ /*
+ * Report the timeline counters (ACTIVITY_START/STOP)
+ */
+ for (cnt = FIRST_TIMELINE_EVENT; cnt < (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS); cnt++) {
+ mali_counter *counter = &counters[cnt];
+ if (counter->enabled) {
+ const int index = cnt - FIRST_TIMELINE_EVENT;
+ unsigned int value;
+
+ /* If the activity is still running, reset its start time to the start of this sample period
+ * to correct the count. Add the time up to the end of the sample onto the count. */
+ if (timeline_event_starttime[index].tv_sec != 0) {
+ const long event_duration = get_duration_us(&timeline_event_starttime[index], &read_timestamp);
+ timeline_data[index] += event_duration;
+ timeline_event_starttime[index] = read_timestamp; /* Activity is still running. */
+ }
+
+ if (sample_interval_us != 0) {
+ /* Convert the counter to a percent-of-sample value */
+ value = (timeline_data[index] * 100) / sample_interval_us;
+ } else {
+ pr_debug("gator: Mali-T6xx: setting value to zero\n");
+ value = 0;
+ }
+
+ /* Clear the counter value ready for the next sample. */
+ timeline_data[index] = 0;
+
+ counter_dump[len++] = counter->key;
+ counter_dump[len++] = value;
+ }
+ }
+
+ /* Report the software counters */
+ for (cnt = FIRST_SOFTWARE_COUNTER; cnt < (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS); cnt++) {
+ const mali_counter *counter = &counters[cnt];
+ if (counter->enabled) {
+ const int index = cnt - FIRST_SOFTWARE_COUNTER;
+ counter_dump[len++] = counter->key;
+ counter_dump[len++] = sw_counter_data[index];
+ /* Set the value to zero for the next time */
+ sw_counter_data[index] = 0;
+ }
+ }
+
+ /* Report the accumulators */
+ for (cnt = FIRST_ACCUMULATOR; cnt < (FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS); cnt++) {
+ const mali_counter *counter = &counters[cnt];
+ if (counter->enabled) {
+ const int index = cnt - FIRST_ACCUMULATOR;
+ counter_dump[len++] = counter->key;
+ counter_dump[len++] = accumulators_data[index];
+ /* Do not zero the accumulator */
+ }
+ }
+
+ /* Update the buffer */
+ if (buffer) {
+ *buffer = (int *)counter_dump;
+ }
+
+ return len;
+}
+
+static struct gator_interface gator_events_mali_t6xx_interface = {
+ .create_files = create_files,
+ .start = start,
+ .stop = stop,
+ .read = read
+};
+
+extern int gator_events_mali_t6xx_init(void)
+{
+ pr_debug("gator: Mali-T6xx: sw_counters init\n");
+
+ gator_mali_initialise_counters(counters, NUMBER_OF_EVENTS);
+
+ return gator_events_install(&gator_events_mali_t6xx_interface);
+}
+
+gator_events_init(gator_events_mali_t6xx_init);
diff --git a/drivers/gator/gator_events_mali_t6xx_hw.c b/drivers/gator/gator_events_mali_t6xx_hw.c
new file mode 100644
index 000000000000..f557350eb9bc
--- /dev/null
+++ b/drivers/gator/gator_events_mali_t6xx_hw.c
@@ -0,0 +1,758 @@
+/**
+ * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "gator.h"
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+/* Mali T6xx DDK includes */
+#include "linux/mali_linux_trace.h"
+#include "kbase/src/common/mali_kbase.h"
+#include "kbase/src/linux/mali_kbase_mem_linux.h"
+
+#include "gator_events_mali_common.h"
+
+/* If API version is not specified then assume API version 1. */
+#ifndef MALI_DDK_GATOR_API_VERSION
+#define MALI_DDK_GATOR_API_VERSION 1
+#endif
+
+#if (MALI_DDK_GATOR_API_VERSION != 1) && (MALI_DDK_GATOR_API_VERSION != 2)
+#error MALI_DDK_GATOR_API_VERSION is invalid (must be 1 for r1/r2 DDK, or 2 for r3 DDK).
+#endif
+
+/*
+ * Mali-T6xx
+ */
+typedef struct kbase_device *kbase_find_device_type(int);
+typedef kbase_context *kbase_create_context_type(kbase_device *);
+typedef void kbase_destroy_context_type(kbase_context *);
+
+#if MALI_DDK_GATOR_API_VERSION == 1
+typedef void *kbase_va_alloc_type(kbase_context *, u32);
+typedef void kbase_va_free_type(kbase_context *, void *);
+#elif MALI_DDK_GATOR_API_VERSION == 2
+typedef void *kbase_va_alloc_type(kbase_context *, u32, kbase_hwc_dma_mapping * handle);
+typedef void kbase_va_free_type(kbase_context *, kbase_hwc_dma_mapping * handle);
+#endif
+
+typedef mali_error kbase_instr_hwcnt_enable_type(kbase_context *, kbase_uk_hwcnt_setup *);
+typedef mali_error kbase_instr_hwcnt_disable_type(kbase_context *);
+typedef mali_error kbase_instr_hwcnt_clear_type(kbase_context *);
+typedef mali_error kbase_instr_hwcnt_dump_irq_type(kbase_context *);
+typedef mali_bool kbase_instr_hwcnt_dump_complete_type(kbase_context *, mali_bool *);
+
+static kbase_find_device_type *kbase_find_device_symbol;
+static kbase_create_context_type *kbase_create_context_symbol;
+static kbase_va_alloc_type *kbase_va_alloc_symbol;
+static kbase_instr_hwcnt_enable_type *kbase_instr_hwcnt_enable_symbol;
+static kbase_instr_hwcnt_clear_type *kbase_instr_hwcnt_clear_symbol;
+static kbase_instr_hwcnt_dump_irq_type *kbase_instr_hwcnt_dump_irq_symbol;
+static kbase_instr_hwcnt_dump_complete_type *kbase_instr_hwcnt_dump_complete_symbol;
+static kbase_instr_hwcnt_disable_type *kbase_instr_hwcnt_disable_symbol;
+static kbase_va_free_type *kbase_va_free_symbol;
+static kbase_destroy_context_type *kbase_destroy_context_symbol;
+
+/** The interval between reads, in ns.
+ *
+ * Earlier we introduced
+ * a 'hold off for 1ms after last read' to resolve MIDBASE-2178 and MALINE-724.
+ * However, the 1ms hold off is too long if no context switches occur as there is a race
+ * between this value and the tick of the read clock in gator which is also 1ms. If we 'miss' the
+ * current read, the counter values are effectively 'spread' over 2ms and the values seen are half
+ * what they should be (since Streamline averages over sample time). In the presence of context switches
+ * this spread can vary and markedly affect the counters. Currently there is no 'proper' solution to
+ * this, but empirically we have found that reducing the minimum read interval to 950us causes the
+ * counts to be much more stable.
+ */
+static const int READ_INTERVAL_NSEC = 950000;
+
+#if GATOR_TEST
+#include "gator_events_mali_t6xx_hw_test.c"
+#endif
+
+/* Blocks for HW counters */
+enum {
+ JM_BLOCK = 0,
+ TILER_BLOCK,
+ SHADER_BLOCK,
+ MMU_BLOCK
+};
+
+/* Counters for Mali-T6xx:
+ *
+ * - HW counters, 4 blocks
+ * For HW counters we need strings to create /dev/gator/events files.
+ * Enums are not needed because the position of the HW name in the array is the same
+ * of the corresponding value in the received block of memory.
+ * HW counters are requested by calculating a bitmask, passed then to the driver.
+ * Every millisecond a HW counters dump is requested, and if the previous has been completed they are read.
+ */
+
+/* Hardware Counters */
+static const char *const hardware_counter_names[] = {
+ /* Job Manager */
+ "",
+ "",
+ "",
+ "",
+ "MESSAGES_SENT",
+ "MESSAGES_RECEIVED",
+ "GPU_ACTIVE", /* 6 */
+ "IRQ_ACTIVE",
+ "JS0_JOBS",
+ "JS0_TASKS",
+ "JS0_ACTIVE",
+ "",
+ "JS0_WAIT_READ",
+ "JS0_WAIT_ISSUE",
+ "JS0_WAIT_DEPEND",
+ "JS0_WAIT_FINISH",
+ "JS1_JOBS",
+ "JS1_TASKS",
+ "JS1_ACTIVE",
+ "",
+ "JS1_WAIT_READ",
+ "JS1_WAIT_ISSUE",
+ "JS1_WAIT_DEPEND",
+ "JS1_WAIT_FINISH",
+ "JS2_JOBS",
+ "JS2_TASKS",
+ "JS2_ACTIVE",
+ "",
+ "JS2_WAIT_READ",
+ "JS2_WAIT_ISSUE",
+ "JS2_WAIT_DEPEND",
+ "JS2_WAIT_FINISH",
+ "JS3_JOBS",
+ "JS3_TASKS",
+ "JS3_ACTIVE",
+ "",
+ "JS3_WAIT_READ",
+ "JS3_WAIT_ISSUE",
+ "JS3_WAIT_DEPEND",
+ "JS3_WAIT_FINISH",
+ "JS4_JOBS",
+ "JS4_TASKS",
+ "JS4_ACTIVE",
+ "",
+ "JS4_WAIT_READ",
+ "JS4_WAIT_ISSUE",
+ "JS4_WAIT_DEPEND",
+ "JS4_WAIT_FINISH",
+ "JS5_JOBS",
+ "JS5_TASKS",
+ "JS5_ACTIVE",
+ "",
+ "JS5_WAIT_READ",
+ "JS5_WAIT_ISSUE",
+ "JS5_WAIT_DEPEND",
+ "JS5_WAIT_FINISH",
+ "JS6_JOBS",
+ "JS6_TASKS",
+ "JS6_ACTIVE",
+ "",
+ "JS6_WAIT_READ",
+ "JS6_WAIT_ISSUE",
+ "JS6_WAIT_DEPEND",
+ "JS6_WAIT_FINISH",
+
+ /*Tiler */
+ "",
+ "",
+ "",
+ "JOBS_PROCESSED",
+ "TRIANGLES",
+ "QUADS",
+ "POLYGONS",
+ "POINTS",
+ "LINES",
+ "VCACHE_HIT",
+ "VCACHE_MISS",
+ "FRONT_FACING",
+ "BACK_FACING",
+ "PRIM_VISIBLE",
+ "PRIM_CULLED",
+ "PRIM_CLIPPED",
+ "LEVEL0",
+ "LEVEL1",
+ "LEVEL2",
+ "LEVEL3",
+ "LEVEL4",
+ "LEVEL5",
+ "LEVEL6",
+ "LEVEL7",
+ "COMMAND_1",
+ "COMMAND_2",
+ "COMMAND_3",
+ "COMMAND_4",
+ "COMMAND_4_7",
+ "COMMAND_8_15",
+ "COMMAND_16_63",
+ "COMMAND_64",
+ "COMPRESS_IN",
+ "COMPRESS_OUT",
+ "COMPRESS_FLUSH",
+ "TIMESTAMPS",
+ "PCACHE_HIT",
+ "PCACHE_MISS",
+ "PCACHE_LINE",
+ "PCACHE_STALL",
+ "WRBUF_HIT",
+ "WRBUF_MISS",
+ "WRBUF_LINE",
+ "WRBUF_PARTIAL",
+ "WRBUF_STALL",
+ "ACTIVE",
+ "LOADING_DESC",
+ "INDEX_WAIT",
+ "INDEX_RANGE_WAIT",
+ "VERTEX_WAIT",
+ "PCACHE_WAIT",
+ "WRBUF_WAIT",
+ "BUS_READ",
+ "BUS_WRITE",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "UTLB_STALL",
+ "UTLB_REPLAY_MISS",
+ "UTLB_REPLAY_FULL",
+ "UTLB_NEW_MISS",
+ "UTLB_HIT",
+
+ /* Shader Core */
+ "",
+ "",
+ "",
+ "SHADER_CORE_ACTIVE",
+ "FRAG_ACTIVE",
+ "FRAG_PRIMATIVES",
+ "FRAG_PRIMATIVES_DROPPED",
+ "FRAG_CYCLE_DESC",
+ "FRAG_CYCLES_PLR",
+ "FRAG_CYCLES_VERT",
+ "FRAG_CYCLES_TRISETUP",
+ "FRAG_CYCLES_RAST",
+ "FRAG_THREADS",
+ "FRAG_DUMMY_THREADS",
+ "FRAG_QUADS_RAST",
+ "FRAG_QUADS_EZS_TEST",
+ "FRAG_QUADS_EZS_KILLED",
+ "FRAG_QUADS_LZS_TEST",
+ "FRAG_QUADS_LZS_KILLED",
+ "FRAG_CYCLE_NO_TILE",
+ "FRAG_NUM_TILES",
+ "FRAG_TRANS_ELIM",
+ "COMPUTE_ACTIVE",
+ "COMPUTE_TASKS",
+ "COMPUTE_THREADS",
+ "COMPUTE_CYCLES_DESC",
+ "TRIPIPE_ACTIVE",
+ "ARITH_WORDS",
+ "ARITH_CYCLES_REG",
+ "ARITH_CYCLES_L0",
+ "ARITH_FRAG_DEPEND",
+ "LS_WORDS",
+ "LS_ISSUES",
+ "LS_RESTARTS",
+ "LS_REISSUES_MISS",
+ "LS_REISSUES_VD",
+ "LS_REISSUE_ATTRIB_MISS",
+ "LS_NO_WB",
+ "TEX_WORDS",
+ "TEX_BUBBLES",
+ "TEX_WORDS_L0",
+ "TEX_WORDS_DESC",
+ "TEX_THREADS",
+ "TEX_RECIRC_FMISS",
+ "TEX_RECIRC_DESC",
+ "TEX_RECIRC_MULTI",
+ "TEX_RECIRC_PMISS",
+ "TEX_RECIRC_CONF",
+ "LSC_READ_HITS",
+ "LSC_READ_MISSES",
+ "LSC_WRITE_HITS",
+ "LSC_WRITE_MISSES",
+ "LSC_ATOMIC_HITS",
+ "LSC_ATOMIC_MISSES",
+ "LSC_LINE_FETCHES",
+ "LSC_DIRTY_LINE",
+ "LSC_SNOOPS",
+ "AXI_TLB_STALL",
+ "AXI_TLB_MIESS",
+ "AXI_TLB_TRANSACTION",
+ "LS_TLB_MISS",
+ "LS_TLB_HIT",
+ "AXI_BEATS_READ",
+ "AXI_BEATS_WRITTEN",
+
+ /*L2 and MMU */
+ "",
+ "",
+ "",
+ "",
+ "MMU_HIT",
+ "MMU_NEW_MISS",
+ "MMU_REPLAY_FULL",
+ "MMU_REPLAY_MISS",
+ "MMU_TABLE_WALK",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "UTLB_HIT",
+ "UTLB_NEW_MISS",
+ "UTLB_REPLAY_FULL",
+ "UTLB_REPLAY_MISS",
+ "UTLB_STALL",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "L2_WRITE_BEATS",
+ "L2_READ_BEATS",
+ "L2_ANY_LOOKUP",
+ "L2_READ_LOOKUP",
+ "L2_SREAD_LOOKUP",
+ "L2_READ_REPLAY",
+ "L2_READ_SNOOP",
+ "L2_READ_HIT",
+ "L2_CLEAN_MISS",
+ "L2_WRITE_LOOKUP",
+ "L2_SWRITE_LOOKUP",
+ "L2_WRITE_REPLAY",
+ "L2_WRITE_SNOOP",
+ "L2_WRITE_HIT",
+ "L2_EXT_READ_FULL",
+ "L2_EXT_READ_HALF",
+ "L2_EXT_WRITE_FULL",
+ "L2_EXT_WRITE_HALF",
+ "L2_EXT_READ",
+ "L2_EXT_READ_LINE",
+ "L2_EXT_WRITE",
+ "L2_EXT_WRITE_LINE",
+ "L2_EXT_WRITE_SMALL",
+ "L2_EXT_BARRIER",
+ "L2_EXT_AR_STALL",
+ "L2_EXT_R_BUF_FULL",
+ "L2_EXT_RD_BUF_FULL",
+ "L2_EXT_R_RAW",
+ "L2_EXT_W_STALL",
+ "L2_EXT_W_BUF_FULL",
+ "L2_EXT_R_W_HAZARD",
+ "L2_TAG_HAZARD",
+ "L2_SNOOP_FULL",
+ "L2_REPLAY_FULL"
+};
+
+#define NUMBER_OF_HARDWARE_COUNTERS (sizeof(hardware_counter_names) / sizeof(hardware_counter_names[0]))
+
+#define GET_HW_BLOCK(c) (((c) >> 6) & 0x3)
+#define GET_COUNTER_OFFSET(c) ((c) & 0x3f)
+
+/* Memory to dump hardware counters into */
+static void *kernel_dump_buffer;
+
+#if MALI_DDK_GATOR_API_VERSION == 2
+/* DMA state used to manage lifetime of the buffer */
+kbase_hwc_dma_mapping kernel_dump_buffer_handle;
+#endif
+
+/* kbase context and device */
+static kbase_context *kbcontext = NULL;
+static struct kbase_device *kbdevice = NULL;
+
+/*
+ * The following function has no external prototype in older DDK revisions. When the DDK
+ * is updated then this should be removed.
+ */
+struct kbase_device *kbase_find_device(int minor);
+
+static volatile bool kbase_device_busy = false;
+static unsigned int num_hardware_counters_enabled;
+
+/*
+ * gatorfs variables for counter enable state
+ */
+static mali_counter counters[NUMBER_OF_HARDWARE_COUNTERS];
+
+/* An array used to return the data we recorded
+ * as key,value pairs hence the *2
+ */
+static unsigned long counter_dump[NUMBER_OF_HARDWARE_COUNTERS * 2];
+
+#define SYMBOL_GET(FUNCTION, ERROR_COUNT) \
+ if(FUNCTION ## _symbol) \
+ { \
+ printk("gator: mali " #FUNCTION " symbol was already registered\n"); \
+ (ERROR_COUNT)++; \
+ } \
+ else \
+ { \
+ FUNCTION ## _symbol = symbol_get(FUNCTION); \
+ if(! FUNCTION ## _symbol) \
+ { \
+ printk("gator: mali online " #FUNCTION " symbol not found\n"); \
+ (ERROR_COUNT)++; \
+ } \
+ }
+
+#define SYMBOL_CLEANUP(FUNCTION) \
+ if(FUNCTION ## _symbol) \
+ { \
+ symbol_put(FUNCTION); \
+ FUNCTION ## _symbol = NULL; \
+ }
+
+/**
+ * Execute symbol_get for all the Mali symbols and check for success.
+ * @return the number of symbols not loaded.
+ */
+static int init_symbols(void)
+{
+ int error_count = 0;
+ SYMBOL_GET(kbase_find_device, error_count);
+ SYMBOL_GET(kbase_create_context, error_count);
+ SYMBOL_GET(kbase_va_alloc, error_count);
+ SYMBOL_GET(kbase_instr_hwcnt_enable, error_count);
+ SYMBOL_GET(kbase_instr_hwcnt_clear, error_count);
+ SYMBOL_GET(kbase_instr_hwcnt_dump_irq, error_count);
+ SYMBOL_GET(kbase_instr_hwcnt_dump_complete, error_count);
+ SYMBOL_GET(kbase_instr_hwcnt_disable, error_count);
+ SYMBOL_GET(kbase_va_free, error_count);
+ SYMBOL_GET(kbase_destroy_context, error_count);
+
+ return error_count;
+}
+
+/**
+ * Execute symbol_put for all the registered Mali symbols.
+ */
+static void clean_symbols(void)
+{
+ SYMBOL_CLEANUP(kbase_find_device);
+ SYMBOL_CLEANUP(kbase_create_context);
+ SYMBOL_CLEANUP(kbase_va_alloc);
+ SYMBOL_CLEANUP(kbase_instr_hwcnt_enable);
+ SYMBOL_CLEANUP(kbase_instr_hwcnt_clear);
+ SYMBOL_CLEANUP(kbase_instr_hwcnt_dump_irq);
+ SYMBOL_CLEANUP(kbase_instr_hwcnt_dump_complete);
+ SYMBOL_CLEANUP(kbase_instr_hwcnt_disable);
+ SYMBOL_CLEANUP(kbase_va_free);
+ SYMBOL_CLEANUP(kbase_destroy_context);
+}
+
+/**
+ * Determines whether a read should take place
+ * @param current_time The current time, obtained from getnstimeofday()
+ * @param prev_time_s The number of seconds at the previous read attempt.
+ * @param next_read_time_ns The time (in ns) when the next read should be allowed.
+ *
+ * Note that this function has been separated out here to allow it to be tested.
+ */
+static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns)
+{
+ /* If the current ns count rolls over a second, roll the next read time too. */
+ if (current_time->tv_sec != *prev_time_s) {
+ *next_read_time_ns = *next_read_time_ns - NSEC_PER_SEC;
+ }
+
+ /* Abort the read if the next read time has not arrived. */
+ if (current_time->tv_nsec < *next_read_time_ns) {
+ return 0;
+ }
+
+ /* Set the next read some fixed time after this one, and update the read timestamp. */
+ *next_read_time_ns = current_time->tv_nsec + READ_INTERVAL_NSEC;
+
+ *prev_time_s = current_time->tv_sec;
+ return 1;
+}
+
+static int start(void)
+{
+ kbase_uk_hwcnt_setup setup;
+ mali_error err;
+ int cnt;
+ u16 bitmask[] = { 0, 0, 0, 0 };
+
+ /* Setup HW counters */
+ num_hardware_counters_enabled = 0;
+
+ if (NUMBER_OF_HARDWARE_COUNTERS != 256) {
+ pr_debug("Unexpected number of hardware counters defined: expecting 256, got %d\n", NUMBER_OF_HARDWARE_COUNTERS);
+ }
+
+ /* Calculate enable bitmasks based on counters_enabled array */
+ for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) {
+ const mali_counter *counter = &counters[cnt];
+ if (counter->enabled) {
+ int block = GET_HW_BLOCK(cnt);
+ int enable_bit = GET_COUNTER_OFFSET(cnt) / 4;
+ bitmask[block] |= (1 << enable_bit);
+ pr_debug("gator: Mali-T6xx: hardware counter %s selected [%d]\n", hardware_counter_names[cnt], cnt);
+ num_hardware_counters_enabled++;
+ }
+ }
+
+ /* Create a kbase context for HW counters */
+ if (num_hardware_counters_enabled > 0) {
+ if (init_symbols() > 0) {
+ clean_symbols();
+ /* No Mali driver code entrypoints found - not a fault. */
+ return 0;
+ }
+
+ kbdevice = kbase_find_device_symbol(-1);
+
+ /* If we already got a context, fail */
+ if (kbcontext) {
+ pr_debug("gator: Mali-T6xx: error context already present\n");
+ goto out;
+ }
+
+ /* kbcontext will only be valid after all the Mali symbols are loaded successfully */
+ kbcontext = kbase_create_context_symbol(kbdevice);
+ if (!kbcontext) {
+ pr_debug("gator: Mali-T6xx: error creating kbase context\n");
+ goto out;
+ }
+
+ /*
+ * The amount of memory needed to store the dump (bytes)
+ * DUMP_SIZE = number of core groups
+ * * number of blocks (always 8 for midgard)
+ * * number of counters per block (always 64 for midgard)
+ * * number of bytes per counter (always 4 in midgard)
+ * For a Mali-T6xx with a single core group = 1 * 8 * 64 * 4 = 2048
+ * For a Mali-T6xx with a dual core group = 2 * 8 * 64 * 4 = 4096
+ */
+#if MALI_DDK_GATOR_API_VERSION == 1
+ kernel_dump_buffer = kbase_va_alloc_symbol(kbcontext, 4096);
+#elif MALI_DDK_GATOR_API_VERSION == 2
+ kernel_dump_buffer = kbase_va_alloc_symbol(kbcontext, 4096, &kernel_dump_buffer_handle);
+#endif
+ if (!kernel_dump_buffer) {
+ pr_debug("gator: Mali-T6xx: error trying to allocate va\n");
+ goto destroy_context;
+ }
+
+ setup.dump_buffer = (uintptr_t)kernel_dump_buffer;
+ setup.jm_bm = bitmask[JM_BLOCK];
+ setup.tiler_bm = bitmask[TILER_BLOCK];
+ setup.shader_bm = bitmask[SHADER_BLOCK];
+ setup.mmu_l2_bm = bitmask[MMU_BLOCK];
+ /* These counters do not exist on Mali-T60x */
+ setup.l3_cache_bm = 0;
+
+ /* Use kbase API to enable hardware counters and provide dump buffer */
+ err = kbase_instr_hwcnt_enable_symbol(kbcontext, &setup);
+ if (err != MALI_ERROR_NONE) {
+ pr_debug("gator: Mali-T6xx: can't setup hardware counters\n");
+ goto free_buffer;
+ }
+ pr_debug("gator: Mali-T6xx: hardware counters enabled\n");
+ kbase_instr_hwcnt_clear_symbol(kbcontext);
+ pr_debug("gator: Mali-T6xx: hardware counters cleared \n");
+
+ kbase_device_busy = false;
+ }
+
+ return 0;
+
+free_buffer:
+#if MALI_DDK_GATOR_API_VERSION == 1
+ kbase_va_free_symbol(kbcontext, kernel_dump_buffer);
+#elif MALI_DDK_GATOR_API_VERSION == 2
+ kbase_va_free_symbol(kbcontext, &kernel_dump_buffer_handle);
+#endif
+
+destroy_context:
+ kbase_destroy_context_symbol(kbcontext);
+
+out:
+ clean_symbols();
+ return -1;
+}
+
+static void stop(void)
+{
+ unsigned int cnt;
+ kbase_context *temp_kbcontext;
+
+ pr_debug("gator: Mali-T6xx: stop\n");
+
+ /* Set all counters as disabled */
+ for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) {
+ counters[cnt].enabled = 0;
+ }
+
+ /* Destroy the context for HW counters */
+ if (num_hardware_counters_enabled > 0 && kbcontext != NULL) {
+ /*
+ * Set the global variable to NULL before destroying it, because
+ * other function will check this before using it.
+ */
+ temp_kbcontext = kbcontext;
+ kbcontext = NULL;
+
+ kbase_instr_hwcnt_disable_symbol(temp_kbcontext);
+
+#if MALI_DDK_GATOR_API_VERSION == 1
+ kbase_va_free_symbol(temp_kbcontext, kernel_dump_buffer);
+#elif MALI_DDK_GATOR_API_VERSION == 2
+ kbase_va_free_symbol(temp_kbcontext, &kernel_dump_buffer_handle);
+#endif
+
+ kbase_destroy_context_symbol(temp_kbcontext);
+
+ pr_debug("gator: Mali-T6xx: hardware counters stopped\n");
+
+ clean_symbols();
+ }
+}
+
+static int read(int **buffer)
+{
+ int cnt;
+ int len = 0;
+ u32 value = 0;
+ mali_bool success;
+
+ struct timespec current_time;
+ static u32 prev_time_s = 0;
+ static s32 next_read_time_ns = 0;
+
+ if (!on_primary_core()) {
+ return 0;
+ }
+
+ getnstimeofday(&current_time);
+
+ /*
+ * Discard reads unless a respectable time has passed. This reduces the load on the GPU without sacrificing
+ * accuracy on the Streamline display.
+ */
+ if (!is_read_scheduled(&current_time, &prev_time_s, &next_read_time_ns)) {
+ return 0;
+ }
+
+ /*
+ * Report the HW counters
+ * Only process hardware counters if at least one of the hardware counters is enabled.
+ */
+ if (num_hardware_counters_enabled > 0) {
+ const unsigned int vithar_blocks[] = {
+ 0x700, /* VITHAR_JOB_MANAGER, Block 0 */
+ 0x400, /* VITHAR_TILER, Block 1 */
+ 0x000, /* VITHAR_SHADER_CORE, Block 2 */
+ 0x500 /* VITHAR_MEMORY_SYSTEM, Block 3 */
+ };
+
+ if (!kbcontext) {
+ return -1;
+ }
+
+ /* Mali symbols can be called safely since a kbcontext is valid */
+ if (kbase_instr_hwcnt_dump_complete_symbol(kbcontext, &success) == MALI_TRUE) {
+ kbase_device_busy = false;
+
+ if (success == MALI_TRUE) {
+ for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) {
+ const mali_counter *counter = &counters[cnt];
+ if (counter->enabled) {
+ const int block = GET_HW_BLOCK(cnt);
+ const int counter_offset = GET_COUNTER_OFFSET(cnt);
+ const u32 *counter_block = (u32 *) ((uintptr_t)kernel_dump_buffer + vithar_blocks[block]);
+ const u32 *counter_address = counter_block + counter_offset;
+
+ value = *counter_address;
+
+ if (block == SHADER_BLOCK) {
+ /* (counter_address + 0x000) has already been accounted-for above. */
+ value += *(counter_address + 0x100);
+ value += *(counter_address + 0x200);
+ value += *(counter_address + 0x300);
+ }
+
+ counter_dump[len++] = counter->key;
+ counter_dump[len++] = value;
+ }
+ }
+ }
+ }
+
+ if (!kbase_device_busy) {
+ kbase_device_busy = true;
+ kbase_instr_hwcnt_dump_irq_symbol(kbcontext);
+ }
+ }
+
+ /* Update the buffer */
+ if (buffer) {
+ *buffer = (int *)counter_dump;
+ }
+
+ return len;
+}
+
+static int create_files(struct super_block *sb, struct dentry *root)
+{
+ unsigned int event;
+ /*
+ * Create the filesystem for all events
+ */
+ int counter_index = 0;
+ const char *mali_name = gator_mali_get_mali_name();
+
+ for (event = 0; event < NUMBER_OF_HARDWARE_COUNTERS; event++) {
+ if (gator_mali_create_file_system(mali_name, hardware_counter_names[counter_index], sb, root, &counters[event]) != 0)
+ return -1;
+ counter_index++;
+ }
+
+ return 0;
+}
+
+static struct gator_interface gator_events_mali_t6xx_interface = {
+ .create_files = create_files,
+ .start = start,
+ .stop = stop,
+ .read = read
+};
+
+int gator_events_mali_t6xx_hw_init(void)
+{
+ pr_debug("gator: Mali-T6xx: sw_counters init\n");
+
+#if GATOR_TEST
+ test_all_is_read_scheduled();
+#endif
+
+ gator_mali_initialise_counters(counters, NUMBER_OF_HARDWARE_COUNTERS);
+
+ return gator_events_install(&gator_events_mali_t6xx_interface);
+}
+
+gator_events_init(gator_events_mali_t6xx_hw_init);
diff --git a/drivers/gator/gator_events_mali_t6xx_hw_test.c b/drivers/gator/gator_events_mali_t6xx_hw_test.c
new file mode 100644
index 000000000000..efb32ddf5483
--- /dev/null
+++ b/drivers/gator/gator_events_mali_t6xx_hw_test.c
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/**
+ * Test functions for mali_t600_hw code.
+ */
+
+static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns);
+
+static int test_is_read_scheduled(u32 s, u32 ns, u32 prev_s, s32 next_ns, int expected_result, s32 expected_next_ns)
+{
+ struct timespec current_time;
+ u32 prev_time_s = prev_s;
+ s32 next_read_time_ns = next_ns;
+
+ current_time.tv_sec = s;
+ current_time.tv_nsec = ns;
+
+ if (is_read_scheduled(&current_time, &prev_time_s, &next_read_time_ns) != expected_result) {
+ printk("Failed do_read(%u, %u, %u, %d): expected %d\n", s, ns, prev_s, next_ns, expected_result);
+ return 0;
+ }
+
+ if (next_read_time_ns != expected_next_ns) {
+ printk("Failed: next_read_ns expected=%d, actual=%d\n", expected_next_ns, next_read_time_ns);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void test_all_is_read_scheduled(void)
+{
+ const int HIGHEST_NS = 999999999;
+ int n_tests_passed = 0;
+
+ printk("gator: running tests on %s\n", __FILE__);
+
+ n_tests_passed += test_is_read_scheduled(0, 0, 0, 0, 1, READ_INTERVAL_NSEC); /* Null time */
+ n_tests_passed += test_is_read_scheduled(100, 1000, 0, 0, 1, READ_INTERVAL_NSEC + 1000); /* Initial values */
+
+ n_tests_passed += test_is_read_scheduled(100, HIGHEST_NS, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500);
+ n_tests_passed += test_is_read_scheduled(101, 0001, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500 - NSEC_PER_SEC);
+ n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500 - NSEC_PER_SEC, 1, 600 + READ_INTERVAL_NSEC);
+
+ n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500, 1, 600 + READ_INTERVAL_NSEC);
+
+ printk("gator: %d tests passed\n", n_tests_passed);
+}
diff --git a/drivers/gator/gator_events_meminfo.c b/drivers/gator/gator_events_meminfo.c
new file mode 100644
index 000000000000..c1e360d12895
--- /dev/null
+++ b/drivers/gator/gator_events_meminfo.c
@@ -0,0 +1,240 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "gator.h"
+#include <linux/workqueue.h>
+#include <trace/events/kmem.h>
+#include <linux/hardirq.h>
+
+#define MEMINFO_MEMFREE 0
+#define MEMINFO_MEMUSED 1
+#define MEMINFO_BUFFERRAM 2
+#define MEMINFO_TOTAL 3
+
+static ulong meminfo_global_enabled;
+static ulong meminfo_enabled[MEMINFO_TOTAL];
+static ulong meminfo_key[MEMINFO_TOTAL];
+static unsigned long long meminfo_buffer[MEMINFO_TOTAL * 2];
+static int meminfo_length = 0;
+static unsigned int mem_event = 0;
+static bool new_data_avail;
+
+static void wq_sched_handler(struct work_struct *wsptr);
+DECLARE_WORK(work, wq_sched_handler);
+static struct timer_list meminfo_wake_up_timer;
+static void meminfo_wake_up_handler(unsigned long unused_data);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order))
+#else
+GATOR_DEFINE_PROBE(mm_page_free, TP_PROTO(struct page *page, unsigned int order))
+#endif
+{
+ mem_event++;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold))
+#else
+GATOR_DEFINE_PROBE(mm_page_free_batched, TP_PROTO(struct page *page, int cold))
+#endif
+{
+ mem_event++;
+}
+
+GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype))
+{
+ mem_event++;
+}
+
+static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int i;
+
+ for (i = 0; i < MEMINFO_TOTAL; i++) {
+ switch (i) {
+ case MEMINFO_MEMFREE:
+ dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memfree");
+ break;
+ case MEMINFO_MEMUSED:
+ dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memused");
+ break;
+ case MEMINFO_BUFFERRAM:
+ dir = gatorfs_mkdir(sb, root, "Linux_meminfo_bufferram");
+ break;
+ default:
+ return -1;
+ }
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_key[i]);
+ }
+
+ return 0;
+}
+
+static int gator_events_meminfo_start(void)
+{
+ int i;
+
+ new_data_avail = true;
+ for (i = 0; i < MEMINFO_TOTAL; i++) {
+ if (meminfo_enabled[i]) {
+ meminfo_global_enabled = 1;
+ }
+ }
+
+ if (meminfo_global_enabled == 0)
+ return 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ if (GATOR_REGISTER_TRACE(mm_page_free_direct))
+#else
+ if (GATOR_REGISTER_TRACE(mm_page_free))
+#endif
+ goto mm_page_free_exit;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ if (GATOR_REGISTER_TRACE(mm_pagevec_free))
+#else
+ if (GATOR_REGISTER_TRACE(mm_page_free_batched))
+#endif
+ goto mm_page_free_batched_exit;
+ if (GATOR_REGISTER_TRACE(mm_page_alloc))
+ goto mm_page_alloc_exit;
+
+ setup_timer(&meminfo_wake_up_timer, meminfo_wake_up_handler, 0);
+ return 0;
+
+mm_page_alloc_exit:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ GATOR_UNREGISTER_TRACE(mm_pagevec_free);
+#else
+ GATOR_UNREGISTER_TRACE(mm_page_free_batched);
+#endif
+mm_page_free_batched_exit:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ GATOR_UNREGISTER_TRACE(mm_page_free_direct);
+#else
+ GATOR_UNREGISTER_TRACE(mm_page_free);
+#endif
+mm_page_free_exit:
+ return -1;
+}
+
+static void gator_events_meminfo_stop(void)
+{
+ int i;
+
+ if (meminfo_global_enabled) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ GATOR_UNREGISTER_TRACE(mm_page_free_direct);
+ GATOR_UNREGISTER_TRACE(mm_pagevec_free);
+#else
+ GATOR_UNREGISTER_TRACE(mm_page_free);
+ GATOR_UNREGISTER_TRACE(mm_page_free_batched);
+#endif
+ GATOR_UNREGISTER_TRACE(mm_page_alloc);
+
+ del_timer_sync(&meminfo_wake_up_timer);
+ }
+
+ meminfo_global_enabled = 0;
+ for (i = 0; i < MEMINFO_TOTAL; i++) {
+ meminfo_enabled[i] = 0;
+ }
+}
+
+// Must be run in process context as the kernel function si_meminfo() can sleep
+static void wq_sched_handler(struct work_struct *wsptr)
+{
+ struct sysinfo info;
+ int i, len;
+ unsigned long long value;
+
+ meminfo_length = len = 0;
+
+ si_meminfo(&info);
+ for (i = 0; i < MEMINFO_TOTAL; i++) {
+ if (meminfo_enabled[i]) {
+ switch (i) {
+ case MEMINFO_MEMFREE:
+ value = info.freeram * PAGE_SIZE;
+ break;
+ case MEMINFO_MEMUSED:
+ value = (info.totalram - info.freeram) * PAGE_SIZE;
+ break;
+ case MEMINFO_BUFFERRAM:
+ value = info.bufferram * PAGE_SIZE;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ meminfo_buffer[len++] = (unsigned long long)meminfo_key[i];
+ meminfo_buffer[len++] = value;
+ }
+ }
+
+ meminfo_length = len;
+ new_data_avail = true;
+}
+
+static void meminfo_wake_up_handler(unsigned long unused_data)
+{
+ // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater
+ schedule_work(&work);
+}
+
+static int gator_events_meminfo_read(long long **buffer)
+{
+ static unsigned int last_mem_event = 0;
+
+ if (!on_primary_core() || !meminfo_global_enabled)
+ return 0;
+
+ if (last_mem_event != mem_event) {
+ last_mem_event = mem_event;
+ mod_timer(&meminfo_wake_up_timer, jiffies + 1);
+ }
+
+ if (!new_data_avail)
+ return 0;
+
+ new_data_avail = false;
+
+ if (buffer)
+ *buffer = meminfo_buffer;
+
+ return meminfo_length;
+}
+
+static struct gator_interface gator_events_meminfo_interface = {
+ .create_files = gator_events_meminfo_create_files,
+ .start = gator_events_meminfo_start,
+ .stop = gator_events_meminfo_stop,
+ .read64 = gator_events_meminfo_read,
+};
+
+int gator_events_meminfo_init(void)
+{
+ int i;
+
+ meminfo_global_enabled = 0;
+ for (i = 0; i < MEMINFO_TOTAL; i++) {
+ meminfo_enabled[i] = 0;
+ meminfo_key[i] = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_meminfo_interface);
+}
+
+gator_events_init(gator_events_meminfo_init);
diff --git a/drivers/gator/gator_events_mmaped.c b/drivers/gator/gator_events_mmaped.c
new file mode 100644
index 000000000000..f7670f62a258
--- /dev/null
+++ b/drivers/gator/gator_events_mmaped.c
@@ -0,0 +1,229 @@
+/*
+ * Example events provider
+ *
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Similar entries to those below must be present in the events.xml file.
+ * To add them to the events.xml, create an events-mmap.xml with the
+ * following contents and rebuild gatord:
+ *
+ * <counter_set name="mmaped_cnt" count="3"/>
+ * <category name="mmaped" counter_set="mmaped_cnt" per_cpu="no">
+ * <event event="0x0" title="Simulated1" name="Sine" display="maximum" average_selection="yes" description="Sort-of-sine"/>
+ * <event event="0x1" title="Simulated2" name="Triangle" display="maximum" average_selection="yes" description="Triangular wave"/>
+ * <event event="0x2" title="Simulated3" name="PWM" display="maximum" average_selection="yes" description="PWM Signal"/>
+ * </category>
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ratelimit.h>
+
+#include "gator.h"
+
+#define MMAPED_COUNTERS_NUM 3
+
+static struct {
+ unsigned long enabled;
+ unsigned long event;
+ unsigned long key;
+} mmaped_counters[MMAPED_COUNTERS_NUM];
+
+static int mmaped_buffer[MMAPED_COUNTERS_NUM * 2];
+
+#ifdef TODO
+static void __iomem *mmaped_base;
+#endif
+
+#ifndef TODO
+static s64 prev_time;
+#endif
+
+/* Adds mmaped_cntX directories and enabled, event, and key files to /dev/gator/events */
+static int gator_events_mmaped_create_files(struct super_block *sb,
+ struct dentry *root)
+{
+ int i;
+
+ for (i = 0; i < MMAPED_COUNTERS_NUM; i++) {
+ char buf[16];
+ struct dentry *dir;
+
+ snprintf(buf, sizeof(buf), "mmaped_cnt%d", i);
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (WARN_ON(!dir))
+ return -1;
+ gatorfs_create_ulong(sb, dir, "enabled",
+ &mmaped_counters[i].enabled);
+ gatorfs_create_ulong(sb, dir, "event",
+ &mmaped_counters[i].event);
+ gatorfs_create_ro_ulong(sb, dir, "key",
+ &mmaped_counters[i].key);
+ }
+
+ return 0;
+}
+
+static int gator_events_mmaped_start(void)
+{
+#ifdef TODO
+ for (i = 0; i < MMAPED_COUNTERS_NUM; i++)
+ writel(mmaped_counters[i].event,
+ mmaped_base + COUNTERS_CONFIG_OFFSET[i]);
+
+ writel(ENABLED, COUNTERS_CONTROL_OFFSET);
+#endif
+
+#ifndef TODO
+ struct timespec ts;
+ getnstimeofday(&ts);
+ prev_time = timespec_to_ns(&ts);
+#endif
+
+ return 0;
+}
+
+static void gator_events_mmaped_stop(void)
+{
+#ifdef TODO
+ writel(DISABLED, COUNTERS_CONTROL_OFFSET);
+#endif
+}
+
+#ifndef TODO
+/* This function "simulates" counters, generating values of fancy
+ * functions like sine or triangle... */
+static int mmaped_simulate(int counter, int delta_in_us)
+{
+ int result = 0;
+
+ switch (counter) {
+ case 0: /* sort-of-sine */
+ {
+ static int t = 0;
+ int x;
+
+ t += delta_in_us;
+ if (t > 2048000)
+ t = 0;
+
+ if (t % 1024000 < 512000)
+ x = 512000 - (t % 512000);
+ else
+ x = t % 512000;
+
+ result = 32 * x / 512000;
+ result = result * result;
+
+ if (t < 1024000)
+ result = 1922 - result;
+ }
+ break;
+ case 1: /* triangle */
+ {
+ static int v, d = 1;
+
+ v = v + d * delta_in_us;
+ if (v < 0) {
+ v = 0;
+ d = 1;
+ } else if (v > 1000000) {
+ v = 1000000;
+ d = -1;
+ }
+
+ result = v;
+ }
+ break;
+ case 2: /* PWM signal */
+ {
+ static int dc, x, t = 0;
+
+ t += delta_in_us;
+ if (t > 1000000)
+ t = 0;
+ if (x / 1000000 != (x + delta_in_us) / 1000000)
+ dc = (dc + 100000) % 1000000;
+ x += delta_in_us;
+
+ result = t < dc ? 0 : 10;
+ }
+ break;
+ }
+
+ return result;
+}
+#endif
+
+static int gator_events_mmaped_read(int **buffer)
+{
+ int i;
+ int len = 0;
+#ifndef TODO
+ int delta_in_us;
+ struct timespec ts;
+ s64 time;
+#endif
+
+ /* System wide counters - read from one core only */
+ if (!on_primary_core())
+ return 0;
+
+#ifndef TODO
+ getnstimeofday(&ts);
+ time = timespec_to_ns(&ts);
+ delta_in_us = (int)(time - prev_time) / 1000;
+ prev_time = time;
+#endif
+
+ for (i = 0; i < MMAPED_COUNTERS_NUM; i++) {
+ if (mmaped_counters[i].enabled) {
+ mmaped_buffer[len++] = mmaped_counters[i].key;
+#ifdef TODO
+ mmaped_buffer[len++] =
+ readl(mmaped_base + COUNTERS_VALUE_OFFSET[i]);
+#else
+ mmaped_buffer[len++] =
+ mmaped_simulate(mmaped_counters[i].event,
+ delta_in_us);
+#endif
+ }
+ }
+
+ if (buffer)
+ *buffer = mmaped_buffer;
+
+ return len;
+}
+
+static struct gator_interface gator_events_mmaped_interface = {
+ .create_files = gator_events_mmaped_create_files,
+ .start = gator_events_mmaped_start,
+ .stop = gator_events_mmaped_stop,
+ .read = gator_events_mmaped_read,
+};
+
+/* Must not be static! */
+int __init gator_events_mmaped_init(void)
+{
+ int i;
+
+#ifdef TODO
+ mmaped_base = ioremap(COUNTERS_PHYS_ADDR, SZ_4K);
+ if (!mmaped_base)
+ return -ENOMEM;
+#endif
+
+ for (i = 0; i < MMAPED_COUNTERS_NUM; i++) {
+ mmaped_counters[i].enabled = 0;
+ mmaped_counters[i].key = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_mmaped_interface);
+}
+
+gator_events_init(gator_events_mmaped_init);
diff --git a/drivers/gator/gator_events_net.c b/drivers/gator/gator_events_net.c
new file mode 100644
index 000000000000..80cdee41ae3d
--- /dev/null
+++ b/drivers/gator/gator_events_net.c
@@ -0,0 +1,171 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "gator.h"
+#include <linux/netdevice.h>
+#include <linux/hardirq.h>
+
+#define NETRX 0
+#define NETTX 1
+#define TOTALNET 2
+
+static ulong netrx_enabled;
+static ulong nettx_enabled;
+static ulong netrx_key;
+static ulong nettx_key;
+static int rx_total, tx_total;
+static ulong netPrev[TOTALNET];
+static int netGet[TOTALNET * 4];
+
+static struct timer_list net_wake_up_timer;
+
+// Must be run in process context as the kernel function dev_get_stats() can sleep
+static void get_network_stats(struct work_struct *wsptr)
+{
+ int rx = 0, tx = 0;
+ struct net_device *dev;
+
+ for_each_netdev(&init_net, dev) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+ const struct net_device_stats *stats = dev_get_stats(dev);
+#else
+ struct rtnl_link_stats64 temp;
+ const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
+#endif
+ rx += stats->rx_bytes;
+ tx += stats->tx_bytes;
+ }
+ rx_total = rx;
+ tx_total = tx;
+}
+
+DECLARE_WORK(wq_get_stats, get_network_stats);
+
+static void net_wake_up_handler(unsigned long unused_data)
+{
+ // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater
+ schedule_work(&wq_get_stats);
+}
+
+static void calculate_delta(int *rx, int *tx)
+{
+ int rx_calc, tx_calc;
+
+ rx_calc = (int)(rx_total - netPrev[NETRX]);
+ if (rx_calc < 0)
+ rx_calc = 0;
+ netPrev[NETRX] += rx_calc;
+
+ tx_calc = (int)(tx_total - netPrev[NETTX]);
+ if (tx_calc < 0)
+ tx_calc = 0;
+ netPrev[NETTX] += tx_calc;
+
+ *rx = rx_calc;
+ *tx = tx_calc;
+}
+
+static int gator_events_net_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+
+ dir = gatorfs_mkdir(sb, root, "Linux_net_rx");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &netrx_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &netrx_key);
+
+ dir = gatorfs_mkdir(sb, root, "Linux_net_tx");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &nettx_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &nettx_key);
+
+ return 0;
+}
+
+static int gator_events_net_start(void)
+{
+ get_network_stats(0);
+ netPrev[NETRX] = rx_total;
+ netPrev[NETTX] = tx_total;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+ setup_timer(&net_wake_up_timer, net_wake_up_handler, 0);
+#else
+ setup_deferrable_timer_on_stack(&net_wake_up_timer, net_wake_up_handler, 0);
+#endif
+ return 0;
+}
+
+static void gator_events_net_stop(void)
+{
+ del_timer_sync(&net_wake_up_timer);
+ netrx_enabled = 0;
+ nettx_enabled = 0;
+}
+
+static int gator_events_net_read(int **buffer)
+{
+ int len, rx_delta, tx_delta;
+ static int last_rx_delta = 0, last_tx_delta = 0;
+
+ if (!on_primary_core())
+ return 0;
+
+ if (!netrx_enabled && !nettx_enabled)
+ return 0;
+
+ mod_timer(&net_wake_up_timer, jiffies + 1);
+
+ calculate_delta(&rx_delta, &tx_delta);
+
+ len = 0;
+ if (netrx_enabled && last_rx_delta != rx_delta) {
+ last_rx_delta = rx_delta;
+ netGet[len++] = netrx_key;
+ netGet[len++] = 0; // indicates to Streamline that rx_delta bytes were transmitted now, not since the last message
+ netGet[len++] = netrx_key;
+ netGet[len++] = rx_delta;
+ }
+
+ if (nettx_enabled && last_tx_delta != tx_delta) {
+ last_tx_delta = tx_delta;
+ netGet[len++] = nettx_key;
+ netGet[len++] = 0; // indicates to Streamline that tx_delta bytes were transmitted now, not since the last message
+ netGet[len++] = nettx_key;
+ netGet[len++] = tx_delta;
+ }
+
+ if (buffer)
+ *buffer = netGet;
+
+ return len;
+}
+
+static struct gator_interface gator_events_net_interface = {
+ .create_files = gator_events_net_create_files,
+ .start = gator_events_net_start,
+ .stop = gator_events_net_stop,
+ .read = gator_events_net_read,
+};
+
+int gator_events_net_init(void)
+{
+ netrx_key = gator_events_get_key();
+ nettx_key = gator_events_get_key();
+
+ netrx_enabled = 0;
+ nettx_enabled = 0;
+
+ return gator_events_install(&gator_events_net_interface);
+}
+
+gator_events_init(gator_events_net_init);
diff --git a/drivers/gator/gator_events_perf_pmu.c b/drivers/gator/gator_events_perf_pmu.c
new file mode 100644
index 000000000000..53b2d0a5afbf
--- /dev/null
+++ b/drivers/gator/gator_events_perf_pmu.c
@@ -0,0 +1,516 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/perf_event.h>
+#include "gator.h"
+
+// gator_events_armvX.c is used for Linux 2.6.x
+#if GATOR_PERF_PMU_SUPPORT
+
+extern bool event_based_sampling;
+
+// Maximum number of per-core counters - currently reserves enough space for two full hardware PMUs for big.LITTLE
+#define CNTMAX 16
+#define CCI_400 4
+// Maximum number of uncore counters
+// + 1 for the cci-400 cycles counter
+#define UCCNT (CCI_400 + 1)
+
+// A gator_attr is needed for every counter
+struct gator_attr {
+ // Set once in gator_events_perf_pmu_*_init - the name of the event in the gatorfs
+ char name[40];
+ // Exposed in gatorfs - set by gatord to enable this counter
+ unsigned long enabled;
+ // Set once in gator_events_perf_pmu_*_init - the perf type to use, see perf_type_id in the perf_event.h header file.
+ unsigned long type;
+ // Exposed in gatorfs - set by gatord to select the event to collect
+ unsigned long event;
+ // Exposed in gatorfs - set by gatord with the sample period to use and enable EBS for this counter
+ unsigned long count;
+ // Exposed as read only in gatorfs - set once in __attr_init as the key to use in the APC data
+ unsigned long key;
+};
+
+// Per-core counter attributes
+static struct gator_attr attrs[CNTMAX];
+// Number of initialized per-core counters
+static int attr_count;
+// Uncore counter attributes
+static struct gator_attr uc_attrs[UCCNT];
+// Number of initialized uncore counters
+static int uc_attr_count;
+
+struct gator_event {
+ int curr;
+ int prev;
+ int prev_delta;
+ bool zero;
+ struct perf_event *pevent;
+ struct perf_event_attr *pevent_attr;
+};
+
+static DEFINE_PER_CPU(struct gator_event[CNTMAX], events);
+static struct gator_event uc_events[UCCNT];
+static DEFINE_PER_CPU(int[(CNTMAX + UCCNT)*2], perf_cnt);
+
+static void gator_events_perf_pmu_stop(void);
+
+static int __create_files(struct super_block *sb, struct dentry *root, struct gator_attr *const attr)
+{
+ struct dentry *dir;
+
+ if (attr->name[0] == '\0') {
+ return 0;
+ }
+ dir = gatorfs_mkdir(sb, root, attr->name);
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &attr->enabled);
+ gatorfs_create_ulong(sb, dir, "count", &attr->count);
+ gatorfs_create_ro_ulong(sb, dir, "key", &attr->key);
+ gatorfs_create_ulong(sb, dir, "event", &attr->event);
+
+ return 0;
+}
+
+static int gator_events_perf_pmu_create_files(struct super_block *sb, struct dentry *root)
+{
+ int cnt;
+
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ if (__create_files(sb, root, &attrs[cnt]) != 0) {
+ return -1;
+ }
+ }
+
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ if (__create_files(sb, root, &uc_attrs[cnt]) != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+static void ebs_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs)
+#else
+static void ebs_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs)
+#endif
+{
+ gator_backtrace_handler(regs);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+static void dummy_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs)
+#else
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs)
+#endif
+{
+// Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll
+}
+
+static int gator_events_perf_pmu_read(int **buffer);
+
+static int gator_events_perf_pmu_online(int **buffer, bool migrate)
+{
+ return gator_events_perf_pmu_read(buffer);
+}
+
+static void __online_dispatch(int cpu, bool migrate, struct gator_attr *const attr, struct gator_event *const event)
+{
+ perf_overflow_handler_t handler;
+
+ event->zero = true;
+
+ if (event->pevent != NULL || event->pevent_attr == 0 || migrate) {
+ return;
+ }
+
+ if (attr->count > 0) {
+ handler = ebs_overflow_handler;
+ } else {
+ handler = dummy_handler;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+ event->pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, 0, handler);
+#else
+ event->pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, 0, handler, 0);
+#endif
+ if (IS_ERR(event->pevent)) {
+ pr_debug("gator: unable to online a counter on cpu %d\n", cpu);
+ event->pevent = NULL;
+ return;
+ }
+
+ if (event->pevent->state != PERF_EVENT_STATE_ACTIVE) {
+ pr_debug("gator: inactive counter on cpu %d\n", cpu);
+ perf_event_release_kernel(event->pevent);
+ event->pevent = NULL;
+ return;
+ }
+}
+
+static void gator_events_perf_pmu_online_dispatch(int cpu, bool migrate)
+{
+ int cnt;
+
+ cpu = pcpu_to_lcpu(cpu);
+
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ __online_dispatch(cpu, migrate, &attrs[cnt], &per_cpu(events, cpu)[cnt]);
+ }
+
+ if (cpu == 0) {
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ __online_dispatch(cpu, migrate, &uc_attrs[cnt], &uc_events[cnt]);
+ }
+ }
+}
+
+static void __offline_dispatch(int cpu, struct gator_event *const event)
+{
+ struct perf_event *pe = NULL;
+
+ if (event->pevent) {
+ pe = event->pevent;
+ event->pevent = NULL;
+ }
+
+ if (pe) {
+ perf_event_release_kernel(pe);
+ }
+}
+
+static void gator_events_perf_pmu_offline_dispatch(int cpu, bool migrate)
+{
+ int cnt;
+
+ if (migrate) {
+ return;
+ }
+ cpu = pcpu_to_lcpu(cpu);
+
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ __offline_dispatch(cpu, &per_cpu(events, cpu)[cnt]);
+ }
+
+ if (cpu == 0) {
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ __offline_dispatch(cpu, &uc_events[cnt]);
+ }
+ }
+}
+
+static int __check_ebs(struct gator_attr *const attr)
+{
+ if (attr->count > 0) {
+ if (!event_based_sampling) {
+ event_based_sampling = true;
+ } else {
+ printk(KERN_WARNING "gator: Only one ebs counter is allowed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int __start(struct gator_attr *const attr, struct gator_event *const event)
+{
+ u32 size = sizeof(struct perf_event_attr);
+
+ event->pevent = NULL;
+ if (!attr->enabled) { // Skip disabled counters
+ return 0;
+ }
+
+ event->prev = 0;
+ event->curr = 0;
+ event->prev_delta = 0;
+ event->pevent_attr = kmalloc(size, GFP_KERNEL);
+ if (!event->pevent_attr) {
+ gator_events_perf_pmu_stop();
+ return -1;
+ }
+
+ memset(event->pevent_attr, 0, size);
+ event->pevent_attr->type = attr->type;
+ event->pevent_attr->size = size;
+ event->pevent_attr->config = attr->event;
+ event->pevent_attr->sample_period = attr->count;
+ event->pevent_attr->pinned = 1;
+
+ return 0;
+}
+
+static int gator_events_perf_pmu_start(void)
+{
+ int cnt, cpu;
+
+ event_based_sampling = false;
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ if (__check_ebs(&attrs[cnt]) != 0) {
+ return -1;
+ }
+ }
+
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ if (__check_ebs(&uc_attrs[cnt]) != 0) {
+ return -1;
+ }
+ }
+
+ for_each_present_cpu(cpu) {
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ if (__start(&attrs[cnt], &per_cpu(events, cpu)[cnt]) != 0) {
+ return -1;
+ }
+ }
+ }
+
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ if (__start(&uc_attrs[cnt], &uc_events[cnt]) != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void __event_stop(struct gator_event *const event)
+{
+ if (event->pevent_attr) {
+ kfree(event->pevent_attr);
+ event->pevent_attr = NULL;
+ }
+}
+
+static void __attr_stop(struct gator_attr *const attr)
+{
+ attr->enabled = 0;
+ attr->event = 0;
+ attr->count = 0;
+}
+
+static void gator_events_perf_pmu_stop(void)
+{
+ unsigned int cnt, cpu;
+
+ for_each_present_cpu(cpu) {
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ __event_stop(&per_cpu(events, cpu)[cnt]);
+ }
+ }
+
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ __event_stop(&uc_events[cnt]);
+ }
+
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ __attr_stop(&attrs[cnt]);
+ }
+
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ __attr_stop(&uc_attrs[cnt]);
+ }
+}
+
+static void __read(int *const len, int cpu, struct gator_attr *const attr, struct gator_event *const event)
+{
+ int delta;
+
+ struct perf_event *const ev = event->pevent;
+ if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) {
+ /* After creating the perf counter in __online_dispatch, there
+ * is a race condition between gator_events_perf_pmu_online and
+ * gator_events_perf_pmu_read. So have
+ * gator_events_perf_pmu_online call gator_events_perf_pmu_read
+ * and in __read check to see if it's the first call after
+ * __online_dispatch and if so, run the online code.
+ */
+ if (event->zero) {
+ ev->pmu->read(ev);
+ event->prev = event->curr = local64_read(&ev->count);
+ event->prev_delta = 0;
+ per_cpu(perf_cnt, cpu)[(*len)++] = attr->key;
+ per_cpu(perf_cnt, cpu)[(*len)++] = 0;
+ event->zero = false;
+ } else {
+ ev->pmu->read(ev);
+ event->curr = local64_read(&ev->count);
+ delta = event->curr - event->prev;
+ if (delta != 0 || delta != event->prev_delta) {
+ event->prev_delta = delta;
+ event->prev = event->curr;
+ per_cpu(perf_cnt, cpu)[(*len)++] = attr->key;
+ if (delta < 0) {
+ delta *= -1;
+ }
+ per_cpu(perf_cnt, cpu)[(*len)++] = delta;
+ }
+ }
+ }
+}
+
+static int gator_events_perf_pmu_read(int **buffer)
+{
+ int cnt, len = 0;
+ const int cpu = get_logical_cpu();
+
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ __read(&len, cpu, &attrs[cnt], &per_cpu(events, cpu)[cnt]);
+ }
+
+ if (cpu == 0) {
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ __read(&len, cpu, &uc_attrs[cnt], &uc_events[cnt]);
+ }
+ }
+
+ if (buffer) {
+ *buffer = per_cpu(perf_cnt, cpu);
+ }
+
+ return len;
+}
+
+static struct gator_interface gator_events_perf_pmu_interface = {
+ .create_files = gator_events_perf_pmu_create_files,
+ .start = gator_events_perf_pmu_start,
+ .stop = gator_events_perf_pmu_stop,
+ .online = gator_events_perf_pmu_online,
+ .online_dispatch = gator_events_perf_pmu_online_dispatch,
+ .offline_dispatch = gator_events_perf_pmu_offline_dispatch,
+ .read = gator_events_perf_pmu_read,
+};
+
+static void __attr_init(struct gator_attr *const attr)
+{
+ attr->name[0] = '\0';
+ attr->enabled = 0;
+ attr->type = 0;
+ attr->event = 0;
+ attr->count = 0;
+ attr->key = gator_events_get_key();
+}
+
+static void gator_events_perf_pmu_cci_init(const int type)
+{
+ int cnt;
+
+ strncpy(uc_attrs[uc_attr_count].name, "cci-400_ccnt", sizeof(uc_attrs[uc_attr_count].name));
+ uc_attrs[uc_attr_count].type = type;
+ ++uc_attr_count;
+
+ for (cnt = 0; cnt < CCI_400; ++cnt, ++uc_attr_count) {
+ struct gator_attr *const attr = &uc_attrs[uc_attr_count];
+ snprintf(attr->name, sizeof(attr->name), "cci-400_cnt%d", cnt);
+ attr->type = type;
+ }
+}
+
+static void gator_events_perf_pmu_cpu_init(const struct gator_cpu *const gator_cpu, const int type)
+{
+ int cnt;
+
+ snprintf(attrs[attr_count].name, sizeof(attrs[attr_count].name), "%s_ccnt", gator_cpu->pmnc_name);
+ attrs[attr_count].type = type;
+ ++attr_count;
+
+ for (cnt = 0; cnt < gator_cpu->pmnc_counters; ++cnt, ++attr_count) {
+ struct gator_attr *const attr = &attrs[attr_count];
+ snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", gator_cpu->pmnc_name, cnt);
+ attr->type = type;
+ }
+}
+
+int gator_events_perf_pmu_init(void)
+{
+ struct perf_event_attr pea;
+ struct perf_event *pe;
+ const struct gator_cpu *gator_cpu;
+ int type;
+ int cpu;
+ int cnt;
+ bool found_cpu = false;
+
+ for (cnt = 0; cnt < CNTMAX; cnt++) {
+ __attr_init(&attrs[cnt]);
+ }
+ for (cnt = 0; cnt < UCCNT; cnt++) {
+ __attr_init(&uc_attrs[cnt]);
+ }
+
+ memset(&pea, 0, sizeof(pea));
+ pea.size = sizeof(pea);
+ pea.config = 0xFF;
+ attr_count = 0;
+ uc_attr_count = 0;
+ for (type = PERF_TYPE_MAX; type < 0x20; ++type) {
+ pea.type = type;
+
+ // A particular PMU may work on some but not all cores, so try on each core
+ pe = NULL;
+ for_each_present_cpu(cpu) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+ pe = perf_event_create_kernel_counter(&pea, cpu, 0, dummy_handler);
+#else
+ pe = perf_event_create_kernel_counter(&pea, cpu, 0, dummy_handler, 0);
+#endif
+ if (!IS_ERR(pe)) {
+ break;
+ }
+ }
+ // Assume that valid PMUs are contiguous
+ if (IS_ERR(pe)) {
+ break;
+ }
+
+ if (pe->pmu != NULL && type == pe->pmu->type) {
+ if (strcmp("CCI", pe->pmu->name) == 0) {
+ gator_events_perf_pmu_cci_init(type);
+ } else if ((gator_cpu = gator_find_cpu_by_pmu_name(pe->pmu->name)) != NULL) {
+ found_cpu = true;
+ gator_events_perf_pmu_cpu_init(gator_cpu, type);
+ }
+ // Initialize gator_attrs for dynamic PMUs here
+ }
+
+ perf_event_release_kernel(pe);
+ }
+
+ if (!found_cpu) {
+ const struct gator_cpu *const gator_cpu = gator_find_cpu_by_cpuid(gator_cpuid());
+ if (gator_cpu == NULL) {
+ return -1;
+ }
+ gator_events_perf_pmu_cpu_init(gator_cpu, PERF_TYPE_RAW);
+ }
+
+ // Initialize gator_attrs for non-dynamic PMUs here
+
+ if (attr_count > CNTMAX) {
+ printk(KERN_ERR "gator: Too many perf counters\n");
+ return -1;
+ }
+
+ if (uc_attr_count > UCCNT) {
+ printk(KERN_ERR "gator: Too many perf uncore counters\n");
+ return -1;
+ }
+
+ return gator_events_install(&gator_events_perf_pmu_interface);
+}
+
+gator_events_init(gator_events_perf_pmu_init);
+#endif
diff --git a/drivers/gator/gator_events_sched.c b/drivers/gator/gator_events_sched.c
new file mode 100644
index 000000000000..461a0511143d
--- /dev/null
+++ b/drivers/gator/gator_events_sched.c
@@ -0,0 +1,115 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "gator.h"
+#include <trace/events/sched.h>
+
+#define SCHED_SWITCH 0
+#define SCHED_TOTAL (SCHED_SWITCH+1)
+
+static ulong sched_switch_enabled;
+static ulong sched_switch_key;
+static DEFINE_PER_CPU(int[SCHED_TOTAL], schedCnt);
+static DEFINE_PER_CPU(int[SCHED_TOTAL * 2], schedGet);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next))
+#else
+GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
+#endif
+{
+ unsigned long flags;
+
+ // disable interrupts to synchronize with gator_events_sched_read()
+ // spinlocks not needed since percpu buffers are used
+ local_irq_save(flags);
+ per_cpu(schedCnt, get_physical_cpu())[SCHED_SWITCH]++;
+ local_irq_restore(flags);
+}
+
+static int gator_events_sched_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+
+ /* switch */
+ dir = gatorfs_mkdir(sb, root, "Linux_sched_switch");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &sched_switch_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &sched_switch_key);
+
+ return 0;
+}
+
+static int gator_events_sched_start(void)
+{
+ // register tracepoints
+ if (sched_switch_enabled)
+ if (GATOR_REGISTER_TRACE(sched_switch))
+ goto sched_switch_exit;
+ pr_debug("gator: registered scheduler event tracepoints\n");
+
+ return 0;
+
+ // unregister tracepoints on error
+sched_switch_exit:
+ pr_err("gator: scheduler event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
+
+ return -1;
+}
+
+static void gator_events_sched_stop(void)
+{
+ if (sched_switch_enabled)
+ GATOR_UNREGISTER_TRACE(sched_switch);
+ pr_debug("gator: unregistered scheduler event tracepoints\n");
+
+ sched_switch_enabled = 0;
+}
+
+static int gator_events_sched_read(int **buffer)
+{
+ unsigned long flags;
+ int len, value;
+ int cpu = get_physical_cpu();
+
+ len = 0;
+ if (sched_switch_enabled) {
+ local_irq_save(flags);
+ value = per_cpu(schedCnt, cpu)[SCHED_SWITCH];
+ per_cpu(schedCnt, cpu)[SCHED_SWITCH] = 0;
+ local_irq_restore(flags);
+ per_cpu(schedGet, cpu)[len++] = sched_switch_key;
+ per_cpu(schedGet, cpu)[len++] = value;
+ }
+
+ if (buffer)
+ *buffer = per_cpu(schedGet, cpu);
+
+ return len;
+}
+
+static struct gator_interface gator_events_sched_interface = {
+ .create_files = gator_events_sched_create_files,
+ .start = gator_events_sched_start,
+ .stop = gator_events_sched_stop,
+ .read = gator_events_sched_read,
+};
+
+int gator_events_sched_init(void)
+{
+ sched_switch_enabled = 0;
+
+ sched_switch_key = gator_events_get_key();
+
+ return gator_events_install(&gator_events_sched_interface);
+}
+
+gator_events_init(gator_events_sched_init);
diff --git a/drivers/gator/gator_events_scorpion.c b/drivers/gator/gator_events_scorpion.c
new file mode 100644
index 000000000000..aaf306a4b4c8
--- /dev/null
+++ b/drivers/gator/gator_events_scorpion.c
@@ -0,0 +1,676 @@
+/**
+ * Copyright (C) ARM Limited 2011-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "gator.h"
+
+// gator_events_perf_pmu.c is used if perf is supported
+#if GATOR_NO_PERF_SUPPORT
+
+static const char *pmnc_name;
+static int pmnc_counters;
+
+// Per-CPU PMNC: config reg
+#define PMNC_E (1 << 0) /* Enable all counters */
+#define PMNC_P (1 << 1) /* Reset all counters */
+#define PMNC_C (1 << 2) /* Cycle counter reset */
+#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */
+#define PMNC_X (1 << 4) /* Export to ETM */
+#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug */
+#define PMNC_MASK 0x3f /* Mask for writable bits */
+
+// ccnt reg
+#define CCNT_REG (1 << 31)
+
+#define CCNT 0
+#define CNT0 1
+#define CNTMAX (4+1)
+
+static unsigned long pmnc_enabled[CNTMAX];
+static unsigned long pmnc_event[CNTMAX];
+static unsigned long pmnc_key[CNTMAX];
+
+static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
+
+enum scorpion_perf_types {
+ SCORPION_ICACHE_EXPL_INV = 0x4c,
+ SCORPION_ICACHE_MISS = 0x4d,
+ SCORPION_ICACHE_ACCESS = 0x4e,
+ SCORPION_ICACHE_CACHEREQ_L2 = 0x4f,
+ SCORPION_ICACHE_NOCACHE_L2 = 0x50,
+ SCORPION_HIQUP_NOPED = 0x51,
+ SCORPION_DATA_ABORT = 0x52,
+ SCORPION_IRQ = 0x53,
+ SCORPION_FIQ = 0x54,
+ SCORPION_ALL_EXCPT = 0x55,
+ SCORPION_UNDEF = 0x56,
+ SCORPION_SVC = 0x57,
+ SCORPION_SMC = 0x58,
+ SCORPION_PREFETCH_ABORT = 0x59,
+ SCORPION_INDEX_CHECK = 0x5a,
+ SCORPION_NULL_CHECK = 0x5b,
+ SCORPION_EXPL_ICIALLU = 0x5c,
+ SCORPION_IMPL_ICIALLU = 0x5d,
+ SCORPION_NONICIALLU_BTAC_INV = 0x5e,
+ SCORPION_ICIMVAU_IMPL_ICIALLU = 0x5f,
+ SCORPION_SPIPE_ONLY_CYCLES = 0x60,
+ SCORPION_XPIPE_ONLY_CYCLES = 0x61,
+ SCORPION_DUAL_CYCLES = 0x62,
+ SCORPION_DISPATCH_ANY_CYCLES = 0x63,
+ SCORPION_FIFO_FULLBLK_CMT = 0x64,
+ SCORPION_FAIL_COND_INST = 0x65,
+ SCORPION_PASS_COND_INST = 0x66,
+ SCORPION_ALLOW_VU_CLK = 0x67,
+ SCORPION_VU_IDLE = 0x68,
+ SCORPION_ALLOW_L2_CLK = 0x69,
+ SCORPION_L2_IDLE = 0x6a,
+ SCORPION_DTLB_IMPL_INV_SCTLR_DACR = 0x6b,
+ SCORPION_DTLB_EXPL_INV = 0x6c,
+ SCORPION_DTLB_MISS = 0x6d,
+ SCORPION_DTLB_ACCESS = 0x6e,
+ SCORPION_ITLB_MISS = 0x6f,
+ SCORPION_ITLB_IMPL_INV = 0x70,
+ SCORPION_ITLB_EXPL_INV = 0x71,
+ SCORPION_UTLB_D_MISS = 0x72,
+ SCORPION_UTLB_D_ACCESS = 0x73,
+ SCORPION_UTLB_I_MISS = 0x74,
+ SCORPION_UTLB_I_ACCESS = 0x75,
+ SCORPION_UTLB_INV_ASID = 0x76,
+ SCORPION_UTLB_INV_MVA = 0x77,
+ SCORPION_UTLB_INV_ALL = 0x78,
+ SCORPION_S2_HOLD_RDQ_UNAVAIL = 0x79,
+ SCORPION_S2_HOLD = 0x7a,
+ SCORPION_S2_HOLD_DEV_OP = 0x7b,
+ SCORPION_S2_HOLD_ORDER = 0x7c,
+ SCORPION_S2_HOLD_BARRIER = 0x7d,
+ SCORPION_VIU_DUAL_CYCLE = 0x7e,
+ SCORPION_VIU_SINGLE_CYCLE = 0x7f,
+ SCORPION_VX_PIPE_WAR_STALL_CYCLES = 0x80,
+ SCORPION_VX_PIPE_WAW_STALL_CYCLES = 0x81,
+ SCORPION_VX_PIPE_RAW_STALL_CYCLES = 0x82,
+ SCORPION_VX_PIPE_LOAD_USE_STALL = 0x83,
+ SCORPION_VS_PIPE_WAR_STALL_CYCLES = 0x84,
+ SCORPION_VS_PIPE_WAW_STALL_CYCLES = 0x85,
+ SCORPION_VS_PIPE_RAW_STALL_CYCLES = 0x86,
+ SCORPION_EXCEPTIONS_INV_OPERATION = 0x87,
+ SCORPION_EXCEPTIONS_DIV_BY_ZERO = 0x88,
+ SCORPION_COND_INST_FAIL_VX_PIPE = 0x89,
+ SCORPION_COND_INST_FAIL_VS_PIPE = 0x8a,
+ SCORPION_EXCEPTIONS_OVERFLOW = 0x8b,
+ SCORPION_EXCEPTIONS_UNDERFLOW = 0x8c,
+ SCORPION_EXCEPTIONS_DENORM = 0x8d,
+#ifdef CONFIG_ARCH_MSM_SCORPIONMP
+ SCORPIONMP_NUM_BARRIERS = 0x8e,
+ SCORPIONMP_BARRIER_CYCLES = 0x8f,
+#else
+ SCORPION_BANK_AB_HIT = 0x8e,
+ SCORPION_BANK_AB_ACCESS = 0x8f,
+ SCORPION_BANK_CD_HIT = 0x90,
+ SCORPION_BANK_CD_ACCESS = 0x91,
+ SCORPION_BANK_AB_DSIDE_HIT = 0x92,
+ SCORPION_BANK_AB_DSIDE_ACCESS = 0x93,
+ SCORPION_BANK_CD_DSIDE_HIT = 0x94,
+ SCORPION_BANK_CD_DSIDE_ACCESS = 0x95,
+ SCORPION_BANK_AB_ISIDE_HIT = 0x96,
+ SCORPION_BANK_AB_ISIDE_ACCESS = 0x97,
+ SCORPION_BANK_CD_ISIDE_HIT = 0x98,
+ SCORPION_BANK_CD_ISIDE_ACCESS = 0x99,
+ SCORPION_ISIDE_RD_WAIT = 0x9a,
+ SCORPION_DSIDE_RD_WAIT = 0x9b,
+ SCORPION_BANK_BYPASS_WRITE = 0x9c,
+ SCORPION_BANK_AB_NON_CASTOUT = 0x9d,
+ SCORPION_BANK_AB_L2_CASTOUT = 0x9e,
+ SCORPION_BANK_CD_NON_CASTOUT = 0x9f,
+ SCORPION_BANK_CD_L2_CASTOUT = 0xa0,
+#endif
+ MSM_MAX_EVT
+};
+
+struct scorp_evt {
+ u32 evt_type;
+ u32 val;
+ u8 grp;
+ u32 evt_type_act;
+};
+
+static const struct scorp_evt sc_evt[] = {
+ {SCORPION_ICACHE_EXPL_INV, 0x80000500, 0, 0x4d},
+ {SCORPION_ICACHE_MISS, 0x80050000, 0, 0x4e},
+ {SCORPION_ICACHE_ACCESS, 0x85000000, 0, 0x4f},
+ {SCORPION_ICACHE_CACHEREQ_L2, 0x86000000, 0, 0x4f},
+ {SCORPION_ICACHE_NOCACHE_L2, 0x87000000, 0, 0x4f},
+ {SCORPION_HIQUP_NOPED, 0x80080000, 0, 0x4e},
+ {SCORPION_DATA_ABORT, 0x8000000a, 0, 0x4c},
+ {SCORPION_IRQ, 0x80000a00, 0, 0x4d},
+ {SCORPION_FIQ, 0x800a0000, 0, 0x4e},
+ {SCORPION_ALL_EXCPT, 0x8a000000, 0, 0x4f},
+ {SCORPION_UNDEF, 0x8000000b, 0, 0x4c},
+ {SCORPION_SVC, 0x80000b00, 0, 0x4d},
+ {SCORPION_SMC, 0x800b0000, 0, 0x4e},
+ {SCORPION_PREFETCH_ABORT, 0x8b000000, 0, 0x4f},
+ {SCORPION_INDEX_CHECK, 0x8000000c, 0, 0x4c},
+ {SCORPION_NULL_CHECK, 0x80000c00, 0, 0x4d},
+ {SCORPION_EXPL_ICIALLU, 0x8000000d, 0, 0x4c},
+ {SCORPION_IMPL_ICIALLU, 0x80000d00, 0, 0x4d},
+ {SCORPION_NONICIALLU_BTAC_INV, 0x800d0000, 0, 0x4e},
+ {SCORPION_ICIMVAU_IMPL_ICIALLU, 0x8d000000, 0, 0x4f},
+
+ {SCORPION_SPIPE_ONLY_CYCLES, 0x80000600, 1, 0x51},
+ {SCORPION_XPIPE_ONLY_CYCLES, 0x80060000, 1, 0x52},
+ {SCORPION_DUAL_CYCLES, 0x86000000, 1, 0x53},
+ {SCORPION_DISPATCH_ANY_CYCLES, 0x89000000, 1, 0x53},
+ {SCORPION_FIFO_FULLBLK_CMT, 0x8000000d, 1, 0x50},
+ {SCORPION_FAIL_COND_INST, 0x800d0000, 1, 0x52},
+ {SCORPION_PASS_COND_INST, 0x8d000000, 1, 0x53},
+ {SCORPION_ALLOW_VU_CLK, 0x8000000e, 1, 0x50},
+ {SCORPION_VU_IDLE, 0x80000e00, 1, 0x51},
+ {SCORPION_ALLOW_L2_CLK, 0x800e0000, 1, 0x52},
+ {SCORPION_L2_IDLE, 0x8e000000, 1, 0x53},
+
+ {SCORPION_DTLB_IMPL_INV_SCTLR_DACR, 0x80000001, 2, 0x54},
+ {SCORPION_DTLB_EXPL_INV, 0x80000100, 2, 0x55},
+ {SCORPION_DTLB_MISS, 0x80010000, 2, 0x56},
+ {SCORPION_DTLB_ACCESS, 0x81000000, 2, 0x57},
+ {SCORPION_ITLB_MISS, 0x80000200, 2, 0x55},
+ {SCORPION_ITLB_IMPL_INV, 0x80020000, 2, 0x56},
+ {SCORPION_ITLB_EXPL_INV, 0x82000000, 2, 0x57},
+ {SCORPION_UTLB_D_MISS, 0x80000003, 2, 0x54},
+ {SCORPION_UTLB_D_ACCESS, 0x80000300, 2, 0x55},
+ {SCORPION_UTLB_I_MISS, 0x80030000, 2, 0x56},
+ {SCORPION_UTLB_I_ACCESS, 0x83000000, 2, 0x57},
+ {SCORPION_UTLB_INV_ASID, 0x80000400, 2, 0x55},
+ {SCORPION_UTLB_INV_MVA, 0x80040000, 2, 0x56},
+ {SCORPION_UTLB_INV_ALL, 0x84000000, 2, 0x57},
+ {SCORPION_S2_HOLD_RDQ_UNAVAIL, 0x80000800, 2, 0x55},
+ {SCORPION_S2_HOLD, 0x88000000, 2, 0x57},
+ {SCORPION_S2_HOLD_DEV_OP, 0x80000900, 2, 0x55},
+ {SCORPION_S2_HOLD_ORDER, 0x80090000, 2, 0x56},
+ {SCORPION_S2_HOLD_BARRIER, 0x89000000, 2, 0x57},
+
+ {SCORPION_VIU_DUAL_CYCLE, 0x80000001, 4, 0x5c},
+ {SCORPION_VIU_SINGLE_CYCLE, 0x80000100, 4, 0x5d},
+ {SCORPION_VX_PIPE_WAR_STALL_CYCLES, 0x80000005, 4, 0x5c},
+ {SCORPION_VX_PIPE_WAW_STALL_CYCLES, 0x80000500, 4, 0x5d},
+ {SCORPION_VX_PIPE_RAW_STALL_CYCLES, 0x80050000, 4, 0x5e},
+ {SCORPION_VX_PIPE_LOAD_USE_STALL, 0x80000007, 4, 0x5c},
+ {SCORPION_VS_PIPE_WAR_STALL_CYCLES, 0x80000008, 4, 0x5c},
+ {SCORPION_VS_PIPE_WAW_STALL_CYCLES, 0x80000800, 4, 0x5d},
+ {SCORPION_VS_PIPE_RAW_STALL_CYCLES, 0x80080000, 4, 0x5e},
+ {SCORPION_EXCEPTIONS_INV_OPERATION, 0x8000000b, 4, 0x5c},
+ {SCORPION_EXCEPTIONS_DIV_BY_ZERO, 0x80000b00, 4, 0x5d},
+ {SCORPION_COND_INST_FAIL_VX_PIPE, 0x800b0000, 4, 0x5e},
+ {SCORPION_COND_INST_FAIL_VS_PIPE, 0x8b000000, 4, 0x5f},
+ {SCORPION_EXCEPTIONS_OVERFLOW, 0x8000000c, 4, 0x5c},
+ {SCORPION_EXCEPTIONS_UNDERFLOW, 0x80000c00, 4, 0x5d},
+ {SCORPION_EXCEPTIONS_DENORM, 0x8c000000, 4, 0x5f},
+
+#ifdef CONFIG_ARCH_MSM_SCORPIONMP
+ {SCORPIONMP_NUM_BARRIERS, 0x80000e00, 3, 0x59},
+ {SCORPIONMP_BARRIER_CYCLES, 0x800e0000, 3, 0x5a},
+#else
+ {SCORPION_BANK_AB_HIT, 0x80000001, 3, 0x58},
+ {SCORPION_BANK_AB_ACCESS, 0x80000100, 3, 0x59},
+ {SCORPION_BANK_CD_HIT, 0x80010000, 3, 0x5a},
+ {SCORPION_BANK_CD_ACCESS, 0x81000000, 3, 0x5b},
+ {SCORPION_BANK_AB_DSIDE_HIT, 0x80000002, 3, 0x58},
+ {SCORPION_BANK_AB_DSIDE_ACCESS, 0x80000200, 3, 0x59},
+ {SCORPION_BANK_CD_DSIDE_HIT, 0x80020000, 3, 0x5a},
+ {SCORPION_BANK_CD_DSIDE_ACCESS, 0x82000000, 3, 0x5b},
+ {SCORPION_BANK_AB_ISIDE_HIT, 0x80000003, 3, 0x58},
+ {SCORPION_BANK_AB_ISIDE_ACCESS, 0x80000300, 3, 0x59},
+ {SCORPION_BANK_CD_ISIDE_HIT, 0x80030000, 3, 0x5a},
+ {SCORPION_BANK_CD_ISIDE_ACCESS, 0x83000000, 3, 0x5b},
+ {SCORPION_ISIDE_RD_WAIT, 0x80000009, 3, 0x58},
+ {SCORPION_DSIDE_RD_WAIT, 0x80090000, 3, 0x5a},
+ {SCORPION_BANK_BYPASS_WRITE, 0x8000000a, 3, 0x58},
+ {SCORPION_BANK_AB_NON_CASTOUT, 0x8000000c, 3, 0x58},
+ {SCORPION_BANK_AB_L2_CASTOUT, 0x80000c00, 3, 0x59},
+ {SCORPION_BANK_CD_NON_CASTOUT, 0x800c0000, 3, 0x5a},
+ {SCORPION_BANK_CD_L2_CASTOUT, 0x8c000000, 3, 0x5b},
+#endif
+};
+
+static inline void scorpion_pmnc_write(u32 val)
+{
+ val &= PMNC_MASK;
+ asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
+}
+
+static inline u32 scorpion_pmnc_read(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
+ return val;
+}
+
+static inline u32 scorpion_ccnt_read(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
+ return val;
+}
+
+static inline u32 scorpion_cntn_read(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
+ return val;
+}
+
+static inline u32 scorpion_pmnc_enable_counter(unsigned int cnt)
+{
+ u32 val;
+
+ if (cnt >= CNTMAX) {
+ pr_err("gator: CPU%u enabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
+ return -1;
+ }
+
+ if (cnt == CCNT)
+ val = CCNT_REG;
+ else
+ val = (1 << (cnt - CNT0));
+
+ asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
+
+ return cnt;
+}
+
+static inline u32 scorpion_pmnc_disable_counter(unsigned int cnt)
+{
+ u32 val;
+
+ if (cnt >= CNTMAX) {
+ pr_err("gator: CPU%u disabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
+ return -1;
+ }
+
+ if (cnt == CCNT)
+ val = CCNT_REG;
+ else
+ val = (1 << (cnt - CNT0));
+
+ asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
+
+ return cnt;
+}
+
+static inline int scorpion_pmnc_select_counter(unsigned int cnt)
+{
+ u32 val;
+
+ if ((cnt == CCNT) || (cnt >= CNTMAX)) {
+ pr_err("gator: CPU%u selecting wrong PMNC counter %d\n", smp_processor_id(), cnt);
+ return -1;
+ }
+
+ val = (cnt - CNT0);
+ asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
+
+ return cnt;
+}
+
+static u32 scorpion_read_lpm0(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c15, c0, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_write_lpm0(u32 val)
+{
+ asm volatile("mcr p15, 0, %0, c15, c0, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_lpm1(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 1, %0, c15, c0, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_write_lpm1(u32 val)
+{
+ asm volatile("mcr p15, 1, %0, c15, c0, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_lpm2(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 2, %0, c15, c0, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_write_lpm2(u32 val)
+{
+ asm volatile("mcr p15, 2, %0, c15, c0, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_l2lpm(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 3, %0, c15, c2, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_write_l2lpm(u32 val)
+{
+ asm volatile("mcr p15, 3, %0, c15, c2, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_vlpm(void)
+{
+ u32 val;
+ asm volatile("mrc p10, 7, %0, c11, c0, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_write_vlpm(u32 val)
+{
+ asm volatile("mcr p10, 7, %0, c11, c0, 0" : : "r" (val));
+}
+
+struct scorpion_access_funcs {
+ u32(*read)(void);
+ void (*write)(u32);
+};
+
+struct scorpion_access_funcs scor_func[] = {
+ {scorpion_read_lpm0, scorpion_write_lpm0},
+ {scorpion_read_lpm1, scorpion_write_lpm1},
+ {scorpion_read_lpm2, scorpion_write_lpm2},
+ {scorpion_read_l2lpm, scorpion_write_l2lpm},
+ {scorpion_read_vlpm, scorpion_write_vlpm},
+};
+
+u32 venum_orig_val;
+u32 fp_orig_val;
+
+static void scorpion_pre_vlpm(void)
+{
+ u32 venum_new_val;
+ u32 fp_new_val;
+
+ /* CPACR Enable CP10 access */
+ asm volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (venum_orig_val));
+ venum_new_val = venum_orig_val | 0x00300000;
+ asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_new_val));
+ /* Enable FPEXC */
+ asm volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (fp_orig_val));
+ fp_new_val = fp_orig_val | 0x40000000;
+ asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_new_val));
+}
+
+static void scorpion_post_vlpm(void)
+{
+ /* Restore FPEXC */
+ asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_orig_val));
+ /* Restore CPACR */
+ asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_orig_val));
+}
+
+#define COLMN0MASK 0x000000ff
+#define COLMN1MASK 0x0000ff00
+#define COLMN2MASK 0x00ff0000
+static u32 scorpion_get_columnmask(u32 setval)
+{
+ if (setval & COLMN0MASK)
+ return 0xffffff00;
+ else if (setval & COLMN1MASK)
+ return 0xffff00ff;
+ else if (setval & COLMN2MASK)
+ return 0xff00ffff;
+ else
+ return 0x80ffffff;
+}
+
+static void scorpion_evt_setup(u32 gr, u32 setval)
+{
+ u32 val;
+ if (gr == 4)
+ scorpion_pre_vlpm();
+ val = scorpion_get_columnmask(setval) & scor_func[gr].read();
+ val = val | setval;
+ scor_func[gr].write(val);
+ if (gr == 4)
+ scorpion_post_vlpm();
+}
+
+static int get_scorpion_evtinfo(unsigned int evt_type, struct scorp_evt *evtinfo)
+{
+ u32 idx;
+ if ((evt_type < 0x4c) || (evt_type >= MSM_MAX_EVT))
+ return 0;
+ idx = evt_type - 0x4c;
+ if (sc_evt[idx].evt_type == evt_type) {
+ evtinfo->val = sc_evt[idx].val;
+ evtinfo->grp = sc_evt[idx].grp;
+ evtinfo->evt_type_act = sc_evt[idx].evt_type_act;
+ return 1;
+ }
+ return 0;
+}
+
+static inline void scorpion_pmnc_write_evtsel(unsigned int cnt, u32 val)
+{
+ if (scorpion_pmnc_select_counter(cnt) == cnt) {
+ if (val < 0x40) {
+ asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
+ } else {
+ u32 zero = 0;
+ struct scorp_evt evtinfo;
+ // extract evtinfo.grp and evtinfo.tevt_type_act from val
+ if (get_scorpion_evtinfo(val, &evtinfo) == 0)
+ return;
+ asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (evtinfo.evt_type_act));
+ asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (zero));
+ scorpion_evt_setup(evtinfo.grp, val);
+ }
+ }
+}
+
+static void scorpion_pmnc_reset_counter(unsigned int cnt)
+{
+ u32 val = 0;
+
+ if (cnt == CCNT) {
+ scorpion_pmnc_disable_counter(cnt);
+
+ asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
+
+ if (pmnc_enabled[cnt] != 0)
+ scorpion_pmnc_enable_counter(cnt);
+
+ } else if (cnt >= CNTMAX) {
+ pr_err("gator: CPU%u resetting wrong PMNC counter %d\n", smp_processor_id(), cnt);
+ } else {
+ scorpion_pmnc_disable_counter(cnt);
+
+ if (scorpion_pmnc_select_counter(cnt) == cnt)
+ asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
+
+ if (pmnc_enabled[cnt] != 0)
+ scorpion_pmnc_enable_counter(cnt);
+ }
+}
+
+static int gator_events_scorpion_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int i;
+
+ for (i = 0; i < pmnc_counters; i++) {
+ char buf[40];
+ if (i == 0) {
+ snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name);
+ } else {
+ snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i - 1);
+ }
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
+ if (i > 0) {
+ gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int gator_events_scorpion_online(int **buffer, bool migrate)
+{
+ unsigned int cnt, len = 0, cpu = smp_processor_id();
+
+ if (scorpion_pmnc_read() & PMNC_E) {
+ scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E);
+ }
+
+ /* Initialize & Reset PMNC: C bit and P bit */
+ scorpion_pmnc_write(PMNC_P | PMNC_C);
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ unsigned long event;
+
+ if (!pmnc_enabled[cnt])
+ continue;
+
+ // disable counter
+ scorpion_pmnc_disable_counter(cnt);
+
+ event = pmnc_event[cnt] & 255;
+
+ // Set event (if destined for PMNx counters), We don't need to set the event if it's a cycle count
+ if (cnt != CCNT)
+ scorpion_pmnc_write_evtsel(cnt, event);
+
+ // reset counter
+ scorpion_pmnc_reset_counter(cnt);
+
+ // Enable counter, do not enable interrupt for this counter
+ scorpion_pmnc_enable_counter(cnt);
+ }
+
+ // enable
+ scorpion_pmnc_write(scorpion_pmnc_read() | PMNC_E);
+
+ // read the counters and toss the invalid data, return zero instead
+ for (cnt = 0; cnt < pmnc_counters; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ if (cnt == CCNT) {
+ scorpion_ccnt_read();
+ } else if (scorpion_pmnc_select_counter(cnt) == cnt) {
+ scorpion_cntn_read();
+ }
+ scorpion_pmnc_reset_counter(cnt);
+
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = 0;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static int gator_events_scorpion_offline(int **buffer, bool migrate)
+{
+ scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E);
+ return 0;
+}
+
+static void gator_events_scorpion_stop(void)
+{
+ unsigned int cnt;
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ }
+}
+
+static int gator_events_scorpion_read(int **buffer)
+{
+ int cnt, len = 0;
+ int cpu = smp_processor_id();
+
+ // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled
+ if (!(scorpion_pmnc_read() & PMNC_E)) {
+ return 0;
+ }
+
+ for (cnt = 0; cnt < pmnc_counters; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ int value;
+ if (cnt == CCNT) {
+ value = scorpion_ccnt_read();
+ } else if (scorpion_pmnc_select_counter(cnt) == cnt) {
+ value = scorpion_cntn_read();
+ } else {
+ value = 0;
+ }
+ scorpion_pmnc_reset_counter(cnt);
+
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = value;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static struct gator_interface gator_events_scorpion_interface = {
+ .create_files = gator_events_scorpion_create_files,
+ .stop = gator_events_scorpion_stop,
+ .online = gator_events_scorpion_online,
+ .offline = gator_events_scorpion_offline,
+ .read = gator_events_scorpion_read,
+};
+
+int gator_events_scorpion_init(void)
+{
+ unsigned int cnt;
+
+ switch (gator_cpuid()) {
+ case SCORPION:
+ pmnc_name = "Scorpion";
+ pmnc_counters = 4;
+ break;
+ case SCORPIONMP:
+ pmnc_name = "ScorpionMP";
+ pmnc_counters = 4;
+ break;
+ default:
+ return -1;
+ }
+
+ pmnc_counters++; // CNT[n] + CCNT
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ pmnc_key[cnt] = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_scorpion_interface);
+}
+
+gator_events_init(gator_events_scorpion_init);
+
+#else
+int gator_events_scorpion_init(void)
+{
+ return -1;
+}
+#endif
diff --git a/drivers/gator/gator_fs.c b/drivers/gator/gator_fs.c
new file mode 100644
index 000000000000..fe6f83d547e9
--- /dev/null
+++ b/drivers/gator/gator_fs.c
@@ -0,0 +1,382 @@
+/**
+ * @file gatorfs.c
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ *
+ * A simple filesystem for configuration and
+ * access of oprofile.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <asm/uaccess.h>
+
+#define gatorfs_MAGIC 0x24051020
+#define TMPBUFSIZE 50
+DEFINE_SPINLOCK(gatorfs_lock);
+
+static struct inode *gatorfs_get_inode(struct super_block *sb, int mode)
+{
+ struct inode *inode = new_inode(sb);
+
+ if (inode) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+ inode->i_ino = get_next_ino();
+#endif
+ inode->i_mode = mode;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ }
+ return inode;
+}
+
+static const struct super_operations s_ops = {
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
+};
+
+ssize_t gatorfs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset)
+{
+ return simple_read_from_buffer(buf, count, offset, str, strlen(str));
+}
+
+ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset)
+{
+ char tmpbuf[TMPBUFSIZE];
+ size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val);
+ if (maxlen > TMPBUFSIZE)
+ maxlen = TMPBUFSIZE;
+ return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen);
+}
+
+ssize_t gatorfs_u64_to_user(u64 val, char __user *buf, size_t count, loff_t *offset)
+{
+ char tmpbuf[TMPBUFSIZE];
+ size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%llu\n", val);
+ if (maxlen > TMPBUFSIZE)
+ maxlen = TMPBUFSIZE;
+ return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen);
+}
+
+int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count)
+{
+ char tmpbuf[TMPBUFSIZE];
+ unsigned long flags;
+
+ if (!count)
+ return 0;
+
+ if (count > TMPBUFSIZE - 1)
+ return -EINVAL;
+
+ memset(tmpbuf, 0x0, TMPBUFSIZE);
+
+ if (copy_from_user(tmpbuf, buf, count))
+ return -EFAULT;
+
+ spin_lock_irqsave(&gatorfs_lock, flags);
+ *val = simple_strtoul(tmpbuf, NULL, 0);
+ spin_unlock_irqrestore(&gatorfs_lock, flags);
+ return 0;
+}
+
+int gatorfs_u64_from_user(u64 *val, char const __user *buf, size_t count)
+{
+ char tmpbuf[TMPBUFSIZE];
+ unsigned long flags;
+
+ if (!count)
+ return 0;
+
+ if (count > TMPBUFSIZE - 1)
+ return -EINVAL;
+
+ memset(tmpbuf, 0x0, TMPBUFSIZE);
+
+ if (copy_from_user(tmpbuf, buf, count))
+ return -EFAULT;
+
+ spin_lock_irqsave(&gatorfs_lock, flags);
+ *val = simple_strtoull(tmpbuf, NULL, 0);
+ spin_unlock_irqrestore(&gatorfs_lock, flags);
+ return 0;
+}
+
+static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ unsigned long *val = file->private_data;
+ return gatorfs_ulong_to_user(*val, buf, count, offset);
+}
+
+static ssize_t u64_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ u64 *val = file->private_data;
+ return gatorfs_u64_to_user(*val, buf, count, offset);
+}
+
+static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset)
+{
+ unsigned long *value = file->private_data;
+ int retval;
+
+ if (*offset)
+ return -EINVAL;
+
+ retval = gatorfs_ulong_from_user(value, buf, count);
+
+ if (retval)
+ return retval;
+ return count;
+}
+
+static ssize_t u64_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset)
+{
+ u64 *value = file->private_data;
+ int retval;
+
+ if (*offset)
+ return -EINVAL;
+
+ retval = gatorfs_u64_from_user(value, buf, count);
+
+ if (retval)
+ return retval;
+ return count;
+}
+
+static int default_open(struct inode *inode, struct file *filp)
+{
+ if (inode->i_private)
+ filp->private_data = inode->i_private;
+ return 0;
+}
+
+static const struct file_operations ulong_fops = {
+ .read = ulong_read_file,
+ .write = ulong_write_file,
+ .open = default_open,
+};
+
+static const struct file_operations u64_fops = {
+ .read = u64_read_file,
+ .write = u64_write_file,
+ .open = default_open,
+};
+
+static const struct file_operations ulong_ro_fops = {
+ .read = ulong_read_file,
+ .open = default_open,
+};
+
+static const struct file_operations u64_ro_fops = {
+ .read = u64_read_file,
+ .open = default_open,
+};
+
+static struct dentry *__gatorfs_create_file(struct super_block *sb,
+ struct dentry *root,
+ char const *name,
+ const struct file_operations *fops,
+ int perm)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+
+ dentry = d_alloc_name(root, name);
+ if (!dentry)
+ return NULL;
+ inode = gatorfs_get_inode(sb, S_IFREG | perm);
+ if (!inode) {
+ dput(dentry);
+ return NULL;
+ }
+ inode->i_fop = fops;
+ d_add(dentry, inode);
+ return dentry;
+}
+
+int gatorfs_create_ulong(struct super_block *sb, struct dentry *root,
+ char const *name, unsigned long *val)
+{
+ struct dentry *d = __gatorfs_create_file(sb, root, name,
+ &ulong_fops, 0644);
+ if (!d)
+ return -EFAULT;
+
+ d->d_inode->i_private = val;
+ return 0;
+}
+
+int gatorfs_create_u64(struct super_block *sb, struct dentry *root,
+ char const *name, u64 *val)
+{
+ struct dentry *d = __gatorfs_create_file(sb, root, name,
+ &u64_fops, 0644);
+ if (!d)
+ return -EFAULT;
+
+ d->d_inode->i_private = val;
+ return 0;
+}
+
+int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root,
+ char const *name, unsigned long *val)
+{
+ struct dentry *d = __gatorfs_create_file(sb, root, name,
+ &ulong_ro_fops, 0444);
+ if (!d)
+ return -EFAULT;
+
+ d->d_inode->i_private = val;
+ return 0;
+}
+
+int gatorfs_create_ro_u64(struct super_block *sb, struct dentry *root,
+ char const *name, u64 * val)
+{
+ struct dentry *d =
+ __gatorfs_create_file(sb, root, name, &u64_ro_fops, 0444);
+ if (!d)
+ return -EFAULT;
+
+ d->d_inode->i_private = val;
+ return 0;
+}
+
+static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ atomic_t *val = file->private_data;
+ return gatorfs_ulong_to_user(atomic_read(val), buf, count, offset);
+}
+
+static const struct file_operations atomic_ro_fops = {
+ .read = atomic_read_file,
+ .open = default_open,
+};
+
+int gatorfs_create_ro_atomic(struct super_block *sb, struct dentry *root,
+ char const *name, atomic_t *val)
+{
+ struct dentry *d = __gatorfs_create_file(sb, root, name,
+ &atomic_ro_fops, 0444);
+ if (!d)
+ return -EFAULT;
+
+ d->d_inode->i_private = val;
+ return 0;
+}
+
+int gatorfs_create_file(struct super_block *sb, struct dentry *root,
+ char const *name, const struct file_operations *fops)
+{
+ if (!__gatorfs_create_file(sb, root, name, fops, 0644))
+ return -EFAULT;
+ return 0;
+}
+
+int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root,
+ char const *name,
+ const struct file_operations *fops, int perm)
+{
+ if (!__gatorfs_create_file(sb, root, name, fops, perm))
+ return -EFAULT;
+ return 0;
+}
+
+struct dentry *gatorfs_mkdir(struct super_block *sb,
+ struct dentry *root, char const *name)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+
+ dentry = d_alloc_name(root, name);
+ if (!dentry)
+ return NULL;
+ inode = gatorfs_get_inode(sb, S_IFDIR | 0755);
+ if (!inode) {
+ dput(dentry);
+ return NULL;
+ }
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+ d_add(dentry, inode);
+ return dentry;
+}
+
+static int gatorfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct inode *root_inode;
+ struct dentry *root_dentry;
+
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = gatorfs_MAGIC;
+ sb->s_op = &s_ops;
+ sb->s_time_gran = 1;
+
+ root_inode = gatorfs_get_inode(sb, S_IFDIR | 0755);
+ if (!root_inode)
+ return -ENOMEM;
+ root_inode->i_op = &simple_dir_inode_operations;
+ root_inode->i_fop = &simple_dir_operations;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+ root_dentry = d_alloc_root(root_inode);
+#else
+ root_dentry = d_make_root(root_inode);
+#endif
+
+ if (!root_dentry) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+ iput(root_inode);
+#endif
+ return -ENOMEM;
+ }
+
+ sb->s_root = root_dentry;
+
+ gator_op_create_files(sb, root_dentry);
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
+static int gatorfs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data,
+ struct vfsmount *mnt)
+{
+ return get_sb_single(fs_type, flags, data, gatorfs_fill_super, mnt);
+}
+#else
+static struct dentry *gatorfs_mount(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return mount_nodev(fs_type, flags, data, gatorfs_fill_super);
+}
+#endif
+
+static struct file_system_type gatorfs_type = {
+ .owner = THIS_MODULE,
+ .name = "gatorfs",
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
+ .get_sb = gatorfs_get_sb,
+#else
+ .mount = gatorfs_mount,
+#endif
+
+ .kill_sb = kill_litter_super,
+};
+
+int __init gatorfs_register(void)
+{
+ return register_filesystem(&gatorfs_type);
+}
+
+void gatorfs_unregister(void)
+{
+ unregister_filesystem(&gatorfs_type);
+}
diff --git a/drivers/gator/gator_hrtimer_gator.c b/drivers/gator/gator_hrtimer_gator.c
new file mode 100644
index 000000000000..b0c947afe1e1
--- /dev/null
+++ b/drivers/gator/gator_hrtimer_gator.c
@@ -0,0 +1,86 @@
+/**
+ * Copyright (C) ARM Limited 2011-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+// gator_hrtimer_perf.c is used if perf is supported
+// update, gator_hrtimer_gator.c always used until issues resolved with perf hrtimers
+#if 1
+
+void (*callback)(void);
+DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer);
+DEFINE_PER_CPU(ktime_t, hrtimer_expire);
+DEFINE_PER_CPU(int, hrtimer_is_active);
+static ktime_t profiling_interval;
+static void gator_hrtimer_online(void);
+static void gator_hrtimer_offline(void);
+
+static enum hrtimer_restart gator_hrtimer_notify(struct hrtimer *hrtimer)
+{
+ int cpu = get_logical_cpu();
+ hrtimer_forward(hrtimer, per_cpu(hrtimer_expire, cpu), profiling_interval);
+ per_cpu(hrtimer_expire, cpu) = ktime_add(per_cpu(hrtimer_expire, cpu), profiling_interval);
+ (*callback)();
+ return HRTIMER_RESTART;
+}
+
+static void gator_hrtimer_online(void)
+{
+ int cpu = get_logical_cpu();
+ struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu);
+
+ if (per_cpu(hrtimer_is_active, cpu) || profiling_interval.tv64 == 0)
+ return;
+
+ per_cpu(hrtimer_is_active, cpu) = 1;
+ hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ hrtimer->function = gator_hrtimer_notify;
+#ifdef CONFIG_PREEMPT_RT_BASE
+ hrtimer->irqsafe = 1;
+#endif
+ per_cpu(hrtimer_expire, cpu) = ktime_add(hrtimer->base->get_time(), profiling_interval);
+ hrtimer_start(hrtimer, per_cpu(hrtimer_expire, cpu), HRTIMER_MODE_ABS_PINNED);
+}
+
+static void gator_hrtimer_offline(void)
+{
+ int cpu = get_logical_cpu();
+ struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu);
+
+ if (!per_cpu(hrtimer_is_active, cpu))
+ return;
+
+ per_cpu(hrtimer_is_active, cpu) = 0;
+ hrtimer_cancel(hrtimer);
+}
+
+static int gator_hrtimer_init(int interval, void (*func)(void))
+{
+ int cpu;
+
+ (callback) = (func);
+
+ for_each_present_cpu(cpu) {
+ per_cpu(hrtimer_is_active, cpu) = 0;
+ }
+
+ // calculate profiling interval
+ if (interval > 0) {
+ profiling_interval = ns_to_ktime(1000000000UL / interval);
+ } else {
+ profiling_interval.tv64 = 0;
+ }
+
+ return 0;
+}
+
+static void gator_hrtimer_shutdown(void)
+{
+ /* empty */
+}
+
+#endif
diff --git a/drivers/gator/gator_hrtimer_perf.c b/drivers/gator/gator_hrtimer_perf.c
new file mode 100644
index 000000000000..7b95399478e4
--- /dev/null
+++ b/drivers/gator/gator_hrtimer_perf.c
@@ -0,0 +1,113 @@
+/**
+ * Copyright (C) ARM Limited 2011-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+// gator_hrtimer_gator.c is used if perf is not supported
+// update, gator_hrtimer_gator.c always used until issues resolved with perf hrtimers
+#if 0
+
+// Note: perf Cortex support added in 2.6.35 and PERF_COUNT_SW_CPU_CLOCK/hrtimer broken on 2.6.35 and 2.6.36
+// not relevant as this code is not active until 3.0.0, but wanted to document the issue
+
+void (*callback)(void);
+static int profiling_interval;
+static DEFINE_PER_CPU(struct perf_event *, perf_hrtimer);
+static DEFINE_PER_CPU(struct perf_event_attr *, perf_hrtimer_attr);
+
+static void gator_hrtimer_shutdown(void);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+static void hrtimer_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs)
+#else
+static void hrtimer_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs)
+#endif
+{
+ (*callback)();
+}
+
+static int gator_online_single_hrtimer(int cpu)
+{
+ if (per_cpu(perf_hrtimer, cpu) != 0 || per_cpu(perf_hrtimer_attr, cpu) == 0)
+ return 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+ per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler);
+#else
+ per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler, 0);
+#endif
+ if (IS_ERR(per_cpu(perf_hrtimer, cpu))) {
+ per_cpu(perf_hrtimer, cpu) = NULL;
+ return -1;
+ }
+
+ if (per_cpu(perf_hrtimer, cpu)->state != PERF_EVENT_STATE_ACTIVE) {
+ perf_event_release_kernel(per_cpu(perf_hrtimer, cpu));
+ per_cpu(perf_hrtimer, cpu) = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+static void gator_hrtimer_online(int cpu)
+{
+ if (gator_online_single_hrtimer(cpu) < 0) {
+ pr_debug("gator: unable to online the hrtimer on cpu%d\n", cpu);
+ }
+}
+
+static void gator_hrtimer_offline(int cpu)
+{
+ if (per_cpu(perf_hrtimer, cpu)) {
+ perf_event_release_kernel(per_cpu(perf_hrtimer, cpu));
+ per_cpu(perf_hrtimer, cpu) = NULL;
+ }
+}
+
+static int gator_hrtimer_init(int interval, void (*func)(void))
+{
+ u32 size = sizeof(struct perf_event_attr);
+ int cpu;
+
+ callback = func;
+
+ // calculate profiling interval
+ profiling_interval = 1000000000 / interval;
+
+ for_each_present_cpu(cpu) {
+ per_cpu(perf_hrtimer, cpu) = 0;
+ per_cpu(perf_hrtimer_attr, cpu) = kmalloc(size, GFP_KERNEL);
+ if (per_cpu(perf_hrtimer_attr, cpu) == 0) {
+ gator_hrtimer_shutdown();
+ return -1;
+ }
+
+ memset(per_cpu(perf_hrtimer_attr, cpu), 0, size);
+ per_cpu(perf_hrtimer_attr, cpu)->type = PERF_TYPE_SOFTWARE;
+ per_cpu(perf_hrtimer_attr, cpu)->size = size;
+ per_cpu(perf_hrtimer_attr, cpu)->config = PERF_COUNT_SW_CPU_CLOCK;
+ per_cpu(perf_hrtimer_attr, cpu)->sample_period = profiling_interval;
+ per_cpu(perf_hrtimer_attr, cpu)->pinned = 1;
+ }
+
+ return 0;
+}
+
+static void gator_hrtimer_shutdown(void)
+{
+ int cpu;
+
+ for_each_present_cpu(cpu) {
+ if (per_cpu(perf_hrtimer_attr, cpu)) {
+ kfree(per_cpu(perf_hrtimer_attr, cpu));
+ per_cpu(perf_hrtimer_attr, cpu) = NULL;
+ }
+ }
+}
+
+#endif
diff --git a/drivers/gator/gator_iks.c b/drivers/gator/gator_iks.c
new file mode 100644
index 000000000000..24233d775581
--- /dev/null
+++ b/drivers/gator/gator_iks.c
@@ -0,0 +1,195 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#if GATOR_IKS_SUPPORT
+
+#include <linux/of.h>
+#include <asm/bL_switcher.h>
+#include <asm/smp_plat.h>
+#include <trace/events/power_cpu_migrate.h>
+
+static bool map_cpuids;
+static int mpidr_cpuids[NR_CPUS];
+static const struct gator_cpu * mpidr_cpus[NR_CPUS];
+static int __lcpu_to_pcpu[NR_CPUS];
+
+static const struct gator_cpu *gator_find_cpu_by_dt_name(const char *const name)
+{
+ int i;
+
+ for (i = 0; gator_cpus[i].cpuid != 0; ++i) {
+ const struct gator_cpu *const gator_cpu = &gator_cpus[i];
+ if (gator_cpu->dt_name != NULL && strcmp(gator_cpu->dt_name, name) == 0) {
+ return gator_cpu;
+ }
+ }
+
+ return NULL;
+}
+
+static void calc_first_cluster_size(void)
+{
+ int len;
+ const u32 *val;
+ const char *compatible;
+ struct device_node *cn = NULL;
+ int mpidr_cpuids_count = 0;
+
+ // Zero is a valid cpuid, so initialize the array to 0xff's
+ memset(&mpidr_cpuids, 0xff, sizeof(mpidr_cpuids));
+ memset(&mpidr_cpus, 0, sizeof(mpidr_cpus));
+
+ while ((cn = of_find_node_by_type(cn, "cpu"))) {
+ BUG_ON(mpidr_cpuids_count >= NR_CPUS);
+
+ val = of_get_property(cn, "reg", &len);
+ if (!val || len != 4) {
+ pr_err("%s missing reg property\n", cn->full_name);
+ continue;
+ }
+ compatible = of_get_property(cn, "compatible", NULL);
+ if (compatible == NULL) {
+ pr_err("%s missing compatible property\n", cn->full_name);
+ continue;
+ }
+
+ mpidr_cpuids[mpidr_cpuids_count] = be32_to_cpup(val);
+ mpidr_cpus[mpidr_cpuids_count] = gator_find_cpu_by_dt_name(compatible);
+ ++mpidr_cpuids_count;
+ }
+
+ map_cpuids = (mpidr_cpuids_count == nr_cpu_ids);
+}
+
+static int linearize_mpidr(int mpidr)
+{
+ int i;
+ for (i = 0; i < nr_cpu_ids; ++i) {
+ if (mpidr_cpuids[i] == mpidr) {
+ return i;
+ }
+ }
+
+ BUG();
+}
+
+int lcpu_to_pcpu(const int lcpu)
+{
+ int pcpu;
+
+ if (!map_cpuids)
+ return lcpu;
+
+ BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0);
+ pcpu = __lcpu_to_pcpu[lcpu];
+ BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0);
+ return pcpu;
+}
+
+int pcpu_to_lcpu(const int pcpu)
+{
+ int lcpu;
+
+ if (!map_cpuids)
+ return pcpu;
+
+ BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0);
+ for (lcpu = 0; lcpu < nr_cpu_ids; ++lcpu) {
+ if (__lcpu_to_pcpu[lcpu] == pcpu) {
+ BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0);
+ return lcpu;
+ }
+ }
+ BUG();
+}
+
+static void gator_update_cpu_mapping(u32 cpu_hwid)
+{
+ int lcpu = smp_processor_id();
+ int pcpu = linearize_mpidr(cpu_hwid & MPIDR_HWID_BITMASK);
+ BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0);
+ BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0);
+ __lcpu_to_pcpu[lcpu] = pcpu;
+}
+
+GATOR_DEFINE_PROBE(cpu_migrate_begin, TP_PROTO(u64 timestamp, u32 cpu_hwid))
+{
+ const int cpu = get_physical_cpu();
+
+ gator_timer_offline((void *)1);
+ gator_timer_offline_dispatch(cpu, true);
+}
+
+GATOR_DEFINE_PROBE(cpu_migrate_finish, TP_PROTO(u64 timestamp, u32 cpu_hwid))
+{
+ int cpu;
+
+ gator_update_cpu_mapping(cpu_hwid);
+
+ // get_physical_cpu must be called after gator_update_cpu_mapping
+ cpu = get_physical_cpu();
+ gator_timer_online_dispatch(cpu, true);
+ gator_timer_online((void *)1);
+}
+
+GATOR_DEFINE_PROBE(cpu_migrate_current, TP_PROTO(u64 timestamp, u32 cpu_hwid))
+{
+ gator_update_cpu_mapping(cpu_hwid);
+}
+
+static void gator_send_iks_core_names(void)
+{
+ int cpu;
+ // Send the cpu names
+ for (cpu = 0; cpu < nr_cpu_ids; ++cpu) {
+ if (mpidr_cpus[cpu] != NULL) {
+ gator_send_core_name(cpu, mpidr_cpus[cpu]->cpuid, mpidr_cpus[cpu]);
+ }
+ }
+}
+
+static int gator_migrate_start(void)
+{
+ int retval = 0;
+
+ if (!map_cpuids)
+ return retval;
+
+ if (retval == 0)
+ retval = GATOR_REGISTER_TRACE(cpu_migrate_begin);
+ if (retval == 0)
+ retval = GATOR_REGISTER_TRACE(cpu_migrate_finish);
+ if (retval == 0)
+ retval = GATOR_REGISTER_TRACE(cpu_migrate_current);
+ if (retval == 0) {
+ // Initialize the logical to physical cpu mapping
+ memset(&__lcpu_to_pcpu, 0xff, sizeof(__lcpu_to_pcpu));
+ bL_switcher_trace_trigger();
+ }
+ return retval;
+}
+
+static void gator_migrate_stop(void)
+{
+ if (!map_cpuids)
+ return;
+
+ GATOR_UNREGISTER_TRACE(cpu_migrate_current);
+ GATOR_UNREGISTER_TRACE(cpu_migrate_finish);
+ GATOR_UNREGISTER_TRACE(cpu_migrate_begin);
+}
+
+#else
+
+#define calc_first_cluster_size()
+#define gator_send_iks_core_names()
+#define gator_migrate_start() 0
+#define gator_migrate_stop()
+
+#endif
diff --git a/drivers/gator/gator_main.c b/drivers/gator/gator_main.c
new file mode 100644
index 000000000000..e0db65596697
--- /dev/null
+++ b/drivers/gator/gator_main.c
@@ -0,0 +1,1440 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+// This version must match the gator daemon version
+static unsigned long gator_protocol_version = 16;
+
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/vmalloc.h>
+#include <linux/hardirq.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/perf_event.h>
+#include <linux/utsname.h>
+#include <asm/stacktrace.h>
+#include <asm/uaccess.h>
+
+#include "gator.h"
+#include "gator_events.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+#error kernels prior to 2.6.32 are not supported
+#endif
+
+#if !defined(CONFIG_GENERIC_TRACER) && !defined(CONFIG_TRACING)
+#error gator requires the kernel to have CONFIG_GENERIC_TRACER or CONFIG_TRACING defined
+#endif
+
+#ifndef CONFIG_PROFILING
+#error gator requires the kernel to have CONFIG_PROFILING defined
+#endif
+
+#ifndef CONFIG_HIGH_RES_TIMERS
+#error gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined to support PC sampling
+#endif
+
+#if defined(__arm__) && defined(CONFIG_SMP) && !defined(CONFIG_LOCAL_TIMERS)
+#error gator requires the kernel to have CONFIG_LOCAL_TIMERS defined on SMP systems
+#endif
+
+#if (GATOR_PERF_SUPPORT) && (!(GATOR_PERF_PMU_SUPPORT))
+#ifndef CONFIG_PERF_EVENTS
+#warning gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters
+#elif !defined CONFIG_HW_PERF_EVENTS
+#warning gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters
+#endif
+#endif
+
+/******************************************************************************
+ * DEFINES
+ ******************************************************************************/
+#define SUMMARY_BUFFER_SIZE (1*1024)
+#define BACKTRACE_BUFFER_SIZE (128*1024)
+#define NAME_BUFFER_SIZE (64*1024)
+#define COUNTER_BUFFER_SIZE (64*1024) // counters have the core as part of the data and the core value in the frame header may be discarded
+#define BLOCK_COUNTER_BUFFER_SIZE (128*1024)
+#define ANNOTATE_BUFFER_SIZE (128*1024) // annotate counters have the core as part of the data and the core value in the frame header may be discarded
+#define SCHED_TRACE_BUFFER_SIZE (128*1024)
+#define GPU_TRACE_BUFFER_SIZE (64*1024) // gpu trace counters have the core as part of the data and the core value in the frame header may be discarded
+#define IDLE_BUFFER_SIZE (32*1024) // idle counters have the core as part of the data and the core value in the frame header may be discarded
+
+#define NO_COOKIE 0U
+#define UNRESOLVED_COOKIE ~0U
+
+#define FRAME_SUMMARY 1
+#define FRAME_BACKTRACE 2
+#define FRAME_NAME 3
+#define FRAME_COUNTER 4
+#define FRAME_BLOCK_COUNTER 5
+#define FRAME_ANNOTATE 6
+#define FRAME_SCHED_TRACE 7
+#define FRAME_GPU_TRACE 8
+#define FRAME_IDLE 9
+
+#define MESSAGE_END_BACKTRACE 1
+
+#define MESSAGE_COOKIE 1
+#define MESSAGE_THREAD_NAME 2
+#define HRTIMER_CORE_NAME 3
+
+#define MESSAGE_GPU_START 1
+#define MESSAGE_GPU_STOP 2
+
+#define MESSAGE_SCHED_SWITCH 1
+#define MESSAGE_SCHED_EXIT 2
+#define MESSAGE_SCHED_START 3
+
+#define MESSAGE_IDLE_ENTER 1
+#define MESSAGE_IDLE_EXIT 2
+
+#define MAXSIZE_PACK32 5
+#define MAXSIZE_PACK64 10
+
+#define FRAME_HEADER_SIZE 3
+
+#if defined(__arm__)
+#define PC_REG regs->ARM_pc
+#elif defined(__aarch64__)
+#define PC_REG regs->pc
+#else
+#define PC_REG regs->ip
+#endif
+
+enum {
+ SUMMARY_BUF,
+ BACKTRACE_BUF,
+ NAME_BUF,
+ COUNTER_BUF,
+ BLOCK_COUNTER_BUF,
+ ANNOTATE_BUF,
+ SCHED_TRACE_BUF,
+ GPU_TRACE_BUF,
+ IDLE_BUF,
+ NUM_GATOR_BUFS
+};
+
+/******************************************************************************
+ * Globals
+ ******************************************************************************/
+static unsigned long gator_cpu_cores;
+// Size of the largest buffer. Effectively constant, set in gator_op_create_files
+static unsigned long userspace_buffer_size;
+static unsigned long gator_backtrace_depth;
+// How often to commit the buffers for live in nanoseconds
+static u64 gator_live_rate;
+
+static unsigned long gator_started;
+static u64 gator_monotonic_started;
+static unsigned long gator_buffer_opened;
+static unsigned long gator_timer_count;
+static unsigned long gator_response_type;
+static DEFINE_MUTEX(start_mutex);
+static DEFINE_MUTEX(gator_buffer_mutex);
+
+bool event_based_sampling;
+
+static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait);
+static DECLARE_WAIT_QUEUE_HEAD(gator_annotate_wait);
+static struct timer_list gator_buffer_wake_up_timer;
+static LIST_HEAD(gator_events);
+
+static DEFINE_PER_CPU(u64, last_timestamp);
+
+static bool printed_monotonic_warning;
+
+static bool sent_core_name[NR_CPUS];
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+static void buffer_check(int cpu, int buftype, u64 time);
+static void gator_commit_buffer(int cpu, int buftype, u64 time);
+static int buffer_bytes_available(int cpu, int buftype);
+static bool buffer_check_space(int cpu, int buftype, int bytes);
+static int contiguous_space_available(int cpu, int bufytpe);
+static void gator_buffer_write_packed_int(int cpu, int buftype, int x);
+static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x);
+static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len);
+static void gator_buffer_write_string(int cpu, int buftype, const char *x);
+static void gator_add_trace(int cpu, unsigned long address);
+static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time);
+static u64 gator_get_time(void);
+
+// Size of the buffer, must be a power of 2. Effectively constant, set in gator_op_setup.
+static uint32_t gator_buffer_size[NUM_GATOR_BUFS];
+// gator_buffer_size - 1, bitwise and with pos to get offset into the array. Effectively constant, set in gator_op_setup.
+static uint32_t gator_buffer_mask[NUM_GATOR_BUFS];
+// Read position in the buffer. Initialized to zero in gator_op_setup and incremented after bytes are read by userspace in userspace_buffer_read
+static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_read);
+// Write position in the buffer. Initialized to zero in gator_op_setup and incremented after bytes are written to the buffer
+static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_write);
+// Commit position in the buffer. Initialized to zero in gator_op_setup and incremented after a frame is ready to be read by userspace
+static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_commit);
+// If set to false, decreases the number of bytes returned by buffer_bytes_available. Set in buffer_check_space if no space is remaining. Initialized to true in gator_op_setup
+// This means that if we run out of space, continue to report that no space is available until bytes are read by userspace
+static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], buffer_space_available);
+// The buffer. Allocated in gator_op_setup
+static DEFINE_PER_CPU(char *[NUM_GATOR_BUFS], gator_buffer);
+// The time after which the buffer should be committed for live display
+static DEFINE_PER_CPU(u64, gator_buffer_commit_time);
+
+/******************************************************************************
+ * Application Includes
+ ******************************************************************************/
+#include "gator_marshaling.c"
+#include "gator_hrtimer_perf.c"
+#include "gator_hrtimer_gator.c"
+#include "gator_cookies.c"
+#include "gator_annotate.c"
+#include "gator_trace_sched.c"
+#include "gator_trace_power.c"
+#include "gator_trace_gpu.c"
+#include "gator_backtrace.c"
+#include "gator_fs.c"
+#include "gator_pack.c"
+
+/******************************************************************************
+ * Misc
+ ******************************************************************************/
+
+const struct gator_cpu gator_cpus[] = {
+ {
+ .cpuid = ARM1136,
+ .core_name = "ARM1136",
+ .pmnc_name = "ARM_ARM11",
+ .dt_name = "arm,arm1136",
+ .pmnc_counters = 3,
+ },
+ {
+ .cpuid = ARM1156,
+ .core_name = "ARM1156",
+ .pmnc_name = "ARM_ARM11",
+ .dt_name = "arm,arm1156",
+ .pmnc_counters = 3,
+ },
+ {
+ .cpuid = ARM1176,
+ .core_name = "ARM1176",
+ .pmnc_name = "ARM_ARM11",
+ .dt_name = "arm,arm1176",
+ .pmnc_counters = 3,
+ },
+ {
+ .cpuid = ARM11MPCORE,
+ .core_name = "ARM11MPCore",
+ .pmnc_name = "ARM_ARM11MPCore",
+ .dt_name = "arm,arm11mpcore",
+ .pmnc_counters = 3,
+ },
+ {
+ .cpuid = CORTEX_A5,
+ .core_name = "Cortex-A5",
+ .pmu_name = "ARMv7_Cortex_A5",
+ .pmnc_name = "ARM_Cortex-A5",
+ .dt_name = "arm,cortex-a5",
+ .pmnc_counters = 2,
+ },
+ {
+ .cpuid = CORTEX_A7,
+ .core_name = "Cortex-A7",
+ .pmu_name = "ARMv7_Cortex_A7",
+ .pmnc_name = "ARM_Cortex-A7",
+ .dt_name = "arm,cortex-a7",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = CORTEX_A8,
+ .core_name = "Cortex-A8",
+ .pmu_name = "ARMv7_Cortex_A8",
+ .pmnc_name = "ARM_Cortex-A8",
+ .dt_name = "arm,cortex-a8",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = CORTEX_A9,
+ .core_name = "Cortex-A9",
+ .pmu_name = "ARMv7_Cortex_A9",
+ .pmnc_name = "ARM_Cortex-A9",
+ .dt_name = "arm,cortex-a9",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = CORTEX_A12,
+ .core_name = "Cortex-A12",
+ .pmu_name = "ARMv7_Cortex_A12",
+ .pmnc_name = "ARM_Cortex-A12",
+ .dt_name = "arm,cortex-a12",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = CORTEX_A15,
+ .core_name = "Cortex-A15",
+ .pmu_name = "ARMv7_Cortex_A15",
+ .pmnc_name = "ARM_Cortex-A15",
+ .dt_name = "arm,cortex-a15",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = SCORPION,
+ .core_name = "Scorpion",
+ .pmnc_name = "Scorpion",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = SCORPIONMP,
+ .core_name = "ScorpionMP",
+ .pmnc_name = "ScorpionMP",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = KRAITSIM,
+ .core_name = "KraitSIM",
+ .pmnc_name = "Krait",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = KRAIT,
+ .core_name = "Krait",
+ .pmnc_name = "Krait",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = KRAIT_S4_PRO,
+ .core_name = "Krait S4 Pro",
+ .pmnc_name = "Krait",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = CORTEX_A53,
+ .core_name = "Cortex-A53",
+ .pmnc_name = "ARM_Cortex-A53",
+ .dt_name = "arm,cortex-a53",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = CORTEX_A57,
+ .core_name = "Cortex-A57",
+ .pmnc_name = "ARM_Cortex-A57",
+ .dt_name = "arm,cortex-a57",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = AARCH64,
+ .core_name = "AArch64",
+ .pmnc_name = "ARM_AArch64",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = OTHER,
+ .core_name = "Other",
+ .pmnc_name = "Other",
+ .pmnc_counters = 6,
+ },
+ {}
+};
+
+const struct gator_cpu *gator_find_cpu_by_cpuid(const u32 cpuid)
+{
+ int i;
+
+ for (i = 0; gator_cpus[i].cpuid != 0; ++i) {
+ const struct gator_cpu *const gator_cpu = &gator_cpus[i];
+ if (gator_cpu->cpuid == cpuid) {
+ return gator_cpu;
+ }
+ }
+
+ return NULL;
+}
+
+const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name)
+{
+ int i;
+
+ for (i = 0; gator_cpus[i].cpuid != 0; ++i) {
+ const struct gator_cpu *const gator_cpu = &gator_cpus[i];
+ if (gator_cpu->pmu_name != NULL && strcmp(gator_cpu->pmu_name, name) == 0) {
+ return gator_cpu;
+ }
+ }
+
+ return NULL;
+}
+
+u32 gator_cpuid(void)
+{
+#if defined(__arm__) || defined(__aarch64__)
+ u32 val;
+#if !defined(__aarch64__)
+ asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (val));
+#else
+ asm volatile("mrs %0, midr_el1" : "=r" (val));
+#endif
+ return (val >> 4) & 0xfff;
+#else
+ return OTHER;
+#endif
+}
+
+static void gator_buffer_wake_up(unsigned long data)
+{
+ wake_up(&gator_buffer_wait);
+}
+
+/******************************************************************************
+ * Commit interface
+ ******************************************************************************/
+static bool buffer_commit_ready(int *cpu, int *buftype)
+{
+ int cpu_x, x;
+ for_each_present_cpu(cpu_x) {
+ for (x = 0; x < NUM_GATOR_BUFS; x++)
+ if (per_cpu(gator_buffer_commit, cpu_x)[x] != per_cpu(gator_buffer_read, cpu_x)[x]) {
+ *cpu = cpu_x;
+ *buftype = x;
+ return true;
+ }
+ }
+ *cpu = -1;
+ *buftype = -1;
+ return false;
+}
+
+/******************************************************************************
+ * Buffer management
+ ******************************************************************************/
+static int buffer_bytes_available(int cpu, int buftype)
+{
+ int remaining, filled;
+
+ filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_read, cpu)[buftype];
+ if (filled < 0) {
+ filled += gator_buffer_size[buftype];
+ }
+
+ remaining = gator_buffer_size[buftype] - filled;
+
+ if (per_cpu(buffer_space_available, cpu)[buftype]) {
+ // Give some extra room; also allows space to insert the overflow error packet
+ remaining -= 200;
+ } else {
+ // Hysteresis, prevents multiple overflow messages
+ remaining -= 2000;
+ }
+
+ return remaining;
+}
+
+static int contiguous_space_available(int cpu, int buftype)
+{
+ int remaining = buffer_bytes_available(cpu, buftype);
+ int contiguous = gator_buffer_size[buftype] - per_cpu(gator_buffer_write, cpu)[buftype];
+ if (remaining < contiguous)
+ return remaining;
+ else
+ return contiguous;
+}
+
+static bool buffer_check_space(int cpu, int buftype, int bytes)
+{
+ int remaining = buffer_bytes_available(cpu, buftype);
+
+ if (remaining < bytes) {
+ per_cpu(buffer_space_available, cpu)[buftype] = false;
+ } else {
+ per_cpu(buffer_space_available, cpu)[buftype] = true;
+ }
+
+ return per_cpu(buffer_space_available, cpu)[buftype];
+}
+
+static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len)
+{
+ int i;
+ u32 write = per_cpu(gator_buffer_write, cpu)[buftype];
+ u32 mask = gator_buffer_mask[buftype];
+ char *buffer = per_cpu(gator_buffer, cpu)[buftype];
+
+ for (i = 0; i < len; i++) {
+ buffer[write] = x[i];
+ write = (write + 1) & mask;
+ }
+
+ per_cpu(gator_buffer_write, cpu)[buftype] = write;
+}
+
+static void gator_buffer_write_string(int cpu, int buftype, const char *x)
+{
+ int len = strlen(x);
+ gator_buffer_write_packed_int(cpu, buftype, len);
+ gator_buffer_write_bytes(cpu, buftype, x, len);
+}
+
+static void gator_commit_buffer(int cpu, int buftype, u64 time)
+{
+ int type_length, commit, length, byte;
+
+ if (!per_cpu(gator_buffer, cpu)[buftype])
+ return;
+
+ // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
+ type_length = gator_response_type ? 1 : 0;
+ commit = per_cpu(gator_buffer_commit, cpu)[buftype];
+ length = per_cpu(gator_buffer_write, cpu)[buftype] - commit;
+ if (length < 0) {
+ length += gator_buffer_size[buftype];
+ }
+ length = length - type_length - sizeof(s32);
+
+ if (length <= FRAME_HEADER_SIZE) {
+ // Nothing to write, only the frame header is present
+ return;
+ }
+
+ for (byte = 0; byte < sizeof(s32); byte++) {
+ per_cpu(gator_buffer, cpu)[buftype][(commit + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF;
+ }
+
+ per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype];
+
+ if (gator_live_rate > 0) {
+ while (time > per_cpu(gator_buffer_commit_time, cpu)) {
+ per_cpu(gator_buffer_commit_time, cpu) += gator_live_rate;
+ }
+ }
+
+ marshal_frame(cpu, buftype);
+
+ // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater
+ mod_timer(&gator_buffer_wake_up_timer, jiffies + 1);
+}
+
+static void buffer_check(int cpu, int buftype, u64 time)
+{
+ int filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_commit, cpu)[buftype];
+ if (filled < 0) {
+ filled += gator_buffer_size[buftype];
+ }
+ if (filled >= ((gator_buffer_size[buftype] * 3) / 4)) {
+ gator_commit_buffer(cpu, buftype, time);
+ }
+}
+
+static void gator_add_trace(int cpu, unsigned long address)
+{
+ off_t offset = 0;
+ unsigned long cookie = get_address_cookie(cpu, current, address & ~1, &offset);
+
+ if (cookie == NO_COOKIE || cookie == UNRESOLVED_COOKIE) {
+ offset = address;
+ }
+
+ marshal_backtrace(offset & ~1, cookie);
+}
+
+static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time)
+{
+ bool inKernel;
+ unsigned long exec_cookie;
+
+ if (!regs)
+ return;
+
+ inKernel = !user_mode(regs);
+ exec_cookie = get_exec_cookie(cpu, current);
+
+ if (!marshal_backtrace_header(exec_cookie, current->tgid, current->pid, inKernel, time))
+ return;
+
+ if (inKernel) {
+ kernel_backtrace(cpu, regs);
+ } else {
+ // Cookie+PC
+ gator_add_trace(cpu, PC_REG);
+
+ // Backtrace
+ if (gator_backtrace_depth)
+ arm_backtrace_eabi(cpu, regs, gator_backtrace_depth);
+ }
+
+ marshal_backtrace_footer(time);
+}
+
+/******************************************************************************
+ * hrtimer interrupt processing
+ ******************************************************************************/
+static void gator_timer_interrupt(void)
+{
+ struct pt_regs *const regs = get_irq_regs();
+ gator_backtrace_handler(regs);
+}
+
+void gator_backtrace_handler(struct pt_regs *const regs)
+{
+ u64 time = gator_get_time();
+ int cpu = get_physical_cpu();
+
+ // Output backtrace
+ gator_add_sample(cpu, regs, time);
+
+ // Collect counters
+ if (!per_cpu(collecting, cpu)) {
+ collect_counters(time);
+ }
+}
+
+static int gator_running;
+
+// This function runs in interrupt context and on the appropriate core
+static void gator_timer_offline(void *migrate)
+{
+ struct gator_interface *gi;
+ int i, len, cpu = get_physical_cpu();
+ int *buffer;
+ u64 time;
+
+ gator_trace_sched_offline();
+ gator_trace_power_offline();
+
+ if (!migrate) {
+ gator_hrtimer_offline();
+ }
+
+ // Offline any events and output counters
+ time = gator_get_time();
+ if (marshal_event_header(time)) {
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->offline) {
+ len = gi->offline(&buffer, migrate);
+ marshal_event(len, buffer);
+ }
+ }
+ // Only check after writing all counters so that time and corresponding counters appear in the same frame
+ buffer_check(cpu, BLOCK_COUNTER_BUF, time);
+ }
+
+ // Flush all buffers on this core
+ for (i = 0; i < NUM_GATOR_BUFS; i++)
+ gator_commit_buffer(cpu, i, time);
+}
+
+// This function runs in interrupt context and may be running on a core other than core 'cpu'
+static void gator_timer_offline_dispatch(int cpu, bool migrate)
+{
+ struct gator_interface *gi;
+
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->offline_dispatch) {
+ gi->offline_dispatch(cpu, migrate);
+ }
+ }
+}
+
+static void gator_timer_stop(void)
+{
+ int cpu;
+
+ if (gator_running) {
+ on_each_cpu(gator_timer_offline, NULL, 1);
+ for_each_online_cpu(cpu) {
+ gator_timer_offline_dispatch(lcpu_to_pcpu(cpu), false);
+ }
+
+ gator_running = 0;
+ gator_hrtimer_shutdown();
+ }
+}
+
+#if defined(__arm__) || defined(__aarch64__)
+static void gator_send_core_name(int cpu, const u32 cpuid, const struct gator_cpu *const gator_cpu) {
+ const char *core_name = NULL;
+ char core_name_buf[32];
+
+ if (!sent_core_name[cpu]) {
+ if (gator_cpu != NULL) {
+ core_name = gator_cpu->core_name;
+ } else {
+ snprintf(core_name_buf, sizeof(core_name_buf), "Unknown (0x%.3x)", cpuid);
+ core_name = core_name_buf;
+ }
+
+ marshal_core_name(cpu, cpuid, core_name);
+ sent_core_name[cpu] = true;
+ }
+}
+#endif
+
+// This function runs in interrupt context and on the appropriate core
+static void gator_timer_online(void *migrate)
+{
+ struct gator_interface *gi;
+ int len, cpu = get_physical_cpu();
+ int *buffer;
+ u64 time;
+
+ gator_trace_power_online();
+
+ // online any events and output counters
+ time = gator_get_time();
+ if (marshal_event_header(time)) {
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->online) {
+ len = gi->online(&buffer, migrate);
+ marshal_event(len, buffer);
+ }
+ }
+ // Only check after writing all counters so that time and corresponding counters appear in the same frame
+ buffer_check(cpu, BLOCK_COUNTER_BUF, time);
+ }
+
+ if (!migrate) {
+ gator_hrtimer_online();
+ }
+
+#if defined(__arm__) || defined(__aarch64__)
+ if (!sent_core_name[cpu]) {
+ const u32 cpuid = gator_cpuid();
+ gator_send_core_name(cpu, cpuid, gator_find_cpu_by_cpuid(cpuid));
+ }
+#endif
+}
+
+// This function runs in interrupt context and may be running on a core other than core 'cpu'
+static void gator_timer_online_dispatch(int cpu, bool migrate)
+{
+ struct gator_interface *gi;
+
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->online_dispatch) {
+ gi->online_dispatch(cpu, migrate);
+ }
+ }
+}
+
+#include "gator_iks.c"
+
+int gator_timer_start(unsigned long sample_rate)
+{
+ int cpu;
+
+ if (gator_running) {
+ pr_notice("gator: already running\n");
+ return 0;
+ }
+
+ gator_running = 1;
+
+ // event based sampling trumps hr timer based sampling
+ if (event_based_sampling) {
+ sample_rate = 0;
+ }
+
+ if (gator_hrtimer_init(sample_rate, gator_timer_interrupt) == -1)
+ return -1;
+
+ gator_send_iks_core_names();
+ for_each_online_cpu(cpu) {
+ gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false);
+ }
+ on_each_cpu(gator_timer_online, NULL, 1);
+
+ return 0;
+}
+
+static u64 gator_get_time(void)
+{
+ struct timespec ts;
+ u64 timestamp;
+ u64 prev_timestamp;
+ u64 delta;
+ int cpu = smp_processor_id();
+
+ // Match clock_gettime(CLOCK_MONOTONIC_RAW, &ts) from userspace
+ getrawmonotonic(&ts);
+ timestamp = timespec_to_ns(&ts);
+
+ // getrawmonotonic is not monotonic on all systems. Detect and attempt to correct these cases.
+ // up to 0.5ms delta has been seen on some systems, which can skew Streamline data when viewing at high resolution.
+ // This doesn't work well with interrupts, but that it's OK - the real concern is to catch big jumps in time
+ prev_timestamp = per_cpu(last_timestamp, cpu);
+ if (prev_timestamp <= timestamp) {
+ per_cpu(last_timestamp, cpu) = timestamp;
+ } else {
+ delta = prev_timestamp - timestamp;
+ // Log the error once
+ if (!printed_monotonic_warning && delta > 500000) {
+ printk(KERN_ERR "%s: getrawmonotonic is not monotonic cpu: %i delta: %lli\nSkew in Streamline data may be present at the fine zoom levels\n", __FUNCTION__, cpu, delta);
+ printed_monotonic_warning = true;
+ }
+ timestamp = prev_timestamp;
+ }
+
+ return timestamp - gator_monotonic_started;
+}
+
+/******************************************************************************
+ * cpu hotplug and pm notifiers
+ ******************************************************************************/
+static int __cpuinit gator_hotcpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
+{
+ int cpu = lcpu_to_pcpu((long)hcpu);
+
+ switch (action) {
+ case CPU_DOWN_PREPARE:
+ case CPU_DOWN_PREPARE_FROZEN:
+ smp_call_function_single(cpu, gator_timer_offline, NULL, 1);
+ gator_timer_offline_dispatch(cpu, false);
+ break;
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ gator_timer_online_dispatch(cpu, false);
+ smp_call_function_single(cpu, gator_timer_online, NULL, 1);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __refdata gator_hotcpu_notifier = {
+ .notifier_call = gator_hotcpu_notify,
+};
+
+// n.b. calling "on_each_cpu" only runs on those that are online
+// Registered linux events are not disabled, so their counters will continue to collect
+static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy)
+{
+ int cpu;
+
+ switch (event) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ unregister_hotcpu_notifier(&gator_hotcpu_notifier);
+ unregister_scheduler_tracepoints();
+ on_each_cpu(gator_timer_offline, NULL, 1);
+ for_each_online_cpu(cpu) {
+ gator_timer_offline_dispatch(lcpu_to_pcpu(cpu), false);
+ }
+ break;
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ for_each_online_cpu(cpu) {
+ gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false);
+ }
+ on_each_cpu(gator_timer_online, NULL, 1);
+ register_scheduler_tracepoints();
+ register_hotcpu_notifier(&gator_hotcpu_notifier);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block gator_pm_notifier = {
+ .notifier_call = gator_pm_notify,
+};
+
+static int gator_notifier_start(void)
+{
+ int retval;
+ retval = register_hotcpu_notifier(&gator_hotcpu_notifier);
+ if (retval == 0)
+ retval = register_pm_notifier(&gator_pm_notifier);
+ return retval;
+}
+
+static void gator_notifier_stop(void)
+{
+ unregister_pm_notifier(&gator_pm_notifier);
+ unregister_hotcpu_notifier(&gator_hotcpu_notifier);
+}
+
+/******************************************************************************
+ * Main
+ ******************************************************************************/
+static void gator_summary(void)
+{
+ u64 timestamp, uptime;
+ struct timespec ts;
+ char uname_buf[512];
+ void (*m2b)(struct timespec *ts);
+ unsigned long flags;
+
+ snprintf(uname_buf, sizeof(uname_buf), "%s %s %s %s %s GNU/Linux", utsname()->sysname, utsname()->nodename, utsname()->release, utsname()->version, utsname()->machine);
+
+ getnstimeofday(&ts);
+ timestamp = timespec_to_ns(&ts);
+
+ do_posix_clock_monotonic_gettime(&ts);
+ // monotonic_to_bootbased is not defined for some versions of Android
+ m2b = symbol_get(monotonic_to_bootbased);
+ if (m2b) {
+ m2b(&ts);
+ }
+ uptime = timespec_to_ns(&ts);
+
+ // Disable interrupts as gator_get_time calls smp_processor_id to verify time is monotonic
+ local_irq_save(flags);
+ // Set monotonic_started to zero as gator_get_time is uptime minus monotonic_started
+ gator_monotonic_started = 0;
+ gator_monotonic_started = gator_get_time();
+ local_irq_restore(flags);
+
+ marshal_summary(timestamp, uptime, gator_monotonic_started, uname_buf);
+}
+
+int gator_events_install(struct gator_interface *interface)
+{
+ list_add_tail(&interface->list, &gator_events);
+
+ return 0;
+}
+
+int gator_events_get_key(void)
+{
+ // key of zero is reserved as a timestamp
+ static int key = 1;
+
+ const int ret = key;
+ key += 2;
+ return ret;
+}
+
+static int gator_init(void)
+{
+ int i;
+
+ calc_first_cluster_size();
+
+ // events sources (gator_events.h, generated by gator_events.sh)
+ for (i = 0; i < ARRAY_SIZE(gator_events_list); i++)
+ if (gator_events_list[i])
+ gator_events_list[i]();
+
+ gator_trace_sched_init();
+ gator_trace_power_init();
+
+ return 0;
+}
+
+static void gator_exit(void)
+{
+ struct gator_interface *gi;
+
+ list_for_each_entry(gi, &gator_events, list)
+ if (gi->shutdown)
+ gi->shutdown();
+}
+
+static int gator_start(void)
+{
+ unsigned long cpu, i;
+ struct gator_interface *gi;
+
+ if (gator_migrate_start())
+ goto migrate_failure;
+
+ // Initialize the buffer with the frame type and core
+ for_each_present_cpu(cpu) {
+ for (i = 0; i < NUM_GATOR_BUFS; i++) {
+ marshal_frame(cpu, i);
+ }
+ per_cpu(last_timestamp, cpu) = 0;
+ }
+ printed_monotonic_warning = false;
+
+ // Capture the start time
+ gator_summary();
+
+ // start all events
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->start && gi->start() != 0) {
+ struct list_head *ptr = gi->list.prev;
+
+ while (ptr != &gator_events) {
+ gi = list_entry(ptr, struct gator_interface, list);
+
+ if (gi->stop)
+ gi->stop();
+
+ ptr = ptr->prev;
+ }
+ goto events_failure;
+ }
+ }
+
+ // cookies shall be initialized before trace_sched_start() and gator_timer_start()
+ if (cookies_initialize())
+ goto cookies_failure;
+ if (gator_annotate_start())
+ goto annotate_failure;
+ if (gator_trace_sched_start())
+ goto sched_failure;
+ if (gator_trace_power_start())
+ goto power_failure;
+ if (gator_trace_gpu_start())
+ goto gpu_failure;
+ if (gator_timer_start(gator_timer_count))
+ goto timer_failure;
+ if (gator_notifier_start())
+ goto notifier_failure;
+
+ return 0;
+
+notifier_failure:
+ gator_timer_stop();
+timer_failure:
+ gator_trace_gpu_stop();
+gpu_failure:
+ gator_trace_power_stop();
+power_failure:
+ gator_trace_sched_stop();
+sched_failure:
+ gator_annotate_stop();
+annotate_failure:
+ cookies_release();
+cookies_failure:
+ // stop all events
+ list_for_each_entry(gi, &gator_events, list)
+ if (gi->stop)
+ gi->stop();
+events_failure:
+ gator_migrate_stop();
+migrate_failure:
+
+ return -1;
+}
+
+static void gator_stop(void)
+{
+ struct gator_interface *gi;
+
+ gator_annotate_stop();
+ gator_trace_sched_stop();
+ gator_trace_power_stop();
+ gator_trace_gpu_stop();
+
+ // stop all interrupt callback reads before tearing down other interfaces
+ gator_notifier_stop(); // should be called before gator_timer_stop to avoid re-enabling the hrtimer after it has been offlined
+ gator_timer_stop();
+
+ // stop all events
+ list_for_each_entry(gi, &gator_events, list)
+ if (gi->stop)
+ gi->stop();
+
+ gator_migrate_stop();
+}
+
+/******************************************************************************
+ * Filesystem
+ ******************************************************************************/
+/* fopen("buffer") */
+static int gator_op_setup(void)
+{
+ int err = 0;
+ int cpu, i;
+
+ mutex_lock(&start_mutex);
+
+ gator_buffer_size[SUMMARY_BUF] = SUMMARY_BUFFER_SIZE;
+ gator_buffer_mask[SUMMARY_BUF] = SUMMARY_BUFFER_SIZE - 1;
+
+ gator_buffer_size[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE;
+ gator_buffer_mask[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE - 1;
+
+ gator_buffer_size[NAME_BUF] = NAME_BUFFER_SIZE;
+ gator_buffer_mask[NAME_BUF] = NAME_BUFFER_SIZE - 1;
+
+ gator_buffer_size[COUNTER_BUF] = COUNTER_BUFFER_SIZE;
+ gator_buffer_mask[COUNTER_BUF] = COUNTER_BUFFER_SIZE - 1;
+
+ gator_buffer_size[BLOCK_COUNTER_BUF] = BLOCK_COUNTER_BUFFER_SIZE;
+ gator_buffer_mask[BLOCK_COUNTER_BUF] = BLOCK_COUNTER_BUFFER_SIZE - 1;
+
+ gator_buffer_size[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE;
+ gator_buffer_mask[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE - 1;
+
+ gator_buffer_size[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE;
+ gator_buffer_mask[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE - 1;
+
+ gator_buffer_size[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE;
+ gator_buffer_mask[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE - 1;
+
+ gator_buffer_size[IDLE_BUF] = IDLE_BUFFER_SIZE;
+ gator_buffer_mask[IDLE_BUF] = IDLE_BUFFER_SIZE - 1;
+
+ // Initialize percpu per buffer variables
+ for (i = 0; i < NUM_GATOR_BUFS; i++) {
+ // Verify buffers are a power of 2
+ if (gator_buffer_size[i] & (gator_buffer_size[i] - 1)) {
+ err = -ENOEXEC;
+ goto setup_error;
+ }
+
+ for_each_present_cpu(cpu) {
+ per_cpu(gator_buffer_read, cpu)[i] = 0;
+ per_cpu(gator_buffer_write, cpu)[i] = 0;
+ per_cpu(gator_buffer_commit, cpu)[i] = 0;
+ per_cpu(buffer_space_available, cpu)[i] = true;
+ per_cpu(gator_buffer_commit_time, cpu) = gator_live_rate;
+
+ // Annotation is a special case that only uses a single buffer
+ if (cpu > 0 && i == ANNOTATE_BUF) {
+ per_cpu(gator_buffer, cpu)[i] = NULL;
+ continue;
+ }
+
+ per_cpu(gator_buffer, cpu)[i] = vmalloc(gator_buffer_size[i]);
+ if (!per_cpu(gator_buffer, cpu)[i]) {
+ err = -ENOMEM;
+ goto setup_error;
+ }
+ }
+ }
+
+setup_error:
+ mutex_unlock(&start_mutex);
+ return err;
+}
+
+/* Actually start profiling (echo 1>/dev/gator/enable) */
+static int gator_op_start(void)
+{
+ int err = 0;
+
+ mutex_lock(&start_mutex);
+
+ if (gator_started || gator_start())
+ err = -EINVAL;
+ else
+ gator_started = 1;
+
+ mutex_unlock(&start_mutex);
+
+ return err;
+}
+
+/* echo 0>/dev/gator/enable */
+static void gator_op_stop(void)
+{
+ mutex_lock(&start_mutex);
+
+ if (gator_started) {
+ gator_stop();
+
+ mutex_lock(&gator_buffer_mutex);
+
+ gator_started = 0;
+ gator_monotonic_started = 0;
+ cookies_release();
+ wake_up(&gator_buffer_wait);
+
+ mutex_unlock(&gator_buffer_mutex);
+ }
+
+ mutex_unlock(&start_mutex);
+}
+
+static void gator_shutdown(void)
+{
+ int cpu, i;
+
+ mutex_lock(&start_mutex);
+
+ for_each_present_cpu(cpu) {
+ mutex_lock(&gator_buffer_mutex);
+ for (i = 0; i < NUM_GATOR_BUFS; i++) {
+ vfree(per_cpu(gator_buffer, cpu)[i]);
+ per_cpu(gator_buffer, cpu)[i] = NULL;
+ per_cpu(gator_buffer_read, cpu)[i] = 0;
+ per_cpu(gator_buffer_write, cpu)[i] = 0;
+ per_cpu(gator_buffer_commit, cpu)[i] = 0;
+ per_cpu(buffer_space_available, cpu)[i] = true;
+ per_cpu(gator_buffer_commit_time, cpu) = 0;
+ }
+ mutex_unlock(&gator_buffer_mutex);
+ }
+
+ memset(&sent_core_name, 0, sizeof(sent_core_name));
+
+ mutex_unlock(&start_mutex);
+}
+
+static int gator_set_backtrace(unsigned long val)
+{
+ int err = 0;
+
+ mutex_lock(&start_mutex);
+
+ if (gator_started)
+ err = -EBUSY;
+ else
+ gator_backtrace_depth = val;
+
+ mutex_unlock(&start_mutex);
+
+ return err;
+}
+
+static ssize_t enable_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ return gatorfs_ulong_to_user(gator_started, buf, count, offset);
+}
+
+static ssize_t enable_write(struct file *file, char const __user *buf, size_t count, loff_t *offset)
+{
+ unsigned long val;
+ int retval;
+
+ if (*offset)
+ return -EINVAL;
+
+ retval = gatorfs_ulong_from_user(&val, buf, count);
+ if (retval)
+ return retval;
+
+ if (val)
+ retval = gator_op_start();
+ else
+ gator_op_stop();
+
+ if (retval)
+ return retval;
+ return count;
+}
+
+static const struct file_operations enable_fops = {
+ .read = enable_read,
+ .write = enable_write,
+};
+
+static int userspace_buffer_open(struct inode *inode, struct file *file)
+{
+ int err = -EPERM;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (test_and_set_bit_lock(0, &gator_buffer_opened))
+ return -EBUSY;
+
+ if ((err = gator_op_setup()))
+ goto fail;
+
+ /* NB: the actual start happens from userspace
+ * echo 1 >/dev/gator/enable
+ */
+
+ return 0;
+
+fail:
+ __clear_bit_unlock(0, &gator_buffer_opened);
+ return err;
+}
+
+static int userspace_buffer_release(struct inode *inode, struct file *file)
+{
+ gator_op_stop();
+ gator_shutdown();
+ __clear_bit_unlock(0, &gator_buffer_opened);
+ return 0;
+}
+
+static ssize_t userspace_buffer_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ int commit, length1, length2, read;
+ char *buffer1;
+ char *buffer2;
+ int cpu, buftype;
+ int written = 0;
+
+ // ensure there is enough space for a whole frame
+ if (count < userspace_buffer_size || *offset) {
+ return -EINVAL;
+ }
+
+ // sleep until the condition is true or a signal is received
+ // the condition is checked each time gator_buffer_wait is woken up
+ wait_event_interruptible(gator_buffer_wait, buffer_commit_ready(&cpu, &buftype) || !gator_started);
+
+ if (signal_pending(current)) {
+ return -EINTR;
+ }
+
+ if (buftype == -1 || cpu == -1) {
+ return 0;
+ }
+
+ mutex_lock(&gator_buffer_mutex);
+
+ do {
+ read = per_cpu(gator_buffer_read, cpu)[buftype];
+ commit = per_cpu(gator_buffer_commit, cpu)[buftype];
+
+ // May happen if the buffer is freed during pending reads.
+ if (!per_cpu(gator_buffer, cpu)[buftype]) {
+ break;
+ }
+
+ // determine the size of two halves
+ length1 = commit - read;
+ length2 = 0;
+ buffer1 = &(per_cpu(gator_buffer, cpu)[buftype][read]);
+ buffer2 = &(per_cpu(gator_buffer, cpu)[buftype][0]);
+ if (length1 < 0) {
+ length1 = gator_buffer_size[buftype] - read;
+ length2 = commit;
+ }
+
+ if (length1 + length2 > count - written) {
+ break;
+ }
+
+ // start, middle or end
+ if (length1 > 0 && copy_to_user(&buf[written], buffer1, length1)) {
+ break;
+ }
+
+ // possible wrap around
+ if (length2 > 0 && copy_to_user(&buf[written + length1], buffer2, length2)) {
+ break;
+ }
+
+ per_cpu(gator_buffer_read, cpu)[buftype] = commit;
+ written += length1 + length2;
+
+ // Wake up annotate_write if more space is available
+ if (buftype == ANNOTATE_BUF) {
+ wake_up(&gator_annotate_wait);
+ }
+ } while (buffer_commit_ready(&cpu, &buftype));
+
+ mutex_unlock(&gator_buffer_mutex);
+
+ // kick just in case we've lost an SMP event
+ wake_up(&gator_buffer_wait);
+
+ return written > 0 ? written : -EFAULT;
+}
+
+const struct file_operations gator_event_buffer_fops = {
+ .open = userspace_buffer_open,
+ .release = userspace_buffer_release,
+ .read = userspace_buffer_read,
+};
+
+static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ return gatorfs_ulong_to_user(gator_backtrace_depth, buf, count, offset);
+}
+
+static ssize_t depth_write(struct file *file, char const __user *buf, size_t count, loff_t *offset)
+{
+ unsigned long val;
+ int retval;
+
+ if (*offset)
+ return -EINVAL;
+
+ retval = gatorfs_ulong_from_user(&val, buf, count);
+ if (retval)
+ return retval;
+
+ retval = gator_set_backtrace(val);
+
+ if (retval)
+ return retval;
+ return count;
+}
+
+static const struct file_operations depth_fops = {
+ .read = depth_read,
+ .write = depth_write
+};
+
+void gator_op_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ struct gator_interface *gi;
+ int cpu;
+
+ /* reinitialize default values */
+ gator_cpu_cores = 0;
+ for_each_present_cpu(cpu) {
+ gator_cpu_cores++;
+ }
+ userspace_buffer_size = BACKTRACE_BUFFER_SIZE;
+ gator_response_type = 1;
+ gator_live_rate = 0;
+
+ gatorfs_create_file(sb, root, "enable", &enable_fops);
+ gatorfs_create_file(sb, root, "buffer", &gator_event_buffer_fops);
+ gatorfs_create_file(sb, root, "backtrace_depth", &depth_fops);
+ gatorfs_create_ro_ulong(sb, root, "cpu_cores", &gator_cpu_cores);
+ gatorfs_create_ro_ulong(sb, root, "buffer_size", &userspace_buffer_size);
+ gatorfs_create_ulong(sb, root, "tick", &gator_timer_count);
+ gatorfs_create_ulong(sb, root, "response_type", &gator_response_type);
+ gatorfs_create_ro_ulong(sb, root, "version", &gator_protocol_version);
+ gatorfs_create_ro_u64(sb, root, "started", &gator_monotonic_started);
+ gatorfs_create_u64(sb, root, "live_rate", &gator_live_rate);
+
+ // Annotate interface
+ gator_annotate_create_files(sb, root);
+
+ // Linux Events
+ dir = gatorfs_mkdir(sb, root, "events");
+ list_for_each_entry(gi, &gator_events, list)
+ if (gi->create_files)
+ gi->create_files(sb, dir);
+
+ // Sched Events
+ sched_trace_create_files(sb, dir);
+
+ // Power interface
+ gator_trace_power_create_files(sb, dir);
+}
+
+/******************************************************************************
+ * Module
+ ******************************************************************************/
+static int __init gator_module_init(void)
+{
+ if (gatorfs_register()) {
+ return -1;
+ }
+
+ if (gator_init()) {
+ gatorfs_unregister();
+ return -1;
+ }
+
+ setup_timer(&gator_buffer_wake_up_timer, gator_buffer_wake_up, 0);
+
+ return 0;
+}
+
+static void __exit gator_module_exit(void)
+{
+ del_timer_sync(&gator_buffer_wake_up_timer);
+ tracepoint_synchronize_unregister();
+ gator_exit();
+ gatorfs_unregister();
+}
+
+module_init(gator_module_init);
+module_exit(gator_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("ARM Ltd");
+MODULE_DESCRIPTION("Gator system profiler");
diff --git a/drivers/gator/gator_marshaling.c b/drivers/gator/gator_marshaling.c
new file mode 100644
index 000000000000..3282de843f71
--- /dev/null
+++ b/drivers/gator/gator_marshaling.c
@@ -0,0 +1,413 @@
+/**
+ * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define NEWLINE_CANARY \
+ /* Unix */ \
+ "1\n" \
+ /* Windows */ \
+ "2\r\n" \
+ /* Mac OS */ \
+ "3\r" \
+ /* RISC OS */ \
+ "4\n\r" \
+ /* Add another character so the length isn't 0x0a bytes */ \
+ "5"
+
+#ifdef MALI_SUPPORT
+#include "gator_events_mali_common.h"
+#endif
+
+static void marshal_summary(long long timestamp, long long uptime, long long monotonic_delta, const char * uname)
+{
+ unsigned long flags;
+ int cpu = 0;
+
+ local_irq_save(flags);
+ gator_buffer_write_string(cpu, SUMMARY_BUF, NEWLINE_CANARY);
+ gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, timestamp);
+ gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, uptime);
+ gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, monotonic_delta);
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "uname");
+ gator_buffer_write_string(cpu, SUMMARY_BUF, uname);
+#if GATOR_IKS_SUPPORT
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "iks");
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "");
+#endif
+ // Let Streamline know which GPU is used so that it can label the GPU Activity appropriately. This is a temporary fix, to be improved in a future release.
+#ifdef MALI_SUPPORT
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "mali_type");
+#if (MALI_SUPPORT == MALI_4xx)
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "4xx");
+#elif (MALI_SUPPORT == MALI_T6xx)
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "6xx");
+#else
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "unknown");
+#endif
+#endif
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "");
+ // Commit the buffer now so it can be one of the first frames read by Streamline
+ gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time());
+ local_irq_restore(flags);
+}
+
+static bool marshal_cookie_header(const char *text)
+{
+ int cpu = get_physical_cpu();
+ return buffer_check_space(cpu, NAME_BUF, strlen(text) + 3 * MAXSIZE_PACK32);
+}
+
+static void marshal_cookie(int cookie, const char *text)
+{
+ int cpu = get_physical_cpu();
+ // buffer_check_space already called by marshal_cookie_header
+ gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_COOKIE);
+ gator_buffer_write_packed_int(cpu, NAME_BUF, cookie);
+ gator_buffer_write_string(cpu, NAME_BUF, text);
+ buffer_check(cpu, NAME_BUF, gator_get_time());
+}
+
+static void marshal_thread_name(int pid, char *name)
+{
+ unsigned long flags, cpu;
+ u64 time;
+ local_irq_save(flags);
+ cpu = get_physical_cpu();
+ time = gator_get_time();
+ if (buffer_check_space(cpu, NAME_BUF, TASK_COMM_LEN + 3 * MAXSIZE_PACK32 + MAXSIZE_PACK64)) {
+ gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_THREAD_NAME);
+ gator_buffer_write_packed_int64(cpu, NAME_BUF, time);
+ gator_buffer_write_packed_int(cpu, NAME_BUF, pid);
+ gator_buffer_write_string(cpu, NAME_BUF, name);
+ }
+ buffer_check(cpu, NAME_BUF, time);
+ local_irq_restore(flags);
+}
+
+static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, int inKernel, u64 time)
+{
+ int cpu = get_physical_cpu();
+ if (!buffer_check_space(cpu, BACKTRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32 + gator_backtrace_depth * 2 * MAXSIZE_PACK32)) {
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, BACKTRACE_BUF, time);
+
+ return false;
+ }
+
+ gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, time);
+ gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, exec_cookie);
+ gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, tgid);
+ gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, pid);
+ gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, inKernel);
+
+ return true;
+}
+
+static void marshal_backtrace(unsigned long address, int cookie)
+{
+ int cpu = get_physical_cpu();
+ gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, cookie);
+ gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, address);
+}
+
+static void marshal_backtrace_footer(u64 time)
+{
+ int cpu = get_physical_cpu();
+ gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_END_BACKTRACE);
+
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, BACKTRACE_BUF, time);
+}
+
+static bool marshal_event_header(u64 time)
+{
+ unsigned long flags, cpu = get_physical_cpu();
+ bool retval = false;
+
+ local_irq_save(flags);
+ if (buffer_check_space(cpu, BLOCK_COUNTER_BUF, MAXSIZE_PACK32 + MAXSIZE_PACK64)) {
+ gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, 0); // key of zero indicates a timestamp
+ gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, time);
+ retval = true;
+ }
+ local_irq_restore(flags);
+
+ return retval;
+}
+
+static void marshal_event(int len, int *buffer)
+{
+ unsigned long i, flags, cpu = get_physical_cpu();
+
+ if (len <= 0)
+ return;
+
+ // length must be even since all data is a (key, value) pair
+ if (len & 0x1) {
+ pr_err("gator: invalid counter data detected and discarded");
+ return;
+ }
+
+ // events must be written in key,value pairs
+ local_irq_save(flags);
+ for (i = 0; i < len; i += 2) {
+ if (!buffer_check_space(cpu, BLOCK_COUNTER_BUF, 2 * MAXSIZE_PACK32)) {
+ break;
+ }
+ gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, buffer[i]);
+ gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, buffer[i + 1]);
+ }
+ local_irq_restore(flags);
+}
+
+static void marshal_event64(int len, long long *buffer64)
+{
+ unsigned long i, flags, cpu = get_physical_cpu();
+
+ if (len <= 0)
+ return;
+
+ // length must be even since all data is a (key, value) pair
+ if (len & 0x1) {
+ pr_err("gator: invalid counter data detected and discarded");
+ return;
+ }
+
+ // events must be written in key,value pairs
+ local_irq_save(flags);
+ for (i = 0; i < len; i += 2) {
+ if (!buffer_check_space(cpu, BLOCK_COUNTER_BUF, 2 * MAXSIZE_PACK64)) {
+ break;
+ }
+ gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, buffer64[i]);
+ gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, buffer64[i + 1]);
+ }
+ local_irq_restore(flags);
+}
+
+#if GATOR_CPU_FREQ_SUPPORT
+static void marshal_event_single(int core, int key, int value)
+{
+ unsigned long flags, cpu;
+ u64 time;
+
+ local_irq_save(flags);
+ cpu = get_physical_cpu();
+ time = gator_get_time();
+ if (buffer_check_space(cpu, COUNTER_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int64(cpu, COUNTER_BUF, time);
+ gator_buffer_write_packed_int(cpu, COUNTER_BUF, core);
+ gator_buffer_write_packed_int(cpu, COUNTER_BUF, key);
+ gator_buffer_write_packed_int(cpu, COUNTER_BUF, value);
+ }
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, COUNTER_BUF, time);
+ local_irq_restore(flags);
+}
+#endif
+
+static void marshal_sched_gpu_start(int unit, int core, int tgid, int pid)
+{
+ unsigned long cpu = get_physical_cpu(), flags;
+ u64 time;
+
+ if (!per_cpu(gator_buffer, cpu)[GPU_TRACE_BUF])
+ return;
+
+ local_irq_save(flags);
+ time = gator_get_time();
+ if (buffer_check_space(cpu, GPU_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, MESSAGE_GPU_START);
+ gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, time);
+ gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit);
+ gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core);
+ gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, tgid);
+ gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, pid);
+ }
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, GPU_TRACE_BUF, time);
+ local_irq_restore(flags);
+}
+
+static void marshal_sched_gpu_stop(int unit, int core)
+{
+ unsigned long cpu = get_physical_cpu(), flags;
+ u64 time;
+
+ if (!per_cpu(gator_buffer, cpu)[GPU_TRACE_BUF])
+ return;
+
+ local_irq_save(flags);
+ time = gator_get_time();
+ if (buffer_check_space(cpu, GPU_TRACE_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, MESSAGE_GPU_STOP);
+ gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, time);
+ gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit);
+ gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core);
+ }
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, GPU_TRACE_BUF, time);
+ local_irq_restore(flags);
+}
+
+static void marshal_sched_trace_start(int tgid, int pid, int cookie)
+{
+ unsigned long cpu = get_physical_cpu(), flags;
+ u64 time;
+
+ if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF])
+ return;
+
+ local_irq_save(flags);
+ time = gator_get_time();
+ if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_START);
+ gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time);
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, tgid);
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid);
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie);
+ }
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, SCHED_TRACE_BUF, time);
+ local_irq_restore(flags);
+}
+
+static void marshal_sched_trace_switch(int tgid, int pid, int cookie, int state)
+{
+ unsigned long cpu = get_physical_cpu(), flags;
+ u64 time;
+
+ if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF])
+ return;
+
+ local_irq_save(flags);
+ time = gator_get_time();
+ if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_SWITCH);
+ gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time);
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, tgid);
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid);
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie);
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, state);
+ }
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, SCHED_TRACE_BUF, time);
+ local_irq_restore(flags);
+}
+
+static void marshal_sched_trace_exit(int tgid, int pid)
+{
+ unsigned long cpu = get_physical_cpu(), flags;
+ u64 time;
+
+ if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF])
+ return;
+
+ local_irq_save(flags);
+ time = gator_get_time();
+ if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_EXIT);
+ gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time);
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid);
+ }
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, SCHED_TRACE_BUF, time);
+ local_irq_restore(flags);
+}
+
+#if GATOR_CPU_FREQ_SUPPORT
+static void marshal_idle(int core, int state)
+{
+ unsigned long flags, cpu;
+ u64 time;
+
+ local_irq_save(flags);
+ cpu = get_physical_cpu();
+ time = gator_get_time();
+ if (buffer_check_space(cpu, IDLE_BUF, MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int(cpu, IDLE_BUF, state);
+ gator_buffer_write_packed_int64(cpu, IDLE_BUF, time);
+ gator_buffer_write_packed_int(cpu, IDLE_BUF, core);
+ }
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, IDLE_BUF, time);
+ local_irq_restore(flags);
+}
+#endif
+
+static void marshal_frame(int cpu, int buftype)
+{
+ int frame;
+
+ if (!per_cpu(gator_buffer, cpu)[buftype]) {
+ return;
+ }
+
+ switch (buftype) {
+ case SUMMARY_BUF:
+ frame = FRAME_SUMMARY;
+ break;
+ case BACKTRACE_BUF:
+ frame = FRAME_BACKTRACE;
+ break;
+ case NAME_BUF:
+ frame = FRAME_NAME;
+ break;
+ case COUNTER_BUF:
+ frame = FRAME_COUNTER;
+ break;
+ case BLOCK_COUNTER_BUF:
+ frame = FRAME_BLOCK_COUNTER;
+ break;
+ case ANNOTATE_BUF:
+ frame = FRAME_ANNOTATE;
+ break;
+ case SCHED_TRACE_BUF:
+ frame = FRAME_SCHED_TRACE;
+ break;
+ case GPU_TRACE_BUF:
+ frame = FRAME_GPU_TRACE;
+ break;
+ case IDLE_BUF:
+ frame = FRAME_IDLE;
+ break;
+ default:
+ frame = -1;
+ break;
+ }
+
+ // add response type
+ if (gator_response_type > 0) {
+ gator_buffer_write_packed_int(cpu, buftype, gator_response_type);
+ }
+
+ // leave space for 4-byte unpacked length
+ per_cpu(gator_buffer_write, cpu)[buftype] = (per_cpu(gator_buffer_write, cpu)[buftype] + sizeof(s32)) & gator_buffer_mask[buftype];
+
+ // add frame type and core number
+ gator_buffer_write_packed_int(cpu, buftype, frame);
+ gator_buffer_write_packed_int(cpu, buftype, cpu);
+}
+
+#if defined(__arm__) || defined(__aarch64__)
+static void marshal_core_name(const int core, const int cpuid, const char *name)
+{
+ int cpu = get_physical_cpu();
+ unsigned long flags;
+ local_irq_save(flags);
+ if (buffer_check_space(cpu, NAME_BUF, MAXSIZE_PACK32 + MAXSIZE_CORE_NAME)) {
+ gator_buffer_write_packed_int(cpu, NAME_BUF, HRTIMER_CORE_NAME);
+ gator_buffer_write_packed_int(cpu, NAME_BUF, core);
+ gator_buffer_write_packed_int(cpu, NAME_BUF, cpuid);
+ gator_buffer_write_string(cpu, NAME_BUF, name);
+ }
+ // Commit core names now so that they can show up in live
+ gator_commit_buffer(cpu, NAME_BUF, gator_get_time());
+ local_irq_restore(flags);
+}
+#endif
diff --git a/drivers/gator/gator_pack.c b/drivers/gator/gator_pack.c
new file mode 100644
index 000000000000..2c082f283adc
--- /dev/null
+++ b/drivers/gator/gator_pack.c
@@ -0,0 +1,58 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+static void gator_buffer_write_packed_int(int cpu, int buftype, int x)
+{
+ uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype];
+ uint32_t mask = gator_buffer_mask[buftype];
+ char *buffer = per_cpu(gator_buffer, cpu)[buftype];
+ int packedBytes = 0;
+ int more = true;
+ while (more) {
+ // low order 7 bits of x
+ char b = x & 0x7f;
+ x >>= 7;
+
+ if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) {
+ more = false;
+ } else {
+ b |= 0x80;
+ }
+
+ buffer[(write + packedBytes) & mask] = b;
+ packedBytes++;
+ }
+
+ per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask;
+}
+
+static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x)
+{
+ uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype];
+ uint32_t mask = gator_buffer_mask[buftype];
+ char *buffer = per_cpu(gator_buffer, cpu)[buftype];
+ int packedBytes = 0;
+ int more = true;
+ while (more) {
+ // low order 7 bits of x
+ char b = x & 0x7f;
+ x >>= 7;
+
+ if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) {
+ more = false;
+ } else {
+ b |= 0x80;
+ }
+
+ buffer[(write + packedBytes) & mask] = b;
+ packedBytes++;
+ }
+
+ per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask;
+}
diff --git a/drivers/gator/gator_trace_gpu.c b/drivers/gator/gator_trace_gpu.c
new file mode 100644
index 000000000000..12623c4036ee
--- /dev/null
+++ b/drivers/gator/gator_trace_gpu.c
@@ -0,0 +1,294 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "gator.h"
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+
+#ifdef MALI_SUPPORT
+#include "linux/mali_linux_trace.h"
+#endif
+#include "gator_trace_gpu.h"
+
+/*
+ * Taken from MALI_PROFILING_EVENT_TYPE_* items in Mali DDK.
+ */
+#define EVENT_TYPE_SINGLE 0
+#define EVENT_TYPE_START 1
+#define EVENT_TYPE_STOP 2
+#define EVENT_TYPE_SUSPEND 3
+#define EVENT_TYPE_RESUME 4
+
+/* Note whether tracepoints have been registered */
+static int mali_timeline_trace_registered;
+static int mali_job_slots_trace_registered;
+static int gpu_trace_registered;
+
+enum {
+ GPU_UNIT_NONE = 0,
+ GPU_UNIT_VP,
+ GPU_UNIT_FP,
+ GPU_UNIT_CL,
+ NUMBER_OF_GPU_UNITS
+};
+
+#define MALI_4xx (0x0b07)
+#define MALI_T6xx (0x0056)
+
+struct mali_gpu_job {
+ int count;
+ int last_tgid;
+ int last_pid;
+ int last_job_id;
+};
+
+#define NUMBER_OF_GPU_CORES 16
+static struct mali_gpu_job mali_gpu_jobs[NUMBER_OF_GPU_UNITS][NUMBER_OF_GPU_CORES];
+static DEFINE_SPINLOCK(mali_gpu_jobs_lock);
+
+/* Only one event should be running on a unit and core at a time (ie, a start
+ * event can only be followed by a stop and vice versa), but because the kernel
+ * only knows when a job is enqueued and not started, it is possible for a
+ * start1, start2, stop1, stop2. Change it back into start1, stop1, start2,
+ * stop2 by queueing up start2 and releasing it when stop1 is received.
+ */
+static void mali_gpu_enqueue(int unit, int core, int tgid, int pid, int job_id)
+{
+ int count;
+
+ spin_lock(&mali_gpu_jobs_lock);
+ count = mali_gpu_jobs[unit][core].count;
+ BUG_ON(count < 0);
+ ++mali_gpu_jobs[unit][core].count;
+ if (count) {
+ mali_gpu_jobs[unit][core].last_tgid = tgid;
+ mali_gpu_jobs[unit][core].last_pid = pid;
+ mali_gpu_jobs[unit][core].last_job_id = job_id;
+ }
+ spin_unlock(&mali_gpu_jobs_lock);
+
+ if (!count) {
+ marshal_sched_gpu_start(unit, core, tgid, pid/*, job_id*/);
+ }
+}
+
+static void mali_gpu_stop(int unit, int core)
+{
+ int count;
+ int last_tgid = 0;
+ int last_pid = 0;
+ int last_job_id = 0;
+
+ spin_lock(&mali_gpu_jobs_lock);
+ if (mali_gpu_jobs[unit][core].count == 0) {
+ spin_unlock(&mali_gpu_jobs_lock);
+ return;
+ }
+ --mali_gpu_jobs[unit][core].count;
+ count = mali_gpu_jobs[unit][core].count;
+ if (count) {
+ last_tgid = mali_gpu_jobs[unit][core].last_tgid;
+ last_pid = mali_gpu_jobs[unit][core].last_pid;
+ last_job_id = mali_gpu_jobs[unit][core].last_job_id;
+ }
+ spin_unlock(&mali_gpu_jobs_lock);
+
+ marshal_sched_gpu_stop(unit, core);
+ if (count) {
+ marshal_sched_gpu_start(unit, core, last_tgid, last_pid/*, last_job_id*/);
+ }
+}
+
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx)
+#include "gator_events_mali_4xx.h"
+
+/*
+ * Taken from MALI_PROFILING_EVENT_CHANNEL_* in Mali DDK.
+ */
+enum {
+ EVENT_CHANNEL_SOFTWARE = 0,
+ EVENT_CHANNEL_VP0 = 1,
+ EVENT_CHANNEL_FP0 = 5,
+ EVENT_CHANNEL_FP1,
+ EVENT_CHANNEL_FP2,
+ EVENT_CHANNEL_FP3,
+ EVENT_CHANNEL_FP4,
+ EVENT_CHANNEL_FP5,
+ EVENT_CHANNEL_FP6,
+ EVENT_CHANNEL_FP7,
+ EVENT_CHANNEL_GPU = 21
+};
+
+/**
+ * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from the GPU channel
+ */
+enum {
+ EVENT_REASON_SINGLE_GPU_NONE = 0,
+ EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE = 1,
+};
+
+GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4))
+{
+ unsigned int component, state;
+
+ // do as much work as possible before disabling interrupts
+ component = (event_id >> 16) & 0xFF; // component is an 8-bit field
+ state = (event_id >> 24) & 0xF; // state is a 4-bit field
+
+ switch (state) {
+ case EVENT_TYPE_START:
+ if (component == EVENT_CHANNEL_VP0) {
+ /* tgid = d0; pid = d1; */
+ mali_gpu_enqueue(GPU_UNIT_VP, 0, d0, d1, 0);
+ } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) {
+ /* tgid = d0; pid = d1; */
+ mali_gpu_enqueue(GPU_UNIT_FP, component - EVENT_CHANNEL_FP0, d0, d1, 0);
+ }
+ break;
+
+ case EVENT_TYPE_STOP:
+ if (component == EVENT_CHANNEL_VP0) {
+ mali_gpu_stop(GPU_UNIT_VP, 0);
+ } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) {
+ mali_gpu_stop(GPU_UNIT_FP, component - EVENT_CHANNEL_FP0);
+ }
+ break;
+
+ case EVENT_TYPE_SINGLE:
+ if (component == EVENT_CHANNEL_GPU) {
+ unsigned int reason = (event_id & 0xffff);
+
+ if (reason == EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE) {
+ gator_events_mali_log_dvfs_event(d0, d1);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+#endif
+
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx)
+#if defined(MALI_JOB_SLOTS_EVENT_CHANGED)
+GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid, unsigned char job_id))
+#else
+GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid))
+#endif
+{
+ unsigned int component, state, unit;
+#if !defined(MALI_JOB_SLOTS_EVENT_CHANGED)
+ unsigned char job_id = 0;
+#endif
+
+ component = (event_id >> 16) & 0xFF; // component is an 8-bit field
+ state = (event_id >> 24) & 0xF; // state is a 4-bit field
+
+ switch (component) {
+ case 0:
+ unit = GPU_UNIT_FP;
+ break;
+ case 1:
+ unit = GPU_UNIT_VP;
+ break;
+ case 2:
+ unit = GPU_UNIT_CL;
+ break;
+ default:
+ unit = GPU_UNIT_NONE;
+ }
+
+ if (unit != GPU_UNIT_NONE) {
+ switch (state) {
+ case EVENT_TYPE_START:
+ mali_gpu_enqueue(unit, 0, tgid, (pid != 0 ? pid : tgid), job_id);
+ break;
+ case EVENT_TYPE_STOP:
+ mali_gpu_stop(unit, 0);
+ break;
+ default:
+ /*
+ * Some jobs can be soft-stopped, so ensure that this terminates the activity trace.
+ */
+ mali_gpu_stop(unit, 0);
+ }
+ }
+}
+#endif
+
+GATOR_DEFINE_PROBE(gpu_activity_start, TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p))
+{
+ mali_gpu_enqueue(gpu_unit, gpu_core, (int)p->tgid, (int)p->pid, 0);
+}
+
+GATOR_DEFINE_PROBE(gpu_activity_stop, TP_PROTO(int gpu_unit, int gpu_core))
+{
+ mali_gpu_stop(gpu_unit, gpu_core);
+}
+
+int gator_trace_gpu_start(void)
+{
+ /*
+ * Returns nonzero for installation failed
+ * Absence of gpu trace points is not an error
+ */
+
+ memset(&mali_gpu_jobs, sizeof(mali_gpu_jobs), 0);
+ gpu_trace_registered = mali_timeline_trace_registered = mali_job_slots_trace_registered = 0;
+
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx)
+ if (!GATOR_REGISTER_TRACE(mali_timeline_event)) {
+ mali_timeline_trace_registered = 1;
+ }
+#endif
+
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx)
+ if (!GATOR_REGISTER_TRACE(mali_job_slots_event)) {
+ mali_job_slots_trace_registered = 1;
+ }
+#endif
+
+ if (!mali_timeline_trace_registered) {
+ if (GATOR_REGISTER_TRACE(gpu_activity_start)) {
+ return 0;
+ }
+ if (GATOR_REGISTER_TRACE(gpu_activity_stop)) {
+ GATOR_UNREGISTER_TRACE(gpu_activity_start);
+ return 0;
+ }
+ gpu_trace_registered = 1;
+ }
+
+ return 0;
+}
+
+void gator_trace_gpu_stop(void)
+{
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx)
+ if (mali_timeline_trace_registered) {
+ GATOR_UNREGISTER_TRACE(mali_timeline_event);
+ }
+#endif
+
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx)
+ if (mali_job_slots_trace_registered) {
+ GATOR_UNREGISTER_TRACE(mali_job_slots_event);
+ }
+#endif
+
+ if (gpu_trace_registered) {
+ GATOR_UNREGISTER_TRACE(gpu_activity_stop);
+ GATOR_UNREGISTER_TRACE(gpu_activity_start);
+ }
+
+ gpu_trace_registered = mali_timeline_trace_registered = mali_job_slots_trace_registered = 0;
+}
diff --git a/drivers/gator/gator_trace_gpu.h b/drivers/gator/gator_trace_gpu.h
new file mode 100644
index 000000000000..bb0f42d290da
--- /dev/null
+++ b/drivers/gator/gator_trace_gpu.h
@@ -0,0 +1,79 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#undef TRACE_GPU
+#define TRACE_GPU gpu
+
+#if !defined(_TRACE_GPU_H)
+#define _TRACE_GPU_H
+
+#include <linux/tracepoint.h>
+
+/*
+ * UNIT - the GPU processor type
+ * 1 = Vertex Processor
+ * 2 = Fragment Processor
+ *
+ * CORE - the GPU processor core number
+ * this is not the CPU core number
+ */
+
+/*
+ * Tracepoint for calling GPU unit start activity on core
+ */
+TRACE_EVENT(gpu_activity_start,
+
+ TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p),
+
+ TP_ARGS(gpu_unit, gpu_core, p),
+
+ TP_STRUCT__entry(
+ __field(int, gpu_unit)
+ __field(int, gpu_core)
+ __array(char, comm, TASK_COMM_LEN)
+ __field(pid_t, pid)
+ ),
+
+ TP_fast_assign(
+ __entry->gpu_unit = gpu_unit;
+ __entry->gpu_core = gpu_core;
+ memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
+ __entry->pid = p->pid;
+ ),
+
+ TP_printk("unit=%d core=%d comm=%s pid=%d",
+ __entry->gpu_unit, __entry->gpu_core, __entry->comm,
+ __entry->pid)
+ );
+
+/*
+ * Tracepoint for calling GPU unit stop activity on core
+ */
+TRACE_EVENT(gpu_activity_stop,
+
+ TP_PROTO(int gpu_unit, int gpu_core),
+
+ TP_ARGS(gpu_unit, gpu_core),
+
+ TP_STRUCT__entry(
+ __field(int, gpu_unit)
+ __field(int, gpu_core)
+ ),
+
+ TP_fast_assign(
+ __entry->gpu_unit = gpu_unit;
+ __entry->gpu_core = gpu_core;
+ ),
+
+ TP_printk("unit=%d core=%d", __entry->gpu_unit, __entry->gpu_core)
+ );
+
+#endif /* _TRACE_GPU_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/gator/gator_trace_power.c b/drivers/gator/gator_trace_power.c
new file mode 100644
index 000000000000..272e05684ee8
--- /dev/null
+++ b/drivers/gator/gator_trace_power.c
@@ -0,0 +1,203 @@
+/**
+ * Copyright (C) ARM Limited 2011-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/cpufreq.h>
+#include <trace/events/power.h>
+
+#if defined(__arm__)
+
+#include <asm/mach-types.h>
+
+#define implements_wfi() (!machine_is_omap3_beagle())
+
+#else
+
+#define implements_wfi() false
+
+#endif
+
+// cpu_frequency and cpu_idle trace points were introduced in Linux kernel v2.6.38
+// the now deprecated power_frequency trace point was available prior to 2.6.38, but only for x86
+#if GATOR_CPU_FREQ_SUPPORT
+enum {
+ POWER_CPU_FREQ,
+ POWER_CPU_IDLE,
+ POWER_TOTAL
+};
+
+static DEFINE_PER_CPU(ulong, idle_prev_state);
+static ulong power_cpu_enabled[POWER_TOTAL];
+static ulong power_cpu_key[POWER_TOTAL];
+
+static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int cpu;
+ bool found_nonzero_freq = false;
+
+ // Even if CONFIG_CPU_FREQ is defined, it still may not be used. Check
+ // for non-zero values from cpufreq_quick_get
+ for_each_online_cpu(cpu) {
+ if (cpufreq_quick_get(cpu) > 0) {
+ found_nonzero_freq = true;
+ break;
+ }
+ }
+
+ if (found_nonzero_freq) {
+ // cpu_frequency
+ dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_FREQ]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_FREQ]);
+ }
+
+ // cpu_idle
+ dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_idle");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_IDLE]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_IDLE]);
+
+ return 0;
+}
+
+// 'cpu' may not equal smp_processor_id(), i.e. may not be running on the core that is having the freq/idle state change
+GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu))
+{
+ cpu = lcpu_to_pcpu(cpu);
+ marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000);
+}
+
+GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu))
+{
+ cpu = lcpu_to_pcpu(cpu);
+
+ if (state == per_cpu(idle_prev_state, cpu)) {
+ return;
+ }
+
+ if (implements_wfi()) {
+ if (state == PWR_EVENT_EXIT) {
+ // transition from wfi to non-wfi
+ marshal_idle(cpu, MESSAGE_IDLE_EXIT);
+ } else {
+ // transition from non-wfi to wfi
+ marshal_idle(cpu, MESSAGE_IDLE_ENTER);
+ }
+ }
+
+ per_cpu(idle_prev_state, cpu) = state;
+
+ if (power_cpu_enabled[POWER_CPU_IDLE]) {
+ // Increment state so that no negative numbers are sent
+ marshal_event_single(cpu, power_cpu_key[POWER_CPU_IDLE], state + 1);
+ }
+}
+
+static void gator_trace_power_online(void)
+{
+ int pcpu = get_physical_cpu();
+ int lcpu = get_logical_cpu();
+ if (power_cpu_enabled[POWER_CPU_FREQ]) {
+ marshal_event_single(pcpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(lcpu) * 1000);
+ }
+}
+
+static void gator_trace_power_offline(void)
+{
+ // Set frequency to zero on an offline
+ int cpu = get_physical_cpu();
+ if (power_cpu_enabled[POWER_CPU_FREQ]) {
+ marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], 0);
+ }
+}
+
+static int gator_trace_power_start(void)
+{
+ int cpu;
+
+ // register tracepoints
+ if (power_cpu_enabled[POWER_CPU_FREQ])
+ if (GATOR_REGISTER_TRACE(cpu_frequency))
+ goto fail_cpu_frequency_exit;
+
+ // Always register for cpu:idle for detecting WFI, independent of power_cpu_enabled[POWER_CPU_IDLE]
+ if (GATOR_REGISTER_TRACE(cpu_idle))
+ goto fail_cpu_idle_exit;
+ pr_debug("gator: registered power event tracepoints\n");
+
+ for_each_present_cpu(cpu) {
+ per_cpu(idle_prev_state, cpu) = 0;
+ }
+
+ return 0;
+
+ // unregister tracepoints on error
+fail_cpu_idle_exit:
+ if (power_cpu_enabled[POWER_CPU_FREQ])
+ GATOR_UNREGISTER_TRACE(cpu_frequency);
+fail_cpu_frequency_exit:
+ pr_err("gator: power event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
+
+ return -1;
+}
+
+static void gator_trace_power_stop(void)
+{
+ int i;
+
+ if (power_cpu_enabled[POWER_CPU_FREQ])
+ GATOR_UNREGISTER_TRACE(cpu_frequency);
+ GATOR_UNREGISTER_TRACE(cpu_idle);
+ pr_debug("gator: unregistered power event tracepoints\n");
+
+ for (i = 0; i < POWER_TOTAL; i++) {
+ power_cpu_enabled[i] = 0;
+ }
+}
+
+void gator_trace_power_init(void)
+{
+ int i;
+ for (i = 0; i < POWER_TOTAL; i++) {
+ power_cpu_enabled[i] = 0;
+ power_cpu_key[i] = gator_events_get_key();
+ }
+}
+#else
+static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root)
+{
+ return 0;
+}
+
+static void gator_trace_power_online(void)
+{
+}
+
+static void gator_trace_power_offline(void)
+{
+}
+
+static int gator_trace_power_start(void)
+{
+ return 0;
+}
+
+static void gator_trace_power_stop(void)
+{
+}
+
+void gator_trace_power_init(void)
+{
+}
+#endif
diff --git a/drivers/gator/gator_trace_sched.c b/drivers/gator/gator_trace_sched.c
new file mode 100644
index 000000000000..e98815e25b9c
--- /dev/null
+++ b/drivers/gator/gator_trace_sched.c
@@ -0,0 +1,256 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <trace/events/sched.h>
+#include "gator.h"
+
+#define TASK_MAP_ENTRIES 1024 /* must be power of 2 */
+#define TASK_MAX_COLLISIONS 2
+
+enum {
+ STATE_WAIT_ON_OTHER = 0,
+ STATE_CONTENTION,
+ STATE_WAIT_ON_IO,
+ CPU_WAIT_TOTAL
+};
+
+static DEFINE_PER_CPU(uint64_t *, taskname_keys);
+static DEFINE_PER_CPU(int, collecting);
+
+// this array is never read as the cpu wait charts are derived counters
+// the files are needed, nonetheless, to show that these counters are available
+static ulong cpu_wait_enabled[CPU_WAIT_TOTAL];
+static ulong sched_cpu_key[CPU_WAIT_TOTAL];
+
+static int sched_trace_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+
+ // CPU Wait - Contention
+ dir = gatorfs_mkdir(sb, root, "Linux_cpu_wait_contention");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &cpu_wait_enabled[STATE_CONTENTION]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &sched_cpu_key[STATE_CONTENTION]);
+
+ // CPU Wait - I/O
+ dir = gatorfs_mkdir(sb, root, "Linux_cpu_wait_io");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &cpu_wait_enabled[STATE_WAIT_ON_IO]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &sched_cpu_key[STATE_WAIT_ON_IO]);
+
+ return 0;
+}
+
+void emit_pid_name(struct task_struct *task)
+{
+ bool found = false;
+ char taskcomm[TASK_COMM_LEN + 3];
+ unsigned long x, cpu = get_physical_cpu();
+ uint64_t *keys = &(per_cpu(taskname_keys, cpu)[(task->pid & 0xFF) * TASK_MAX_COLLISIONS]);
+ uint64_t value;
+
+ value = gator_chksum_crc32(task->comm);
+ value = (value << 32) | (uint32_t)task->pid;
+
+ // determine if the thread name was emitted already
+ for (x = 0; x < TASK_MAX_COLLISIONS; x++) {
+ if (keys[x] == value) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // shift values, new value always in front
+ uint64_t oldv, newv = value;
+ for (x = 0; x < TASK_MAX_COLLISIONS; x++) {
+ oldv = keys[x];
+ keys[x] = newv;
+ newv = oldv;
+ }
+
+ // emit pid names, cannot use get_task_comm, as it's not exported on all kernel versions
+ if (strlcpy(taskcomm, task->comm, TASK_COMM_LEN) == TASK_COMM_LEN - 1) {
+ // append ellipses if task->comm has length of TASK_COMM_LEN - 1
+ strcat(taskcomm, "...");
+ }
+
+ marshal_thread_name(task->pid, taskcomm);
+ }
+}
+
+static void collect_counters(u64 time)
+{
+ int *buffer, len, cpu = get_physical_cpu();
+ long long *buffer64;
+ struct gator_interface *gi;
+
+ if (marshal_event_header(time)) {
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->read) {
+ len = gi->read(&buffer);
+ marshal_event(len, buffer);
+ } else if (gi->read64) {
+ len = gi->read64(&buffer64);
+ marshal_event64(len, buffer64);
+ }
+ }
+ // Only check after writing all counters so that time and corresponding counters appear in the same frame
+ buffer_check(cpu, BLOCK_COUNTER_BUF, time);
+
+ // Commit buffers on timeout
+ if (gator_live_rate > 0 && time >= per_cpu(gator_buffer_commit_time, cpu)) {
+ static const int buftypes[] = { COUNTER_BUF, BLOCK_COUNTER_BUF, SCHED_TRACE_BUF };
+ int i;
+ for (i = 0; i < ARRAY_SIZE(buftypes); ++i) {
+ gator_commit_buffer(cpu, buftypes[i], time);
+ }
+ // Try to preemptively flush the annotate buffer to reduce the chance of the buffer being full
+ if (on_primary_core() && spin_trylock(&annotate_lock)) {
+ gator_commit_buffer(0, ANNOTATE_BUF, time);
+ spin_unlock(&annotate_lock);
+ }
+ }
+ }
+}
+
+// special case used during a suspend of the system
+static void trace_sched_insert_idle(void)
+{
+ marshal_sched_trace_switch(0, 0, 0, 0);
+}
+
+GATOR_DEFINE_PROBE(sched_process_fork, TP_PROTO(struct task_struct *parent, struct task_struct *child))
+{
+ int cookie;
+ int cpu = get_physical_cpu();
+
+ cookie = get_exec_cookie(cpu, child);
+ emit_pid_name(child);
+
+ marshal_sched_trace_start(child->tgid, child->pid, cookie);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next))
+#else
+GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
+#endif
+{
+ int cookie;
+ int state;
+ int cpu = get_physical_cpu();
+
+ // do as much work as possible before disabling interrupts
+ cookie = get_exec_cookie(cpu, next);
+ emit_pid_name(next);
+ if (prev->state == TASK_RUNNING) {
+ state = STATE_CONTENTION;
+ } else if (prev->in_iowait) {
+ state = STATE_WAIT_ON_IO;
+ } else {
+ state = STATE_WAIT_ON_OTHER;
+ }
+
+ per_cpu(collecting, cpu) = 1;
+ collect_counters(gator_get_time());
+ per_cpu(collecting, cpu) = 0;
+
+ marshal_sched_trace_switch(next->tgid, next->pid, cookie, state);
+}
+
+GATOR_DEFINE_PROBE(sched_process_free, TP_PROTO(struct task_struct *p))
+{
+ marshal_sched_trace_exit(p->tgid, p->pid);
+}
+
+static void do_nothing(void *info)
+{
+ // Intentionally do nothing
+ (void)info;
+}
+
+static int register_scheduler_tracepoints(void)
+{
+ // register tracepoints
+ if (GATOR_REGISTER_TRACE(sched_process_fork))
+ goto fail_sched_process_fork;
+ if (GATOR_REGISTER_TRACE(sched_switch))
+ goto fail_sched_switch;
+ if (GATOR_REGISTER_TRACE(sched_process_free))
+ goto fail_sched_process_free;
+ pr_debug("gator: registered tracepoints\n");
+
+ // Now that the scheduler tracepoint is registered, force a context switch
+ // on all cpus to capture what is currently running.
+ on_each_cpu(do_nothing, NULL, 0);
+
+ return 0;
+
+ // unregister tracepoints on error
+fail_sched_process_free:
+ GATOR_UNREGISTER_TRACE(sched_switch);
+fail_sched_switch:
+ GATOR_UNREGISTER_TRACE(sched_process_fork);
+fail_sched_process_fork:
+ pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
+
+ return -1;
+}
+
+int gator_trace_sched_start(void)
+{
+ int cpu, size;
+
+ for_each_present_cpu(cpu) {
+ size = TASK_MAP_ENTRIES * TASK_MAX_COLLISIONS * sizeof(uint64_t);
+ per_cpu(taskname_keys, cpu) = (uint64_t *)kmalloc(size, GFP_KERNEL);
+ if (!per_cpu(taskname_keys, cpu))
+ return -1;
+ memset(per_cpu(taskname_keys, cpu), 0, size);
+ }
+
+ return register_scheduler_tracepoints();
+}
+
+void gator_trace_sched_offline(void)
+{
+ trace_sched_insert_idle();
+}
+
+static void unregister_scheduler_tracepoints(void)
+{
+ GATOR_UNREGISTER_TRACE(sched_process_fork);
+ GATOR_UNREGISTER_TRACE(sched_switch);
+ GATOR_UNREGISTER_TRACE(sched_process_free);
+ pr_debug("gator: unregistered tracepoints\n");
+}
+
+void gator_trace_sched_stop(void)
+{
+ int cpu;
+ unregister_scheduler_tracepoints();
+
+ for_each_present_cpu(cpu) {
+ kfree(per_cpu(taskname_keys, cpu));
+ }
+}
+
+void gator_trace_sched_init(void)
+{
+ int i;
+ for (i = 0; i < CPU_WAIT_TOTAL; i++) {
+ cpu_wait_enabled[i] = 0;
+ sched_cpu_key[i] = gator_events_get_key();
+ }
+}
diff --git a/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h
new file mode 100644
index 000000000000..3db454371d59
--- /dev/null
+++ b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h
@@ -0,0 +1,164 @@
+/*
+ * This confidential and proprietary software may be used only as
+ * authorised by a licensing agreement from ARM Limited
+ * (C) COPYRIGHT 2013 ARM Limited
+ * ALL RIGHTS RESERVED
+ * The entire notice above must be reproduced on all authorised
+ * copies and copies may only be made to the extent permitted
+ * by a licensing agreement from ARM Limited.
+ */
+
+#ifndef __MALI_MJOLLNIR_PROFILING_GATOR_API_H__
+#define __MALI_MJOLLNIR_PROFILING_GATOR_API_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+/*
+ * The number of processor cores. Update to suit your hardware implementation.
+ */
+#define MAX_NUM_FP_CORES (4)
+#define MAX_NUM_VP_CORES (1)
+#define MAX_NUM_L2_CACHE_CORES (1)
+
+enum counters
+{
+ /* Timeline activity */
+ ACTIVITY_VP_0 = 0,
+ ACTIVITY_FP_0,
+ ACTIVITY_FP_1,
+ ACTIVITY_FP_2,
+ ACTIVITY_FP_3,
+
+ /* L2 cache counters */
+ COUNTER_L2_0_C0,
+ COUNTER_L2_0_C1,
+
+ /* Vertex processor counters */
+ COUNTER_VP_0_C0,
+ COUNTER_VP_0_C1,
+
+ /* Fragment processor counters */
+ COUNTER_FP_0_C0,
+ COUNTER_FP_0_C1,
+ COUNTER_FP_1_C0,
+ COUNTER_FP_1_C1,
+ COUNTER_FP_2_C0,
+ COUNTER_FP_2_C1,
+ COUNTER_FP_3_C0,
+ COUNTER_FP_3_C1,
+
+ /* EGL Software Counters */
+ COUNTER_EGL_BLIT_TIME,
+
+ /* GLES Software Counters */
+ COUNTER_GLES_DRAW_ELEMENTS_CALLS,
+ COUNTER_GLES_DRAW_ELEMENTS_NUM_INDICES,
+ COUNTER_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED,
+ COUNTER_GLES_DRAW_ARRAYS_CALLS,
+ COUNTER_GLES_DRAW_ARRAYS_NUM_TRANSFORMED,
+ COUNTER_GLES_DRAW_POINTS,
+ COUNTER_GLES_DRAW_LINES,
+ COUNTER_GLES_DRAW_LINE_LOOP,
+ COUNTER_GLES_DRAW_LINE_STRIP,
+ COUNTER_GLES_DRAW_TRIANGLES,
+ COUNTER_GLES_DRAW_TRIANGLE_STRIP,
+ COUNTER_GLES_DRAW_TRIANGLE_FAN,
+ COUNTER_GLES_NON_VBO_DATA_COPY_TIME,
+ COUNTER_GLES_UNIFORM_BYTES_COPIED_TO_MALI,
+ COUNTER_GLES_UPLOAD_TEXTURE_TIME,
+ COUNTER_GLES_UPLOAD_VBO_TIME,
+ COUNTER_GLES_NUM_FLUSHES,
+ COUNTER_GLES_NUM_VSHADERS_GENERATED,
+ COUNTER_GLES_NUM_FSHADERS_GENERATED,
+ COUNTER_GLES_VSHADER_GEN_TIME,
+ COUNTER_GLES_FSHADER_GEN_TIME,
+ COUNTER_GLES_INPUT_TRIANGLES,
+ COUNTER_GLES_VXCACHE_HIT,
+ COUNTER_GLES_VXCACHE_MISS,
+ COUNTER_GLES_VXCACHE_COLLISION,
+ COUNTER_GLES_CULLED_TRIANGLES,
+ COUNTER_GLES_CULLED_LINES,
+ COUNTER_GLES_BACKFACE_TRIANGLES,
+ COUNTER_GLES_GBCLIP_TRIANGLES,
+ COUNTER_GLES_GBCLIP_LINES,
+ COUNTER_GLES_TRIANGLES_DRAWN,
+ COUNTER_GLES_DRAWCALL_TIME,
+ COUNTER_GLES_TRIANGLES_COUNT,
+ COUNTER_GLES_INDEPENDENT_TRIANGLES_COUNT,
+ COUNTER_GLES_STRIP_TRIANGLES_COUNT,
+ COUNTER_GLES_FAN_TRIANGLES_COUNT,
+ COUNTER_GLES_LINES_COUNT,
+ COUNTER_GLES_INDEPENDENT_LINES_COUNT,
+ COUNTER_GLES_STRIP_LINES_COUNT,
+ COUNTER_GLES_LOOP_LINES_COUNT,
+
+ COUNTER_FILMSTRIP,
+ COUNTER_FREQUENCY,
+ COUNTER_VOLTAGE,
+
+ NUMBER_OF_EVENTS
+};
+
+#define FIRST_ACTIVITY_EVENT ACTIVITY_VP_0
+#define LAST_ACTIVITY_EVENT ACTIVITY_FP_3
+
+#define FIRST_HW_COUNTER COUNTER_L2_0_C0
+#define LAST_HW_COUNTER COUNTER_FP_3_C1
+
+#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME
+#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT
+
+/* Signifies that the system is able to report voltage and frequency numbers. */
+#define DVFS_REPORTED_BY_DDK 1
+
+/**
+ * Structure to pass performance counter data of a Mali core
+ */
+typedef struct _mali_profiling_core_counters
+{
+ u32 source0;
+ u32 value0;
+ u32 source1;
+ u32 value1;
+} _mali_profiling_core_counters;
+
+/*
+ * For compatibility with utgard.
+ */
+typedef struct _mali_profiling_l2_counter_values
+{
+ struct _mali_profiling_core_counters cores[MAX_NUM_L2_CACHE_CORES];
+} _mali_profiling_l2_counter_values;
+
+typedef struct _mali_profiling_mali_version
+{
+ u32 mali_product_id;
+ u32 mali_version_major;
+ u32 mali_version_minor;
+ u32 num_of_l2_cores;
+ u32 num_of_fp_cores;
+ u32 num_of_vp_cores;
+} _mali_profiling_mali_version;
+
+extern void _mali_profiling_get_mali_version(struct _mali_profiling_mali_version *values);
+extern u32 _mali_profiling_get_l2_counters(_mali_profiling_l2_counter_values *values);
+
+/*
+ * List of possible actions allowing DDK to be controlled by Streamline.
+ * The following numbers are used by DDK to control the frame buffer dumping.
+ */
+#define FBDUMP_CONTROL_ENABLE (1)
+#define FBDUMP_CONTROL_RATE (2)
+#define SW_COUNTER_ENABLE (3)
+#define FBDUMP_CONTROL_RESIZE_FACTOR (4)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MALI_MJOLLNIR_PROFILING_GATOR_API_H__ */
diff --git a/drivers/gator/mali/mali_utgard_profiling_gator_api.h b/drivers/gator/mali/mali_utgard_profiling_gator_api.h
new file mode 100644
index 000000000000..c02a1a43efff
--- /dev/null
+++ b/drivers/gator/mali/mali_utgard_profiling_gator_api.h
@@ -0,0 +1,202 @@
+/*
+ * This confidential and proprietary software may be used only as
+ * authorised by a licensing agreement from ARM Limited
+ * (C) COPYRIGHT 2013 ARM Limited
+ * ALL RIGHTS RESERVED
+ * The entire notice above must be reproduced on all authorised
+ * copies and copies may only be made to the extent permitted
+ * by a licensing agreement from ARM Limited.
+ */
+
+#ifndef __MALI_UTGARD_PROFILING_GATOR_API_H__
+#define __MALI_UTGARD_PROFILING_GATOR_API_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define MALI_PROFILING_API_VERSION 4
+
+#define MAX_NUM_L2_CACHE_CORES 3
+#define MAX_NUM_FP_CORES 8
+#define MAX_NUM_VP_CORES 1
+
+/** The list of events supported by the Mali DDK. */
+typedef enum
+{
+ /* Vertex processor activity */
+ ACTIVITY_VP_0 = 0,
+
+ /* Fragment processor activity */
+ ACTIVITY_FP_0, /* 1 */
+ ACTIVITY_FP_1,
+ ACTIVITY_FP_2,
+ ACTIVITY_FP_3,
+ ACTIVITY_FP_4,
+ ACTIVITY_FP_5,
+ ACTIVITY_FP_6,
+ ACTIVITY_FP_7,
+
+ /* L2 cache counters */
+ COUNTER_L2_0_C0,
+ COUNTER_L2_0_C1,
+ COUNTER_L2_1_C0,
+ COUNTER_L2_1_C1,
+ COUNTER_L2_2_C0,
+ COUNTER_L2_2_C1,
+
+ /* Vertex processor counters */
+ COUNTER_VP_0_C0, /*15*/
+ COUNTER_VP_0_C1,
+
+ /* Fragment processor counters */
+ COUNTER_FP_0_C0,
+ COUNTER_FP_0_C1,
+ COUNTER_FP_1_C0,
+ COUNTER_FP_1_C1,
+ COUNTER_FP_2_C0,
+ COUNTER_FP_2_C1,
+ COUNTER_FP_3_C0,
+ COUNTER_FP_3_C1,
+ COUNTER_FP_4_C0,
+ COUNTER_FP_4_C1,
+ COUNTER_FP_5_C0,
+ COUNTER_FP_5_C1,
+ COUNTER_FP_6_C0,
+ COUNTER_FP_6_C1,
+ COUNTER_FP_7_C0,
+ COUNTER_FP_7_C1, /* 32 */
+
+ /*
+ * If more hardware counters are added, the _mali_osk_hw_counter_table
+ * below should also be updated.
+ */
+
+ /* EGL software counters */
+ COUNTER_EGL_BLIT_TIME,
+
+ /* GLES software counters */
+ COUNTER_GLES_DRAW_ELEMENTS_CALLS,
+ COUNTER_GLES_DRAW_ELEMENTS_NUM_INDICES,
+ COUNTER_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED,
+ COUNTER_GLES_DRAW_ARRAYS_CALLS,
+ COUNTER_GLES_DRAW_ARRAYS_NUM_TRANSFORMED,
+ COUNTER_GLES_DRAW_POINTS,
+ COUNTER_GLES_DRAW_LINES,
+ COUNTER_GLES_DRAW_LINE_LOOP,
+ COUNTER_GLES_DRAW_LINE_STRIP,
+ COUNTER_GLES_DRAW_TRIANGLES,
+ COUNTER_GLES_DRAW_TRIANGLE_STRIP,
+ COUNTER_GLES_DRAW_TRIANGLE_FAN,
+ COUNTER_GLES_NON_VBO_DATA_COPY_TIME,
+ COUNTER_GLES_UNIFORM_BYTES_COPIED_TO_MALI,
+ COUNTER_GLES_UPLOAD_TEXTURE_TIME,
+ COUNTER_GLES_UPLOAD_VBO_TIME,
+ COUNTER_GLES_NUM_FLUSHES,
+ COUNTER_GLES_NUM_VSHADERS_GENERATED,
+ COUNTER_GLES_NUM_FSHADERS_GENERATED,
+ COUNTER_GLES_VSHADER_GEN_TIME,
+ COUNTER_GLES_FSHADER_GEN_TIME,
+ COUNTER_GLES_INPUT_TRIANGLES,
+ COUNTER_GLES_VXCACHE_HIT,
+ COUNTER_GLES_VXCACHE_MISS,
+ COUNTER_GLES_VXCACHE_COLLISION,
+ COUNTER_GLES_CULLED_TRIANGLES,
+ COUNTER_GLES_CULLED_LINES,
+ COUNTER_GLES_BACKFACE_TRIANGLES,
+ COUNTER_GLES_GBCLIP_TRIANGLES,
+ COUNTER_GLES_GBCLIP_LINES,
+ COUNTER_GLES_TRIANGLES_DRAWN,
+ COUNTER_GLES_DRAWCALL_TIME,
+ COUNTER_GLES_TRIANGLES_COUNT,
+ COUNTER_GLES_INDEPENDENT_TRIANGLES_COUNT,
+ COUNTER_GLES_STRIP_TRIANGLES_COUNT,
+ COUNTER_GLES_FAN_TRIANGLES_COUNT,
+ COUNTER_GLES_LINES_COUNT,
+ COUNTER_GLES_INDEPENDENT_LINES_COUNT,
+ COUNTER_GLES_STRIP_LINES_COUNT,
+ COUNTER_GLES_LOOP_LINES_COUNT,
+
+ /* Framebuffer capture pseudo-counter */
+ COUNTER_FILMSTRIP,
+
+ NUMBER_OF_EVENTS
+} _mali_osk_counter_id;
+
+#define FIRST_ACTIVITY_EVENT ACTIVITY_VP_0
+#define LAST_ACTIVITY_EVENT ACTIVITY_FP_7
+
+#define FIRST_HW_COUNTER COUNTER_L2_0_C0
+#define LAST_HW_COUNTER COUNTER_FP_7_C1
+
+#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME
+#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT
+
+#define FIRST_SPECIAL_COUNTER COUNTER_FILMSTRIP
+#define LAST_SPECIAL_COUNTER COUNTER_FILMSTRIP
+
+/**
+ * Structure to pass performance counter data of a Mali core
+ */
+typedef struct _mali_profiling_core_counters
+{
+ u32 source0;
+ u32 value0;
+ u32 source1;
+ u32 value1;
+} _mali_profiling_core_counters;
+
+/**
+ * Structure to pass performance counter data of Mali L2 cache cores
+ */
+typedef struct _mali_profiling_l2_counter_values
+{
+ struct _mali_profiling_core_counters cores[MAX_NUM_L2_CACHE_CORES];
+} _mali_profiling_l2_counter_values;
+
+/**
+ * Structure to pass data defining Mali instance in use:
+ *
+ * mali_product_id - Mali product id
+ * mali_version_major - Mali version major number
+ * mali_version_minor - Mali version minor number
+ * num_of_l2_cores - number of L2 cache cores
+ * num_of_fp_cores - number of fragment processor cores
+ * num_of_vp_cores - number of vertex processor cores
+ */
+typedef struct _mali_profiling_mali_version
+{
+ u32 mali_product_id;
+ u32 mali_version_major;
+ u32 mali_version_minor;
+ u32 num_of_l2_cores;
+ u32 num_of_fp_cores;
+ u32 num_of_vp_cores;
+} _mali_profiling_mali_version;
+
+/*
+ * List of possible actions to be controlled by Streamline.
+ * The following numbers are used by gator to control the frame buffer dumping and s/w counter reporting.
+ * We cannot use the enums in mali_uk_types.h because they are unknown inside gator.
+ */
+#define FBDUMP_CONTROL_ENABLE (1)
+#define FBDUMP_CONTROL_RATE (2)
+#define SW_COUNTER_ENABLE (3)
+#define FBDUMP_CONTROL_RESIZE_FACTOR (4)
+
+void _mali_profiling_control(u32 action, u32 value);
+
+u32 _mali_profiling_get_l2_counters(_mali_profiling_l2_counter_values *values);
+
+int _mali_profiling_set_event(u32 counter_id, s32 event_id);
+
+u32 _mali_profiling_get_api_version(void);
+
+void _mali_profiling_get_mali_version(struct _mali_profiling_mali_version *values);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MALI_UTGARD_PROFILING_GATOR_API_H__ */
diff --git a/drivers/gator/mali_t6xx.mk b/drivers/gator/mali_t6xx.mk
new file mode 100644
index 000000000000..2cc64113ea2c
--- /dev/null
+++ b/drivers/gator/mali_t6xx.mk
@@ -0,0 +1,25 @@
+# Defines for Mali-T6xx driver
+EXTRA_CFLAGS += -DMALI_USE_UMP=1 \
+ -DMALI_LICENSE_IS_GPL=1 \
+ -DMALI_BASE_TRACK_MEMLEAK=0 \
+ -DMALI_DEBUG=0 \
+ -DMALI_ERROR_INJECT_ON=0 \
+ -DMALI_CUSTOMER_RELEASE=1 \
+ -DMALI_UNIT_TEST=0 \
+ -DMALI_BACKEND_KERNEL=1 \
+ -DMALI_NO_MALI=0
+
+KBASE_DIR = $(DDK_DIR)/kernel/drivers/gpu/arm/t6xx/kbase
+OSK_DIR = $(DDK_DIR)/kernel/drivers/gpu/arm/t6xx/kbase/osk
+UMP_DIR = $(DDK_DIR)/kernel/include/linux
+
+# Include directories in the DDK
+EXTRA_CFLAGS += -I$(DDK_DIR) \
+ -I$(KBASE_DIR)/.. \
+ -I$(OSK_DIR)/.. \
+ -I$(UMP_DIR)/.. \
+ -I$(DDK_DIR)/kernel/include \
+ -I$(KBASE_DIR)/osk/src/linux/include \
+ -I$(KBASE_DIR)/platform_dummy \
+ -I$(KBASE_DIR)/src
+
diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c
index 86c17de87692..71d86143aec5 100644
--- a/drivers/gpio/gpio-lynxpoint.c
+++ b/drivers/gpio/gpio-lynxpoint.c
@@ -248,14 +248,15 @@ static void lp_gpio_irq_handler(unsigned irq, struct irq_desc *desc)
struct lp_gpio *lg = irq_data_get_irq_handler_data(data);
struct irq_chip *chip = irq_data_get_irq_chip(data);
u32 base, pin, mask;
- unsigned long reg, pending;
+ unsigned long reg, ena, pending;
unsigned virq;
/* check from GPIO controller which pin triggered the interrupt */
for (base = 0; base < lg->chip.ngpio; base += 32) {
reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT);
+ ena = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE);
- while ((pending = inl(reg))) {
+ while ((pending = (inl(reg) & inl(ena)))) {
pin = __ffs(pending);
mask = BIT(pin);
/* Clear before handling so we don't lose an edge */
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 4a430360af5a..54052125859a 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -63,6 +63,7 @@ struct gpio_bank {
struct gpio_chip chip;
struct clk *dbck;
u32 mod_usage;
+ u32 irq_usage;
u32 dbck_enable_mask;
bool dbck_enabled;
struct device *dev;
@@ -86,6 +87,9 @@ struct gpio_bank {
#define GPIO_BIT(bank, gpio) (1 << GPIO_INDEX(bank, gpio))
#define GPIO_MOD_CTRL_BIT BIT(0)
+#define BANK_USED(bank) (bank->mod_usage || bank->irq_usage)
+#define LINE_USED(line, offset) (line & (1 << offset))
+
static int irq_to_gpio(struct gpio_bank *bank, unsigned int gpio_irq)
{
return bank->chip.base + gpio_irq;
@@ -420,15 +424,69 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio,
return 0;
}
+static void _enable_gpio_module(struct gpio_bank *bank, unsigned offset)
+{
+ if (bank->regs->pinctrl) {
+ void __iomem *reg = bank->base + bank->regs->pinctrl;
+
+ /* Claim the pin for MPU */
+ __raw_writel(__raw_readl(reg) | (1 << offset), reg);
+ }
+
+ if (bank->regs->ctrl && !BANK_USED(bank)) {
+ void __iomem *reg = bank->base + bank->regs->ctrl;
+ u32 ctrl;
+
+ ctrl = __raw_readl(reg);
+ /* Module is enabled, clocks are not gated */
+ ctrl &= ~GPIO_MOD_CTRL_BIT;
+ __raw_writel(ctrl, reg);
+ bank->context.ctrl = ctrl;
+ }
+}
+
+static void _disable_gpio_module(struct gpio_bank *bank, unsigned offset)
+{
+ void __iomem *base = bank->base;
+
+ if (bank->regs->wkup_en &&
+ !LINE_USED(bank->mod_usage, offset) &&
+ !LINE_USED(bank->irq_usage, offset)) {
+ /* Disable wake-up during idle for dynamic tick */
+ _gpio_rmw(base, bank->regs->wkup_en, 1 << offset, 0);
+ bank->context.wake_en =
+ __raw_readl(bank->base + bank->regs->wkup_en);
+ }
+
+ if (bank->regs->ctrl && !BANK_USED(bank)) {
+ void __iomem *reg = bank->base + bank->regs->ctrl;
+ u32 ctrl;
+
+ ctrl = __raw_readl(reg);
+ /* Module is disabled, clocks are gated */
+ ctrl |= GPIO_MOD_CTRL_BIT;
+ __raw_writel(ctrl, reg);
+ bank->context.ctrl = ctrl;
+ }
+}
+
+static int gpio_is_input(struct gpio_bank *bank, int mask)
+{
+ void __iomem *reg = bank->base + bank->regs->direction;
+
+ return __raw_readl(reg) & mask;
+}
+
static int gpio_irq_type(struct irq_data *d, unsigned type)
{
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
unsigned gpio = 0;
int retval;
unsigned long flags;
+ unsigned offset;
- if (WARN_ON(!bank->mod_usage))
- return -EINVAL;
+ if (!BANK_USED(bank))
+ pm_runtime_get_sync(bank->dev);
#ifdef CONFIG_ARCH_OMAP1
if (d->irq > IH_MPUIO_BASE)
@@ -446,7 +504,17 @@ static int gpio_irq_type(struct irq_data *d, unsigned type)
return -EINVAL;
spin_lock_irqsave(&bank->lock, flags);
- retval = _set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), type);
+ offset = GPIO_INDEX(bank, gpio);
+ retval = _set_gpio_triggering(bank, offset, type);
+ if (!LINE_USED(bank->mod_usage, offset)) {
+ _enable_gpio_module(bank, offset);
+ _set_gpio_direction(bank, offset, 1);
+ } else if (!gpio_is_input(bank, 1 << offset)) {
+ spin_unlock_irqrestore(&bank->lock, flags);
+ return -EINVAL;
+ }
+
+ bank->irq_usage |= 1 << GPIO_INDEX(bank, gpio);
spin_unlock_irqrestore(&bank->lock, flags);
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
@@ -603,35 +671,19 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
* If this is the first gpio_request for the bank,
* enable the bank module.
*/
- if (!bank->mod_usage)
+ if (!BANK_USED(bank))
pm_runtime_get_sync(bank->dev);
spin_lock_irqsave(&bank->lock, flags);
/* Set trigger to none. You need to enable the desired trigger with
- * request_irq() or set_irq_type().
+ * request_irq() or set_irq_type(). Only do this if the IRQ line has
+ * not already been requested.
*/
- _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
-
- if (bank->regs->pinctrl) {
- void __iomem *reg = bank->base + bank->regs->pinctrl;
-
- /* Claim the pin for MPU */
- __raw_writel(__raw_readl(reg) | (1 << offset), reg);
- }
-
- if (bank->regs->ctrl && !bank->mod_usage) {
- void __iomem *reg = bank->base + bank->regs->ctrl;
- u32 ctrl;
-
- ctrl = __raw_readl(reg);
- /* Module is enabled, clocks are not gated */
- ctrl &= ~GPIO_MOD_CTRL_BIT;
- __raw_writel(ctrl, reg);
- bank->context.ctrl = ctrl;
+ if (!LINE_USED(bank->irq_usage, offset)) {
+ _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
+ _enable_gpio_module(bank, offset);
}
-
bank->mod_usage |= 1 << offset;
-
spin_unlock_irqrestore(&bank->lock, flags);
return 0;
@@ -640,31 +692,11 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
- void __iomem *base = bank->base;
unsigned long flags;
spin_lock_irqsave(&bank->lock, flags);
-
- if (bank->regs->wkup_en) {
- /* Disable wake-up during idle for dynamic tick */
- _gpio_rmw(base, bank->regs->wkup_en, 1 << offset, 0);
- bank->context.wake_en =
- __raw_readl(bank->base + bank->regs->wkup_en);
- }
-
bank->mod_usage &= ~(1 << offset);
-
- if (bank->regs->ctrl && !bank->mod_usage) {
- void __iomem *reg = bank->base + bank->regs->ctrl;
- u32 ctrl;
-
- ctrl = __raw_readl(reg);
- /* Module is disabled, clocks are gated */
- ctrl |= GPIO_MOD_CTRL_BIT;
- __raw_writel(ctrl, reg);
- bank->context.ctrl = ctrl;
- }
-
+ _disable_gpio_module(bank, offset);
_reset_gpio(bank, bank->chip.base + offset);
spin_unlock_irqrestore(&bank->lock, flags);
@@ -672,7 +704,7 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
* If this is the last gpio to be freed in the bank,
* disable the bank module.
*/
- if (!bank->mod_usage)
+ if (!BANK_USED(bank))
pm_runtime_put(bank->dev);
}
@@ -762,10 +794,20 @@ static void gpio_irq_shutdown(struct irq_data *d)
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
unsigned int gpio = irq_to_gpio(bank, d->hwirq);
unsigned long flags;
+ unsigned offset = GPIO_INDEX(bank, gpio);
spin_lock_irqsave(&bank->lock, flags);
+ bank->irq_usage &= ~(1 << offset);
+ _disable_gpio_module(bank, offset);
_reset_gpio(bank, gpio);
spin_unlock_irqrestore(&bank->lock, flags);
+
+ /*
+ * If this is the last IRQ to be freed in the bank,
+ * disable the bank module.
+ */
+ if (!BANK_USED(bank))
+ pm_runtime_put(bank->dev);
}
static void gpio_ack_irq(struct irq_data *d)
@@ -897,13 +939,6 @@ static int gpio_input(struct gpio_chip *chip, unsigned offset)
return 0;
}
-static int gpio_is_input(struct gpio_bank *bank, int mask)
-{
- void __iomem *reg = bank->base + bank->regs->direction;
-
- return __raw_readl(reg) & mask;
-}
-
static int gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank;
@@ -922,13 +957,22 @@ static int gpio_output(struct gpio_chip *chip, unsigned offset, int value)
{
struct gpio_bank *bank;
unsigned long flags;
+ int retval = 0;
bank = container_of(chip, struct gpio_bank, chip);
spin_lock_irqsave(&bank->lock, flags);
+
+ if (LINE_USED(bank->irq_usage, offset)) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
bank->set_dataout(bank, offset, value);
_set_gpio_direction(bank, offset, 0);
+
+exit:
spin_unlock_irqrestore(&bank->lock, flags);
- return 0;
+ return retval;
}
static int gpio_debounce(struct gpio_chip *chip, unsigned offset,
@@ -1400,7 +1444,7 @@ void omap2_gpio_prepare_for_idle(int pwr_mode)
struct gpio_bank *bank;
list_for_each_entry(bank, &omap_gpio_list, node) {
- if (!bank->mod_usage || !bank->loses_context)
+ if (!BANK_USED(bank) || !bank->loses_context)
continue;
bank->power_mode = pwr_mode;
@@ -1414,7 +1458,7 @@ void omap2_gpio_resume_after_idle(void)
struct gpio_bank *bank;
list_for_each_entry(bank, &omap_gpio_list, node) {
- if (!bank->mod_usage || !bank->loses_context)
+ if (!BANK_USED(bank) || !bank->loses_context)
continue;
pm_runtime_get_sync(bank->dev);
diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h
index 02e52d543e4b..b6b7d70f2832 100644
--- a/drivers/gpu/drm/ast/ast_drv.h
+++ b/drivers/gpu/drm/ast/ast_drv.h
@@ -177,7 +177,7 @@ uint8_t ast_get_index_reg_mask(struct ast_private *ast,
static inline void ast_open_key(struct ast_private *ast)
{
- ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xA1, 0xFF, 0x04);
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x80, 0xA8);
}
#define AST_VIDMEM_SIZE_8M 0x00800000
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index e7e92429d10f..8759d699bd8e 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -2501,10 +2501,22 @@ int drm_mode_getfb(struct drm_device *dev,
r->depth = fb->depth;
r->bpp = fb->bits_per_pixel;
r->pitch = fb->pitches[0];
- if (fb->funcs->create_handle)
- ret = fb->funcs->create_handle(fb, file_priv, &r->handle);
- else
+ if (fb->funcs->create_handle) {
+ if (file_priv->is_master || capable(CAP_SYS_ADMIN)) {
+ ret = fb->funcs->create_handle(fb, file_priv,
+ &r->handle);
+ } else {
+ /* GET_FB() is an unprivileged ioctl so we must not
+ * return a buffer-handle to non-master processes! For
+ * backwards-compatibility reasons, we cannot make
+ * GET_FB() privileged, so just return an invalid handle
+ * for non-masters. */
+ r->handle = 0;
+ ret = 0;
+ }
+ } else {
ret = -ENODEV;
+ }
drm_framebuffer_unreference(fb);
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 9cc247f55502..2ab782cb38a2 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -406,9 +406,16 @@ long drm_ioctl(struct file *filp,
cmd = ioctl->cmd_drv;
}
else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) {
+ u32 drv_size;
+
ioctl = &drm_ioctls[nr];
- cmd = ioctl->cmd;
+
+ drv_size = _IOC_SIZE(ioctl->cmd);
usize = asize = _IOC_SIZE(cmd);
+ if (drv_size > asize)
+ asize = drv_size;
+
+ cmd = ioctl->cmd;
} else
goto err_i1;
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 9e62bbedb5ad..0cb9b5d8e30a 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -125,6 +125,9 @@ static struct edid_quirk {
/* ViewSonic VA2026w */
{ "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING },
+
+ /* Medion MD 30217 PG */
+ { "MED", 0x7b8, EDID_QUIRK_PREFER_LARGE_75 },
};
/*
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index e5e328691e3a..c8d16a622f3c 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1009,6 +1009,34 @@ done:
return ret;
}
+static void i915_error_wake_up(struct drm_i915_private *dev_priv,
+ bool reset_completed)
+{
+ struct intel_ring_buffer *ring;
+ int i;
+
+ /*
+ * Notify all waiters for GPU completion events that reset state has
+ * been changed, and that they need to restart their wait after
+ * checking for potential errors (and bail out to drop locks if there is
+ * a gpu reset pending so that i915_error_work_func can acquire them).
+ */
+
+ /* Wake up __wait_seqno, potentially holding dev->struct_mutex. */
+ for_each_ring(ring, dev_priv, i)
+ wake_up_all(&ring->irq_queue);
+
+ /* Wake up intel_crtc_wait_for_pending_flips, holding crtc->mutex. */
+ wake_up_all(&dev_priv->pending_flip_queue);
+
+ /*
+ * Signal tasks blocked in i915_gem_wait_for_error that the pending
+ * reset state is cleared.
+ */
+ if (reset_completed)
+ wake_up_all(&dev_priv->gpu_error.reset_queue);
+}
+
/**
* i915_error_work_func - do process context error handling work
* @work: work struct
@@ -1023,11 +1051,10 @@ static void i915_error_work_func(struct work_struct *work)
drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t,
gpu_error);
struct drm_device *dev = dev_priv->dev;
- struct intel_ring_buffer *ring;
char *error_event[] = { "ERROR=1", NULL };
char *reset_event[] = { "RESET=1", NULL };
char *reset_done_event[] = { "ERROR=0", NULL };
- int i, ret;
+ int ret;
kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
@@ -1046,8 +1073,16 @@ static void i915_error_work_func(struct work_struct *work)
kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE,
reset_event);
+ /*
+ * All state reset _must_ be completed before we update the
+ * reset counter, for otherwise waiters might miss the reset
+ * pending state and not properly drop locks, resulting in
+ * deadlocks with the reset work.
+ */
ret = i915_reset(dev);
+ intel_display_handle_reset(dev);
+
if (ret == 0) {
/*
* After all the gem state is reset, increment the reset
@@ -1068,12 +1103,11 @@ static void i915_error_work_func(struct work_struct *work)
atomic_set(&error->reset_counter, I915_WEDGED);
}
- for_each_ring(ring, dev_priv, i)
- wake_up_all(&ring->irq_queue);
-
- intel_display_handle_reset(dev);
-
- wake_up_all(&dev_priv->gpu_error.reset_queue);
+ /*
+ * Note: The wake_up also serves as a memory barrier so that
+ * waiters see the update value of the reset counter atomic_t.
+ */
+ i915_error_wake_up(dev_priv, true);
}
}
@@ -1709,8 +1743,6 @@ static void i915_report_and_clear_eir(struct drm_device *dev)
void i915_handle_error(struct drm_device *dev, bool wedged)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
- int i;
i915_capture_error_state(dev);
i915_report_and_clear_eir(dev);
@@ -1720,14 +1752,28 @@ void i915_handle_error(struct drm_device *dev, bool wedged)
&dev_priv->gpu_error.reset_counter);
/*
- * Wakeup waiting processes so that the reset work item
- * doesn't deadlock trying to grab various locks.
+ * Wakeup waiting processes so that the reset work function
+ * i915_error_work_func doesn't deadlock trying to grab various
+ * locks. By bumping the reset counter first, the woken
+ * processes will see a reset in progress and back off,
+ * releasing their locks and then wait for the reset completion.
+ * We must do this for _all_ gpu waiters that might hold locks
+ * that the reset work needs to acquire.
+ *
+ * Note: The wake_up serves as the required memory barrier to
+ * ensure that the waiters see the updated value of the reset
+ * counter atomic_t.
*/
- for_each_ring(ring, dev_priv, i)
- wake_up_all(&ring->irq_queue);
+ i915_error_wake_up(dev_priv, false);
}
- queue_work(dev_priv->wq, &dev_priv->gpu_error.work);
+ /*
+ * Our reset work can grab modeset locks (since it needs to reset the
+ * state of outstanding pagelips). Hence it must not be run on our own
+ * dev-priv->wq work queue for otherwise the flush_work in the pageflip
+ * code will deadlock.
+ */
+ schedule_work(&dev_priv->gpu_error.work);
}
static void __always_unused i915_pageflip_stall_check(struct drm_device *dev, int pipe)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index a365780aeb1e..b1a0cdba59e7 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1675,10 +1675,16 @@
#define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2)
#define PORT_HOTPLUG_STAT (dev_priv->info->display_mmio_offset + 0x61114)
-/* HDMI/DP bits are gen4+ */
-#define PORTB_HOTPLUG_LIVE_STATUS (1 << 29)
+/*
+ * HDMI/DP bits are gen4+
+ *
+ * WARNING: Bspec for hpd status bits on gen4 seems to be completely confused.
+ * Please check the detailed lore in the commit message for for experimental
+ * evidence.
+ */
+#define PORTD_HOTPLUG_LIVE_STATUS (1 << 29)
#define PORTC_HOTPLUG_LIVE_STATUS (1 << 28)
-#define PORTD_HOTPLUG_LIVE_STATUS (1 << 27)
+#define PORTB_HOTPLUG_LIVE_STATUS (1 << 27)
#define PORTD_HOTPLUG_INT_STATUS (3 << 21)
#define PORTC_HOTPLUG_INT_STATUS (3 << 19)
#define PORTB_HOTPLUG_INT_STATUS (3 << 17)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index eea5982657a8..ab952595e088 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3946,8 +3946,6 @@ static void intel_connector_check_state(struct intel_connector *connector)
* consider. */
void intel_connector_dpms(struct drm_connector *connector, int mode)
{
- struct intel_encoder *encoder = intel_attached_encoder(connector);
-
/* All the simple cases only support two dpms states. */
if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
@@ -3958,10 +3956,8 @@ void intel_connector_dpms(struct drm_connector *connector, int mode)
connector->dpms = mode;
/* Only need to change hw state when actually enabled */
- if (encoder->base.crtc)
- intel_encoder_dpms(encoder, mode);
- else
- WARN_ON(encoder->connectors_active != false);
+ if (connector->encoder)
+ intel_encoder_dpms(to_intel_encoder(connector->encoder), mode);
intel_modeset_check_state(connector->dev);
}
@@ -4564,6 +4560,10 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc)
pipeconf = I915_READ(PIPECONF(intel_crtc->pipe));
+ if (dev_priv->quirks & QUIRK_PIPEA_FORCE &&
+ I915_READ(PIPECONF(intel_crtc->pipe)) & PIPECONF_ENABLE)
+ pipeconf |= PIPECONF_ENABLE;
+
if (intel_crtc->pipe == 0 && INTEL_INFO(dev)->gen < 4) {
/* Enable pixel doubling when the dot clock is > 90% of the (display)
* core speed.
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 70789b1b5642..80feaec88d2b 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -604,7 +604,18 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
DRM_DEBUG_KMS("aux_ch native nack\n");
return -EREMOTEIO;
case AUX_NATIVE_REPLY_DEFER:
- udelay(100);
+ /*
+ * For now, just give more slack to branch devices. We
+ * could check the DPCD for I2C bit rate capabilities,
+ * and if available, adjust the interval. We could also
+ * be more careful with DP-to-Legacy adapters where a
+ * long legacy cable may force very low I2C bit rates.
+ */
+ if (intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+ DP_DWN_STRM_PORT_PRESENT)
+ usleep_range(500, 600);
+ else
+ usleep_range(300, 400);
continue;
default:
DRM_ERROR("aux_ch invalid native reply 0x%02x\n",
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index b945bc54207a..a202d8d08c56 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -921,6 +921,14 @@ intel_tv_compute_config(struct intel_encoder *encoder,
DRM_DEBUG_KMS("forcing bpc to 8 for TV\n");
pipe_config->pipe_bpp = 8*3;
+ /* TV has it's own notion of sync and other mode flags, so clear them. */
+ pipe_config->adjusted_mode.flags = 0;
+
+ /*
+ * FIXME: We don't check whether the input mode is actually what we want
+ * or whether userspace is doing something stupid.
+ */
+
return true;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c
index f02fd9f443ff..a66b27c0fcab 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c
@@ -49,18 +49,23 @@ int
nv50_dac_sense(struct nv50_disp_priv *priv, int or, u32 loadval)
{
const u32 doff = (or * 0x800);
- int load = -EINVAL;
+
nv_mask(priv, 0x61a004 + doff, 0x807f0000, 0x80150000);
nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000);
+
nv_wr32(priv, 0x61a00c + doff, 0x00100000 | loadval);
mdelay(9);
udelay(500);
- nv_wr32(priv, 0x61a00c + doff, 0x80000000);
- load = (nv_rd32(priv, 0x61a00c + doff) & 0x38000000) >> 27;
- nv_wr32(priv, 0x61a00c + doff, 0x00000000);
+ loadval = nv_mask(priv, 0x61a00c + doff, 0xffffffff, 0x00000000);
+
nv_mask(priv, 0x61a004 + doff, 0x807f0000, 0x80550000);
nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000);
- return load;
+
+ nv_debug(priv, "DAC%d sense: 0x%08x\n", or, loadval);
+ if (!(loadval & 0x80000000))
+ return -ETIMEDOUT;
+
+ return (loadval & 0x38000000) >> 27;
}
int
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
index c434d398d16f..e2d7f38447cc 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
@@ -580,8 +580,22 @@ static void
init_reserved(struct nvbios_init *init)
{
u8 opcode = nv_ro08(init->bios, init->offset);
- trace("RESERVED\t0x%02x\n", opcode);
- init->offset += 1;
+ u8 length, i;
+
+ switch (opcode) {
+ case 0xaa:
+ length = 4;
+ break;
+ default:
+ length = 1;
+ break;
+ }
+
+ trace("RESERVED 0x%02x\t", opcode);
+ for (i = 1; i < length; i++)
+ cont(" 0x%02x", nv_ro08(init->bios, init->offset + i));
+ cont("\n");
+ init->offset += length;
}
/**
@@ -2136,6 +2150,7 @@ static struct nvbios_init_opcode {
[0x99] = { init_zm_auxch },
[0x9a] = { init_i2c_long_if },
[0xa9] = { init_gpio_ne },
+ [0xaa] = { init_reserved },
};
#define init_opcode_nr (sizeof(init_opcode) / sizeof(init_opcode[0]))
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 32501f6ec991..16023986d301 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -50,7 +50,7 @@ static char *pre_emph_names[] = {
* or from atom. Note that atom operates on
* dw units.
*/
-static void radeon_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le)
+void radeon_atom_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le)
{
#ifdef __BIG_ENDIAN
u8 src_tmp[20], dst_tmp[20]; /* used for byteswapping */
@@ -100,7 +100,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1);
- radeon_copy_swap(base, send, send_bytes, true);
+ radeon_atom_copy_swap(base, send, send_bytes, true);
args.v1.lpAuxRequest = cpu_to_le16((u16)(0 + 4));
args.v1.lpDataOut = cpu_to_le16((u16)(16 + 4));
@@ -137,7 +137,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
recv_bytes = recv_size;
if (recv && recv_size)
- radeon_copy_swap(recv, base + 16, recv_bytes, false);
+ radeon_atom_copy_swap(recv, base + 16, recv_bytes, false);
return recv_bytes;
}
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index 4120d355cadd..4c81e9faa635 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -1636,8 +1636,12 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0);
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
- /* some early dce3.2 boards have a bug in their transmitter control table */
- if ((rdev->family != CHIP_RV710) && (rdev->family != CHIP_RV730))
+ /* some dce3.x boards have a bug in their transmitter control table.
+ * ACTION_ENABLE_OUTPUT can probably be dropped since ACTION_ENABLE
+ * does the same thing and more.
+ */
+ if ((rdev->family != CHIP_RV710) && (rdev->family != CHIP_RV730) &&
+ (rdev->family != CHIP_RS780) && (rdev->family != CHIP_RS880))
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
}
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) {
diff --git a/drivers/gpu/drm/radeon/atombios_i2c.c b/drivers/gpu/drm/radeon/atombios_i2c.c
index 082338df708a..2ca389d19258 100644
--- a/drivers/gpu/drm/radeon/atombios_i2c.c
+++ b/drivers/gpu/drm/radeon/atombios_i2c.c
@@ -27,6 +27,8 @@
#include "radeon.h"
#include "atom.h"
+extern void radeon_atom_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le);
+
#define TARGET_HW_I2C_CLOCK 50
/* these are a limitation of ProcessI2cChannelTransaction not the hw */
@@ -77,7 +79,7 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
}
if (!(flags & HW_I2C_WRITE))
- memcpy(buf, base, num);
+ radeon_atom_copy_swap(buf, base, num, false);
return 0;
}
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 687b421f75c3..8b6b0ba6ae40 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -1718,7 +1718,8 @@ static u32 evergreen_line_buffer_adjust(struct radeon_device *rdev,
struct drm_display_mode *mode,
struct drm_display_mode *other_mode)
{
- u32 tmp;
+ u32 tmp, buffer_alloc, i;
+ u32 pipe_offset = radeon_crtc->crtc_id * 0x20;
/*
* Line Buffer Setup
* There are 3 line buffers, each one shared by 2 display controllers.
@@ -1741,18 +1742,34 @@ static u32 evergreen_line_buffer_adjust(struct radeon_device *rdev,
* non-linked crtcs for maximum line buffer allocation.
*/
if (radeon_crtc->base.enabled && mode) {
- if (other_mode)
+ if (other_mode) {
tmp = 0; /* 1/2 */
- else
+ buffer_alloc = 1;
+ } else {
tmp = 2; /* whole */
- } else
+ buffer_alloc = 2;
+ }
+ } else {
tmp = 0;
+ buffer_alloc = 0;
+ }
/* second controller of the pair uses second half of the lb */
if (radeon_crtc->crtc_id % 2)
tmp += 4;
WREG32(DC_LB_MEMORY_SPLIT + radeon_crtc->crtc_offset, tmp);
+ if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) {
+ WREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset,
+ DMIF_BUFFERS_ALLOCATED(buffer_alloc));
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (RREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset) &
+ DMIF_BUFFERS_ALLOCATED_COMPLETED)
+ break;
+ udelay(1);
+ }
+ }
+
if (radeon_crtc->base.enabled && mode) {
switch (tmp) {
case 0:
@@ -2973,7 +2990,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
rdev->config.evergreen.sx_max_export_size = 256;
rdev->config.evergreen.sx_max_export_pos_size = 64;
rdev->config.evergreen.sx_max_export_smx_size = 192;
- rdev->config.evergreen.max_hw_contexts = 8;
+ rdev->config.evergreen.max_hw_contexts = 4;
rdev->config.evergreen.sq_num_cf_insts = 2;
rdev->config.evergreen.sc_prim_fifo_size = 0x40;
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index 75c05631146d..150e3186bec0 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -810,6 +810,10 @@
# define LATENCY_LOW_WATERMARK(x) ((x) << 0)
# define LATENCY_HIGH_WATERMARK(x) ((x) << 16)
+#define PIPE0_DMIF_BUFFER_CONTROL 0x0ca0
+# define DMIF_BUFFERS_ALLOCATED(x) ((x) << 0)
+# define DMIF_BUFFERS_ALLOCATED_COMPLETED (1 << 4)
+
#define IH_RB_CNTL 0x3e00
# define IH_RB_ENABLE (1 << 0)
# define IH_IB_SIZE(x) ((x) << 1) /* log2 */
@@ -1100,7 +1104,7 @@
* 6. COMMAND [29:22] | BYTE_COUNT [20:0]
*/
# define PACKET3_CP_DMA_DST_SEL(x) ((x) << 20)
- /* 0 - SRC_ADDR
+ /* 0 - DST_ADDR
* 1 - GDS
*/
# define PACKET3_CP_DMA_ENGINE(x) ((x) << 27)
@@ -1115,7 +1119,7 @@
# define PACKET3_CP_DMA_CP_SYNC (1 << 31)
/* COMMAND */
# define PACKET3_CP_DMA_DIS_WC (1 << 21)
-# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23)
+# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 22)
/* 0 - none
* 1 - 8 in 16
* 2 - 8 in 32
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index d0314ecbd7c1..46470dd7c710 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -2935,9 +2935,11 @@ static int r100_debugfs_cp_ring_info(struct seq_file *m, void *data)
seq_printf(m, "CP_RB_RPTR 0x%08x\n", rdp);
seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw);
seq_printf(m, "%u dwords in ring\n", count);
- for (j = 0; j <= count; j++) {
- i = (rdp + j) & ring->ptr_mask;
- seq_printf(m, "r[%04d]=0x%08x\n", i, ring->ring[i]);
+ if (ring->ready) {
+ for (j = 0; j <= count; j++) {
+ i = (rdp + j) & ring->ptr_mask;
+ seq_printf(m, "r[%04d]=0x%08x\n", i, ring->ring[i]);
+ }
}
return 0;
}
diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c
index b9b1139da356..0efe2a93ca97 100644
--- a/drivers/gpu/drm/radeon/r600_hdmi.c
+++ b/drivers/gpu/drm/radeon/r600_hdmi.c
@@ -238,9 +238,19 @@ void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock)
* number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE
* is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator
*/
- if (ASIC_IS_DCE3(rdev)) {
+ if (ASIC_IS_DCE32(rdev)) {
+ if (dig->dig_encoder == 0) {
+ WREG32(DCCG_AUDIO_DTO0_PHASE, base_rate * 100);
+ WREG32(DCCG_AUDIO_DTO0_MODULE, clock * 100);
+ WREG32(DCCG_AUDIO_DTO_SELECT, 0); /* select DTO0 */
+ } else {
+ WREG32(DCCG_AUDIO_DTO1_PHASE, base_rate * 100);
+ WREG32(DCCG_AUDIO_DTO1_MODULE, clock * 100);
+ WREG32(DCCG_AUDIO_DTO_SELECT, 1); /* select DTO1 */
+ }
+ } else if (ASIC_IS_DCE3(rdev)) {
/* according to the reg specs, this should DCE3.2 only, but in
- * practice it seems to cover DCE3.0 as well.
+ * practice it seems to cover DCE3.0/3.1 as well.
*/
if (dig->dig_encoder == 0) {
WREG32(DCCG_AUDIO_DTO0_PHASE, base_rate * 100);
@@ -252,7 +262,7 @@ void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock)
WREG32(DCCG_AUDIO_DTO_SELECT, 1); /* select DTO1 */
}
} else {
- /* according to the reg specs, this should be DCE2.0 and DCE3.0 */
+ /* according to the reg specs, this should be DCE2.0 and DCE3.0/3.1 */
WREG32(AUDIO_DTO, AUDIO_DTO_PHASE(base_rate / 10) |
AUDIO_DTO_MODULE(clock / 10));
}
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index 79df558f8c40..2fd2241b6df9 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -1259,7 +1259,7 @@
*/
# define PACKET3_CP_DMA_CP_SYNC (1 << 31)
/* COMMAND */
-# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23)
+# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 22)
/* 0 - none
* 1 - 8 in 16
* 2 - 8 in 32
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index dea6f63c9724..239a4074f122 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -715,13 +715,16 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
(ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *)
(ctx->bios + data_offset +
le16_to_cpu(router_obj->asObjects[k].usSrcDstTableOffset));
+ u8 *num_dst_objs = (u8 *)
+ ((u8 *)router_src_dst_table + 1 +
+ (router_src_dst_table->ucNumberOfSrc * 2));
+ u16 *dst_objs = (u16 *)(num_dst_objs + 1);
int enum_id;
router.router_id = router_obj_id;
- for (enum_id = 0; enum_id < router_src_dst_table->ucNumberOfDst;
- enum_id++) {
+ for (enum_id = 0; enum_id < (*num_dst_objs); enum_id++) {
if (le16_to_cpu(path->usConnObjectId) ==
- le16_to_cpu(router_src_dst_table->usDstObjectID[enum_id]))
+ le16_to_cpu(dst_objs[enum_id]))
break;
}
@@ -1651,7 +1654,9 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
kfree(edid);
}
}
- record += sizeof(ATOM_FAKE_EDID_PATCH_RECORD);
+ record += fake_edid_record->ucFakeEDIDLength ?
+ fake_edid_record->ucFakeEDIDLength + 2 :
+ sizeof(ATOM_FAKE_EDID_PATCH_RECORD);
break;
case LCD_PANEL_RESOLUTION_RECORD_TYPE:
panel_res_record = (ATOM_PANEL_RESOLUTION_PATCH_RECORD *)record;
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 2399f25ec037..5a87c9fc78d3 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -1489,6 +1489,24 @@ static const struct drm_connector_funcs radeon_dp_connector_funcs = {
.force = radeon_dvi_force,
};
+static const struct drm_connector_funcs radeon_edp_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = radeon_dp_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = radeon_lvds_set_property,
+ .destroy = radeon_dp_connector_destroy,
+ .force = radeon_dvi_force,
+};
+
+static const struct drm_connector_funcs radeon_lvds_bridge_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = radeon_dp_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = radeon_lvds_set_property,
+ .destroy = radeon_dp_connector_destroy,
+ .force = radeon_dvi_force,
+};
+
void
radeon_add_atom_connector(struct drm_device *dev,
uint32_t connector_id,
@@ -1580,8 +1598,6 @@ radeon_add_atom_connector(struct drm_device *dev,
goto failed;
radeon_dig_connector->igp_lane_info = igp_lane_info;
radeon_connector->con_priv = radeon_dig_connector;
- drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type);
- drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
if (i2c_bus->valid) {
/* add DP i2c bus */
if (connector_type == DRM_MODE_CONNECTOR_eDP)
@@ -1598,6 +1614,10 @@ radeon_add_atom_connector(struct drm_device *dev,
case DRM_MODE_CONNECTOR_VGA:
case DRM_MODE_CONNECTOR_DVIA:
default:
+ drm_connector_init(dev, &radeon_connector->base,
+ &radeon_dp_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base,
+ &radeon_dp_connector_helper_funcs);
connector->interlace_allowed = true;
connector->doublescan_allowed = true;
radeon_connector->dac_load_detect = true;
@@ -1610,6 +1630,10 @@ radeon_add_atom_connector(struct drm_device *dev,
case DRM_MODE_CONNECTOR_HDMIA:
case DRM_MODE_CONNECTOR_HDMIB:
case DRM_MODE_CONNECTOR_DisplayPort:
+ drm_connector_init(dev, &radeon_connector->base,
+ &radeon_dp_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base,
+ &radeon_dp_connector_helper_funcs);
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.underscan_property,
UNDERSCAN_OFF);
@@ -1634,6 +1658,10 @@ radeon_add_atom_connector(struct drm_device *dev,
break;
case DRM_MODE_CONNECTOR_LVDS:
case DRM_MODE_CONNECTOR_eDP:
+ drm_connector_init(dev, &radeon_connector->base,
+ &radeon_lvds_bridge_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base,
+ &radeon_dp_connector_helper_funcs);
drm_object_attach_property(&radeon_connector->base.base,
dev->mode_config.scaling_mode_property,
DRM_MODE_SCALE_FULLSCREEN);
@@ -1797,7 +1825,7 @@ radeon_add_atom_connector(struct drm_device *dev,
goto failed;
radeon_dig_connector->igp_lane_info = igp_lane_info;
radeon_connector->con_priv = radeon_dig_connector;
- drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type);
+ drm_connector_init(dev, &radeon_connector->base, &radeon_edp_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
if (i2c_bus->valid) {
/* add DP i2c bus */
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index 7e265a58141f..fe36f1d9496d 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -80,9 +80,11 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
p->relocs[i].lobj.bo = p->relocs[i].robj;
p->relocs[i].lobj.written = !!r->write_domain;
- /* the first reloc of an UVD job is the
- msg and that must be in VRAM */
- if (p->ring == R600_RING_TYPE_UVD_INDEX && i == 0) {
+ /* the first reloc of an UVD job is the msg and that must be in
+ VRAM, also but everything into VRAM on AGP cards to avoid
+ image corruptions */
+ if (p->ring == R600_RING_TYPE_UVD_INDEX &&
+ (i == 0 || drm_pci_device_is_agp(p->rdev->ddev))) {
/* TODO: is this still needed for NI+ ? */
p->relocs[i].lobj.domain =
RADEON_GEM_DOMAIN_VRAM;
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index b0dc0b6cb4e0..8df1525f71d2 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1196,13 +1196,22 @@ int radeon_device_init(struct radeon_device *rdev,
return r;
}
if ((radeon_testing & 1)) {
- radeon_test_moves(rdev);
+ if (rdev->accel_working)
+ radeon_test_moves(rdev);
+ else
+ DRM_INFO("radeon: acceleration disabled, skipping move tests\n");
}
if ((radeon_testing & 2)) {
- radeon_test_syncing(rdev);
+ if (rdev->accel_working)
+ radeon_test_syncing(rdev);
+ else
+ DRM_INFO("radeon: acceleration disabled, skipping sync tests\n");
}
if (radeon_benchmarking) {
- radeon_benchmark(rdev, radeon_benchmarking);
+ if (rdev->accel_working)
+ radeon_benchmark(rdev, radeon_benchmarking);
+ else
+ DRM_INFO("radeon: acceleration disabled, skipping benchmarks\n");
}
return 0;
}
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index 4f2d4f4c1dab..7e292d899209 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -414,6 +414,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
value = rdev->config.si.tile_mode_array;
value_size = sizeof(uint32_t)*32;
break;
+ case RADEON_INFO_SI_CP_DMA_COMPUTE:
+ *value = 1;
+ break;
default:
DRM_DEBUG_KMS("Invalid request %d\n", info->request);
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index 83f6295a0e5e..6e0f4809bda0 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -823,9 +823,11 @@ static int radeon_debugfs_ring_info(struct seq_file *m, void *data)
* packet that is the root issue
*/
i = (ring->rptr + ring->ptr_mask + 1 - 32) & ring->ptr_mask;
- for (j = 0; j <= (count + 32); j++) {
- seq_printf(m, "r[%5d]=0x%08x\n", i, ring->ring[i]);
- i = (i + 1) & ring->ptr_mask;
+ if (ring->ready) {
+ for (j = 0; j <= (count + 32); j++) {
+ seq_printf(m, "r[%5d]=0x%08x\n", i, ring->ring[i]);
+ i = (i + 1) & ring->ptr_mask;
+ }
}
return 0;
}
diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c
index bbed4af8d0bc..f9ebf2bf8b57 100644
--- a/drivers/gpu/drm/radeon/radeon_test.c
+++ b/drivers/gpu/drm/radeon/radeon_test.c
@@ -37,8 +37,8 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
struct radeon_bo **gtt_obj = NULL;
struct radeon_fence *fence = NULL;
uint64_t gtt_addr, vram_addr;
- unsigned i, n, size;
- int r, ring;
+ unsigned n, size;
+ int i, r, ring;
switch (flag) {
case RADEON_TEST_COPY_DMA:
diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c
index 233a9b9fa1f7..b8074a8ec75a 100644
--- a/drivers/gpu/drm/radeon/rs400.c
+++ b/drivers/gpu/drm/radeon/rs400.c
@@ -174,10 +174,13 @@ int rs400_gart_enable(struct radeon_device *rdev)
/* FIXME: according to doc we should set HIDE_MMCFG_BAR=0,
* AGPMODE30=0 & AGP30ENHANCED=0 in NB_CNTL */
if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740)) {
- WREG32_MC(RS480_MC_MISC_CNTL,
- (RS480_GART_INDEX_REG_EN | RS690_BLOCK_GFX_D3_EN));
+ tmp = RREG32_MC(RS480_MC_MISC_CNTL);
+ tmp |= RS480_GART_INDEX_REG_EN | RS690_BLOCK_GFX_D3_EN;
+ WREG32_MC(RS480_MC_MISC_CNTL, tmp);
} else {
- WREG32_MC(RS480_MC_MISC_CNTL, RS480_GART_INDEX_REG_EN);
+ tmp = RREG32_MC(RS480_MC_MISC_CNTL);
+ tmp |= RS480_GART_INDEX_REG_EN;
+ WREG32_MC(RS480_MC_MISC_CNTL, tmp);
}
/* Enable gart */
WREG32_MC(RS480_AGP_ADDRESS_SPACE_SIZE, (RS480_GART_EN | size_reg));
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index 1a96a16b9996..f054a3b6c4b8 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -1467,7 +1467,8 @@ static u32 dce6_line_buffer_adjust(struct radeon_device *rdev,
struct drm_display_mode *mode,
struct drm_display_mode *other_mode)
{
- u32 tmp;
+ u32 tmp, buffer_alloc, i;
+ u32 pipe_offset = radeon_crtc->crtc_id * 0x20;
/*
* Line Buffer Setup
* There are 3 line buffers, each one shared by 2 display controllers.
@@ -1482,16 +1483,30 @@ static u32 dce6_line_buffer_adjust(struct radeon_device *rdev,
* non-linked crtcs for maximum line buffer allocation.
*/
if (radeon_crtc->base.enabled && mode) {
- if (other_mode)
+ if (other_mode) {
tmp = 0; /* 1/2 */
- else
+ buffer_alloc = 1;
+ } else {
tmp = 2; /* whole */
- } else
+ buffer_alloc = 2;
+ }
+ } else {
tmp = 0;
+ buffer_alloc = 0;
+ }
WREG32(DC_LB_MEMORY_SPLIT + radeon_crtc->crtc_offset,
DC_LB_MEMORY_CONFIG(tmp));
+ WREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset,
+ DMIF_BUFFERS_ALLOCATED(buffer_alloc));
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (RREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset) &
+ DMIF_BUFFERS_ALLOCATED_COMPLETED)
+ break;
+ udelay(1);
+ }
+
if (radeon_crtc->base.enabled && mode) {
switch (tmp) {
case 0:
@@ -3796,13 +3811,64 @@ static int si_vm_packet3_ce_check(struct radeon_device *rdev,
return 0;
}
+static int si_vm_packet3_cp_dma_check(u32 *ib, u32 idx)
+{
+ u32 start_reg, reg, i;
+ u32 command = ib[idx + 4];
+ u32 info = ib[idx + 1];
+ u32 idx_value = ib[idx];
+ if (command & PACKET3_CP_DMA_CMD_SAS) {
+ /* src address space is register */
+ if (((info & 0x60000000) >> 29) == 0) {
+ start_reg = idx_value << 2;
+ if (command & PACKET3_CP_DMA_CMD_SAIC) {
+ reg = start_reg;
+ if (!si_vm_reg_valid(reg)) {
+ DRM_ERROR("CP DMA Bad SRC register\n");
+ return -EINVAL;
+ }
+ } else {
+ for (i = 0; i < (command & 0x1fffff); i++) {
+ reg = start_reg + (4 * i);
+ if (!si_vm_reg_valid(reg)) {
+ DRM_ERROR("CP DMA Bad SRC register\n");
+ return -EINVAL;
+ }
+ }
+ }
+ }
+ }
+ if (command & PACKET3_CP_DMA_CMD_DAS) {
+ /* dst address space is register */
+ if (((info & 0x00300000) >> 20) == 0) {
+ start_reg = ib[idx + 2];
+ if (command & PACKET3_CP_DMA_CMD_DAIC) {
+ reg = start_reg;
+ if (!si_vm_reg_valid(reg)) {
+ DRM_ERROR("CP DMA Bad DST register\n");
+ return -EINVAL;
+ }
+ } else {
+ for (i = 0; i < (command & 0x1fffff); i++) {
+ reg = start_reg + (4 * i);
+ if (!si_vm_reg_valid(reg)) {
+ DRM_ERROR("CP DMA Bad DST register\n");
+ return -EINVAL;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
static int si_vm_packet3_gfx_check(struct radeon_device *rdev,
u32 *ib, struct radeon_cs_packet *pkt)
{
+ int r;
u32 idx = pkt->idx + 1;
u32 idx_value = ib[idx];
u32 start_reg, end_reg, reg, i;
- u32 command, info;
switch (pkt->opcode) {
case PACKET3_NOP:
@@ -3903,50 +3969,9 @@ static int si_vm_packet3_gfx_check(struct radeon_device *rdev,
}
break;
case PACKET3_CP_DMA:
- command = ib[idx + 4];
- info = ib[idx + 1];
- if (command & PACKET3_CP_DMA_CMD_SAS) {
- /* src address space is register */
- if (((info & 0x60000000) >> 29) == 0) {
- start_reg = idx_value << 2;
- if (command & PACKET3_CP_DMA_CMD_SAIC) {
- reg = start_reg;
- if (!si_vm_reg_valid(reg)) {
- DRM_ERROR("CP DMA Bad SRC register\n");
- return -EINVAL;
- }
- } else {
- for (i = 0; i < (command & 0x1fffff); i++) {
- reg = start_reg + (4 * i);
- if (!si_vm_reg_valid(reg)) {
- DRM_ERROR("CP DMA Bad SRC register\n");
- return -EINVAL;
- }
- }
- }
- }
- }
- if (command & PACKET3_CP_DMA_CMD_DAS) {
- /* dst address space is register */
- if (((info & 0x00300000) >> 20) == 0) {
- start_reg = ib[idx + 2];
- if (command & PACKET3_CP_DMA_CMD_DAIC) {
- reg = start_reg;
- if (!si_vm_reg_valid(reg)) {
- DRM_ERROR("CP DMA Bad DST register\n");
- return -EINVAL;
- }
- } else {
- for (i = 0; i < (command & 0x1fffff); i++) {
- reg = start_reg + (4 * i);
- if (!si_vm_reg_valid(reg)) {
- DRM_ERROR("CP DMA Bad DST register\n");
- return -EINVAL;
- }
- }
- }
- }
- }
+ r = si_vm_packet3_cp_dma_check(ib, idx);
+ if (r)
+ return r;
break;
default:
DRM_ERROR("Invalid GFX packet3: 0x%x\n", pkt->opcode);
@@ -3958,6 +3983,7 @@ static int si_vm_packet3_gfx_check(struct radeon_device *rdev,
static int si_vm_packet3_compute_check(struct radeon_device *rdev,
u32 *ib, struct radeon_cs_packet *pkt)
{
+ int r;
u32 idx = pkt->idx + 1;
u32 idx_value = ib[idx];
u32 start_reg, reg, i;
@@ -4030,6 +4056,11 @@ static int si_vm_packet3_compute_check(struct radeon_device *rdev,
return -EINVAL;
}
break;
+ case PACKET3_CP_DMA:
+ r = si_vm_packet3_cp_dma_check(ib, idx);
+ if (r)
+ return r;
+ break;
default:
DRM_ERROR("Invalid Compute packet3: 0x%x\n", pkt->opcode);
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h
index 8f2d7d4f9b28..495f41f2900b 100644
--- a/drivers/gpu/drm/radeon/sid.h
+++ b/drivers/gpu/drm/radeon/sid.h
@@ -97,6 +97,10 @@
#define DMIF_ADDR_CALC 0xC00
+#define PIPE0_DMIF_BUFFER_CONTROL 0x0ca0
+# define DMIF_BUFFERS_ALLOCATED(x) ((x) << 0)
+# define DMIF_BUFFERS_ALLOCATED_COMPLETED (1 << 4)
+
#define SRBM_STATUS 0xE50
#define GRBM_RQ_PENDING (1 << 5)
#define VMC_BUSY (1 << 8)
@@ -924,7 +928,7 @@
* 6. COMMAND [30:21] | BYTE_COUNT [20:0]
*/
# define PACKET3_CP_DMA_DST_SEL(x) ((x) << 20)
- /* 0 - SRC_ADDR
+ /* 0 - DST_ADDR
* 1 - GDS
*/
# define PACKET3_CP_DMA_ENGINE(x) ((x) << 27)
@@ -939,7 +943,7 @@
# define PACKET3_CP_DMA_CP_SYNC (1 << 31)
/* COMMAND */
# define PACKET3_CP_DMA_DIS_WC (1 << 21)
-# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23)
+# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 22)
/* 0 - none
* 1 - 8 in 16
* 2 - 8 in 32
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
index 5e93a52d4f2c..210d50365162 100644
--- a/drivers/gpu/drm/ttm/ttm_tt.c
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
@@ -170,7 +170,7 @@ void ttm_tt_destroy(struct ttm_tt *ttm)
ttm_tt_unbind(ttm);
}
- if (likely(ttm->pages != NULL)) {
+ if (ttm->state == tt_unbound) {
ttm->bdev->driver->ttm_tt_unpopulate(ttm);
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 07dfd823cc30..6c44c69a5ba4 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -740,9 +740,17 @@ static void vmw_postclose(struct drm_device *dev,
struct vmw_fpriv *vmw_fp;
vmw_fp = vmw_fpriv(file_priv);
- ttm_object_file_release(&vmw_fp->tfile);
- if (vmw_fp->locked_master)
+
+ if (vmw_fp->locked_master) {
+ struct vmw_master *vmaster =
+ vmw_master(vmw_fp->locked_master);
+
+ ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
+ ttm_vt_unlock(&vmaster->lock);
drm_master_put(&vmw_fp->locked_master);
+ }
+
+ ttm_object_file_release(&vmw_fp->tfile);
kfree(vmw_fp);
}
@@ -942,14 +950,13 @@ static void vmw_master_drop(struct drm_device *dev,
vmw_fp->locked_master = drm_master_get(file_priv->master);
ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile);
- vmw_execbuf_release_pinned_bo(dev_priv);
-
if (unlikely((ret != 0))) {
DRM_ERROR("Unable to lock TTM at VT switch.\n");
drm_master_put(&vmw_fp->locked_master);
}
- ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
+ ttm_lock_set_kill(&vmaster->lock, false, SIGTERM);
+ vmw_execbuf_release_pinned_bo(dev_priv);
if (!dev_priv->enable_fb) {
ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index bc784254e78e..407d7f9fe8a8 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -970,7 +970,7 @@ void vmw_resource_unreserve(struct vmw_resource *res,
if (new_backup)
res->backup_offset = new_backup_offset;
- if (!res->func->may_evict)
+ if (!res->func->may_evict || res->id == -1)
return;
write_lock(&dev_priv->resource_lock);
diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile
index 306fff970de4..9c956659124b 100644
--- a/drivers/gpu/ion/Makefile
+++ b/drivers/gpu/ion/Makefile
@@ -1,3 +1,6 @@
obj-$(CONFIG_ION) += ion.o ion_heap.o ion_page_pool.o ion_system_heap.o \
- ion_carveout_heap.o ion_chunk_heap.o
+ ion_carveout_heap.o ion_chunk_heap.o ion_cma_heap.o
+ifdef CONFIG_COMPAT
+obj-$(CONFIG_ION) += compat_ion.o
+endif
obj-$(CONFIG_ION_TEGRA) += tegra/
diff --git a/drivers/gpu/ion/compat_ion.c b/drivers/gpu/ion/compat_ion.c
new file mode 100644
index 000000000000..e0d2839952a0
--- /dev/null
+++ b/drivers/gpu/ion/compat_ion.c
@@ -0,0 +1,172 @@
+/*
+ * drivers/gpu/ion/compat_ion.c
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/ion.h>
+#include <linux/compat.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+
+#include "compat_ion.h"
+
+/* See include/linux/ion.h for the definition of these structs */
+struct compat_ion_allocation_data {
+ compat_size_t len;
+ compat_size_t align;
+ compat_uint_t heap_id_mask;
+ compat_uint_t flags;
+ compat_int_t handle;
+};
+
+struct compat_ion_custom_data {
+ compat_uint_t cmd;
+ compat_ulong_t arg;
+};
+
+static int compat_get_ion_allocation_data(
+ struct compat_ion_allocation_data __user *data32,
+ struct ion_allocation_data __user *data)
+{
+ compat_size_t s;
+ compat_uint_t u;
+ compat_int_t i;
+ int err;
+
+ err = get_user(s, &data32->len);
+ err |= put_user(s, &data->len);
+ err |= get_user(s, &data32->align);
+ err |= put_user(s, &data->align);
+ err |= get_user(u, &data32->heap_id_mask);
+ err |= put_user(u, &data->heap_id_mask);
+ err |= get_user(u, &data32->flags);
+ err |= put_user(u, &data->flags);
+ err |= get_user(i, &data32->handle);
+ err |= put_user(i, &data->handle);
+
+ return err;
+}
+
+static int compat_put_ion_allocation_data(
+ struct compat_ion_allocation_data __user *data32,
+ struct ion_allocation_data __user *data)
+{
+ compat_size_t s;
+ compat_uint_t u;
+ compat_int_t i;
+ int err;
+
+ err = get_user(s, &data->len);
+ err |= put_user(s, &data32->len);
+ err |= get_user(s, &data->align);
+ err |= put_user(s, &data32->align);
+ err |= get_user(u, &data->heap_id_mask);
+ err |= put_user(u, &data32->heap_id_mask);
+ err |= get_user(u, &data->flags);
+ err |= put_user(u, &data32->flags);
+ err |= get_user(i, &data->handle);
+ err |= put_user(i, &data32->handle);
+
+ return err;
+}
+
+static int compat_get_ion_custom_data(
+ struct compat_ion_custom_data __user *data32,
+ struct ion_custom_data __user *data)
+{
+ compat_uint_t cmd;
+ compat_ulong_t arg;
+ int err;
+
+ err = get_user(cmd, &data32->cmd);
+ err |= put_user(cmd, &data->cmd);
+ err |= get_user(arg, &data32->arg);
+ err |= put_user(arg, &data->arg);
+
+ return err;
+};
+
+long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ if (!filp->f_op || !filp->f_op->unlocked_ioctl)
+ return -ENOTTY;
+
+ switch (cmd) {
+ case ION_IOC_ALLOC:
+ {
+ struct compat_ion_allocation_data __user *data32;
+ struct ion_allocation_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_allocation_data(data32, data);
+ if (err)
+ return err;
+
+ ret = filp->f_op->unlocked_ioctl(filp, cmd,
+ (unsigned long)data);
+ err = compat_put_ion_allocation_data(data32, data);
+ return ret ? ret : err;
+ }
+ case ION_IOC_FREE:
+ {
+ struct compat_ion_allocation_data __user *data32;
+ struct ion_allocation_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_allocation_data(data32, data);
+ if (err)
+ return err;
+
+ return filp->f_op->unlocked_ioctl(filp, cmd,
+ (unsigned long)data);
+ }
+ case ION_IOC_CUSTOM: {
+ struct compat_ion_custom_data __user *data32;
+ struct ion_custom_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_custom_data(data32, data);
+ if (err)
+ return err;
+
+ return filp->f_op->unlocked_ioctl(filp, cmd,
+ (unsigned long)data);
+ }
+ case ION_IOC_SHARE:
+ case ION_IOC_MAP:
+ case ION_IOC_IMPORT:
+ case ION_IOC_SYNC:
+ return filp->f_op->unlocked_ioctl(filp, cmd,
+ (unsigned long)compat_ptr(arg));
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
diff --git a/drivers/gpu/ion/compat_ion.h b/drivers/gpu/ion/compat_ion.h
new file mode 100644
index 000000000000..3a9c8c08c240
--- /dev/null
+++ b/drivers/gpu/ion/compat_ion.h
@@ -0,0 +1,30 @@
+/*
+
+ * drivers/gpu/ion/compat_ion.h
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_COMPAT_ION_H
+#define _LINUX_COMPAT_ION_H
+
+#if IS_ENABLED(CONFIG_COMPAT)
+
+long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+
+#else
+
+#define compat_ion_ioctl NULL
+
+#endif /* CONFIG_COMPAT */
+#endif /* _LINUX_COMPAT_ION_H */
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index 6c93365c3dbc..e4ffc9d5b94b 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -32,10 +32,13 @@
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
#include <linux/debugfs.h>
#include <linux/dma-buf.h>
+#include <linux/idr.h>
#include "ion_priv.h"
+#include "compat_ion.h"
/**
* struct ion_device - the metadata of the ion device node
@@ -63,6 +66,7 @@ struct ion_device {
* @node: node in the tree of all clients
* @dev: backpointer to ion device
* @handles: an rb tree of all the handles in this client
+ * @idr: an idr space for allocating handle ids
* @lock: lock protecting the tree of handles
* @name: used for debugging
* @task: used for debugging
@@ -75,6 +79,7 @@ struct ion_client {
struct rb_node node;
struct ion_device *dev;
struct rb_root handles;
+ struct idr idr;
struct mutex lock;
const char *name;
struct task_struct *task;
@@ -89,7 +94,7 @@ struct ion_client {
* @buffer: pointer to the buffer
* @node: node in the client's handle rbtree
* @kmap_cnt: count of times this client has mapped to kernel
- * @dmap_cnt: count of times this client has mapped for dma
+ * @id: client-unique id allocated by client->idr
*
* Modifications to node, map_cnt or mapping should be protected by the
* lock in the client. Other fields are never changed after initialization.
@@ -100,17 +105,38 @@ struct ion_handle {
struct ion_buffer *buffer;
struct rb_node node;
unsigned int kmap_cnt;
+ int id;
};
bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer)
{
- return ((buffer->flags & ION_FLAG_CACHED) &&
- !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC));
+ return ((buffer->flags & ION_FLAG_CACHED) &&
+ !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC));
}
bool ion_buffer_cached(struct ion_buffer *buffer)
{
- return !!(buffer->flags & ION_FLAG_CACHED);
+ return !!(buffer->flags & ION_FLAG_CACHED);
+}
+
+static inline struct page *ion_buffer_page(struct page *page)
+{
+ return (struct page *)((unsigned long)page & ~(1UL));
+}
+
+static inline bool ion_buffer_page_is_dirty(struct page *page)
+{
+ return !!((unsigned long)page & 1UL);
+}
+
+static inline void ion_buffer_page_dirty(struct page **page)
+{
+ *page = (struct page *)((unsigned long)(*page) | 1UL);
+}
+
+static inline void ion_buffer_page_clean(struct page **page)
+{
+ *page = (struct page *)((unsigned long)(*page) & ~(1UL));
}
/* this function should only be called while dev->lock is held */
@@ -139,8 +165,6 @@ static void ion_buffer_add(struct ion_device *dev,
rb_insert_color(&buffer->node, &dev->buffers);
}
-static int ion_buffer_alloc_dirty(struct ion_buffer *buffer);
-
/* this function should only be called while dev->lock is held */
static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
struct ion_device *dev,
@@ -178,24 +202,32 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
buffer->size = len;
table = heap->ops->map_dma(heap, buffer);
- if (IS_ERR_OR_NULL(table)) {
+ if (WARN_ONCE(table == NULL, "heap->ops->map_dma should return ERR_PTR on error"))
+ table = ERR_PTR(-EINVAL);
+ if (IS_ERR(table)) {
heap->ops->free(buffer);
kfree(buffer);
return ERR_PTR(PTR_ERR(table));
}
buffer->sg_table = table;
if (ion_buffer_fault_user_mappings(buffer)) {
- for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents,
- i) {
- if (sg_dma_len(sg) == PAGE_SIZE)
- continue;
- pr_err("%s: cached mappings that will be faulted in "
- "must have pagewise sg_lists\n", __func__);
- ret = -EINVAL;
- goto err;
+ int num_pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE;
+ struct scatterlist *sg;
+ int i, j, k = 0;
+
+ buffer->pages = vmalloc(sizeof(struct page *) * num_pages);
+ if (!buffer->pages) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ struct page *page = sg_page(sg);
+
+ for (j = 0; j < sg_dma_len(sg) / PAGE_SIZE; j++)
+ buffer->pages[k++] = page++;
}
- ret = ion_buffer_alloc_dirty(buffer);
if (ret)
goto err;
}
@@ -222,6 +254,9 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
err:
heap->ops->unmap_dma(heap, buffer);
heap->ops->free(buffer);
+err1:
+ if (buffer->pages)
+ vfree(buffer->pages);
err2:
kfree(buffer);
return ERR_PTR(ret);
@@ -233,8 +268,8 @@ void ion_buffer_destroy(struct ion_buffer *buffer)
buffer->heap->ops->unmap_kernel(buffer->heap, buffer);
buffer->heap->ops->unmap_dma(buffer->heap, buffer);
buffer->heap->ops->free(buffer);
- if (buffer->flags & ION_FLAG_CACHED)
- kfree(buffer->dirty);
+ if (buffer->pages)
+ vfree(buffer->pages);
kfree(buffer);
}
@@ -326,6 +361,7 @@ static void ion_handle_destroy(struct kref *kref)
ion_handle_kmap_put(handle);
mutex_unlock(&buffer->lock);
+ idr_remove(&client->idr, handle->id);
if (!RB_EMPTY_NODE(&handle->node))
rb_erase(&handle->node, &client->handles);
@@ -353,47 +389,56 @@ static int ion_handle_put(struct ion_handle *handle)
static struct ion_handle *ion_handle_lookup(struct ion_client *client,
struct ion_buffer *buffer)
{
- struct rb_node *n;
-
- for (n = rb_first(&client->handles); n; n = rb_next(n)) {
- struct ion_handle *handle = rb_entry(n, struct ion_handle,
- node);
- if (handle->buffer == buffer)
- return handle;
- }
- return NULL;
-}
-
-static bool ion_handle_validate(struct ion_client *client, struct ion_handle *handle)
-{
struct rb_node *n = client->handles.rb_node;
while (n) {
- struct ion_handle *handle_node = rb_entry(n, struct ion_handle,
- node);
- if (handle < handle_node)
+ struct ion_handle *entry = rb_entry(n, struct ion_handle, node);
+ if (buffer < entry->buffer)
n = n->rb_left;
- else if (handle > handle_node)
+ else if (buffer > entry->buffer)
n = n->rb_right;
else
- return true;
+ return entry;
}
- return false;
+ return ERR_PTR(-EINVAL);
+}
+
+static struct ion_handle *ion_uhandle_get(struct ion_client *client, int id)
+{
+ return idr_find(&client->idr, id);
}
-static void ion_handle_add(struct ion_client *client, struct ion_handle *handle)
+static bool ion_handle_validate(struct ion_client *client, struct ion_handle *handle)
{
+ return (ion_uhandle_get(client, handle->id) == handle);
+}
+
+static int ion_handle_add(struct ion_client *client, struct ion_handle *handle)
+{
+ int rc;
struct rb_node **p = &client->handles.rb_node;
struct rb_node *parent = NULL;
struct ion_handle *entry;
+ do {
+ int id;
+ rc = idr_pre_get(&client->idr, GFP_KERNEL);
+ if (!rc)
+ return -ENOMEM;
+ rc = idr_get_new_above(&client->idr, handle, 1, &id);
+ handle->id = id;
+ } while (rc == -EAGAIN);
+
+ if (rc < 0)
+ return rc;
+
while (*p) {
parent = *p;
entry = rb_entry(parent, struct ion_handle, node);
- if (handle < entry)
+ if (handle->buffer < entry->buffer)
p = &(*p)->rb_left;
- else if (handle > entry)
+ else if (handle->buffer > entry->buffer)
p = &(*p)->rb_right;
else
WARN(1, "%s: buffer already found.", __func__);
@@ -401,6 +446,8 @@ static void ion_handle_add(struct ion_client *client, struct ion_handle *handle)
rb_link_node(&handle->node, parent, p);
rb_insert_color(&handle->node, &client->handles);
+
+ return 0;
}
struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
@@ -411,6 +458,7 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
struct ion_device *dev = client->dev;
struct ion_buffer *buffer = NULL;
struct ion_heap *heap;
+ int ret;
pr_debug("%s: len %d align %d heap_id_mask %u flags %x\n", __func__,
len, align, heap_id_mask, flags);
@@ -431,7 +479,7 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
if (!((1 << heap->id) & heap_id_mask))
continue;
buffer = ion_buffer_create(heap, dev, len, align, flags);
- if (!IS_ERR_OR_NULL(buffer))
+ if (!IS_ERR(buffer))
break;
}
up_read(&dev->lock);
@@ -450,12 +498,16 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
*/
ion_buffer_put(buffer);
- if (!IS_ERR(handle)) {
- mutex_lock(&client->lock);
- ion_handle_add(client, handle);
- mutex_unlock(&client->lock);
- }
+ if (IS_ERR(handle))
+ return handle;
+ mutex_lock(&client->lock);
+ ret = ion_handle_add(client, handle);
+ if (ret) {
+ ion_handle_put(handle);
+ handle = ERR_PTR(ret);
+ }
+ mutex_unlock(&client->lock);
return handle;
}
@@ -515,7 +567,9 @@ static void *ion_buffer_kmap_get(struct ion_buffer *buffer)
return buffer->vaddr;
}
vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer);
- if (IS_ERR_OR_NULL(vaddr))
+ if (WARN_ONCE(vaddr == NULL, "heap->ops->map_kernel should return ERR_PTR on error"))
+ return ERR_PTR(-EINVAL);
+ if (IS_ERR(vaddr))
return vaddr;
buffer->vaddr = vaddr;
buffer->kmap_cnt++;
@@ -532,7 +586,7 @@ static void *ion_handle_kmap_get(struct ion_handle *handle)
return buffer->vaddr;
}
vaddr = ion_buffer_kmap_get(buffer);
- if (IS_ERR_OR_NULL(vaddr))
+ if (IS_ERR(vaddr))
return vaddr;
handle->kmap_cnt++;
return vaddr;
@@ -673,6 +727,7 @@ struct ion_client *ion_client_create(struct ion_device *dev,
client->dev = dev;
client->handles = RB_ROOT;
+ idr_init(&client->idr);
mutex_init(&client->lock);
client->name = name;
client->task = task;
@@ -713,6 +768,10 @@ void ion_client_destroy(struct ion_client *client)
node);
ion_handle_destroy(&handle->ref);
}
+
+ idr_remove_all(&client->idr);
+ idr_destroy(&client->idr);
+
down_write(&dev->lock);
if (client->task)
put_task_struct(client->task);
@@ -764,17 +823,6 @@ static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment,
{
}
-static int ion_buffer_alloc_dirty(struct ion_buffer *buffer)
-{
- unsigned long pages = buffer->sg_table->nents;
- unsigned long length = (pages + BITS_PER_LONG - 1)/BITS_PER_LONG;
-
- buffer->dirty = kzalloc(length * sizeof(unsigned long), GFP_KERNEL);
- if (!buffer->dirty)
- return -ENOMEM;
- return 0;
-}
-
struct ion_vma_list {
struct list_head list;
struct vm_area_struct *vma;
@@ -784,9 +832,9 @@ static void ion_buffer_sync_for_device(struct ion_buffer *buffer,
struct device *dev,
enum dma_data_direction dir)
{
- struct scatterlist *sg;
- int i;
struct ion_vma_list *vma_list;
+ int pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE;
+ int i;
pr_debug("%s: syncing for device %s\n", __func__,
dev ? dev_name(dev) : "null");
@@ -795,11 +843,12 @@ static void ion_buffer_sync_for_device(struct ion_buffer *buffer,
return;
mutex_lock(&buffer->lock);
- for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) {
- if (!test_bit(i, buffer->dirty))
- continue;
- dma_sync_sg_for_device(dev, sg, 1, dir);
- clear_bit(i, buffer->dirty);
+ for (i = 0; i < pages; i++) {
+ struct page *page = buffer->pages[i];
+
+ if (ion_buffer_page_is_dirty(page))
+ __dma_page_cpu_to_dev(page, 0, PAGE_SIZE, dir);
+ ion_buffer_page_clean(buffer->pages + i);
}
list_for_each_entry(vma_list, &buffer->vmas, list) {
struct vm_area_struct *vma = vma_list->vma;
@@ -813,21 +862,18 @@ static void ion_buffer_sync_for_device(struct ion_buffer *buffer,
int ion_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct ion_buffer *buffer = vma->vm_private_data;
- struct scatterlist *sg;
- int i;
+ int ret;
mutex_lock(&buffer->lock);
- set_bit(vmf->pgoff, buffer->dirty);
+ ion_buffer_page_dirty(buffer->pages + vmf->pgoff);
- for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) {
- if (i != vmf->pgoff)
- continue;
- dma_sync_sg_for_cpu(NULL, sg, 1, DMA_BIDIRECTIONAL);
- vm_insert_page(vma, (unsigned long)vmf->virtual_address,
- sg_page(sg));
- break;
- }
+ BUG_ON(!buffer->pages || !buffer->pages[vmf->pgoff]);
+ ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address,
+ ion_buffer_page(buffer->pages[vmf->pgoff]));
mutex_unlock(&buffer->lock);
+ if (ret)
+ return VM_FAULT_ERROR;
+
return VM_FAULT_NOPAGE;
}
@@ -939,8 +985,6 @@ static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, size_t start,
mutex_unlock(&buffer->lock);
if (IS_ERR(vaddr))
return PTR_ERR(vaddr);
- if (!vaddr)
- return -ENOMEM;
return 0;
}
@@ -1017,9 +1061,10 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd)
struct dma_buf *dmabuf;
struct ion_buffer *buffer;
struct ion_handle *handle;
+ int ret;
dmabuf = dma_buf_get(fd);
- if (IS_ERR_OR_NULL(dmabuf))
+ if (IS_ERR(dmabuf))
return ERR_PTR(PTR_ERR(dmabuf));
/* if this memory came from ion */
@@ -1034,14 +1079,18 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd)
mutex_lock(&client->lock);
/* if a handle exists for this buffer just take a reference to it */
handle = ion_handle_lookup(client, buffer);
- if (!IS_ERR_OR_NULL(handle)) {
+ if (!IS_ERR(handle)) {
ion_handle_get(handle);
goto end;
}
handle = ion_handle_create(client, buffer);
- if (IS_ERR_OR_NULL(handle))
+ if (IS_ERR(handle))
goto end;
- ion_handle_add(client, handle);
+ ret = ion_handle_add(client, handle);
+ if (ret) {
+ ion_handle_put(handle);
+ handle = ERR_PTR(ret);
+ }
end:
mutex_unlock(&client->lock);
dma_buf_put(dmabuf);
@@ -1055,7 +1104,7 @@ static int ion_sync_for_device(struct ion_client *client, int fd)
struct ion_buffer *buffer;
dmabuf = dma_buf_get(fd);
- if (IS_ERR_OR_NULL(dmabuf))
+ if (IS_ERR(dmabuf))
return PTR_ERR(dmabuf);
/* if this memory came from ion */
@@ -1081,17 +1130,20 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case ION_IOC_ALLOC:
{
struct ion_allocation_data data;
+ struct ion_handle *handle;
if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
return -EFAULT;
- data.handle = ion_alloc(client, data.len, data.align,
+ handle = ion_alloc(client, data.len, data.align,
data.heap_id_mask, data.flags);
- if (IS_ERR(data.handle))
- return PTR_ERR(data.handle);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ data.handle = handle->id;
if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
- ion_free(client, data.handle);
+ ion_free(client, handle);
return -EFAULT;
}
break;
@@ -1099,27 +1151,29 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case ION_IOC_FREE:
{
struct ion_handle_data data;
- bool valid;
+ struct ion_handle *handle;
if (copy_from_user(&data, (void __user *)arg,
sizeof(struct ion_handle_data)))
return -EFAULT;
mutex_lock(&client->lock);
- valid = ion_handle_validate(client, data.handle);
+ handle = ion_uhandle_get(client, data.handle);
mutex_unlock(&client->lock);
- if (!valid)
+ if (!handle)
return -EINVAL;
- ion_free(client, data.handle);
+ ion_free(client, handle);
break;
}
case ION_IOC_SHARE:
case ION_IOC_MAP:
{
struct ion_fd_data data;
+ struct ion_handle *handle;
if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
return -EFAULT;
- data.fd = ion_share_dma_buf_fd(client, data.handle);
+ handle = ion_uhandle_get(client, data.handle);
+ data.fd = ion_share_dma_buf_fd(client, handle);
if (copy_to_user((void __user *)arg, &data, sizeof(data)))
return -EFAULT;
if (data.fd < 0)
@@ -1129,15 +1183,17 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case ION_IOC_IMPORT:
{
struct ion_fd_data data;
+ struct ion_handle *handle;
int ret = 0;
if (copy_from_user(&data, (void __user *)arg,
sizeof(struct ion_fd_data)))
return -EFAULT;
- data.handle = ion_import_dma_buf(client, data.fd);
- if (IS_ERR(data.handle)) {
- ret = PTR_ERR(data.handle);
- data.handle = NULL;
- }
+ handle = ion_import_dma_buf(client, data.fd);
+ if (IS_ERR(handle))
+ ret = PTR_ERR(handle);
+ else
+ data.handle = handle->id;
+
if (copy_to_user((void __user *)arg, &data,
sizeof(struct ion_fd_data)))
return -EFAULT;
@@ -1189,7 +1245,7 @@ static int ion_open(struct inode *inode, struct file *file)
pr_debug("%s: %d\n", __func__, __LINE__);
client = ion_client_create(dev, "user");
- if (IS_ERR_OR_NULL(client))
+ if (IS_ERR(client))
return PTR_ERR(client);
file->private_data = client;
@@ -1201,6 +1257,7 @@ static const struct file_operations ion_fops = {
.open = ion_open,
.release = ion_release,
.unlocked_ioctl = ion_ioctl,
+ .compat_ioctl = compat_ion_ioctl,
};
static size_t ion_debug_heap_total(struct ion_client *client,
@@ -1271,6 +1328,9 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused)
seq_printf(s, "%16.s %16u\n", "total orphaned",
total_orphaned_size);
seq_printf(s, "%16.s %16u\n", "total ", total_size);
+ if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
+ seq_printf(s, "%16.s %16u\n", "deferred free",
+ heap->free_list_size);
seq_printf(s, "----------------------------------------------------\n");
if (heap->debug_show)
@@ -1382,7 +1442,7 @@ struct ion_device *ion_device_create(long (*custom_ioctl)
}
idev->debug_root = debugfs_create_dir("ion", NULL);
- if (IS_ERR_OR_NULL(idev->debug_root))
+ if (!idev->debug_root)
pr_err("ion: failed to create debug files.\n");
idev->custom_ioctl = custom_ioctl;
diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c
index ce8d311968f6..86f35545eaf7 100644
--- a/drivers/gpu/ion/ion_carveout_heap.c
+++ b/drivers/gpu/ion/ion_carveout_heap.c
@@ -112,13 +112,18 @@ void ion_carveout_heap_unmap_dma(struct ion_heap *heap,
void *ion_carveout_heap_map_kernel(struct ion_heap *heap,
struct ion_buffer *buffer)
{
+ void *ret;
int mtype = MT_MEMORY_NONCACHED;
if (buffer->flags & ION_FLAG_CACHED)
mtype = MT_MEMORY;
- return __arm_ioremap(buffer->priv_phys, buffer->size,
+ ret = __arm_ioremap(buffer->priv_phys, buffer->size,
mtype);
+ if (ret == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ return ret;
}
void ion_carveout_heap_unmap_kernel(struct ion_heap *heap,
diff --git a/drivers/gpu/ion/ion_chunk_heap.c b/drivers/gpu/ion/ion_chunk_heap.c
index 8c8f5c3c5537..cd01aad6ac2d 100644
--- a/drivers/gpu/ion/ion_chunk_heap.c
+++ b/drivers/gpu/ion/ion_chunk_heap.c
@@ -47,15 +47,15 @@ static int ion_chunk_heap_allocate(struct ion_heap *heap,
struct scatterlist *sg;
int ret, i;
unsigned long num_chunks;
+ unsigned long allocated_size;
if (ion_buffer_fault_user_mappings(buffer))
return -ENOMEM;
- num_chunks = ALIGN(size, chunk_heap->chunk_size) /
- chunk_heap->chunk_size;
- buffer->size = num_chunks * chunk_heap->chunk_size;
+ allocated_size = ALIGN(size, chunk_heap->chunk_size);
+ num_chunks = allocated_size / chunk_heap->chunk_size;
- if (buffer->size > chunk_heap->size - chunk_heap->allocated)
+ if (allocated_size > chunk_heap->size - chunk_heap->allocated)
return -ENOMEM;
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
@@ -78,7 +78,7 @@ static int ion_chunk_heap_allocate(struct ion_heap *heap,
}
buffer->priv_virt = table;
- chunk_heap->allocated += buffer->size;
+ chunk_heap->allocated += allocated_size;
return 0;
err:
sg = table->sgl;
@@ -100,6 +100,9 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer)
struct sg_table *table = buffer->priv_virt;
struct scatterlist *sg;
int i;
+ unsigned long allocated_size;
+
+ allocated_size = ALIGN(buffer->size, chunk_heap->chunk_size);
ion_heap_buffer_zero(buffer);
@@ -111,7 +114,7 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer)
gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)),
sg_dma_len(sg));
}
- chunk_heap->allocated -= buffer->size;
+ chunk_heap->allocated -= allocated_size;
sg_free_table(table);
kfree(table);
}
diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c
new file mode 100644
index 000000000000..1eaa8c11e04c
--- /dev/null
+++ b/drivers/gpu/ion/ion_cma_heap.c
@@ -0,0 +1,205 @@
+/*
+ * drivers/gpu/ion/ion_cma_heap.c
+ *
+ * Copyright (C) Linaro 2012
+ * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/ion.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+
+/* for ion_heap_ops structure */
+#include "ion_priv.h"
+
+#define ION_CMA_ALLOCATE_FAILED -1
+
+struct ion_cma_heap {
+ struct ion_heap heap;
+ struct device *dev;
+};
+
+#define to_cma_heap(x) container_of(x, struct ion_cma_heap, heap)
+
+struct ion_cma_buffer_info {
+ void *cpu_addr;
+ dma_addr_t handle;
+ struct sg_table *table;
+};
+
+/*
+ * Create scatter-list for the already allocated DMA buffer.
+ * This function could be replaced by dma_common_get_sgtable
+ * as soon as it will avalaible.
+ */
+int ion_cma_get_sgtable(struct device *dev, struct sg_table *sgt,
+ void *cpu_addr, dma_addr_t handle, size_t size)
+{
+ struct page *page = virt_to_page(cpu_addr);
+ int ret;
+
+ ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+ if (unlikely(ret))
+ return ret;
+
+ sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
+ return 0;
+}
+
+/* ION CMA heap operations functions */
+static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
+ unsigned long len, unsigned long align,
+ unsigned long flags)
+{
+ struct ion_cma_heap *cma_heap = to_cma_heap(heap);
+ struct device *dev = cma_heap->dev;
+ struct ion_cma_buffer_info *info;
+
+ dev_dbg(dev, "Request buffer allocation len %ld\n", len);
+
+ info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL);
+ if (!info) {
+ dev_err(dev, "Can't allocate buffer info\n");
+ return ION_CMA_ALLOCATE_FAILED;
+ }
+
+ info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle), 0);
+
+ if (!info->cpu_addr) {
+ dev_err(dev, "Fail to allocate buffer\n");
+ goto err;
+ }
+
+ info->table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!info->table) {
+ dev_err(dev, "Fail to allocate sg table\n");
+ goto free_mem;
+ }
+
+ if (ion_cma_get_sgtable
+ (dev, info->table, info->cpu_addr, info->handle, len))
+ goto free_table;
+ /* keep this for memory release */
+ buffer->priv_virt = info;
+ dev_dbg(dev, "Allocate buffer %p\n", buffer);
+ return 0;
+
+free_table:
+ kfree(info->table);
+free_mem:
+ dma_free_coherent(dev, len, info->cpu_addr, info->handle);
+err:
+ kfree(info);
+ return ION_CMA_ALLOCATE_FAILED;
+}
+
+static void ion_cma_free(struct ion_buffer *buffer)
+{
+ struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap);
+ struct device *dev = cma_heap->dev;
+ struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+ dev_dbg(dev, "Release buffer %p\n", buffer);
+ /* release memory */
+ dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle);
+ /* release sg table */
+ sg_free_table(info->table);
+ kfree(info->table);
+ kfree(info);
+}
+
+/* return physical address in addr */
+static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer,
+ ion_phys_addr_t *addr, size_t *len)
+{
+ struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap);
+ struct device *dev = cma_heap->dev;
+ struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+ dev_dbg(dev, "Return buffer %p physical address 0x%x\n", buffer,
+ info->handle);
+
+ *addr = info->handle;
+ *len = buffer->size;
+
+ return 0;
+}
+
+struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+ return info->table;
+}
+
+void ion_cma_heap_unmap_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ return;
+}
+
+static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer,
+ struct vm_area_struct *vma)
+{
+ struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap);
+ struct device *dev = cma_heap->dev;
+ struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+ return dma_mmap_coherent(dev, vma, info->cpu_addr, info->handle,
+ buffer->size);
+}
+
+void *ion_cma_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer)
+{
+ struct ion_cma_buffer_info *info = buffer->priv_virt;
+ /* kernel memory mapping has been done at allocation time */
+ return info->cpu_addr;
+}
+
+static struct ion_heap_ops ion_cma_ops = {
+ .allocate = ion_cma_allocate,
+ .free = ion_cma_free,
+ .map_dma = ion_cma_heap_map_dma,
+ .unmap_dma = ion_cma_heap_unmap_dma,
+ .phys = ion_cma_phys,
+ .map_user = ion_cma_mmap,
+ .map_kernel = ion_cma_map_kernel,
+};
+
+struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data)
+{
+ struct ion_cma_heap *cma_heap;
+
+ cma_heap = kzalloc(sizeof(struct ion_cma_heap), GFP_KERNEL);
+
+ if (!cma_heap)
+ return ERR_PTR(-ENOMEM);
+
+ cma_heap->heap.ops = &ion_cma_ops;
+ /* get device from private heaps data, later it will be
+ * used to make the link with reserved CMA memory */
+ cma_heap->dev = data->priv;
+ cma_heap->heap.type = ION_HEAP_TYPE_DMA;
+ return &cma_heap->heap;
+}
+
+void ion_cma_heap_destroy(struct ion_heap *heap)
+{
+ struct ion_cma_heap *cma_heap = to_cma_heap(heap);
+
+ kfree(cma_heap);
+}
diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c
index 4a16aa27384c..786302de7ed0 100644
--- a/drivers/gpu/ion/ion_heap.c
+++ b/drivers/gpu/ion/ion_heap.c
@@ -56,6 +56,9 @@ void *ion_heap_map_kernel(struct ion_heap *heap,
vaddr = vmap(pages, npages, VM_MAP, pgprot);
vfree(pages);
+ if (vaddr == NULL)
+ return ERR_PTR(-ENOMEM);
+
return vaddr;
}
@@ -134,8 +137,22 @@ end:
return ret;
}
-void ion_heap_free_page(struct ion_buffer *buffer, struct page *page,
- unsigned int order)
+struct page *ion_heap_alloc_pages(struct ion_buffer *buffer, gfp_t gfp_flags,
+ unsigned int order)
+{
+ struct page *page = alloc_pages(gfp_flags, order);
+
+ if (!page)
+ return page;
+
+ if (ion_buffer_fault_user_mappings(buffer))
+ split_page(page, order);
+
+ return page;
+}
+
+void ion_heap_free_pages(struct ion_buffer *buffer, struct page *page,
+ unsigned int order)
{
int i;
@@ -254,6 +271,9 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
case ION_HEAP_TYPE_CHUNK:
heap = ion_chunk_heap_create(heap_data);
break;
+ case ION_HEAP_TYPE_DMA:
+ heap = ion_cma_heap_create(heap_data);
+ break;
default:
pr_err("%s: Invalid heap type %d\n", __func__,
heap_data->type);
@@ -290,6 +310,9 @@ void ion_heap_destroy(struct ion_heap *heap)
case ION_HEAP_TYPE_CHUNK:
ion_chunk_heap_destroy(heap);
break;
+ case ION_HEAP_TYPE_DMA:
+ ion_cma_heap_destroy(heap);
+ break;
default:
pr_err("%s: Invalid heap type %d\n", __func__,
heap->type);
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index f9bccbc131ed..32461e946735 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -45,9 +45,8 @@ struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
* @vaddr: the kenrel mapping if kmap_cnt is not zero
* @dmap_cnt: number of times the buffer is mapped for dma
* @sg_table: the sg table for the buffer if dmap_cnt is not zero
- * @dirty: bitmask representing which pages of this buffer have
- * been dirtied by the cpu and need cache maintenance
- * before dma
+ * @pages: flat array of pages in the buffer -- used by fault
+ * handler and only valid for buffers that are faulted in
* @vmas: list of vma's mapping this buffer
* @handle_count: count of handles referencing this buffer
* @task_comm: taskcomm of last client to reference this buffer in a
@@ -74,7 +73,7 @@ struct ion_buffer {
void *vaddr;
int dmap_cnt;
struct sg_table *sg_table;
- unsigned long *dirty;
+ struct page **pages;
struct list_head vmas;
/* used to track orphaned buffers */
int handle_count;
@@ -94,6 +93,9 @@ void ion_buffer_destroy(struct ion_buffer *buffer);
* @map_kernel map memory to the kernel
* @unmap_kernel unmap memory to the kernel
* @map_user map memory to userspace
+ *
+ * allocate, phys, and map_user return 0 on success, -errno on error.
+ * map_dma and map_kernel return pointer on success, ERR_PTR on error.
*/
struct ion_heap_ops {
int (*allocate) (struct ion_heap *heap,
@@ -213,6 +215,19 @@ int ion_heap_map_user(struct ion_heap *, struct ion_buffer *,
int ion_heap_buffer_zero(struct ion_buffer *buffer);
/**
+ * ion_heap_alloc_pages - allocate pages from alloc_pages
+ * @buffer: the buffer to allocate for, used to extract the flags
+ * @gfp_flags: the gfp_t for the allocation
+ * @order: the order of the allocatoin
+ *
+ * This funciton allocations from alloc pages and also does any other
+ * necessary operations based on the buffer->flags. For buffers which
+ * will be faulted in the pages are split using split_page
+ */
+struct page *ion_heap_alloc_pages(struct ion_buffer *buffer, gfp_t gfp_flags,
+ unsigned int order);
+
+/**
* ion_heap_init_deferred_free -- initialize deferred free functionality
* @heap: the heap
*
@@ -269,6 +284,9 @@ void ion_carveout_heap_destroy(struct ion_heap *);
struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *);
void ion_chunk_heap_destroy(struct ion_heap *);
+struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *);
+void ion_cma_heap_destroy(struct ion_heap *);
+
/**
* kernel api to allocate/free from carveout -- used when carveout is
* used to back an architecture specific custom heap
diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c
index e101db5da5b4..5fe81a76f2f5 100644
--- a/drivers/gpu/ion/ion_system_heap.c
+++ b/drivers/gpu/ion/ion_system_heap.c
@@ -64,7 +64,6 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap,
unsigned long order)
{
bool cached = ion_buffer_cached(buffer);
- bool split_pages = ion_buffer_fault_user_mappings(buffer);
struct ion_page_pool *pool = heap->pools[order_to_index(order)];
struct page *page;
@@ -75,7 +74,7 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap,
if (order > 4)
gfp_flags = high_order_gfp_flags;
- page = alloc_pages(gfp_flags, order);
+ page = ion_heap_alloc_pages(buffer, gfp_flags, order);
if (!page)
return 0;
arm_dma_ops.sync_single_for_device(NULL,
@@ -85,8 +84,6 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap,
if (!page)
return 0;
- if (split_pages)
- split_page(page, order);
return page;
}
@@ -153,7 +150,6 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
int i = 0;
long size_remaining = PAGE_ALIGN(size);
unsigned int max_order = orders[0];
- bool split_pages = ion_buffer_fault_user_mappings(buffer);
INIT_LIST_HEAD(&pages);
while (size_remaining > 0) {
@@ -170,28 +166,15 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
if (!table)
goto err;
- if (split_pages)
- ret = sg_alloc_table(table, PAGE_ALIGN(size) / PAGE_SIZE,
- GFP_KERNEL);
- else
- ret = sg_alloc_table(table, i, GFP_KERNEL);
-
+ ret = sg_alloc_table(table, i, GFP_KERNEL);
if (ret)
goto err1;
sg = table->sgl;
list_for_each_entry_safe(info, tmp_info, &pages, list) {
struct page *page = info->page;
- if (split_pages) {
- for (i = 0; i < (1 << info->order); i++) {
- sg_set_page(sg, page + i, PAGE_SIZE, 0);
- sg = sg_next(sg);
- }
- } else {
- sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE,
- 0);
- sg = sg_next(sg);
- }
+ sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE, 0);
+ sg = sg_next(sg);
list_del(&info->list);
kfree(info);
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 402f48689943..e04bf0ef93a9 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -63,6 +63,8 @@ struct hid_report *hid_register_report(struct hid_device *device, unsigned type,
struct hid_report_enum *report_enum = device->report_enum + type;
struct hid_report *report;
+ if (id >= HID_MAX_IDS)
+ return NULL;
if (report_enum->report_id_hash[id])
return report_enum->report_id_hash[id];
@@ -92,7 +94,6 @@ EXPORT_SYMBOL_GPL(hid_register_report);
static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values)
{
struct hid_field *field;
- int i;
if (report->maxfield == HID_MAX_FIELDS) {
hid_err(report->device, "too many fields in report\n");
@@ -111,9 +112,6 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
field->value = (s32 *)(field->usage + usages);
field->report = report;
- for (i = 0; i < usages; i++)
- field->usage[i].usage_index = i;
-
return field;
}
@@ -224,9 +222,9 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
{
struct hid_report *report;
struct hid_field *field;
- int usages;
+ unsigned usages;
unsigned offset;
- int i;
+ unsigned i;
report = hid_register_report(parser->device, report_type, parser->global.report_id);
if (!report) {
@@ -253,7 +251,8 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
if (!parser->local.usage_index) /* Ignore padding fields */
return 0;
- usages = max_t(int, parser->local.usage_index, parser->global.report_count);
+ usages = max_t(unsigned, parser->local.usage_index,
+ parser->global.report_count);
field = hid_register_field(report, usages, parser->global.report_count);
if (!field)
@@ -264,13 +263,14 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION);
for (i = 0; i < usages; i++) {
- int j = i;
+ unsigned j = i;
/* Duplicate the last usage we parsed if we have excess values */
if (i >= parser->local.usage_index)
j = parser->local.usage_index - 1;
field->usage[i].hid = parser->local.usage[j];
field->usage[i].collection_index =
parser->local.collection_index[j];
+ field->usage[i].usage_index = i;
}
field->maxusage = usages;
@@ -404,8 +404,10 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
case HID_GLOBAL_ITEM_TAG_REPORT_ID:
parser->global.report_id = item_udata(item);
- if (parser->global.report_id == 0) {
- hid_err(parser->device, "report_id 0 is invalid\n");
+ if (parser->global.report_id == 0 ||
+ parser->global.report_id >= HID_MAX_IDS) {
+ hid_err(parser->device, "report_id %u is invalid\n",
+ parser->global.report_id);
return -1;
}
return 0;
@@ -575,7 +577,7 @@ static void hid_close_report(struct hid_device *device)
for (i = 0; i < HID_REPORT_TYPES; i++) {
struct hid_report_enum *report_enum = device->report_enum + i;
- for (j = 0; j < 256; j++) {
+ for (j = 0; j < HID_MAX_IDS; j++) {
struct hid_report *report = report_enum->report_id_hash[j];
if (report)
hid_free_report(report);
@@ -755,6 +757,64 @@ int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size)
}
EXPORT_SYMBOL_GPL(hid_parse_report);
+static const char * const hid_report_names[] = {
+ "HID_INPUT_REPORT",
+ "HID_OUTPUT_REPORT",
+ "HID_FEATURE_REPORT",
+};
+/**
+ * hid_validate_values - validate existing device report's value indexes
+ *
+ * @device: hid device
+ * @type: which report type to examine
+ * @id: which report ID to examine (0 for first)
+ * @field_index: which report field to examine
+ * @report_counts: expected number of values
+ *
+ * Validate the number of values in a given field of a given report, after
+ * parsing.
+ */
+struct hid_report *hid_validate_values(struct hid_device *hid,
+ unsigned int type, unsigned int id,
+ unsigned int field_index,
+ unsigned int report_counts)
+{
+ struct hid_report *report;
+
+ if (type > HID_FEATURE_REPORT) {
+ hid_err(hid, "invalid HID report type %u\n", type);
+ return NULL;
+ }
+
+ if (id >= HID_MAX_IDS) {
+ hid_err(hid, "invalid HID report id %u\n", id);
+ return NULL;
+ }
+
+ /*
+ * Explicitly not using hid_get_report() here since it depends on
+ * ->numbered being checked, which may not always be the case when
+ * drivers go to access report values.
+ */
+ report = hid->report_enum[type].report_id_hash[id];
+ if (!report) {
+ hid_err(hid, "missing %s %u\n", hid_report_names[type], id);
+ return NULL;
+ }
+ if (report->maxfield <= field_index) {
+ hid_err(hid, "not enough fields in %s %u\n",
+ hid_report_names[type], id);
+ return NULL;
+ }
+ if (report->field[field_index]->report_count < report_counts) {
+ hid_err(hid, "not enough values in %s %u field %u\n",
+ hid_report_names[type], id, field_index);
+ return NULL;
+ }
+ return report;
+}
+EXPORT_SYMBOL_GPL(hid_validate_values);
+
/**
* hid_open_report - open a driver-specific device report
*
@@ -1128,7 +1188,8 @@ static void hid_output_field(const struct hid_device *hid,
}
/*
- * Create a report.
+ * Create a report. 'data' has to be allocated using
+ * hid_alloc_report_buf() so that it has proper size.
*/
void hid_output_report(struct hid_report *report, __u8 *data)
@@ -1145,6 +1206,22 @@ void hid_output_report(struct hid_report *report, __u8 *data)
EXPORT_SYMBOL_GPL(hid_output_report);
/*
+ * Allocator for buffer that is going to be passed to hid_output_report()
+ */
+u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags)
+{
+ /*
+ * 7 extra bytes are necessary to achieve proper functionality
+ * of implement() working on 8 byte chunks
+ */
+
+ int len = ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7;
+
+ return kmalloc(len, flags);
+}
+EXPORT_SYMBOL_GPL(hid_alloc_report_buf);
+
+/*
* Set a field value. The report this field belongs to has to be
* created and transferred to the device, to set this value in the
* device.
@@ -1152,7 +1229,12 @@ EXPORT_SYMBOL_GPL(hid_output_report);
int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
{
- unsigned size = field->report_size;
+ unsigned size;
+
+ if (!field)
+ return -1;
+
+ size = field->report_size;
hid_dump_input(field->report->device, field->usage + offset, value);
@@ -1228,7 +1310,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
goto out;
}
- if (hid->claimed != HID_CLAIMED_HIDRAW) {
+ if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
for (a = 0; a < report->maxfield; a++)
hid_input_field(hid, report->field[a], cdata, interrupt);
hdrv = hid->driver;
@@ -1673,6 +1755,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 216888538c50..114fd1661834 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -135,9 +135,9 @@
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256
-#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0291
-#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0292
-#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0293
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
@@ -646,6 +646,7 @@
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16 0x0012
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17 0x0013
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18 0x0014
+#define USB_DEVICE_ID_NTRIG_DUOSENSE 0x1500
#define USB_VENDOR_ID_ONTRAK 0x0a07
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
@@ -704,6 +705,7 @@
#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced
#define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51
#define USB_DEVICE_ID_ROCCAT_KONEPURE 0x2dbe
+#define USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL 0x2db4
#define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22
#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50
#define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 4602d291c9bc..90c718f6eded 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -340,7 +340,7 @@ static int hidinput_get_battery_property(struct power_supply *psy,
{
struct hid_device *dev = container_of(psy, struct hid_device, battery);
int ret = 0;
- __u8 buf[2] = {};
+ __u8 *buf;
switch (prop) {
case POWER_SUPPLY_PROP_PRESENT:
@@ -349,13 +349,20 @@ static int hidinput_get_battery_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_CAPACITY:
+
+ buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ break;
+ }
ret = dev->hid_get_raw_report(dev, dev->battery_report_id,
- buf, sizeof(buf),
+ buf, 2,
dev->battery_report_type);
if (ret != 2) {
if (ret >= 0)
ret = -EINVAL;
+ kfree(buf);
break;
}
@@ -364,6 +371,7 @@ static int hidinput_get_battery_property(struct power_supply *psy,
buf[1] <= dev->battery_max)
val->intval = (100 * (buf[1] - dev->battery_min)) /
(dev->battery_max - dev->battery_min);
+ kfree(buf);
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
@@ -477,6 +485,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
if (field->flags & HID_MAIN_ITEM_CONSTANT)
goto ignore;
+ /* Ignore if report count is out of bounds. */
+ if (field->report_count < 1)
+ goto ignore;
+
/* only LED usages are supported in output fields */
if (field->report_type == HID_OUTPUT_REPORT &&
(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
@@ -1155,7 +1167,11 @@ static void report_features(struct hid_device *hid)
rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
list_for_each_entry(rep, &rep_enum->report_list, list)
- for (i = 0; i < rep->maxfield; i++)
+ for (i = 0; i < rep->maxfield; i++) {
+ /* Ignore if report count is out of bounds. */
+ if (rep->field[i]->report_count < 1)
+ continue;
+
for (j = 0; j < rep->field[i]->maxusage; j++) {
/* Verify if Battery Strength feature is available */
hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]);
@@ -1164,6 +1180,7 @@ static void report_features(struct hid_device *hid)
drv->feature_mapping(hid, rep->field[i],
rep->field[i]->usage + j);
}
+ }
}
static struct hid_input *hidinput_allocate(struct hid_device *hid)
diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c
index 07837f5a4eb8..31cf29a6ba17 100644
--- a/drivers/hid/hid-lenovo-tpkbd.c
+++ b/drivers/hid/hid-lenovo-tpkbd.c
@@ -339,7 +339,15 @@ static int tpkbd_probe_tp(struct hid_device *hdev)
struct tpkbd_data_pointer *data_pointer;
size_t name_sz = strlen(dev_name(dev)) + 16;
char *name_mute, *name_micmute;
- int ret;
+ int i, ret;
+
+ /* Validate required reports. */
+ for (i = 0; i < 4; i++) {
+ if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1))
+ return -ENODEV;
+ }
+ if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2))
+ return -ENODEV;
if (sysfs_create_group(&hdev->dev.kobj,
&tpkbd_attr_group_pointer)) {
@@ -406,22 +414,27 @@ static int tpkbd_probe(struct hid_device *hdev,
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "hid_parse failed\n");
- goto err_free;
+ goto err;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "hid_hw_start failed\n");
- goto err_free;
+ goto err;
}
uhdev = (struct usbhid_device *) hdev->driver_data;
- if (uhdev->ifnum == 1)
- return tpkbd_probe_tp(hdev);
+ if (uhdev->ifnum == 1) {
+ ret = tpkbd_probe_tp(hdev);
+ if (ret)
+ goto err_hid;
+ }
return 0;
-err_free:
+err_hid:
+ hid_hw_stop(hdev);
+err:
return ret;
}
diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c
index b3cd1507dda2..1a42eaa6ca02 100644
--- a/drivers/hid/hid-lg2ff.c
+++ b/drivers/hid/hid-lg2ff.c
@@ -64,26 +64,13 @@ int lg2ff_init(struct hid_device *hid)
struct hid_report *report;
struct hid_input *hidinput = list_entry(hid->inputs.next,
struct hid_input, list);
- struct list_head *report_list =
- &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
int error;
- if (list_empty(report_list)) {
- hid_err(hid, "no output report found\n");
+ /* Check that the report looks ok */
+ report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7);
+ if (!report)
return -ENODEV;
- }
-
- report = list_entry(report_list->next, struct hid_report, list);
-
- if (report->maxfield < 1) {
- hid_err(hid, "output report is empty\n");
- return -ENODEV;
- }
- if (report->field[0]->report_count < 7) {
- hid_err(hid, "not enough values in the field\n");
- return -ENODEV;
- }
lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL);
if (!lg2ff)
diff --git a/drivers/hid/hid-lg3ff.c b/drivers/hid/hid-lg3ff.c
index e52f181f6aa1..8c2da183d3bc 100644
--- a/drivers/hid/hid-lg3ff.c
+++ b/drivers/hid/hid-lg3ff.c
@@ -66,10 +66,11 @@ static int hid_lg3ff_play(struct input_dev *dev, void *data,
int x, y;
/*
- * Maxusage should always be 63 (maximum fields)
- * likely a better way to ensure this data is clean
+ * Available values in the field should always be 63, but we only use up to
+ * 35. Instead, clear the entire area, however big it is.
*/
- memset(report->field[0]->value, 0, sizeof(__s32)*report->field[0]->maxusage);
+ memset(report->field[0]->value, 0,
+ sizeof(__s32) * report->field[0]->report_count);
switch (effect->type) {
case FF_CONSTANT:
@@ -129,32 +130,14 @@ static const signed short ff3_joystick_ac[] = {
int lg3ff_init(struct hid_device *hid)
{
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
- struct hid_report *report;
- struct hid_field *field;
const signed short *ff_bits = ff3_joystick_ac;
int error;
int i;
- /* Find the report to use */
- if (list_empty(report_list)) {
- hid_err(hid, "No output report found\n");
- return -1;
- }
-
/* Check that the report looks ok */
- report = list_entry(report_list->next, struct hid_report, list);
- if (!report) {
- hid_err(hid, "NULL output report\n");
- return -1;
- }
-
- field = report->field[0];
- if (!field) {
- hid_err(hid, "NULL field\n");
- return -1;
- }
+ if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35))
+ return -ENODEV;
/* Assume single fixed device G940 */
for (i = 0; ff_bits[i] >= 0; i++)
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index 0ddae2a00d59..8782fe1aaa07 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -484,34 +484,16 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
int lg4ff_init(struct hid_device *hid)
{
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
- struct hid_report *report;
- struct hid_field *field;
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
struct usb_device_descriptor *udesc;
int error, i, j;
__u16 bcdDevice, rev_maj, rev_min;
- /* Find the report to use */
- if (list_empty(report_list)) {
- hid_err(hid, "No output report found\n");
- return -1;
- }
-
/* Check that the report looks ok */
- report = list_entry(report_list->next, struct hid_report, list);
- if (!report) {
- hid_err(hid, "NULL output report\n");
+ if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
return -1;
- }
-
- field = report->field[0];
- if (!field) {
- hid_err(hid, "NULL field\n");
- return -1;
- }
/* Check what wheel has been connected */
for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c
index d7ea8c845b40..e1394af0ae7b 100644
--- a/drivers/hid/hid-lgff.c
+++ b/drivers/hid/hid-lgff.c
@@ -128,27 +128,14 @@ static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude)
int lgff_init(struct hid_device* hid)
{
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
- struct hid_report *report;
- struct hid_field *field;
const signed short *ff_bits = ff_joystick;
int error;
int i;
- /* Find the report to use */
- if (list_empty(report_list)) {
- hid_err(hid, "No output report found\n");
- return -1;
- }
-
/* Check that the report looks ok */
- report = list_entry(report_list->next, struct hid_report, list);
- field = report->field[0];
- if (!field) {
- hid_err(hid, "NULL field\n");
- return -1;
- }
+ if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
+ return -ENODEV;
for (i = 0; i < ARRAY_SIZE(devices); i++) {
if (dev->id.vendor == devices[i].idVendor &&
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 5207591a598c..4f762bc9456a 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -421,7 +421,7 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
struct hid_report *report;
struct hid_report_enum *output_report_enum;
u8 *data = (u8 *)(&dj_report->device_index);
- int i;
+ unsigned int i;
output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT];
report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
@@ -431,7 +431,7 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
return -ENODEV;
}
- for (i = 0; i < report->field[0]->report_count; i++)
+ for (i = 0; i < DJREPORT_SHORT_LENGTH - 1; i++)
report->field[0]->value[i] = data[i];
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
@@ -574,7 +574,7 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
struct hid_field *field;
struct hid_report *report;
- unsigned char data[8];
+ unsigned char *data;
int offset;
dbg_hid("%s: %s, type:%d | code:%d | value:%d\n",
@@ -590,6 +590,13 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
return -1;
}
hid_set_field(field, offset, value);
+
+ data = hid_alloc_report_buf(field->report, GFP_KERNEL);
+ if (!data) {
+ dev_warn(&dev->dev, "failed to allocate report buf memory\n");
+ return -1;
+ }
+
hid_output_report(field->report, &data[0]);
output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT];
@@ -600,8 +607,9 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT);
- return 0;
+ kfree(data);
+ return 0;
}
static int logi_dj_ll_start(struct hid_device *hid)
@@ -738,6 +746,12 @@ static int logi_dj_probe(struct hid_device *hdev,
goto hid_parse_fail;
}
+ if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, REPORT_ID_DJ_SHORT,
+ 0, DJREPORT_SHORT_LENGTH - 1)) {
+ retval = -ENODEV;
+ goto hid_parse_fail;
+ }
+
/* Starts the usb device and connects to upper interfaces hiddev and
* hidraw */
retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index f4b77f4d5fb8..bb6fe3ee0030 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -101,9 +101,9 @@ struct mt_device {
unsigned last_slot_field; /* the last field of a slot */
unsigned mt_report_id; /* the report ID of the multitouch device */
unsigned pen_report_id; /* the report ID of the pen device */
- __s8 inputmode; /* InputMode HID feature, -1 if non-existent */
- __s8 inputmode_index; /* InputMode HID feature index in the report */
- __s8 maxcontact_report_id; /* Maximum Contact Number HID feature,
+ __s16 inputmode; /* InputMode HID feature, -1 if non-existent */
+ __s16 inputmode_index; /* InputMode HID feature index in the report */
+ __s16 maxcontact_report_id; /* Maximum Contact Number HID feature,
-1 if non-existent */
__u8 num_received; /* how many contacts we received */
__u8 num_expected; /* expected last contact index */
@@ -317,20 +317,18 @@ static void mt_feature_mapping(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage)
{
struct mt_device *td = hid_get_drvdata(hdev);
- int i;
switch (usage->hid) {
case HID_DG_INPUTMODE:
- td->inputmode = field->report->id;
- td->inputmode_index = 0; /* has to be updated below */
-
- for (i=0; i < field->maxusage; i++) {
- if (field->usage[i].hid == usage->hid) {
- td->inputmode_index = i;
- break;
- }
+ /* Ignore if value index is out of bounds. */
+ if (usage->usage_index >= field->report_count) {
+ dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n");
+ break;
}
+ td->inputmode = field->report->id;
+ td->inputmode_index = usage->usage_index;
+
break;
case HID_DG_CONTACTMAX:
td->maxcontact_report_id = field->report->id;
@@ -546,6 +544,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
mt_store_field(usage, td, hi);
return 1;
case HID_DG_CONTACTCOUNT:
+ /* Ignore if indexes are out of bounds. */
+ if (field->index >= field->report->maxfield ||
+ usage->usage_index >= field->report_count)
+ return 1;
td->cc_index = field->index;
td->cc_value_index = usage->usage_index;
return 1;
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index ef95102515e4..5482156ab4de 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -115,7 +115,8 @@ static inline int ntrig_get_mode(struct hid_device *hdev)
struct hid_report *report = hdev->report_enum[HID_FEATURE_REPORT].
report_id_hash[0x0d];
- if (!report)
+ if (!report || report->maxfield < 1 ||
+ report->field[0]->report_count < 1)
return -EINVAL;
hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
index e346038f0f11..59d5eb1e742c 100644
--- a/drivers/hid/hid-picolcd_cir.c
+++ b/drivers/hid/hid-picolcd_cir.c
@@ -145,6 +145,7 @@ void picolcd_exit_cir(struct picolcd_data *data)
struct rc_dev *rdev = data->rc_dev;
data->rc_dev = NULL;
- rc_unregister_device(rdev);
+ if (rdev)
+ rc_unregister_device(rdev);
}
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c
index b48092d0e139..acbb021065ec 100644
--- a/drivers/hid/hid-picolcd_core.c
+++ b/drivers/hid/hid-picolcd_core.c
@@ -290,7 +290,7 @@ static ssize_t picolcd_operation_mode_store(struct device *dev,
buf += 10;
cnt -= 10;
}
- if (!report)
+ if (!report || report->maxfield != 1)
return -EINVAL;
while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))
diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c
index 59ab8e157e6b..024cdf3c2297 100644
--- a/drivers/hid/hid-picolcd_debugfs.c
+++ b/drivers/hid/hid-picolcd_debugfs.c
@@ -394,7 +394,7 @@ static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
void picolcd_debug_out_report(struct picolcd_data *data,
struct hid_device *hdev, struct hid_report *report)
{
- u8 raw_data[70];
+ u8 *raw_data;
int raw_size = (report->size >> 3) + 1;
char *buff;
#define BUFF_SZ 256
@@ -407,20 +407,20 @@ void picolcd_debug_out_report(struct picolcd_data *data,
if (!buff)
return;
- snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ",
- report->id, raw_size);
- hid_debug_event(hdev, buff);
- if (raw_size + 5 > sizeof(raw_data)) {
+ raw_data = hid_alloc_report_buf(report, GFP_ATOMIC);
+ if (!raw_data) {
kfree(buff);
- hid_debug_event(hdev, " TOO BIG\n");
return;
- } else {
- raw_data[0] = report->id;
- hid_output_report(report, raw_data);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
- hid_debug_event(hdev, buff);
}
+ snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ",
+ report->id, raw_size);
+ hid_debug_event(hdev, buff);
+ raw_data[0] = report->id;
+ hid_output_report(report, raw_data);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
+ hid_debug_event(hdev, buff);
+
switch (report->id) {
case REPORT_LED_STATE:
/* 1 data byte with GPO state */
@@ -644,6 +644,7 @@ void picolcd_debug_out_report(struct picolcd_data *data,
break;
}
wake_up_interruptible(&hdev->debug_wait);
+ kfree(raw_data);
kfree(buff);
}
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
index 591f6b22aa94..c930ab8554ea 100644
--- a/drivers/hid/hid-picolcd_fb.c
+++ b/drivers/hid/hid-picolcd_fb.c
@@ -593,10 +593,14 @@ err_nomem:
void picolcd_exit_framebuffer(struct picolcd_data *data)
{
struct fb_info *info = data->fb_info;
- struct picolcd_fb_data *fbdata = info->par;
+ struct picolcd_fb_data *fbdata;
unsigned long flags;
+ if (!info)
+ return;
+
device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
+ fbdata = info->par;
/* disconnect framebuffer from HID dev */
spin_lock_irqsave(&fbdata->lock, flags);
diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c
index d29112fa5cd5..2dcd7d98dbd6 100644
--- a/drivers/hid/hid-pl.c
+++ b/drivers/hid/hid-pl.c
@@ -132,8 +132,14 @@ static int plff_init(struct hid_device *hid)
strong = &report->field[0]->value[2];
weak = &report->field[0]->value[3];
debug("detected single-field device");
- } else if (report->maxfield >= 4 && report->field[0]->maxusage == 1 &&
- report->field[0]->usage[0].hid == (HID_UP_LED | 0x43)) {
+ } else if (report->field[0]->maxusage == 1 &&
+ report->field[0]->usage[0].hid ==
+ (HID_UP_LED | 0x43) &&
+ report->maxfield >= 4 &&
+ report->field[0]->report_count >= 1 &&
+ report->field[1]->report_count >= 1 &&
+ report->field[2]->report_count >= 1 &&
+ report->field[3]->report_count >= 1) {
report->field[0]->value[0] = 0x00;
report->field[1]->value[0] = 0x00;
strong = &report->field[2]->value[0];
diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c
index c79d0b06c143..5850959d48f5 100644
--- a/drivers/hid/hid-roccat-konepure.c
+++ b/drivers/hid/hid-roccat-konepure.c
@@ -262,6 +262,7 @@ static int konepure_raw_event(struct hid_device *hdev,
static const struct hid_device_id konepure_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) },
{ }
};
@@ -300,5 +301,5 @@ module_init(konepure_init);
module_exit(konepure_exit);
MODULE_AUTHOR("Stefan Achatz");
-MODULE_DESCRIPTION("USB Roccat KonePure driver");
+MODULE_DESCRIPTION("USB Roccat KonePure/Optical driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index ca7498107327..aa34755ca205 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -221,7 +221,8 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
mutex_lock(&data->mutex);
report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT);
- if (!report || (field_index >= report->maxfield)) {
+ if (!report || (field_index >= report->maxfield) ||
+ report->field[field_index]->report_count < 1) {
ret = -EINVAL;
goto done_proc;
}
diff --git a/drivers/hid/hid-speedlink.c b/drivers/hid/hid-speedlink.c
index a2f587d004e1..7112f3e832ee 100644
--- a/drivers/hid/hid-speedlink.c
+++ b/drivers/hid/hid-speedlink.c
@@ -3,7 +3,7 @@
* Fixes "jumpy" cursor and removes nonexistent keyboard LEDS from
* the HID descriptor.
*
- * Copyright (c) 2011 Stefan Kriwanek <mail@stefankriwanek.de>
+ * Copyright (c) 2011, 2013 Stefan Kriwanek <dev@stefankriwanek.de>
*/
/*
@@ -46,8 +46,13 @@ static int speedlink_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
/* No other conditions due to usage_table. */
- /* Fix "jumpy" cursor (invalid events sent by device). */
- if (value == 256)
+
+ /* This fixes the "jumpy" cursor occuring due to invalid events sent
+ * by the device. Some devices only send them with value==+256, others
+ * don't. However, catching abs(value)>=256 is restrictive enough not
+ * to interfere with devices that were bug-free (has been tested).
+ */
+ if (abs(value) >= 256)
return 1;
/* Drop useless distance 0 events (on button clicks etc.) as well */
if (value == 0)
diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c
index d16491192112..29f328f411fb 100644
--- a/drivers/hid/hid-steelseries.c
+++ b/drivers/hid/hid-steelseries.c
@@ -249,6 +249,11 @@ static int steelseries_srws1_probe(struct hid_device *hdev,
goto err_free;
}
+ if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) {
+ ret = -ENODEV;
+ goto err_free;
+ }
+
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "hw start failed\n");
diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c
index 6ec28a37c146..a29756c6ca02 100644
--- a/drivers/hid/hid-zpff.c
+++ b/drivers/hid/hid-zpff.c
@@ -68,21 +68,13 @@ static int zpff_init(struct hid_device *hid)
struct hid_report *report;
struct hid_input *hidinput = list_entry(hid->inputs.next,
struct hid_input, list);
- struct list_head *report_list =
- &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
- int error;
+ int i, error;
- if (list_empty(report_list)) {
- hid_err(hid, "no output report found\n");
- return -ENODEV;
- }
-
- report = list_entry(report_list->next, struct hid_report, list);
-
- if (report->maxfield < 4) {
- hid_err(hid, "not enough fields in report\n");
- return -ENODEV;
+ for (i = 0; i < 4; i++) {
+ report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1);
+ if (!report)
+ return -ENODEV;
}
zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL);
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index a7451632ceb4..612a655bc9f0 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -113,7 +113,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
__u8 *buf;
int ret = 0;
- if (!hidraw_table[minor]) {
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
ret = -ENODEV;
goto out;
}
@@ -261,7 +261,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
}
mutex_lock(&minors_lock);
- if (!hidraw_table[minor]) {
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
err = -ENODEV;
goto out_unlock;
}
@@ -302,39 +302,38 @@ static int hidraw_fasync(int fd, struct file *file, int on)
return fasync_helper(fd, file, on, &list->fasync);
}
+static void drop_ref(struct hidraw *hidraw, int exists_bit)
+{
+ if (exists_bit) {
+ hid_hw_close(hidraw->hid);
+ hidraw->exist = 0;
+ if (hidraw->open)
+ wake_up_interruptible(&hidraw->wait);
+ } else {
+ --hidraw->open;
+ }
+
+ if (!hidraw->open && !hidraw->exist) {
+ device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
+ hidraw_table[hidraw->minor] = NULL;
+ kfree(hidraw);
+ }
+}
+
static int hidraw_release(struct inode * inode, struct file * file)
{
unsigned int minor = iminor(inode);
- struct hidraw *dev;
struct hidraw_list *list = file->private_data;
- int ret;
- int i;
mutex_lock(&minors_lock);
- if (!hidraw_table[minor]) {
- ret = -ENODEV;
- goto unlock;
- }
list_del(&list->node);
- dev = hidraw_table[minor];
- if (!--dev->open) {
- if (list->hidraw->exist) {
- hid_hw_power(dev->hid, PM_HINT_NORMAL);
- hid_hw_close(dev->hid);
- } else {
- kfree(list->hidraw);
- }
- }
-
- for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i)
- kfree(list->buffer[i].value);
kfree(list);
- ret = 0;
-unlock:
- mutex_unlock(&minors_lock);
- return ret;
+ drop_ref(hidraw_table[minor], 0);
+
+ mutex_unlock(&minors_lock);
+ return 0;
}
static long hidraw_ioctl(struct file *file, unsigned int cmd,
@@ -539,18 +538,9 @@ void hidraw_disconnect(struct hid_device *hid)
struct hidraw *hidraw = hid->hidraw;
mutex_lock(&minors_lock);
- hidraw->exist = 0;
-
- device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
- hidraw_table[hidraw->minor] = NULL;
+ drop_ref(hidraw, 1);
- if (hidraw->open) {
- hid_hw_close(hid);
- wake_up_interruptible(&hidraw->wait);
- } else {
- kfree(hidraw);
- }
mutex_unlock(&minors_lock);
}
EXPORT_SYMBOL_GPL(hidraw_disconnect);
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index fc307e0422af..145a4cb5c872 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -640,7 +640,7 @@ static const struct file_operations uhid_fops = {
static struct miscdevice uhid_misc = {
.fops = &uhid_fops,
- .minor = MISC_DYNAMIC_MINOR,
+ .minor = UHID_MINOR,
.name = UHID_NAME,
};
@@ -659,3 +659,5 @@ module_exit(uhid_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem");
+MODULE_ALIAS_MISCDEV(UHID_MINOR);
+MODULE_ALIAS("devname:" UHID_NAME);
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 99418285222c..ada164e1b3a1 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -535,7 +535,6 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
{
int head;
struct usbhid_device *usbhid = hid->driver_data;
- int len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
return;
@@ -546,7 +545,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
return;
}
- usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC);
+ usbhid->out[usbhid->outhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC);
if (!usbhid->out[usbhid->outhead].raw_report) {
hid_warn(hid, "output queueing failed\n");
return;
@@ -595,7 +594,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
}
if (dir == USB_DIR_OUT) {
- usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC);
+ usbhid->ctrl[usbhid->ctrlhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC);
if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) {
hid_warn(hid, "control queueing failed\n");
return;
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 19b8360f2330..07345521f421 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -109,6 +109,8 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
+
{ 0, 0 }
};
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 62c2e32e25ef..3288f13d2d87 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -230,6 +230,7 @@ static int send_argument(const char *key)
static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
{
+ u8 status, data = 0;
int i;
if (send_command(cmd) || send_argument(key)) {
@@ -237,6 +238,7 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
return -EIO;
}
+ /* This has no effect on newer (2012) SMCs */
if (send_byte(len, APPLESMC_DATA_PORT)) {
pr_warn("%.4s: read len fail\n", key);
return -EIO;
@@ -250,6 +252,17 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
buffer[i] = inb(APPLESMC_DATA_PORT);
}
+ /* Read the data port until bit0 is cleared */
+ for (i = 0; i < 16; i++) {
+ udelay(APPLESMC_MIN_WAIT);
+ status = inb(APPLESMC_CMD_PORT);
+ if (!(status & 0x01))
+ break;
+ data = inb(APPLESMC_DATA_PORT);
+ }
+ if (i)
+ pr_warn("flushed %d bytes, last value is: %d\n", i, data);
+
return 0;
}
@@ -525,16 +538,25 @@ static int applesmc_init_smcreg_try(void)
{
struct applesmc_registers *s = &smcreg;
bool left_light_sensor, right_light_sensor;
+ unsigned int count;
u8 tmp[1];
int ret;
if (s->init_complete)
return 0;
- ret = read_register_count(&s->key_count);
+ ret = read_register_count(&count);
if (ret)
return ret;
+ if (s->cache && s->key_count != count) {
+ pr_warn("key count changed from %d to %d\n",
+ s->key_count, count);
+ kfree(s->cache);
+ s->cache = NULL;
+ }
+ s->key_count = count;
+
if (!s->cache)
s->cache = kcalloc(s->key_count, sizeof(*s->cache), GFP_KERNEL);
if (!s->cache)
diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c
index cd82eb44e4c4..7c9f053556f2 100644
--- a/drivers/i2c/busses/i2c-ismt.c
+++ b/drivers/i2c/busses/i2c-ismt.c
@@ -393,6 +393,9 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
desc = &priv->hw[priv->head];
+ /* Initialize the DMA buffer */
+ memset(priv->dma_buffer, 0, sizeof(priv->dma_buffer));
+
/* Initialize the descriptor */
memset(desc, 0, sizeof(struct ismt_desc));
desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write);
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index e02f9e36a7b2..b06be8e3bb76 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -941,6 +941,9 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
/*
* ProDB0017052: Clear ARDY bit twice
*/
+ if (stat & OMAP_I2C_STAT_ARDY)
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_ARDY);
+
if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
OMAP_I2C_STAT_AL)) {
omap_i2c_ack_stat(dev, (OMAP_I2C_STAT_RRDY |
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 3f3f0416fbdd..6c66a728a37f 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -1588,7 +1588,7 @@ static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch,
int resp_data_len;
int resp_len;
- resp_data_len = (rsp_code == SRP_TSK_MGMT_SUCCESS) ? 0 : 4;
+ resp_data_len = 4;
resp_len = sizeof(*srp_rsp) + resp_data_len;
srp_rsp = ioctx->ioctx.buf;
@@ -1600,11 +1600,9 @@ static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch,
+ atomic_xchg(&ch->req_lim_delta, 0));
srp_rsp->tag = tag;
- if (rsp_code != SRP_TSK_MGMT_SUCCESS) {
- srp_rsp->flags |= SRP_RSP_FLAG_RSPVALID;
- srp_rsp->resp_data_len = cpu_to_be32(resp_data_len);
- srp_rsp->data[3] = rsp_code;
- }
+ srp_rsp->flags |= SRP_RSP_FLAG_RSPVALID;
+ srp_rsp->resp_data_len = cpu_to_be32(resp_data_len);
+ srp_rsp->data[3] = rsp_code;
return resp_len;
}
@@ -2358,6 +2356,8 @@ static void srpt_release_channel_work(struct work_struct *w)
transport_deregister_session(se_sess);
ch->sess = NULL;
+ ib_destroy_cm_id(ch->cm_id);
+
srpt_destroy_ch_ib(ch);
srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_ring,
@@ -2368,8 +2368,6 @@ static void srpt_release_channel_work(struct work_struct *w)
list_del(&ch->list);
spin_unlock_irq(&sdev->spinlock);
- ib_destroy_cm_id(ch->cm_id);
-
if (ch->release_done)
complete(ch->release_done);
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
index 4ef4d5e198ae..a73f9618b0ad 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -89,9 +89,9 @@
#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a
#define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b
/* MacbookAir6,2 (unibody, June 2013) */
-#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0291
-#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0292
-#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0293
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292
#define BCM5974_DEVICE(prod) { \
.match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index b4f0e28dfa41..fa004b112a89 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -890,56 +890,54 @@ static int dma_pte_clear_range(struct dmar_domain *domain,
return order;
}
+static void dma_pte_free_level(struct dmar_domain *domain, int level,
+ struct dma_pte *pte, unsigned long pfn,
+ unsigned long start_pfn, unsigned long last_pfn)
+{
+ pfn = max(start_pfn, pfn);
+ pte = &pte[pfn_level_offset(pfn, level)];
+
+ do {
+ unsigned long level_pfn;
+ struct dma_pte *level_pte;
+
+ if (!dma_pte_present(pte) || dma_pte_superpage(pte))
+ goto next;
+
+ level_pfn = pfn & level_mask(level - 1);
+ level_pte = phys_to_virt(dma_pte_addr(pte));
+
+ if (level > 2)
+ dma_pte_free_level(domain, level - 1, level_pte,
+ level_pfn, start_pfn, last_pfn);
+
+ /* If range covers entire pagetable, free it */
+ if (!(start_pfn > level_pfn ||
+ last_pfn < level_pfn + level_size(level))) {
+ dma_clear_pte(pte);
+ domain_flush_cache(domain, pte, sizeof(*pte));
+ free_pgtable_page(level_pte);
+ }
+next:
+ pfn += level_size(level);
+ } while (!first_pte_in_page(++pte) && pfn <= last_pfn);
+}
+
/* free page table pages. last level pte should already be cleared */
static void dma_pte_free_pagetable(struct dmar_domain *domain,
unsigned long start_pfn,
unsigned long last_pfn)
{
int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
- struct dma_pte *first_pte, *pte;
- int total = agaw_to_level(domain->agaw);
- int level;
- unsigned long tmp;
- int large_page = 2;
BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width);
BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width);
BUG_ON(start_pfn > last_pfn);
/* We don't need lock here; nobody else touches the iova range */
- level = 2;
- while (level <= total) {
- tmp = align_to_level(start_pfn, level);
-
- /* If we can't even clear one PTE at this level, we're done */
- if (tmp + level_size(level) - 1 > last_pfn)
- return;
-
- do {
- large_page = level;
- first_pte = pte = dma_pfn_level_pte(domain, tmp, level, &large_page);
- if (large_page > level)
- level = large_page + 1;
- if (!pte) {
- tmp = align_to_level(tmp + 1, level + 1);
- continue;
- }
- do {
- if (dma_pte_present(pte)) {
- free_pgtable_page(phys_to_virt(dma_pte_addr(pte)));
- dma_clear_pte(pte);
- }
- pte++;
- tmp += level_size(level);
- } while (!first_pte_in_page(pte) &&
- tmp + level_size(level) - 1 <= last_pfn);
+ dma_pte_free_level(domain, agaw_to_level(domain->agaw),
+ domain->pgd, 0, start_pfn, last_pfn);
- domain_flush_cache(domain, first_pte,
- (void *)pte - (void *)first_pte);
-
- } while (tmp && tmp + level_size(level) - 1 <= last_pfn);
- level++;
- }
/* free pgd */
if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) {
free_pgtable_page(domain->pgd);
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 6f00dfa7d4fd..65bc83747f66 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -41,6 +41,7 @@
#include <linux/slab.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/arm-gic.h>
+#include <trace/events/arm-ipi.h>
#include <asm/irq.h>
#include <asm/exception.h>
@@ -656,8 +657,10 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
raw_spin_lock_irqsave(&irq_controller_lock, flags);
/* Convert our logical CPU mask into a physical one. */
- for_each_cpu(cpu, mask)
+ for_each_cpu(cpu, mask) {
+ trace_arm_ipi_send(irq, cpu);
map |= gic_cpu_map[cpu];
+ }
/*
* Ensure that stores to Normal memory are visible to the
diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c
index 6bd5c679d877..b7d83d6838f3 100644
--- a/drivers/leds/leds-wm831x-status.c
+++ b/drivers/leds/leds-wm831x-status.c
@@ -230,9 +230,9 @@ static int wm831x_status_probe(struct platform_device *pdev)
int id = pdev->id % ARRAY_SIZE(chip_pdata->status);
int ret;
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ res = platform_get_resource(pdev, IORESOURCE_REG, 0);
if (res == NULL) {
- dev_err(&pdev->dev, "No I/O resource\n");
+ dev_err(&pdev->dev, "No register resource\n");
ret = -EINVAL;
goto err;
}
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index c42b14b2304c..6bc016eabdc9 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -499,7 +499,7 @@ struct cached_dev {
*/
atomic_t has_dirty;
- struct ratelimit writeback_rate;
+ struct bch_ratelimit writeback_rate;
struct delayed_work writeback_rate_update;
/*
@@ -508,10 +508,9 @@ struct cached_dev {
*/
sector_t last_read;
- /* Number of writeback bios in flight */
- atomic_t in_flight;
+ /* Limit number of writeback bios in flight */
+ struct semaphore in_flight;
struct closure_with_timer writeback;
- struct closure_waitlist writeback_wait;
struct keybuf writeback_keys;
diff --git a/drivers/md/bcache/bset.c b/drivers/md/bcache/bset.c
index cb4578a327b9..14032e8c7731 100644
--- a/drivers/md/bcache/bset.c
+++ b/drivers/md/bcache/bset.c
@@ -918,28 +918,45 @@ struct bkey *bch_next_recurse_key(struct btree *b, struct bkey *search)
/* Mergesort */
+static void sort_key_next(struct btree_iter *iter,
+ struct btree_iter_set *i)
+{
+ i->k = bkey_next(i->k);
+
+ if (i->k == i->end)
+ *i = iter->data[--iter->used];
+}
+
static void btree_sort_fixup(struct btree_iter *iter)
{
while (iter->used > 1) {
struct btree_iter_set *top = iter->data, *i = top + 1;
- struct bkey *k;
if (iter->used > 2 &&
btree_iter_cmp(i[0], i[1]))
i++;
- for (k = i->k;
- k != i->end && bkey_cmp(top->k, &START_KEY(k)) > 0;
- k = bkey_next(k))
- if (top->k > i->k)
- __bch_cut_front(top->k, k);
- else if (KEY_SIZE(k))
- bch_cut_back(&START_KEY(k), top->k);
-
- if (top->k < i->k || k == i->k)
+ if (bkey_cmp(top->k, &START_KEY(i->k)) <= 0)
break;
- heap_sift(iter, i - top, btree_iter_cmp);
+ if (!KEY_SIZE(i->k)) {
+ sort_key_next(iter, i);
+ heap_sift(iter, i - top, btree_iter_cmp);
+ continue;
+ }
+
+ if (top->k > i->k) {
+ if (bkey_cmp(top->k, i->k) >= 0)
+ sort_key_next(iter, i);
+ else
+ bch_cut_front(top->k, i->k);
+
+ heap_sift(iter, i - top, btree_iter_cmp);
+ } else {
+ /* can't happen because of comparison func */
+ BUG_ON(!bkey_cmp(&START_KEY(top->k), &START_KEY(i->k)));
+ bch_cut_back(&START_KEY(i->k), top->k);
+ }
}
}
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
index 833c590806ba..7d3deab11fce 100644
--- a/drivers/md/bcache/btree.c
+++ b/drivers/md/bcache/btree.c
@@ -633,7 +633,7 @@ static int bch_mca_shrink(struct shrinker *shrink, struct shrink_control *sc)
return mca_can_free(c) * c->btree_pages;
/* Return -1 if we can't do anything right now */
- if (sc->gfp_mask & __GFP_WAIT)
+ if (sc->gfp_mask & __GFP_IO)
mutex_lock(&c->bucket_lock);
else if (!mutex_trylock(&c->bucket_lock))
return -1;
diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c
index b49abb246bb6..151a4ab90dd4 100644
--- a/drivers/md/bcache/journal.c
+++ b/drivers/md/bcache/journal.c
@@ -151,7 +151,8 @@ int bch_journal_read(struct cache_set *c, struct list_head *list,
bitmap_zero(bitmap, SB_JOURNAL_BUCKETS);
pr_debug("%u journal buckets", ca->sb.njournal_buckets);
- /* Read journal buckets ordered by golden ratio hash to quickly
+ /*
+ * Read journal buckets ordered by golden ratio hash to quickly
* find a sequence of buckets with valid journal entries
*/
for (i = 0; i < ca->sb.njournal_buckets; i++) {
@@ -164,18 +165,20 @@ int bch_journal_read(struct cache_set *c, struct list_head *list,
goto bsearch;
}
- /* If that fails, check all the buckets we haven't checked
+ /*
+ * If that fails, check all the buckets we haven't checked
* already
*/
pr_debug("falling back to linear search");
- for (l = 0; l < ca->sb.njournal_buckets; l++) {
- if (test_bit(l, bitmap))
- continue;
-
+ for (l = find_first_zero_bit(bitmap, ca->sb.njournal_buckets);
+ l < ca->sb.njournal_buckets;
+ l = find_next_zero_bit(bitmap, ca->sb.njournal_buckets, l + 1))
if (read_bucket(l))
goto bsearch;
- }
+
+ if (list_empty(list))
+ continue;
bsearch:
/* Binary search */
m = r = find_next_bit(bitmap, ca->sb.njournal_buckets, l + 1);
@@ -195,10 +198,12 @@ bsearch:
r = m;
}
- /* Read buckets in reverse order until we stop finding more
+ /*
+ * Read buckets in reverse order until we stop finding more
* journal entries
*/
- pr_debug("finishing up");
+ pr_debug("finishing up: m %u njournal_buckets %u",
+ m, ca->sb.njournal_buckets);
l = m;
while (1) {
@@ -226,9 +231,10 @@ bsearch:
}
}
- c->journal.seq = list_entry(list->prev,
- struct journal_replay,
- list)->j.seq;
+ if (!list_empty(list))
+ c->journal.seq = list_entry(list->prev,
+ struct journal_replay,
+ list)->j.seq;
return 0;
#undef read_bucket
@@ -425,7 +431,7 @@ static void do_journal_discard(struct cache *ca)
return;
}
- switch (atomic_read(&ja->discard_in_flight) == DISCARD_IN_FLIGHT) {
+ switch (atomic_read(&ja->discard_in_flight)) {
case DISCARD_IN_FLIGHT:
return;
@@ -686,6 +692,7 @@ void bch_journal_meta(struct cache_set *c, struct closure *cl)
if (cl)
BUG_ON(!closure_wait(&w->wait, cl));
+ closure_flush(&c->journal.io);
__journal_try_write(c, true);
}
}
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index afb9a998a737..a30a0f8a41c0 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -1055,17 +1055,19 @@ static void request_write(struct cached_dev *dc, struct search *s)
} else {
trace_bcache_writeback(s->orig_bio);
bch_writeback_add(dc, bio_sectors(bio));
+ s->op.cache_bio = bio;
- if (s->op.flush_journal) {
+ if (bio->bi_rw & REQ_FLUSH) {
/* Also need to send a flush to the backing device */
- s->op.cache_bio = bio_clone_bioset(bio, GFP_NOIO,
- dc->disk.bio_split);
-
- bio->bi_size = 0;
- bio->bi_vcnt = 0;
- closure_bio_submit(bio, cl, s->d);
- } else {
- s->op.cache_bio = bio;
+ struct bio *flush = bio_alloc_bioset(GFP_NOIO, 0,
+ dc->disk.bio_split);
+
+ flush->bi_rw = WRITE_FLUSH;
+ flush->bi_bdev = bio->bi_bdev;
+ flush->bi_end_io = request_endio;
+ flush->bi_private = cl;
+
+ closure_bio_submit(flush, cl, s->d);
}
}
out:
diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
index 4d9cca47e4c6..e9bd6c0cca5b 100644
--- a/drivers/md/bcache/sysfs.c
+++ b/drivers/md/bcache/sysfs.c
@@ -214,7 +214,13 @@ STORE(__cached_dev)
}
if (attr == &sysfs_label) {
- memcpy(dc->sb.label, buf, SB_LABEL_SIZE);
+ if (size > SB_LABEL_SIZE)
+ return -EINVAL;
+ memcpy(dc->sb.label, buf, size);
+ if (size < SB_LABEL_SIZE)
+ dc->sb.label[size] = '\0';
+ if (size && dc->sb.label[size - 1] == '\n')
+ dc->sb.label[size - 1] = '\0';
bch_write_bdev_super(dc, NULL);
if (dc->disk.c) {
memcpy(dc->disk.c->uuids[dc->disk.id].label,
diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
index da3a99e85b1e..38a43f8d36ec 100644
--- a/drivers/md/bcache/util.c
+++ b/drivers/md/bcache/util.c
@@ -190,7 +190,16 @@ void bch_time_stats_update(struct time_stats *stats, uint64_t start_time)
stats->last = now ?: 1;
}
-unsigned bch_next_delay(struct ratelimit *d, uint64_t done)
+/**
+ * bch_next_delay() - increment @d by the amount of work done, and return how
+ * long to delay until the next time to do some work.
+ *
+ * @d - the struct bch_ratelimit to update
+ * @done - the amount of work done, in arbitrary units
+ *
+ * Returns the amount of time to delay by, in jiffies
+ */
+uint64_t bch_next_delay(struct bch_ratelimit *d, uint64_t done)
{
uint64_t now = local_clock();
diff --git a/drivers/md/bcache/util.h b/drivers/md/bcache/util.h
index 577393e38c3a..43fd78affcea 100644
--- a/drivers/md/bcache/util.h
+++ b/drivers/md/bcache/util.h
@@ -452,17 +452,23 @@ read_attribute(name ## _last_ ## frequency_units)
(ewma) >> factor; \
})
-struct ratelimit {
+struct bch_ratelimit {
+ /* Next time we want to do some work, in nanoseconds */
uint64_t next;
+
+ /*
+ * Rate at which we want to do work, in units per nanosecond
+ * The units here correspond to the units passed to bch_next_delay()
+ */
unsigned rate;
};
-static inline void ratelimit_reset(struct ratelimit *d)
+static inline void bch_ratelimit_reset(struct bch_ratelimit *d)
{
d->next = local_clock();
}
-unsigned bch_next_delay(struct ratelimit *d, uint64_t done);
+uint64_t bch_next_delay(struct bch_ratelimit *d, uint64_t done);
#define __DIV_SAFE(n, d, zero) \
({ \
diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c
index 2714ed3991d1..841f0490d4ef 100644
--- a/drivers/md/bcache/writeback.c
+++ b/drivers/md/bcache/writeback.c
@@ -91,11 +91,15 @@ static void update_writeback_rate(struct work_struct *work)
static unsigned writeback_delay(struct cached_dev *dc, unsigned sectors)
{
+ uint64_t ret;
+
if (atomic_read(&dc->disk.detaching) ||
!dc->writeback_percent)
return 0;
- return bch_next_delay(&dc->writeback_rate, sectors * 10000000ULL);
+ ret = bch_next_delay(&dc->writeback_rate, sectors * 10000000ULL);
+
+ return min_t(uint64_t, ret, HZ);
}
/* Background writeback */
@@ -165,7 +169,7 @@ static void refill_dirty(struct closure *cl)
up_write(&dc->writeback_lock);
- ratelimit_reset(&dc->writeback_rate);
+ bch_ratelimit_reset(&dc->writeback_rate);
/* Punt to workqueue only so we don't recurse and blow the stack */
continue_at(cl, read_dirty, dirty_wq);
@@ -246,9 +250,7 @@ static void write_dirty_finish(struct closure *cl)
}
bch_keybuf_del(&dc->writeback_keys, w);
- atomic_dec_bug(&dc->in_flight);
-
- closure_wake_up(&dc->writeback_wait);
+ up(&dc->in_flight);
closure_return_with_destructor(cl, dirty_io_destructor);
}
@@ -278,7 +280,7 @@ static void write_dirty(struct closure *cl)
trace_bcache_write_dirty(&io->bio);
closure_bio_submit(&io->bio, cl, &io->dc->disk);
- continue_at(cl, write_dirty_finish, dirty_wq);
+ continue_at(cl, write_dirty_finish, system_wq);
}
static void read_dirty_endio(struct bio *bio, int error)
@@ -299,7 +301,7 @@ static void read_dirty_submit(struct closure *cl)
trace_bcache_read_dirty(&io->bio);
closure_bio_submit(&io->bio, cl, &io->dc->disk);
- continue_at(cl, write_dirty, dirty_wq);
+ continue_at(cl, write_dirty, system_wq);
}
static void read_dirty(struct closure *cl)
@@ -324,12 +326,8 @@ static void read_dirty(struct closure *cl)
if (delay > 0 &&
(KEY_START(&w->key) != dc->last_read ||
- jiffies_to_msecs(delay) > 50)) {
- w->private = NULL;
-
- closure_delay(&dc->writeback, delay);
- continue_at(cl, read_dirty, dirty_wq);
- }
+ jiffies_to_msecs(delay) > 50))
+ delay = schedule_timeout_uninterruptible(delay);
dc->last_read = KEY_OFFSET(&w->key);
@@ -354,15 +352,10 @@ static void read_dirty(struct closure *cl)
pr_debug("%s", pkey(&w->key));
- closure_call(&io->cl, read_dirty_submit, NULL, &dc->disk.cl);
+ down(&dc->in_flight);
+ closure_call(&io->cl, read_dirty_submit, NULL, cl);
delay = writeback_delay(dc, KEY_SIZE(&w->key));
-
- atomic_inc(&dc->in_flight);
-
- if (!closure_wait_event(&dc->writeback_wait, cl,
- atomic_read(&dc->in_flight) < 64))
- continue_at(cl, read_dirty, dirty_wq);
}
if (0) {
@@ -372,11 +365,16 @@ err:
bch_keybuf_del(&dc->writeback_keys, w);
}
- refill_dirty(cl);
+ /*
+ * Wait for outstanding writeback IOs to finish (and keybuf slots to be
+ * freed) before refilling again
+ */
+ continue_at(cl, refill_dirty, dirty_wq);
}
void bch_cached_dev_writeback_init(struct cached_dev *dc)
{
+ sema_init(&dc->in_flight, 64);
closure_init_unlocked(&dc->writeback);
init_rwsem(&dc->writeback_lock);
@@ -406,7 +404,7 @@ void bch_writeback_exit(void)
int __init bch_writeback_init(void)
{
- dirty_wq = create_singlethread_workqueue("bcache_writeback");
+ dirty_wq = create_workqueue("bcache_writeback");
if (!dirty_wq)
return -ENOMEM;
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 5adede17ddf6..c5b7b3dd91cc 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1284,8 +1284,17 @@ static int do_end_io(struct multipath *m, struct request *clone,
if (!error && !clone->errors)
return 0; /* I/O complete */
- if (error == -EOPNOTSUPP || error == -EREMOTEIO || error == -EILSEQ)
+ if (error == -EOPNOTSUPP || error == -EREMOTEIO || error == -EILSEQ) {
+ if ((clone->cmd_flags & REQ_WRITE_SAME) &&
+ !clone->q->limits.max_write_same_sectors) {
+ struct queue_limits *limits;
+
+ /* device doesn't really support WRITE SAME, disable it */
+ limits = dm_get_queue_limits(dm_table_get_md(m->ti->table));
+ limits->max_write_same_sectors = 0;
+ }
return error;
+ }
if (mpio->pgpath)
fail_path(mpio->pgpath);
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index 1d3fe1a40a9b..2dea49c4279e 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -380,7 +380,7 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size)
static int validate_raid_redundancy(struct raid_set *rs)
{
unsigned i, rebuild_cnt = 0;
- unsigned rebuilds_per_group, copies, d;
+ unsigned rebuilds_per_group = 0, copies, d;
unsigned group_size, last_group_start;
for (i = 0; i < rs->md.raid_disks; i++)
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index 3ac415675b6c..2d2b1b7588d7 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -256,7 +256,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,
*/
INIT_WORK_ONSTACK(&req.work, do_metadata);
queue_work(ps->metadata_wq, &req.work);
- flush_work(&req.work);
+ flush_workqueue(ps->metadata_wq);
return req.result;
}
@@ -269,6 +269,14 @@ static chunk_t area_location(struct pstore *ps, chunk_t area)
return NUM_SNAPSHOT_HDR_CHUNKS + ((ps->exceptions_per_area + 1) * area);
}
+static void skip_metadata(struct pstore *ps)
+{
+ uint32_t stride = ps->exceptions_per_area + 1;
+ chunk_t next_free = ps->next_free;
+ if (sector_div(next_free, stride) == NUM_SNAPSHOT_HDR_CHUNKS)
+ ps->next_free++;
+}
+
/*
* Read or write a metadata area. Remembering to skip the first
* chunk which holds the header.
@@ -502,6 +510,8 @@ static int read_exceptions(struct pstore *ps,
ps->current_area--;
+ skip_metadata(ps);
+
return 0;
}
@@ -616,8 +626,6 @@ static int persistent_prepare_exception(struct dm_exception_store *store,
struct dm_exception *e)
{
struct pstore *ps = get_info(store);
- uint32_t stride;
- chunk_t next_free;
sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev);
/* Is there enough room ? */
@@ -630,10 +638,8 @@ static int persistent_prepare_exception(struct dm_exception_store *store,
* Move onto the next free pending, making sure to take
* into account the location of the metadata chunks.
*/
- stride = (ps->exceptions_per_area + 1);
- next_free = ++ps->next_free;
- if (sector_div(next_free, stride) == 1)
- ps->next_free++;
+ ps->next_free++;
+ skip_metadata(ps);
atomic_inc(&ps->pending_count);
return 0;
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index c434e5aab2df..aec57d76db5d 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -725,17 +725,16 @@ static int calc_max_buckets(void)
*/
static int init_hash_tables(struct dm_snapshot *s)
{
- sector_t hash_size, cow_dev_size, origin_dev_size, max_buckets;
+ sector_t hash_size, cow_dev_size, max_buckets;
/*
* Calculate based on the size of the original volume or
* the COW volume...
*/
cow_dev_size = get_dev_size(s->cow->bdev);
- origin_dev_size = get_dev_size(s->origin->bdev);
max_buckets = calc_max_buckets();
- hash_size = min(origin_dev_size, cow_dev_size) >> s->store->chunk_shift;
+ hash_size = cow_dev_size >> s->store->chunk_shift;
hash_size = min(hash_size, max_buckets);
if (hash_size < 64)
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 33f20103d8d5..1c13071a81be 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2219,6 +2219,17 @@ struct target_type *dm_get_immutable_target_type(struct mapped_device *md)
}
/*
+ * The queue_limits are only valid as long as you have a reference
+ * count on 'md'.
+ */
+struct queue_limits *dm_get_queue_limits(struct mapped_device *md)
+{
+ BUG_ON(!atomic_read(&md->holders));
+ return &md->queue->limits;
+}
+EXPORT_SYMBOL_GPL(dm_get_queue_limits);
+
+/*
* Fully initialize a request-based queue (->elevator, ->request_fn, etc).
*/
static int dm_init_request_based_queue(struct mapped_device *md)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 51f0345a4ba4..d78f1fffab01 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -8072,6 +8072,7 @@ static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
u64 *p;
int lo, hi;
int rv = 1;
+ unsigned long flags;
if (bb->shift < 0)
/* badblocks are disabled */
@@ -8086,7 +8087,7 @@ static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
sectors = next - s;
}
- write_seqlock_irq(&bb->lock);
+ write_seqlock_irqsave(&bb->lock, flags);
p = bb->page;
lo = 0;
@@ -8202,7 +8203,7 @@ static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
bb->changed = 1;
if (!acknowledged)
bb->unacked_exist = 1;
- write_sequnlock_irq(&bb->lock);
+ write_sequnlock_irqrestore(&bb->lock, flags);
return rv;
}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 6f4824426e86..afaa5d425e9a 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1479,6 +1479,7 @@ static int raid1_spare_active(struct mddev *mddev)
}
}
if (rdev
+ && rdev->recovery_offset == MaxSector
&& !test_bit(Faulty, &rdev->flags)
&& !test_and_set_bit(In_sync, &rdev->flags)) {
count++;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 081bb3345353..0add86821755 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1762,6 +1762,7 @@ static int raid10_spare_active(struct mddev *mddev)
}
sysfs_notify_dirent_safe(tmp->replacement->sysfs_state);
} else if (tmp->rdev
+ && tmp->rdev->recovery_offset == MaxSector
&& !test_bit(Faulty, &tmp->rdev->flags)
&& !test_and_set_bit(In_sync, &tmp->rdev->flags)) {
count++;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index a35b846af4f8..4bed5454b8dc 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -668,6 +668,12 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
bi->bi_io_vec[0].bv_offset = 0;
bi->bi_size = STRIPE_SIZE;
+ /*
+ * If this is discard request, set bi_vcnt 0. We don't
+ * want to confuse SCSI because SCSI will replace payload
+ */
+ if (rw & REQ_DISCARD)
+ bi->bi_vcnt = 0;
if (rrdev)
set_bit(R5_DOUBLE_LOCKED, &sh->dev[i].flags);
@@ -706,6 +712,12 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
rbi->bi_io_vec[0].bv_len = STRIPE_SIZE;
rbi->bi_io_vec[0].bv_offset = 0;
rbi->bi_size = STRIPE_SIZE;
+ /*
+ * If this is discard request, set bi_vcnt 0. We don't
+ * want to confuse SCSI because SCSI will replace payload
+ */
+ if (rw & REQ_DISCARD)
+ rbi->bi_vcnt = 0;
if (conf->mddev->gendisk)
trace_block_bio_remap(bdev_get_queue(rbi->bi_bdev),
rbi, disk_devt(conf->mddev->gendisk),
@@ -2800,6 +2812,14 @@ static void handle_stripe_clean_event(struct r5conf *conf,
}
/* now that discard is done we can proceed with any sync */
clear_bit(STRIPE_DISCARD, &sh->state);
+ /*
+ * SCSI discard will change some bio fields and the stripe has
+ * no updated data, so remove it from hash list and the stripe
+ * will be reinitialized
+ */
+ spin_lock_irq(&conf->device_lock);
+ remove_hash(sh);
+ spin_unlock_irq(&conf->device_lock);
if (test_bit(STRIPE_SYNC_REQUESTED, &sh->state))
set_bit(STRIPE_HANDLE, &sh->state);
diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c
index 297f1b2f9a32..8df1aea5fbff 100644
--- a/drivers/media/common/siano/smsdvb-main.c
+++ b/drivers/media/common/siano/smsdvb-main.c
@@ -275,7 +275,8 @@ static void smsdvb_update_per_slices(struct smsdvb_client_t *client,
/* Legacy PER/BER */
tmp = p->ets_packets * 65535;
- do_div(tmp, p->ts_packets + p->ets_packets);
+ if (p->ts_packets + p->ets_packets)
+ do_div(tmp, p->ts_packets + p->ets_packets);
client->legacy_per = tmp;
}
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index 856374bd3676..2c7217fb1415 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -157,7 +157,6 @@ static struct regdata mb86a20s_init2[] = {
{ 0x45, 0x04 }, /* CN symbol 4 */
{ 0x48, 0x04 }, /* CN manual mode */
- { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */
{ 0x50, 0xd6 }, { 0x51, 0x1f },
{ 0x50, 0xd2 }, { 0x51, 0x03 },
{ 0x50, 0xd7 }, { 0x51, 0xbf },
@@ -1860,16 +1859,15 @@ static int mb86a20s_initfe(struct dvb_frontend *fe)
dev_dbg(&state->i2c->dev, "%s: IF=%d, IF reg=0x%06llx\n",
__func__, state->if_freq, (long long)pll);
- if (!state->config->is_serial) {
+ if (!state->config->is_serial)
regD5 &= ~1;
- rc = mb86a20s_writereg(state, 0x50, 0xd5);
- if (rc < 0)
- goto err;
- rc = mb86a20s_writereg(state, 0x51, regD5);
- if (rc < 0)
- goto err;
- }
+ rc = mb86a20s_writereg(state, 0x50, 0xd5);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x51, regD5);
+ if (rc < 0)
+ goto err;
rc = mb86a20s_writeregdata(state, mb86a20s_init2);
if (rc < 0)
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index d4e2ed3f27e5..53196f1366f3 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -1,6 +1,7 @@
+if PCI && MEDIA_SUPPORT
+
menuconfig MEDIA_PCI_SUPPORT
bool "Media PCI Adapters"
- depends on PCI && MEDIA_SUPPORT
help
Enable media drivers for PCI/PCIe bus.
If you have such devices, say Y.
@@ -45,3 +46,4 @@ source "drivers/media/pci/ddbridge/Kconfig"
endif
endif #MEDIA_PCI_SUPPORT
+endif #PCI
diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
index 9d1481a60bd9..c504f70d4e90 100644
--- a/drivers/media/platform/coda.c
+++ b/drivers/media/platform/coda.c
@@ -1933,7 +1933,7 @@ MODULE_DEVICE_TABLE(platform, coda_platform_ids);
#ifdef CONFIG_OF
static const struct of_device_id coda_dt_ids[] = {
- { .compatible = "fsl,imx27-vpu", .data = &coda_platform_ids[CODA_IMX27] },
+ { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] },
{ .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] },
{ /* sentinel */ }
};
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index 33b5ffc8d66d..f45b940d6072 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -1122,10 +1122,14 @@ static int gsc_probe(struct platform_device *pdev)
goto err_clk;
}
- ret = gsc_register_m2m_device(gsc);
+ ret = v4l2_device_register(dev, &gsc->v4l2_dev);
if (ret)
goto err_clk;
+ ret = gsc_register_m2m_device(gsc);
+ if (ret)
+ goto err_v4l2;
+
platform_set_drvdata(pdev, gsc);
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(&pdev->dev);
@@ -1147,6 +1151,8 @@ err_pm:
pm_runtime_put(dev);
err_m2m:
gsc_unregister_m2m_device(gsc);
+err_v4l2:
+ v4l2_device_unregister(&gsc->v4l2_dev);
err_clk:
gsc_clk_put(gsc);
return ret;
@@ -1157,6 +1163,7 @@ static int gsc_remove(struct platform_device *pdev)
struct gsc_dev *gsc = platform_get_drvdata(pdev);
gsc_unregister_m2m_device(gsc);
+ v4l2_device_unregister(&gsc->v4l2_dev);
vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h
index cc19bba09bd1..76435d3bf62d 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.h
+++ b/drivers/media/platform/exynos-gsc/gsc-core.h
@@ -343,6 +343,7 @@ struct gsc_dev {
unsigned long state;
struct vb2_alloc_ctx *alloc_ctx;
struct video_device vdev;
+ struct v4l2_device v4l2_dev;
};
/**
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c
index 40a73f7d20da..e576ff2de3de 100644
--- a/drivers/media/platform/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c
@@ -751,6 +751,7 @@ int gsc_register_m2m_device(struct gsc_dev *gsc)
gsc->vdev.release = video_device_release_empty;
gsc->vdev.lock = &gsc->lock;
gsc->vdev.vfl_dir = VFL_DIR_M2M;
+ gsc->vdev.v4l2_dev = &gsc->v4l2_dev;
snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",
GSC_MODULE_NAME, gsc->id);
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index 15ef8f28239b..b5b480befcc3 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -1441,9 +1441,9 @@ static int fimc_md_probe(struct platform_device *pdev)
err_unlock:
mutex_unlock(&fmd->media_dev.graph_mutex);
err_clk:
- media_device_unregister(&fmd->media_dev);
fimc_md_put_clocks(fmd);
fimc_md_unregister_entities(fmd);
+ media_device_unregister(&fmd->media_dev);
err_md:
v4l2_device_unregister(&fmd->v4l2_dev);
return ret;
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index 553d87e5ceab..fd6289d60cde 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -784,6 +784,7 @@ static int g2d_probe(struct platform_device *pdev)
}
*vfd = g2d_videodev;
vfd->lock = &dev->mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
if (ret) {
v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c
index 7d0235069c87..5d538e7cd1bb 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/sh_vou.c
@@ -776,7 +776,7 @@ static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
&pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
- for (i = 0; ARRAY_SIZE(vou_fmt); i++)
+ for (i = 0; i < ARRAY_SIZE(vou_fmt); i++)
if (vou_fmt[i].pfmt == pix->pixelformat)
return 0;
diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig
index 0a7d520636a9..7cac453e34c7 100644
--- a/drivers/media/usb/Kconfig
+++ b/drivers/media/usb/Kconfig
@@ -1,6 +1,7 @@
+if USB && MEDIA_SUPPORT
+
menuconfig MEDIA_USB_SUPPORT
bool "Media USB Adapters"
- depends on USB && MEDIA_SUPPORT
help
Enable media drivers for USB bus.
If you have such devices, say Y.
@@ -52,3 +53,4 @@ source "drivers/media/usb/em28xx/Kconfig"
endif
endif #MEDIA_USB_SUPPORT
+endif #USB
diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c
index 8247c19d6260..77d7b7fbdc7e 100644
--- a/drivers/media/usb/hdpvr/hdpvr-core.c
+++ b/drivers/media/usb/hdpvr/hdpvr-core.c
@@ -311,6 +311,11 @@ static int hdpvr_probe(struct usb_interface *interface,
dev->workqueue = 0;
+ /* init video transfer queues first of all */
+ /* to prevent oops in hdpvr_delete() on error paths */
+ INIT_LIST_HEAD(&dev->free_buff_list);
+ INIT_LIST_HEAD(&dev->rec_buff_list);
+
/* register v4l2_device early so it can be used for printks */
if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) {
dev_err(&interface->dev, "v4l2_device_register failed\n");
@@ -333,10 +338,6 @@ static int hdpvr_probe(struct usb_interface *interface,
if (!dev->workqueue)
goto error;
- /* init video transfer queues */
- INIT_LIST_HEAD(&dev->free_buff_list);
- INIT_LIST_HEAD(&dev->rec_buff_list);
-
dev->options = hdpvr_default_options;
if (default_video_input < HDPVR_VIDEO_INPUTS)
@@ -413,7 +414,7 @@ static int hdpvr_probe(struct usb_interface *interface,
video_nr[atomic_inc_return(&dev_nr)]);
if (retval < 0) {
v4l2_err(&dev->v4l2_dev, "registering videodev failed\n");
- goto error;
+ goto reg_fail;
}
/* let the user know what node this device is now attached to */
diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c
index 494d0500bda6..a6dc56e1bc58 100644
--- a/drivers/misc/atmel_pwm.c
+++ b/drivers/misc/atmel_pwm.c
@@ -90,8 +90,10 @@ int pwm_channel_alloc(int index, struct pwm_channel *ch)
unsigned long flags;
int status = 0;
- /* insist on PWM init, with this signal pinned out */
- if (!pwm || !(pwm->mask & 1 << index))
+ if (!pwm)
+ return -EPROBE_DEFER;
+
+ if (!(pwm->mask & 1 << index))
return -ENODEV;
if (index < 0 || index >= PWM_NCHAN || !ch)
diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c
index b3e50984d2c8..3db9291849f1 100644
--- a/drivers/misc/mei/amthif.c
+++ b/drivers/misc/mei/amthif.c
@@ -57,6 +57,7 @@ void mei_amthif_reset_params(struct mei_device *dev)
dev->iamthif_ioctl = false;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
dev->iamthif_timer = 0;
+ dev->iamthif_stall_timer = 0;
}
/**
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index 9ecd49a7be1b..99cc0b07a713 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -295,10 +295,13 @@ int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
if (cl->reading_state != MEI_READ_COMPLETE &&
!waitqueue_active(&cl->rx_wait)) {
+
mutex_unlock(&dev->device_lock);
if (wait_event_interruptible(cl->rx_wait,
- (MEI_READ_COMPLETE == cl->reading_state))) {
+ cl->reading_state == MEI_READ_COMPLETE ||
+ mei_cl_is_transitioning(cl))) {
+
if (signal_pending(current))
return -EINTR;
return -ERESTARTSYS;
diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h
index cfdb144526aa..467d9dda97fa 100644
--- a/drivers/misc/mei/client.h
+++ b/drivers/misc/mei/client.h
@@ -76,6 +76,12 @@ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
(cl1->host_client_id == cl2->host_client_id) &&
(cl1->me_client_id == cl2->me_client_id);
}
+static inline bool mei_cl_is_transitioning(struct mei_cl *cl)
+{
+ return (MEI_FILE_INITIALIZING == cl->state ||
+ MEI_FILE_DISCONNECTED == cl->state ||
+ MEI_FILE_DISCONNECTING == cl->state);
+}
int mei_cl_flow_ctrl_creds(struct mei_cl *cl);
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index 6916045166eb..23b5b7bcba9f 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -35,11 +35,15 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev)
struct mei_me_client *clients;
int b;
+ dev->me_clients_num = 0;
+ dev->me_client_presentation_num = 0;
+ dev->me_client_index = 0;
+
/* count how many ME clients we have */
for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
dev->me_clients_num++;
- if (dev->me_clients_num <= 0)
+ if (dev->me_clients_num == 0)
return;
kfree(dev->me_clients);
@@ -221,7 +225,7 @@ static int mei_hbm_prop_req(struct mei_device *dev)
struct hbm_props_request *prop_req;
const size_t len = sizeof(struct hbm_props_request);
unsigned long next_client_index;
- u8 client_num;
+ unsigned long client_num;
client_num = dev->me_client_presentation_num;
@@ -650,8 +654,6 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
dev->hbm_state == MEI_HBM_ENUM_CLIENTS) {
dev->init_clients_timer = 0;
- dev->me_client_presentation_num = 0;
- dev->me_client_index = 0;
mei_hbm_me_cl_allocate(dev);
dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index 6eec689ba97e..878bc1c29d2b 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -164,6 +164,9 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
}
+ /* we're already in reset, cancel the init timer */
+ dev->init_clients_timer = 0;
+
dev->me_clients_num = 0;
dev->rd_msg_hdr = 0;
dev->wd_pending = false;
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 053139f61086..701698de5ac7 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -262,19 +262,16 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
mutex_unlock(&dev->device_lock);
if (wait_event_interruptible(cl->rx_wait,
- (MEI_READ_COMPLETE == cl->reading_state ||
- MEI_FILE_INITIALIZING == cl->state ||
- MEI_FILE_DISCONNECTED == cl->state ||
- MEI_FILE_DISCONNECTING == cl->state))) {
+ MEI_READ_COMPLETE == cl->reading_state ||
+ mei_cl_is_transitioning(cl))) {
+
if (signal_pending(current))
return -EINTR;
return -ERESTARTSYS;
}
mutex_lock(&dev->device_lock);
- if (MEI_FILE_INITIALIZING == cl->state ||
- MEI_FILE_DISCONNECTED == cl->state ||
- MEI_FILE_DISCONNECTING == cl->state) {
+ if (mei_cl_is_transitioning(cl)) {
rets = -EBUSY;
goto out;
}
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 4de5140e7379..73c7700aee21 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -402,9 +402,9 @@ struct mei_device {
struct mei_me_client *me_clients; /* Note: memory has to be allocated */
DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX);
DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX);
- u8 me_clients_num;
- u8 me_client_presentation_num;
- u8 me_client_index;
+ unsigned long me_clients_num;
+ unsigned long me_client_presentation_num;
+ unsigned long me_client_index;
struct mei_cl wd_cl;
enum mei_wd_states wd_state;
diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c
index fff928604859..491e9ecc92c2 100644
--- a/drivers/mmc/host/tmio_mmc_dma.c
+++ b/drivers/mmc/host/tmio_mmc_dma.c
@@ -104,6 +104,7 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host)
pio:
if (!desc) {
/* DMA failed, fall back to PIO */
+ tmio_mmc_enable_dma(host, false);
if (ret >= 0)
ret = -EIO;
host->chan_rx = NULL;
@@ -116,7 +117,6 @@ pio:
}
dev_warn(&host->pdev->dev,
"DMA failed: %d, falling back to PIO\n", ret);
- tmio_mmc_enable_dma(host, false);
}
dev_dbg(&host->pdev->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__,
@@ -185,6 +185,7 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host)
pio:
if (!desc) {
/* DMA failed, fall back to PIO */
+ tmio_mmc_enable_dma(host, false);
if (ret >= 0)
ret = -EIO;
host->chan_tx = NULL;
@@ -197,7 +198,6 @@ pio:
}
dev_warn(&host->pdev->dev,
"DMA failed: %d, falling back to PIO\n", ret);
- tmio_mmc_enable_dma(host, false);
}
dev_dbg(&host->pdev->dev, "%s(): desc %p, cookie %d\n", __func__,
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index dfcd0a565c5b..fb8c4deaac27 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2793,7 +2793,9 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
if (!chip->select_chip)
chip->select_chip = nand_select_chip;
- if (!chip->read_byte)
+
+ /* If called twice, pointers that depend on busw may need to be reset */
+ if (!chip->read_byte || chip->read_byte == nand_read_byte)
chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
if (!chip->read_word)
chip->read_word = nand_read_word;
@@ -2801,9 +2803,9 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
chip->block_bad = nand_block_bad;
if (!chip->block_markbad)
chip->block_markbad = nand_default_block_markbad;
- if (!chip->write_buf)
+ if (!chip->write_buf || chip->write_buf == nand_write_buf)
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
- if (!chip->read_buf)
+ if (!chip->read_buf || chip->read_buf == nand_read_buf)
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!chip->scan_bbt)
chip->scan_bbt = nand_default_bbt;
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 5df49d3cb5c7..c95bfb183c62 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -1069,6 +1069,9 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) {
dbg_wl("no WL needed: min used EC %d, max free EC %d",
e1->ec, e2->ec);
+
+ /* Give the unused PEB back */
+ wl_tree_add(e2, &ubi->free);
goto out_cancel;
}
self_check_in_wl_tree(ubi, e1, &ubi->used);
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 666cf3a49b6d..8395b0992a89 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1991,6 +1991,7 @@ static int __bond_release_one(struct net_device *bond_dev,
struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave, *oldcurrent;
struct sockaddr addr;
+ int old_flags = bond_dev->flags;
netdev_features_t old_features = bond_dev->features;
/* slave is not a slave or master is not master of this slave */
@@ -2123,12 +2124,18 @@ static int __bond_release_one(struct net_device *bond_dev,
* already taken care of above when we detached the slave
*/
if (!USES_PRIMARY(bond->params.mode)) {
- /* unset promiscuity level from slave */
- if (bond_dev->flags & IFF_PROMISC)
+ /* unset promiscuity level from slave
+ * NOTE: The NETDEV_CHANGEADDR call above may change the value
+ * of the IFF_PROMISC flag in the bond_dev, but we need the
+ * value of that flag before that change, as that was the value
+ * when this slave was attached, so we cache at the start of the
+ * function and use it here. Same goes for ALLMULTI below
+ */
+ if (old_flags & IFF_PROMISC)
dev_set_promiscuity(slave_dev, -1);
/* unset allmulti level from slave */
- if (bond_dev->flags & IFF_ALLMULTI)
+ if (old_flags & IFF_ALLMULTI)
dev_set_allmulti(slave_dev, -1);
/* flush master's mc_list from slave */
diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c
index db52f4414def..535d5dd8d816 100644
--- a/drivers/net/can/at91_can.c
+++ b/drivers/net/can/at91_can.c
@@ -1409,10 +1409,10 @@ static int at91_can_remove(struct platform_device *pdev)
static const struct platform_device_id at91_can_id_table[] = {
{
- .name = "at91_can",
+ .name = "at91sam9x5_can",
.driver_data = (kernel_ulong_t)&at91_at91sam9x5_data,
}, {
- .name = "at91sam9x5_can",
+ .name = "at91_can",
.driver_data = (kernel_ulong_t)&at91_at91sam9263_data,
}, {
/* sentinel */
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index f9cba4123c66..1870c4731a57 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -705,14 +705,14 @@ static size_t can_get_size(const struct net_device *dev)
size_t size;
size = nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */
- size += sizeof(struct can_ctrlmode); /* IFLA_CAN_CTRLMODE */
+ size += nla_total_size(sizeof(struct can_ctrlmode)); /* IFLA_CAN_CTRLMODE */
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */
- size += sizeof(struct can_bittiming); /* IFLA_CAN_BITTIMING */
- size += sizeof(struct can_clock); /* IFLA_CAN_CLOCK */
+ size += nla_total_size(sizeof(struct can_bittiming)); /* IFLA_CAN_BITTIMING */
+ size += nla_total_size(sizeof(struct can_clock)); /* IFLA_CAN_CLOCK */
if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */
- size += sizeof(struct can_berr_counter);
+ size += nla_total_size(sizeof(struct can_berr_counter));
if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */
- size += sizeof(struct can_bittiming_const);
+ size += nla_total_size(sizeof(struct can_bittiming_const));
return size;
}
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 769d29ed106d..a8f33a525dd6 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -63,7 +63,7 @@
#define FLEXCAN_MCR_BCC BIT(16)
#define FLEXCAN_MCR_LPRIO_EN BIT(13)
#define FLEXCAN_MCR_AEN BIT(12)
-#define FLEXCAN_MCR_MAXMB(x) ((x) & 0xf)
+#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x1f)
#define FLEXCAN_MCR_IDAM_A (0 << 8)
#define FLEXCAN_MCR_IDAM_B (1 << 8)
#define FLEXCAN_MCR_IDAM_C (2 << 8)
@@ -745,9 +745,11 @@ static int flexcan_chip_start(struct net_device *dev)
*
*/
reg_mcr = flexcan_read(&regs->mcr);
+ reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |
FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN |
- FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS;
+ FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS |
+ FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);
netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
flexcan_write(reg_mcr, &regs->mcr);
@@ -792,6 +794,10 @@ static int flexcan_chip_start(struct net_device *dev)
&regs->cantxfg[i].can_ctrl);
}
+ /* Abort any pending TX, mark Mailbox as INACTIVE */
+ flexcan_write(FLEXCAN_MB_CNT_CODE(0x4),
+ &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
+
/* acceptance mask/acceptance code (accept everything) */
flexcan_write(0x0, &regs->rxgmask);
flexcan_write(0x0, &regs->rx14mask);
@@ -983,9 +989,9 @@ static void unregister_flexcandev(struct net_device *dev)
}
static const struct of_device_id flexcan_of_match[] = {
- { .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
- { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
{ .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
+ { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
+ { .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, flexcan_of_match);
diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
index eec0af45b859..1c6bc9678774 100644
--- a/drivers/net/ethernet/broadcom/bgmac.c
+++ b/drivers/net/ethernet/broadcom/bgmac.c
@@ -908,7 +908,7 @@ static void bgmac_chip_reset(struct bgmac *bgmac)
struct bcma_drv_cc *cc = &bgmac->core->bus->drv_cc;
u8 et_swtype = 0;
u8 sw_type = BGMAC_CHIPCTL_1_SW_TYPE_EPHY |
- BGMAC_CHIPCTL_1_IF_TYPE_RMII;
+ BGMAC_CHIPCTL_1_IF_TYPE_MII;
char buf[2];
if (bcm47xx_nvram_getenv("et_swtype", buf, 1) > 0) {
diff --git a/drivers/net/ethernet/broadcom/bgmac.h b/drivers/net/ethernet/broadcom/bgmac.h
index 98d4b5fcc070..12a35cf9bb81 100644
--- a/drivers/net/ethernet/broadcom/bgmac.h
+++ b/drivers/net/ethernet/broadcom/bgmac.h
@@ -333,7 +333,7 @@
#define BGMAC_CHIPCTL_1_IF_TYPE_MASK 0x00000030
#define BGMAC_CHIPCTL_1_IF_TYPE_RMII 0x00000000
-#define BGMAC_CHIPCTL_1_IF_TYPE_MI 0x00000010
+#define BGMAC_CHIPCTL_1_IF_TYPE_MII 0x00000010
#define BGMAC_CHIPCTL_1_IF_TYPE_RGMII 0x00000020
#define BGMAC_CHIPCTL_1_SW_TYPE_MASK 0x000000C0
#define BGMAC_CHIPCTL_1_SW_TYPE_EPHY 0x00000000
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index 638e55435b04..8c4babc0efbd 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -670,6 +670,7 @@ static void bnx2x_gro_receive(struct bnx2x *bp, struct bnx2x_fastpath *fp,
}
}
#endif
+ skb_record_rx_queue(skb, fp->rx_queue);
napi_gro_receive(&fp->napi, skb);
}
diff --git a/drivers/net/ethernet/chelsio/cxgb3/sge.c b/drivers/net/ethernet/chelsio/cxgb3/sge.c
index f12e6b85a653..f057a189d975 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c
@@ -1600,7 +1600,8 @@ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb,
flits = skb_transport_offset(skb) / 8;
sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl;
sgl_flits = make_sgl(skb, sgp, skb_transport_header(skb),
- skb->tail - skb->transport_header,
+ skb_tail_pointer(skb) -
+ skb_transport_header(skb),
adap->pdev);
if (need_skb_unmap()) {
setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits);
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index 1db2df61b8af..696674edbdc4 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -1150,7 +1150,6 @@ int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo)
if (lancer_chip(adapter)) {
req->hdr.version = 1;
- req->if_id = cpu_to_le16(adapter->if_handle);
} else if (BEx_chip(adapter)) {
if (adapter->function_caps & BE_FUNCTION_CAPS_SUPER_NIC)
req->hdr.version = 2;
@@ -1158,6 +1157,8 @@ int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo)
req->hdr.version = 2;
}
+ if (req->hdr.version > 0)
+ req->if_id = cpu_to_le16(adapter->if_handle);
req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
req->ulp_num = BE_ULP1_NUM;
req->type = BE_ETH_TX_RING_TYPE_STANDARD;
diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
index d1cbfb12c1ca..4be11ff516a0 100644
--- a/drivers/net/ethernet/marvell/mv643xx_eth.c
+++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
@@ -1125,15 +1125,13 @@ static void mib_counters_update(struct mv643xx_eth_private *mp)
p->rx_discard += rdlp(mp, RX_DISCARD_FRAME_CNT);
p->rx_overrun += rdlp(mp, RX_OVERRUN_FRAME_CNT);
spin_unlock_bh(&mp->mib_counters_lock);
-
- mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ);
}
static void mib_counters_timer_wrapper(unsigned long _mp)
{
struct mv643xx_eth_private *mp = (void *)_mp;
-
mib_counters_update(mp);
+ mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ);
}
@@ -2231,6 +2229,7 @@ static int mv643xx_eth_open(struct net_device *dev)
mp->int_mask |= INT_TX_END_0 << i;
}
+ add_timer(&mp->mib_counters_timer);
port_start(mp);
wrlp(mp, INT_MASK_EXT, INT_EXT_LINK_PHY | INT_EXT_TX);
@@ -2739,7 +2738,6 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
mp->mib_counters_timer.data = (unsigned long)mp;
mp->mib_counters_timer.function = mib_counters_timer_wrapper;
mp->mib_counters_timer.expires = jiffies + 30 * HZ;
- add_timer(&mp->mib_counters_timer);
spin_lock_init(&mp->mib_counters_lock);
diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c
index 0e572a527154..28d706bd12eb 100644
--- a/drivers/net/ethernet/mellanox/mlx4/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c
@@ -1544,7 +1544,7 @@ static void mlx4_master_deactivate_admin_state(struct mlx4_priv *priv, int slave
vp_oper->vlan_idx = NO_INDX;
}
if (NO_INDX != vp_oper->mac_idx) {
- __mlx4_unregister_mac(&priv->dev, port, vp_oper->mac_idx);
+ __mlx4_unregister_mac(&priv->dev, port, vp_oper->state.mac);
vp_oper->mac_idx = NO_INDX;
}
}
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 393f961a013c..7199d2a8dcf8 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -4218,6 +4218,7 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_23:
case RTL_GIGA_MAC_VER_24:
case RTL_GIGA_MAC_VER_34:
+ case RTL_GIGA_MAC_VER_35:
RTL_W32(RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST);
break;
case RTL_GIGA_MAC_VER_40:
diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c
index 860e15ddfbcb..7233610164cd 100644
--- a/drivers/net/ethernet/ti/davinci_emac.c
+++ b/drivers/net/ethernet/ti/davinci_emac.c
@@ -876,8 +876,7 @@ static void emac_dev_mcast_set(struct net_device *ndev)
netdev_mc_count(ndev) > EMAC_DEF_MAX_MULTICAST_ADDRESSES) {
mbp_enable = (mbp_enable | EMAC_MBP_RXMCAST);
emac_add_mcast(priv, EMAC_ALL_MULTI_SET, NULL);
- }
- if (!netdev_mc_empty(ndev)) {
+ } else if (!netdev_mc_empty(ndev)) {
struct netdev_hw_addr *ha;
mbp_enable = (mbp_enable | EMAC_MBP_RXMCAST);
diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c
index ca98acabf1b4..75b82b6e8c54 100644
--- a/drivers/net/ethernet/via/via-rhine.c
+++ b/drivers/net/ethernet/via/via-rhine.c
@@ -32,7 +32,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define DRV_NAME "via-rhine"
-#define DRV_VERSION "1.5.0"
+#define DRV_VERSION "1.5.1"
#define DRV_RELDATE "2010-10-09"
#include <linux/types.h>
@@ -1694,7 +1694,12 @@ static netdev_tx_t rhine_start_tx(struct sk_buff *skb,
cpu_to_le32(TXDESC | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
if (unlikely(vlan_tx_tag_present(skb))) {
- rp->tx_ring[entry].tx_status = cpu_to_le32((vlan_tx_tag_get(skb)) << 16);
+ u16 vid_pcp = vlan_tx_tag_get(skb);
+
+ /* drop CFI/DEI bit, register needs VID and PCP */
+ vid_pcp = (vid_pcp & VLAN_VID_MASK) |
+ ((vid_pcp & VLAN_PRIO_MASK) >> 1);
+ rp->tx_ring[entry].tx_status = cpu_to_le32((vid_pcp) << 16);
/* request tagging */
rp->tx_ring[entry].desc_length |= cpu_to_le32(0x020000);
}
diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c
index 57c2e5ef2804..5444f2b87d01 100644
--- a/drivers/net/ethernet/xilinx/ll_temac_main.c
+++ b/drivers/net/ethernet/xilinx/ll_temac_main.c
@@ -297,6 +297,12 @@ static int temac_dma_bd_init(struct net_device *ndev)
lp->rx_bd_p + (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1)));
lp->dma_out(lp, TX_CURDESC_PTR, lp->tx_bd_p);
+ /* Init descriptor indexes */
+ lp->tx_bd_ci = 0;
+ lp->tx_bd_next = 0;
+ lp->tx_bd_tail = 0;
+ lp->rx_bd_ci = 0;
+
return 0;
out:
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index 162464fe86bf..7f10588fe668 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -281,7 +281,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
nf_reset(skb);
skb->ip_summed = CHECKSUM_NONE;
- ip_select_ident(iph, &rt->dst, NULL);
+ ip_select_ident(skb, &rt->dst, NULL);
ip_send_check(iph);
ip_local_out(skb);
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 7fd9c66dbe38..a4f35b0b7c81 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1693,11 +1693,11 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
INIT_LIST_HEAD(&tun->disabled);
err = tun_attach(tun, file);
if (err < 0)
- goto err_free_dev;
+ goto err_free_flow;
err = register_netdevice(tun->dev);
if (err < 0)
- goto err_free_dev;
+ goto err_detach;
if (device_create_file(&tun->dev->dev, &dev_attr_tun_flags) ||
device_create_file(&tun->dev->dev, &dev_attr_owner) ||
@@ -1741,7 +1741,12 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
strcpy(ifr->ifr_name, tun->dev->name);
return 0;
- err_free_dev:
+err_detach:
+ tun_detach_all(dev);
+err_free_flow:
+ tun_flow_uninit(tun);
+ security_tun_dev_free_security(tun->security);
+err_free_dev:
free_netdev(dev);
return err;
}
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 04ee044dde51..b1897c7afdd8 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -709,6 +709,11 @@ static const struct usb_device_id products [] = {
.bInterfaceProtocol = USB_CDC_PROTO_NONE,
.driver_info = (unsigned long)&wwan_info,
}, {
+ /* Telit modules */
+ USB_VENDOR_AND_INTERFACE_INFO(0x1bc7, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = (kernel_ulong_t) &wwan_info,
+}, {
USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
USB_CDC_PROTO_NONE),
.driver_info = (unsigned long) &cdc_info,
diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c
index 2dbb9460349d..c6867f926cff 100644
--- a/drivers/net/usb/dm9601.c
+++ b/drivers/net/usb/dm9601.c
@@ -303,7 +303,7 @@ static void dm9601_set_multicast(struct net_device *net)
rx_ctl |= 0x02;
} else if (net->flags & IFF_ALLMULTI ||
netdev_mc_count(net) > DM_MAX_MCAST) {
- rx_ctl |= 0x04;
+ rx_ctl |= 0x08;
} else if (!netdev_mc_empty(net)) {
struct netdev_hw_addr *ha;
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 56459215a22b..34a081f1527f 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -518,6 +518,135 @@ static const struct usb_device_id products[] = {
/* 3. Combined interface devices matching on interface number */
{QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */
+ {QMI_FIXED_INTF(0x05c6, 0x7000, 0)},
+ {QMI_FIXED_INTF(0x05c6, 0x7001, 1)},
+ {QMI_FIXED_INTF(0x05c6, 0x7002, 1)},
+ {QMI_FIXED_INTF(0x05c6, 0x7101, 1)},
+ {QMI_FIXED_INTF(0x05c6, 0x7101, 2)},
+ {QMI_FIXED_INTF(0x05c6, 0x7101, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x7102, 1)},
+ {QMI_FIXED_INTF(0x05c6, 0x7102, 2)},
+ {QMI_FIXED_INTF(0x05c6, 0x7102, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x8000, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x8001, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9000, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9003, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9005, 2)},
+ {QMI_FIXED_INTF(0x05c6, 0x900a, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x900b, 2)},
+ {QMI_FIXED_INTF(0x05c6, 0x900c, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x900c, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x900c, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x900d, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x900f, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x900f, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x900f, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9010, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9010, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9011, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9011, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9021, 1)},
+ {QMI_FIXED_INTF(0x05c6, 0x9022, 2)},
+ {QMI_FIXED_INTF(0x05c6, 0x9025, 4)}, /* Alcatel-sbell ASB TL131 TDD LTE (China Mobile) */
+ {QMI_FIXED_INTF(0x05c6, 0x9026, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x902e, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9031, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9032, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9033, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9033, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9033, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9033, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9034, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9034, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9034, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9034, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9034, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x9035, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9036, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9037, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9038, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x903b, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x903c, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x903d, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x903e, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9043, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9046, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9046, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9046, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9047, 2)},
+ {QMI_FIXED_INTF(0x05c6, 0x9047, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9047, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9048, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9048, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9048, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9048, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x9048, 8)},
+ {QMI_FIXED_INTF(0x05c6, 0x904c, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x904c, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x904c, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x904c, 8)},
+ {QMI_FIXED_INTF(0x05c6, 0x9050, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9052, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9053, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9053, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x9054, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9054, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9055, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9055, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9055, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9055, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9055, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x9056, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9062, 2)},
+ {QMI_FIXED_INTF(0x05c6, 0x9062, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9062, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9062, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9062, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9062, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x9062, 8)},
+ {QMI_FIXED_INTF(0x05c6, 0x9062, 9)},
+ {QMI_FIXED_INTF(0x05c6, 0x9064, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9065, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9065, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x9066, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9066, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9067, 1)},
+ {QMI_FIXED_INTF(0x05c6, 0x9068, 2)},
+ {QMI_FIXED_INTF(0x05c6, 0x9068, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9068, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9068, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9068, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9068, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x9069, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9069, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9069, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x9069, 8)},
+ {QMI_FIXED_INTF(0x05c6, 0x9070, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9070, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9075, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9076, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9076, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9076, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9076, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x9076, 8)},
+ {QMI_FIXED_INTF(0x05c6, 0x9077, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9077, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9077, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9077, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9078, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9079, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x9079, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9079, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9079, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x9079, 8)},
+ {QMI_FIXED_INTF(0x05c6, 0x9080, 5)},
+ {QMI_FIXED_INTF(0x05c6, 0x9080, 6)},
+ {QMI_FIXED_INTF(0x05c6, 0x9080, 7)},
+ {QMI_FIXED_INTF(0x05c6, 0x9080, 8)},
+ {QMI_FIXED_INTF(0x05c6, 0x9083, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9084, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x920d, 0)},
+ {QMI_FIXED_INTF(0x05c6, 0x920d, 5)},
{QMI_FIXED_INTF(0x12d1, 0x140c, 1)}, /* Huawei E173 */
{QMI_FIXED_INTF(0x12d1, 0x14ac, 1)}, /* Huawei E1820 */
{QMI_FIXED_INTF(0x19d2, 0x0002, 1)},
@@ -610,7 +739,6 @@ static const struct usb_device_id products[] = {
{QMI_GOBI_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */
{QMI_GOBI_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */
{QMI_GOBI_DEVICE(0x05c6, 0x920b)}, /* Generic Gobi 2000 Modem device */
- {QMI_GOBI_DEVICE(0x05c6, 0x920d)}, /* Gobi 3000 Composite */
{QMI_GOBI_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */
{QMI_GOBI_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */
{QMI_GOBI_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 42d670a468f8..64cf70247048 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -902,7 +902,6 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs)
struct scatterlist sg;
struct virtio_net_ctrl_mq s;
struct net_device *dev = vi->dev;
- int i;
if (!vi->has_cvq || !virtio_has_feature(vi->vdev, VIRTIO_NET_F_MQ))
return 0;
@@ -916,10 +915,10 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs)
queue_pairs);
return -EINVAL;
} else {
- for (i = vi->curr_queue_pairs; i < queue_pairs; i++)
- if (!try_fill_recv(&vi->rq[i], GFP_KERNEL))
- schedule_delayed_work(&vi->refill, 0);
vi->curr_queue_pairs = queue_pairs;
+ /* virtnet_open() will refill when device is going to up. */
+ if (dev->flags & IFF_UP)
+ schedule_delayed_work(&vi->refill, 0);
}
return 0;
@@ -1109,6 +1108,7 @@ static int virtnet_cpu_callback(struct notifier_block *nfb,
default:
break;
}
+
return NOTIFY_OK;
}
@@ -1665,6 +1665,8 @@ static int virtnet_freeze(struct virtio_device *vdev)
struct virtnet_info *vi = vdev->priv;
int i;
+ unregister_hotcpu_notifier(&vi->nb);
+
/* Prevent config work handler from accessing the device */
mutex_lock(&vi->config_lock);
vi->config_enable = false;
@@ -1709,7 +1711,13 @@ static int virtnet_restore(struct virtio_device *vdev)
vi->config_enable = true;
mutex_unlock(&vi->config_lock);
+ rtnl_lock();
virtnet_set_queues(vi, vi->curr_queue_pairs);
+ rtnl_unlock();
+
+ err = register_hotcpu_notifier(&vi->nb);
+ if (err)
+ return err;
return 0;
}
diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c
index 3f0c4f268751..bcfff0d62de4 100644
--- a/drivers/net/wan/farsync.c
+++ b/drivers/net/wan/farsync.c
@@ -1972,6 +1972,7 @@ fst_get_iface(struct fst_card_info *card, struct fst_port_info *port,
}
i = port->index;
+ memset(&sync, 0, sizeof(sync));
sync.clock_rate = FST_RDL(card, portConfig[i].lineSpeed);
/* Lucky card and linux use same encoding here */
sync.clock_type = FST_RDB(card, portConfig[i].internalClock) ==
diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c
index 6a24a5a70cc7..4c0a69779b89 100644
--- a/drivers/net/wan/wanxl.c
+++ b/drivers/net/wan/wanxl.c
@@ -355,6 +355,7 @@ static int wanxl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
ifr->ifr_settings.size = size; /* data size wanted */
return -ENOBUFS;
}
+ memset(&line, 0, sizeof(line));
line.clock_type = get_status(port)->clocking;
line.clock_rate = 0;
line.loopback = 0;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index e1714d7c9eeb..3457ca5382f4 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -1076,6 +1076,10 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
* is_on == 0 means MRC CCK is OFF (more noise imm)
*/
bool is_on = param ? 1 : 0;
+
+ if (ah->caps.rx_chainmask == 1)
+ break;
+
REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
AR_PHY_MRC_CCK_ENABLE, is_on);
REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 42b03dc39d14..4ebd9fd8a0a4 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -79,10 +79,6 @@ struct ath_config {
sizeof(struct ath_buf_state)); \
} while (0)
-#define ATH_RXBUF_RESET(_bf) do { \
- (_bf)->bf_stale = false; \
- } while (0)
-
/**
* enum buffer_type - Buffer type flags
*
@@ -316,6 +312,7 @@ struct ath_rx {
struct ath_descdma rxdma;
struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
+ struct ath_buf *buf_hold;
struct sk_buff *frag;
u32 ampdu_ref;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index e33a659e224b..a8fee08479ef 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -209,6 +209,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
unsigned long flags;
+ int i;
if (ath_startrecv(sc) != 0) {
ath_err(common, "Unable to restart recv logic\n");
@@ -236,6 +237,15 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
}
work:
ath_restart_work(sc);
+
+ for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
+ if (!ATH_TXQ_SETUP(sc, i))
+ continue;
+
+ spin_lock_bh(&sc->tx.txq[i].axq_lock);
+ ath_txq_schedule(sc, &sc->tx.txq[i]);
+ spin_unlock_bh(&sc->tx.txq[i].axq_lock);
+ }
}
if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3)
@@ -543,21 +553,10 @@ chip_reset:
static int ath_reset(struct ath_softc *sc)
{
- int i, r;
+ int r;
ath9k_ps_wakeup(sc);
-
r = ath_reset_internal(sc, NULL);
-
- for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
- if (!ATH_TXQ_SETUP(sc, i))
- continue;
-
- spin_lock_bh(&sc->tx.txq[i].axq_lock);
- ath_txq_schedule(sc, &sc->tx.txq[i]);
- spin_unlock_bh(&sc->tx.txq[i].axq_lock);
- }
-
ath9k_ps_restore(sc);
return r;
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 8be2b5d8c155..f53dbd1133ce 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -42,8 +42,6 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
struct ath_desc *ds;
struct sk_buff *skb;
- ATH_RXBUF_RESET(bf);
-
ds = bf->bf_desc;
ds->ds_link = 0; /* link to null */
ds->ds_data = bf->bf_buf_addr;
@@ -70,6 +68,14 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
sc->rx.rxlink = &ds->ds_link;
}
+static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf)
+{
+ if (sc->rx.buf_hold)
+ ath_rx_buf_link(sc, sc->rx.buf_hold);
+
+ sc->rx.buf_hold = bf;
+}
+
static void ath_setdefantenna(struct ath_softc *sc, u32 antenna)
{
/* XXX block beacon interrupts */
@@ -117,7 +123,6 @@ static bool ath_rx_edma_buf_link(struct ath_softc *sc,
skb = bf->bf_mpdu;
- ATH_RXBUF_RESET(bf);
memset(skb->data, 0, ah->caps.rx_status_len);
dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
ah->caps.rx_status_len, DMA_TO_DEVICE);
@@ -432,6 +437,7 @@ int ath_startrecv(struct ath_softc *sc)
if (list_empty(&sc->rx.rxbuf))
goto start_recv;
+ sc->rx.buf_hold = NULL;
sc->rx.rxlink = NULL;
list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
ath_rx_buf_link(sc, bf);
@@ -677,6 +683,9 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
}
bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
+ if (bf == sc->rx.buf_hold)
+ return NULL;
+
ds = bf->bf_desc;
/*
@@ -1378,7 +1387,7 @@ requeue:
if (edma) {
ath_rx_edma_buf_link(sc, qtype);
} else {
- ath_rx_buf_link(sc, bf);
+ ath_rx_buf_relink(sc, bf);
ath9k_hw_rxena(ah);
}
} while (1);
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 83ab6be3fe6d..e752f5d4995d 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2387,6 +2387,7 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an)
for (acno = 0, ac = &an->ac[acno];
acno < IEEE80211_NUM_ACS; acno++, ac++) {
ac->sched = false;
+ ac->clear_ps_filter = true;
ac->txq = sc->tx.txq_map[acno];
INIT_LIST_HEAD(&ac->tid_q);
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index 44fa0cdbf97b..0f6eb2b6d381 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -575,8 +575,6 @@ static struct sdio_driver brcmf_sdmmc_driver = {
static int brcmf_sdio_pd_probe(struct platform_device *pdev)
{
- int ret;
-
brcmf_dbg(SDIO, "Enter\n");
brcmfmac_sdio_pdata = pdev->dev.platform_data;
@@ -584,11 +582,7 @@ static int brcmf_sdio_pd_probe(struct platform_device *pdev)
if (brcmfmac_sdio_pdata->power_on)
brcmfmac_sdio_pdata->power_on();
- ret = sdio_register_driver(&brcmf_sdmmc_driver);
- if (ret)
- brcmf_err("sdio_register_driver failed: %d\n", ret);
-
- return ret;
+ return 0;
}
static int brcmf_sdio_pd_remove(struct platform_device *pdev)
@@ -610,6 +604,15 @@ static struct platform_driver brcmf_sdio_pd = {
}
};
+void brcmf_sdio_register(void)
+{
+ int ret;
+
+ ret = sdio_register_driver(&brcmf_sdmmc_driver);
+ if (ret)
+ brcmf_err("sdio_register_driver failed: %d\n", ret);
+}
+
void brcmf_sdio_exit(void)
{
brcmf_dbg(SDIO, "Enter\n");
@@ -620,18 +623,13 @@ void brcmf_sdio_exit(void)
sdio_unregister_driver(&brcmf_sdmmc_driver);
}
-void brcmf_sdio_init(void)
+void __init brcmf_sdio_init(void)
{
int ret;
brcmf_dbg(SDIO, "Enter\n");
ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe);
- if (ret == -ENODEV) {
- brcmf_dbg(SDIO, "No platform data available, registering without.\n");
- ret = sdio_register_driver(&brcmf_sdmmc_driver);
- }
-
- if (ret)
- brcmf_err("driver registration failed: %d\n", ret);
+ if (ret == -ENODEV)
+ brcmf_dbg(SDIO, "No platform data available.\n");
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
index 080395f49fa5..e715d3396c2c 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
@@ -154,10 +154,11 @@ extern int brcmf_bus_start(struct device *dev);
#ifdef CONFIG_BRCMFMAC_SDIO
extern void brcmf_sdio_exit(void);
extern void brcmf_sdio_init(void);
+extern void brcmf_sdio_register(void);
#endif
#ifdef CONFIG_BRCMFMAC_USB
extern void brcmf_usb_exit(void);
-extern void brcmf_usb_init(void);
+extern void brcmf_usb_register(void);
#endif
#endif /* _BRCMF_BUS_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index 2c593570497c..95d709959e16 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -1034,21 +1034,23 @@ u32 brcmf_get_chip_info(struct brcmf_if *ifp)
return bus->chip << 4 | bus->chiprev;
}
-static void brcmf_driver_init(struct work_struct *work)
+static void brcmf_driver_register(struct work_struct *work)
{
- brcmf_debugfs_init();
-
#ifdef CONFIG_BRCMFMAC_SDIO
- brcmf_sdio_init();
+ brcmf_sdio_register();
#endif
#ifdef CONFIG_BRCMFMAC_USB
- brcmf_usb_init();
+ brcmf_usb_register();
#endif
}
-static DECLARE_WORK(brcmf_driver_work, brcmf_driver_init);
+static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register);
static int __init brcmfmac_module_init(void)
{
+ brcmf_debugfs_init();
+#ifdef CONFIG_BRCMFMAC_SDIO
+ brcmf_sdio_init();
+#endif
if (!schedule_work(&brcmf_driver_work))
return -EBUSY;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
index 01aed7ad6bec..b0ab98b0a226 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
@@ -1532,7 +1532,7 @@ void brcmf_usb_exit(void)
brcmf_release_fw(&fw_image_list);
}
-void brcmf_usb_init(void)
+void brcmf_usb_register(void)
{
brcmf_dbg(USB, "Enter\n");
INIT_LIST_HEAD(&fw_image_list);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c
index 1860c572b3c4..4fb9635d3919 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c
@@ -1015,9 +1015,10 @@ static bool dma64_txidle(struct dma_info *di)
/*
* post receive buffers
- * return false is refill failed completely and ring is empty this will stall
- * the rx dma and user might want to call rxfill again asap. This unlikely
- * happens on memory-rich NIC, but often on memory-constrained dongle
+ * Return false if refill failed completely or dma mapping failed. The ring
+ * is empty, which will stall the rx dma and user might want to call rxfill
+ * again asap. This is unlikely to happen on a memory-rich NIC, but often on
+ * memory-constrained dongle.
*/
bool dma_rxfill(struct dma_pub *pub)
{
@@ -1078,6 +1079,8 @@ bool dma_rxfill(struct dma_pub *pub)
pa = dma_map_single(di->dmadev, p->data, di->rxbufsize,
DMA_FROM_DEVICE);
+ if (dma_mapping_error(di->dmadev, pa))
+ return false;
/* save the free packet pointer */
di->rxp[rxout] = p;
@@ -1284,7 +1287,11 @@ static void dma_txenq(struct dma_info *di, struct sk_buff *p)
/* get physical address of buffer start */
pa = dma_map_single(di->dmadev, data, len, DMA_TO_DEVICE);
-
+ /* if mapping failed, free skb */
+ if (dma_mapping_error(di->dmadev, pa)) {
+ brcmu_pkt_buf_free_skb(p);
+ return;
+ }
/* With a DMA segment list, Descriptor table is filled
* using the segment list instead of looping over
* buffers in multi-chain DMA. Therefore, EOF for SGLIST
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index b5ab8d1bcac0..5282088d6c14 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -268,6 +268,12 @@ const struct iwl_cfg iwl6035_2agn_cfg = {
.ht_params = &iwl6000_ht_params,
};
+const struct iwl_cfg iwl6035_2agn_sff_cfg = {
+ .name = "Intel(R) Centrino(R) Ultimate-N 6235 AGN",
+ IWL_DEVICE_6035,
+ .ht_params = &iwl6000_ht_params,
+};
+
const struct iwl_cfg iwl1030_bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 1030 BGN",
IWL_DEVICE_6030,
diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c
index dc94d44d95cd..822443c5a3b5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-7000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-7000.c
@@ -125,7 +125,7 @@ static const struct iwl_ht_params iwl7000_ht_params = {
const struct iwl_cfg iwl7260_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC7260",
+ .name = "Intel(R) Dual Band Wireless AC 7260",
.fw_name_pre = IWL7260_FW_PRE,
IWL_DEVICE_7000,
.ht_params = &iwl7000_ht_params,
@@ -133,8 +133,44 @@ const struct iwl_cfg iwl7260_2ac_cfg = {
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
};
-const struct iwl_cfg iwl3160_ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC3160",
+const struct iwl_cfg iwl7260_2n_cfg = {
+ .name = "Intel(R) Dual Band Wireless N 7260",
+ .fw_name_pre = IWL7260_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL7260_NVM_VERSION,
+ .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl7260_n_cfg = {
+ .name = "Intel(R) Wireless N 7260",
+ .fw_name_pre = IWL7260_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL7260_NVM_VERSION,
+ .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl3160_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 3160",
+ .fw_name_pre = IWL3160_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL3160_NVM_VERSION,
+ .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl3160_2n_cfg = {
+ .name = "Intel(R) Dual Band Wireless N 3160",
+ .fw_name_pre = IWL3160_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL3160_NVM_VERSION,
+ .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl3160_n_cfg = {
+ .name = "Intel(R) Wireless N 3160",
.fw_name_pre = IWL3160_FW_PRE,
IWL_DEVICE_7000,
.ht_params = &iwl7000_ht_params,
diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h
index c38aa8f77554..44e3370ce343 100644
--- a/drivers/net/wireless/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/iwlwifi/iwl-config.h
@@ -316,10 +316,15 @@ extern const struct iwl_cfg iwl2000_2bgn_cfg;
extern const struct iwl_cfg iwl2000_2bgn_d_cfg;
extern const struct iwl_cfg iwl2030_2bgn_cfg;
extern const struct iwl_cfg iwl6035_2agn_cfg;
+extern const struct iwl_cfg iwl6035_2agn_sff_cfg;
extern const struct iwl_cfg iwl105_bgn_cfg;
extern const struct iwl_cfg iwl105_bgn_d_cfg;
extern const struct iwl_cfg iwl135_bgn_cfg;
extern const struct iwl_cfg iwl7260_2ac_cfg;
-extern const struct iwl_cfg iwl3160_ac_cfg;
+extern const struct iwl_cfg iwl7260_2n_cfg;
+extern const struct iwl_cfg iwl7260_n_cfg;
+extern const struct iwl_cfg iwl3160_2ac_cfg;
+extern const struct iwl_cfg iwl3160_2n_cfg;
+extern const struct iwl_cfg iwl3160_n_cfg;
#endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
index 5283b5552e6f..b53e5c3f403b 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c
@@ -138,13 +138,16 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
/* 6x00 Series */
{IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1108, iwl6000_3agn_cfg)},
{IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1128, iwl6000_3agn_cfg)},
{IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)},
{IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)},
{IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)},
{IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)},
{IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)},
{IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x4238, 0x1118, iwl6000_3agn_cfg)},
{IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)},
{IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)},
@@ -152,12 +155,16 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
{IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)},
{IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)},
{IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1308, iwl6005_2agn_cfg)},
{IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)},
{IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0082, 0x1328, iwl6005_2agn_cfg)},
{IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0x1318, iwl6005_2agn_cfg)},
{IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)},
{IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)},
{IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)},
+ {IWL_PCI_DEVICE(0x0085, 0xC228, iwl6005_2agn_sff_cfg)},
{IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)},
{IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */
{IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */
@@ -239,8 +246,11 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
/* 6x35 Series */
{IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x406A, iwl6035_2agn_sff_cfg)},
{IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x088F, 0x426A, iwl6035_2agn_sff_cfg)},
{IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6035_2agn_sff_cfg)},
{IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)},
{IWL_PCI_DEVICE(0x088F, 0x5260, iwl6035_2agn_cfg)},
@@ -257,10 +267,83 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
/* 7000 Series */
{IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x406A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4272, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x426A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4472, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x446A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4570, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4560, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
{IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC072, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC06A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC770, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC760, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC272, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC26A, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC472, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC570, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC560, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC370, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC360, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC02A, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)},
+
+/* 3160 Series */
+ {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0072, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0172, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x0272, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0472, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x0370, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8072, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8172, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)},
{0}
};
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c
index a78e0651409c..d69d02435b6b 100644
--- a/drivers/net/wireless/mwifiex/11n_aggr.c
+++ b/drivers/net/wireless/mwifiex/11n_aggr.c
@@ -149,7 +149,7 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv,
*/
int
mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
- struct mwifiex_ra_list_tbl *pra_list, int headroom,
+ struct mwifiex_ra_list_tbl *pra_list,
int ptrindex, unsigned long ra_list_flags)
__releases(&priv->wmm.ra_list_spinlock)
{
@@ -159,6 +159,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
int pad = 0, ret;
struct mwifiex_tx_param tx_param;
struct txpd *ptx_pd = NULL;
+ int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN;
skb_src = skb_peek(&pra_list->skb_head);
if (!skb_src) {
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/mwifiex/11n_aggr.h
index 900e1c62a0cc..892098d6a696 100644
--- a/drivers/net/wireless/mwifiex/11n_aggr.h
+++ b/drivers/net/wireless/mwifiex/11n_aggr.h
@@ -26,7 +26,7 @@
int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv,
struct sk_buff *skb);
int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
- struct mwifiex_ra_list_tbl *ptr, int headroom,
+ struct mwifiex_ra_list_tbl *ptr,
int ptr_index, unsigned long flags)
__releases(&priv->wmm.ra_list_spinlock);
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index 26755d9acb55..7a97f6cba089 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -1154,7 +1154,7 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions);
if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) &&
- adapter->iface_type == MWIFIEX_SDIO) {
+ adapter->iface_type != MWIFIEX_USB) {
mwifiex_hs_activated_event(priv, true);
return 0;
} else {
@@ -1166,8 +1166,7 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
}
if (conditions != HS_CFG_CANCEL) {
adapter->is_hs_configured = true;
- if (adapter->iface_type == MWIFIEX_USB ||
- adapter->iface_type == MWIFIEX_PCIE)
+ if (adapter->iface_type == MWIFIEX_USB)
mwifiex_hs_activated_event(priv, true);
} else {
adapter->is_hs_configured = false;
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index c4a2e775fe1a..fc3fe8ddcf62 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -270,10 +270,12 @@ process_start:
}
} while (true);
- if ((adapter->int_status) || IS_CARD_RX_RCVD(adapter))
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ if ((adapter->int_status) || IS_CARD_RX_RCVD(adapter)) {
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
goto process_start;
+ }
- spin_lock_irqsave(&adapter->main_proc_lock, flags);
adapter->mwifiex_processing = false;
spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
@@ -559,9 +561,8 @@ static void mwifiex_set_multicast_list(struct net_device *dev)
mcast_list.mode = MWIFIEX_ALL_MULTI_MODE;
} else {
mcast_list.mode = MWIFIEX_MULTICAST_MODE;
- if (netdev_mc_count(dev))
- mcast_list.num_multicast_addr =
- mwifiex_copy_mcast_addr(&mcast_list, dev);
+ mcast_list.num_multicast_addr =
+ mwifiex_copy_mcast_addr(&mcast_list, dev);
}
mwifiex_request_set_multicast_list(priv, &mcast_list);
}
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index 1a8a19dbd635..23aa910bc5d0 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -104,16 +104,14 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
} else {
priv->curr_pkt_filter &=
~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
- if (mcast_list->num_multicast_addr) {
- dev_dbg(priv->adapter->dev,
- "info: Set multicast list=%d\n",
- mcast_list->num_multicast_addr);
- /* Send multicast addresses to firmware */
- ret = mwifiex_send_cmd_async(priv,
- HostCmd_CMD_MAC_MULTICAST_ADR,
- HostCmd_ACT_GEN_SET, 0,
- mcast_list);
- }
+ dev_dbg(priv->adapter->dev,
+ "info: Set multicast list=%d\n",
+ mcast_list->num_multicast_addr);
+ /* Send multicast addresses to firmware */
+ ret = mwifiex_send_cmd_async(priv,
+ HostCmd_CMD_MAC_MULTICAST_ADR,
+ HostCmd_ACT_GEN_SET, 0,
+ mcast_list);
}
}
dev_dbg(priv->adapter->dev,
diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c
index f90fe21e5bfd..b7adf3d46463 100644
--- a/drivers/net/wireless/mwifiex/usb.c
+++ b/drivers/net/wireless/mwifiex/usb.c
@@ -446,9 +446,6 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message)
*/
adapter->is_suspended = true;
- for (i = 0; i < adapter->priv_num; i++)
- netif_carrier_off(adapter->priv[i]->netdev);
-
if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb)
usb_kill_urb(card->rx_cmd.urb);
@@ -508,10 +505,6 @@ static int mwifiex_usb_resume(struct usb_interface *intf)
MWIFIEX_RX_CMD_BUF_SIZE);
}
- for (i = 0; i < adapter->priv_num; i++)
- if (adapter->priv[i]->media_connected)
- netif_carrier_on(adapter->priv[i]->netdev);
-
/* Disable Host Sleep */
if (adapter->hs_activated)
mwifiex_cancel_hs(mwifiex_get_priv(adapter,
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c
index 4be3d33ceae8..ae31e8df44d7 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/mwifiex/wmm.c
@@ -1236,8 +1236,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
if (mwifiex_is_amsdu_allowed(priv, tid) &&
mwifiex_is_11n_aggragation_possible(priv, ptr,
adapter->tx_buf_size))
- mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN,
- ptr_index, flags);
+ mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags);
/* ra_list_spinlock has been freed in
mwifiex_11n_aggregate_pkt() */
else
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
index b9deef66cf4b..f42dc3ccd496 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/p54/p54usb.c
@@ -83,6 +83,7 @@ static struct usb_device_id p54u_table[] = {
{USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */
{USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */
{USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */
+ {USB_DEVICE(0x07aa, 0x0020)}, /* Corega WLUSB2GTST USB */
{USB_DEVICE(0x0803, 0x4310)}, /* Zoom 4410a */
{USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */
{USB_DEVICE(0x083a, 0x4531)}, /* T-Com Sinus 154 data II */
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 7e66a903cfee..f281971919f5 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -2767,6 +2767,13 @@ static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev)
int i;
/*
+ * First check if temperature compensation is supported.
+ */
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+ if (!rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC))
+ return 0;
+
+ /*
* Read TSSI boundaries for temperature compensation from
* the EEPROM.
*
@@ -4041,10 +4048,6 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
u8 reg_id;
u8 value;
- if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) ||
- rt2800_wait_bbp_ready(rt2x00dev)))
- return -EACCES;
-
if (rt2x00_rt(rt2x00dev, RT5592)) {
rt2800_init_bbp_5592(rt2x00dev);
return 0;
@@ -5185,20 +5188,23 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
rt2800_init_registers(rt2x00dev)))
return -EIO;
+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev)))
+ return -EIO;
+
/*
* Send signal to firmware during boot time.
*/
rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
- if (rt2x00_is_usb(rt2x00dev)) {
+ if (rt2x00_is_usb(rt2x00dev))
rt2800_register_write(rt2x00dev, H2M_INT_SRC, 0);
- rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0);
- }
+ rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0);
msleep(1);
- if (unlikely(rt2800_init_bbp(rt2x00dev)))
+ if (unlikely(rt2800_wait_bbp_ready(rt2x00dev)))
return -EIO;
+ rt2800_init_bbp(rt2x00dev);
rt2800_init_rfcsr(rt2x00dev);
if (rt2x00_is_usb(rt2x00dev) &&
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
index 763cf1defab5..5a060e537fbe 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
@@ -343,7 +343,8 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw,
(bool)GET_RX_DESC_PAGGR(pdesc));
rx_status->mactime = GET_RX_DESC_TSFL(pdesc);
if (phystatus) {
- p_drvinfo = (struct rx_fwinfo_92c *)(pdesc + RTL_RX_DESC_SIZE);
+ p_drvinfo = (struct rx_fwinfo_92c *)(skb->data +
+ stats->rx_bufshift);
rtl92c_translate_rx_signal_stuff(hw, skb, stats, pdesc,
p_drvinfo);
}
diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h
index cc03e7c87cbe..703258742d28 100644
--- a/drivers/net/wireless/rtlwifi/wifi.h
+++ b/drivers/net/wireless/rtlwifi/wifi.h
@@ -2057,7 +2057,7 @@ struct rtl_priv {
that it points to the data allocated
beyond this structure like:
rtl_pci_priv or rtl_usb_priv */
- u8 priv[0];
+ u8 priv[0] __aligned(sizeof(void *));
};
#define rtl_priv(hw) (((struct rtl_priv *)(hw)->priv))
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
index 9d7f1723dd8f..f2faa779e3fe 100644
--- a/drivers/net/xen-netback/common.h
+++ b/drivers/net/xen-netback/common.h
@@ -88,6 +88,7 @@ struct xenvif {
unsigned long credit_usec;
unsigned long remaining_credit;
struct timer_list credit_timeout;
+ u64 credit_window_start;
/* Statistics */
unsigned long rx_gso_checksum_fixup;
@@ -115,6 +116,7 @@ struct xenvif *xenvif_alloc(struct device *parent,
int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
unsigned long rx_ring_ref, unsigned int evtchn);
void xenvif_disconnect(struct xenvif *vif);
+void xenvif_free(struct xenvif *vif);
void xenvif_get(struct xenvif *vif);
void xenvif_put(struct xenvif *vif);
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
index d98414168485..c4a2eb2cd8a0 100644
--- a/drivers/net/xen-netback/interface.c
+++ b/drivers/net/xen-netback/interface.c
@@ -275,8 +275,7 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
vif->credit_bytes = vif->remaining_credit = ~0UL;
vif->credit_usec = 0UL;
init_timer(&vif->credit_timeout);
- /* Initialize 'expires' now: it's used to track the credit window. */
- vif->credit_timeout.expires = jiffies;
+ vif->credit_window_start = get_jiffies_64();
dev->netdev_ops = &xenvif_netdev_ops;
dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
@@ -304,6 +303,9 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
}
netdev_dbg(dev, "Successfully created xenvif\n");
+
+ __module_get(THIS_MODULE);
+
return vif;
}
@@ -369,9 +371,14 @@ void xenvif_disconnect(struct xenvif *vif)
if (vif->irq)
unbind_from_irqhandler(vif->irq, vif);
- unregister_netdev(vif->dev);
-
xen_netbk_unmap_frontend_rings(vif);
+}
+
+void xenvif_free(struct xenvif *vif)
+{
+ unregister_netdev(vif->dev);
free_netdev(vif->dev);
+
+ module_put(THIS_MODULE);
}
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 8c20935d72c9..36efb418c26f 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -354,6 +354,49 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
return false;
}
+struct xenvif_count_slot_state {
+ unsigned long copy_off;
+ bool head;
+};
+
+unsigned int xenvif_count_frag_slots(struct xenvif *vif,
+ unsigned long offset, unsigned long size,
+ struct xenvif_count_slot_state *state)
+{
+ unsigned count = 0;
+
+ offset &= ~PAGE_MASK;
+
+ while (size > 0) {
+ unsigned long bytes;
+
+ bytes = PAGE_SIZE - offset;
+
+ if (bytes > size)
+ bytes = size;
+
+ if (start_new_rx_buffer(state->copy_off, bytes, state->head)) {
+ count++;
+ state->copy_off = 0;
+ }
+
+ if (state->copy_off + bytes > MAX_BUFFER_OFFSET)
+ bytes = MAX_BUFFER_OFFSET - state->copy_off;
+
+ state->copy_off += bytes;
+
+ offset += bytes;
+ size -= bytes;
+
+ if (offset == PAGE_SIZE)
+ offset = 0;
+
+ state->head = false;
+ }
+
+ return count;
+}
+
/*
* Figure out how many ring slots we're going to need to send @skb to
* the guest. This function is essentially a dry run of
@@ -361,48 +404,39 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
*/
unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb)
{
+ struct xenvif_count_slot_state state;
unsigned int count;
- int i, copy_off;
+ unsigned char *data;
+ unsigned i;
- count = DIV_ROUND_UP(skb_headlen(skb), PAGE_SIZE);
+ state.head = true;
+ state.copy_off = 0;
- copy_off = skb_headlen(skb) % PAGE_SIZE;
+ /* Slot for the first (partial) page of data. */
+ count = 1;
+ /* Need a slot for the GSO prefix for GSO extra data? */
if (skb_shinfo(skb)->gso_size)
count++;
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- unsigned long size = skb_frag_size(&skb_shinfo(skb)->frags[i]);
- unsigned long offset = skb_shinfo(skb)->frags[i].page_offset;
- unsigned long bytes;
-
- offset &= ~PAGE_MASK;
-
- while (size > 0) {
- BUG_ON(offset >= PAGE_SIZE);
- BUG_ON(copy_off > MAX_BUFFER_OFFSET);
-
- bytes = PAGE_SIZE - offset;
-
- if (bytes > size)
- bytes = size;
+ data = skb->data;
+ while (data < skb_tail_pointer(skb)) {
+ unsigned long offset = offset_in_page(data);
+ unsigned long size = PAGE_SIZE - offset;
- if (start_new_rx_buffer(copy_off, bytes, 0)) {
- count++;
- copy_off = 0;
- }
+ if (data + size > skb_tail_pointer(skb))
+ size = skb_tail_pointer(skb) - data;
- if (copy_off + bytes > MAX_BUFFER_OFFSET)
- bytes = MAX_BUFFER_OFFSET - copy_off;
+ count += xenvif_count_frag_slots(vif, offset, size, &state);
- copy_off += bytes;
+ data += size;
+ }
- offset += bytes;
- size -= bytes;
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ unsigned long size = skb_frag_size(&skb_shinfo(skb)->frags[i]);
+ unsigned long offset = skb_shinfo(skb)->frags[i].page_offset;
- if (offset == PAGE_SIZE)
- offset = 0;
- }
+ count += xenvif_count_frag_slots(vif, offset, size, &state);
}
return count;
}
@@ -1389,9 +1423,8 @@ out:
static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
{
- unsigned long now = jiffies;
- unsigned long next_credit =
- vif->credit_timeout.expires +
+ u64 now = get_jiffies_64();
+ u64 next_credit = vif->credit_window_start +
msecs_to_jiffies(vif->credit_usec / 1000);
/* Timer could already be pending in rare cases. */
@@ -1399,8 +1432,8 @@ static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
return true;
/* Passed the point where we can replenish credit? */
- if (time_after_eq(now, next_credit)) {
- vif->credit_timeout.expires = now;
+ if (time_after_eq64(now, next_credit)) {
+ vif->credit_window_start = now;
tx_add_credit(vif);
}
@@ -1412,6 +1445,7 @@ static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
tx_credit_callback;
mod_timer(&vif->credit_timeout,
next_credit);
+ vif->credit_window_start = next_credit;
return true;
}
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index 410018c4c528..8a9e8750703f 100644
--- a/drivers/net/xen-netback/xenbus.c
+++ b/drivers/net/xen-netback/xenbus.c
@@ -24,6 +24,12 @@
struct backend_info {
struct xenbus_device *dev;
struct xenvif *vif;
+
+ /* This is the state that will be reflected in xenstore when any
+ * active hotplug script completes.
+ */
+ enum xenbus_state state;
+
enum xenbus_state frontend_state;
struct xenbus_watch hotplug_status_watch;
u8 have_hotplug_status_watch:1;
@@ -33,16 +39,20 @@ static int connect_rings(struct backend_info *);
static void connect(struct backend_info *);
static void backend_create_xenvif(struct backend_info *be);
static void unregister_hotplug_status_watch(struct backend_info *be);
+static void set_backend_state(struct backend_info *be,
+ enum xenbus_state state);
static int netback_remove(struct xenbus_device *dev)
{
struct backend_info *be = dev_get_drvdata(&dev->dev);
+ set_backend_state(be, XenbusStateClosed);
+
unregister_hotplug_status_watch(be);
if (be->vif) {
kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE);
xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status");
- xenvif_disconnect(be->vif);
+ xenvif_free(be->vif);
be->vif = NULL;
}
kfree(be);
@@ -126,6 +136,8 @@ static int netback_probe(struct xenbus_device *dev,
if (err)
goto fail;
+ be->state = XenbusStateInitWait;
+
/* This kicks hotplug scripts, so do it immediately. */
backend_create_xenvif(be);
@@ -198,15 +210,113 @@ static void backend_create_xenvif(struct backend_info *be)
kobject_uevent(&dev->dev.kobj, KOBJ_ONLINE);
}
+static void backend_disconnect(struct backend_info *be)
+{
+ if (be->vif)
+ xenvif_disconnect(be->vif);
+}
-static void disconnect_backend(struct xenbus_device *dev)
+static void backend_connect(struct backend_info *be)
{
- struct backend_info *be = dev_get_drvdata(&dev->dev);
+ if (be->vif)
+ connect(be);
+}
- if (be->vif) {
- xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status");
- xenvif_disconnect(be->vif);
- be->vif = NULL;
+static inline void backend_switch_state(struct backend_info *be,
+ enum xenbus_state state)
+{
+ struct xenbus_device *dev = be->dev;
+
+ pr_debug("%s -> %s\n", dev->nodename, xenbus_strstate(state));
+ be->state = state;
+
+ /* If we are waiting for a hotplug script then defer the
+ * actual xenbus state change.
+ */
+ if (!be->have_hotplug_status_watch)
+ xenbus_switch_state(dev, state);
+}
+
+/* Handle backend state transitions:
+ *
+ * The backend state starts in InitWait and the following transitions are
+ * allowed.
+ *
+ * InitWait -> Connected
+ *
+ * ^ \ |
+ * | \ |
+ * | \ |
+ * | \ |
+ * | \ |
+ * | \ |
+ * | V V
+ *
+ * Closed <-> Closing
+ *
+ * The state argument specifies the eventual state of the backend and the
+ * function transitions to that state via the shortest path.
+ */
+static void set_backend_state(struct backend_info *be,
+ enum xenbus_state state)
+{
+ while (be->state != state) {
+ switch (be->state) {
+ case XenbusStateClosed:
+ switch (state) {
+ case XenbusStateInitWait:
+ case XenbusStateConnected:
+ pr_info("%s: prepare for reconnect\n",
+ be->dev->nodename);
+ backend_switch_state(be, XenbusStateInitWait);
+ break;
+ case XenbusStateClosing:
+ backend_switch_state(be, XenbusStateClosing);
+ break;
+ default:
+ BUG();
+ }
+ break;
+ case XenbusStateInitWait:
+ switch (state) {
+ case XenbusStateConnected:
+ backend_connect(be);
+ backend_switch_state(be, XenbusStateConnected);
+ break;
+ case XenbusStateClosing:
+ case XenbusStateClosed:
+ backend_switch_state(be, XenbusStateClosing);
+ break;
+ default:
+ BUG();
+ }
+ break;
+ case XenbusStateConnected:
+ switch (state) {
+ case XenbusStateInitWait:
+ case XenbusStateClosing:
+ case XenbusStateClosed:
+ backend_disconnect(be);
+ backend_switch_state(be, XenbusStateClosing);
+ break;
+ default:
+ BUG();
+ }
+ break;
+ case XenbusStateClosing:
+ switch (state) {
+ case XenbusStateInitWait:
+ case XenbusStateConnected:
+ case XenbusStateClosed:
+ backend_switch_state(be, XenbusStateClosed);
+ break;
+ default:
+ BUG();
+ }
+ break;
+ default:
+ BUG();
+ }
}
}
@@ -218,43 +328,33 @@ static void frontend_changed(struct xenbus_device *dev,
{
struct backend_info *be = dev_get_drvdata(&dev->dev);
- pr_debug("frontend state %s", xenbus_strstate(frontend_state));
+ pr_debug("%s -> %s\n", dev->otherend, xenbus_strstate(frontend_state));
be->frontend_state = frontend_state;
switch (frontend_state) {
case XenbusStateInitialising:
- if (dev->state == XenbusStateClosed) {
- printk(KERN_INFO "%s: %s: prepare for reconnect\n",
- __func__, dev->nodename);
- xenbus_switch_state(dev, XenbusStateInitWait);
- }
+ set_backend_state(be, XenbusStateInitWait);
break;
case XenbusStateInitialised:
break;
case XenbusStateConnected:
- if (dev->state == XenbusStateConnected)
- break;
- backend_create_xenvif(be);
- if (be->vif)
- connect(be);
+ set_backend_state(be, XenbusStateConnected);
break;
case XenbusStateClosing:
- if (be->vif)
- kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE);
- disconnect_backend(dev);
- xenbus_switch_state(dev, XenbusStateClosing);
+ set_backend_state(be, XenbusStateClosing);
break;
case XenbusStateClosed:
- xenbus_switch_state(dev, XenbusStateClosed);
+ set_backend_state(be, XenbusStateClosed);
if (xenbus_dev_is_online(dev))
break;
/* fall through if not online */
case XenbusStateUnknown:
+ set_backend_state(be, XenbusStateClosed);
device_unregister(&dev->dev);
break;
@@ -347,7 +447,9 @@ static void hotplug_status_changed(struct xenbus_watch *watch,
if (IS_ERR(str))
return;
if (len == sizeof("connected")-1 && !memcmp(str, "connected", len)) {
- xenbus_switch_state(be->dev, XenbusStateConnected);
+ /* Complete any pending state change */
+ xenbus_switch_state(be->dev, be->state);
+
/* Not interested in this watch anymore. */
unregister_hotplug_status_watch(be);
}
@@ -377,12 +479,8 @@ static void connect(struct backend_info *be)
err = xenbus_watch_pathfmt(dev, &be->hotplug_status_watch,
hotplug_status_changed,
"%s/%s", dev->nodename, "hotplug-status");
- if (err) {
- /* Switch now, since we can't do a watch. */
- xenbus_switch_state(dev, XenbusStateConnected);
- } else {
+ if (!err)
be->have_hotplug_status_watch = 1;
- }
netif_wake_queue(be->vif->dev);
}
diff --git a/drivers/ntb/ntb_hw.c b/drivers/ntb/ntb_hw.c
index 2dacd19e1b8a..b9bf8b551e3c 100644
--- a/drivers/ntb/ntb_hw.c
+++ b/drivers/ntb/ntb_hw.c
@@ -78,6 +78,8 @@ enum {
BWD_HW,
};
+static struct dentry *debugfs_dir;
+
/* Translate memory window 0,1 to BAR 2,4 */
#define MW_TO_BAR(mw) (mw * 2 + 2)
@@ -531,9 +533,9 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
}
if (val & SNB_PPD_DEV_TYPE)
- ndev->dev_type = NTB_DEV_DSD;
- else
ndev->dev_type = NTB_DEV_USD;
+ else
+ ndev->dev_type = NTB_DEV_DSD;
ndev->reg_ofs.pdb = ndev->reg_base + SNB_PDOORBELL_OFFSET;
ndev->reg_ofs.pdb_mask = ndev->reg_base + SNB_PDBMSK_OFFSET;
@@ -547,7 +549,7 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
if (ndev->conn_type == NTB_CONN_B2B) {
ndev->reg_ofs.sdb = ndev->reg_base + SNB_B2B_DOORBELL_OFFSET;
ndev->reg_ofs.spad_write = ndev->reg_base + SNB_B2B_SPAD_OFFSET;
- ndev->limits.max_spads = SNB_MAX_SPADS;
+ ndev->limits.max_spads = SNB_MAX_B2B_SPADS;
} else {
ndev->reg_ofs.sdb = ndev->reg_base + SNB_SDOORBELL_OFFSET;
ndev->reg_ofs.spad_write = ndev->reg_base + SNB_SPAD_OFFSET;
@@ -644,10 +646,16 @@ static int ntb_device_setup(struct ntb_device *ndev)
rc = -ENODEV;
}
+ if (rc)
+ return rc;
+
+ dev_info(&ndev->pdev->dev, "Device Type = %s\n",
+ ndev->dev_type == NTB_DEV_USD ? "USD/DSP" : "DSD/USP");
+
/* Enable Bus Master and Memory Space on the secondary side */
writew(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER, ndev->reg_ofs.spci_cmd);
- return rc;
+ return 0;
}
static void ntb_device_free(struct ntb_device *ndev)
@@ -992,6 +1000,28 @@ static void ntb_free_callbacks(struct ntb_device *ndev)
kfree(ndev->db_cb);
}
+static void ntb_setup_debugfs(struct ntb_device *ndev)
+{
+ if (!debugfs_initialized())
+ return;
+
+ if (!debugfs_dir)
+ debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
+
+ ndev->debugfs_dir = debugfs_create_dir(pci_name(ndev->pdev),
+ debugfs_dir);
+}
+
+static void ntb_free_debugfs(struct ntb_device *ndev)
+{
+ debugfs_remove_recursive(ndev->debugfs_dir);
+
+ if (debugfs_dir && simple_empty(debugfs_dir)) {
+ debugfs_remove_recursive(debugfs_dir);
+ debugfs_dir = NULL;
+ }
+}
+
static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct ntb_device *ndev;
@@ -1004,6 +1034,7 @@ static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ndev->pdev = pdev;
ndev->link_status = NTB_LINK_DOWN;
pci_set_drvdata(pdev, ndev);
+ ntb_setup_debugfs(ndev);
rc = pci_enable_device(pdev);
if (rc)
@@ -1100,6 +1131,7 @@ err2:
err1:
pci_disable_device(pdev);
err:
+ ntb_free_debugfs(ndev);
kfree(ndev);
dev_err(&pdev->dev, "Error loading %s module\n", KBUILD_MODNAME);
@@ -1129,6 +1161,7 @@ static void ntb_pci_remove(struct pci_dev *pdev)
iounmap(ndev->reg_base);
pci_release_selected_regions(pdev, NTB_BAR_MASK);
pci_disable_device(pdev);
+ ntb_free_debugfs(ndev);
kfree(ndev);
}
diff --git a/drivers/ntb/ntb_hw.h b/drivers/ntb/ntb_hw.h
index 3a3038ca83e6..6a4f56f564ee 100644
--- a/drivers/ntb/ntb_hw.h
+++ b/drivers/ntb/ntb_hw.h
@@ -127,6 +127,8 @@ struct ntb_device {
unsigned char link_status;
struct delayed_work hb_timer;
unsigned long last_ts;
+
+ struct dentry *debugfs_dir;
};
/**
@@ -155,6 +157,20 @@ static inline struct pci_dev *ntb_query_pdev(struct ntb_device *ndev)
return ndev->pdev;
}
+/**
+ * ntb_query_debugfs() - return the debugfs pointer
+ * @ndev: pointer to ntb_device instance
+ *
+ * Given the ntb pointer, return the debugfs directory pointer for the NTB
+ * hardware device
+ *
+ * RETURNS: a pointer to the debugfs directory
+ */
+static inline struct dentry *ntb_query_debugfs(struct ntb_device *ndev)
+{
+ return ndev->debugfs_dir;
+}
+
struct ntb_device *ntb_register_transport(struct pci_dev *pdev,
void *transport);
void ntb_unregister_transport(struct ntb_device *ndev);
diff --git a/drivers/ntb/ntb_regs.h b/drivers/ntb/ntb_regs.h
index 5bfa8c06c059..96209b4abc22 100644
--- a/drivers/ntb/ntb_regs.h
+++ b/drivers/ntb/ntb_regs.h
@@ -53,8 +53,8 @@
#define NTB_LINK_WIDTH_MASK 0x03f0
#define SNB_MSIX_CNT 4
-#define SNB_MAX_SPADS 16
-#define SNB_MAX_COMPAT_SPADS 8
+#define SNB_MAX_B2B_SPADS 16
+#define SNB_MAX_COMPAT_SPADS 16
/* Reserve the uppermost bit for link interrupt */
#define SNB_MAX_DB_BITS 15
#define SNB_DB_BITS_PER_VEC 5
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index f8d7081ee301..c3089151aa49 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -157,7 +157,6 @@ struct ntb_transport {
bool transport_link;
struct delayed_work link_work;
struct work_struct link_cleanup;
- struct dentry *debugfs_dir;
};
enum {
@@ -824,12 +823,12 @@ static void ntb_transport_init_queue(struct ntb_transport *nt,
qp->tx_max_frame = min(transport_mtu, tx_size / 2);
qp->tx_max_entry = tx_size / qp->tx_max_frame;
- if (nt->debugfs_dir) {
+ if (ntb_query_debugfs(nt->ndev)) {
char debugfs_name[4];
snprintf(debugfs_name, 4, "qp%d", qp_num);
qp->debugfs_dir = debugfs_create_dir(debugfs_name,
- nt->debugfs_dir);
+ ntb_query_debugfs(nt->ndev));
qp->debugfs_stats = debugfs_create_file("stats", S_IRUSR,
qp->debugfs_dir, qp,
@@ -857,11 +856,6 @@ int ntb_transport_init(struct pci_dev *pdev)
if (!nt)
return -ENOMEM;
- if (debugfs_initialized())
- nt->debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
- else
- nt->debugfs_dir = NULL;
-
nt->ndev = ntb_register_transport(pdev, nt);
if (!nt->ndev) {
rc = -EIO;
@@ -907,7 +901,6 @@ err2:
err1:
ntb_unregister_transport(nt->ndev);
err:
- debugfs_remove_recursive(nt->debugfs_dir);
kfree(nt);
return rc;
}
@@ -921,16 +914,16 @@ void ntb_transport_free(void *transport)
nt->transport_link = NTB_LINK_DOWN;
/* verify that all the qp's are freed */
- for (i = 0; i < nt->max_qps; i++)
+ for (i = 0; i < nt->max_qps; i++) {
if (!test_bit(i, &nt->qp_bitmap))
ntb_transport_free_queue(&nt->qps[i]);
+ debugfs_remove_recursive(nt->qps[i].debugfs_dir);
+ }
ntb_bus_remove(nt);
cancel_delayed_work_sync(&nt->link_work);
- debugfs_remove_recursive(nt->debugfs_dir);
-
ntb_unregister_event_callback(nt->ndev);
pdev = ntb_query_pdev(nt->ndev);
diff --git a/drivers/of/base.c b/drivers/of/base.c
index a6f584a7f4a1..1d10b4ec6814 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1629,6 +1629,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
ap = dt_alloc(sizeof(*ap) + len + 1, 4);
if (!ap)
continue;
+ memset(ap, 0, sizeof(*ap) + len + 1);
ap->alias = start;
of_alias_add(ap, np, id, start, len);
}
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 336b3f94a19a..0eab3a3c51c5 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -47,6 +47,9 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev)
return;
+ if (pci_dev->pme_poll)
+ pci_dev->pme_poll = false;
+
if (pci_dev->current_state == PCI_D3cold) {
pci_wakeup_event(pci_dev);
pm_runtime_resume(&pci_dev->dev);
@@ -57,9 +60,6 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
if (pci_dev->pme_support)
pci_check_pme_status(pci_dev);
- if (pci_dev->pme_poll)
- pci_dev->pme_poll = false;
-
pci_wakeup_event(pci_dev);
pm_runtime_resume(&pci_dev->dev);
diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c
index 5d7529ed5392..314e5e8e9122 100644
--- a/drivers/pinctrl/pinctrl-at91.c
+++ b/drivers/pinctrl/pinctrl-at91.c
@@ -325,7 +325,7 @@ static void at91_mux_disable_interrupt(void __iomem *pio, unsigned mask)
static unsigned at91_mux_get_pullup(void __iomem *pio, unsigned pin)
{
- return (readl_relaxed(pio + PIO_PUSR) >> pin) & 0x1;
+ return !((readl_relaxed(pio + PIO_PUSR) >> pin) & 0x1);
}
static void at91_mux_set_pullup(void __iomem *pio, unsigned mask, bool on)
@@ -445,7 +445,7 @@ static void at91_mux_pio3_set_debounce(void __iomem *pio, unsigned mask,
static bool at91_mux_pio3_get_pulldown(void __iomem *pio, unsigned pin)
{
- return (__raw_readl(pio + PIO_PPDSR) >> pin) & 0x1;
+ return !((__raw_readl(pio + PIO_PPDSR) >> pin) & 0x1);
}
static void at91_mux_pio3_set_pulldown(void __iomem *pio, unsigned mask, bool is_on)
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 430ef8c8b6a8..7b8979c63f48 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -204,16 +204,6 @@ config BATTERY_MAX17042
with MAX17042. This driver also supports max17047/50 chips which are
improved version of max17042.
-config BATTERY_ANDROID
- tristate "Battery driver for Android"
- help
- Say Y to enable generic support for battery charging according
- to common Android policies.
- This driver adds periodic battery level and health monitoring,
- kernel log reporting and other debugging features, common board
- battery file glue logic for battery/case temperature sensors,
- etc.
-
config BATTERY_Z2
tristate "Z2 battery driver"
depends on I2C && MACH_ZIPIT2
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 27d9deef51b6..653bf6ceff30 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -16,7 +16,6 @@ obj-$(CONFIG_WM8350_POWER) += wm8350_power.o
obj-$(CONFIG_TEST_POWER) += test_power.o
obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
-obj-$(CONFIG_BATTERY_ANDROID) += android_battery.o
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c
deleted file mode 100644
index 8d45ff0f3677..000000000000
--- a/drivers/power/android_battery.c
+++ /dev/null
@@ -1,692 +0,0 @@
-/*
- * android_battery.c
- * Android Battery Driver
- *
- * Copyright (C) 2012 Google, Inc.
- * Copyright (C) 2012 Samsung Electronics
- *
- * Based on work by himihee.seo@samsung.com, ms925.kim@samsung.com, and
- * joshua.chang@samsung.com.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/jiffies.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-#include <linux/wakelock.h>
-#include <linux/workqueue.h>
-#include <linux/alarmtimer.h>
-#include <linux/timer.h>
-#include <linux/mutex.h>
-#include <linux/debugfs.h>
-#include <linux/platform_data/android_battery.h>
-
-#define FAST_POLL (1 * 60)
-#define SLOW_POLL (10 * 60)
-
-struct android_bat_data {
- struct android_bat_platform_data *pdata;
- struct android_bat_callbacks callbacks;
-
- struct device *dev;
-
- struct power_supply psy_bat;
-
- struct wake_lock monitor_wake_lock;
- struct wake_lock charger_wake_lock;
-
- int charge_source;
-
- int batt_temp;
- int batt_current;
- unsigned int batt_health;
- unsigned int batt_vcell;
- unsigned int batt_soc;
- unsigned int charging_status;
- bool recharging;
- unsigned long charging_start_time;
-
- struct workqueue_struct *monitor_wqueue;
- struct work_struct monitor_work;
- struct work_struct charger_work;
-
- struct alarm monitor_alarm;
- ktime_t last_poll;
-
- struct dentry *debugfs_entry;
-};
-
-static enum power_supply_property android_battery_props[] = {
- POWER_SUPPLY_PROP_STATUS,
- POWER_SUPPLY_PROP_HEALTH,
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_TEMP,
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_TECHNOLOGY,
- POWER_SUPPLY_PROP_CURRENT_NOW,
-};
-
-static DEFINE_MUTEX(android_bat_state_lock);
-
-static void android_bat_update_data(struct android_bat_data *battery);
-static int android_bat_enable_charging(struct android_bat_data *battery,
- bool enable);
-
-static char *charge_source_str(int charge_source)
-{
- switch (charge_source) {
- case CHARGE_SOURCE_NONE:
- return "none";
- case CHARGE_SOURCE_AC:
- return "ac";
- case CHARGE_SOURCE_USB:
- return "usb";
- default:
- break;
- }
-
- return "?";
-}
-
-static int android_bat_get_property(struct power_supply *ps,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct android_bat_data *battery =
- container_of(ps, struct android_bat_data, psy_bat);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- val->intval = battery->charging_status;
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- val->intval = battery->batt_health;
- break;
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = 1;
- break;
- case POWER_SUPPLY_PROP_TEMP:
- val->intval = battery->batt_temp;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- val->intval = 1;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- android_bat_update_data(battery);
- val->intval = battery->batt_vcell;
- if (val->intval == -1)
- return -EINVAL;
- break;
- case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = battery->batt_soc;
- if (val->intval == -1)
- return -EINVAL;
- break;
- case POWER_SUPPLY_PROP_TECHNOLOGY:
- val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
- break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- android_bat_update_data(battery);
- val->intval = battery->batt_current;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static void android_bat_get_temp(struct android_bat_data *battery)
-{
- int batt_temp = 42; /* 4.2C */
- int health = battery->batt_health;
-
- if (battery->pdata->get_temperature)
- battery->pdata->get_temperature(&batt_temp);
-
- if (battery->charge_source != CHARGE_SOURCE_NONE) {
- if (batt_temp >= battery->pdata->temp_high_threshold) {
- if (health != POWER_SUPPLY_HEALTH_OVERHEAT &&
- health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) {
- pr_info("battery overheat (%d>=%d), " \
- "charging unavailable\n",
- batt_temp,
- battery->pdata->temp_high_threshold);
- battery->batt_health =
- POWER_SUPPLY_HEALTH_OVERHEAT;
- }
- } else if (batt_temp <= battery->pdata->temp_high_recovery &&
- batt_temp >= battery->pdata->temp_low_recovery) {
- if (health == POWER_SUPPLY_HEALTH_OVERHEAT ||
- health == POWER_SUPPLY_HEALTH_COLD) {
- pr_info("battery recovery (%d,%d~%d)," \
- "charging available\n",
- batt_temp,
- battery->pdata->temp_low_recovery,
- battery->pdata->temp_high_recovery);
- battery->batt_health =
- POWER_SUPPLY_HEALTH_GOOD;
- }
- } else if (batt_temp <= battery->pdata->temp_low_threshold) {
- if (health != POWER_SUPPLY_HEALTH_COLD &&
- health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) {
- pr_info("battery cold (%d <= %d)," \
- "charging unavailable\n",
- batt_temp,
- battery->pdata->temp_low_threshold);
- battery->batt_health =
- POWER_SUPPLY_HEALTH_COLD;
- }
- }
- }
-
- battery->batt_temp = batt_temp;
-}
-
-/*
- * android_bat_state_lock not held, may call back into
- * android_bat_charge_source_changed. Gathering data here can be
- * non-atomic; updating our state based on the data may need to be
- * atomic.
- */
-
-static void android_bat_update_data(struct android_bat_data *battery)
-{
- int ret;
- int v;
-
- if (battery->pdata->poll_charge_source)
- battery->charge_source = battery->pdata->poll_charge_source();
-
- if (battery->pdata->get_voltage_now) {
- ret = battery->pdata->get_voltage_now();
- battery->batt_vcell = ret >= 0 ? ret : 4242000;
- }
-
- if (battery->pdata->get_capacity) {
- ret = battery->pdata->get_capacity();
- battery->batt_soc = ret >= 0 ? ret : 42;
- }
-
- if (battery->pdata->get_current_now) {
- ret = battery->pdata->get_current_now(&v);
-
- if (!ret)
- battery->batt_current = v;
- }
-
- android_bat_get_temp(battery);
-}
-
-static void android_bat_set_charge_time(struct android_bat_data *battery,
- bool enable)
-{
- if (enable && !battery->charging_start_time) {
- struct timespec cur_time;
-
- get_monotonic_boottime(&cur_time);
- /* record start time for charge timeout timer */
- battery->charging_start_time = cur_time.tv_sec;
- } else if (!enable) {
- /* clear charge timeout timer */
- battery->charging_start_time = 0;
- }
-}
-
-static int android_bat_enable_charging(struct android_bat_data *battery,
- bool enable)
-{
- if (enable && (battery->batt_health != POWER_SUPPLY_HEALTH_GOOD)) {
- battery->charging_status =
- POWER_SUPPLY_STATUS_NOT_CHARGING;
- return -EPERM;
- }
-
- if (enable) {
- if (battery->pdata && battery->pdata->set_charging_current)
- battery->pdata->set_charging_current
- (battery->charge_source);
- }
-
- if (battery->pdata && battery->pdata->set_charging_enable)
- battery->pdata->set_charging_enable(enable);
-
- android_bat_set_charge_time(battery, enable);
- pr_info("battery: enable=%d charger: %s\n", enable,
- charge_source_str(battery->charge_source));
- return 0;
-}
-
-static bool android_bat_charge_timeout(struct android_bat_data *battery,
- unsigned long timeout)
-{
- struct timespec cur_time;
-
- if (!battery->charging_start_time)
- return 0;
-
- get_monotonic_boottime(&cur_time);
- pr_debug("%s: Start time: %ld, End time: %ld, current time: %ld\n",
- __func__, battery->charging_start_time,
- battery->charging_start_time + timeout,
- cur_time.tv_sec);
- return cur_time.tv_sec >= battery->charging_start_time + timeout;
-}
-
-static void android_bat_charging_timer(struct android_bat_data *battery)
-{
- if (!battery->charging_start_time &&
- battery->charging_status == POWER_SUPPLY_STATUS_CHARGING) {
- android_bat_enable_charging(battery, true);
- battery->recharging = true;
- pr_debug("%s: charge status charging but timer is expired\n",
- __func__);
- } else if (battery->charging_start_time == 0) {
- pr_debug("%s: charging_start_time never initialized\n",
- __func__);
- return;
- }
-
- if (android_bat_charge_timeout(
- battery,
- battery->recharging ? battery->pdata->recharging_time :
- battery->pdata->full_charging_time)) {
- android_bat_enable_charging(battery, false);
- if (battery->batt_vcell >
- battery->pdata->recharging_voltage &&
- battery->batt_soc == 100)
- battery->charging_status =
- POWER_SUPPLY_STATUS_FULL;
- battery->recharging = false;
- battery->charging_start_time = 0;
- pr_info("battery: charging timer expired\n");
- }
-
- return;
-}
-
-static void android_bat_charge_source_changed(struct android_bat_callbacks *ptr,
- int charge_source)
-{
- struct android_bat_data *battery =
- container_of(ptr, struct android_bat_data, callbacks);
-
- wake_lock(&battery->charger_wake_lock);
- mutex_lock(&android_bat_state_lock);
- battery->charge_source = charge_source;
-
- pr_info("battery: charge source type was changed: %s\n",
- charge_source_str(battery->charge_source));
-
- mutex_unlock(&android_bat_state_lock);
- queue_work(battery->monitor_wqueue, &battery->charger_work);
-}
-
-static void android_bat_set_full_status(struct android_bat_callbacks *ptr)
-{
- struct android_bat_data *battery =
- container_of(ptr, struct android_bat_data, callbacks);
-
- mutex_lock(&android_bat_state_lock);
- pr_info("battery: battery full\n");
- battery->charging_status = POWER_SUPPLY_STATUS_FULL;
- android_bat_enable_charging(battery, false);
- battery->recharging = false;
- mutex_unlock(&android_bat_state_lock);
- power_supply_changed(&battery->psy_bat);
-}
-
-static void android_bat_charger_work(struct work_struct *work)
-{
- struct android_bat_data *battery =
- container_of(work, struct android_bat_data, charger_work);
-
- mutex_lock(&android_bat_state_lock);
-
- switch (battery->charge_source) {
- case CHARGE_SOURCE_NONE:
- battery->charging_status = POWER_SUPPLY_STATUS_DISCHARGING;
- android_bat_enable_charging(battery, false);
- battery->batt_health = POWER_SUPPLY_HEALTH_GOOD;
- battery->recharging = false;
- battery->charging_start_time = 0;
- break;
- case CHARGE_SOURCE_USB:
- case CHARGE_SOURCE_AC:
- /*
- * If charging status indicates a charger was already
- * connected prior to this and the status is something
- * other than charging ("full" or "not-charging"), leave
- * the status alone.
- */
- if (battery->charging_status ==
- POWER_SUPPLY_STATUS_DISCHARGING ||
- battery->charging_status == POWER_SUPPLY_STATUS_UNKNOWN)
- battery->charging_status = POWER_SUPPLY_STATUS_CHARGING;
-
- /*
- * Don't re-enable charging if the battery is full and we
- * are not actively re-charging it, or if "not-charging"
- * status is set.
- */
- if (!((battery->charging_status == POWER_SUPPLY_STATUS_FULL
- && !battery->recharging) || battery->charging_status ==
- POWER_SUPPLY_STATUS_NOT_CHARGING))
- android_bat_enable_charging(battery, true);
-
- break;
- default:
- pr_err("%s: Invalid charger type\n", __func__);
- break;
- }
-
- mutex_unlock(&android_bat_state_lock);
- wake_lock_timeout(&battery->charger_wake_lock, HZ * 2);
- power_supply_changed(&battery->psy_bat);
-}
-
-
-static void android_bat_monitor_set_alarm(struct android_bat_data *battery,
- int seconds)
-{
- alarm_start(&battery->monitor_alarm,
- ktime_add(battery->last_poll, ktime_set(seconds, 0)));
-}
-
-static void android_bat_monitor_work(struct work_struct *work)
-{
- struct android_bat_data *battery =
- container_of(work, struct android_bat_data, monitor_work);
- struct timespec cur_time;
-
- wake_lock(&battery->monitor_wake_lock);
- android_bat_update_data(battery);
- mutex_lock(&android_bat_state_lock);
-
- switch (battery->charging_status) {
- case POWER_SUPPLY_STATUS_FULL:
- if (battery->batt_vcell < battery->pdata->recharging_voltage &&
- !battery->recharging) {
- battery->recharging = true;
- android_bat_enable_charging(battery, true);
- pr_info("battery: start recharging, v=%d\n",
- battery->batt_vcell/1000);
- }
- break;
- case POWER_SUPPLY_STATUS_DISCHARGING:
- break;
- case POWER_SUPPLY_STATUS_CHARGING:
- switch (battery->batt_health) {
- case POWER_SUPPLY_HEALTH_OVERHEAT:
- case POWER_SUPPLY_HEALTH_COLD:
- case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
- case POWER_SUPPLY_HEALTH_DEAD:
- case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
- battery->charging_status =
- POWER_SUPPLY_STATUS_NOT_CHARGING;
- android_bat_enable_charging(battery, false);
-
- pr_info("battery: Not charging, health=%d\n",
- battery->batt_health);
- break;
- default:
- break;
- }
- break;
- case POWER_SUPPLY_STATUS_NOT_CHARGING:
- if (battery->batt_health == POWER_SUPPLY_HEALTH_GOOD) {
- pr_info("battery: battery health recovered\n");
- if (battery->charge_source != CHARGE_SOURCE_NONE) {
- android_bat_enable_charging(battery, true);
- battery->charging_status
- = POWER_SUPPLY_STATUS_CHARGING;
- } else {
- battery->charging_status
- = POWER_SUPPLY_STATUS_DISCHARGING;
- }
- }
- break;
- default:
- pr_err("%s: Undefined battery status: %d\n", __func__,
- battery->charging_status);
- break;
- }
-
- android_bat_charging_timer(battery);
- get_monotonic_boottime(&cur_time);
- pr_info("battery: l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n",
- battery->batt_soc, battery->batt_vcell/1000,
- battery->batt_current, battery->batt_temp < 0 ? "-" : "",
- abs(battery->batt_temp / 10), abs(battery->batt_temp % 10),
- battery->batt_health, battery->charging_status,
- battery->recharging ? "r" : "",
- battery->charging_start_time ?
- cur_time.tv_sec - battery->charging_start_time : 0,
- charge_source_str(battery->charge_source));
- mutex_unlock(&android_bat_state_lock);
- power_supply_changed(&battery->psy_bat);
- battery->last_poll = ktime_get_boottime();
- android_bat_monitor_set_alarm(battery, FAST_POLL);
- wake_unlock(&battery->monitor_wake_lock);
- return;
-}
-
-static enum alarmtimer_restart android_bat_monitor_alarm(
- struct alarm *alarm, ktime_t now)
-{
- struct android_bat_data *battery =
- container_of(alarm, struct android_bat_data, monitor_alarm);
-
- wake_lock(&battery->monitor_wake_lock);
- queue_work(battery->monitor_wqueue, &battery->monitor_work);
- return ALARMTIMER_NORESTART;
-}
-
-static int android_power_debug_dump(struct seq_file *s, void *unused)
-{
- struct android_bat_data *battery = s->private;
- struct timespec cur_time;
-
- android_bat_update_data(battery);
- get_monotonic_boottime(&cur_time);
- mutex_lock(&android_bat_state_lock);
- seq_printf(s, "l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n",
- battery->batt_soc, battery->batt_vcell/1000,
- battery->batt_current, battery->batt_temp < 0 ? "-" : "",
- abs(battery->batt_temp / 10), abs(battery->batt_temp % 10),
- battery->batt_health, battery->charging_status,
- battery->recharging ? "r" : "",
- battery->charging_start_time ?
- cur_time.tv_sec - battery->charging_start_time : 0,
- charge_source_str(battery->charge_source));
- mutex_unlock(&android_bat_state_lock);
- return 0;
-}
-
-static int android_power_debug_open(struct inode *inode, struct file *file)
-{
- return single_open(file, android_power_debug_dump, inode->i_private);
-}
-
-static const struct file_operations android_power_debug_fops = {
- .open = android_power_debug_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int android_bat_probe(struct platform_device *pdev)
-{
- struct android_bat_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct android_bat_data *battery;
- int ret = 0;
-
- dev_info(&pdev->dev, "Android Battery Driver\n");
- battery = kzalloc(sizeof(*battery), GFP_KERNEL);
- if (!battery)
- return -ENOMEM;
-
- battery->pdata = pdata;
- if (!battery->pdata) {
- pr_err("%s : No platform data\n", __func__);
- ret = -EINVAL;
- goto err_pdata;
- }
-
- battery->dev = &pdev->dev;
- platform_set_drvdata(pdev, battery);
- battery->batt_health = POWER_SUPPLY_HEALTH_GOOD;
-
- battery->psy_bat.name = "android-battery",
- battery->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY,
- battery->psy_bat.properties = android_battery_props,
- battery->psy_bat.num_properties = ARRAY_SIZE(android_battery_props),
- battery->psy_bat.get_property = android_bat_get_property,
-
- battery->batt_vcell = -1;
- battery->batt_soc = -1;
-
- wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND,
- "android-battery-monitor");
- wake_lock_init(&battery->charger_wake_lock, WAKE_LOCK_SUSPEND,
- "android-chargerdetect");
-
- ret = power_supply_register(&pdev->dev, &battery->psy_bat);
- if (ret) {
- dev_err(battery->dev, "%s: failed to register psy_bat\n",
- __func__);
- goto err_psy_bat_reg;
- }
-
- battery->monitor_wqueue =
- alloc_workqueue(dev_name(&pdev->dev), WQ_FREEZABLE, 1);
- if (!battery->monitor_wqueue) {
- dev_err(battery->dev, "%s: fail to create workqueue\n",
- __func__);
- goto err_wq;
- }
-
- INIT_WORK(&battery->monitor_work, android_bat_monitor_work);
- INIT_WORK(&battery->charger_work, android_bat_charger_work);
-
- battery->callbacks.charge_source_changed =
- android_bat_charge_source_changed;
- battery->callbacks.battery_set_full =
- android_bat_set_full_status;
- if (battery->pdata && battery->pdata->register_callbacks)
- battery->pdata->register_callbacks(&battery->callbacks);
-
- /* get initial charger status */
- if (battery->pdata->poll_charge_source)
- battery->charge_source = battery->pdata->poll_charge_source();
-
- wake_lock(&battery->charger_wake_lock);
- queue_work(battery->monitor_wqueue, &battery->charger_work);
-
- wake_lock(&battery->monitor_wake_lock);
- battery->last_poll = ktime_get_boottime();
- alarm_init(&battery->monitor_alarm, ALARM_BOOTTIME,
- android_bat_monitor_alarm);
- queue_work(battery->monitor_wqueue, &battery->monitor_work);
-
- battery->debugfs_entry =
- debugfs_create_file("android-power", S_IRUGO, NULL,
- battery, &android_power_debug_fops);
- if (!battery->debugfs_entry)
- pr_err("failed to create android-power debugfs entry\n");
-
- return 0;
-
-err_wq:
- power_supply_unregister(&battery->psy_bat);
-err_psy_bat_reg:
- wake_lock_destroy(&battery->monitor_wake_lock);
- wake_lock_destroy(&battery->charger_wake_lock);
-err_pdata:
- kfree(battery);
-
- return ret;
-}
-
-static int android_bat_remove(struct platform_device *pdev)
-{
- struct android_bat_data *battery = platform_get_drvdata(pdev);
-
- alarm_cancel(&battery->monitor_alarm);
- flush_workqueue(battery->monitor_wqueue);
- destroy_workqueue(battery->monitor_wqueue);
- power_supply_unregister(&battery->psy_bat);
- wake_lock_destroy(&battery->monitor_wake_lock);
- wake_lock_destroy(&battery->charger_wake_lock);
- debugfs_remove(battery->debugfs_entry);
- kfree(battery);
- return 0;
-}
-
-static int android_bat_suspend(struct device *dev)
-{
- struct android_bat_data *battery = dev_get_drvdata(dev);
-
- cancel_work_sync(&battery->monitor_work);
- android_bat_monitor_set_alarm(
- battery,
- battery->charge_source == CHARGE_SOURCE_NONE ?
- SLOW_POLL : FAST_POLL);
- return 0;
-}
-
-static void android_bat_resume(struct device *dev)
-{
- struct android_bat_data *battery = dev_get_drvdata(dev);
-
- android_bat_monitor_set_alarm(battery, FAST_POLL);
- return;
-}
-
-static const struct dev_pm_ops android_bat_pm_ops = {
- .prepare = android_bat_suspend,
- .complete = android_bat_resume,
-};
-
-static struct platform_driver android_bat_driver = {
- .driver = {
- .name = "android-battery",
- .owner = THIS_MODULE,
- .pm = &android_bat_pm_ops,
- },
- .probe = android_bat_probe,
- .remove = android_bat_remove,
-};
-
-static int __init android_bat_init(void)
-{
- return platform_driver_register(&android_bat_driver);
-}
-
-static void __exit android_bat_exit(void)
-{
- platform_driver_unregister(&android_bat_driver);
-}
-
-late_initcall(android_bat_init);
-module_exit(android_bat_exit);
-
-MODULE_DESCRIPTION("Android battery driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 408a42ef787a..f0d432c139d0 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -771,6 +771,8 @@ static long aac_compat_do_ioctl(struct aac_dev *dev, unsigned cmd, unsigned long
static int aac_compat_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
{
struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata;
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
return aac_compat_do_ioctl(dev, cmd, (unsigned long)arg);
}
diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c
index 34552bf1c023..55548dc5cec3 100644
--- a/drivers/scsi/esp_scsi.c
+++ b/drivers/scsi/esp_scsi.c
@@ -530,7 +530,7 @@ static int esp_need_to_nego_sync(struct esp_target_data *tp)
static int esp_alloc_lun_tag(struct esp_cmd_entry *ent,
struct esp_lun_data *lp)
{
- if (!ent->tag[0]) {
+ if (!ent->orig_tag[0]) {
/* Non-tagged, slot already taken? */
if (lp->non_tagged_cmd)
return -EBUSY;
@@ -564,9 +564,9 @@ static int esp_alloc_lun_tag(struct esp_cmd_entry *ent,
return -EBUSY;
}
- BUG_ON(lp->tagged_cmds[ent->tag[1]]);
+ BUG_ON(lp->tagged_cmds[ent->orig_tag[1]]);
- lp->tagged_cmds[ent->tag[1]] = ent;
+ lp->tagged_cmds[ent->orig_tag[1]] = ent;
lp->num_tagged++;
return 0;
@@ -575,9 +575,9 @@ static int esp_alloc_lun_tag(struct esp_cmd_entry *ent,
static void esp_free_lun_tag(struct esp_cmd_entry *ent,
struct esp_lun_data *lp)
{
- if (ent->tag[0]) {
- BUG_ON(lp->tagged_cmds[ent->tag[1]] != ent);
- lp->tagged_cmds[ent->tag[1]] = NULL;
+ if (ent->orig_tag[0]) {
+ BUG_ON(lp->tagged_cmds[ent->orig_tag[1]] != ent);
+ lp->tagged_cmds[ent->orig_tag[1]] = NULL;
lp->num_tagged--;
} else {
BUG_ON(lp->non_tagged_cmd != ent);
@@ -667,6 +667,8 @@ static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp)
ent->tag[0] = 0;
ent->tag[1] = 0;
}
+ ent->orig_tag[0] = ent->tag[0];
+ ent->orig_tag[1] = ent->tag[1];
if (esp_alloc_lun_tag(ent, lp) < 0)
continue;
diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h
index 28e22acf87ea..cd68805e8d78 100644
--- a/drivers/scsi/esp_scsi.h
+++ b/drivers/scsi/esp_scsi.h
@@ -271,6 +271,7 @@ struct esp_cmd_entry {
#define ESP_CMD_FLAG_AUTOSENSE 0x04 /* Doing automatic REQUEST_SENSE */
u8 tag[2];
+ u8 orig_tag[2];
u8 status;
u8 message;
diff --git a/drivers/scsi/mpt3sas/Makefile b/drivers/scsi/mpt3sas/Makefile
index 4c1d2e7a1176..efb0c4c2e310 100644
--- a/drivers/scsi/mpt3sas/Makefile
+++ b/drivers/scsi/mpt3sas/Makefile
@@ -1,5 +1,5 @@
# mpt3sas makefile
-obj-m += mpt3sas.o
+obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas.o
mpt3sas-y += mpt3sas_base.o \
mpt3sas_config.o \
mpt3sas_scsih.o \
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 610417ec45af..734a29a70f63 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2409,14 +2409,9 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
}
}
- if (modepage == 0x3F) {
- sd_printk(KERN_ERR, sdkp, "No Caching mode page "
- "present\n");
- goto defaults;
- } else if ((buffer[offset] & 0x3f) != modepage) {
- sd_printk(KERN_ERR, sdkp, "Got wrong page\n");
- goto defaults;
- }
+ sd_printk(KERN_ERR, sdkp, "No Caching mode page found\n");
+ goto defaults;
+
Page_found:
if (modepage == 8) {
sdkp->WCE = ((buffer[offset + 2] & 0x04) != 0);
@@ -2848,6 +2843,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
gd->events |= DISK_EVENT_MEDIA_CHANGE;
}
+ blk_pm_runtime_init(sdp->request_queue, dev);
add_disk(gd);
if (sdkp->capacity)
sd_dif_config_host(sdkp);
@@ -2856,7 +2852,6 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
sdp->removable ? "removable " : "");
- blk_pm_runtime_init(sdp->request_queue, dev);
scsi_autopm_put_device(sdp);
put_device(&sdkp->dev);
}
diff --git a/drivers/staging/android/sw_sync.h b/drivers/staging/android/sw_sync.h
index 585040be5f18..aba25cbb0382 100644
--- a/drivers/staging/android/sw_sync.h
+++ b/drivers/staging/android/sw_sync.h
@@ -21,6 +21,7 @@
#ifdef __KERNEL__
+#include <linux/kconfig.h>
#include "sync.h"
struct sw_sync_timeline {
@@ -35,10 +36,27 @@ struct sw_sync_pt {
u32 value;
};
+#if IS_ENABLED(CONFIG_SW_SYNC)
struct sw_sync_timeline *sw_sync_timeline_create(const char *name);
void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc);
struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value);
+#else
+static inline struct sw_sync_timeline *sw_sync_timeline_create(const char *name)
+{
+ return NULL;
+}
+
+static inline void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc)
+{
+}
+
+static inline struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj,
+ u32 value)
+{
+ return NULL;
+}
+#endif /* IS_ENABLED(CONFIG_SW_SYNC) */
#endif /* __KERNEL __ */
diff --git a/drivers/staging/android/sync.c b/drivers/staging/android/sync.c
index 3893a3574769..d38305b40930 100644
--- a/drivers/staging/android/sync.c
+++ b/drivers/staging/android/sync.c
@@ -79,27 +79,27 @@ static void sync_timeline_free(struct kref *kref)
container_of(kref, struct sync_timeline, kref);
unsigned long flags;
- if (obj->ops->release_obj)
- obj->ops->release_obj(obj);
-
spin_lock_irqsave(&sync_timeline_list_lock, flags);
list_del(&obj->sync_timeline_list);
spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
+ if (obj->ops->release_obj)
+ obj->ops->release_obj(obj);
+
kfree(obj);
}
void sync_timeline_destroy(struct sync_timeline *obj)
{
obj->destroyed = true;
+ smp_wmb();
/*
- * If this is not the last reference, signal any children
- * that their parent is going away.
+ * signal any children that their parent is going away.
*/
+ sync_timeline_signal(obj);
- if (!kref_put(&obj->kref, sync_timeline_free))
- sync_timeline_signal(obj);
+ kref_put(&obj->kref, sync_timeline_free);
}
EXPORT_SYMBOL(sync_timeline_destroy);
diff --git a/drivers/staging/bcm/Bcmchar.c b/drivers/staging/bcm/Bcmchar.c
index 35641e529396..8fa64d964b16 100644
--- a/drivers/staging/bcm/Bcmchar.c
+++ b/drivers/staging/bcm/Bcmchar.c
@@ -1960,6 +1960,7 @@ cntrlEnd:
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, OSAL_DBG, DBG_LVL_ALL, "Called IOCTL_BCM_GET_DEVICE_DRIVER_INFO\n");
+ memset(&DevInfo, 0, sizeof(DevInfo));
DevInfo.MaxRDMBufferSize = BUFFER_4K;
DevInfo.u32DSDStartOffset = EEPROM_CALPARAM_START;
DevInfo.u32RxAlignmentCorrection = 0;
diff --git a/drivers/staging/comedi/drivers/dt282x.c b/drivers/staging/comedi/drivers/dt282x.c
index 90f2de9bc402..f4c1e998cbe9 100644
--- a/drivers/staging/comedi/drivers/dt282x.c
+++ b/drivers/staging/comedi/drivers/dt282x.c
@@ -269,8 +269,9 @@ struct dt282x_private {
} \
udelay(5); \
} \
- if (_i) \
+ if (_i) { \
b \
+ } \
} while (0)
static int prep_ai_dma(struct comedi_device *dev, int chan, int size);
diff --git a/drivers/staging/comedi/drivers/ni_65xx.c b/drivers/staging/comedi/drivers/ni_65xx.c
index 3f71f0f54d3c..05eb6fefb745 100644
--- a/drivers/staging/comedi/drivers/ni_65xx.c
+++ b/drivers/staging/comedi/drivers/ni_65xx.c
@@ -383,28 +383,23 @@ static int ni_65xx_dio_insn_bits(struct comedi_device *dev,
{
const struct ni_65xx_board *board = comedi_board(dev);
struct ni_65xx_private *devpriv = dev->private;
- unsigned base_bitfield_channel;
- const unsigned max_ports_per_bitfield = 5;
+ int base_bitfield_channel;
unsigned read_bits = 0;
- unsigned j;
+ int last_port_offset = ni_65xx_port_by_channel(s->n_chan - 1);
+ int port_offset;
base_bitfield_channel = CR_CHAN(insn->chanspec);
- for (j = 0; j < max_ports_per_bitfield; ++j) {
- const unsigned port_offset =
- ni_65xx_port_by_channel(base_bitfield_channel) + j;
- const unsigned port =
- sprivate(s)->base_port + port_offset;
- unsigned base_port_channel;
+ for (port_offset = ni_65xx_port_by_channel(base_bitfield_channel);
+ port_offset <= last_port_offset; port_offset++) {
+ unsigned port = sprivate(s)->base_port + port_offset;
+ int base_port_channel = port_offset * ni_65xx_channels_per_port;
unsigned port_mask, port_data, port_read_bits;
- int bitshift;
- if (port >= ni_65xx_total_num_ports(board))
+ int bitshift = base_port_channel - base_bitfield_channel;
+
+ if (bitshift >= 32)
break;
- base_port_channel = port_offset * ni_65xx_channels_per_port;
port_mask = data[0];
port_data = data[1];
- bitshift = base_port_channel - base_bitfield_channel;
- if (bitshift >= 32 || bitshift <= -32)
- break;
if (bitshift > 0) {
port_mask >>= bitshift;
port_data >>= bitshift;
diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c
index 163c638e4095..972a0723afac 100644
--- a/drivers/staging/iio/adc/mxs-lradc.c
+++ b/drivers/staging/iio/adc/mxs-lradc.c
@@ -234,7 +234,6 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
{
struct mxs_lradc *lradc = iio_priv(iio_dev);
int ret;
- unsigned long mask;
if (m != IIO_CHAN_INFO_RAW)
return -EINVAL;
@@ -243,12 +242,6 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
if (chan->channel > LRADC_MAX_TOTAL_CHANS)
return -EINVAL;
- /* Validate the channel if it doesn't intersect with reserved chans. */
- bitmap_set(&mask, chan->channel, 1);
- ret = iio_validate_scan_mask_onehot(iio_dev, &mask);
- if (ret)
- return -EINVAL;
-
/*
* See if there is no buffered operation in progess. If there is, simply
* bail out. This can be improved to support both buffered and raw IO at
@@ -661,12 +654,13 @@ static int mxs_lradc_trigger_init(struct iio_dev *iio)
{
int ret;
struct iio_trigger *trig;
+ struct mxs_lradc *lradc = iio_priv(iio);
trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
if (trig == NULL)
return -ENOMEM;
- trig->dev.parent = iio->dev.parent;
+ trig->dev.parent = lradc->dev;
iio_trigger_set_drvdata(trig, iio);
trig->ops = &mxs_lradc_trigger_ops;
@@ -676,15 +670,17 @@ static int mxs_lradc_trigger_init(struct iio_dev *iio)
return ret;
}
- iio->trig = trig;
+ lradc->trig = trig;
return 0;
}
static void mxs_lradc_trigger_remove(struct iio_dev *iio)
{
- iio_trigger_unregister(iio->trig);
- iio_trigger_free(iio->trig);
+ struct mxs_lradc *lradc = iio_priv(iio);
+
+ iio_trigger_unregister(lradc->trig);
+ iio_trigger_free(lradc->trig);
}
static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
diff --git a/drivers/staging/ozwpan/ozcdev.c b/drivers/staging/ozwpan/ozcdev.c
index 27d06666c81a..224ccff75d4f 100644
--- a/drivers/staging/ozwpan/ozcdev.c
+++ b/drivers/staging/ozwpan/ozcdev.c
@@ -153,6 +153,9 @@ static ssize_t oz_cdev_write(struct file *filp, const char __user *buf,
struct oz_app_hdr *app_hdr;
struct oz_serial_ctx *ctx;
+ if (count > sizeof(ei->data) - sizeof(*elt) - sizeof(*app_hdr))
+ return -EINVAL;
+
spin_lock_bh(&g_cdev.lock);
pd = g_cdev.active_pd;
if (pd)
diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c
index cd94f6c27723..b90e96b7ca01 100644
--- a/drivers/staging/sb105x/sb_pci_mp.c
+++ b/drivers/staging/sb105x/sb_pci_mp.c
@@ -1063,7 +1063,7 @@ static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg)
static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt)
{
- struct serial_icounter_struct icount;
+ struct serial_icounter_struct icount = {};
struct sb_uart_icount cnow;
struct sb_uart_port *port = state->port;
diff --git a/drivers/staging/vt6656/iwctl.c b/drivers/staging/vt6656/iwctl.c
index d0cf7d8a20e5..8872e0f84f40 100644
--- a/drivers/staging/vt6656/iwctl.c
+++ b/drivers/staging/vt6656/iwctl.c
@@ -1634,6 +1634,9 @@ int iwctl_siwencodeext(struct net_device *dev, struct iw_request_info *info,
if (pMgmt == NULL)
return -EFAULT;
+ if (!(pDevice->flags & DEVICE_FLAGS_OPENED))
+ return -ENODEV;
+
buf = kzalloc(sizeof(struct viawget_wpa_param), GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c
index 3a3fdc58b6da..06b966cf5bdd 100644
--- a/drivers/staging/vt6656/main_usb.c
+++ b/drivers/staging/vt6656/main_usb.c
@@ -1099,6 +1099,8 @@ static int device_close(struct net_device *dev)
memset(pMgmt->abyCurrBSSID, 0, 6);
pMgmt->eCurrState = WMAC_STATE_IDLE;
+ pDevice->flags &= ~DEVICE_FLAGS_OPENED;
+
device_free_tx_bufs(pDevice);
device_free_rx_bufs(pDevice);
device_free_int_bufs(pDevice);
@@ -1110,7 +1112,6 @@ static int device_close(struct net_device *dev)
usb_free_urb(pDevice->pInterruptURB);
BSSvClearNodeDBTable(pDevice, 0);
- pDevice->flags &=(~DEVICE_FLAGS_OPENED);
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "device_close2 \n");
diff --git a/drivers/staging/wlags49_h2/wl_priv.c b/drivers/staging/wlags49_h2/wl_priv.c
index c97e0e154d28..7e10dcdc3090 100644
--- a/drivers/staging/wlags49_h2/wl_priv.c
+++ b/drivers/staging/wlags49_h2/wl_priv.c
@@ -570,6 +570,7 @@ int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp)
ltv_t *pLtv;
bool_t ltvAllocated = FALSE;
ENCSTRCT sEncryption;
+ size_t len;
#ifdef USE_WDS
hcf_16 hcfPort = HCF_PORT_0;
@@ -686,7 +687,8 @@ int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp)
break;
case CFG_CNF_OWN_NAME:
memset(lp->StationName, 0, sizeof(lp->StationName));
- memcpy((void *)lp->StationName, (void *)&pLtv->u.u8[2], (size_t)pLtv->u.u16[0]);
+ len = min_t(size_t, pLtv->u.u16[0], sizeof(lp->StationName));
+ strlcpy(lp->StationName, &pLtv->u.u8[2], len);
pLtv->u.u16[0] = CNV_INT_TO_LITTLE(pLtv->u.u16[0]);
break;
case CFG_CNF_LOAD_BALANCING:
@@ -1783,6 +1785,7 @@ int wvlan_set_station_nickname(struct net_device *dev,
{
struct wl_private *lp = wl_priv(dev);
unsigned long flags;
+ size_t len;
int ret = 0;
/*------------------------------------------------------------------------*/
@@ -1793,8 +1796,8 @@ int wvlan_set_station_nickname(struct net_device *dev,
wl_lock(lp, &flags);
memset(lp->StationName, 0, sizeof(lp->StationName));
-
- memcpy(lp->StationName, extra, wrqu->data.length);
+ len = min_t(size_t, wrqu->data.length, sizeof(lp->StationName));
+ strlcpy(lp->StationName, extra, len);
/* Commit the adapter parameters */
wl_apply(lp);
diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c
index e05fc2591147..e373b625806e 100644
--- a/drivers/switch/switch_class.c
+++ b/drivers/switch/switch_class.c
@@ -54,8 +54,8 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%s\n", sdev->name);
}
-static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL);
-static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL);
+static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
+static DEVICE_ATTR(name, S_IRUGO, name_show, NULL);
void switch_set_state(struct switch_dev *sdev, int state)
{
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
index 96e7fdbba9fc..77dad2474c8c 100644
--- a/drivers/target/iscsi/iscsi_target_util.c
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -734,7 +734,7 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown)
* Fallthrough
*/
case ISCSI_OP_SCSI_TMFUNC:
- rc = transport_generic_free_cmd(&cmd->se_cmd, 1);
+ rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown);
if (!rc && shutdown && se_cmd && se_cmd->se_sess) {
__iscsit_free_cmd(cmd, true, shutdown);
target_put_sess_cmd(se_cmd->se_sess, se_cmd);
@@ -750,7 +750,7 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown)
se_cmd = &cmd->se_cmd;
__iscsit_free_cmd(cmd, true, shutdown);
- rc = transport_generic_free_cmd(&cmd->se_cmd, 1);
+ rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown);
if (!rc && shutdown && se_cmd->se_sess) {
__iscsit_free_cmd(cmd, true, shutdown);
target_put_sess_cmd(se_cmd->se_sess, se_cmd);
diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
index cbe48ab41745..f608fbc14a27 100644
--- a/drivers/target/target_core_alua.c
+++ b/drivers/target/target_core_alua.c
@@ -730,7 +730,7 @@ static int core_alua_write_tpg_metadata(
if (ret < 0)
pr_err("Error writing ALUA metadata file: %s\n", path);
fput(file);
- return ret ? -EIO : 0;
+ return (ret < 0) ? -EIO : 0;
}
/*
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 3240f2cc81ef..04a74938bb43 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -1987,7 +1987,7 @@ static int __core_scsi3_write_aptpl_to_file(
pr_debug("Error writing APTPL metadata file: %s\n", path);
fput(file);
- return ret ? -EIO : 0;
+ return (ret < 0) ? -EIO : 0;
}
static int
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
index e992b27aa090..3250ba2594e0 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -134,10 +134,10 @@ static int pscsi_pmode_enable_hba(struct se_hba *hba, unsigned long mode_flag)
* pSCSI Host ID and enable for phba mode
*/
sh = scsi_host_lookup(phv->phv_host_id);
- if (IS_ERR(sh)) {
+ if (!sh) {
pr_err("pSCSI: Unable to locate SCSI Host for"
" phv_host_id: %d\n", phv->phv_host_id);
- return PTR_ERR(sh);
+ return -EINVAL;
}
phv->phv_lld_host = sh;
@@ -515,10 +515,10 @@ static int pscsi_configure_device(struct se_device *dev)
sh = phv->phv_lld_host;
} else {
sh = scsi_host_lookup(pdv->pdv_host_id);
- if (IS_ERR(sh)) {
+ if (!sh) {
pr_err("pSCSI: Unable to locate"
" pdv_host_id: %d\n", pdv->pdv_host_id);
- return PTR_ERR(sh);
+ return -EINVAL;
}
}
} else {
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c
index 682210d778bd..4fc32c8091e9 100644
--- a/drivers/tty/hvc/hvc_xen.c
+++ b/drivers/tty/hvc/hvc_xen.c
@@ -636,6 +636,7 @@ struct console xenboot_console = {
.name = "xenboot",
.write = xenboot_write_console,
.flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
+ .index = -1,
};
#endif /* CONFIG_EARLY_PRINTK */
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 572d48189de9..20e4c9446992 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -658,11 +658,12 @@ static int dma_push_rx(struct eg20t_port *priv, int size)
dev_warn(port->dev, "Rx overrun: dropping %u bytes\n",
size - room);
if (!room)
- return room;
+ goto out;
tty_insert_flip_string(tport, sg_virt(&priv->sg_rx), size);
port->icount.rx += room;
+out:
tty_kref_put(tty);
return room;
@@ -1071,6 +1072,8 @@ static void pch_uart_err_ir(struct eg20t_port *priv, unsigned int lsr)
if (tty == NULL) {
for (i = 0; error_msg[i] != NULL; i++)
dev_err(&priv->pdev->dev, error_msg[i]);
+ } else {
+ tty_kref_put(tty);
}
}
diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index 9799d043a9bd..357a8370ce2a 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -726,7 +726,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
static void tegra_uart_stop_rx(struct uart_port *u)
{
struct tegra_uart_port *tup = to_tegra_uport(u);
- struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
+ struct tty_struct *tty;
struct tty_port *port = &u->state->port;
struct dma_tx_state state;
unsigned long ier;
@@ -738,6 +738,8 @@ static void tegra_uart_stop_rx(struct uart_port *u)
if (!tup->rx_in_progress)
return;
+ tty = tty_port_tty_get(&tup->uport.state->port);
+
tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */
ier = tup->ier_shadow;
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 1a8bc2275ea4..f72b43fbbef9 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -559,12 +559,13 @@ static int vt8500_serial_probe(struct platform_device *pdev)
if (!mmres || !irqres)
return -ENODEV;
- if (np)
+ if (np) {
port = of_alias_get_id(np, "serial");
if (port >= VT8500_MAX_PORTS)
port = -1;
- else
+ } else {
port = -1;
+ }
if (port < 0) {
/* calculate the port id */
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 447668213900..59d26ef538d8 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -850,7 +850,8 @@ void disassociate_ctty(int on_exit)
struct pid *tty_pgrp = tty_get_pgrp(tty);
if (tty_pgrp) {
kill_pgrp(tty_pgrp, SIGHUP, on_exit);
- kill_pgrp(tty_pgrp, SIGCONT, on_exit);
+ if (!on_exit)
+ kill_pgrp(tty_pgrp, SIGCONT, on_exit);
put_pid(tty_pgrp);
}
}
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index 3500d4114147..088b4ca7d805 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -1201,6 +1201,9 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
}
return 0;
case TCFLSH:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
return __tty_perform_flush(tty, arg);
default:
/* Try the mode commands */
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index b645c47501b4..2d57a00dc173 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -630,36 +630,57 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
return 0;
}
-static const struct vm_operations_struct uio_vm_ops = {
+static const struct vm_operations_struct uio_logical_vm_ops = {
.open = uio_vma_open,
.close = uio_vma_close,
.fault = uio_vma_fault,
};
+static int uio_mmap_logical(struct vm_area_struct *vma)
+{
+ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+ vma->vm_ops = &uio_logical_vm_ops;
+ uio_vma_open(vma);
+ return 0;
+}
+
+static const struct vm_operations_struct uio_physical_vm_ops = {
+#ifdef CONFIG_HAVE_IOREMAP_PROT
+ .access = generic_access_phys,
+#endif
+};
+
static int uio_mmap_physical(struct vm_area_struct *vma)
{
struct uio_device *idev = vma->vm_private_data;
int mi = uio_find_mem_index(vma);
+ struct uio_mem *mem;
if (mi < 0)
return -EINVAL;
+ mem = idev->info->mem + mi;
+ if (vma->vm_end - vma->vm_start > mem->size)
+ return -EINVAL;
+
+ vma->vm_ops = &uio_physical_vm_ops;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ /*
+ * We cannot use the vm_iomap_memory() helper here,
+ * because vma->vm_pgoff is the map index we looked
+ * up above in uio_find_mem_index(), rather than an
+ * actual page offset into the mmap.
+ *
+ * So we just do the physical mmap without a page
+ * offset.
+ */
return remap_pfn_range(vma,
vma->vm_start,
- idev->info->mem[mi].addr >> PAGE_SHIFT,
+ mem->addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
}
-static int uio_mmap_logical(struct vm_area_struct *vma)
-{
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
- vma->vm_ops = &uio_vm_ops;
- uio_vma_open(vma);
- return 0;
-}
-
static int uio_mmap(struct file *filep, struct vm_area_struct *vma)
{
struct uio_listener *listener = filep->private_data;
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 8a230f0ef77c..d3318a0df8ee 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -209,6 +209,7 @@ skip_error:
static void wdm_int_callback(struct urb *urb)
{
int rv = 0;
+ int responding;
int status = urb->status;
struct wdm_device *desc;
struct usb_cdc_notification *dr;
@@ -262,8 +263,8 @@ static void wdm_int_callback(struct urb *urb)
spin_lock(&desc->iuspin);
clear_bit(WDM_READ, &desc->flags);
- set_bit(WDM_RESPONDING, &desc->flags);
- if (!test_bit(WDM_DISCONNECTING, &desc->flags)
+ responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);
+ if (!responding && !test_bit(WDM_DISCONNECTING, &desc->flags)
&& !test_bit(WDM_SUSPENDING, &desc->flags)) {
rv = usb_submit_urb(desc->response, GFP_ATOMIC);
dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
@@ -685,16 +686,20 @@ static void wdm_rxwork(struct work_struct *work)
{
struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
unsigned long flags;
- int rv;
+ int rv = 0;
+ int responding;
spin_lock_irqsave(&desc->iuspin, flags);
if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
spin_unlock_irqrestore(&desc->iuspin, flags);
} else {
+ responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);
spin_unlock_irqrestore(&desc->iuspin, flags);
- rv = usb_submit_urb(desc->response, GFP_KERNEL);
+ if (!responding)
+ rv = usb_submit_urb(desc->response, GFP_KERNEL);
if (rv < 0 && rv != -EPERM) {
spin_lock_irqsave(&desc->iuspin, flags);
+ clear_bit(WDM_RESPONDING, &desc->flags);
if (!test_bit(WDM_DISCONNECTING, &desc->flags))
schedule_work(&desc->rxwork);
spin_unlock_irqrestore(&desc->iuspin, flags);
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 7199adccf444..a6b2cabe7930 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -424,7 +424,8 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
- config->desc.bLength < USB_DT_CONFIG_SIZE) {
+ config->desc.bLength < USB_DT_CONFIG_SIZE ||
+ config->desc.bLength > size) {
dev_err(ddev, "invalid descriptor for config index %d: "
"type = 0x%X, length = %d\n", cfgidx,
config->desc.bDescriptorType, config->desc.bLength);
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index c88c4fb9459d..ce773cca2bf5 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -742,6 +742,22 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
if ((index & ~USB_DIR_IN) == 0)
return 0;
ret = findintfep(ps->dev, index);
+ if (ret < 0) {
+ /*
+ * Some not fully compliant Win apps seem to get
+ * index wrong and have the endpoint number here
+ * rather than the endpoint address (with the
+ * correct direction). Win does let this through,
+ * so we'll not reject it here but leave it to
+ * the device to not break KVM. But we warn.
+ */
+ ret = findintfep(ps->dev, index ^ 0x80);
+ if (ret >= 0)
+ dev_info(&ps->dev->dev,
+ "%s: process %i (%s) requesting ep %02x but needs %02x\n",
+ __func__, task_pid_nr(current),
+ current->comm, index, index ^ 0x80);
+ }
if (ret >= 0)
ret = checkintf(ps, ret);
break;
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index d53547d2e4c7..d3aa353908aa 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1010,6 +1010,7 @@ static int register_root_hub(struct usb_hcd *hcd)
dev_name(&usb_dev->dev), retval);
return retval;
}
+ usb_dev->lpm_capable = usb_device_supports_lpm(usb_dev);
}
retval = usb_new_device (usb_dev);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index da2905a1a18b..c8b9fa0e9275 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -135,7 +135,7 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)
return usb_get_intfdata(hdev->actconfig->interface[0]);
}
-static int usb_device_supports_lpm(struct usb_device *udev)
+int usb_device_supports_lpm(struct usb_device *udev)
{
/* USB 2.1 (and greater) devices indicate LPM support through
* their USB 2.0 Extended Capabilities BOS descriptor.
@@ -156,6 +156,11 @@ static int usb_device_supports_lpm(struct usb_device *udev)
"Power management will be impacted.\n");
return 0;
}
+
+ /* udev is root hub */
+ if (!udev->parent)
+ return 1;
+
if (udev->parent->lpm_capable)
return 1;
@@ -1557,10 +1562,15 @@ static int hub_configure(struct usb_hub *hub,
if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
- for (i = 0; i < hdev->maxchild; i++)
- if (usb_hub_create_port_device(hub, i + 1) < 0)
+ for (i = 0; i < hdev->maxchild; i++) {
+ ret = usb_hub_create_port_device(hub, i + 1);
+ if (ret < 0) {
dev_err(hub->intfdev,
"couldn't create port%d device.\n", i + 1);
+ hdev->maxchild = i;
+ goto fail_keep_maxchild;
+ }
+ }
usb_hub_adjust_deviceremovable(hdev, hub->descriptor);
@@ -1568,6 +1578,8 @@ static int hub_configure(struct usb_hub *hub,
return 0;
fail:
+ hdev->maxchild = 0;
+fail_keep_maxchild:
dev_err (hub_dev, "config failed, %s (err %d)\n",
message, ret);
/* hub_disconnect() frees urb and descriptor */
@@ -2916,7 +2928,6 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
{
struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
struct usb_port *port_dev = hub->ports[udev->portnum - 1];
- enum pm_qos_flags_status pm_qos_stat;
int port1 = udev->portnum;
int status;
bool really_suspend = true;
@@ -2954,7 +2965,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
status);
/* bail if autosuspend is requested */
if (PMSG_IS_AUTO(msg))
- return status;
+ goto err_wakeup;
}
}
@@ -2963,14 +2974,16 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
usb_set_usb2_hardware_lpm(udev, 0);
if (usb_disable_ltm(udev)) {
- dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.",
- __func__);
- return -ENOMEM;
+ dev_err(&udev->dev, "Failed to disable LTM before suspend\n.");
+ status = -ENOMEM;
+ if (PMSG_IS_AUTO(msg))
+ goto err_ltm;
}
if (usb_unlocked_disable_lpm(udev)) {
- dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.",
- __func__);
- return -ENOMEM;
+ dev_err(&udev->dev, "Failed to disable LPM before suspend\n.");
+ status = -ENOMEM;
+ if (PMSG_IS_AUTO(msg))
+ goto err_lpm3;
}
/* see 7.1.7.6 */
@@ -2998,28 +3011,31 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
if (status) {
dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
port1, status);
- /* paranoia: "should not happen" */
- if (udev->do_remote_wakeup) {
- if (!hub_is_superspeed(hub->hdev)) {
- (void) usb_control_msg(udev,
- usb_sndctrlpipe(udev, 0),
- USB_REQ_CLEAR_FEATURE,
- USB_RECIP_DEVICE,
- USB_DEVICE_REMOTE_WAKEUP, 0,
- NULL, 0,
- USB_CTRL_SET_TIMEOUT);
- } else
- (void) usb_disable_function_remotewakeup(udev);
-
- }
+ /* Try to enable USB3 LPM and LTM again */
+ usb_unlocked_enable_lpm(udev);
+ err_lpm3:
+ usb_enable_ltm(udev);
+ err_ltm:
/* Try to enable USB2 hardware LPM again */
if (udev->usb2_hw_lpm_capable == 1)
usb_set_usb2_hardware_lpm(udev, 1);
- /* Try to enable USB3 LTM and LPM again */
- usb_enable_ltm(udev);
- usb_unlocked_enable_lpm(udev);
+ if (udev->do_remote_wakeup) {
+ if (udev->speed < USB_SPEED_SUPER)
+ usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_CLEAR_FEATURE,
+ USB_RECIP_DEVICE,
+ USB_DEVICE_REMOTE_WAKEUP, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ else
+ usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_CLEAR_FEATURE,
+ USB_RECIP_INTERFACE,
+ USB_INTRF_FUNC_SUSPEND, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ }
+ err_wakeup:
/* System sleep transitions should never fail */
if (!PMSG_IS_AUTO(msg))
@@ -3037,16 +3053,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
usb_set_device_state(udev, USB_STATE_SUSPENDED);
}
- /*
- * Check whether current status meets the requirement of
- * usb port power off mechanism
- */
- pm_qos_stat = dev_pm_qos_flags(&port_dev->dev,
- PM_QOS_FLAG_NO_POWER_OFF);
- if (!udev->do_remote_wakeup
- && pm_qos_stat != PM_QOS_FLAGS_ALL
- && udev->persist_enabled
- && !status) {
+ if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled) {
pm_runtime_put_sync(&port_dev->dev);
port_dev->did_runtime_put = true;
}
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index b8bad294eeb8..ef07b3596d06 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -89,22 +89,19 @@ static int usb_port_runtime_resume(struct device *dev)
retval = usb_hub_set_port_power(hdev, port1, true);
if (port_dev->child && !retval) {
/*
- * Wait for usb hub port to be reconnected in order to make
- * the resume procedure successful.
+ * Attempt to wait for usb hub port to be reconnected in order
+ * to make the resume procedure successful. The device may have
+ * disconnected while the port was powered off, so ignore the
+ * return status.
*/
retval = hub_port_debounce_be_connected(hub, port1);
- if (retval < 0) {
+ if (retval < 0)
dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n",
retval);
- goto out;
- }
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
-
- /* Set return value to 0 if debounce successful */
retval = 0;
}
-out:
clear_bit(port1, hub->busy_bits);
usb_autopm_put_interface(intf);
return retval;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 5b44cd47da5b..01fe36273f3b 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -97,6 +97,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Alcor Micro Corp. Hub */
{ USB_DEVICE(0x058f, 0x9254), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* MicroTouch Systems touchscreen */
+ { USB_DEVICE(0x0596, 0x051e), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* appletouch */
{ USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -130,6 +133,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Broadcom BCM92035DGROM BT dongle */
{ USB_DEVICE(0x0a5c, 0x2021), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* MAYA44USB sound device */
+ { USB_DEVICE(0x0a92, 0x0091), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* Action Semiconductor flash disk */
{ USB_DEVICE(0x10d6, 0x2200), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 823857767a16..c49383669cd8 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -35,6 +35,7 @@ extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
extern int usb_get_bos_descriptor(struct usb_device *dev);
extern void usb_release_bos_descriptor(struct usb_device *dev);
+extern int usb_device_supports_lpm(struct usb_device *udev);
extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern int usb_choose_configuration(struct usb_device *udev);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index eba9e2baf32b..2357c4e5dc43 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -48,6 +48,8 @@
/* FIXME define these in <linux/pci_ids.h> */
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
+#define PCI_DEVICE_ID_INTEL_BYT 0x0f37
+#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e
struct dwc3_pci {
struct device *dev;
@@ -208,6 +210,8 @@ static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
},
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), },
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f77083fedc68..14d28d6184f6 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1508,6 +1508,15 @@ static int dwc3_gadget_start(struct usb_gadget *g,
int irq;
u32 reg;
+ irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+ ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
+ IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+ irq, ret);
+ goto err0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
if (dwc->gadget_driver) {
@@ -1515,7 +1524,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
dwc->gadget.name,
dwc->gadget_driver->driver.name);
ret = -EBUSY;
- goto err0;
+ goto err1;
}
dwc->gadget_driver = driver;
@@ -1551,42 +1560,38 @@ static int dwc3_gadget_start(struct usb_gadget *g,
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
- goto err0;
+ goto err2;
}
dep = dwc->eps[1];
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
- goto err1;
+ goto err3;
}
/* begin to receive SETUP packets */
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
- ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
- IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc);
- if (ret) {
- dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
- irq, ret);
- goto err1;
- }
-
dwc3_gadget_enable_irq(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
-err1:
+err3:
__dwc3_gadget_ep_disable(dwc->eps[0]);
-err0:
+err2:
dwc->gadget_driver = NULL;
+
+err1:
spin_unlock_irqrestore(&dwc->lock, flags);
+ free_irq(irq, dwc);
+
+err0:
return ret;
}
@@ -1600,9 +1605,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_disable_irq(dwc);
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
- free_irq(irq, dwc);
-
__dwc3_gadget_ep_disable(dwc->eps[0]);
__dwc3_gadget_ep_disable(dwc->eps[1]);
@@ -1610,6 +1612,9 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
spin_unlock_irqrestore(&dwc->lock, flags);
+ irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+ free_irq(irq, dwc);
+
return 0;
}
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index d0ec54b810c1..04cbeb134814 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -1490,7 +1490,7 @@ static int __init init(void)
err = usb_composite_probe(&android_usb_driver);
if (err) {
pr_err("%s: failed to probe driver %d", __func__, err);
- goto err_create;
+ goto err_probe;
}
/* HACK: exchange composite's setup with ours */
@@ -1499,6 +1499,8 @@ static int __init init(void)
return 0;
+err_probe:
+ device_destroy(android_class, dev->dev->devt);
err_create:
kfree(dev);
err_dev:
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index c588e8e486e5..ac0e79e2c2e9 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -923,8 +923,9 @@ static int dummy_udc_stop(struct usb_gadget *g,
struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g);
struct dummy *dum = dum_hcd->dum;
- dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n",
- driver->driver.name);
+ if (driver)
+ dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n",
+ driver->driver.name);
dum->driver = NULL;
@@ -1000,8 +1001,8 @@ static int dummy_udc_remove(struct platform_device *pdev)
{
struct dummy *dum = platform_get_drvdata(pdev);
- usb_del_gadget_udc(&dum->gadget);
device_remove_file(&dum->gadget.dev, &dev_attr_function);
+ usb_del_gadget_udc(&dum->gadget);
return 0;
}
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index 3d132cb786f0..bf7d627b9454 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -1034,37 +1034,19 @@ struct ffs_sb_fill_data {
struct ffs_file_perms perms;
umode_t root_mode;
const char *dev_name;
- union {
- /* set by ffs_fs_mount(), read by ffs_sb_fill() */
- void *private_data;
- /* set by ffs_sb_fill(), read by ffs_fs_mount */
- struct ffs_data *ffs_data;
- };
+ struct ffs_data *ffs_data;
};
static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
{
struct ffs_sb_fill_data *data = _data;
struct inode *inode;
- struct ffs_data *ffs;
+ struct ffs_data *ffs = data->ffs_data;
ENTER();
- /* Initialise data */
- ffs = ffs_data_new();
- if (unlikely(!ffs))
- goto Enomem;
-
ffs->sb = sb;
- ffs->dev_name = kstrdup(data->dev_name, GFP_KERNEL);
- if (unlikely(!ffs->dev_name))
- goto Enomem;
- ffs->file_perms = data->perms;
- ffs->private_data = data->private_data;
-
- /* used by the caller of this function */
- data->ffs_data = ffs;
-
+ data->ffs_data = NULL;
sb->s_fs_info = ffs;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
@@ -1080,17 +1062,14 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
&data->perms);
sb->s_root = d_make_root(inode);
if (unlikely(!sb->s_root))
- goto Enomem;
+ return -ENOMEM;
/* EP0 file */
if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs,
&ffs_ep0_operations, NULL)))
- goto Enomem;
+ return -ENOMEM;
return 0;
-
-Enomem:
- return -ENOMEM;
}
static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
@@ -1193,6 +1172,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
struct dentry *rv;
int ret;
void *ffs_dev;
+ struct ffs_data *ffs;
ENTER();
@@ -1200,18 +1180,30 @@ ffs_fs_mount(struct file_system_type *t, int flags,
if (unlikely(ret < 0))
return ERR_PTR(ret);
+ ffs = ffs_data_new();
+ if (unlikely(!ffs))
+ return ERR_PTR(-ENOMEM);
+ ffs->file_perms = data.perms;
+
+ ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
+ if (unlikely(!ffs->dev_name)) {
+ ffs_data_put(ffs);
+ return ERR_PTR(-ENOMEM);
+ }
+
ffs_dev = functionfs_acquire_dev_callback(dev_name);
- if (IS_ERR(ffs_dev))
- return ffs_dev;
+ if (IS_ERR(ffs_dev)) {
+ ffs_data_put(ffs);
+ return ERR_CAST(ffs_dev);
+ }
+ ffs->private_data = ffs_dev;
+ data.ffs_data = ffs;
- data.dev_name = dev_name;
- data.private_data = ffs_dev;
rv = mount_nodev(t, flags, &data, ffs_sb_fill);
-
- /* data.ffs_data is set by ffs_sb_fill */
- if (IS_ERR(rv))
+ if (IS_ERR(rv) && data.ffs_data) {
functionfs_release_dev_callback(data.ffs_data);
-
+ ffs_data_put(data.ffs_data);
+ }
return rv;
}
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index 9ab94697c196..12fb818ab147 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -410,15 +410,6 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev,
ep->driver_data = dev; /* claim the endpoint */
dev->ep_out = ep;
- ep = usb_ep_autoconfig(cdev->gadget, out_desc);
- if (!ep) {
- DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
- return -ENODEV;
- }
- DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name);
- ep->driver_data = dev; /* claim the endpoint */
- dev->ep_out = ep;
-
ep = usb_ep_autoconfig(cdev->gadget, intr_desc);
if (!ep) {
DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n");
diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c
index 7ce27e35550b..de456a5a5e0a 100644
--- a/drivers/usb/gadget/uvc_queue.c
+++ b/drivers/usb/gadget/uvc_queue.c
@@ -177,12 +177,16 @@ static int uvc_queue_buffer(struct uvc_video_queue *queue,
mutex_lock(&queue->mutex);
ret = vb2_qbuf(&queue->queue, buf);
+ if (ret < 0)
+ goto done;
+
spin_lock_irqsave(&queue->irqlock, flags);
ret = (queue->flags & UVC_QUEUE_PAUSED) != 0;
queue->flags &= ~UVC_QUEUE_PAUSED;
spin_unlock_irqrestore(&queue->irqlock, flags);
- mutex_unlock(&queue->mutex);
+done:
+ mutex_unlock(&queue->mutex);
return ret;
}
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 3be3df233a0e..3c0a49a298dd 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -130,7 +130,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
}
/* Enable USB controller, 83xx or 8536 */
- if (pdata->have_sysif_regs)
+ if (pdata->have_sysif_regs && pdata->controller_ver < FSL_USB_VER_1_6)
setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4);
/* Don't need to set host mode here. It will be done by tdi_reset() */
@@ -232,15 +232,9 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
case FSL_USB2_PHY_ULPI:
if (pdata->have_sysif_regs && pdata->controller_ver) {
/* controller version 1.6 or above */
+ clrbits32(non_ehci + FSL_SOC_USB_CTRL, UTMI_PHY_EN);
setbits32(non_ehci + FSL_SOC_USB_CTRL,
- ULPI_PHY_CLK_SEL);
- /*
- * Due to controller issue of PHY_CLK_VALID in ULPI
- * mode, we set USB_CTRL_USB_EN before checking
- * PHY_CLK_VALID, otherwise PHY_CLK_VALID doesn't work.
- */
- clrsetbits_be32(non_ehci + FSL_SOC_USB_CTRL,
- UTMI_PHY_EN, USB_CTRL_USB_EN);
+ ULPI_PHY_CLK_SEL | USB_CTRL_USB_EN);
}
portsc |= PORT_PTS_ULPI;
break;
diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c
index c369767b00e2..ec128bc72deb 100644
--- a/drivers/usb/host/ehci-mxc.c
+++ b/drivers/usb/host/ehci-mxc.c
@@ -184,7 +184,7 @@ static int ehci_mxc_drv_remove(struct platform_device *pdev)
if (pdata && pdata->exit)
pdata->exit(pdev);
- if (pdata->otg)
+ if (pdata && pdata->otg)
usb_phy_shutdown(pdata->otg);
clk_disable_unprepare(priv->usbclk);
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 595d210655b6..8fe401c7d152 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -403,7 +403,7 @@ static struct pci_driver ehci_pci_driver = {
.remove = usb_hcd_pci_remove,
.shutdown = usb_hcd_pci_shutdown,
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
.driver = {
.pm = &usb_hcd_pci_pm_ops
},
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index fc627fd54116..865946cde765 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -231,31 +231,26 @@ static int ohci_urb_enqueue (
frame &= ~(ed->interval - 1);
frame |= ed->branch;
urb->start_frame = frame;
+ ed->last_iso = frame + ed->interval * (size - 1);
}
} else if (ed->type == PIPE_ISOCHRONOUS) {
u16 next = ohci_frame_no(ohci) + 1;
u16 frame = ed->last_iso + ed->interval;
+ u16 length = ed->interval * (size - 1);
/* Behind the scheduling threshold? */
if (unlikely(tick_before(frame, next))) {
- /* USB_ISO_ASAP: Round up to the first available slot */
+ /* URB_ISO_ASAP: Round up to the first available slot */
if (urb->transfer_flags & URB_ISO_ASAP) {
frame += (next - frame + ed->interval - 1) &
-ed->interval;
/*
- * Not ASAP: Use the next slot in the stream. If
- * the entire URB falls before the threshold, fail.
+ * Not ASAP: Use the next slot in the stream,
+ * no matter what.
*/
} else {
- if (tick_before(frame + ed->interval *
- (urb->number_of_packets - 1), next)) {
- retval = -EXDEV;
- usb_hcd_unlink_urb_from_ep(hcd, urb);
- goto fail;
- }
-
/*
* Some OHCI hardware doesn't handle late TDs
* correctly. After retiring them it proceeds
@@ -266,9 +261,16 @@ static int ohci_urb_enqueue (
urb_priv->td_cnt = DIV_ROUND_UP(
(u16) (next - frame),
ed->interval);
+ if (urb_priv->td_cnt >= urb_priv->length) {
+ ++urb_priv->td_cnt; /* Mark it */
+ ohci_dbg(ohci, "iso underrun %p (%u+%u < %u)\n",
+ urb, frame, length,
+ next);
+ }
}
}
urb->start_frame = frame;
+ ed->last_iso = frame + length;
}
/* fill the TDs and link them to the ed; and
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 951514ef446d..ef6782bd1fa9 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -371,7 +371,7 @@ static struct pci_driver ohci_pci_driver = {
.remove = usb_hcd_pci_remove,
.shutdown = usb_hcd_pci_shutdown,
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
.driver = {
.pm = &usb_hcd_pci_pm_ops
},
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index 88731b7c5f42..37dc8373200a 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -41,8 +41,12 @@ finish_urb(struct ohci_hcd *ohci, struct urb *urb, int status)
__releases(ohci->lock)
__acquires(ohci->lock)
{
+ struct usb_host_endpoint *ep = urb->ep;
+ struct urb_priv *urb_priv;
+
// ASSERT (urb->hcpriv != 0);
+ restart:
urb_free_priv (ohci, urb->hcpriv);
urb->hcpriv = NULL;
if (likely(status == -EINPROGRESS))
@@ -79,6 +83,21 @@ __acquires(ohci->lock)
ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_IE);
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
}
+
+ /*
+ * An isochronous URB that is sumitted too late won't have any TDs
+ * (marked by the fact that the td_cnt value is larger than the
+ * actual number of TDs). If the next URB on this endpoint is like
+ * that, give it back now.
+ */
+ if (!list_empty(&ep->urb_list)) {
+ urb = list_first_entry(&ep->urb_list, struct urb, urb_list);
+ urb_priv = urb->hcpriv;
+ if (urb_priv->td_cnt > urb_priv->length) {
+ status = 0;
+ goto restart;
+ }
+ }
}
@@ -545,7 +564,6 @@ td_fill (struct ohci_hcd *ohci, u32 info,
td->hwCBP = cpu_to_hc32 (ohci, data & 0xFFFFF000);
*ohci_hwPSWp(ohci, td, 0) = cpu_to_hc16 (ohci,
(data & 0x0FFF) | 0xE000);
- td->ed->last_iso = info & 0xffff;
} else {
td->hwCBP = cpu_to_hc32 (ohci, data);
}
@@ -994,7 +1012,7 @@ rescan_this:
urb_priv->td_cnt++;
/* if URB is done, clean up */
- if (urb_priv->td_cnt == urb_priv->length) {
+ if (urb_priv->td_cnt >= urb_priv->length) {
modified = completed = 1;
finish_urb(ohci, urb, 0);
}
@@ -1084,7 +1102,7 @@ static void takeback_td(struct ohci_hcd *ohci, struct td *td)
urb_priv->td_cnt++;
/* If all this urb's TDs are done, call complete() */
- if (urb_priv->td_cnt == urb_priv->length)
+ if (urb_priv->td_cnt >= urb_priv->length)
finish_urb(ohci, urb, status);
/* clean schedule: unlink EDs that are no longer busy */
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
index c300bd2f7d1c..0f228c46eeda 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -293,7 +293,7 @@ static struct pci_driver uhci_pci_driver = {
.remove = usb_hcd_pci_remove,
.shutdown = uhci_shutdown,
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
.driver = {
.pm = &usb_hcd_pci_pm_ops
},
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 041c6ddb695c..da6f56d996ce 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -1303,7 +1303,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
}
/* Fell behind? */
- if (uhci_frame_before_eq(frame, next)) {
+ if (!uhci_frame_before_eq(next, frame)) {
/* USB_ISO_ASAP: Round up to the first available slot */
if (urb->transfer_flags & URB_ISO_ASAP)
@@ -1311,13 +1311,17 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
-qh->period;
/*
- * Not ASAP: Use the next slot in the stream. If
- * the entire URB falls before the threshold, fail.
+ * Not ASAP: Use the next slot in the stream,
+ * no matter what.
*/
else if (!uhci_frame_before_eq(next,
frame + (urb->number_of_packets - 1) *
qh->period))
- return -EXDEV;
+ dev_dbg(uhci_dev(uhci), "iso underrun %p (%u+%u < %u)\n",
+ urb, frame,
+ (urb->number_of_packets - 1) *
+ qh->period,
+ next);
}
}
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 187a3ec1069a..7cdcfd024744 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -286,7 +286,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue)
xhci_queue_stop_endpoint(xhci, slot_id, i, suspend);
}
- cmd->command_trb = xhci->cmd_ring->enqueue;
+ cmd->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
list_add_tail(&cmd->cmd_list, &virt_dev->cmd_list);
xhci_queue_stop_endpoint(xhci, slot_id, 0, suspend);
xhci_ring_cmd_db(xhci);
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index f00cb203faea..159e3c6d92b9 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -345,7 +345,7 @@ static struct pci_driver xhci_pci_driver = {
/* suspend and resume implemented later */
.shutdown = usb_hcd_pci_shutdown,
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
.driver = {
.pm = &usb_hcd_pci_pm_ops
},
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 93ad67eca053..6e70ce976769 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -24,7 +24,7 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
* here that the generic code does not try to make a pci_dev from our
* dev struct in order to setup MSI
*/
- xhci->quirks |= XHCI_BROKEN_MSI;
+ xhci->quirks |= XHCI_PLAT;
}
/* called during probe() after chip reset completes */
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index cc3bfc5d590d..bcfb08e41eb6 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -122,6 +122,16 @@ static int enqueue_is_link_trb(struct xhci_ring *ring)
return TRB_TYPE_LINK_LE32(link->control);
}
+union xhci_trb *xhci_find_next_enqueue(struct xhci_ring *ring)
+{
+ /* Enqueue pointer can be left pointing to the link TRB,
+ * we must handle that
+ */
+ if (TRB_TYPE_LINK_LE32(ring->enqueue->link.control))
+ return ring->enq_seg->next->trbs;
+ return ring->enqueue;
+}
+
/* Updates trb to point to the next TRB in the ring, and updates seg if the next
* TRB is in a new segment. This does not skip over link TRBs, and it does not
* effect the ring dequeue or enqueue pointers.
@@ -847,8 +857,12 @@ remove_finished_td:
/* Otherwise ring the doorbell(s) to restart queued transfers */
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
}
- ep->stopped_td = NULL;
- ep->stopped_trb = NULL;
+
+ /* Clear stopped_td and stopped_trb if endpoint is not halted */
+ if (!(ep->ep_state & EP_HALTED)) {
+ ep->stopped_td = NULL;
+ ep->stopped_trb = NULL;
+ }
/*
* Drop the lock and complete the URBs in the cancelled TD list.
@@ -1390,6 +1404,12 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
inc_deq(xhci, xhci->cmd_ring);
return;
}
+ /* There is no command to handle if we get a stop event when the
+ * command ring is empty, event->cmd_trb points to the next
+ * unset command
+ */
+ if (xhci->cmd_ring->dequeue == xhci->cmd_ring->enqueue)
+ return;
}
switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 9a550b6ad01d..4a5251f3614c 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -342,9 +342,14 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
static int xhci_try_enable_msi(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+ struct pci_dev *pdev;
int ret;
+ /* The xhci platform device has set up IRQs through usb_add_hcd. */
+ if (xhci->quirks & XHCI_PLAT)
+ return 0;
+
+ pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
/*
* Some Fresco Logic host controllers advertise MSI, but fail to
* generate interrupts. Don't even try to enable MSI.
@@ -2587,15 +2592,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
if (command) {
cmd_completion = command->completion;
cmd_status = &command->status;
- command->command_trb = xhci->cmd_ring->enqueue;
-
- /* Enqueue pointer can be left pointing to the link TRB,
- * we must handle that
- */
- if (TRB_TYPE_LINK_LE32(command->command_trb->link.control))
- command->command_trb =
- xhci->cmd_ring->enq_seg->next->trbs;
-
+ command->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
list_add_tail(&command->cmd_list, &virt_dev->cmd_list);
} else {
cmd_completion = &virt_dev->cmd_completion;
@@ -2603,7 +2600,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
}
init_completion(cmd_completion);
- cmd_trb = xhci->cmd_ring->dequeue;
+ cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
if (!ctx_change)
ret = xhci_queue_configure_endpoint(xhci, in_ctx->dma,
udev->slot_id, must_succeed);
@@ -3388,14 +3385,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
/* Attempt to submit the Reset Device command to the command ring */
spin_lock_irqsave(&xhci->lock, flags);
- reset_device_cmd->command_trb = xhci->cmd_ring->enqueue;
-
- /* Enqueue pointer can be left pointing to the link TRB,
- * we must handle that
- */
- if (TRB_TYPE_LINK_LE32(reset_device_cmd->command_trb->link.control))
- reset_device_cmd->command_trb =
- xhci->cmd_ring->enq_seg->next->trbs;
+ reset_device_cmd->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
list_add_tail(&reset_device_cmd->cmd_list, &virt_dev->cmd_list);
ret = xhci_queue_reset_device(xhci, slot_id);
@@ -3506,10 +3496,21 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_virt_device *virt_dev;
+ struct device *dev = hcd->self.controller;
unsigned long flags;
u32 state;
int i, ret;
+#ifndef CONFIG_USB_DEFAULT_PERSIST
+ /*
+ * We called pm_runtime_get_noresume when the device was attached.
+ * Decrement the counter here to allow controller to runtime suspend
+ * if no devices remain.
+ */
+ if (xhci->quirks & XHCI_RESET_ON_RESUME)
+ pm_runtime_put_noidle(dev);
+#endif
+
ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
/* If the host is halted due to driver unload, we still need to free the
* device.
@@ -3581,13 +3582,14 @@ static int xhci_reserve_host_control_ep_resources(struct xhci_hcd *xhci)
int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct device *dev = hcd->self.controller;
unsigned long flags;
int timeleft;
int ret;
union xhci_trb *cmd_trb;
spin_lock_irqsave(&xhci->lock, flags);
- cmd_trb = xhci->cmd_ring->dequeue;
+ cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
ret = xhci_queue_slot_control(xhci, TRB_ENABLE_SLOT, 0);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -3633,6 +3635,16 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
goto disable_slot;
}
udev->slot_id = xhci->slot_id;
+
+#ifndef CONFIG_USB_DEFAULT_PERSIST
+ /*
+ * If resetting upon resume, we can't put the controller into runtime
+ * suspend if there is a device attached.
+ */
+ if (xhci->quirks & XHCI_RESET_ON_RESUME)
+ pm_runtime_get_noresume(dev);
+#endif
+
/* Is this a LS or FS device under a HS hub? */
/* Hub or peripherial? */
return 1;
@@ -3704,7 +3716,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2);
spin_lock_irqsave(&xhci->lock, flags);
- cmd_trb = xhci->cmd_ring->dequeue;
+ cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma,
udev->slot_id);
if (ret) {
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 77600cefcaf1..627fcd9388ca 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1516,6 +1516,7 @@ struct xhci_hcd {
#define XHCI_SPURIOUS_REBOOT (1 << 13)
#define XHCI_COMP_MODE_QUIRK (1 << 14)
#define XHCI_AVOID_BEI (1 << 15)
+#define XHCI_PLAT (1 << 16)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1820,6 +1821,7 @@ int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
union xhci_trb *cmd_trb);
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, unsigned int stream_id);
+union xhci_trb *xhci_find_next_enqueue(struct xhci_ring *ring);
/* xHCI roothub code */
void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index b65e657c641d..aa3aed5458a6 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -906,6 +906,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) },
/* Crucible Devices */
{ USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_Z3X_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 1b8af461b522..a7019d1e3058 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -1307,3 +1307,9 @@
* Manufacturer: Crucible Technologies
*/
#define FTDI_CT_COMET_PID 0x8e08
+
+/*
+ * Product: Z3X Box
+ * Manufacturer: Smart GSM Team
+ */
+#define FTDI_Z3X_PID 0x0011
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 5050cc8584ba..0f16bf6ea71c 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -374,7 +374,7 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport,
kfree(urbtrack);
return -ENOMEM;
}
- urbtrack->setup = kmalloc(sizeof(*urbtrack->setup), GFP_KERNEL);
+ urbtrack->setup = kmalloc(sizeof(*urbtrack->setup), GFP_ATOMIC);
if (!urbtrack->setup) {
usb_free_urb(urbtrack->urb);
kfree(urbtrack);
@@ -382,8 +382,8 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport,
}
urbtrack->setup->bRequestType = (__u8)0x40;
urbtrack->setup->bRequest = (__u8)0x0e;
- urbtrack->setup->wValue = get_reg_value(reg, dummy);
- urbtrack->setup->wIndex = get_reg_index(reg);
+ urbtrack->setup->wValue = cpu_to_le16(get_reg_value(reg, dummy));
+ urbtrack->setup->wIndex = cpu_to_le16(get_reg_index(reg));
urbtrack->setup->wLength = 0;
usb_fill_control_urb(urbtrack->urb, usbdev,
usb_sndctrlpipe(usbdev, 0),
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 1cf6f125f5f0..c3d94853b4ab 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -81,6 +81,7 @@ static void option_instat_callback(struct urb *urb);
#define HUAWEI_VENDOR_ID 0x12D1
#define HUAWEI_PRODUCT_E173 0x140C
+#define HUAWEI_PRODUCT_E1750 0x1406
#define HUAWEI_PRODUCT_K4505 0x1464
#define HUAWEI_PRODUCT_K3765 0x1465
#define HUAWEI_PRODUCT_K4605 0x14C6
@@ -450,6 +451,10 @@ static void option_instat_callback(struct urb *urb);
#define CHANGHONG_VENDOR_ID 0x2077
#define CHANGHONG_PRODUCT_CH690 0x7001
+/* Inovia */
+#define INOVIA_VENDOR_ID 0x20a6
+#define INOVIA_SEW858 0x1105
+
/* some devices interfaces need special handling due to a number of reasons */
enum option_blacklist_reason {
OPTION_BLACKLIST_NONE = 0,
@@ -567,6 +572,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c23, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t) &net_intf1_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1750, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t) &net_intf2_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1441, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1442, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff),
@@ -686,6 +693,222 @@ static const struct usb_device_id option_ids[] = {
{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7A) },
{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7B) },
{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x02) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x03) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x04) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x06) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x10) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x12) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x13) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x14) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x15) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x17) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x18) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x19) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x1A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x1B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x1C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x31) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x32) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x33) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x34) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x35) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x36) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x48) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x49) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x4A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x4B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x4C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x61) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x62) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x63) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x64) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x65) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x66) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x78) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x79) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x7A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x7B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x7C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x02) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x03) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x04) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x06) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x10) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x12) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x13) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x14) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x15) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x17) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x18) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x19) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x1A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x1B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x1C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x31) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x32) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x33) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x34) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x35) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x36) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x48) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x49) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x4A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x4B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x4C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x61) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x62) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x63) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x64) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x65) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x66) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x78) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x79) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x7A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x7B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x7C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x02) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x03) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x04) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x06) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x10) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x12) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x13) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x14) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x15) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x17) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x18) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x19) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x1A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x1B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x1C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x31) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x32) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x33) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x34) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x35) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x36) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x48) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x49) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x4A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x4B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x4C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x61) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x62) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x63) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x64) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x65) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x66) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x78) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x79) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x7A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x7B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x7C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x02) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x03) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x04) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x06) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x10) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x12) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x13) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x14) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x15) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x17) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x18) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x19) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x1A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x1B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x1C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x31) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x32) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x33) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x34) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x35) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x36) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x48) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x49) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x4A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x4B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x4C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x61) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x62) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x63) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x64) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x65) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x66) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x78) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x79) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x7A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x7B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x7C) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) },
@@ -1153,6 +1376,23 @@ static const struct usb_device_id option_ids[] = {
.driver_info = (kernel_ulong_t)&net_intf2_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff), /* ZTE MF91 */
.driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1533, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1534, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1535, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1545, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1546, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1547, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1565, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1566, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1567, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1589, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1590, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1591, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1592, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1594, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1596, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1598, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1600, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff,
0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) },
@@ -1254,7 +1494,9 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD145) },
- { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD200) },
+ { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD200),
+ .driver_info = (kernel_ulong_t)&net_intf6_blacklist
+ },
{ USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */
{ USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730 LTE USB modem.*/
{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEM600) },
@@ -1342,6 +1584,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
+ { USB_DEVICE(INOVIA_VENDOR_ID, INOVIA_SEW858) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 32bdd5eac59b..4cc84c0c990d 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -203,6 +203,7 @@ static struct usb_device_id ti_id_table_combined[19+2*TI_EXTRA_VID_PID_COUNT+1]
{ USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) },
{ USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) },
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) },
+ { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
{ USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
{ }
};
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 92b05d95ec5e..5db153260827 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -211,8 +211,11 @@ static int slave_configure(struct scsi_device *sdev)
/*
* Many devices do not respond properly to READ_CAPACITY_16.
* Tell the SCSI layer to try READ_CAPACITY_10 first.
+ * However some USB 3.0 drive enclosures return capacity
+ * modulo 2TB. Those must use READ_CAPACITY_16
*/
- sdev->try_rc_10_first = 1;
+ if (!(us->fflags & US_FL_NEEDS_CAP16))
+ sdev->try_rc_10_first = 1;
/* assume SPC3 or latter devices support sense size > 18 */
if (sdev->scsi_level > SCSI_SPC_2)
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index c015f2c16729..de32cfa5bfa6 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -1925,6 +1925,13 @@ UNUSUAL_DEV( 0x1652, 0x6600, 0x0201, 0x0201,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE ),
+/* Reported by Oliver Neukum <oneukum@suse.com> */
+UNUSUAL_DEV( 0x174c, 0x55aa, 0x0100, 0x0100,
+ "ASMedia",
+ "AS2105",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NEEDS_CAP16),
+
/* Reported by Jesse Feddema <jdfeddema@gmail.com> */
UNUSUAL_DEV( 0x177f, 0x0400, 0x0000, 0x0000,
"Yarvik",
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 701420297225..962c7e3c3baa 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -1017,7 +1017,7 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs,
if (data_direction != DMA_NONE) {
ret = vhost_scsi_map_iov_to_sgl(tv_cmd,
&vq->iov[data_first], data_num,
- data_direction == DMA_TO_DEVICE);
+ data_direction == DMA_FROM_DEVICE);
if (unlikely(ret)) {
vq_err(vq, "Failed to map iov to sgl\n");
goto err_free;
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 5fc798c12688..328e03ca7312 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2498,6 +2498,7 @@ source "drivers/video/exynos/Kconfig"
source "drivers/video/hisilicon/Kconfig"
source "drivers/video/mmp/Kconfig"
source "drivers/video/backlight/Kconfig"
+source "drivers/video/adf/Kconfig"
if VT
source "drivers/video/console/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 82c84ec55b05..8b6269857b99 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -12,6 +12,7 @@ fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
modedb.o fbcvt.o
fb-objs := $(fb-y)
+obj-$(CONFIG_ADF) += adf/
obj-$(CONFIG_VT) += console/
obj-$(CONFIG_LOGO) += logo/
obj-y += backlight/
diff --git a/drivers/video/adf/Kconfig b/drivers/video/adf/Kconfig
new file mode 100644
index 000000000000..33858b73d8bb
--- /dev/null
+++ b/drivers/video/adf/Kconfig
@@ -0,0 +1,14 @@
+menuconfig ADF
+ depends on SYNC
+ depends on DMA_SHARED_BUFFER
+ tristate "Atomic Display Framework"
+
+menuconfig ADF_FBDEV
+ depends on ADF
+ depends on FB
+ tristate "Helper for implementing the fbdev API in ADF drivers"
+
+menuconfig ADF_MEMBLOCK
+ depends on ADF
+ depends on HAVE_MEMBLOCK
+ tristate "Helper for using memblocks as buffers in ADF drivers"
diff --git a/drivers/video/adf/Makefile b/drivers/video/adf/Makefile
new file mode 100644
index 000000000000..78d0915122f4
--- /dev/null
+++ b/drivers/video/adf/Makefile
@@ -0,0 +1,15 @@
+ccflags-y := -Idrivers/staging/android
+
+CFLAGS_adf.o := -I$(src)
+
+obj-$(CONFIG_ADF) += adf.o \
+ adf_client.o \
+ adf_fops.o \
+ adf_format.o \
+ adf_sysfs.o
+
+obj-$(CONFIG_COMPAT) += adf_fops32.o
+
+obj-$(CONFIG_ADF_FBDEV) += adf_fbdev.o
+
+obj-$(CONFIG_ADF_MEMBLOCK) += adf_memblock.o
diff --git a/drivers/video/adf/adf.c b/drivers/video/adf/adf.c
new file mode 100644
index 000000000000..fd5bcde87850
--- /dev/null
+++ b/drivers/video/adf/adf.c
@@ -0,0 +1,1168 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ * adf_modeinfo_{set_name,set_vrefresh} modified from
+ * drivers/gpu/drm/drm_modes.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/idr.h>
+#include <linux/highmem.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/adf_format.h>
+
+#include "sw_sync.h"
+#include "sync.h"
+
+#include "adf.h"
+#include "adf_fops.h"
+#include "adf_sysfs.h"
+
+#define CREATE_TRACE_POINTS
+#include "adf_trace.h"
+
+#define ADF_SHORT_FENCE_TIMEOUT (1 * MSEC_PER_SEC)
+#define ADF_LONG_FENCE_TIMEOUT (10 * MSEC_PER_SEC)
+
+static void adf_fence_wait(struct adf_device *dev, struct sync_fence *fence)
+{
+ /* sync_fence_wait() dumps debug information on timeout. Experience
+ has shown that if the pipeline gets stuck, a short timeout followed
+ by a longer one provides useful information for debugging. */
+ int err = sync_fence_wait(fence, ADF_SHORT_FENCE_TIMEOUT);
+ if (err >= 0)
+ return;
+
+ if (err == -ETIME)
+ err = sync_fence_wait(fence, ADF_LONG_FENCE_TIMEOUT);
+
+ if (err < 0)
+ dev_warn(&dev->base.dev, "error waiting on fence: %d\n", err);
+}
+
+void adf_buffer_cleanup(struct adf_buffer *buf)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(buf->dma_bufs); i++)
+ if (buf->dma_bufs[i])
+ dma_buf_put(buf->dma_bufs[i]);
+
+ if (buf->acquire_fence)
+ sync_fence_put(buf->acquire_fence);
+}
+
+void adf_buffer_mapping_cleanup(struct adf_buffer_mapping *mapping,
+ struct adf_buffer *buf)
+{
+ /* calling adf_buffer_mapping_cleanup() is safe even if mapping is
+ uninitialized or partially-initialized, as long as it was
+ zeroed on allocation */
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(mapping->sg_tables); i++) {
+ if (mapping->sg_tables[i])
+ dma_buf_unmap_attachment(mapping->attachments[i],
+ mapping->sg_tables[i], DMA_TO_DEVICE);
+ if (mapping->attachments[i])
+ dma_buf_detach(buf->dma_bufs[i],
+ mapping->attachments[i]);
+ }
+}
+
+void adf_post_cleanup(struct adf_device *dev, struct adf_pending_post *post)
+{
+ size_t i;
+
+ if (post->state)
+ dev->ops->state_free(dev, post->state);
+
+ for (i = 0; i < post->config.n_bufs; i++) {
+ adf_buffer_mapping_cleanup(&post->config.mappings[i],
+ &post->config.bufs[i]);
+ adf_buffer_cleanup(&post->config.bufs[i]);
+ }
+
+ kfree(post->config.custom_data);
+ kfree(post->config.mappings);
+ kfree(post->config.bufs);
+ kfree(post);
+}
+
+static void adf_sw_advance_timeline(struct adf_device *dev)
+{
+#ifdef CONFIG_SW_SYNC
+ sw_sync_timeline_inc(dev->timeline, 1);
+#else
+ BUG();
+#endif
+}
+
+static void adf_post_work_func(struct kthread_work *work)
+{
+ struct adf_device *dev =
+ container_of(work, struct adf_device, post_work);
+ struct adf_pending_post *post, *next;
+ struct list_head saved_list;
+
+ mutex_lock(&dev->post_lock);
+ memcpy(&saved_list, &dev->post_list, sizeof(saved_list));
+ list_replace_init(&dev->post_list, &saved_list);
+ mutex_unlock(&dev->post_lock);
+
+ list_for_each_entry_safe(post, next, &saved_list, head) {
+ int i;
+
+ for (i = 0; i < post->config.n_bufs; i++) {
+ struct sync_fence *fence =
+ post->config.bufs[i].acquire_fence;
+ if (fence)
+ adf_fence_wait(dev, fence);
+ }
+
+ dev->ops->post(dev, &post->config, post->state);
+
+ if (dev->ops->advance_timeline)
+ dev->ops->advance_timeline(dev, &post->config,
+ post->state);
+ else
+ adf_sw_advance_timeline(dev);
+
+ list_del(&post->head);
+ if (dev->onscreen)
+ adf_post_cleanup(dev, dev->onscreen);
+ dev->onscreen = post;
+ }
+}
+
+void adf_attachment_free(struct adf_attachment_list *attachment)
+{
+ list_del(&attachment->head);
+ kfree(attachment);
+}
+
+struct adf_event_refcount *adf_obj_find_event_refcount(struct adf_obj *obj,
+ enum adf_event_type type)
+{
+ struct rb_root *root = &obj->event_refcount;
+ struct rb_node **new = &(root->rb_node);
+ struct rb_node *parent = NULL;
+ struct adf_event_refcount *refcount;
+
+ while (*new) {
+ refcount = container_of(*new, struct adf_event_refcount, node);
+ parent = *new;
+
+ if (refcount->type > type)
+ new = &(*new)->rb_left;
+ else if (refcount->type < type)
+ new = &(*new)->rb_right;
+ else
+ return refcount;
+ }
+
+ refcount = kzalloc(sizeof(*refcount), GFP_KERNEL);
+ if (!refcount)
+ return NULL;
+ refcount->type = type;
+
+ rb_link_node(&refcount->node, parent, new);
+ rb_insert_color(&refcount->node, root);
+ return refcount;
+}
+
+/**
+ * adf_event_get - increase the refcount for an event
+ *
+ * @obj: the object that produces the event
+ * @type: the event type
+ *
+ * ADF will call the object's set_event() op if needed. ops are allowed
+ * to sleep, so adf_event_get() must NOT be called from an atomic context.
+ *
+ * Returns 0 if successful, or -%EINVAL if the object does not support the
+ * requested event type.
+ */
+int adf_event_get(struct adf_obj *obj, enum adf_event_type type)
+{
+ struct adf_event_refcount *refcount;
+ int old_refcount;
+ int ret;
+
+ ret = adf_obj_check_supports_event(obj, type);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&obj->event_lock);
+
+ refcount = adf_obj_find_event_refcount(obj, type);
+ if (!refcount) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ old_refcount = refcount->refcount++;
+
+ if (old_refcount == 0) {
+ obj->ops->set_event(obj, type, true);
+ trace_adf_event_enable(obj, type);
+ }
+
+done:
+ mutex_unlock(&obj->event_lock);
+ return ret;
+}
+EXPORT_SYMBOL(adf_event_get);
+
+/**
+ * adf_event_put - decrease the refcount for an event
+ *
+ * @obj: the object that produces the event
+ * @type: the event type
+ *
+ * ADF will call the object's set_event() op if needed. ops are allowed
+ * to sleep, so adf_event_put() must NOT be called from an atomic context.
+ *
+ * Returns 0 if successful, -%EINVAL if the object does not support the
+ * requested event type, or -%EALREADY if the refcount is already 0.
+ */
+int adf_event_put(struct adf_obj *obj, enum adf_event_type type)
+{
+ struct adf_event_refcount *refcount;
+ int old_refcount;
+ int ret;
+
+ ret = adf_obj_check_supports_event(obj, type);
+ if (ret < 0)
+ return ret;
+
+
+ mutex_lock(&obj->event_lock);
+
+ refcount = adf_obj_find_event_refcount(obj, type);
+ if (!refcount) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ old_refcount = refcount->refcount--;
+
+ if (WARN_ON(old_refcount == 0)) {
+ refcount->refcount++;
+ ret = -EALREADY;
+ } else if (old_refcount == 1) {
+ obj->ops->set_event(obj, type, false);
+ trace_adf_event_disable(obj, type);
+ }
+
+done:
+ mutex_unlock(&obj->event_lock);
+ return ret;
+}
+EXPORT_SYMBOL(adf_event_put);
+
+/**
+ * adf_vsync_wait - wait for a vsync event on a display interface
+ *
+ * @intf: the display interface
+ * @timeout: timeout in jiffies (0 = wait indefinitely)
+ *
+ * adf_vsync_wait() may sleep, so it must NOT be called from an atomic context.
+ *
+ * This function returns -%ERESTARTSYS if it is interrupted by a signal.
+ * If @timeout == 0 then this function returns 0 on vsync. If @timeout > 0 then
+ * this function returns the number of remaining jiffies or -%ETIMEDOUT on
+ * timeout.
+ */
+int adf_vsync_wait(struct adf_interface *intf, long timeout)
+{
+ ktime_t timestamp;
+ int ret;
+ unsigned long flags;
+
+ read_lock_irqsave(&intf->vsync_lock, flags);
+ timestamp = intf->vsync_timestamp;
+ read_unlock_irqrestore(&intf->vsync_lock, flags);
+
+ adf_vsync_get(intf);
+ if (timeout) {
+ ret = wait_event_interruptible_timeout(intf->vsync_wait,
+ !ktime_equal(timestamp,
+ intf->vsync_timestamp),
+ msecs_to_jiffies(timeout));
+ if (ret == 0 && ktime_equal(timestamp, intf->vsync_timestamp))
+ ret = -ETIMEDOUT;
+ } else {
+ ret = wait_event_interruptible(intf->vsync_wait,
+ !ktime_equal(timestamp,
+ intf->vsync_timestamp));
+ }
+ adf_vsync_put(intf);
+
+ return ret;
+}
+EXPORT_SYMBOL(adf_vsync_wait);
+
+static void adf_event_queue(struct adf_obj *obj, struct adf_event *event)
+{
+ struct adf_file *file;
+ unsigned long flags;
+
+ trace_adf_event(obj, event->type);
+
+ spin_lock_irqsave(&obj->file_lock, flags);
+
+ list_for_each_entry(file, &obj->file_list, head)
+ if (test_bit(event->type, file->event_subscriptions))
+ adf_file_queue_event(file, event);
+
+ spin_unlock_irqrestore(&obj->file_lock, flags);
+}
+
+/**
+ * adf_event_notify - notify userspace of a driver-private event
+ *
+ * @obj: the ADF object that produced the event
+ * @event: the event
+ *
+ * adf_event_notify() may be called safely from an atomic context. It will
+ * copy @event if needed, so @event may point to a variable on the stack.
+ *
+ * Drivers must NOT call adf_event_notify() for vsync and hotplug events.
+ * ADF provides adf_vsync_notify() and
+ * adf_hotplug_notify_{connected,disconnected}() for these events.
+ */
+int adf_event_notify(struct adf_obj *obj, struct adf_event *event)
+{
+ if (WARN_ON(event->type == ADF_EVENT_VSYNC ||
+ event->type == ADF_EVENT_HOTPLUG))
+ return -EINVAL;
+
+ adf_event_queue(obj, event);
+ return 0;
+}
+EXPORT_SYMBOL(adf_event_notify);
+
+/**
+ * adf_vsync_notify - notify ADF of a display interface's vsync event
+ *
+ * @intf: the display interface
+ * @timestamp: the time the vsync occurred
+ *
+ * adf_vsync_notify() may be called safely from an atomic context.
+ */
+void adf_vsync_notify(struct adf_interface *intf, ktime_t timestamp)
+{
+ unsigned long flags;
+ struct adf_vsync_event event;
+
+ write_lock_irqsave(&intf->vsync_lock, flags);
+ intf->vsync_timestamp = timestamp;
+ write_unlock_irqrestore(&intf->vsync_lock, flags);
+
+ wake_up_interruptible_all(&intf->vsync_wait);
+
+ event.base.type = ADF_EVENT_VSYNC;
+ event.base.length = sizeof(event);
+ event.timestamp = ktime_to_ns(timestamp);
+ adf_event_queue(&intf->base, &event.base);
+}
+EXPORT_SYMBOL(adf_vsync_notify);
+
+void adf_hotplug_notify(struct adf_interface *intf, bool connected,
+ struct drm_mode_modeinfo *modelist, size_t n_modes)
+{
+ unsigned long flags;
+ struct adf_hotplug_event event;
+ struct drm_mode_modeinfo *old_modelist;
+
+ write_lock_irqsave(&intf->hotplug_modelist_lock, flags);
+ old_modelist = intf->modelist;
+ intf->hotplug_detect = connected;
+ intf->modelist = modelist;
+ intf->n_modes = n_modes;
+ write_unlock_irqrestore(&intf->hotplug_modelist_lock, flags);
+
+ kfree(old_modelist);
+
+ event.base.length = sizeof(event);
+ event.base.type = ADF_EVENT_HOTPLUG;
+ event.connected = connected;
+ adf_event_queue(&intf->base, &event.base);
+}
+
+/**
+ * adf_hotplug_notify_connected - notify ADF of a display interface being
+ * connected to a display
+ *
+ * @intf: the display interface
+ * @modelist: hardware modes supported by display
+ * @n_modes: length of modelist
+ *
+ * @modelist is copied as needed, so it may point to a variable on the stack.
+ *
+ * adf_hotplug_notify_connected() may NOT be called safely from an atomic
+ * context.
+ *
+ * Returns 0 on success or error code (<0) on error.
+ */
+int adf_hotplug_notify_connected(struct adf_interface *intf,
+ struct drm_mode_modeinfo *modelist, size_t n_modes)
+{
+ struct drm_mode_modeinfo *modelist_copy;
+
+ if (n_modes > ADF_MAX_MODES)
+ return -ENOMEM;
+
+ modelist_copy = kzalloc(sizeof(modelist_copy[0]) * n_modes,
+ GFP_KERNEL);
+ if (!modelist_copy)
+ return -ENOMEM;
+ memcpy(modelist_copy, modelist, sizeof(modelist_copy[0]) * n_modes);
+
+ adf_hotplug_notify(intf, true, modelist_copy, n_modes);
+ return 0;
+}
+EXPORT_SYMBOL(adf_hotplug_notify_connected);
+
+/**
+ * adf_hotplug_notify_disconnected - notify ADF of a display interface being
+ * disconnected from a display
+ *
+ * @intf: the display interface
+ *
+ * adf_hotplug_notify_disconnected() may be called safely from an atomic
+ * context.
+ */
+void adf_hotplug_notify_disconnected(struct adf_interface *intf)
+{
+ adf_hotplug_notify(intf, false, NULL, 0);
+}
+EXPORT_SYMBOL(adf_hotplug_notify_disconnected);
+
+static int adf_obj_init(struct adf_obj *obj, enum adf_obj_type type,
+ struct idr *idr, struct adf_device *parent,
+ const struct adf_obj_ops *ops, const char *fmt, va_list args)
+{
+ if (ops && ops->supports_event && !ops->set_event) {
+ pr_err("%s: %s implements supports_event but not set_event\n",
+ __func__, adf_obj_type_str(type));
+ return -EINVAL;
+ }
+
+ if (idr) {
+ int ret = idr_alloc(idr, obj, 0, 0, GFP_KERNEL);
+ if (ret < 0) {
+ pr_err("%s: allocating object id failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ obj->id = ret;
+ } else {
+ obj->id = -1;
+ }
+
+ vscnprintf(obj->name, sizeof(obj->name), fmt, args);
+
+ obj->type = type;
+ obj->ops = ops;
+ obj->parent = parent;
+ mutex_init(&obj->event_lock);
+ obj->event_refcount = RB_ROOT;
+ spin_lock_init(&obj->file_lock);
+ INIT_LIST_HEAD(&obj->file_list);
+ return 0;
+}
+
+static void adf_obj_destroy(struct adf_obj *obj, struct idr *idr)
+{
+ struct rb_node *node = rb_first(&obj->event_refcount);
+
+ while (node) {
+ struct adf_event_refcount *refcount =
+ container_of(node, struct adf_event_refcount,
+ node);
+ kfree(refcount);
+ node = rb_first(&obj->event_refcount);
+ }
+
+ mutex_destroy(&obj->event_lock);
+ if (idr)
+ idr_remove(idr, obj->id);
+}
+
+/**
+ * adf_device_init - initialize ADF-internal data for a display device
+ * and create sysfs entries
+ *
+ * @dev: the display device
+ * @parent: the device's parent device
+ * @ops: the device's associated ops
+ * @fmt: formatting string for the display device's name
+ *
+ * @fmt specifies the device's sysfs filename and the name returned to
+ * userspace through the %ADF_GET_DEVICE_DATA ioctl.
+ *
+ * Returns 0 on success or error code (<0) on failure.
+ */
+int adf_device_init(struct adf_device *dev, struct device *parent,
+ const struct adf_device_ops *ops, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+
+ if (!ops->validate || !ops->post) {
+ pr_err("%s: device must implement validate and post\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!ops->complete_fence && !ops->advance_timeline) {
+ if (!IS_ENABLED(CONFIG_SW_SYNC)) {
+ pr_err("%s: device requires sw_sync but it is not enabled in the kernel\n",
+ __func__);
+ return -EINVAL;
+ }
+ } else if (!(ops->complete_fence && ops->advance_timeline)) {
+ pr_err("%s: device must implement both complete_fence and advance_timeline, or implement neither\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ va_start(args, fmt);
+ ret = adf_obj_init(&dev->base, ADF_OBJ_DEVICE, NULL, dev, &ops->base,
+ fmt, args);
+ va_end(args);
+ if (ret < 0)
+ return ret;
+
+ dev->dev = parent;
+ dev->ops = ops;
+ idr_init(&dev->overlay_engines);
+ idr_init(&dev->interfaces);
+ mutex_init(&dev->client_lock);
+ INIT_LIST_HEAD(&dev->post_list);
+ mutex_init(&dev->post_lock);
+ init_kthread_worker(&dev->post_worker);
+ INIT_LIST_HEAD(&dev->attached);
+ INIT_LIST_HEAD(&dev->attach_allowed);
+
+ dev->post_thread = kthread_run(kthread_worker_fn,
+ &dev->post_worker, dev->base.name);
+ if (IS_ERR(dev->post_thread)) {
+ ret = PTR_ERR(dev->post_thread);
+ dev->post_thread = NULL;
+
+ pr_err("%s: failed to run config posting thread: %d\n",
+ __func__, ret);
+ goto err;
+ }
+ init_kthread_work(&dev->post_work, adf_post_work_func);
+
+ ret = adf_device_sysfs_init(dev);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ adf_device_destroy(dev);
+ return ret;
+}
+EXPORT_SYMBOL(adf_device_init);
+
+/**
+ * adf_device_destroy - clean up ADF-internal data for a display device
+ *
+ * @dev: the display device
+ */
+void adf_device_destroy(struct adf_device *dev)
+{
+ struct adf_attachment_list *entry, *next;
+
+ idr_destroy(&dev->interfaces);
+ idr_destroy(&dev->overlay_engines);
+
+ if (dev->post_thread) {
+ flush_kthread_worker(&dev->post_worker);
+ kthread_stop(dev->post_thread);
+ }
+
+ if (dev->onscreen)
+ adf_post_cleanup(dev, dev->onscreen);
+ adf_device_sysfs_destroy(dev);
+ list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) {
+ adf_attachment_free(entry);
+ }
+ list_for_each_entry_safe(entry, next, &dev->attached, head) {
+ adf_attachment_free(entry);
+ }
+ mutex_destroy(&dev->post_lock);
+ mutex_destroy(&dev->client_lock);
+ adf_obj_destroy(&dev->base, NULL);
+}
+EXPORT_SYMBOL(adf_device_destroy);
+
+/**
+ * adf_interface_init - initialize ADF-internal data for a display interface
+ * and create sysfs entries
+ *
+ * @intf: the display interface
+ * @dev: the interface's "parent" display device
+ * @type: interface type (see enum @adf_interface_type)
+ * @idx: which interface of type @type;
+ * e.g. interface DSI.1 -> @type=%ADF_INTF_TYPE_DSI, @idx=1
+ * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values)
+ * @ops: the interface's associated ops
+ * @fmt: formatting string for the display interface's name
+ *
+ * @dev must have previously been initialized with adf_device_init().
+ *
+ * @fmt affects the name returned to userspace through the
+ * %ADF_GET_INTERFACE_DATA ioctl. It does not affect the sysfs filename,
+ * which is derived from @dev's name.
+ *
+ * Returns 0 on success or error code (<0) on failure.
+ */
+int adf_interface_init(struct adf_interface *intf, struct adf_device *dev,
+ enum adf_interface_type type, u32 idx, u32 flags,
+ const struct adf_interface_ops *ops, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+ const u32 allowed_flags = ADF_INTF_FLAG_PRIMARY |
+ ADF_INTF_FLAG_EXTERNAL;
+
+ if (dev->n_interfaces == ADF_MAX_INTERFACES) {
+ pr_err("%s: parent device %s has too many interfaces\n",
+ __func__, dev->base.name);
+ return -ENOMEM;
+ }
+
+ if (type >= ADF_INTF_MEMORY && type <= ADF_INTF_TYPE_DEVICE_CUSTOM) {
+ pr_err("%s: invalid interface type %u\n", __func__, type);
+ return -EINVAL;
+ }
+
+ if (flags & ~allowed_flags) {
+ pr_err("%s: invalid interface flags 0x%X\n", __func__,
+ flags & ~allowed_flags);
+ return -EINVAL;
+ }
+
+ memset(intf, 0, sizeof(*intf));
+
+ va_start(args, fmt);
+ ret = adf_obj_init(&intf->base, ADF_OBJ_INTERFACE, &dev->interfaces,
+ dev, ops ? &ops->base : NULL, fmt, args);
+ va_end(args);
+ if (ret < 0)
+ return ret;
+
+ intf->type = type;
+ intf->idx = idx;
+ intf->flags = flags;
+ intf->ops = ops;
+ intf->dpms_state = DRM_MODE_DPMS_OFF;
+ init_waitqueue_head(&intf->vsync_wait);
+ rwlock_init(&intf->vsync_lock);
+ rwlock_init(&intf->hotplug_modelist_lock);
+
+ ret = adf_interface_sysfs_init(intf);
+ if (ret < 0)
+ goto err;
+ dev->n_interfaces++;
+
+ return 0;
+
+err:
+ adf_obj_destroy(&intf->base, &dev->interfaces);
+ return ret;
+}
+EXPORT_SYMBOL(adf_interface_init);
+
+/**
+ * adf_interface_destroy - clean up ADF-internal data for a display interface
+ *
+ * @intf: the display interface
+ */
+void adf_interface_destroy(struct adf_interface *intf)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ struct adf_attachment_list *entry, *next;
+
+ mutex_lock(&dev->client_lock);
+ list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) {
+ if (entry->attachment.interface == intf) {
+ adf_attachment_free(entry);
+ dev->n_attach_allowed--;
+ }
+ }
+ list_for_each_entry_safe(entry, next, &dev->attached, head) {
+ if (entry->attachment.interface == intf) {
+ adf_device_detach_op(dev,
+ entry->attachment.overlay_engine, intf);
+ adf_attachment_free(entry);
+ dev->n_attached--;
+ }
+ }
+ kfree(intf->modelist);
+ adf_interface_sysfs_destroy(intf);
+ adf_obj_destroy(&intf->base, &dev->interfaces);
+ dev->n_interfaces--;
+ mutex_unlock(&dev->client_lock);
+}
+EXPORT_SYMBOL(adf_interface_destroy);
+
+static bool adf_overlay_engine_has_custom_formats(
+ const struct adf_overlay_engine_ops *ops)
+{
+ size_t i;
+ for (i = 0; i < ops->n_supported_formats; i++)
+ if (!adf_format_is_standard(ops->supported_formats[i]))
+ return true;
+ return false;
+}
+
+/**
+ * adf_overlay_engine_init - initialize ADF-internal data for an
+ * overlay engine and create sysfs entries
+ *
+ * @eng: the overlay engine
+ * @dev: the overlay engine's "parent" display device
+ * @ops: the overlay engine's associated ops
+ * @fmt: formatting string for the overlay engine's name
+ *
+ * @dev must have previously been initialized with adf_device_init().
+ *
+ * @fmt affects the name returned to userspace through the
+ * %ADF_GET_OVERLAY_ENGINE_DATA ioctl. It does not affect the sysfs filename,
+ * which is derived from @dev's name.
+ *
+ * Returns 0 on success or error code (<0) on failure.
+ */
+int adf_overlay_engine_init(struct adf_overlay_engine *eng,
+ struct adf_device *dev,
+ const struct adf_overlay_engine_ops *ops, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+
+ if (!ops->supported_formats) {
+ pr_err("%s: overlay engine must support at least one format\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (ops->n_supported_formats > ADF_MAX_SUPPORTED_FORMATS) {
+ pr_err("%s: overlay engine supports too many formats\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (adf_overlay_engine_has_custom_formats(ops) &&
+ !dev->ops->validate_custom_format) {
+ pr_err("%s: overlay engine has custom formats but parent device %s does not implement validate_custom_format\n",
+ __func__, dev->base.name);
+ return -EINVAL;
+ }
+
+ memset(eng, 0, sizeof(*eng));
+
+ va_start(args, fmt);
+ ret = adf_obj_init(&eng->base, ADF_OBJ_OVERLAY_ENGINE,
+ &dev->overlay_engines, dev, &ops->base, fmt, args);
+ va_end(args);
+ if (ret < 0)
+ return ret;
+
+ eng->ops = ops;
+
+ ret = adf_overlay_engine_sysfs_init(eng);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ adf_obj_destroy(&eng->base, &dev->overlay_engines);
+ return ret;
+}
+EXPORT_SYMBOL(adf_overlay_engine_init);
+
+/**
+ * adf_interface_destroy - clean up ADF-internal data for an overlay engine
+ *
+ * @eng: the overlay engine
+ */
+void adf_overlay_engine_destroy(struct adf_overlay_engine *eng)
+{
+ struct adf_device *dev = adf_overlay_engine_parent(eng);
+ struct adf_attachment_list *entry, *next;
+
+ mutex_lock(&dev->client_lock);
+ list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) {
+ if (entry->attachment.overlay_engine == eng) {
+ adf_attachment_free(entry);
+ dev->n_attach_allowed--;
+ }
+ }
+ list_for_each_entry_safe(entry, next, &dev->attached, head) {
+ if (entry->attachment.overlay_engine == eng) {
+ adf_device_detach_op(dev, eng,
+ entry->attachment.interface);
+ adf_attachment_free(entry);
+ dev->n_attached--;
+ }
+ }
+ adf_overlay_engine_sysfs_destroy(eng);
+ adf_obj_destroy(&eng->base, &dev->overlay_engines);
+ mutex_unlock(&dev->client_lock);
+}
+EXPORT_SYMBOL(adf_overlay_engine_destroy);
+
+struct adf_attachment_list *adf_attachment_find(struct list_head *list,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ struct adf_attachment_list *entry;
+ list_for_each_entry(entry, list, head) {
+ if (entry->attachment.interface == intf &&
+ entry->attachment.overlay_engine == eng)
+ return entry;
+ }
+ return NULL;
+}
+
+int adf_attachment_validate(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ struct adf_device *intf_dev = adf_interface_parent(intf);
+ struct adf_device *eng_dev = adf_overlay_engine_parent(eng);
+
+ if (intf_dev != dev) {
+ dev_err(&dev->base.dev, "can't attach interface %s belonging to device %s\n",
+ intf->base.name, intf_dev->base.name);
+ return -EINVAL;
+ }
+
+ if (eng_dev != dev) {
+ dev_err(&dev->base.dev, "can't attach overlay engine %s belonging to device %s\n",
+ eng->base.name, eng_dev->base.name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * adf_attachment_allow - add a new entry to the list of allowed
+ * attachments
+ *
+ * @dev: the parent device
+ * @eng: the overlay engine
+ * @intf: the interface
+ *
+ * adf_attachment_allow() indicates that the underlying display hardware allows
+ * @intf to scan out @eng's output. It is intended to be called at
+ * driver initialization for each supported overlay engine + interface pair.
+ *
+ * Returns 0 on success, -%EALREADY if the entry already exists, or -errno on
+ * any other failure.
+ */
+int adf_attachment_allow(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ int ret;
+ struct adf_attachment_list *entry = NULL;
+
+ ret = adf_attachment_validate(dev, eng, intf);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&dev->client_lock);
+
+ if (dev->n_attach_allowed == ADF_MAX_ATTACHMENTS) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (adf_attachment_find(&dev->attach_allowed, eng, intf)) {
+ ret = -EALREADY;
+ goto done;
+ }
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ entry->attachment.interface = intf;
+ entry->attachment.overlay_engine = eng;
+ list_add_tail(&entry->head, &dev->attach_allowed);
+ dev->n_attach_allowed++;
+
+done:
+ mutex_unlock(&dev->client_lock);
+ if (ret < 0)
+ kfree(entry);
+
+ return ret;
+}
+
+/**
+ * adf_obj_type_str - string representation of an adf_obj_type
+ *
+ * @type: the object type
+ */
+const char *adf_obj_type_str(enum adf_obj_type type)
+{
+ switch (type) {
+ case ADF_OBJ_OVERLAY_ENGINE:
+ return "overlay engine";
+
+ case ADF_OBJ_INTERFACE:
+ return "interface";
+
+ case ADF_OBJ_DEVICE:
+ return "device";
+
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(adf_obj_type_str);
+
+/**
+ * adf_interface_type_str - string representation of an adf_interface's type
+ *
+ * @intf: the interface
+ */
+const char *adf_interface_type_str(struct adf_interface *intf)
+{
+ switch (intf->type) {
+ case ADF_INTF_DSI:
+ return "DSI";
+
+ case ADF_INTF_eDP:
+ return "eDP";
+
+ case ADF_INTF_DPI:
+ return "DPI";
+
+ case ADF_INTF_VGA:
+ return "VGA";
+
+ case ADF_INTF_DVI:
+ return "DVI";
+
+ case ADF_INTF_HDMI:
+ return "HDMI";
+
+ case ADF_INTF_MEMORY:
+ return "memory";
+
+ default:
+ if (intf->type >= ADF_INTF_TYPE_DEVICE_CUSTOM) {
+ if (intf->ops && intf->ops->type_str)
+ return intf->ops->type_str(intf);
+ return "custom";
+ }
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(adf_interface_type_str);
+
+/**
+ * adf_event_type_str - string representation of an adf_event_type
+ *
+ * @obj: ADF object that produced the event
+ * @type: event type
+ */
+const char *adf_event_type_str(struct adf_obj *obj, enum adf_event_type type)
+{
+ switch (type) {
+ case ADF_EVENT_VSYNC:
+ return "vsync";
+
+ case ADF_EVENT_HOTPLUG:
+ return "hotplug";
+
+ default:
+ if (type >= ADF_EVENT_DEVICE_CUSTOM) {
+ if (obj->ops && obj->ops->event_type_str)
+ return obj->ops->event_type_str(obj, type);
+ return "custom";
+ }
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(adf_event_type_str);
+
+/**
+ * adf_format_str - string representation of an ADF/DRM fourcc format
+ *
+ * @format: format fourcc
+ * @buf: target buffer for the format's string representation
+ */
+void adf_format_str(u32 format, char buf[ADF_FORMAT_STR_SIZE])
+{
+ buf[0] = format & 0xFF;
+ buf[1] = (format >> 8) & 0xFF;
+ buf[2] = (format >> 16) & 0xFF;
+ buf[3] = (format >> 24) & 0xFF;
+ buf[4] = '\0';
+}
+EXPORT_SYMBOL(adf_format_str);
+
+/**
+ * adf_format_validate_yuv - validate the number and size of planes in buffers
+ * with a custom YUV format.
+ *
+ * @dev: ADF device performing the validation
+ * @buf: buffer to validate
+ * @num_planes: expected number of planes
+ * @hsub: expected horizontal chroma subsampling factor, in pixels
+ * @vsub: expected vertical chroma subsampling factor, in pixels
+ * @cpp: expected bytes per pixel for each plane (length @num_planes)
+ *
+ * adf_format_validate_yuv() is intended to be called as a helper from @dev's
+ * validate_custom_format() op.
+ *
+ * Returns 0 if @buf has the expected number of planes and each plane
+ * has sufficient size, or -EINVAL otherwise.
+ */
+int adf_format_validate_yuv(struct adf_device *dev, struct adf_buffer *buf,
+ u8 num_planes, u8 hsub, u8 vsub, u8 cpp[])
+{
+ u8 i;
+
+ if (num_planes != buf->n_planes) {
+ char format_str[ADF_FORMAT_STR_SIZE];
+ adf_format_str(buf->format, format_str);
+ dev_err(&dev->base.dev, "%u planes expected for format %s but %u planes provided\n",
+ num_planes, format_str, buf->n_planes);
+ return -EINVAL;
+ }
+
+ if (buf->w == 0 || buf->w % hsub) {
+ dev_err(&dev->base.dev, "bad buffer width %u\n", buf->w);
+ return -EINVAL;
+ }
+
+ if (buf->h == 0 || buf->h % vsub) {
+ dev_err(&dev->base.dev, "bad buffer height %u\n", buf->h);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_planes; i++) {
+ u32 width = buf->w / (i != 0 ? hsub : 1);
+ u32 height = buf->h / (i != 0 ? vsub : 1);
+ u8 cpp = adf_format_plane_cpp(buf->format, i);
+
+ if (buf->pitch[i] < (u64) width * cpp) {
+ dev_err(&dev->base.dev, "plane %u pitch is shorter than buffer width (pitch = %u, width = %u, bpp = %u)\n",
+ i, buf->pitch[i], width, cpp * 8);
+ return -EINVAL;
+ }
+
+ if ((u64) height * buf->pitch[i] + buf->offset[i] >
+ buf->dma_bufs[i]->size) {
+ dev_err(&dev->base.dev, "plane %u buffer too small (height = %u, pitch = %u, offset = %u, size = %zu)\n",
+ i, height, buf->pitch[i],
+ buf->offset[i], buf->dma_bufs[i]->size);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(adf_format_validate_yuv);
+
+/**
+ * adf_modeinfo_set_name - sets the name of a mode from its display resolution
+ *
+ * @mode: mode
+ *
+ * adf_modeinfo_set_name() fills in @mode->name in the format
+ * "[hdisplay]x[vdisplay](i)". It is intended to help drivers create
+ * ADF/DRM-style modelists from other mode formats.
+ */
+void adf_modeinfo_set_name(struct drm_mode_modeinfo *mode)
+{
+ bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
+
+ snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d%s",
+ mode->hdisplay, mode->vdisplay,
+ interlaced ? "i" : "");
+}
+EXPORT_SYMBOL(adf_modeinfo_set_name);
+
+/**
+ * adf_modeinfo_set_vrefresh - sets the vrefresh of a mode from its other
+ * timing data
+ *
+ * @mode: mode
+ *
+ * adf_modeinfo_set_vrefresh() calculates @mode->vrefresh from
+ * @mode->{h,v}display and @mode->flags. It is intended to help drivers
+ * create ADF/DRM-style modelists from other mode formats.
+ */
+void adf_modeinfo_set_vrefresh(struct drm_mode_modeinfo *mode)
+{
+ int refresh = 0;
+ unsigned int calc_val;
+
+ if (mode->vrefresh > 0)
+ return;
+
+ if (mode->htotal <= 0 || mode->vtotal <= 0)
+ return;
+
+ /* work out vrefresh the value will be x1000 */
+ calc_val = (mode->clock * 1000);
+ calc_val /= mode->htotal;
+ refresh = (calc_val + mode->vtotal / 2) / mode->vtotal;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ refresh *= 2;
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ refresh /= 2;
+ if (mode->vscan > 1)
+ refresh /= mode->vscan;
+
+ mode->vrefresh = refresh;
+}
+EXPORT_SYMBOL(adf_modeinfo_set_vrefresh);
+
+static int __init adf_init(void)
+{
+ int err;
+
+ err = adf_sysfs_init();
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void __exit adf_exit(void)
+{
+ adf_sysfs_destroy();
+}
+
+module_init(adf_init);
+module_exit(adf_exit);
diff --git a/drivers/video/adf/adf.h b/drivers/video/adf/adf.h
new file mode 100644
index 000000000000..3bcf1fabc23c
--- /dev/null
+++ b/drivers/video/adf/adf.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __VIDEO_ADF_ADF_H
+#define __VIDEO_ADF_ADF_H
+
+#include <linux/idr.h>
+#include <linux/list.h>
+#include <video/adf.h>
+#include "sync.h"
+
+struct adf_event_refcount {
+ struct rb_node node;
+ enum adf_event_type type;
+ int refcount;
+};
+
+void adf_buffer_cleanup(struct adf_buffer *buf);
+void adf_buffer_mapping_cleanup(struct adf_buffer_mapping *mapping,
+ struct adf_buffer *buf);
+void adf_post_cleanup(struct adf_device *dev, struct adf_pending_post *post);
+
+struct adf_attachment_list *adf_attachment_find(struct list_head *list,
+ struct adf_overlay_engine *eng, struct adf_interface *intf);
+int adf_attachment_validate(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf);
+void adf_attachment_free(struct adf_attachment_list *attachment);
+
+struct adf_event_refcount *adf_obj_find_event_refcount(struct adf_obj *obj,
+ enum adf_event_type type);
+
+static inline int adf_obj_check_supports_event(struct adf_obj *obj,
+ enum adf_event_type type)
+{
+ if (!obj->ops || !obj->ops->supports_event)
+ return -EOPNOTSUPP;
+ if (!obj->ops->supports_event(obj, type))
+ return -EINVAL;
+ return 0;
+}
+
+static inline int adf_device_attach_op(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ if (!dev->ops->attach)
+ return 0;
+
+ return dev->ops->attach(dev, eng, intf);
+}
+
+static inline int adf_device_detach_op(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ if (!dev->ops->detach)
+ return 0;
+
+ return dev->ops->detach(dev, eng, intf);
+}
+
+#endif /* __VIDEO_ADF_ADF_H */
diff --git a/drivers/video/adf/adf_client.c b/drivers/video/adf/adf_client.c
new file mode 100644
index 000000000000..e4a792135072
--- /dev/null
+++ b/drivers/video/adf/adf_client.c
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include "sw_sync.h"
+
+#include <video/adf.h>
+#include <video/adf_client.h>
+#include <video/adf_format.h>
+
+#include "adf.h"
+
+static inline bool vsync_active(u8 state)
+{
+ return state == DRM_MODE_DPMS_ON || state == DRM_MODE_DPMS_STANDBY;
+}
+
+/**
+ * adf_interface_blank - set interface's DPMS state
+ *
+ * @intf: the interface
+ * @state: one of %DRM_MODE_DPMS_*
+ *
+ * Returns 0 on success or -errno on failure.
+ */
+int adf_interface_blank(struct adf_interface *intf, u8 state)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ u8 prev_state;
+ bool disable_vsync;
+ bool enable_vsync;
+ int ret = 0;
+ struct adf_event_refcount *vsync_refcount;
+
+ if (!intf->ops || !intf->ops->blank)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&dev->client_lock);
+ if (state != DRM_MODE_DPMS_ON)
+ flush_kthread_worker(&dev->post_worker);
+ mutex_lock(&intf->base.event_lock);
+
+ vsync_refcount = adf_obj_find_event_refcount(&intf->base,
+ ADF_EVENT_VSYNC);
+ if (!vsync_refcount) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ prev_state = intf->dpms_state;
+ if (prev_state == state) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ disable_vsync = vsync_active(prev_state) &&
+ !vsync_active(state) &&
+ vsync_refcount->refcount;
+ enable_vsync = !vsync_active(prev_state) &&
+ vsync_active(state) &&
+ vsync_refcount->refcount;
+
+ if (disable_vsync)
+ intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC,
+ false);
+
+ ret = intf->ops->blank(intf, state);
+ if (ret < 0) {
+ if (disable_vsync)
+ intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC,
+ true);
+ goto done;
+ }
+
+ if (enable_vsync)
+ intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC,
+ true);
+
+ intf->dpms_state = state;
+done:
+ mutex_unlock(&intf->base.event_lock);
+ mutex_unlock(&dev->client_lock);
+ return ret;
+}
+EXPORT_SYMBOL(adf_interface_blank);
+
+/**
+ * adf_interface_blank - get interface's current DPMS state
+ *
+ * @intf: the interface
+ *
+ * Returns one of %DRM_MODE_DPMS_*.
+ */
+u8 adf_interface_dpms_state(struct adf_interface *intf)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ u8 dpms_state;
+
+ mutex_lock(&dev->client_lock);
+ dpms_state = intf->dpms_state;
+ mutex_unlock(&dev->client_lock);
+
+ return dpms_state;
+}
+EXPORT_SYMBOL(adf_interface_dpms_state);
+
+/**
+ * adf_interface_current_mode - get interface's current display mode
+ *
+ * @intf: the interface
+ * @mode: returns the current mode
+ */
+void adf_interface_current_mode(struct adf_interface *intf,
+ struct drm_mode_modeinfo *mode)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+
+ mutex_lock(&dev->client_lock);
+ memcpy(mode, &intf->current_mode, sizeof(*mode));
+ mutex_unlock(&dev->client_lock);
+}
+EXPORT_SYMBOL(adf_interface_current_mode);
+
+/**
+ * adf_interface_modelist - get interface's modelist
+ *
+ * @intf: the interface
+ * @modelist: storage for the modelist (optional)
+ * @n_modes: length of @modelist
+ *
+ * If @modelist is not NULL, adf_interface_modelist() will copy up to @n_modes
+ * modelist entries into @modelist.
+ *
+ * Returns the length of the modelist.
+ */
+size_t adf_interface_modelist(struct adf_interface *intf,
+ struct drm_mode_modeinfo *modelist, size_t n_modes)
+{
+ unsigned long flags;
+ size_t retval;
+
+ read_lock_irqsave(&intf->hotplug_modelist_lock, flags);
+ if (modelist)
+ memcpy(modelist, intf->modelist, sizeof(modelist[0]) *
+ min(n_modes, intf->n_modes));
+ retval = intf->n_modes;
+ read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags);
+
+ return retval;
+}
+EXPORT_SYMBOL(adf_interface_modelist);
+
+/**
+ * adf_interface_set_mode - set interface's display mode
+ *
+ * @intf: the interface
+ * @mode: the new mode
+ *
+ * Returns 0 on success or -errno on failure.
+ */
+int adf_interface_set_mode(struct adf_interface *intf,
+ struct drm_mode_modeinfo *mode)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ int ret = 0;
+
+ if (!intf->ops || !intf->ops->modeset)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&dev->client_lock);
+ flush_kthread_worker(&dev->post_worker);
+
+ ret = intf->ops->modeset(intf, mode);
+ if (ret < 0)
+ goto done;
+
+ memcpy(&intf->current_mode, mode, sizeof(*mode));
+done:
+ mutex_unlock(&dev->client_lock);
+ return ret;
+}
+EXPORT_SYMBOL(adf_interface_set_mode);
+
+/**
+ * adf_interface_screen_size - get size of screen connected to interface
+ *
+ * @intf: the interface
+ * @width_mm: returns the screen width in mm
+ * @height_mm: returns the screen width in mm
+ *
+ * Returns 0 on success or -errno on failure.
+ */
+int adf_interface_get_screen_size(struct adf_interface *intf, u16 *width_mm,
+ u16 *height_mm)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ int ret;
+
+ if (!intf->ops || !intf->ops->screen_size)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&dev->client_lock);
+ ret = intf->ops->screen_size(intf, width_mm, height_mm);
+ mutex_unlock(&dev->client_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(adf_interface_get_screen_size);
+
+/**
+ * adf_overlay_engine_supports_format - returns whether a format is in an
+ * overlay engine's supported list
+ *
+ * @eng: the overlay engine
+ * @format: format fourcc
+ */
+bool adf_overlay_engine_supports_format(struct adf_overlay_engine *eng,
+ u32 format)
+{
+ size_t i;
+ for (i = 0; i < eng->ops->n_supported_formats; i++)
+ if (format == eng->ops->supported_formats[i])
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL(adf_overlay_engine_supports_format);
+
+static int adf_buffer_validate(struct adf_buffer *buf)
+{
+ struct adf_overlay_engine *eng = buf->overlay_engine;
+ struct device *dev = &eng->base.dev;
+ struct adf_device *parent = adf_overlay_engine_parent(eng);
+ u8 hsub, vsub, num_planes, cpp[ADF_MAX_PLANES], i;
+
+ if (!adf_overlay_engine_supports_format(eng, buf->format)) {
+ char format_str[ADF_FORMAT_STR_SIZE];
+ adf_format_str(buf->format, format_str);
+ dev_err(dev, "unsupported format %s\n", format_str);
+ return -EINVAL;
+ }
+
+ if (!adf_format_is_standard(buf->format))
+ return parent->ops->validate_custom_format(parent, buf);
+
+ hsub = adf_format_horz_chroma_subsampling(buf->format);
+ vsub = adf_format_vert_chroma_subsampling(buf->format);
+ num_planes = adf_format_num_planes(buf->format);
+ for (i = 0; i < num_planes; i++)
+ cpp[i] = adf_format_plane_cpp(buf->format, i);
+
+ return adf_format_validate_yuv(parent, buf, num_planes, hsub, vsub,
+ cpp);
+}
+
+static int adf_buffer_map(struct adf_device *dev, struct adf_buffer *buf,
+ struct adf_buffer_mapping *mapping)
+{
+ int ret = 0;
+ size_t i;
+
+ for (i = 0; i < buf->n_planes; i++) {
+ struct dma_buf_attachment *attachment;
+ struct sg_table *sg_table;
+
+ attachment = dma_buf_attach(buf->dma_bufs[i], dev->dev);
+ if (IS_ERR(attachment)) {
+ ret = PTR_ERR(attachment);
+ dev_err(&dev->base.dev, "attaching plane %u failed: %d\n",
+ i, ret);
+ goto done;
+ }
+ mapping->attachments[i] = attachment;
+
+ sg_table = dma_buf_map_attachment(attachment, DMA_TO_DEVICE);
+ if (IS_ERR(sg_table)) {
+ ret = PTR_ERR(sg_table);
+ dev_err(&dev->base.dev, "mapping plane %u failed: %d",
+ i, ret);
+ goto done;
+ } else if (!sg_table) {
+ ret = -ENOMEM;
+ dev_err(&dev->base.dev, "mapping plane %u failed\n", i);
+ goto done;
+ }
+ mapping->sg_tables[i] = sg_table;
+ }
+
+done:
+ if (ret < 0)
+ adf_buffer_mapping_cleanup(mapping, buf);
+
+ return ret;
+}
+
+static struct sync_fence *adf_sw_complete_fence(struct adf_device *dev)
+{
+ struct sync_pt *pt;
+ struct sync_fence *complete_fence;
+
+ if (!dev->timeline) {
+ dev->timeline = sw_sync_timeline_create(dev->base.name);
+ if (!dev->timeline)
+ return ERR_PTR(-ENOMEM);
+ dev->timeline_max = 1;
+ }
+
+ dev->timeline_max++;
+ pt = sw_sync_pt_create(dev->timeline, dev->timeline_max);
+ if (!pt)
+ goto err_pt_create;
+ complete_fence = sync_fence_create(dev->base.name, pt);
+ if (!complete_fence)
+ goto err_fence_create;
+
+ return complete_fence;
+
+err_fence_create:
+ sync_pt_free(pt);
+err_pt_create:
+ dev->timeline_max--;
+ return ERR_PTR(-ENOSYS);
+}
+
+/**
+ * adf_device_post - flip to a new set of buffers
+ *
+ * @dev: device targeted by the flip
+ * @intfs: interfaces targeted by the flip
+ * @n_intfs: number of targeted interfaces
+ * @bufs: description of buffers displayed
+ * @n_bufs: number of buffers displayed
+ * @custom_data: driver-private data
+ * @custom_data_size: size of driver-private data
+ *
+ * adf_device_post() will copy @intfs, @bufs, and @custom_data, so they may
+ * point to variables on the stack. adf_device_post() also takes its own
+ * reference on each of the dma-bufs in @bufs. The adf_device_post_nocopy()
+ * variant transfers ownership of these resources to ADF instead.
+ *
+ * On success, returns a sync fence which signals when the buffers are removed
+ * from the screen. On failure, returns ERR_PTR(-errno).
+ */
+struct sync_fence *adf_device_post(struct adf_device *dev,
+ struct adf_interface **intfs, size_t n_intfs,
+ struct adf_buffer *bufs, size_t n_bufs, void *custom_data,
+ size_t custom_data_size)
+{
+ struct adf_interface **intfs_copy = NULL;
+ struct adf_buffer *bufs_copy = NULL;
+ void *custom_data_copy = NULL;
+ struct sync_fence *ret;
+ size_t i;
+
+ intfs_copy = kzalloc(sizeof(intfs_copy[0]) * n_intfs, GFP_KERNEL);
+ if (!intfs_copy)
+ return ERR_PTR(-ENOMEM);
+
+ bufs_copy = kzalloc(sizeof(bufs_copy[0]) * n_bufs, GFP_KERNEL);
+ if (!bufs_copy) {
+ ret = ERR_PTR(-ENOMEM);
+ goto err_alloc;
+ }
+
+ custom_data_copy = kzalloc(custom_data_size, GFP_KERNEL);
+ if (!custom_data_copy) {
+ ret = ERR_PTR(-ENOMEM);
+ goto err_alloc;
+ }
+
+ for (i = 0; i < n_bufs; i++) {
+ size_t j;
+ for (j = 0; j < bufs[i].n_planes; j++)
+ get_dma_buf(bufs[i].dma_bufs[j]);
+ }
+
+ memcpy(intfs_copy, intfs, sizeof(intfs_copy[0]) * n_intfs);
+ memcpy(bufs_copy, bufs, sizeof(bufs_copy[0]) * n_bufs);
+ memcpy(custom_data_copy, custom_data, custom_data_size);
+
+ ret = adf_device_post_nocopy(dev, intfs_copy, n_intfs, bufs_copy,
+ n_bufs, custom_data_copy, custom_data_size);
+ if (IS_ERR(ret))
+ goto err_post;
+
+ return ret;
+
+err_post:
+ for (i = 0; i < n_bufs; i++) {
+ size_t j;
+ for (j = 0; j < bufs[i].n_planes; j++)
+ dma_buf_put(bufs[i].dma_bufs[j]);
+ }
+err_alloc:
+ kfree(custom_data_copy);
+ kfree(bufs_copy);
+ kfree(intfs_copy);
+ return ret;
+}
+EXPORT_SYMBOL(adf_device_post);
+
+/**
+ * adf_device_post_nocopy - flip to a new set of buffers
+ *
+ * adf_device_post_nocopy() has the same behavior as adf_device_post(),
+ * except ADF does not copy @intfs, @bufs, or @custom_data, and it does
+ * not take an extra reference on the dma-bufs in @bufs.
+ *
+ * @intfs, @bufs, and @custom_data must point to buffers allocated by
+ * kmalloc(). On success, ADF takes ownership of these buffers and the dma-bufs
+ * in @bufs, and will kfree()/dma_buf_put() them when they are no longer needed.
+ * On failure, adf_device_post_nocopy() does NOT take ownership of these
+ * buffers or the dma-bufs, and the caller must clean them up.
+ *
+ * adf_device_post_nocopy() is mainly intended for implementing ADF's ioctls.
+ * Clients may find the nocopy variant useful in limited cases, but most should
+ * call adf_device_post() instead.
+ */
+struct sync_fence *adf_device_post_nocopy(struct adf_device *dev,
+ struct adf_interface **intfs, size_t n_intfs,
+ struct adf_buffer *bufs, size_t n_bufs,
+ void *custom_data, size_t custom_data_size)
+{
+ struct adf_pending_post *cfg;
+ struct adf_buffer_mapping *mappings;
+ struct sync_fence *ret;
+ size_t i;
+ int err;
+
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return ERR_PTR(-ENOMEM);
+
+ mappings = kzalloc(sizeof(mappings[0]) * n_bufs, GFP_KERNEL);
+ if (!mappings) {
+ ret = ERR_PTR(-ENOMEM);
+ goto err_alloc;
+ }
+
+ mutex_lock(&dev->client_lock);
+
+ for (i = 0; i < n_bufs; i++) {
+ err = adf_buffer_validate(&bufs[i]);
+ if (err < 0) {
+ ret = ERR_PTR(err);
+ goto err_buf;
+ }
+
+ err = adf_buffer_map(dev, &bufs[i], &mappings[i]);
+ if (err < 0) {
+ ret = ERR_PTR(err);
+ goto err_buf;
+ }
+ }
+
+ INIT_LIST_HEAD(&cfg->head);
+ cfg->config.n_bufs = n_bufs;
+ cfg->config.bufs = bufs;
+ cfg->config.mappings = mappings;
+ cfg->config.custom_data = custom_data;
+ cfg->config.custom_data_size = custom_data_size;
+
+ err = dev->ops->validate(dev, &cfg->config, &cfg->state);
+ if (err < 0) {
+ ret = ERR_PTR(err);
+ goto err_buf;
+ }
+
+ mutex_lock(&dev->post_lock);
+
+ if (dev->ops->complete_fence)
+ ret = dev->ops->complete_fence(dev, &cfg->config,
+ cfg->state);
+ else
+ ret = adf_sw_complete_fence(dev);
+
+ if (IS_ERR(ret))
+ goto err_fence;
+
+ list_add_tail(&cfg->head, &dev->post_list);
+ queue_kthread_work(&dev->post_worker, &dev->post_work);
+ mutex_unlock(&dev->post_lock);
+ mutex_unlock(&dev->client_lock);
+ kfree(intfs);
+ return ret;
+
+err_fence:
+ mutex_unlock(&dev->post_lock);
+
+err_buf:
+ for (i = 0; i < n_bufs; i++)
+ adf_buffer_mapping_cleanup(&mappings[i], &bufs[i]);
+
+ mutex_unlock(&dev->client_lock);
+ kfree(mappings);
+
+err_alloc:
+ kfree(cfg);
+ return ret;
+}
+EXPORT_SYMBOL(adf_device_post_nocopy);
+
+static void adf_attachment_list_to_array(struct adf_device *dev,
+ struct list_head *src, struct adf_attachment *dst, size_t size)
+{
+ struct adf_attachment_list *entry;
+ size_t i = 0;
+
+ if (!dst)
+ return;
+
+ list_for_each_entry(entry, src, head) {
+ if (i == size)
+ return;
+ dst[i] = entry->attachment;
+ i++;
+ }
+}
+
+/**
+ * adf_device_attachments - get device's list of active attachments
+ *
+ * @dev: the device
+ * @attachments: storage for the attachment list (optional)
+ * @n_attachments: length of @attachments
+ *
+ * If @attachments is not NULL, adf_device_attachments() will copy up to
+ * @n_attachments entries into @attachments.
+ *
+ * Returns the length of the active attachment list.
+ */
+size_t adf_device_attachments(struct adf_device *dev,
+ struct adf_attachment *attachments, size_t n_attachments)
+{
+ size_t retval;
+
+ mutex_lock(&dev->client_lock);
+ adf_attachment_list_to_array(dev, &dev->attached, attachments,
+ n_attachments);
+ retval = dev->n_attached;
+ mutex_unlock(&dev->client_lock);
+
+ return retval;
+}
+EXPORT_SYMBOL(adf_device_attachments);
+
+/**
+ * adf_device_attachments_allowed - get device's list of allowed attachments
+ *
+ * @dev: the device
+ * @attachments: storage for the attachment list (optional)
+ * @n_attachments: length of @attachments
+ *
+ * If @attachments is not NULL, adf_device_attachments_allowed() will copy up to
+ * @n_attachments entries into @attachments.
+ *
+ * Returns the length of the allowed attachment list.
+ */
+size_t adf_device_attachments_allowed(struct adf_device *dev,
+ struct adf_attachment *attachments, size_t n_attachments)
+{
+ size_t retval;
+
+ mutex_lock(&dev->client_lock);
+ adf_attachment_list_to_array(dev, &dev->attach_allowed, attachments,
+ n_attachments);
+ retval = dev->n_attach_allowed;
+ mutex_unlock(&dev->client_lock);
+
+ return retval;
+}
+EXPORT_SYMBOL(adf_device_attachments_allowed);
+
+/**
+ * adf_device_attached - return whether an overlay engine and interface are
+ * attached
+ *
+ * @dev: the parent device
+ * @eng: the overlay engine
+ * @intf: the interface
+ */
+bool adf_device_attached(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf)
+{
+ struct adf_attachment_list *attachment;
+
+ mutex_lock(&dev->client_lock);
+ attachment = adf_attachment_find(&dev->attached, eng, intf);
+ mutex_unlock(&dev->client_lock);
+
+ return attachment != NULL;
+}
+EXPORT_SYMBOL(adf_device_attached);
+
+/**
+ * adf_device_attach_allowed - return whether the ADF device supports attaching
+ * an overlay engine and interface
+ *
+ * @dev: the parent device
+ * @eng: the overlay engine
+ * @intf: the interface
+ */
+bool adf_device_attach_allowed(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ struct adf_attachment_list *attachment;
+
+ mutex_lock(&dev->client_lock);
+ attachment = adf_attachment_find(&dev->attach_allowed, eng, intf);
+ mutex_unlock(&dev->client_lock);
+
+ return attachment != NULL;
+}
+EXPORT_SYMBOL(adf_device_attach_allowed);
+/**
+ * adf_device_attach - attach an overlay engine to an interface
+ *
+ * @dev: the parent device
+ * @eng: the overlay engine
+ * @intf: the interface
+ *
+ * Returns 0 on success, -%EINVAL if attaching @intf and @eng is not allowed,
+ * -%EALREADY if @intf and @eng are already attached, or -errno on any other
+ * failure.
+ */
+int adf_device_attach(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf)
+{
+ int ret;
+ struct adf_attachment_list *attachment = NULL;
+
+ ret = adf_attachment_validate(dev, eng, intf);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&dev->client_lock);
+
+ if (dev->n_attached == ADF_MAX_ATTACHMENTS) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (!adf_attachment_find(&dev->attach_allowed, eng, intf)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (adf_attachment_find(&dev->attached, eng, intf)) {
+ ret = -EALREADY;
+ goto done;
+ }
+
+ ret = adf_device_attach_op(dev, eng, intf);
+ if (ret < 0)
+ goto done;
+
+ attachment = kzalloc(sizeof(*attachment), GFP_KERNEL);
+ if (!attachment) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ attachment->attachment.interface = intf;
+ attachment->attachment.overlay_engine = eng;
+ list_add_tail(&attachment->head, &dev->attached);
+ dev->n_attached++;
+
+done:
+ mutex_unlock(&dev->client_lock);
+ if (ret < 0)
+ kfree(attachment);
+
+ return ret;
+}
+EXPORT_SYMBOL(adf_device_attach);
+
+/**
+ * adf_device_detach - detach an overlay engine from an interface
+ *
+ * @dev: the parent device
+ * @eng: the overlay engine
+ * @intf: the interface
+ *
+ * Returns 0 on success, -%EINVAL if @intf and @eng are not attached,
+ * or -errno on any other failure.
+ */
+int adf_device_detach(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf)
+{
+ int ret;
+ struct adf_attachment_list *attachment;
+
+ ret = adf_attachment_validate(dev, eng, intf);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&dev->client_lock);
+
+ attachment = adf_attachment_find(&dev->attached, eng, intf);
+ if (!attachment) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = adf_device_detach_op(dev, eng, intf);
+ if (ret < 0)
+ goto done;
+
+ adf_attachment_free(attachment);
+ dev->n_attached--;
+done:
+ mutex_unlock(&dev->client_lock);
+ return ret;
+}
+EXPORT_SYMBOL(adf_device_detach);
+
+/**
+ * adf_interface_simple_buffer_alloc - allocate a simple buffer
+ *
+ * @intf: target interface
+ * @w: width in pixels
+ * @h: height in pixels
+ * @format: format fourcc
+ * @dma_buf: returns the allocated buffer
+ * @offset: returns the byte offset of the allocated buffer's first pixel
+ * @pitch: returns the allocated buffer's pitch
+ *
+ * See &struct adf_simple_buffer_alloc for a description of simple buffers and
+ * their limitations.
+ *
+ * Returns 0 on success or -errno on failure.
+ */
+int adf_interface_simple_buffer_alloc(struct adf_interface *intf, u16 w, u16 h,
+ u32 format, struct dma_buf **dma_buf, u32 *offset, u32 *pitch)
+{
+ if (!intf->ops || !intf->ops->alloc_simple_buffer)
+ return -EOPNOTSUPP;
+
+ if (!adf_format_is_rgb(format))
+ return -EINVAL;
+
+ return intf->ops->alloc_simple_buffer(intf, w, h, format, dma_buf,
+ offset, pitch);
+}
+EXPORT_SYMBOL(adf_interface_simple_buffer_alloc);
+
+/**
+ * adf_interface_simple_post - flip to a single buffer
+ *
+ * @intf: interface targeted by the flip
+ * @buf: buffer to display
+ *
+ * adf_interface_simple_post() can be used generically for simple display
+ * configurations, since the client does not need to provide any driver-private
+ * configuration data.
+ *
+ * adf_interface_simple_post() has the same copying semantics as
+ * adf_device_post().
+ *
+ * On success, returns a sync fence which signals when the buffer is removed
+ * from the screen. On failure, returns ERR_PTR(-errno).
+ */
+struct sync_fence *adf_interface_simple_post(struct adf_interface *intf,
+ struct adf_buffer *buf)
+{
+ size_t custom_data_size = 0;
+ void *custom_data = NULL;
+ struct sync_fence *ret;
+
+ if (intf->ops && intf->ops->describe_simple_post) {
+ int err;
+
+ custom_data = kzalloc(ADF_MAX_CUSTOM_DATA_SIZE, GFP_KERNEL);
+ if (!custom_data) {
+ ret = ERR_PTR(-ENOMEM);
+ goto done;
+ }
+
+ err = intf->ops->describe_simple_post(intf, buf, custom_data,
+ &custom_data_size);
+ if (err < 0) {
+ ret = ERR_PTR(err);
+ goto done;
+ }
+ }
+
+ ret = adf_device_post(adf_interface_parent(intf), &intf, 1, buf, 1,
+ custom_data, custom_data_size);
+done:
+ kfree(custom_data);
+ return ret;
+}
+EXPORT_SYMBOL(adf_interface_simple_post);
diff --git a/drivers/video/adf/adf_fbdev.c b/drivers/video/adf/adf_fbdev.c
new file mode 100644
index 000000000000..477abd63ccc2
--- /dev/null
+++ b/drivers/video/adf/adf_fbdev.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/vmalloc.h>
+
+#include <video/adf.h>
+#include <video/adf_client.h>
+#include <video/adf_fbdev.h>
+#include <video/adf_format.h>
+
+#include "adf.h"
+
+struct adf_fbdev_format {
+ u32 fourcc;
+ u32 bpp;
+ u32 r_length;
+ u32 g_length;
+ u32 b_length;
+ u32 a_length;
+ u32 r_offset;
+ u32 g_offset;
+ u32 b_offset;
+ u32 a_offset;
+};
+
+static const struct adf_fbdev_format format_table[] = {
+ {DRM_FORMAT_RGB332, 8, 3, 3, 2, 0, 5, 2, 0, 0},
+ {DRM_FORMAT_BGR233, 8, 3, 3, 2, 0, 0, 3, 5, 0},
+
+ {DRM_FORMAT_XRGB4444, 16, 4, 4, 4, 0, 8, 4, 0, 0},
+ {DRM_FORMAT_XBGR4444, 16, 4, 4, 4, 0, 0, 4, 8, 0},
+ {DRM_FORMAT_RGBX4444, 16, 4, 4, 4, 0, 12, 8, 4, 0},
+ {DRM_FORMAT_BGRX4444, 16, 4, 4, 4, 0, 0, 4, 8, 0},
+
+ {DRM_FORMAT_ARGB4444, 16, 4, 4, 4, 4, 8, 4, 0, 12},
+ {DRM_FORMAT_ABGR4444, 16, 4, 4, 4, 4, 0, 4, 8, 12},
+ {DRM_FORMAT_RGBA4444, 16, 4, 4, 4, 4, 12, 8, 4, 0},
+ {DRM_FORMAT_BGRA4444, 16, 4, 4, 4, 4, 0, 4, 8, 0},
+
+ {DRM_FORMAT_XRGB1555, 16, 5, 5, 5, 0, 10, 5, 0, 0},
+ {DRM_FORMAT_XBGR1555, 16, 5, 5, 5, 0, 0, 5, 10, 0},
+ {DRM_FORMAT_RGBX5551, 16, 5, 5, 5, 0, 11, 6, 1, 0},
+ {DRM_FORMAT_BGRX5551, 16, 5, 5, 5, 0, 1, 6, 11, 0},
+
+ {DRM_FORMAT_ARGB1555, 16, 5, 5, 5, 1, 10, 5, 0, 15},
+ {DRM_FORMAT_ABGR1555, 16, 5, 5, 5, 1, 0, 5, 10, 15},
+ {DRM_FORMAT_RGBA5551, 16, 5, 5, 5, 1, 11, 6, 1, 0},
+ {DRM_FORMAT_BGRA5551, 16, 5, 5, 5, 1, 1, 6, 11, 0},
+
+ {DRM_FORMAT_RGB565, 16, 5, 6, 5, 0, 11, 5, 0, 0},
+ {DRM_FORMAT_BGR565, 16, 5, 6, 5, 0, 0, 5, 11, 0},
+
+ {DRM_FORMAT_RGB888, 24, 8, 8, 8, 0, 16, 8, 0, 0},
+ {DRM_FORMAT_BGR888, 24, 8, 8, 8, 0, 0, 8, 16, 0},
+
+ {DRM_FORMAT_XRGB8888, 32, 8, 8, 8, 0, 16, 8, 0, 0},
+ {DRM_FORMAT_XBGR8888, 32, 8, 8, 8, 0, 0, 8, 16, 0},
+ {DRM_FORMAT_RGBX8888, 32, 8, 8, 8, 0, 24, 16, 8, 0},
+ {DRM_FORMAT_BGRX8888, 32, 8, 8, 8, 0, 8, 16, 24, 0},
+
+ {DRM_FORMAT_ARGB8888, 32, 8, 8, 8, 8, 16, 8, 0, 24},
+ {DRM_FORMAT_ABGR8888, 32, 8, 8, 8, 8, 0, 8, 16, 24},
+ {DRM_FORMAT_RGBA8888, 32, 8, 8, 8, 8, 24, 16, 8, 0},
+ {DRM_FORMAT_BGRA8888, 32, 8, 8, 8, 8, 8, 16, 24, 0},
+
+ {DRM_FORMAT_XRGB2101010, 32, 10, 10, 10, 0, 20, 10, 0, 0},
+ {DRM_FORMAT_XBGR2101010, 32, 10, 10, 10, 0, 0, 10, 20, 0},
+ {DRM_FORMAT_RGBX1010102, 32, 10, 10, 10, 0, 22, 12, 2, 0},
+ {DRM_FORMAT_BGRX1010102, 32, 10, 10, 10, 0, 2, 12, 22, 0},
+
+ {DRM_FORMAT_ARGB2101010, 32, 10, 10, 10, 2, 20, 10, 0, 30},
+ {DRM_FORMAT_ABGR2101010, 32, 10, 10, 10, 2, 0, 10, 20, 30},
+ {DRM_FORMAT_RGBA1010102, 32, 10, 10, 10, 2, 22, 12, 2, 0},
+ {DRM_FORMAT_BGRA1010102, 32, 10, 10, 10, 2, 2, 12, 22, 0},
+};
+
+static u32 drm_fourcc_from_fb_var(struct fb_var_screeninfo *var)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(format_table); i++) {
+ const struct adf_fbdev_format *f = &format_table[i];
+ if (var->red.length == f->r_length &&
+ var->red.offset == f->r_offset &&
+ var->green.length == f->g_length &&
+ var->green.offset == f->g_offset &&
+ var->blue.length == f->b_length &&
+ var->blue.offset == f->b_offset &&
+ var->transp.length == f->a_length &&
+ (var->transp.length == 0 ||
+ var->transp.offset == f->a_offset))
+ return f->fourcc;
+ }
+
+ return 0;
+}
+
+static const struct adf_fbdev_format *fbdev_format_info(u32 format)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(format_table); i++) {
+ const struct adf_fbdev_format *f = &format_table[i];
+ if (f->fourcc == format)
+ return f;
+ }
+
+ BUG();
+}
+
+void adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo *mode,
+ struct fb_videomode *vmode)
+{
+ memset(vmode, 0, sizeof(*vmode));
+
+ vmode->refresh = mode->vrefresh;
+
+ vmode->xres = mode->hdisplay;
+ vmode->yres = mode->vdisplay;
+
+ vmode->pixclock = mode->clock ? KHZ2PICOS(mode->clock) : 0;
+ vmode->left_margin = mode->htotal - mode->hsync_end;
+ vmode->right_margin = mode->hsync_start - mode->hdisplay;
+ vmode->upper_margin = mode->vtotal - mode->vsync_end;
+ vmode->lower_margin = mode->vsync_start - mode->vdisplay;
+ vmode->hsync_len = mode->hsync_end - mode->hsync_start;
+ vmode->vsync_len = mode->vsync_end - mode->vsync_start;
+
+ vmode->sync = 0;
+ if (mode->flags | DRM_MODE_FLAG_PHSYNC)
+ vmode->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (mode->flags | DRM_MODE_FLAG_PVSYNC)
+ vmode->sync |= FB_SYNC_VERT_HIGH_ACT;
+ if (mode->flags | DRM_MODE_FLAG_PCSYNC)
+ vmode->sync |= FB_SYNC_COMP_HIGH_ACT;
+ if (mode->flags | DRM_MODE_FLAG_BCAST)
+ vmode->sync |= FB_SYNC_BROADCAST;
+
+ vmode->vmode = 0;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ vmode->vmode |= FB_VMODE_INTERLACED;
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ vmode->vmode |= FB_VMODE_DOUBLE;
+}
+EXPORT_SYMBOL(adf_modeinfo_to_fb_videomode);
+
+void adf_modeinfo_from_fb_videomode(const struct fb_videomode *vmode,
+ struct drm_mode_modeinfo *mode)
+{
+ memset(mode, 0, sizeof(*mode));
+
+ mode->hdisplay = vmode->xres;
+ mode->hsync_start = mode->hdisplay + vmode->right_margin;
+ mode->hsync_end = mode->hsync_start + vmode->hsync_len;
+ mode->htotal = mode->hsync_end + vmode->left_margin;
+
+ mode->vdisplay = vmode->yres;
+ mode->vsync_start = mode->vdisplay + vmode->lower_margin;
+ mode->vsync_end = mode->vsync_start + vmode->vsync_len;
+ mode->vtotal = mode->vsync_end + vmode->upper_margin;
+
+ mode->clock = vmode->pixclock ? PICOS2KHZ(vmode->pixclock) : 0;
+
+ mode->flags = 0;
+ if (vmode->sync & FB_SYNC_HOR_HIGH_ACT)
+ mode->flags |= DRM_MODE_FLAG_PHSYNC;
+ if (vmode->sync & FB_SYNC_VERT_HIGH_ACT)
+ mode->flags |= DRM_MODE_FLAG_PVSYNC;
+ if (vmode->sync & FB_SYNC_COMP_HIGH_ACT)
+ mode->flags |= DRM_MODE_FLAG_PCSYNC;
+ if (vmode->sync & FB_SYNC_BROADCAST)
+ mode->flags |= DRM_MODE_FLAG_BCAST;
+ if (vmode->vmode & FB_VMODE_INTERLACED)
+ mode->flags |= DRM_MODE_FLAG_INTERLACE;
+ if (vmode->vmode & FB_VMODE_DOUBLE)
+ mode->flags |= DRM_MODE_FLAG_DBLSCAN;
+
+ if (vmode->refresh)
+ mode->vrefresh = vmode->refresh;
+ else
+ adf_modeinfo_set_vrefresh(mode);
+
+ if (vmode->name)
+ strlcpy(mode->name, vmode->name, sizeof(mode->name));
+ else
+ adf_modeinfo_set_name(mode);
+}
+EXPORT_SYMBOL(adf_modeinfo_from_fb_videomode);
+
+static int adf_fbdev_post(struct adf_fbdev *fbdev)
+{
+ struct adf_buffer buf;
+ struct sync_fence *complete_fence;
+ int ret = 0;
+
+ memset(&buf, 0, sizeof(buf));
+ buf.overlay_engine = fbdev->eng;
+ buf.w = fbdev->info->var.xres;
+ buf.h = fbdev->info->var.yres;
+ buf.format = fbdev->format;
+ buf.dma_bufs[0] = fbdev->dma_buf;
+ buf.offset[0] = fbdev->offset +
+ fbdev->info->var.yoffset * fbdev->pitch +
+ fbdev->info->var.xoffset *
+ (fbdev->info->var.bits_per_pixel / 8);
+ buf.pitch[0] = fbdev->pitch;
+ buf.n_planes = 1;
+
+ complete_fence = adf_interface_simple_post(fbdev->intf, &buf);
+ if (IS_ERR(complete_fence)) {
+ ret = PTR_ERR(complete_fence);
+ goto done;
+ }
+
+ sync_fence_put(complete_fence);
+done:
+ return ret;
+}
+
+static const u16 vga_palette[][3] = {
+ {0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0xAAAA},
+ {0x0000, 0xAAAA, 0x0000},
+ {0x0000, 0xAAAA, 0xAAAA},
+ {0xAAAA, 0x0000, 0x0000},
+ {0xAAAA, 0x0000, 0xAAAA},
+ {0xAAAA, 0x5555, 0x0000},
+ {0xAAAA, 0xAAAA, 0xAAAA},
+ {0x5555, 0x5555, 0x5555},
+ {0x5555, 0x5555, 0xFFFF},
+ {0x5555, 0xFFFF, 0x5555},
+ {0x5555, 0xFFFF, 0xFFFF},
+ {0xFFFF, 0x5555, 0x5555},
+ {0xFFFF, 0x5555, 0xFFFF},
+ {0xFFFF, 0xFFFF, 0x5555},
+ {0xFFFF, 0xFFFF, 0xFFFF},
+};
+
+static int adf_fb_alloc(struct adf_fbdev *fbdev)
+{
+ int ret;
+
+ ret = adf_interface_simple_buffer_alloc(fbdev->intf,
+ fbdev->default_xres_virtual,
+ fbdev->default_yres_virtual,
+ fbdev->default_format,
+ &fbdev->dma_buf, &fbdev->offset, &fbdev->pitch);
+ if (ret < 0) {
+ dev_err(fbdev->info->dev, "allocating fb failed: %d\n", ret);
+ return ret;
+ }
+
+ fbdev->vaddr = dma_buf_vmap(fbdev->dma_buf);
+ if (!fbdev->vaddr) {
+ ret = -ENOMEM;
+ dev_err(fbdev->info->dev, "vmapping fb failed\n");
+ goto err_vmap;
+ }
+ fbdev->info->fix.line_length = fbdev->pitch;
+ fbdev->info->var.xres_virtual = fbdev->default_xres_virtual;
+ fbdev->info->var.yres_virtual = fbdev->default_yres_virtual;
+ fbdev->info->fix.smem_len = fbdev->dma_buf->size;
+ fbdev->info->screen_base = fbdev->vaddr;
+
+ return 0;
+
+err_vmap:
+ dma_buf_put(fbdev->dma_buf);
+ return ret;
+}
+
+static void adf_fb_destroy(struct adf_fbdev *fbdev)
+{
+ dma_buf_vunmap(fbdev->dma_buf, fbdev->vaddr);
+ dma_buf_put(fbdev->dma_buf);
+}
+
+static void adf_fbdev_set_format(struct adf_fbdev *fbdev, u32 format)
+{
+ size_t i;
+ const struct adf_fbdev_format *info = fbdev_format_info(format);
+ for (i = 0; i < ARRAY_SIZE(vga_palette); i++) {
+ u16 r = vga_palette[i][0];
+ u16 g = vga_palette[i][1];
+ u16 b = vga_palette[i][2];
+
+ r >>= (16 - info->r_length);
+ g >>= (16 - info->g_length);
+ b >>= (16 - info->b_length);
+
+ fbdev->pseudo_palette[i] =
+ (r << info->r_offset) |
+ (g << info->g_offset) |
+ (b << info->b_offset);
+
+ if (info->a_length) {
+ u16 a = BIT(info->a_length) - 1;
+ fbdev->pseudo_palette[i] |= (a << info->a_offset);
+ }
+ }
+
+ fbdev->info->var.bits_per_pixel = adf_format_bpp(format);
+ fbdev->info->var.red.length = info->r_length;
+ fbdev->info->var.red.offset = info->r_offset;
+ fbdev->info->var.green.length = info->g_length;
+ fbdev->info->var.green.offset = info->g_offset;
+ fbdev->info->var.blue.length = info->b_length;
+ fbdev->info->var.blue.offset = info->b_offset;
+ fbdev->info->var.transp.length = info->a_length;
+ fbdev->info->var.transp.offset = info->a_offset;
+ fbdev->format = format;
+}
+
+static void adf_fbdev_fill_modelist(struct adf_fbdev *fbdev)
+{
+ struct drm_mode_modeinfo *modelist;
+ struct fb_videomode fbmode;
+ size_t n_modes, i;
+ int ret = 0;
+
+ n_modes = adf_interface_modelist(fbdev->intf, NULL, 0);
+ modelist = kzalloc(sizeof(modelist[0]) * n_modes, GFP_KERNEL);
+ if (!modelist) {
+ dev_warn(fbdev->info->dev, "allocating new modelist failed; keeping old modelist\n");
+ return;
+ }
+ adf_interface_modelist(fbdev->intf, modelist, n_modes);
+
+ fb_destroy_modelist(&fbdev->info->modelist);
+
+ for (i = 0; i < n_modes; i++) {
+ adf_modeinfo_to_fb_videomode(&modelist[i], &fbmode);
+ ret = fb_add_videomode(&fbmode, &fbdev->info->modelist);
+ if (ret < 0)
+ dev_warn(fbdev->info->dev, "adding mode %s to modelist failed: %d\n",
+ modelist[i].name, ret);
+ }
+
+ kfree(modelist);
+}
+
+/**
+ * adf_fbdev_open - default implementation of fbdev open op
+ */
+int adf_fbdev_open(struct fb_info *info, int user)
+{
+ struct adf_fbdev *fbdev = info->par;
+ int ret;
+
+ if (!fbdev->open) {
+ struct drm_mode_modeinfo mode;
+ struct fb_videomode fbmode;
+ struct adf_device *dev = adf_interface_parent(fbdev->intf);
+
+ ret = adf_device_attach(dev, fbdev->eng, fbdev->intf);
+ if (ret < 0 && ret != -EALREADY)
+ return ret;
+
+ ret = adf_fb_alloc(fbdev);
+ if (ret < 0)
+ return ret;
+
+ adf_interface_current_mode(fbdev->intf, &mode);
+ adf_modeinfo_to_fb_videomode(&mode, &fbmode);
+ fb_videomode_to_var(&fbdev->info->var, &fbmode);
+
+ adf_fbdev_set_format(fbdev, fbdev->default_format);
+ adf_fbdev_fill_modelist(fbdev);
+ }
+
+ ret = adf_fbdev_post(fbdev);
+ if (ret < 0) {
+ if (!fbdev->open)
+ adf_fb_destroy(fbdev);
+ return ret;
+ }
+
+ fbdev->open = true;
+ return 0;
+}
+EXPORT_SYMBOL(adf_fbdev_open);
+
+/**
+ * adf_fbdev_release - default implementation of fbdev release op
+ */
+int adf_fbdev_release(struct fb_info *info, int user)
+{
+ struct adf_fbdev *fbdev = info->par;
+ adf_fb_destroy(fbdev);
+ fbdev->open = false;
+ return 0;
+}
+EXPORT_SYMBOL(adf_fbdev_release);
+
+/**
+ * adf_fbdev_check_var - default implementation of fbdev check_var op
+ */
+int adf_fbdev_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct adf_fbdev *fbdev = info->par;
+ bool valid_format = true;
+ u32 format = drm_fourcc_from_fb_var(var);
+ u32 pitch = var->xres_virtual * var->bits_per_pixel / 8;
+
+ if (!format) {
+ dev_dbg(info->dev, "%s: unrecognized format\n", __func__);
+ valid_format = false;
+ }
+
+ if (valid_format && var->grayscale) {
+ dev_dbg(info->dev, "%s: grayscale modes not supported\n",
+ __func__);
+ valid_format = false;
+ }
+
+ if (valid_format && var->nonstd) {
+ dev_dbg(info->dev, "%s: nonstandard formats not supported\n",
+ __func__);
+ valid_format = false;
+ }
+
+ if (valid_format && !adf_overlay_engine_supports_format(fbdev->eng,
+ format)) {
+ char format_str[ADF_FORMAT_STR_SIZE];
+ adf_format_str(format, format_str);
+ dev_dbg(info->dev, "%s: format %s not supported by overlay engine %s\n",
+ __func__, format_str, fbdev->eng->base.name);
+ valid_format = false;
+ }
+
+ if (valid_format && pitch > fbdev->pitch) {
+ dev_dbg(info->dev, "%s: fb pitch too small for var (pitch = %u, xres_virtual = %u, bits_per_pixel = %u)\n",
+ __func__, fbdev->pitch, var->xres_virtual,
+ var->bits_per_pixel);
+ valid_format = false;
+ }
+
+ if (valid_format && var->yres_virtual > fbdev->default_yres_virtual) {
+ dev_dbg(info->dev, "%s: fb height too small for var (h = %u, yres_virtual = %u)\n",
+ __func__, fbdev->default_yres_virtual,
+ var->yres_virtual);
+ valid_format = false;
+ }
+
+ if (valid_format) {
+ var->activate = info->var.activate;
+ var->height = info->var.height;
+ var->width = info->var.width;
+ var->accel_flags = info->var.accel_flags;
+ var->rotate = info->var.rotate;
+ var->colorspace = info->var.colorspace;
+ /* userspace can't change these */
+ } else {
+ /* if any part of the format is invalid then fixing it up is
+ impractical, so save just the modesetting bits and
+ overwrite everything else */
+ struct fb_videomode mode;
+ fb_var_to_videomode(&mode, var);
+ memcpy(var, &info->var, sizeof(*var));
+ fb_videomode_to_var(var, &mode);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(adf_fbdev_check_var);
+
+/**
+ * adf_fbdev_set_par - default implementation of fbdev set_par op
+ */
+int adf_fbdev_set_par(struct fb_info *info)
+{
+ struct adf_fbdev *fbdev = info->par;
+ struct adf_interface *intf = fbdev->intf;
+ struct fb_videomode vmode;
+ struct drm_mode_modeinfo mode;
+ int ret;
+ u32 format = drm_fourcc_from_fb_var(&info->var);
+
+ fb_var_to_videomode(&vmode, &info->var);
+ adf_modeinfo_from_fb_videomode(&vmode, &mode);
+ ret = adf_interface_set_mode(intf, &mode);
+ if (ret < 0)
+ return ret;
+
+ ret = adf_fbdev_post(fbdev);
+ if (ret < 0)
+ return ret;
+
+ if (format != fbdev->format)
+ adf_fbdev_set_format(fbdev, format);
+
+ return 0;
+}
+EXPORT_SYMBOL(adf_fbdev_set_par);
+
+/**
+ * adf_fbdev_blank - default implementation of fbdev blank op
+ */
+int adf_fbdev_blank(int blank, struct fb_info *info)
+{
+ struct adf_fbdev *fbdev = info->par;
+ struct adf_interface *intf = fbdev->intf;
+ u8 dpms_state;
+
+ switch (blank) {
+ case FB_BLANK_UNBLANK:
+ dpms_state = DRM_MODE_DPMS_ON;
+ break;
+ case FB_BLANK_NORMAL:
+ dpms_state = DRM_MODE_DPMS_STANDBY;
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ dpms_state = DRM_MODE_DPMS_STANDBY;
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ dpms_state = DRM_MODE_DPMS_SUSPEND;
+ break;
+ case FB_BLANK_POWERDOWN:
+ dpms_state = DRM_MODE_DPMS_OFF;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return adf_interface_blank(intf, dpms_state);
+}
+EXPORT_SYMBOL(adf_fbdev_blank);
+
+/**
+ * adf_fbdev_pan_display - default implementation of fbdev pan_display op
+ */
+int adf_fbdev_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct adf_fbdev *fbdev = info->par;
+ return adf_fbdev_post(fbdev);
+}
+EXPORT_SYMBOL(adf_fbdev_pan_display);
+
+/**
+ * adf_fbdev_mmap - default implementation of fbdev mmap op
+ */
+int adf_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct adf_fbdev *fbdev = info->par;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ return dma_buf_mmap(fbdev->dma_buf, vma, 0);
+}
+EXPORT_SYMBOL(adf_fbdev_mmap);
+
+/**
+ * adf_fbdev_init - initialize helper to wrap ADF device in fbdev API
+ *
+ * @fbdev: the fbdev helper
+ * @interface: the ADF interface that will display the framebuffer
+ * @eng: the ADF overlay engine that will scan out the framebuffer
+ * @xres_virtual: the virtual width of the framebuffer
+ * @yres_virtual: the virtual height of the framebuffer
+ * @format: the format of the framebuffer
+ * @fbops: the device's fbdev ops
+ * @fmt: formatting for the framebuffer identification string
+ * @...: variable arguments
+ *
+ * @format must be a standard, non-indexed RGB format, i.e.,
+ * adf_format_is_rgb(@format) && @format != @DRM_FORMAT_C8.
+ *
+ * Returns 0 on success or -errno on failure.
+ */
+int adf_fbdev_init(struct adf_fbdev *fbdev, struct adf_interface *interface,
+ struct adf_overlay_engine *eng,
+ u16 xres_virtual, u16 yres_virtual, u32 format,
+ struct fb_ops *fbops, const char *fmt, ...)
+{
+ struct adf_device *parent = adf_interface_parent(interface);
+ struct device *dev = &parent->base.dev;
+ u16 width_mm, height_mm;
+ va_list args;
+ int ret;
+
+ if (!adf_format_is_rgb(format) ||
+ format == DRM_FORMAT_C8) {
+ dev_err(dev, "fbdev helper does not support format %u\n",
+ format);
+ return -EINVAL;
+ }
+
+ memset(fbdev, 0, sizeof(*fbdev));
+ fbdev->intf = interface;
+ fbdev->eng = eng;
+ fbdev->info = framebuffer_alloc(0, dev);
+ if (!fbdev->info) {
+ dev_err(dev, "allocating framebuffer device failed\n");
+ return -ENOMEM;
+ }
+ fbdev->default_xres_virtual = xres_virtual;
+ fbdev->default_yres_virtual = yres_virtual;
+ fbdev->default_format = format;
+
+ fbdev->info->flags = FBINFO_FLAG_DEFAULT;
+ ret = adf_interface_get_screen_size(interface, &width_mm, &height_mm);
+ if (ret < 0) {
+ width_mm = 0;
+ height_mm = 0;
+ }
+ fbdev->info->var.width = width_mm;
+ fbdev->info->var.height = height_mm;
+ fbdev->info->var.activate = FB_ACTIVATE_VBL;
+ va_start(args, fmt);
+ vsnprintf(fbdev->info->fix.id, sizeof(fbdev->info->fix.id), fmt, args);
+ va_end(args);
+ fbdev->info->fix.type = FB_TYPE_PACKED_PIXELS;
+ fbdev->info->fix.visual = FB_VISUAL_TRUECOLOR;
+ fbdev->info->fix.xpanstep = 1;
+ fbdev->info->fix.ypanstep = 1;
+ INIT_LIST_HEAD(&fbdev->info->modelist);
+ fbdev->info->fbops = fbops;
+ fbdev->info->pseudo_palette = fbdev->pseudo_palette;
+ fbdev->info->par = fbdev;
+
+ ret = register_framebuffer(fbdev->info);
+ if (ret < 0) {
+ dev_err(dev, "registering framebuffer failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(adf_fbdev_init);
+
+/**
+ * adf_fbdev_destroy - destroy helper to wrap ADF device in fbdev API
+ *
+ * @fbdev: the fbdev helper
+ */
+void adf_fbdev_destroy(struct adf_fbdev *fbdev)
+{
+ unregister_framebuffer(fbdev->info);
+ if (WARN_ON(fbdev->open))
+ adf_fb_destroy(fbdev);
+ framebuffer_release(fbdev->info);
+}
+EXPORT_SYMBOL(adf_fbdev_destroy);
diff --git a/drivers/video/adf/adf_fops.c b/drivers/video/adf/adf_fops.c
new file mode 100644
index 000000000000..abec58ea2ed8
--- /dev/null
+++ b/drivers/video/adf/adf_fops.c
@@ -0,0 +1,957 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/circ_buf.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <video/adf_client.h>
+#include <video/adf_format.h>
+
+#include "sw_sync.h"
+#include "sync.h"
+
+#include "adf.h"
+#include "adf_fops.h"
+#include "adf_sysfs.h"
+
+#ifdef CONFIG_COMPAT
+#include "adf_fops32.h"
+#endif
+
+static int adf_obj_set_event(struct adf_obj *obj, struct adf_file *file,
+ struct adf_set_event __user *arg)
+{
+ struct adf_set_event data;
+ bool enabled;
+ unsigned long flags;
+ int err;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ err = adf_obj_check_supports_event(obj, data.type);
+ if (err < 0)
+ return err;
+
+ spin_lock_irqsave(&obj->file_lock, flags);
+ if (data.enabled)
+ enabled = test_and_set_bit(data.type,
+ file->event_subscriptions);
+ else
+ enabled = test_and_clear_bit(data.type,
+ file->event_subscriptions);
+ spin_unlock_irqrestore(&obj->file_lock, flags);
+
+ if (data.enabled == enabled)
+ return -EALREADY;
+
+ if (data.enabled)
+ adf_event_get(obj, data.type);
+ else
+ adf_event_put(obj, data.type);
+
+ return 0;
+}
+
+static int adf_obj_copy_custom_data_to_user(struct adf_obj *obj,
+ void __user *dst, size_t *dst_size)
+{
+ void *custom_data;
+ size_t custom_data_size;
+ int ret;
+
+ if (!obj->ops || !obj->ops->custom_data) {
+ dev_dbg(&obj->dev, "%s: no custom_data op\n", __func__);
+ return 0;
+ }
+
+ custom_data = kzalloc(ADF_MAX_CUSTOM_DATA_SIZE, GFP_KERNEL);
+ if (!custom_data)
+ return -ENOMEM;
+
+ ret = obj->ops->custom_data(obj, custom_data, &custom_data_size);
+ if (ret < 0)
+ goto done;
+
+ if (copy_to_user(dst, custom_data, min(*dst_size, custom_data_size))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ *dst_size = custom_data_size;
+
+done:
+ kfree(custom_data);
+ return ret;
+}
+
+static int adf_eng_get_data(struct adf_overlay_engine *eng,
+ struct adf_overlay_engine_data __user *arg)
+{
+ struct adf_device *dev = adf_overlay_engine_parent(eng);
+ struct adf_overlay_engine_data data;
+ size_t n_supported_formats;
+ u32 *supported_formats = NULL;
+ int ret = 0;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ strlcpy(data.name, eng->base.name, sizeof(data.name));
+
+ if (data.n_supported_formats > ADF_MAX_SUPPORTED_FORMATS)
+ return -EINVAL;
+
+ n_supported_formats = data.n_supported_formats;
+ data.n_supported_formats = eng->ops->n_supported_formats;
+
+ if (n_supported_formats) {
+ supported_formats = kzalloc(n_supported_formats *
+ sizeof(supported_formats[0]), GFP_KERNEL);
+ if (!supported_formats)
+ return -ENOMEM;
+ }
+
+ memcpy(supported_formats, eng->ops->supported_formats,
+ sizeof(u32) * min(n_supported_formats,
+ eng->ops->n_supported_formats));
+
+ mutex_lock(&dev->client_lock);
+ ret = adf_obj_copy_custom_data_to_user(&eng->base, arg->custom_data,
+ &data.custom_data_size);
+ mutex_unlock(&dev->client_lock);
+
+ if (ret < 0)
+ goto done;
+
+ if (copy_to_user(arg, &data, sizeof(data))) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (supported_formats && copy_to_user(arg->supported_formats,
+ supported_formats,
+ n_supported_formats * sizeof(supported_formats[0])))
+ ret = -EFAULT;
+
+done:
+ kfree(supported_formats);
+ return ret;
+}
+
+static int adf_buffer_import(struct adf_device *dev,
+ struct adf_buffer_config __user *cfg, struct adf_buffer *buf)
+{
+ struct adf_buffer_config user_buf;
+ size_t i;
+ int ret = 0;
+
+ if (copy_from_user(&user_buf, cfg, sizeof(user_buf)))
+ return -EFAULT;
+
+ memset(buf, 0, sizeof(*buf));
+
+ if (user_buf.n_planes > ADF_MAX_PLANES) {
+ dev_err(&dev->base.dev, "invalid plane count %u\n",
+ user_buf.n_planes);
+ return -EINVAL;
+ }
+
+ buf->overlay_engine = idr_find(&dev->overlay_engines,
+ user_buf.overlay_engine);
+ if (!buf->overlay_engine) {
+ dev_err(&dev->base.dev, "invalid overlay engine id %u\n",
+ user_buf.overlay_engine);
+ return -ENOENT;
+ }
+
+ buf->w = user_buf.w;
+ buf->h = user_buf.h;
+ buf->format = user_buf.format;
+ for (i = 0; i < user_buf.n_planes; i++) {
+ buf->dma_bufs[i] = dma_buf_get(user_buf.fd[i]);
+ if (IS_ERR(buf->dma_bufs[i])) {
+ ret = PTR_ERR(buf->dma_bufs[i]);
+ dev_err(&dev->base.dev, "importing dma_buf fd %llu failed: %d\n",
+ user_buf.fd[i], ret);
+ buf->dma_bufs[i] = NULL;
+ goto done;
+ }
+ buf->offset[i] = user_buf.offset[i];
+ buf->pitch[i] = user_buf.pitch[i];
+ }
+ buf->n_planes = user_buf.n_planes;
+
+ if (user_buf.acquire_fence >= 0) {
+ buf->acquire_fence = sync_fence_fdget(user_buf.acquire_fence);
+ if (!buf->acquire_fence) {
+ dev_err(&dev->base.dev, "getting fence fd %lld failed\n",
+ user_buf.acquire_fence);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ if (ret < 0)
+ adf_buffer_cleanup(buf);
+ return ret;
+}
+
+static int adf_device_post_config(struct adf_device *dev,
+ struct adf_post_config __user *arg)
+{
+ struct sync_fence *complete_fence;
+ int complete_fence_fd;
+ struct adf_buffer *bufs = NULL;
+ struct adf_interface **intfs = NULL;
+ size_t n_intfs, n_bufs, i;
+ void *custom_data = NULL;
+ size_t custom_data_size;
+ int ret = 0;
+
+ complete_fence_fd = get_unused_fd();
+ if (complete_fence_fd < 0)
+ return complete_fence_fd;
+
+ if (get_user(n_intfs, &arg->n_interfaces)) {
+ ret = -EFAULT;
+ goto err_get_user;
+ }
+
+ if (n_intfs > ADF_MAX_INTERFACES) {
+ ret = -EINVAL;
+ goto err_get_user;
+ }
+
+ if (get_user(n_bufs, &arg->n_bufs)) {
+ ret = -EFAULT;
+ goto err_get_user;
+ }
+
+ if (n_bufs > ADF_MAX_BUFFERS) {
+ ret = -EINVAL;
+ goto err_get_user;
+ }
+
+ if (get_user(custom_data_size, &arg->custom_data_size)) {
+ ret = -EFAULT;
+ goto err_get_user;
+ }
+
+ if (custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) {
+ ret = -EINVAL;
+ goto err_get_user;
+ }
+
+ if (n_intfs) {
+ intfs = kmalloc(sizeof(intfs[0]) * n_intfs, GFP_KERNEL);
+ if (!intfs) {
+ ret = -ENOMEM;
+ goto err_get_user;
+ }
+ }
+
+ for (i = 0; i < n_intfs; i++) {
+ u32 intf_id;
+ if (get_user(intf_id, &arg->interfaces[i])) {
+ ret = -EFAULT;
+ goto err_get_user;
+ }
+
+ intfs[i] = idr_find(&dev->interfaces, intf_id);
+ if (!intfs[i]) {
+ ret = -EINVAL;
+ goto err_get_user;
+ }
+ }
+
+ if (n_bufs) {
+ bufs = kzalloc(sizeof(bufs[0]) * n_bufs, GFP_KERNEL);
+ if (!bufs) {
+ ret = -ENOMEM;
+ goto err_get_user;
+ }
+ }
+
+ for (i = 0; i < n_bufs; i++) {
+ ret = adf_buffer_import(dev, &arg->bufs[i], &bufs[i]);
+ if (ret < 0) {
+ memset(&bufs[i], 0, sizeof(bufs[i]));
+ goto err_import;
+ }
+ }
+
+ if (custom_data_size) {
+ custom_data = kzalloc(custom_data_size, GFP_KERNEL);
+ if (!custom_data) {
+ ret = -ENOMEM;
+ goto err_import;
+ }
+
+ if (copy_from_user(custom_data, arg->custom_data,
+ custom_data_size)) {
+ ret = -EFAULT;
+ goto err_import;
+ }
+ }
+
+ if (put_user(complete_fence_fd, &arg->complete_fence)) {
+ ret = -EFAULT;
+ goto err_import;
+ }
+
+ complete_fence = adf_device_post_nocopy(dev, intfs, n_intfs, bufs,
+ n_bufs, custom_data, custom_data_size);
+ if (IS_ERR(complete_fence)) {
+ ret = PTR_ERR(complete_fence);
+ goto err_import;
+ }
+
+ sync_fence_install(complete_fence, complete_fence_fd);
+ return 0;
+
+err_import:
+ for (i = 0; i < n_bufs; i++)
+ adf_buffer_cleanup(&bufs[i]);
+
+err_get_user:
+ kfree(custom_data);
+ kfree(bufs);
+ kfree(intfs);
+ put_unused_fd(complete_fence_fd);
+ return ret;
+}
+
+static int adf_intf_simple_post_config(struct adf_interface *intf,
+ struct adf_simple_post_config __user *arg)
+{
+ struct adf_device *dev = intf->base.parent;
+ struct sync_fence *complete_fence;
+ int complete_fence_fd;
+ struct adf_buffer buf;
+ int ret = 0;
+
+ complete_fence_fd = get_unused_fd();
+ if (complete_fence_fd < 0)
+ return complete_fence_fd;
+
+ ret = adf_buffer_import(dev, &arg->buf, &buf);
+ if (ret < 0)
+ goto err_import;
+
+ if (put_user(complete_fence_fd, &arg->complete_fence)) {
+ ret = -EFAULT;
+ goto err_put_user;
+ }
+
+ complete_fence = adf_interface_simple_post(intf, &buf);
+ if (IS_ERR(complete_fence)) {
+ ret = PTR_ERR(complete_fence);
+ goto err_put_user;
+ }
+
+ sync_fence_install(complete_fence, complete_fence_fd);
+ return 0;
+
+err_put_user:
+ adf_buffer_cleanup(&buf);
+err_import:
+ put_unused_fd(complete_fence_fd);
+ return ret;
+}
+
+static int adf_intf_simple_buffer_alloc(struct adf_interface *intf,
+ struct adf_simple_buffer_alloc __user *arg)
+{
+ struct adf_simple_buffer_alloc data;
+ struct dma_buf *dma_buf;
+ int ret = 0;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ data.fd = get_unused_fd_flags(O_CLOEXEC);
+ if (data.fd < 0)
+ return data.fd;
+
+ ret = adf_interface_simple_buffer_alloc(intf, data.w, data.h,
+ data.format, &dma_buf, &data.offset, &data.pitch);
+ if (ret < 0)
+ goto err_alloc;
+
+ if (copy_to_user(arg, &data, sizeof(*arg))) {
+ ret = -EFAULT;
+ goto err_copy;
+ }
+
+ fd_install(data.fd, dma_buf->file);
+ return 0;
+
+err_copy:
+ dma_buf_put(dma_buf);
+
+err_alloc:
+ put_unused_fd(data.fd);
+ return ret;
+}
+
+static int adf_copy_attachment_list_to_user(
+ struct adf_attachment_config __user *to, size_t n_to,
+ struct adf_attachment *from, size_t n_from)
+{
+ struct adf_attachment_config *temp;
+ size_t n = min(n_to, n_from);
+ size_t i;
+ int ret = 0;
+
+ if (!n)
+ return 0;
+
+ temp = kzalloc(n * sizeof(temp[0]), GFP_KERNEL);
+ if (!temp)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++) {
+ temp[i].interface = from[i].interface->base.id;
+ temp[i].overlay_engine = from[i].overlay_engine->base.id;
+ }
+
+ if (copy_to_user(to, temp, n * sizeof(to[0]))) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+done:
+ kfree(temp);
+ return ret;
+}
+
+static int adf_device_get_data(struct adf_device *dev,
+ struct adf_device_data __user *arg)
+{
+ struct adf_device_data data;
+ size_t n_attach;
+ struct adf_attachment *attach = NULL;
+ size_t n_allowed_attach;
+ struct adf_attachment *allowed_attach = NULL;
+ int ret = 0;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ if (data.n_attachments > ADF_MAX_ATTACHMENTS ||
+ data.n_allowed_attachments > ADF_MAX_ATTACHMENTS)
+ return -EINVAL;
+
+ strlcpy(data.name, dev->base.name, sizeof(data.name));
+
+ if (data.n_attachments) {
+ attach = kzalloc(data.n_attachments * sizeof(attach[0]),
+ GFP_KERNEL);
+ if (!attach)
+ return -ENOMEM;
+ }
+ n_attach = adf_device_attachments(dev, attach, data.n_attachments);
+
+ if (data.n_allowed_attachments) {
+ allowed_attach = kzalloc(data.n_allowed_attachments *
+ sizeof(allowed_attach[0]), GFP_KERNEL);
+ if (!allowed_attach) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+ n_allowed_attach = adf_device_attachments_allowed(dev, allowed_attach,
+ data.n_allowed_attachments);
+
+ mutex_lock(&dev->client_lock);
+ ret = adf_obj_copy_custom_data_to_user(&dev->base, arg->custom_data,
+ &data.custom_data_size);
+ mutex_unlock(&dev->client_lock);
+
+ if (ret < 0)
+ goto done;
+
+ ret = adf_copy_attachment_list_to_user(arg->attachments,
+ data.n_attachments, attach, n_attach);
+ if (ret < 0)
+ goto done;
+
+ ret = adf_copy_attachment_list_to_user(arg->allowed_attachments,
+ data.n_allowed_attachments, allowed_attach,
+ n_allowed_attach);
+ if (ret < 0)
+ goto done;
+
+ data.n_attachments = n_attach;
+ data.n_allowed_attachments = n_allowed_attach;
+
+ if (copy_to_user(arg, &data, sizeof(data)))
+ ret = -EFAULT;
+
+done:
+ kfree(allowed_attach);
+ kfree(attach);
+ return ret;
+}
+
+static int adf_device_handle_attachment(struct adf_device *dev,
+ struct adf_attachment_config __user *arg, bool attach)
+{
+ struct adf_attachment_config data;
+ struct adf_overlay_engine *eng;
+ struct adf_interface *intf;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ eng = idr_find(&dev->overlay_engines, data.overlay_engine);
+ if (!eng) {
+ dev_err(&dev->base.dev, "invalid overlay engine id %u\n",
+ data.overlay_engine);
+ return -EINVAL;
+ }
+
+ intf = idr_find(&dev->interfaces, data.interface);
+ if (!intf) {
+ dev_err(&dev->base.dev, "invalid interface id %u\n",
+ data.interface);
+ return -EINVAL;
+ }
+
+ if (attach)
+ return adf_device_attach(dev, eng, intf);
+ else
+ return adf_device_detach(dev, eng, intf);
+}
+
+static int adf_intf_set_mode(struct adf_interface *intf,
+ struct drm_mode_modeinfo __user *arg)
+{
+ struct drm_mode_modeinfo mode;
+
+ if (copy_from_user(&mode, arg, sizeof(mode)))
+ return -EFAULT;
+
+ return adf_interface_set_mode(intf, &mode);
+}
+
+static int adf_intf_get_data(struct adf_interface *intf,
+ struct adf_interface_data __user *arg)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ struct adf_interface_data data;
+ struct drm_mode_modeinfo *modelist;
+ size_t modelist_size;
+ int err;
+ int ret = 0;
+ unsigned long flags;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ strlcpy(data.name, intf->base.name, sizeof(data.name));
+
+ data.type = intf->type;
+ data.id = intf->idx;
+ data.flags = intf->flags;
+
+ err = adf_interface_get_screen_size(intf, &data.width_mm,
+ &data.height_mm);
+ if (err < 0) {
+ data.width_mm = 0;
+ data.height_mm = 0;
+ }
+
+ modelist = kmalloc(sizeof(modelist[0]) * ADF_MAX_MODES, GFP_KERNEL);
+ if (!modelist)
+ return -ENOMEM;
+
+ mutex_lock(&dev->client_lock);
+ read_lock_irqsave(&intf->hotplug_modelist_lock, flags);
+ data.hotplug_detect = intf->hotplug_detect;
+ modelist_size = min(data.n_available_modes, intf->n_modes) *
+ sizeof(intf->modelist[0]);
+ memcpy(modelist, intf->modelist, modelist_size);
+ data.n_available_modes = intf->n_modes;
+ read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags);
+
+ if (copy_to_user(arg->available_modes, modelist, modelist_size)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ data.dpms_state = intf->dpms_state;
+ memcpy(&data.current_mode, &intf->current_mode,
+ sizeof(intf->current_mode));
+
+ ret = adf_obj_copy_custom_data_to_user(&intf->base, arg->custom_data,
+ &data.custom_data_size);
+done:
+ mutex_unlock(&dev->client_lock);
+ kfree(modelist);
+
+ if (ret < 0)
+ return ret;
+
+ if (copy_to_user(arg, &data, sizeof(data)))
+ ret = -EFAULT;
+
+ return ret;
+}
+
+static inline long adf_obj_custom_ioctl(struct adf_obj *obj, unsigned int cmd,
+ unsigned long arg)
+{
+ if (obj->ops && obj->ops->ioctl)
+ return obj->ops->ioctl(obj, cmd, arg);
+ return -ENOTTY;
+}
+
+static long adf_overlay_engine_ioctl(struct adf_overlay_engine *eng,
+ struct adf_file *file, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case ADF_SET_EVENT:
+ return adf_obj_set_event(&eng->base, file,
+ (struct adf_set_event __user *)arg);
+
+ case ADF_GET_OVERLAY_ENGINE_DATA:
+ return adf_eng_get_data(eng,
+ (struct adf_overlay_engine_data __user *)arg);
+
+ case ADF_BLANK:
+ case ADF_POST_CONFIG:
+ case ADF_SET_MODE:
+ case ADF_GET_DEVICE_DATA:
+ case ADF_GET_INTERFACE_DATA:
+ case ADF_SIMPLE_POST_CONFIG:
+ case ADF_SIMPLE_BUFFER_ALLOC:
+ case ADF_ATTACH:
+ case ADF_DETACH:
+ return -EINVAL;
+
+ default:
+ return adf_obj_custom_ioctl(&eng->base, cmd, arg);
+ }
+}
+
+static long adf_interface_ioctl(struct adf_interface *intf,
+ struct adf_file *file, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case ADF_SET_EVENT:
+ return adf_obj_set_event(&intf->base, file,
+ (struct adf_set_event __user *)arg);
+
+ case ADF_BLANK:
+ return adf_interface_blank(intf, arg);
+
+ case ADF_SET_MODE:
+ return adf_intf_set_mode(intf,
+ (struct drm_mode_modeinfo __user *)arg);
+
+ case ADF_GET_INTERFACE_DATA:
+ return adf_intf_get_data(intf,
+ (struct adf_interface_data __user *)arg);
+
+ case ADF_SIMPLE_POST_CONFIG:
+ return adf_intf_simple_post_config(intf,
+ (struct adf_simple_post_config __user *)arg);
+
+ case ADF_SIMPLE_BUFFER_ALLOC:
+ return adf_intf_simple_buffer_alloc(intf,
+ (struct adf_simple_buffer_alloc __user *)arg);
+
+ case ADF_POST_CONFIG:
+ case ADF_GET_DEVICE_DATA:
+ case ADF_GET_OVERLAY_ENGINE_DATA:
+ case ADF_ATTACH:
+ case ADF_DETACH:
+ return -EINVAL;
+
+ default:
+ return adf_obj_custom_ioctl(&intf->base, cmd, arg);
+ }
+}
+
+static long adf_device_ioctl(struct adf_device *dev, struct adf_file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case ADF_SET_EVENT:
+ return adf_obj_set_event(&dev->base, file,
+ (struct adf_set_event __user *)arg);
+
+ case ADF_POST_CONFIG:
+ return adf_device_post_config(dev,
+ (struct adf_post_config __user *)arg);
+
+ case ADF_GET_DEVICE_DATA:
+ return adf_device_get_data(dev,
+ (struct adf_device_data __user *)arg);
+
+ case ADF_ATTACH:
+ return adf_device_handle_attachment(dev,
+ (struct adf_attachment_config __user *)arg,
+ true);
+
+ case ADF_DETACH:
+ return adf_device_handle_attachment(dev,
+ (struct adf_attachment_config __user *)arg,
+ false);
+
+ case ADF_BLANK:
+ case ADF_SET_MODE:
+ case ADF_GET_INTERFACE_DATA:
+ case ADF_GET_OVERLAY_ENGINE_DATA:
+ case ADF_SIMPLE_POST_CONFIG:
+ case ADF_SIMPLE_BUFFER_ALLOC:
+ return -EINVAL;
+
+ default:
+ return adf_obj_custom_ioctl(&dev->base, cmd, arg);
+ }
+}
+
+static int adf_file_open(struct inode *inode, struct file *file)
+{
+ struct adf_obj *obj;
+ struct adf_file *fpriv = NULL;
+ unsigned long flags;
+ int ret = 0;
+
+ obj = adf_obj_sysfs_find(iminor(inode));
+ if (!obj)
+ return -ENODEV;
+
+ dev_dbg(&obj->dev, "opening %s\n", dev_name(&obj->dev));
+
+ if (!try_module_get(obj->parent->ops->owner)) {
+ dev_err(&obj->dev, "getting owner module failed\n");
+ return -ENODEV;
+ }
+
+ fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
+ if (!fpriv) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ INIT_LIST_HEAD(&fpriv->head);
+ fpriv->obj = obj;
+ init_waitqueue_head(&fpriv->event_wait);
+
+ file->private_data = fpriv;
+
+ if (obj->ops && obj->ops->open) {
+ ret = obj->ops->open(obj, inode, file);
+ if (ret < 0)
+ goto done;
+ }
+
+ spin_lock_irqsave(&obj->file_lock, flags);
+ list_add_tail(&fpriv->head, &obj->file_list);
+ spin_unlock_irqrestore(&obj->file_lock, flags);
+
+done:
+ if (ret < 0) {
+ kfree(fpriv);
+ module_put(obj->parent->ops->owner);
+ }
+ return ret;
+}
+
+static int adf_file_release(struct inode *inode, struct file *file)
+{
+ struct adf_file *fpriv = file->private_data;
+ struct adf_obj *obj = fpriv->obj;
+ enum adf_event_type event_type;
+ unsigned long flags;
+
+ if (obj->ops && obj->ops->release)
+ obj->ops->release(obj, inode, file);
+
+ spin_lock_irqsave(&obj->file_lock, flags);
+ list_del(&fpriv->head);
+ spin_unlock_irqrestore(&obj->file_lock, flags);
+
+ for_each_set_bit(event_type, fpriv->event_subscriptions,
+ ADF_EVENT_TYPE_MAX) {
+ adf_event_put(obj, event_type);
+ }
+
+ kfree(fpriv);
+ module_put(obj->parent->ops->owner);
+
+ dev_dbg(&obj->dev, "released %s\n", dev_name(&obj->dev));
+ return 0;
+}
+
+long adf_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct adf_file *fpriv = file->private_data;
+ struct adf_obj *obj = fpriv->obj;
+ long ret = -EINVAL;
+
+ dev_dbg(&obj->dev, "%s ioctl %u\n", dev_name(&obj->dev), _IOC_NR(cmd));
+
+ switch (obj->type) {
+ case ADF_OBJ_OVERLAY_ENGINE:
+ ret = adf_overlay_engine_ioctl(adf_obj_to_overlay_engine(obj),
+ fpriv, cmd, arg);
+ break;
+
+ case ADF_OBJ_INTERFACE:
+ ret = adf_interface_ioctl(adf_obj_to_interface(obj), fpriv, cmd,
+ arg);
+ break;
+
+ case ADF_OBJ_DEVICE:
+ ret = adf_device_ioctl(adf_obj_to_device(obj), fpriv, cmd, arg);
+ break;
+ }
+
+ return ret;
+}
+
+static inline bool adf_file_event_available(struct adf_file *fpriv)
+{
+ int head = fpriv->event_head;
+ int tail = fpriv->event_tail;
+ return CIRC_CNT(head, tail, sizeof(fpriv->event_buf)) != 0;
+}
+
+void adf_file_queue_event(struct adf_file *fpriv, struct adf_event *event)
+{
+ int head = fpriv->event_head;
+ int tail = fpriv->event_tail;
+ size_t space = CIRC_SPACE(head, tail, sizeof(fpriv->event_buf));
+ size_t space_to_end =
+ CIRC_SPACE_TO_END(head, tail, sizeof(fpriv->event_buf));
+
+ if (space < event->length) {
+ dev_dbg(&fpriv->obj->dev,
+ "insufficient buffer space for event %u\n",
+ event->type);
+ return;
+ }
+
+ if (space_to_end >= event->length) {
+ memcpy(fpriv->event_buf + head, event, event->length);
+ } else {
+ memcpy(fpriv->event_buf + head, event, space_to_end);
+ memcpy(fpriv->event_buf, (u8 *)event + space_to_end,
+ event->length - space_to_end);
+ }
+
+ smp_wmb();
+ fpriv->event_head = (fpriv->event_head + event->length) &
+ (sizeof(fpriv->event_buf) - 1);
+ wake_up_interruptible_all(&fpriv->event_wait);
+}
+
+static ssize_t adf_file_copy_to_user(struct adf_file *fpriv,
+ char __user *buffer, size_t buffer_size)
+{
+ int head, tail;
+ u8 *event_buf;
+ size_t cnt, cnt_to_end, copy_size = 0;
+ ssize_t ret = 0;
+ unsigned long flags;
+
+ event_buf = kmalloc(min(buffer_size, sizeof(fpriv->event_buf)),
+ GFP_KERNEL);
+ if (!event_buf)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&fpriv->obj->file_lock, flags);
+
+ if (!adf_file_event_available(fpriv))
+ goto out;
+
+ head = fpriv->event_head;
+ tail = fpriv->event_tail;
+
+ cnt = CIRC_CNT(head, tail, sizeof(fpriv->event_buf));
+ cnt_to_end = CIRC_CNT_TO_END(head, tail, sizeof(fpriv->event_buf));
+ copy_size = min(buffer_size, cnt);
+
+ if (cnt_to_end >= copy_size) {
+ memcpy(event_buf, fpriv->event_buf + tail, copy_size);
+ } else {
+ memcpy(event_buf, fpriv->event_buf + tail, cnt_to_end);
+ memcpy(event_buf + cnt_to_end, fpriv->event_buf,
+ copy_size - cnt_to_end);
+ }
+
+ fpriv->event_tail = (fpriv->event_tail + copy_size) &
+ (sizeof(fpriv->event_buf) - 1);
+
+out:
+ spin_unlock_irqrestore(&fpriv->obj->file_lock, flags);
+ if (copy_size) {
+ if (copy_to_user(buffer, event_buf, copy_size))
+ ret = -EFAULT;
+ else
+ ret = copy_size;
+ }
+ kfree(event_buf);
+ return ret;
+}
+
+ssize_t adf_file_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *offset)
+{
+ struct adf_file *fpriv = filp->private_data;
+ int err;
+
+ err = wait_event_interruptible(fpriv->event_wait,
+ adf_file_event_available(fpriv));
+ if (err < 0)
+ return err;
+
+ return adf_file_copy_to_user(fpriv, buffer, count);
+}
+
+unsigned int adf_file_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ struct adf_file *fpriv = filp->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &fpriv->event_wait, wait);
+
+ if (adf_file_event_available(fpriv))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+const struct file_operations adf_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = adf_file_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = adf_file_compat_ioctl,
+#endif
+ .open = adf_file_open,
+ .release = adf_file_release,
+ .llseek = default_llseek,
+ .read = adf_file_read,
+ .poll = adf_file_poll,
+};
diff --git a/drivers/video/adf/adf_fops.h b/drivers/video/adf/adf_fops.h
new file mode 100644
index 000000000000..90a3a74796d6
--- /dev/null
+++ b/drivers/video/adf/adf_fops.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __VIDEO_ADF_ADF_FOPS_H
+#define __VIDEO_ADF_ADF_FOPS_H
+
+#include <linux/bitmap.h>
+#include <linux/fs.h>
+
+extern const struct file_operations adf_fops;
+
+struct adf_file {
+ struct list_head head;
+ struct adf_obj *obj;
+
+ DECLARE_BITMAP(event_subscriptions, ADF_EVENT_TYPE_MAX);
+ u8 event_buf[4096];
+ int event_head;
+ int event_tail;
+ wait_queue_head_t event_wait;
+};
+
+void adf_file_queue_event(struct adf_file *file, struct adf_event *event);
+long adf_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+
+#endif /* __VIDEO_ADF_ADF_FOPS_H */
diff --git a/drivers/video/adf/adf_fops32.c b/drivers/video/adf/adf_fops32.c
new file mode 100644
index 000000000000..60a47cf5a781
--- /dev/null
+++ b/drivers/video/adf/adf_fops32.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <video/adf.h>
+
+#include "adf_fops.h"
+#include "adf_fops32.h"
+
+long adf_compat_post_config(struct file *file,
+ struct adf_post_config32 __user *arg)
+{
+ struct adf_post_config32 cfg32;
+ struct adf_post_config __user *cfg;
+ int ret;
+
+ if (copy_from_user(&cfg32, arg, sizeof(cfg32)))
+ return -EFAULT;
+
+ cfg = compat_alloc_user_space(sizeof(*cfg));
+ if (!access_ok(VERIFY_WRITE, cfg, sizeof(*cfg)))
+ return -EFAULT;
+
+ if (put_user(cfg32.n_interfaces, &cfg->n_interfaces) ||
+ put_user(compat_ptr(cfg32.interfaces),
+ &cfg->interfaces) ||
+ put_user(cfg32.n_bufs, &cfg->n_bufs) ||
+ put_user(compat_ptr(cfg32.bufs), &cfg->bufs) ||
+ put_user(cfg32.custom_data_size,
+ &cfg->custom_data_size) ||
+ put_user(compat_ptr(cfg32.custom_data),
+ &cfg->custom_data))
+ return -EFAULT;
+
+ ret = adf_file_ioctl(file, ADF_POST_CONFIG, (unsigned long)cfg);
+ if (ret < 0)
+ return ret;
+
+ if (copy_in_user(&arg->complete_fence, &cfg->complete_fence,
+ sizeof(cfg->complete_fence)))
+ return -EFAULT;
+
+ return 0;
+}
+
+long adf_compat_get_device_data(struct file *file,
+ struct adf_device_data32 __user *arg)
+{
+ struct adf_device_data32 data32;
+ struct adf_device_data __user *data;
+ int ret;
+
+ if (copy_from_user(&data32, arg, sizeof(data32)))
+ return -EFAULT;
+
+ data = compat_alloc_user_space(sizeof(*data));
+ if (!access_ok(VERIFY_WRITE, data, sizeof(*data)))
+ return -EFAULT;
+
+ if (put_user(data32.n_attachments, &data->n_attachments) ||
+ put_user(compat_ptr(data32.attachments),
+ &data->attachments) ||
+ put_user(data32.n_allowed_attachments,
+ &data->n_allowed_attachments) ||
+ put_user(compat_ptr(data32.allowed_attachments),
+ &data->allowed_attachments) ||
+ put_user(data32.custom_data_size,
+ &data->custom_data_size) ||
+ put_user(compat_ptr(data32.custom_data),
+ &data->custom_data))
+ return -EFAULT;
+
+ ret = adf_file_ioctl(file, ADF_GET_DEVICE_DATA32, (unsigned long)data);
+ if (ret < 0)
+ return ret;
+
+ if (copy_in_user(arg->name, data->name, sizeof(arg->name)) ||
+ copy_in_user(&arg->n_attachments, &data->n_attachments,
+ sizeof(arg->n_attachments)) ||
+ copy_in_user(&arg->n_allowed_attachments,
+ &data->n_allowed_attachments,
+ sizeof(arg->n_allowed_attachments)) ||
+ copy_in_user(&arg->custom_data_size,
+ &data->custom_data_size,
+ sizeof(arg->custom_data_size)))
+ return -EFAULT;
+
+ return 0;
+}
+
+long adf_compat_get_interface_data(struct file *file,
+ struct adf_interface_data32 __user *arg)
+{
+ struct adf_interface_data32 data32;
+ struct adf_interface_data __user *data;
+ int ret;
+
+ if (copy_from_user(&data32, arg, sizeof(data32)))
+ return -EFAULT;
+
+ data = compat_alloc_user_space(sizeof(*data));
+ if (!access_ok(VERIFY_WRITE, data, sizeof(*data)))
+ return -EFAULT;
+
+ if (put_user(data32.n_available_modes, &data->n_available_modes) ||
+ put_user(compat_ptr(data32.available_modes),
+ &data->available_modes) ||
+ put_user(data32.custom_data_size,
+ &data->custom_data_size) ||
+ put_user(compat_ptr(data32.custom_data),
+ &data->custom_data))
+ return -EFAULT;
+
+ ret = adf_file_ioctl(file, ADF_GET_DEVICE_DATA32, (unsigned long)data);
+ if (ret < 0)
+ return ret;
+
+ if (copy_in_user(arg->name, data->name, sizeof(arg->name)) ||
+ copy_in_user(&arg->type, &data->type,
+ sizeof(arg->type)) ||
+ copy_in_user(&arg->id, &data->id, sizeof(arg->id)) ||
+ copy_in_user(&arg->flags, &data->flags,
+ sizeof(arg->flags)) ||
+ copy_in_user(&arg->dpms_state, &data->dpms_state,
+ sizeof(arg->dpms_state)) ||
+ copy_in_user(&arg->hotplug_detect,
+ &data->hotplug_detect,
+ sizeof(arg->hotplug_detect)) ||
+ copy_in_user(&arg->width_mm, &data->width_mm,
+ sizeof(arg->width_mm)) ||
+ copy_in_user(&arg->height_mm, &data->height_mm,
+ sizeof(arg->height_mm)) ||
+ copy_in_user(&arg->current_mode, &data->current_mode,
+ sizeof(arg->current_mode)) ||
+ copy_in_user(&arg->n_available_modes,
+ &data->n_available_modes,
+ sizeof(arg->n_available_modes)) ||
+ copy_in_user(&arg->custom_data_size,
+ &data->custom_data_size,
+ sizeof(arg->custom_data_size)))
+ return -EFAULT;
+
+ return 0;
+}
+
+long adf_compat_get_overlay_engine_data(struct file *file,
+ struct adf_overlay_engine_data32 __user *arg)
+{
+ struct adf_overlay_engine_data32 data32;
+ struct adf_overlay_engine_data __user *data;
+ int ret;
+
+ if (copy_from_user(&data32, arg, sizeof(data32)))
+ return -EFAULT;
+
+ data = compat_alloc_user_space(sizeof(*data));
+ if (!access_ok(VERIFY_WRITE, data, sizeof(*data)))
+ return -EFAULT;
+
+ if (put_user(data32.n_supported_formats, &data->n_supported_formats) ||
+ put_user(compat_ptr(data32.supported_formats),
+ &data->supported_formats) ||
+ put_user(data32.custom_data_size,
+ &data->custom_data_size) ||
+ put_user(compat_ptr(data32.custom_data),
+ &data->custom_data))
+ return -EFAULT;
+
+ ret = adf_file_ioctl(file, ADF_GET_OVERLAY_ENGINE_DATA,
+ (unsigned long)data);
+ if (ret < 0)
+ return ret;
+
+ if (copy_in_user(arg->name, data->name, sizeof(arg->name)) ||
+ copy_in_user(&arg->n_supported_formats,
+ &data->n_supported_formats,
+ sizeof(arg->n_supported_formats)) ||
+ copy_in_user(&arg->custom_data_size,
+ &data->custom_data_size,
+ sizeof(arg->custom_data_size)))
+ return -EFAULT;
+
+ return 0;
+}
+
+long adf_file_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd) {
+ case ADF_POST_CONFIG32:
+ return adf_compat_post_config(file, compat_ptr(arg));
+
+ case ADF_GET_DEVICE_DATA32:
+ return adf_compat_get_device_data(file, compat_ptr(arg));
+
+ case ADF_GET_INTERFACE_DATA32:
+ return adf_compat_get_interface_data(file, compat_ptr(arg));
+
+ case ADF_GET_OVERLAY_ENGINE_DATA32:
+ return adf_compat_get_overlay_engine_data(file,
+ compat_ptr(arg));
+
+ default:
+ return adf_file_ioctl(file, cmd, arg);
+ }
+}
diff --git a/drivers/video/adf/adf_fops32.h b/drivers/video/adf/adf_fops32.h
new file mode 100644
index 000000000000..18c673dc5e2e
--- /dev/null
+++ b/drivers/video/adf/adf_fops32.h
@@ -0,0 +1,78 @@
+#ifndef __VIDEO_ADF_ADF_FOPS32_H
+#define __VIDEO_ADF_ADF_FOPS32_H
+
+#include <linux/compat.h>
+#include <linux/ioctl.h>
+
+#include <video/adf.h>
+
+#define ADF_POST_CONFIG32 \
+ _IOW('D', 2, struct adf_post_config32)
+#define ADF_GET_DEVICE_DATA32 \
+ _IOR('D', 4, struct adf_device_data32)
+#define ADF_GET_INTERFACE_DATA32 \
+ _IOR('D', 5, struct adf_interface_data32)
+#define ADF_GET_OVERLAY_ENGINE_DATA32 \
+ _IOR('D', 6, struct adf_overlay_engine_data32)
+
+struct adf_post_config32 {
+ compat_size_t n_interfaces;
+ compat_uptr_t interfaces;
+
+ compat_size_t n_bufs;
+ compat_uptr_t bufs;
+
+ compat_size_t custom_data_size;
+ compat_uptr_t custom_data;
+
+ __s64 complete_fence;
+};
+
+struct adf_device_data32 {
+ char name[ADF_NAME_LEN];
+
+ compat_size_t n_attachments;
+ compat_uptr_t attachments;
+
+ compat_size_t n_allowed_attachments;
+ compat_uptr_t allowed_attachments;
+
+ compat_size_t custom_data_size;
+ compat_uptr_t custom_data;
+};
+
+struct adf_interface_data32 {
+ char name[ADF_NAME_LEN];
+
+ __u8 type;
+ __u32 id;
+ /* e.g. type=ADF_INTF_TYPE_DSI, id=1 => DSI.1 */
+ __u32 flags;
+
+ __u8 dpms_state;
+ __u8 hotplug_detect;
+ __u16 width_mm;
+ __u16 height_mm;
+
+ struct drm_mode_modeinfo current_mode;
+ compat_size_t n_available_modes;
+ compat_uptr_t available_modes;
+
+ compat_size_t custom_data_size;
+ compat_uptr_t custom_data;
+};
+
+struct adf_overlay_engine_data32 {
+ char name[ADF_NAME_LEN];
+
+ compat_size_t n_supported_formats;
+ compat_uptr_t supported_formats;
+
+ compat_size_t custom_data_size;
+ compat_uptr_t custom_data;
+};
+
+long adf_file_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+
+#endif /* __VIDEO_ADF_ADF_FOPS32_H */
diff --git a/drivers/video/adf/adf_format.c b/drivers/video/adf/adf_format.c
new file mode 100644
index 000000000000..e3f22c7c85d9
--- /dev/null
+++ b/drivers/video/adf/adf_format.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ * modified from drivers/gpu/drm/drm_crtc.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <drm/drm_fourcc.h>
+#include <video/adf_format.h>
+
+bool adf_format_is_standard(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_C8:
+ case DRM_FORMAT_RGB332:
+ case DRM_FORMAT_BGR233:
+ case DRM_FORMAT_XRGB4444:
+ case DRM_FORMAT_XBGR4444:
+ case DRM_FORMAT_RGBX4444:
+ case DRM_FORMAT_BGRX4444:
+ case DRM_FORMAT_ARGB4444:
+ case DRM_FORMAT_ABGR4444:
+ case DRM_FORMAT_RGBA4444:
+ case DRM_FORMAT_BGRA4444:
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_XBGR1555:
+ case DRM_FORMAT_RGBX5551:
+ case DRM_FORMAT_BGRX5551:
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_BGRA5551:
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_BGR888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_BGRA8888:
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_XBGR2101010:
+ case DRM_FORMAT_RGBX1010102:
+ case DRM_FORMAT_BGRX1010102:
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_BGRA1010102:
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_AYUV:
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ case DRM_FORMAT_YUV410:
+ case DRM_FORMAT_YVU410:
+ case DRM_FORMAT_YUV411:
+ case DRM_FORMAT_YVU411:
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YUV444:
+ case DRM_FORMAT_YVU444:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL(adf_format_is_standard);
+
+bool adf_format_is_rgb(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_C8:
+ case DRM_FORMAT_RGB332:
+ case DRM_FORMAT_BGR233:
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_XBGR1555:
+ case DRM_FORMAT_RGBX5551:
+ case DRM_FORMAT_BGRX5551:
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_BGRA5551:
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_BGR888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_XBGR2101010:
+ case DRM_FORMAT_RGBX1010102:
+ case DRM_FORMAT_BGRX1010102:
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_BGRA1010102:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_BGRA8888:
+ return true;
+
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL(adf_format_is_rgb);
+
+u8 adf_format_num_planes(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_YUV410:
+ case DRM_FORMAT_YVU410:
+ case DRM_FORMAT_YUV411:
+ case DRM_FORMAT_YVU411:
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YUV444:
+ case DRM_FORMAT_YVU444:
+ return 3;
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ return 2;
+ default:
+ return 1;
+ }
+}
+EXPORT_SYMBOL(adf_format_num_planes);
+
+u8 adf_format_bpp(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_C8:
+ case DRM_FORMAT_RGB332:
+ case DRM_FORMAT_BGR233:
+ return 8;
+
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_XBGR1555:
+ case DRM_FORMAT_RGBX5551:
+ case DRM_FORMAT_BGRX5551:
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_BGRA5551:
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ return 16;
+
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_BGR888:
+ return 24;
+
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_XBGR2101010:
+ case DRM_FORMAT_RGBX1010102:
+ case DRM_FORMAT_BGRX1010102:
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_BGRA1010102:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_BGRA8888:
+ return 32;
+
+ default:
+ pr_debug("%s: unsupported pixel format %u\n", __func__, format);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(adf_format_bpp);
+
+u8 adf_format_plane_cpp(u32 format, int plane)
+{
+ if (plane >= adf_format_num_planes(format))
+ return 0;
+
+ switch (format) {
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ return 2;
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ return plane ? 2 : 1;
+ case DRM_FORMAT_YUV410:
+ case DRM_FORMAT_YVU410:
+ case DRM_FORMAT_YUV411:
+ case DRM_FORMAT_YVU411:
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YUV444:
+ case DRM_FORMAT_YVU444:
+ return 1;
+ default:
+ return adf_format_bpp(format) / 8;
+ }
+}
+EXPORT_SYMBOL(adf_format_plane_cpp);
+
+u8 adf_format_horz_chroma_subsampling(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_YUV411:
+ case DRM_FORMAT_YVU411:
+ case DRM_FORMAT_YUV410:
+ case DRM_FORMAT_YVU410:
+ return 4;
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ return 2;
+ default:
+ return 1;
+ }
+}
+EXPORT_SYMBOL(adf_format_horz_chroma_subsampling);
+
+u8 adf_format_vert_chroma_subsampling(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_YUV410:
+ case DRM_FORMAT_YVU410:
+ return 4;
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ return 2;
+ default:
+ return 1;
+ }
+}
+EXPORT_SYMBOL(adf_format_vert_chroma_subsampling);
diff --git a/drivers/video/adf/adf_memblock.c b/drivers/video/adf/adf_memblock.c
new file mode 100644
index 000000000000..3c99f27388db
--- /dev/null
+++ b/drivers/video/adf/adf_memblock.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/highmem.h>
+#include <linux/memblock.h>
+#include <linux/slab.h>
+
+struct adf_memblock_pdata {
+ phys_addr_t base;
+};
+
+static struct sg_table *adf_memblock_map(struct dma_buf_attachment *attach,
+ enum dma_data_direction direction)
+{
+ struct adf_memblock_pdata *pdata = attach->dmabuf->priv;
+ unsigned long pfn = PFN_DOWN(pdata->base);
+ struct page *page = pfn_to_page(pfn);
+ struct sg_table *table;
+ int ret;
+
+ table = kzalloc(sizeof(*table), GFP_KERNEL);
+ if (!table)
+ return ERR_PTR(-ENOMEM);
+
+ ret = sg_alloc_table(table, 1, GFP_KERNEL);
+ if (ret < 0)
+ goto err;
+
+ sg_set_page(table->sgl, page, attach->dmabuf->size, 0);
+ return table;
+
+err:
+ kfree(table);
+ return ERR_PTR(ret);
+}
+
+static void adf_memblock_unmap(struct dma_buf_attachment *attach,
+ struct sg_table *table, enum dma_data_direction direction)
+{
+ sg_free_table(table);
+}
+
+static void __init_memblock adf_memblock_release(struct dma_buf *buf)
+{
+ struct adf_memblock_pdata *pdata = buf->priv;
+ int err = memblock_free(pdata->base, buf->size);
+
+ if (err < 0)
+ pr_warn("%s: freeing memblock failed: %d\n", __func__, err);
+ kfree(pdata);
+}
+
+static void *adf_memblock_do_kmap(struct dma_buf *buf, unsigned long pgoffset,
+ bool atomic)
+{
+ struct adf_memblock_pdata *pdata = buf->priv;
+ unsigned long pfn = PFN_DOWN(pdata->base) + pgoffset;
+ struct page *page = pfn_to_page(pfn);
+
+ if (atomic)
+ return kmap_atomic(page);
+ else
+ return kmap(page);
+}
+
+static void *adf_memblock_kmap_atomic(struct dma_buf *buf,
+ unsigned long pgoffset)
+{
+ return adf_memblock_do_kmap(buf, pgoffset, true);
+}
+
+static void adf_memblock_kunmap_atomic(struct dma_buf *buf,
+ unsigned long pgoffset, void *vaddr)
+{
+ kunmap_atomic(vaddr);
+}
+
+static void *adf_memblock_kmap(struct dma_buf *buf, unsigned long pgoffset)
+{
+ return adf_memblock_do_kmap(buf, pgoffset, false);
+}
+
+static void adf_memblock_kunmap(struct dma_buf *buf, unsigned long pgoffset,
+ void *vaddr)
+{
+ kunmap(vaddr);
+}
+
+static int adf_memblock_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
+{
+ struct adf_memblock_pdata *pdata = buf->priv;
+
+ return remap_pfn_range(vma, vma->vm_start, PFN_DOWN(pdata->base),
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+struct dma_buf_ops adf_memblock_ops = {
+ .map_dma_buf = adf_memblock_map,
+ .unmap_dma_buf = adf_memblock_unmap,
+ .release = adf_memblock_release,
+ .kmap_atomic = adf_memblock_kmap_atomic,
+ .kunmap_atomic = adf_memblock_kunmap_atomic,
+ .kmap = adf_memblock_kmap,
+ .kunmap = adf_memblock_kunmap,
+ .mmap = adf_memblock_mmap,
+};
+
+/**
+ * adf_memblock_export - export a memblock reserved area as a dma-buf
+ *
+ * @base: base physical address
+ * @size: memblock size
+ * @flags: mode flags for the dma-buf's file
+ *
+ * @base and @size must be page-aligned.
+ *
+ * Returns a dma-buf on success or ERR_PTR(-errno) on failure.
+ */
+struct dma_buf *adf_memblock_export(phys_addr_t base, size_t size, int flags)
+{
+ struct adf_memblock_pdata *pdata;
+ struct dma_buf *buf;
+
+ if (PAGE_ALIGN(base) != base || PAGE_ALIGN(size) != size)
+ return ERR_PTR(-EINVAL);
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->base = base;
+ buf = dma_buf_export(pdata, &adf_memblock_ops, size, flags);
+ if (IS_ERR(buf))
+ kfree(pdata);
+
+ return buf;
+}
diff --git a/drivers/video/adf/adf_sysfs.c b/drivers/video/adf/adf_sysfs.c
new file mode 100644
index 000000000000..076ccbd0cd88
--- /dev/null
+++ b/drivers/video/adf/adf_sysfs.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <video/adf_client.h>
+
+#include "adf.h"
+#include "adf_fops.h"
+#include "adf_sysfs.h"
+
+static struct class *adf_class;
+static int adf_major;
+static DEFINE_IDR(adf_minors);
+
+#define dev_to_adf_interface(p) \
+ adf_obj_to_interface(container_of(p, struct adf_obj, dev))
+
+static ssize_t dpms_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ adf_interface_dpms_state(intf));
+}
+
+static ssize_t dpms_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ u8 dpms_state;
+ int err;
+
+ err = kstrtou8(buf, 0, &dpms_state);
+ if (err < 0)
+ return err;
+
+ err = adf_interface_blank(intf, dpms_state);
+ if (err < 0)
+ return err;
+
+ return count;
+}
+
+static ssize_t current_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ struct drm_mode_modeinfo mode;
+
+ adf_interface_current_mode(intf, &mode);
+
+ if (mode.name[0]) {
+ return scnprintf(buf, PAGE_SIZE, "%s\n", mode.name);
+ } else {
+ bool interlaced = !!(mode.flags & DRM_MODE_FLAG_INTERLACE);
+ return scnprintf(buf, PAGE_SIZE, "%ux%u%s\n", mode.hdisplay,
+ mode.vdisplay, interlaced ? "i" : "");
+ }
+}
+
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ return scnprintf(buf, PAGE_SIZE, "%s\n",
+ adf_interface_type_str(intf));
+}
+
+static ssize_t vsync_timestamp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ ktime_t timestamp;
+ unsigned long flags;
+
+ read_lock_irqsave(&intf->vsync_lock, flags);
+ memcpy(&timestamp, &intf->vsync_timestamp, sizeof(timestamp));
+ read_unlock_irqrestore(&intf->vsync_lock, flags);
+
+ return scnprintf(buf, PAGE_SIZE, "%llu\n", ktime_to_ns(timestamp));
+}
+
+static ssize_t hotplug_detect_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ return scnprintf(buf, PAGE_SIZE, "%u\n", intf->hotplug_detect);
+}
+
+static struct device_attribute adf_interface_attrs[] = {
+ __ATTR(dpms_state, S_IRUGO|S_IWUSR, dpms_state_show, dpms_state_store),
+ __ATTR_RO(current_mode),
+ __ATTR_RO(hotplug_detect),
+ __ATTR_RO(type),
+ __ATTR_RO(vsync_timestamp),
+};
+
+static char *adf_devnode(struct device *dev, umode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "adf/%s", dev_name(dev));
+}
+
+int adf_obj_sysfs_init(struct adf_obj *obj, struct device *parent)
+{
+ int ret = idr_alloc(&adf_minors, obj, 0, 0, GFP_KERNEL);
+ if (ret < 0) {
+ pr_err("%s: allocating adf minor failed: %d\n", __func__,
+ ret);
+ return ret;
+ }
+
+ obj->minor = ret;
+ obj->dev.parent = parent;
+ obj->dev.class = adf_class;
+ obj->dev.devt = MKDEV(adf_major, obj->minor);
+
+ ret = device_register(&obj->dev);
+ if (ret < 0) {
+ pr_err("%s: registering adf object failed: %d\n", __func__,
+ ret);
+ goto err_device_register;
+ }
+
+ return 0;
+
+err_device_register:
+ idr_remove(&adf_minors, obj->minor);
+ return ret;
+}
+
+static char *adf_device_devnode(struct device *dev, umode_t *mode,
+ kuid_t *uid, kgid_t *gid)
+{
+ struct adf_obj *obj = container_of(dev, struct adf_obj, dev);
+ return kasprintf(GFP_KERNEL, "adf/%s/device", obj->name);
+}
+
+static char *adf_interface_devnode(struct device *dev, umode_t *mode,
+ kuid_t *uid, kgid_t *gid)
+{
+ struct adf_obj *obj = container_of(dev, struct adf_obj, dev);
+ struct adf_interface *intf = adf_obj_to_interface(obj);
+ struct adf_device *parent = adf_interface_parent(intf);
+ return kasprintf(GFP_KERNEL, "adf/%s/interface%d",
+ parent->base.name, intf->base.id);
+}
+
+static char *adf_overlay_engine_devnode(struct device *dev, umode_t *mode,
+ kuid_t *uid, kgid_t *gid)
+{
+ struct adf_obj *obj = container_of(dev, struct adf_obj, dev);
+ struct adf_overlay_engine *eng = adf_obj_to_overlay_engine(obj);
+ struct adf_device *parent = adf_overlay_engine_parent(eng);
+ return kasprintf(GFP_KERNEL, "adf/%s/overlay-engine%d",
+ parent->base.name, eng->base.id);
+}
+
+static void adf_noop_release(struct device *dev)
+{
+}
+
+static struct device_type adf_device_type = {
+ .name = "adf_device",
+ .devnode = adf_device_devnode,
+ .release = adf_noop_release,
+};
+
+static struct device_type adf_interface_type = {
+ .name = "adf_interface",
+ .devnode = adf_interface_devnode,
+ .release = adf_noop_release,
+};
+
+static struct device_type adf_overlay_engine_type = {
+ .name = "adf_overlay_engine",
+ .devnode = adf_overlay_engine_devnode,
+ .release = adf_noop_release,
+};
+
+int adf_device_sysfs_init(struct adf_device *dev)
+{
+ dev->base.dev.type = &adf_device_type;
+ dev_set_name(&dev->base.dev, "%s", dev->base.name);
+ return adf_obj_sysfs_init(&dev->base, dev->dev);
+}
+
+int adf_interface_sysfs_init(struct adf_interface *intf)
+{
+ struct adf_device *parent = adf_interface_parent(intf);
+ size_t i, j;
+ int ret;
+
+ intf->base.dev.type = &adf_interface_type;
+ dev_set_name(&intf->base.dev, "%s-interface%d", parent->base.name,
+ intf->base.id);
+
+ ret = adf_obj_sysfs_init(&intf->base, &parent->base.dev);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++) {
+ ret = device_create_file(&intf->base.dev,
+ &adf_interface_attrs[i]);
+ if (ret < 0) {
+ dev_err(&intf->base.dev, "creating sysfs attribute %s failed: %d\n",
+ adf_interface_attrs[i].attr.name, ret);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ for (j = 0; j < i; j++)
+ device_remove_file(&intf->base.dev, &adf_interface_attrs[j]);
+ return ret;
+}
+
+int adf_overlay_engine_sysfs_init(struct adf_overlay_engine *eng)
+{
+ struct adf_device *parent = adf_overlay_engine_parent(eng);
+
+ eng->base.dev.type = &adf_overlay_engine_type;
+ dev_set_name(&eng->base.dev, "%s-overlay-engine%d", parent->base.name,
+ eng->base.id);
+
+ return adf_obj_sysfs_init(&eng->base, &parent->base.dev);
+}
+
+struct adf_obj *adf_obj_sysfs_find(int minor)
+{
+ return idr_find(&adf_minors, minor);
+}
+
+void adf_obj_sysfs_destroy(struct adf_obj *obj)
+{
+ idr_remove(&adf_minors, obj->minor);
+ device_unregister(&obj->dev);
+}
+
+void adf_device_sysfs_destroy(struct adf_device *dev)
+{
+ adf_obj_sysfs_destroy(&dev->base);
+}
+
+void adf_interface_sysfs_destroy(struct adf_interface *intf)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++)
+ device_remove_file(&intf->base.dev, &adf_interface_attrs[i]);
+ adf_obj_sysfs_destroy(&intf->base);
+}
+
+void adf_overlay_engine_sysfs_destroy(struct adf_overlay_engine *eng)
+{
+ adf_obj_sysfs_destroy(&eng->base);
+}
+
+int adf_sysfs_init(void)
+{
+ struct class *class;
+ int ret;
+
+ class = class_create(THIS_MODULE, "adf");
+ if (IS_ERR(class)) {
+ ret = PTR_ERR(class);
+ pr_err("%s: creating class failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = register_chrdev(0, "adf", &adf_fops);
+ if (ret < 0) {
+ pr_err("%s: registering device failed: %d\n", __func__, ret);
+ goto err_chrdev;
+ }
+
+ class->devnode = adf_devnode;
+ adf_class = class;
+ adf_major = ret;
+ return 0;
+
+err_chrdev:
+ class_destroy(adf_class);
+ return ret;
+}
+
+void adf_sysfs_destroy(void)
+{
+ idr_destroy(&adf_minors);
+ class_destroy(adf_class);
+}
diff --git a/drivers/video/adf/adf_sysfs.h b/drivers/video/adf/adf_sysfs.h
new file mode 100644
index 000000000000..0613ac364f8d
--- /dev/null
+++ b/drivers/video/adf/adf_sysfs.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __VIDEO_ADF_ADF_SYSFS_H
+#define __VIDEO_ADF_ADF_SYSFS_H
+
+struct adf_device;
+struct adf_interface;
+struct adf_overlay_engine;
+
+int adf_device_sysfs_init(struct adf_device *dev);
+void adf_device_sysfs_destroy(struct adf_device *dev);
+int adf_interface_sysfs_init(struct adf_interface *intf);
+void adf_interface_sysfs_destroy(struct adf_interface *intf);
+int adf_overlay_engine_sysfs_init(struct adf_overlay_engine *eng);
+void adf_overlay_engine_sysfs_destroy(struct adf_overlay_engine *eng);
+struct adf_obj *adf_obj_sysfs_find(int minor);
+
+int adf_sysfs_init(void);
+void adf_sysfs_destroy(void);
+
+#endif /* __VIDEO_ADF_ADF_SYSFS_H */
diff --git a/drivers/video/adf/adf_trace.h b/drivers/video/adf/adf_trace.h
new file mode 100644
index 000000000000..3cb2a84d728c
--- /dev/null
+++ b/drivers/video/adf/adf_trace.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM adf
+
+#if !defined(__VIDEO_ADF_ADF_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __VIDEO_ADF_ADF_TRACE_H
+
+#include <linux/tracepoint.h>
+#include <video/adf.h>
+
+TRACE_EVENT(adf_event,
+ TP_PROTO(struct adf_obj *obj, enum adf_event_type type),
+ TP_ARGS(obj, type),
+
+ TP_STRUCT__entry(
+ __string(name, obj->name)
+ __field(enum adf_event_type, type)
+ __array(char, type_str, 32)
+ ),
+ TP_fast_assign(
+ __assign_str(name, obj->name);
+ __entry->type = type;
+ strlcpy(__entry->type_str, adf_event_type_str(obj, type),
+ sizeof(__entry->type_str));
+ ),
+ TP_printk("obj=%s type=%u (%s)",
+ __get_str(name),
+ __entry->type,
+ __entry->type_str)
+);
+
+TRACE_EVENT(adf_event_enable,
+ TP_PROTO(struct adf_obj *obj, enum adf_event_type type),
+ TP_ARGS(obj, type),
+
+ TP_STRUCT__entry(
+ __string(name, obj->name)
+ __field(enum adf_event_type, type)
+ __array(char, type_str, 32)
+ ),
+ TP_fast_assign(
+ __assign_str(name, obj->name);
+ __entry->type = type;
+ strlcpy(__entry->type_str, adf_event_type_str(obj, type),
+ sizeof(__entry->type_str));
+ ),
+ TP_printk("obj=%s type=%u (%s)",
+ __get_str(name),
+ __entry->type,
+ __entry->type_str)
+);
+
+TRACE_EVENT(adf_event_disable,
+ TP_PROTO(struct adf_obj *obj, enum adf_event_type type),
+ TP_ARGS(obj, type),
+
+ TP_STRUCT__entry(
+ __string(name, obj->name)
+ __field(enum adf_event_type, type)
+ __array(char, type_str, 32)
+ ),
+ TP_fast_assign(
+ __assign_str(name, obj->name);
+ __entry->type = type;
+ strlcpy(__entry->type_str, adf_event_type_str(obj, type),
+ sizeof(__entry->type_str));
+ ),
+ TP_printk("obj=%s type=%u (%s)",
+ __get_str(name),
+ __entry->type,
+ __entry->type_str)
+);
+
+#endif /* __VIDEO_ADF_ADF_TRACE_H */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE adf_trace
+#include <trace/define_trace.h>
diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c
index 700cac067b46..bdc515f5e979 100644
--- a/drivers/video/au1100fb.c
+++ b/drivers/video/au1100fb.c
@@ -361,39 +361,13 @@ void au1100fb_fb_rotate(struct fb_info *fbi, int angle)
int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
{
struct au1100fb_device *fbdev;
- unsigned int len;
- unsigned long start=0, off;
fbdev = to_au1100fb_device(fbi);
- if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
- return -EINVAL;
- }
-
- start = fbdev->fb_phys & PAGE_MASK;
- len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len);
-
- off = vma->vm_pgoff << PAGE_SHIFT;
-
- if ((vma->vm_end - vma->vm_start + off) > len) {
- return -EINVAL;
- }
-
- off += start;
- vma->vm_pgoff = off >> PAGE_SHIFT;
-
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6
- vma->vm_flags |= VM_IO;
-
- if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start,
- vma->vm_page_prot)) {
- return -EAGAIN;
- }
-
- return 0;
+ return vm_iomap_memory(vma, fbdev->fb_phys, fbdev->fb_len);
}
static struct fb_ops au1100fb_ops =
diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c
index 1b59054fc6a4..1d02897d17f2 100644
--- a/drivers/video/au1200fb.c
+++ b/drivers/video/au1200fb.c
@@ -1233,38 +1233,13 @@ static int au1200fb_fb_blank(int blank_mode, struct fb_info *fbi)
* method mainly to allow the use of the TLB streaming flag (CCA=6)
*/
static int au1200fb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
-
{
- unsigned int len;
- unsigned long start=0, off;
struct au1200fb_device *fbdev = info->par;
- if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
- return -EINVAL;
- }
-
- start = fbdev->fb_phys & PAGE_MASK;
- len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len);
-
- off = vma->vm_pgoff << PAGE_SHIFT;
-
- if ((vma->vm_end - vma->vm_start + off) > len) {
- return -EINVAL;
- }
-
- off += start;
- vma->vm_pgoff = off >> PAGE_SHIFT;
-
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
pgprot_val(vma->vm_page_prot) |= _CACHE_MASK; /* CCA=7 */
- vma->vm_flags |= VM_IO;
-
- return io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start,
- vma->vm_page_prot);
-
- return 0;
+ return vm_iomap_memory(vma, fbdev->fb_phys, fbdev->fb_len);
}
static void set_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata)
diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c
index a60d6afca97c..30e4ed52d701 100644
--- a/drivers/video/backlight/atmel-pwm-bl.c
+++ b/drivers/video/backlight/atmel-pwm-bl.c
@@ -118,7 +118,7 @@ static const struct backlight_ops atmel_pwm_bl_ops = {
.update_status = atmel_pwm_bl_set_intensity,
};
-static int __init atmel_pwm_bl_probe(struct platform_device *pdev)
+static int atmel_pwm_bl_probe(struct platform_device *pdev)
{
struct backlight_properties props;
const struct atmel_pwm_bl_platform_data *pdata;
@@ -203,7 +203,7 @@ err_free_mem:
return retval;
}
-static int __exit atmel_pwm_bl_remove(struct platform_device *pdev)
+static int atmel_pwm_bl_remove(struct platform_device *pdev)
{
struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev);
@@ -222,10 +222,11 @@ static struct platform_driver atmel_pwm_bl_driver = {
.name = "atmel-pwm-bl",
},
/* REVISIT add suspend() and resume() */
- .remove = __exit_p(atmel_pwm_bl_remove),
+ .probe = atmel_pwm_bl_probe,
+ .remove = atmel_pwm_bl_remove,
};
-module_platform_driver_probe(atmel_pwm_bl_driver, atmel_pwm_bl_probe);
+module_platform_driver(atmel_pwm_bl_driver);
MODULE_AUTHOR("Hans-Christian egtvedt <hans-christian.egtvedt@atmel.com>");
MODULE_DESCRIPTION("Atmel PWM backlight driver");
diff --git a/drivers/video/hyperv_fb.c b/drivers/video/hyperv_fb.c
index d4d2c5fe2488..0f3b33cf13ef 100644
--- a/drivers/video/hyperv_fb.c
+++ b/drivers/video/hyperv_fb.c
@@ -795,12 +795,21 @@ static int hvfb_remove(struct hv_device *hdev)
}
+static DEFINE_PCI_DEVICE_TABLE(pci_stub_id_table) = {
+ {
+ .vendor = PCI_VENDOR_ID_MICROSOFT,
+ .device = PCI_DEVICE_ID_HYPERV_VIDEO,
+ },
+ { /* end of list */ }
+};
+
static const struct hv_vmbus_device_id id_table[] = {
/* Synthetic Video Device GUID */
{HV_SYNTHVID_GUID},
{}
};
+MODULE_DEVICE_TABLE(pci, pci_stub_id_table);
MODULE_DEVICE_TABLE(vmbus, id_table);
static struct hv_driver hvfb_drv = {
@@ -810,14 +819,43 @@ static struct hv_driver hvfb_drv = {
.remove = hvfb_remove,
};
+static int hvfb_pci_stub_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ return 0;
+}
+
+static void hvfb_pci_stub_remove(struct pci_dev *pdev)
+{
+}
+
+static struct pci_driver hvfb_pci_stub_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = pci_stub_id_table,
+ .probe = hvfb_pci_stub_probe,
+ .remove = hvfb_pci_stub_remove,
+};
static int __init hvfb_drv_init(void)
{
- return vmbus_driver_register(&hvfb_drv);
+ int ret;
+
+ ret = vmbus_driver_register(&hvfb_drv);
+ if (ret != 0)
+ return ret;
+
+ ret = pci_register_driver(&hvfb_pci_stub_driver);
+ if (ret != 0) {
+ vmbus_driver_unregister(&hvfb_drv);
+ return ret;
+ }
+
+ return 0;
}
static void __exit hvfb_drv_exit(void)
{
+ pci_unregister_driver(&hvfb_pci_stub_driver);
vmbus_driver_unregister(&hvfb_drv);
}
diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c
index b8a92459f10f..9ad2bd344eb4 100644
--- a/drivers/watchdog/ts72xx_wdt.c
+++ b/drivers/watchdog/ts72xx_wdt.c
@@ -310,7 +310,8 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
+ error = put_user(0, p);
+ break;
case WDIOC_KEEPALIVE:
ts72xx_wdt_kick(wdt);
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 04c1b2d9b775..d5418c1be9aa 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -729,9 +729,18 @@ void gnttab_request_free_callback(struct gnttab_free_callback *callback,
void (*fn)(void *), void *arg, u16 count)
{
unsigned long flags;
+ struct gnttab_free_callback *cb;
+
spin_lock_irqsave(&gnttab_list_lock, flags);
- if (callback->next)
- goto out;
+
+ /* Check if the callback is already on the list */
+ cb = gnttab_free_callback_list;
+ while (cb) {
+ if (cb == callback)
+ goto out;
+ cb = cb->next;
+ }
+
callback->fn = fn;
callback->arg = arg;
callback->count = count;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index f8a0b0efda44..3aac8e9edac3 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1415,7 +1415,7 @@ static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata,
* long file_ofs
* followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
*/
-static void fill_files_note(struct memelfnote *note)
+static int fill_files_note(struct memelfnote *note)
{
struct vm_area_struct *vma;
unsigned count, size, names_ofs, remaining, n;
@@ -1430,11 +1430,11 @@ static void fill_files_note(struct memelfnote *note)
names_ofs = (2 + 3 * count) * sizeof(data[0]);
alloc:
if (size >= MAX_FILE_NOTE_SIZE) /* paranoia check */
- goto err;
+ return -EINVAL;
size = round_up(size, PAGE_SIZE);
data = vmalloc(size);
if (!data)
- goto err;
+ return -ENOMEM;
start_end_ofs = data + 2;
name_base = name_curpos = ((char *)data) + names_ofs;
@@ -1487,7 +1487,7 @@ static void fill_files_note(struct memelfnote *note)
size = name_curpos - (char *)data;
fill_note(note, "CORE", NT_FILE, size, data);
- err: ;
+ return 0;
}
#ifdef CORE_DUMP_USE_REGSET
@@ -1688,8 +1688,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
fill_auxv_note(&info->auxv, current->mm);
info->size += notesize(&info->auxv);
- fill_files_note(&info->files);
- info->size += notesize(&info->files);
+ if (fill_files_note(&info->files) == 0)
+ info->size += notesize(&info->files);
return 1;
}
@@ -1721,7 +1721,8 @@ static int write_note_info(struct elf_note_info *info,
return 0;
if (first && !writenote(&info->auxv, file, foffset))
return 0;
- if (first && !writenote(&info->files, file, foffset))
+ if (first && info->files.data &&
+ !writenote(&info->files, file, foffset))
return 0;
for (i = 1; i < info->thread_notes; ++i)
@@ -1808,6 +1809,7 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
struct elf_note_info {
struct memelfnote *notes;
+ struct memelfnote *notes_files;
struct elf_prstatus *prstatus; /* NT_PRSTATUS */
struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */
struct list_head thread_list;
@@ -1898,9 +1900,12 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
fill_siginfo_note(info->notes + 2, &info->csigdata, siginfo);
fill_auxv_note(info->notes + 3, current->mm);
- fill_files_note(info->notes + 4);
+ info->numnote = 4;
- info->numnote = 5;
+ if (fill_files_note(info->notes + info->numnote) == 0) {
+ info->notes_files = info->notes + info->numnote;
+ info->numnote++;
+ }
/* Try to dump the FPU. */
info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs,
@@ -1962,8 +1967,9 @@ static void free_note_info(struct elf_note_info *info)
kfree(list_entry(tmp, struct elf_thread_status, list));
}
- /* Free data allocated by fill_files_note(): */
- vfree(info->notes[4].data);
+ /* Free data possibly allocated by fill_files_note(): */
+ if (info->notes_files)
+ vfree(info->notes_files->data);
kfree(info->prstatus);
kfree(info->psinfo);
@@ -2046,7 +2052,7 @@ static int elf_core_dump(struct coredump_params *cprm)
struct vm_area_struct *vma, *gate_vma;
struct elfhdr *elf = NULL;
loff_t offset = 0, dataoff, foffset;
- struct elf_note_info info;
+ struct elf_note_info info = { };
struct elf_phdr *phdr4note = NULL;
struct elf_shdr *shdr4extnum = NULL;
Elf_Half e_phnum;
diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c
index 8fb42916d8a2..45e944fe52a6 100644
--- a/fs/bio-integrity.c
+++ b/fs/bio-integrity.c
@@ -734,7 +734,7 @@ void bioset_integrity_free(struct bio_set *bs)
mempool_destroy(bs->bio_integrity_pool);
if (bs->bvec_integrity_pool)
- mempool_destroy(bs->bio_integrity_pool);
+ mempool_destroy(bs->bvec_integrity_pool);
}
EXPORT_SYMBOL(bioset_integrity_free);
diff --git a/fs/bio.c b/fs/bio.c
index c5eae7251490..5e7507d79297 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -917,8 +917,8 @@ void bio_copy_data(struct bio *dst, struct bio *src)
src_p = kmap_atomic(src_bv->bv_page);
dst_p = kmap_atomic(dst_bv->bv_page);
- memcpy(dst_p + dst_bv->bv_offset,
- src_p + src_bv->bv_offset,
+ memcpy(dst_p + dst_offset,
+ src_p + src_offset,
bytes);
kunmap_atomic(dst_p);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 0b272d068337..3b6d20bc2388 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -2402,6 +2402,8 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
default:
WARN_ON(1);
}
+ } else {
+ list_del_init(&locked_ref->cluster);
}
spin_unlock(&delayed_refs->lock);
@@ -2424,7 +2426,6 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
* list before we release it.
*/
if (btrfs_delayed_ref_is_head(ref)) {
- list_del_init(&locked_ref->cluster);
btrfs_delayed_ref_unlock(locked_ref);
locked_ref = NULL;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 17f3064b4a3e..1e2288dc5346 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -8146,7 +8146,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
/* check for collisions, even if the name isn't there */
- ret = btrfs_check_dir_item_collision(root, new_dir->i_ino,
+ ret = btrfs_check_dir_item_collision(dest, new_dir->i_ino,
new_dentry->d_name.name,
new_dentry->d_name.len);
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 0f81d67cdc8d..8dedf4019672 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3299,6 +3299,9 @@ static long btrfs_ioctl_dev_replace(struct btrfs_root *root, void __user *arg)
switch (p->cmd) {
case BTRFS_IOCTL_DEV_REPLACE_CMD_START:
+ if (root->fs_info->sb->s_flags & MS_RDONLY)
+ return -EROFS;
+
if (atomic_xchg(
&root->fs_info->mutually_exclusive_operation_running,
1)) {
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 4febca4fc2de..b3896d5f233a 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -691,6 +691,7 @@ struct backref_node *build_backref_tree(struct reloc_control *rc,
int cowonly;
int ret;
int err = 0;
+ bool need_check = true;
path1 = btrfs_alloc_path();
path2 = btrfs_alloc_path();
@@ -914,6 +915,7 @@ again:
cur->bytenr);
lower = cur;
+ need_check = true;
for (; level < BTRFS_MAX_LEVEL; level++) {
if (!path2->nodes[level]) {
BUG_ON(btrfs_root_bytenr(&root->root_item) !=
@@ -957,14 +959,12 @@ again:
/*
* add the block to pending list if we
- * need check its backrefs. only block
- * at 'cur->level + 1' is added to the
- * tail of pending list. this guarantees
- * we check backrefs from lower level
- * blocks to upper level blocks.
+ * need check its backrefs, we only do this once
+ * while walking up a tree as we will catch
+ * anything else later on.
*/
- if (!upper->checked &&
- level == cur->level + 1) {
+ if (!upper->checked && need_check) {
+ need_check = false;
list_add_tail(&edge->list[UPPER],
&list);
} else
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index ba9690b9ae24..09ea0bdde65f 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -2524,7 +2524,8 @@ static int did_create_dir(struct send_ctx *sctx, u64 dir)
di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
btrfs_dir_item_key_to_cpu(eb, di, &di_key);
- if (di_key.objectid < sctx->send_progress) {
+ if (di_key.type != BTRFS_ROOT_ITEM_KEY &&
+ di_key.objectid < sctx->send_progress) {
ret = 1;
goto out;
}
diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
index e0b4ef31d3c8..a5ce62eb7806 100644
--- a/fs/ceph/ioctl.c
+++ b/fs/ceph/ioctl.c
@@ -196,8 +196,10 @@ static long ceph_ioctl_get_dataloc(struct file *file, void __user *arg)
r = ceph_calc_file_object_mapping(&ci->i_layout, dl.file_offset, len,
&dl.object_no, &dl.object_offset,
&olen);
- if (r < 0)
+ if (r < 0) {
+ up_read(&osdc->map_sem);
return -EIO;
+ }
dl.file_offset -= dl.object_offset;
dl.object_size = ceph_file_layout_object_size(ci->i_layout);
dl.block_size = ceph_file_layout_su(ci->i_layout);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index d6a5c5ac737b..d05a30072023 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -377,6 +377,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
try_to_freeze();
/* we should try only the port we connected to before */
+ mutex_lock(&server->srv_mutex);
rc = generic_ip_connect(server);
if (rc) {
cifs_dbg(FYI, "reconnect error %d\n", rc);
@@ -388,6 +389,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
server->tcpStatus = CifsNeedNegotiate;
spin_unlock(&GlobalMid_Lock);
}
+ mutex_unlock(&server->srv_mutex);
} while (server->tcpStatus == CifsNeedReconnect);
return rc;
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 5699b5036ed8..0c2425b21974 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -491,6 +491,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
if (server->ops->close)
server->ops->close(xid, tcon, &fid);
cifs_del_pending_open(&open);
+ fput(file);
rc = -ENOMEM;
}
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 10383d8c015b..4f791e0e98d7 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -413,96 +413,108 @@ cifs_ses_oplock_break(struct work_struct *work)
}
static bool
-smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
+smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
+ struct smb2_lease_break_work *lw)
{
- struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer;
- struct list_head *tmp, *tmp1, *tmp2;
- struct cifs_ses *ses;
- struct cifs_tcon *tcon;
- struct cifsInodeInfo *cinode;
+ bool found;
+ __u8 lease_state;
+ struct list_head *tmp;
struct cifsFileInfo *cfile;
struct cifs_pending_open *open;
- struct smb2_lease_break_work *lw;
- bool found;
+ struct cifsInodeInfo *cinode;
int ack_req = le32_to_cpu(rsp->Flags &
SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
- lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
- if (!lw)
- return false;
+ lease_state = smb2_map_lease_to_oplock(rsp->NewLeaseState);
- INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
- lw->lease_state = rsp->NewLeaseState;
+ list_for_each(tmp, &tcon->openFileList) {
+ cfile = list_entry(tmp, struct cifsFileInfo, tlist);
+ cinode = CIFS_I(cfile->dentry->d_inode);
- cifs_dbg(FYI, "Checking for lease break\n");
+ if (memcmp(cinode->lease_key, rsp->LeaseKey,
+ SMB2_LEASE_KEY_SIZE))
+ continue;
- /* look up tcon based on tid & uid */
- spin_lock(&cifs_tcp_ses_lock);
- list_for_each(tmp, &server->smb_ses_list) {
- ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+ cifs_dbg(FYI, "found in the open list\n");
+ cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
+ le32_to_cpu(rsp->NewLeaseState));
- spin_lock(&cifs_file_list_lock);
- list_for_each(tmp1, &ses->tcon_list) {
- tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
+ smb2_set_oplock_level(cinode, lease_state);
- cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
- list_for_each(tmp2, &tcon->openFileList) {
- cfile = list_entry(tmp2, struct cifsFileInfo,
- tlist);
- cinode = CIFS_I(cfile->dentry->d_inode);
+ if (ack_req)
+ cfile->oplock_break_cancelled = false;
+ else
+ cfile->oplock_break_cancelled = true;
- if (memcmp(cinode->lease_key, rsp->LeaseKey,
- SMB2_LEASE_KEY_SIZE))
- continue;
+ queue_work(cifsiod_wq, &cfile->oplock_break);
+ kfree(lw);
+ return true;
+ }
- cifs_dbg(FYI, "found in the open list\n");
- cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
- le32_to_cpu(rsp->NewLeaseState));
+ found = false;
+ list_for_each_entry(open, &tcon->pending_opens, olist) {
+ if (memcmp(open->lease_key, rsp->LeaseKey,
+ SMB2_LEASE_KEY_SIZE))
+ continue;
+
+ if (!found && ack_req) {
+ found = true;
+ memcpy(lw->lease_key, open->lease_key,
+ SMB2_LEASE_KEY_SIZE);
+ lw->tlink = cifs_get_tlink(open->tlink);
+ queue_work(cifsiod_wq, &lw->lease_break);
+ }
- smb2_set_oplock_level(cinode,
- smb2_map_lease_to_oplock(rsp->NewLeaseState));
+ cifs_dbg(FYI, "found in the pending open list\n");
+ cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
+ le32_to_cpu(rsp->NewLeaseState));
- if (ack_req)
- cfile->oplock_break_cancelled = false;
- else
- cfile->oplock_break_cancelled = true;
+ open->oplock = lease_state;
+ }
+ return found;
+}
- queue_work(cifsiod_wq, &cfile->oplock_break);
+static bool
+smb2_is_valid_lease_break(char *buffer)
+{
+ struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer;
+ struct list_head *tmp, *tmp1, *tmp2;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ struct smb2_lease_break_work *lw;
- spin_unlock(&cifs_file_list_lock);
- spin_unlock(&cifs_tcp_ses_lock);
- return true;
- }
+ lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
+ if (!lw)
+ return false;
- found = false;
- list_for_each_entry(open, &tcon->pending_opens, olist) {
- if (memcmp(open->lease_key, rsp->LeaseKey,
- SMB2_LEASE_KEY_SIZE))
- continue;
+ INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
+ lw->lease_state = rsp->NewLeaseState;
- if (!found && ack_req) {
- found = true;
- memcpy(lw->lease_key, open->lease_key,
- SMB2_LEASE_KEY_SIZE);
- lw->tlink = cifs_get_tlink(open->tlink);
- queue_work(cifsiod_wq,
- &lw->lease_break);
- }
+ cifs_dbg(FYI, "Checking for lease break\n");
+
+ /* look up tcon based on tid & uid */
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &cifs_tcp_ses_list) {
+ server = list_entry(tmp, struct TCP_Server_Info, tcp_ses_list);
- cifs_dbg(FYI, "found in the pending open list\n");
- cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
- le32_to_cpu(rsp->NewLeaseState));
+ list_for_each(tmp1, &server->smb_ses_list) {
+ ses = list_entry(tmp1, struct cifs_ses, smb_ses_list);
- open->oplock =
- smb2_map_lease_to_oplock(rsp->NewLeaseState);
- }
- if (found) {
- spin_unlock(&cifs_file_list_lock);
- spin_unlock(&cifs_tcp_ses_lock);
- return true;
+ spin_lock(&cifs_file_list_lock);
+ list_for_each(tmp2, &ses->tcon_list) {
+ tcon = list_entry(tmp2, struct cifs_tcon,
+ tcon_list);
+ cifs_stats_inc(
+ &tcon->stats.cifs_stats.num_oplock_brks);
+ if (smb2_tcon_has_lease(tcon, rsp, lw)) {
+ spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
}
+ spin_unlock(&cifs_file_list_lock);
}
- spin_unlock(&cifs_file_list_lock);
}
spin_unlock(&cifs_tcp_ses_lock);
kfree(lw);
@@ -528,7 +540,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
if (rsp->StructureSize !=
smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
if (le16_to_cpu(rsp->StructureSize) == 44)
- return smb2_is_valid_lease_break(buffer, server);
+ return smb2_is_valid_lease_break(buffer);
else
return false;
}
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index 7d52806c2119..4725a07f003c 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -1149,7 +1149,7 @@ decrypt_pki_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok,
struct ecryptfs_msg_ctx *msg_ctx;
struct ecryptfs_message *msg = NULL;
char *auth_tok_sig;
- char *payload;
+ char *payload = NULL;
size_t payload_len = 0;
int rc;
@@ -1203,6 +1203,7 @@ decrypt_pki_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok,
}
out:
kfree(msg);
+ kfree(payload);
return rc;
}
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index ccbfbbb12dc5..904ca1a21dce 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4706,7 +4706,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
ext4_journal_stop(handle);
}
- if (attr->ia_valid & ATTR_SIZE) {
+ if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) {
+ handle_t *handle;
+ loff_t oldsize = inode->i_size;
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
@@ -4714,73 +4716,60 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
if (attr->ia_size > sbi->s_bitmap_maxbytes)
return -EFBIG;
}
- }
-
- if (S_ISREG(inode->i_mode) &&
- attr->ia_valid & ATTR_SIZE &&
- (attr->ia_size < inode->i_size)) {
- handle_t *handle;
-
- handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
- if (IS_ERR(handle)) {
- error = PTR_ERR(handle);
- goto err_out;
- }
- if (ext4_handle_valid(handle)) {
- error = ext4_orphan_add(handle, inode);
- orphan = 1;
- }
- EXT4_I(inode)->i_disksize = attr->ia_size;
- rc = ext4_mark_inode_dirty(handle, inode);
- if (!error)
- error = rc;
- ext4_journal_stop(handle);
-
- if (ext4_should_order_data(inode)) {
- error = ext4_begin_ordered_truncate(inode,
+ if (S_ISREG(inode->i_mode) &&
+ (attr->ia_size < inode->i_size)) {
+ if (ext4_should_order_data(inode)) {
+ error = ext4_begin_ordered_truncate(inode,
attr->ia_size);
- if (error) {
- /* Do as much error cleanup as possible */
- handle = ext4_journal_start(inode,
- EXT4_HT_INODE, 3);
- if (IS_ERR(handle)) {
- ext4_orphan_del(NULL, inode);
+ if (error)
goto err_out;
- }
- ext4_orphan_del(handle, inode);
- orphan = 0;
- ext4_journal_stop(handle);
+ }
+ handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
+ if (IS_ERR(handle)) {
+ error = PTR_ERR(handle);
+ goto err_out;
+ }
+ if (ext4_handle_valid(handle)) {
+ error = ext4_orphan_add(handle, inode);
+ orphan = 1;
+ }
+ EXT4_I(inode)->i_disksize = attr->ia_size;
+ rc = ext4_mark_inode_dirty(handle, inode);
+ if (!error)
+ error = rc;
+ ext4_journal_stop(handle);
+ if (error) {
+ ext4_orphan_del(NULL, inode);
goto err_out;
}
}
- }
-
- if (attr->ia_valid & ATTR_SIZE) {
- if (attr->ia_size != inode->i_size) {
- loff_t oldsize = inode->i_size;
- i_size_write(inode, attr->ia_size);
- /*
- * Blocks are going to be removed from the inode. Wait
- * for dio in flight. Temporarily disable
- * dioread_nolock to prevent livelock.
- */
- if (orphan) {
- if (!ext4_should_journal_data(inode)) {
- ext4_inode_block_unlocked_dio(inode);
- inode_dio_wait(inode);
- ext4_inode_resume_unlocked_dio(inode);
- } else
- ext4_wait_for_tail_page_commit(inode);
- }
- /*
- * Truncate pagecache after we've waited for commit
- * in data=journal mode to make pages freeable.
- */
- truncate_pagecache(inode, oldsize, inode->i_size);
+ i_size_write(inode, attr->ia_size);
+ /*
+ * Blocks are going to be removed from the inode. Wait
+ * for dio in flight. Temporarily disable
+ * dioread_nolock to prevent livelock.
+ */
+ if (orphan) {
+ if (!ext4_should_journal_data(inode)) {
+ ext4_inode_block_unlocked_dio(inode);
+ inode_dio_wait(inode);
+ ext4_inode_resume_unlocked_dio(inode);
+ } else
+ ext4_wait_for_tail_page_commit(inode);
}
- ext4_truncate(inode);
+ /*
+ * Truncate pagecache after we've waited for commit
+ * in data=journal mode to make pages freeable.
+ */
+ truncate_pagecache(inode, oldsize, inode->i_size);
}
+ /*
+ * We want to call ext4_truncate() even if attr->ia_size ==
+ * inode->i_size for cases like truncation of fallocated space
+ */
+ if (attr->ia_valid & ATTR_SIZE)
+ ext4_truncate(inode);
if (!rc) {
setattr_copy(inode, attr);
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index c081e34f717f..03e9bebba198 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -1350,6 +1350,8 @@ retry:
s_min_extra_isize) {
tried_min_extra_isize++;
new_extra_isize = s_min_extra_isize;
+ kfree(is); is = NULL;
+ kfree(bs); bs = NULL;
goto retry;
}
error = -1;
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 5b1274699b08..e67b13de2ebc 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1175,6 +1175,8 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
return -EIO;
if (reclen > nbytes)
break;
+ if (memchr(dirent->name, '/', dirent->namelen) != NULL)
+ return -EIO;
over = filldir(dstbuf, dirent->name, dirent->namelen,
file->f_pos, dirent->ino, dirent->type);
@@ -1323,6 +1325,8 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
return -EIO;
if (reclen > nbytes)
break;
+ if (memchr(dirent->name, '/', dirent->namelen) != NULL)
+ return -EIO;
if (!over) {
/* We fill entries into dstbuf only as much as
@@ -1594,6 +1598,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
struct file *file)
{
struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_req *req;
struct fuse_setattr_in inarg;
struct fuse_attr_out outarg;
@@ -1621,8 +1626,10 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
if (IS_ERR(req))
return PTR_ERR(req);
- if (is_truncate)
+ if (is_truncate) {
fuse_set_nowrite(inode);
+ set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
+ }
memset(&inarg, 0, sizeof(inarg));
memset(&outarg, 0, sizeof(outarg));
@@ -1684,12 +1691,14 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
invalidate_inode_pages2(inode->i_mapping);
}
+ clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
return 0;
error:
if (is_truncate)
fuse_release_nowrite(inode);
+ clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
return err;
}
@@ -1753,6 +1762,8 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
fc->no_setxattr = 1;
err = -EOPNOTSUPP;
}
+ if (!err)
+ fuse_invalidate_attr(inode);
return err;
}
@@ -1882,6 +1893,8 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
fc->no_removexattr = 1;
err = -EOPNOTSUPP;
}
+ if (!err)
+ fuse_invalidate_attr(inode);
return err;
}
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 35f281033142..4fafb8484bbc 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -630,7 +630,8 @@ static void fuse_read_update_size(struct inode *inode, loff_t size,
struct fuse_inode *fi = get_fuse_inode(inode);
spin_lock(&fc->lock);
- if (attr_ver == fi->attr_version && size < inode->i_size) {
+ if (attr_ver == fi->attr_version && size < inode->i_size &&
+ !test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) {
fi->attr_version = ++fc->attr_version;
i_size_write(inode, size);
}
@@ -1033,12 +1034,16 @@ static ssize_t fuse_perform_write(struct file *file,
{
struct inode *inode = mapping->host;
struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_inode *fi = get_fuse_inode(inode);
int err = 0;
ssize_t res = 0;
if (is_bad_inode(inode))
return -EIO;
+ if (inode->i_size < pos + iov_iter_count(ii))
+ set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
+
do {
struct fuse_req *req;
ssize_t count;
@@ -1074,6 +1079,7 @@ static ssize_t fuse_perform_write(struct file *file,
if (res > 0)
fuse_write_update_size(inode, pos);
+ clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
fuse_invalidate_attr(inode);
return res > 0 ? res : err;
@@ -1530,7 +1536,6 @@ static int fuse_writepage_locked(struct page *page)
inc_bdi_stat(mapping->backing_dev_info, BDI_WRITEBACK);
inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP);
- end_page_writeback(page);
spin_lock(&fc->lock);
list_add(&req->writepages_entry, &fi->writepages);
@@ -1538,6 +1543,8 @@ static int fuse_writepage_locked(struct page *page)
fuse_flush_writepages(inode);
spin_unlock(&fc->lock);
+ end_page_writeback(page);
+
return 0;
err_free:
@@ -2461,6 +2468,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
{
struct fuse_file *ff = file->private_data;
struct inode *inode = file->f_inode;
+ struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_conn *fc = ff->fc;
struct fuse_req *req;
struct fuse_fallocate_in inarg = {
@@ -2478,10 +2486,20 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
if (lock_inode) {
mutex_lock(&inode->i_mutex);
- if (mode & FALLOC_FL_PUNCH_HOLE)
- fuse_set_nowrite(inode);
+ if (mode & FALLOC_FL_PUNCH_HOLE) {
+ loff_t endbyte = offset + length - 1;
+ err = filemap_write_and_wait_range(inode->i_mapping,
+ offset, endbyte);
+ if (err)
+ goto out;
+
+ fuse_sync_writes(inode);
+ }
}
+ if (!(mode & FALLOC_FL_KEEP_SIZE))
+ set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
+
req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -2514,11 +2532,11 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
fuse_invalidate_attr(inode);
out:
- if (lock_inode) {
- if (mode & FALLOC_FL_PUNCH_HOLE)
- fuse_release_nowrite(inode);
+ if (!(mode & FALLOC_FL_KEEP_SIZE))
+ clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
+
+ if (lock_inode)
mutex_unlock(&inode->i_mutex);
- }
return err;
}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index fde7249a3a96..5ced199b50bb 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -115,6 +115,8 @@ struct fuse_inode {
enum {
/** Advise readdirplus */
FUSE_I_ADVISE_RDPLUS,
+ /** An operation changing file size is in progress */
+ FUSE_I_SIZE_UNSTABLE,
};
struct fuse_conn;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 9a0cdde14a08..b5718516825b 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -201,7 +201,8 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
struct timespec old_mtime;
spin_lock(&fc->lock);
- if (attr_version != 0 && fi->attr_version > attr_version) {
+ if ((attr_version != 0 && fi->attr_version > attr_version) ||
+ test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) {
spin_unlock(&fc->lock);
return;
}
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index d9b8aebdeb22..d3705490ff9c 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -125,8 +125,8 @@ static void destroy_inodecache(void)
static int isofs_remount(struct super_block *sb, int *flags, char *data)
{
- /* we probably want a lot more here */
- *flags |= MS_RDONLY;
+ if (!(*flags & MS_RDONLY))
+ return -EROFS;
return 0;
}
@@ -779,15 +779,6 @@ root_found:
*/
s->s_maxbytes = 0x80000000000LL;
- /*
- * The CDROM is read-only, has no nodes (devices) on it, and since
- * all of the files appear to be owned by root, we really do not want
- * to allow suid. (suid or devices will not show up unless we have
- * Rock Ridge extensions)
- */
-
- s->s_flags |= MS_RDONLY /* | MS_NODEV | MS_NOSUID */;
-
/* Set this for reference. Its not currently used except on write
which we don't have .. */
@@ -1546,6 +1537,9 @@ struct inode *isofs_iget(struct super_block *sb,
static struct dentry *isofs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
+ /* We don't support read-write mounts */
+ if (!(flags & MS_RDONLY))
+ return ERR_PTR(-EACCES);
return mount_bdev(fs_type, flags, dev_name, data, isofs_fill_super);
}
diff --git a/fs/jfs/jfs_inode.c b/fs/jfs/jfs_inode.c
index c1a3e603279c..7f464c513ba0 100644
--- a/fs/jfs/jfs_inode.c
+++ b/fs/jfs/jfs_inode.c
@@ -95,7 +95,7 @@ struct inode *ialloc(struct inode *parent, umode_t mode)
if (insert_inode_locked(inode) < 0) {
rc = -EINVAL;
- goto fail_unlock;
+ goto fail_put;
}
inode_init_owner(inode, parent, mode);
@@ -156,7 +156,6 @@ struct inode *ialloc(struct inode *parent, umode_t mode)
fail_drop:
dquot_drop(inode);
inode->i_flags |= S_NOQUOTA;
-fail_unlock:
clear_nlink(inode);
unlock_new_inode(inode);
fail_put:
diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c
index 661a0f611215..678cb8964532 100644
--- a/fs/nfs/nfs4filelayoutdev.c
+++ b/fs/nfs/nfs4filelayoutdev.c
@@ -797,34 +797,34 @@ nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx)
struct nfs4_file_layout_dsaddr *dsaddr = FILELAYOUT_LSEG(lseg)->dsaddr;
struct nfs4_pnfs_ds *ds = dsaddr->ds_list[ds_idx];
struct nfs4_deviceid_node *devid = FILELAYOUT_DEVID_NODE(lseg);
-
- if (filelayout_test_devid_unavailable(devid))
- return NULL;
+ struct nfs4_pnfs_ds *ret = ds;
if (ds == NULL) {
printk(KERN_ERR "NFS: %s: No data server for offset index %d\n",
__func__, ds_idx);
filelayout_mark_devid_invalid(devid);
- return NULL;
+ goto out;
}
if (ds->ds_clp)
- return ds;
+ goto out_test_devid;
if (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) == 0) {
struct nfs_server *s = NFS_SERVER(lseg->pls_layout->plh_inode);
int err;
err = nfs4_ds_connect(s, ds);
- if (err) {
+ if (err)
nfs4_mark_deviceid_unavailable(devid);
- ds = NULL;
- }
nfs4_clear_ds_conn_bit(ds);
} else {
/* Either ds is connected, or ds is NULL */
nfs4_wait_ds_connect(ds);
}
- return ds;
+out_test_devid:
+ if (filelayout_test_devid_unavailable(devid))
+ ret = NULL;
+out:
+ return ret;
}
module_param(dataserver_retrans, uint, 0644);
diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
index 0ba679866e50..da276640f776 100644
--- a/fs/nilfs2/page.c
+++ b/fs/nilfs2/page.c
@@ -94,6 +94,7 @@ void nilfs_forget_buffer(struct buffer_head *bh)
clear_buffer_nilfs_volatile(bh);
clear_buffer_nilfs_checked(bh);
clear_buffer_nilfs_redirected(bh);
+ clear_buffer_async_write(bh);
clear_buffer_dirty(bh);
if (nilfs_page_buffers_clean(page))
__nilfs_clear_page_dirty(page);
@@ -429,6 +430,7 @@ void nilfs_clear_dirty_page(struct page *page, bool silent)
"discard block %llu, size %zu",
(u64)bh->b_blocknr, bh->b_size);
}
+ clear_buffer_async_write(bh);
clear_buffer_dirty(bh);
clear_buffer_nilfs_volatile(bh);
clear_buffer_nilfs_checked(bh);
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index a5752a589932..cbd66188a28b 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -665,7 +665,7 @@ static size_t nilfs_lookup_dirty_data_buffers(struct inode *inode,
bh = head = page_buffers(page);
do {
- if (!buffer_dirty(bh))
+ if (!buffer_dirty(bh) || buffer_async_write(bh))
continue;
get_bh(bh);
list_add_tail(&bh->b_assoc_buffers, listp);
@@ -699,7 +699,8 @@ static void nilfs_lookup_dirty_node_buffers(struct inode *inode,
for (i = 0; i < pagevec_count(&pvec); i++) {
bh = head = page_buffers(pvec.pages[i]);
do {
- if (buffer_dirty(bh)) {
+ if (buffer_dirty(bh) &&
+ !buffer_async_write(bh)) {
get_bh(bh);
list_add_tail(&bh->b_assoc_buffers,
listp);
@@ -1579,6 +1580,7 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci)
list_for_each_entry(bh, &segbuf->sb_segsum_buffers,
b_assoc_buffers) {
+ set_buffer_async_write(bh);
if (bh->b_page != bd_page) {
if (bd_page) {
lock_page(bd_page);
@@ -1592,6 +1594,7 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci)
list_for_each_entry(bh, &segbuf->sb_payload_buffers,
b_assoc_buffers) {
+ set_buffer_async_write(bh);
if (bh == segbuf->sb_super_root) {
if (bh->b_page != bd_page) {
lock_page(bd_page);
@@ -1677,6 +1680,7 @@ static void nilfs_abort_logs(struct list_head *logs, int err)
list_for_each_entry(segbuf, logs, sb_list) {
list_for_each_entry(bh, &segbuf->sb_segsum_buffers,
b_assoc_buffers) {
+ clear_buffer_async_write(bh);
if (bh->b_page != bd_page) {
if (bd_page)
end_page_writeback(bd_page);
@@ -1686,6 +1690,7 @@ static void nilfs_abort_logs(struct list_head *logs, int err)
list_for_each_entry(bh, &segbuf->sb_payload_buffers,
b_assoc_buffers) {
+ clear_buffer_async_write(bh);
if (bh == segbuf->sb_super_root) {
if (bh->b_page != bd_page) {
end_page_writeback(bd_page);
@@ -1755,6 +1760,7 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci)
b_assoc_buffers) {
set_buffer_uptodate(bh);
clear_buffer_dirty(bh);
+ clear_buffer_async_write(bh);
if (bh->b_page != bd_page) {
if (bd_page)
end_page_writeback(bd_page);
@@ -1776,6 +1782,7 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci)
b_assoc_buffers) {
set_buffer_uptodate(bh);
clear_buffer_dirty(bh);
+ clear_buffer_async_write(bh);
clear_buffer_delay(bh);
clear_buffer_nilfs_volatile(bh);
clear_buffer_nilfs_redirected(bh);
diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c
index 2487116d0d33..846064726682 100644
--- a/fs/ocfs2/extent_map.c
+++ b/fs/ocfs2/extent_map.c
@@ -781,7 +781,6 @@ int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
cpos = map_start >> osb->s_clustersize_bits;
mapping_end = ocfs2_clusters_for_bytes(inode->i_sb,
map_start + map_len);
- mapping_end -= cpos;
is_last = 0;
while (cpos < mapping_end && !is_last) {
u32 fe_flags;
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 41a6ea93f486..04ec276c7bab 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -110,7 +110,8 @@ static struct dentry *proc_mount(struct file_system_type *fs_type,
ns = task_active_pid_ns(current);
options = data;
- if (!current_user_ns()->may_mount_proc)
+ if (!current_user_ns()->may_mount_proc ||
+ !ns_capable(ns->user_ns, CAP_SYS_ADMIN))
return ERR_PTR(-EPERM);
}
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 65fc60a07c47..db17f98bc564 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -134,6 +134,56 @@ static void release_task_mempolicy(struct proc_maps_private *priv)
}
#endif
+static void seq_print_vma_name(struct seq_file *m, struct vm_area_struct *vma)
+{
+ const char __user *name = vma_get_anon_name(vma);
+ struct mm_struct *mm = vma->vm_mm;
+
+ unsigned long page_start_vaddr;
+ unsigned long page_offset;
+ unsigned long num_pages;
+ unsigned long max_len = NAME_MAX;
+ int i;
+
+ page_start_vaddr = (unsigned long)name & PAGE_MASK;
+ page_offset = (unsigned long)name - page_start_vaddr;
+ num_pages = DIV_ROUND_UP(page_offset + max_len, PAGE_SIZE);
+
+ seq_puts(m, "[anon:");
+
+ for (i = 0; i < num_pages; i++) {
+ int len;
+ int write_len;
+ const char *kaddr;
+ long pages_pinned;
+ struct page *page;
+
+ pages_pinned = get_user_pages(current, mm, page_start_vaddr,
+ 1, 0, 0, &page, NULL);
+ if (pages_pinned < 1) {
+ seq_puts(m, "<fault>]");
+ return;
+ }
+
+ kaddr = (const char *)kmap(page);
+ len = min(max_len, PAGE_SIZE - page_offset);
+ write_len = strnlen(kaddr + page_offset, len);
+ seq_write(m, kaddr + page_offset, write_len);
+ kunmap(page);
+ put_page(page);
+
+ /* if strnlen hit a null terminator then we're done */
+ if (write_len != len)
+ break;
+
+ max_len -= len;
+ page_offset = 0;
+ page_start_vaddr += PAGE_SIZE;
+ }
+
+ seq_putc(m, ']');
+}
+
static void vma_stop(struct proc_maps_private *priv, struct vm_area_struct *vma)
{
if (vma && vma != priv->tail_vma) {
@@ -335,6 +385,12 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
pad_len_spaces(m, len);
seq_printf(m, "[stack:%d]", tid);
}
+ goto done;
+ }
+
+ if (vma_get_anon_name(vma)) {
+ pad_len_spaces(m, len);
+ seq_print_vma_name(m, vma);
}
}
@@ -634,6 +690,12 @@ static int show_smap(struct seq_file *m, void *v, int is_pid)
show_smap_vma_flags(m, vma);
+ if (vma_get_anon_name(vma)) {
+ seq_puts(m, "Name: ");
+ seq_print_vma_name(m, vma);
+ seq_putc(m, '\n');
+ }
+
if (m->count < m->size) /* vma is copied successfully */
m->version = (vma != get_gate_vma(task->mm))
? vma->vm_start : 0;
diff --git a/fs/seq_file.c b/fs/seq_file.c
index 774c1eb7f1c9..3dd44db1465e 100644
--- a/fs/seq_file.c
+++ b/fs/seq_file.c
@@ -328,6 +328,8 @@ loff_t seq_lseek(struct file *file, loff_t offset, int whence)
m->read_pos = offset;
retval = file->f_pos = offset;
}
+ } else {
+ file->f_pos = offset;
}
}
file->f_version = m->version;
diff --git a/fs/statfs.c b/fs/statfs.c
index c219e733f553..083dc0ac9140 100644
--- a/fs/statfs.c
+++ b/fs/statfs.c
@@ -94,7 +94,7 @@ retry:
int fd_statfs(int fd, struct kstatfs *st)
{
- struct fd f = fdget(fd);
+ struct fd f = fdget_raw(fd);
int error = -EBADF;
if (f.file) {
error = vfs_statfs(&f.file->f_path, st);
diff --git a/fs/sysv/super.c b/fs/sysv/super.c
index d0c6a007ce83..eda10959714f 100644
--- a/fs/sysv/super.c
+++ b/fs/sysv/super.c
@@ -487,6 +487,7 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent)
sbi->s_sb = sb;
sbi->s_block_base = 0;
sbi->s_type = FSTYPE_V7;
+ mutex_init(&sbi->s_lock);
sb->s_fs_info = sbi;
sb_set_blocksize(sb, 512);
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 9ac4057a86c9..839a2bad7f45 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -630,6 +630,12 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
struct udf_sb_info *sbi = UDF_SB(sb);
int error = 0;
+ if (sbi->s_lvid_bh) {
+ int write_rev = le16_to_cpu(udf_sb_lvidiu(sbi)->minUDFWriteRev);
+ if (write_rev > UDF_MAX_WRITE_VERSION && !(*flags & MS_RDONLY))
+ return -EACCES;
+ }
+
uopt.flags = sbi->s_flags;
uopt.uid = sbi->s_uid;
uopt.gid = sbi->s_gid;
@@ -649,12 +655,6 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
sbi->s_dmode = uopt.dmode;
write_unlock(&sbi->s_cred_lock);
- if (sbi->s_lvid_bh) {
- int write_rev = le16_to_cpu(udf_sb_lvidiu(sbi)->minUDFWriteRev);
- if (write_rev > UDF_MAX_WRITE_VERSION)
- *flags |= MS_RDONLY;
- }
-
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
goto out_unlock;
@@ -843,27 +843,38 @@ static int udf_find_fileset(struct super_block *sb,
return 1;
}
+/*
+ * Load primary Volume Descriptor Sequence
+ *
+ * Return <0 on error, 0 on success. -EAGAIN is special meaning next sequence
+ * should be tried.
+ */
static int udf_load_pvoldesc(struct super_block *sb, sector_t block)
{
struct primaryVolDesc *pvoldesc;
struct ustr *instr, *outstr;
struct buffer_head *bh;
uint16_t ident;
- int ret = 1;
+ int ret = -ENOMEM;
instr = kmalloc(sizeof(struct ustr), GFP_NOFS);
if (!instr)
- return 1;
+ return -ENOMEM;
outstr = kmalloc(sizeof(struct ustr), GFP_NOFS);
if (!outstr)
goto out1;
bh = udf_read_tagged(sb, block, block, &ident);
- if (!bh)
+ if (!bh) {
+ ret = -EAGAIN;
goto out2;
+ }
- BUG_ON(ident != TAG_IDENT_PVD);
+ if (ident != TAG_IDENT_PVD) {
+ ret = -EIO;
+ goto out_bh;
+ }
pvoldesc = (struct primaryVolDesc *)bh->b_data;
@@ -889,8 +900,9 @@ static int udf_load_pvoldesc(struct super_block *sb, sector_t block)
if (udf_CS0toUTF8(outstr, instr))
udf_debug("volSetIdent[] = '%s'\n", outstr->u_name);
- brelse(bh);
ret = 0;
+out_bh:
+ brelse(bh);
out2:
kfree(outstr);
out1:
@@ -947,7 +959,7 @@ static int udf_load_metadata_files(struct super_block *sb, int partition)
if (mdata->s_mirror_fe == NULL) {
udf_err(sb, "Both metadata and mirror metadata inode efe can not found\n");
- goto error_exit;
+ return -EIO;
}
}
@@ -964,23 +976,18 @@ static int udf_load_metadata_files(struct super_block *sb, int partition)
addr.logicalBlockNum, addr.partitionReferenceNum);
mdata->s_bitmap_fe = udf_iget(sb, &addr);
-
if (mdata->s_bitmap_fe == NULL) {
if (sb->s_flags & MS_RDONLY)
udf_warn(sb, "bitmap inode efe not found but it's ok since the disc is mounted read-only\n");
else {
udf_err(sb, "bitmap inode efe not found and attempted read-write mount\n");
- goto error_exit;
+ return -EIO;
}
}
}
udf_debug("udf_load_metadata_files Ok\n");
-
return 0;
-
-error_exit:
- return 1;
}
static void udf_load_fileset(struct super_block *sb, struct buffer_head *bh,
@@ -1069,7 +1076,7 @@ static int udf_fill_partdesc_info(struct super_block *sb,
if (!map->s_uspace.s_table) {
udf_debug("cannot load unallocSpaceTable (part %d)\n",
p_index);
- return 1;
+ return -EIO;
}
map->s_partition_flags |= UDF_PART_FLAG_UNALLOC_TABLE;
udf_debug("unallocSpaceTable (part %d) @ %ld\n",
@@ -1079,7 +1086,7 @@ static int udf_fill_partdesc_info(struct super_block *sb,
if (phd->unallocSpaceBitmap.extLength) {
struct udf_bitmap *bitmap = udf_sb_alloc_bitmap(sb, p_index);
if (!bitmap)
- return 1;
+ return -ENOMEM;
map->s_uspace.s_bitmap = bitmap;
bitmap->s_extPosition = le32_to_cpu(
phd->unallocSpaceBitmap.extPosition);
@@ -1102,7 +1109,7 @@ static int udf_fill_partdesc_info(struct super_block *sb,
if (!map->s_fspace.s_table) {
udf_debug("cannot load freedSpaceTable (part %d)\n",
p_index);
- return 1;
+ return -EIO;
}
map->s_partition_flags |= UDF_PART_FLAG_FREED_TABLE;
@@ -1113,7 +1120,7 @@ static int udf_fill_partdesc_info(struct super_block *sb,
if (phd->freedSpaceBitmap.extLength) {
struct udf_bitmap *bitmap = udf_sb_alloc_bitmap(sb, p_index);
if (!bitmap)
- return 1;
+ return -ENOMEM;
map->s_fspace.s_bitmap = bitmap;
bitmap->s_extPosition = le32_to_cpu(
phd->freedSpaceBitmap.extPosition);
@@ -1165,7 +1172,7 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index)
udf_find_vat_block(sb, p_index, type1_index, blocks - 1);
}
if (!sbi->s_vat_inode)
- return 1;
+ return -EIO;
if (map->s_partition_type == UDF_VIRTUAL_MAP15) {
map->s_type_specific.s_virtual.s_start_offset = 0;
@@ -1177,7 +1184,7 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index)
pos = udf_block_map(sbi->s_vat_inode, 0);
bh = sb_bread(sb, pos);
if (!bh)
- return 1;
+ return -EIO;
vat20 = (struct virtualAllocationTable20 *)bh->b_data;
} else {
vat20 = (struct virtualAllocationTable20 *)
@@ -1195,6 +1202,12 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index)
return 0;
}
+/*
+ * Load partition descriptor block
+ *
+ * Returns <0 on error, 0 on success, -EAGAIN is special - try next descriptor
+ * sequence.
+ */
static int udf_load_partdesc(struct super_block *sb, sector_t block)
{
struct buffer_head *bh;
@@ -1204,13 +1217,15 @@ static int udf_load_partdesc(struct super_block *sb, sector_t block)
int i, type1_idx;
uint16_t partitionNumber;
uint16_t ident;
- int ret = 0;
+ int ret;
bh = udf_read_tagged(sb, block, block, &ident);
if (!bh)
- return 1;
- if (ident != TAG_IDENT_PD)
+ return -EAGAIN;
+ if (ident != TAG_IDENT_PD) {
+ ret = 0;
goto out_bh;
+ }
p = (struct partitionDesc *)bh->b_data;
partitionNumber = le16_to_cpu(p->partitionNumber);
@@ -1229,10 +1244,13 @@ static int udf_load_partdesc(struct super_block *sb, sector_t block)
if (i >= sbi->s_partitions) {
udf_debug("Partition (%d) not found in partition map\n",
partitionNumber);
+ ret = 0;
goto out_bh;
}
ret = udf_fill_partdesc_info(sb, p, i);
+ if (ret < 0)
+ goto out_bh;
/*
* Now rescan for VIRTUAL or METADATA partitions when SPARABLE and
@@ -1249,32 +1267,37 @@ static int udf_load_partdesc(struct super_block *sb, sector_t block)
break;
}
- if (i >= sbi->s_partitions)
+ if (i >= sbi->s_partitions) {
+ ret = 0;
goto out_bh;
+ }
ret = udf_fill_partdesc_info(sb, p, i);
- if (ret)
+ if (ret < 0)
goto out_bh;
if (map->s_partition_type == UDF_METADATA_MAP25) {
ret = udf_load_metadata_files(sb, i);
- if (ret) {
+ if (ret < 0) {
udf_err(sb, "error loading MetaData partition map %d\n",
i);
goto out_bh;
}
} else {
- ret = udf_load_vat(sb, i, type1_idx);
- if (ret)
- goto out_bh;
/*
- * Mark filesystem read-only if we have a partition with
- * virtual map since we don't handle writing to it (we
- * overwrite blocks instead of relocating them).
+ * If we have a partition with virtual map, we don't handle
+ * writing to it (we overwrite blocks instead of relocating
+ * them).
*/
- sb->s_flags |= MS_RDONLY;
- pr_notice("Filesystem marked read-only because writing to pseudooverwrite partition is not implemented\n");
+ if (!(sb->s_flags & MS_RDONLY)) {
+ ret = -EACCES;
+ goto out_bh;
+ }
+ ret = udf_load_vat(sb, i, type1_idx);
+ if (ret < 0)
+ goto out_bh;
}
+ ret = 0;
out_bh:
/* In case loading failed, we handle cleanup in udf_fill_super */
brelse(bh);
@@ -1340,11 +1363,11 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
uint16_t ident;
struct buffer_head *bh;
unsigned int table_len;
- int ret = 0;
+ int ret;
bh = udf_read_tagged(sb, block, block, &ident);
if (!bh)
- return 1;
+ return -EAGAIN;
BUG_ON(ident != TAG_IDENT_LVD);
lvd = (struct logicalVolDesc *)bh->b_data;
table_len = le32_to_cpu(lvd->mapTableLength);
@@ -1352,7 +1375,7 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
udf_err(sb, "error loading logical volume descriptor: "
"Partition table too long (%u > %lu)\n", table_len,
sb->s_blocksize - sizeof(*lvd));
- ret = 1;
+ ret = -EIO;
goto out_bh;
}
@@ -1396,11 +1419,10 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
} else if (!strncmp(upm2->partIdent.ident,
UDF_ID_SPARABLE,
strlen(UDF_ID_SPARABLE))) {
- if (udf_load_sparable_map(sb, map,
- (struct sparablePartitionMap *)gpm) < 0) {
- ret = 1;
+ ret = udf_load_sparable_map(sb, map,
+ (struct sparablePartitionMap *)gpm);
+ if (ret < 0)
goto out_bh;
- }
} else if (!strncmp(upm2->partIdent.ident,
UDF_ID_METADATA,
strlen(UDF_ID_METADATA))) {
@@ -1465,7 +1487,7 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
}
if (lvd->integritySeqExt.extLength)
udf_load_logicalvolint(sb, leea_to_cpu(lvd->integritySeqExt));
-
+ ret = 0;
out_bh:
brelse(bh);
return ret;
@@ -1503,22 +1525,18 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_
}
/*
- * udf_process_sequence
- *
- * PURPOSE
- * Process a main/reserve volume descriptor sequence.
- *
- * PRE-CONDITIONS
- * sb Pointer to _locked_ superblock.
- * block First block of first extent of the sequence.
- * lastblock Lastblock of first extent of the sequence.
+ * Process a main/reserve volume descriptor sequence.
+ * @block First block of first extent of the sequence.
+ * @lastblock Lastblock of first extent of the sequence.
+ * @fileset There we store extent containing root fileset
*
- * HISTORY
- * July 1, 1997 - Andrew E. Mileski
- * Written, tested, and released.
+ * Returns <0 on error, 0 on success. -EAGAIN is special - try next descriptor
+ * sequence
*/
-static noinline int udf_process_sequence(struct super_block *sb, long block,
- long lastblock, struct kernel_lb_addr *fileset)
+static noinline int udf_process_sequence(
+ struct super_block *sb,
+ sector_t block, sector_t lastblock,
+ struct kernel_lb_addr *fileset)
{
struct buffer_head *bh = NULL;
struct udf_vds_record vds[VDS_POS_LENGTH];
@@ -1529,6 +1547,7 @@ static noinline int udf_process_sequence(struct super_block *sb, long block,
uint32_t vdsn;
uint16_t ident;
long next_s = 0, next_e = 0;
+ int ret;
memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH);
@@ -1543,7 +1562,7 @@ static noinline int udf_process_sequence(struct super_block *sb, long block,
udf_err(sb,
"Block %llu of volume descriptor sequence is corrupted or we could not read it\n",
(unsigned long long)block);
- return 1;
+ return -EAGAIN;
}
/* Process each descriptor (ISO 13346 3/8.3-8.4) */
@@ -1616,14 +1635,19 @@ static noinline int udf_process_sequence(struct super_block *sb, long block,
*/
if (!vds[VDS_POS_PRIMARY_VOL_DESC].block) {
udf_err(sb, "Primary Volume Descriptor not found!\n");
- return 1;
+ return -EAGAIN;
+ }
+ ret = udf_load_pvoldesc(sb, vds[VDS_POS_PRIMARY_VOL_DESC].block);
+ if (ret < 0)
+ return ret;
+
+ if (vds[VDS_POS_LOGICAL_VOL_DESC].block) {
+ ret = udf_load_logicalvol(sb,
+ vds[VDS_POS_LOGICAL_VOL_DESC].block,
+ fileset);
+ if (ret < 0)
+ return ret;
}
- if (udf_load_pvoldesc(sb, vds[VDS_POS_PRIMARY_VOL_DESC].block))
- return 1;
-
- if (vds[VDS_POS_LOGICAL_VOL_DESC].block && udf_load_logicalvol(sb,
- vds[VDS_POS_LOGICAL_VOL_DESC].block, fileset))
- return 1;
if (vds[VDS_POS_PARTITION_DESC].block) {
/*
@@ -1632,19 +1656,27 @@ static noinline int udf_process_sequence(struct super_block *sb, long block,
*/
for (block = vds[VDS_POS_PARTITION_DESC].block;
block < vds[VDS_POS_TERMINATING_DESC].block;
- block++)
- if (udf_load_partdesc(sb, block))
- return 1;
+ block++) {
+ ret = udf_load_partdesc(sb, block);
+ if (ret < 0)
+ return ret;
+ }
}
return 0;
}
+/*
+ * Load Volume Descriptor Sequence described by anchor in bh
+ *
+ * Returns <0 on error, 0 on success
+ */
static int udf_load_sequence(struct super_block *sb, struct buffer_head *bh,
struct kernel_lb_addr *fileset)
{
struct anchorVolDescPtr *anchor;
- long main_s, main_e, reserve_s, reserve_e;
+ sector_t main_s, main_e, reserve_s, reserve_e;
+ int ret;
anchor = (struct anchorVolDescPtr *)bh->b_data;
@@ -1662,18 +1694,26 @@ static int udf_load_sequence(struct super_block *sb, struct buffer_head *bh,
/* Process the main & reserve sequences */
/* responsible for finding the PartitionDesc(s) */
- if (!udf_process_sequence(sb, main_s, main_e, fileset))
- return 1;
- udf_sb_free_partitions(sb);
- if (!udf_process_sequence(sb, reserve_s, reserve_e, fileset))
- return 1;
+ ret = udf_process_sequence(sb, main_s, main_e, fileset);
+ if (ret != -EAGAIN)
+ return ret;
udf_sb_free_partitions(sb);
- return 0;
+ ret = udf_process_sequence(sb, reserve_s, reserve_e, fileset);
+ if (ret < 0) {
+ udf_sb_free_partitions(sb);
+ /* No sequence was OK, return -EIO */
+ if (ret == -EAGAIN)
+ ret = -EIO;
+ }
+ return ret;
}
/*
* Check whether there is an anchor block in the given block and
* load Volume Descriptor Sequence if so.
+ *
+ * Returns <0 on error, 0 on success, -EAGAIN is special - try next anchor
+ * block
*/
static int udf_check_anchor_block(struct super_block *sb, sector_t block,
struct kernel_lb_addr *fileset)
@@ -1685,33 +1725,40 @@ static int udf_check_anchor_block(struct super_block *sb, sector_t block,
if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV) &&
udf_fixed_to_variable(block) >=
sb->s_bdev->bd_inode->i_size >> sb->s_blocksize_bits)
- return 0;
+ return -EAGAIN;
bh = udf_read_tagged(sb, block, block, &ident);
if (!bh)
- return 0;
+ return -EAGAIN;
if (ident != TAG_IDENT_AVDP) {
brelse(bh);
- return 0;
+ return -EAGAIN;
}
ret = udf_load_sequence(sb, bh, fileset);
brelse(bh);
return ret;
}
-/* Search for an anchor volume descriptor pointer */
-static sector_t udf_scan_anchors(struct super_block *sb, sector_t lastblock,
- struct kernel_lb_addr *fileset)
+/*
+ * Search for an anchor volume descriptor pointer.
+ *
+ * Returns < 0 on error, 0 on success. -EAGAIN is special - try next set
+ * of anchors.
+ */
+static int udf_scan_anchors(struct super_block *sb, sector_t *lastblock,
+ struct kernel_lb_addr *fileset)
{
sector_t last[6];
int i;
struct udf_sb_info *sbi = UDF_SB(sb);
int last_count = 0;
+ int ret;
/* First try user provided anchor */
if (sbi->s_anchor) {
- if (udf_check_anchor_block(sb, sbi->s_anchor, fileset))
- return lastblock;
+ ret = udf_check_anchor_block(sb, sbi->s_anchor, fileset);
+ if (ret != -EAGAIN)
+ return ret;
}
/*
* according to spec, anchor is in either:
@@ -1720,39 +1767,46 @@ static sector_t udf_scan_anchors(struct super_block *sb, sector_t lastblock,
* lastblock
* however, if the disc isn't closed, it could be 512.
*/
- if (udf_check_anchor_block(sb, sbi->s_session + 256, fileset))
- return lastblock;
+ ret = udf_check_anchor_block(sb, sbi->s_session + 256, fileset);
+ if (ret != -EAGAIN)
+ return ret;
/*
* The trouble is which block is the last one. Drives often misreport
* this so we try various possibilities.
*/
- last[last_count++] = lastblock;
- if (lastblock >= 1)
- last[last_count++] = lastblock - 1;
- last[last_count++] = lastblock + 1;
- if (lastblock >= 2)
- last[last_count++] = lastblock - 2;
- if (lastblock >= 150)
- last[last_count++] = lastblock - 150;
- if (lastblock >= 152)
- last[last_count++] = lastblock - 152;
+ last[last_count++] = *lastblock;
+ if (*lastblock >= 1)
+ last[last_count++] = *lastblock - 1;
+ last[last_count++] = *lastblock + 1;
+ if (*lastblock >= 2)
+ last[last_count++] = *lastblock - 2;
+ if (*lastblock >= 150)
+ last[last_count++] = *lastblock - 150;
+ if (*lastblock >= 152)
+ last[last_count++] = *lastblock - 152;
for (i = 0; i < last_count; i++) {
if (last[i] >= sb->s_bdev->bd_inode->i_size >>
sb->s_blocksize_bits)
continue;
- if (udf_check_anchor_block(sb, last[i], fileset))
- return last[i];
+ ret = udf_check_anchor_block(sb, last[i], fileset);
+ if (ret != -EAGAIN) {
+ if (!ret)
+ *lastblock = last[i];
+ return ret;
+ }
if (last[i] < 256)
continue;
- if (udf_check_anchor_block(sb, last[i] - 256, fileset))
- return last[i];
+ ret = udf_check_anchor_block(sb, last[i] - 256, fileset);
+ if (ret != -EAGAIN) {
+ if (!ret)
+ *lastblock = last[i];
+ return ret;
+ }
}
/* Finally try block 512 in case media is open */
- if (udf_check_anchor_block(sb, sbi->s_session + 512, fileset))
- return last[0];
- return 0;
+ return udf_check_anchor_block(sb, sbi->s_session + 512, fileset);
}
/*
@@ -1760,54 +1814,59 @@ static sector_t udf_scan_anchors(struct super_block *sb, sector_t lastblock,
* area specified by it. The function expects sbi->s_lastblock to be the last
* block on the media.
*
- * Return 1 if ok, 0 if not found.
- *
+ * Return <0 on error, 0 if anchor found. -EAGAIN is special meaning anchor
+ * was not found.
*/
static int udf_find_anchor(struct super_block *sb,
struct kernel_lb_addr *fileset)
{
- sector_t lastblock;
struct udf_sb_info *sbi = UDF_SB(sb);
+ sector_t lastblock = sbi->s_last_block;
+ int ret;
- lastblock = udf_scan_anchors(sb, sbi->s_last_block, fileset);
- if (lastblock)
+ ret = udf_scan_anchors(sb, &lastblock, fileset);
+ if (ret != -EAGAIN)
goto out;
/* No anchor found? Try VARCONV conversion of block numbers */
UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
+ lastblock = udf_variable_to_fixed(sbi->s_last_block);
/* Firstly, we try to not convert number of the last block */
- lastblock = udf_scan_anchors(sb,
- udf_variable_to_fixed(sbi->s_last_block),
- fileset);
- if (lastblock)
+ ret = udf_scan_anchors(sb, &lastblock, fileset);
+ if (ret != -EAGAIN)
goto out;
+ lastblock = sbi->s_last_block;
/* Secondly, we try with converted number of the last block */
- lastblock = udf_scan_anchors(sb, sbi->s_last_block, fileset);
- if (!lastblock) {
+ ret = udf_scan_anchors(sb, &lastblock, fileset);
+ if (ret < 0) {
/* VARCONV didn't help. Clear it. */
UDF_CLEAR_FLAG(sb, UDF_FLAG_VARCONV);
- return 0;
}
out:
- sbi->s_last_block = lastblock;
- return 1;
+ if (ret == 0)
+ sbi->s_last_block = lastblock;
+ return ret;
}
/*
* Check Volume Structure Descriptor, find Anchor block and load Volume
- * Descriptor Sequence
+ * Descriptor Sequence.
+ *
+ * Returns < 0 on error, 0 on success. -EAGAIN is special meaning anchor
+ * block was not found.
*/
static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt,
int silent, struct kernel_lb_addr *fileset)
{
struct udf_sb_info *sbi = UDF_SB(sb);
loff_t nsr_off;
+ int ret;
if (!sb_set_blocksize(sb, uopt->blocksize)) {
if (!silent)
udf_warn(sb, "Bad block size\n");
- return 0;
+ return -EINVAL;
}
sbi->s_last_block = uopt->lastblock;
if (!uopt->novrs) {
@@ -1828,12 +1887,13 @@ static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt,
/* Look for anchor block and load Volume Descriptor Sequence */
sbi->s_anchor = uopt->anchor;
- if (!udf_find_anchor(sb, fileset)) {
- if (!silent)
+ ret = udf_find_anchor(sb, fileset);
+ if (ret < 0) {
+ if (!silent && ret == -EAGAIN)
udf_warn(sb, "No anchor found\n");
- return 0;
+ return ret;
}
- return 1;
+ return 0;
}
static void udf_open_lvid(struct super_block *sb)
@@ -1939,7 +1999,7 @@ u64 lvid_get_unique_id(struct super_block *sb)
static int udf_fill_super(struct super_block *sb, void *options, int silent)
{
- int ret;
+ int ret = -EINVAL;
struct inode *inode = NULL;
struct udf_options uopt;
struct kernel_lb_addr rootdir, fileset;
@@ -2011,7 +2071,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
} else {
uopt.blocksize = bdev_logical_block_size(sb->s_bdev);
ret = udf_load_vrs(sb, &uopt, silent, &fileset);
- if (!ret && uopt.blocksize != UDF_DEFAULT_BLOCKSIZE) {
+ if (ret == -EAGAIN && uopt.blocksize != UDF_DEFAULT_BLOCKSIZE) {
if (!silent)
pr_notice("Rescanning with blocksize %d\n",
UDF_DEFAULT_BLOCKSIZE);
@@ -2021,8 +2081,11 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
ret = udf_load_vrs(sb, &uopt, silent, &fileset);
}
}
- if (!ret) {
- udf_warn(sb, "No partition found (1)\n");
+ if (ret < 0) {
+ if (ret == -EAGAIN) {
+ udf_warn(sb, "No partition found (1)\n");
+ ret = -EINVAL;
+ }
goto error_out;
}
@@ -2040,9 +2103,13 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
udf_err(sb, "minUDFReadRev=%x (max is %x)\n",
le16_to_cpu(lvidiu->minUDFReadRev),
UDF_MAX_READ_VERSION);
+ ret = -EINVAL;
+ goto error_out;
+ } else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION &&
+ !(sb->s_flags & MS_RDONLY)) {
+ ret = -EACCES;
goto error_out;
- } else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION)
- sb->s_flags |= MS_RDONLY;
+ }
sbi->s_udfrev = minUDFWriteRev;
@@ -2054,17 +2121,20 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
if (!sbi->s_partitions) {
udf_warn(sb, "No partition found (2)\n");
+ ret = -EINVAL;
goto error_out;
}
if (sbi->s_partmaps[sbi->s_partition].s_partition_flags &
- UDF_PART_FLAG_READ_ONLY) {
- pr_notice("Partition marked readonly; forcing readonly mount\n");
- sb->s_flags |= MS_RDONLY;
+ UDF_PART_FLAG_READ_ONLY &&
+ !(sb->s_flags & MS_RDONLY)) {
+ ret = -EACCES;
+ goto error_out;
}
if (udf_find_fileset(sb, &fileset, &rootdir)) {
udf_warn(sb, "No fileset found\n");
+ ret = -EINVAL;
goto error_out;
}
@@ -2086,6 +2156,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
if (!inode) {
udf_err(sb, "Error in udf_iget, block=%d, partition=%d\n",
rootdir.logicalBlockNum, rootdir.partitionReferenceNum);
+ ret = -EIO;
goto error_out;
}
@@ -2093,6 +2164,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
sb->s_root = d_make_root(inode);
if (!sb->s_root) {
udf_err(sb, "Couldn't allocate root dentry\n");
+ ret = -ENOMEM;
goto error_out;
}
sb->s_maxbytes = MAX_LFS_FILESIZE;
@@ -2113,7 +2185,7 @@ error_out:
kfree(sbi);
sb->s_fs_info = NULL;
- return -EINVAL;
+ return ret;
}
void _udf_err(struct super_block *sb, const char *function,
diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c
index 0b8b2a13cd24..eca6f9d8a263 100644
--- a/fs/xfs/xfs_da_btree.c
+++ b/fs/xfs/xfs_da_btree.c
@@ -1223,6 +1223,7 @@ xfs_da3_node_toosmall(
/* start with smaller blk num */
forward = nodehdr.forw < nodehdr.back;
for (i = 0; i < 2; forward = !forward, i++) {
+ struct xfs_da3_icnode_hdr thdr;
if (forward)
blkno = nodehdr.forw;
else
@@ -1235,10 +1236,10 @@ xfs_da3_node_toosmall(
return(error);
node = bp->b_addr;
- xfs_da3_node_hdr_from_disk(&nodehdr, node);
+ xfs_da3_node_hdr_from_disk(&thdr, node);
xfs_trans_brelse(state->args->trans, bp);
- if (count - nodehdr.count >= 0)
+ if (count - thdr.count >= 0)
break; /* fits with at least 25% to spare */
}
if (i >= 2) {
diff --git a/include/asm-generic/hugetlb.h b/include/asm-generic/hugetlb.h
index d06079c774a0..99b490b4d05a 100644
--- a/include/asm-generic/hugetlb.h
+++ b/include/asm-generic/hugetlb.h
@@ -6,12 +6,12 @@ static inline pte_t mk_huge_pte(struct page *page, pgprot_t pgprot)
return mk_pte(page, pgprot);
}
-static inline int huge_pte_write(pte_t pte)
+static inline unsigned long huge_pte_write(pte_t pte)
{
return pte_write(pte);
}
-static inline int huge_pte_dirty(pte_t pte)
+static inline unsigned long huge_pte_dirty(pte_t pte)
{
return pte_dirty(pte);
}
diff --git a/include/linux/balloon_compaction.h b/include/linux/balloon_compaction.h
index f7f1d7169b11..089743ade734 100644
--- a/include/linux/balloon_compaction.h
+++ b/include/linux/balloon_compaction.h
@@ -159,6 +159,26 @@ static inline bool balloon_page_movable(struct page *page)
}
/*
+ * isolated_balloon_page - identify an isolated balloon page on private
+ * compaction/migration page lists.
+ *
+ * After a compaction thread isolates a balloon page for migration, it raises
+ * the page refcount to prevent concurrent compaction threads from re-isolating
+ * the same page. For that reason putback_movable_pages(), or other routines
+ * that need to identify isolated balloon pages on private pagelists, cannot
+ * rely on balloon_page_movable() to accomplish the task.
+ */
+static inline bool isolated_balloon_page(struct page *page)
+{
+ /* Already isolated balloon pages, by default, have a raised refcount */
+ if (page_flags_cleared(page) && !page_mapped(page) &&
+ page_count(page) >= 2)
+ return __is_movable_balloon_page(page);
+
+ return false;
+}
+
+/*
* balloon_page_insert - insert a page into the balloon's page list and make
* the page->mapping assignment accordingly.
* @page : page to be assigned as a 'balloon page'
@@ -243,6 +263,11 @@ static inline bool balloon_page_movable(struct page *page)
return false;
}
+static inline bool isolated_balloon_page(struct page *page)
+{
+ return false;
+}
+
static inline bool balloon_page_isolate(struct page *page)
{
return false;
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 7f0c1dd09079..ec1aee4aec9c 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -669,6 +669,13 @@ asmlinkage long compat_sys_sigaltstack(const compat_stack_t __user *uss_ptr,
int compat_restore_altstack(const compat_stack_t __user *uss);
int __compat_save_altstack(compat_stack_t __user *, unsigned long);
+#define compat_save_altstack_ex(uss, sp) do { \
+ compat_stack_t __user *__uss = uss; \
+ struct task_struct *t = current; \
+ put_user_ex(ptr_to_compat((void __user *)t->sas_ss_sp), &__uss->ss_sp); \
+ put_user_ex(sas_ss_flags(sp), &__uss->ss_flags); \
+ put_user_ex(t->sas_ss_size, &__uss->ss_size); \
+} while (0);
asmlinkage long compat_sys_sched_rr_get_interval(compat_pid_t pid,
struct compat_timespec __user *interval);
diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h
index 842de225055f..ded429966c1f 100644
--- a/include/linux/compiler-gcc4.h
+++ b/include/linux/compiler-gcc4.h
@@ -65,6 +65,21 @@
#define __visible __attribute__((externally_visible))
#endif
+/*
+ * GCC 'asm goto' miscompiles certain code sequences:
+ *
+ * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58670
+ *
+ * Work it around via a compiler barrier quirk suggested by Jakub Jelinek.
+ * Fixed in GCC 4.8.2 and later versions.
+ *
+ * (asm goto is automatically volatile - the naming reflects this.)
+ */
+#if GCC_VERSION <= 40801
+# define asm_volatile_goto(x...) do { asm goto(x); asm (""); } while (0)
+#else
+# define asm_volatile_goto(x...) do { asm goto(x); } while (0)
+#endif
#ifdef CONFIG_ARCH_USE_BUILTIN_BSWAP
#if GCC_VERSION >= 40400
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 3cd32478f2fd..ef5bd3ff7eb4 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -405,13 +405,14 @@ int dm_noflush_suspending(struct dm_target *ti);
union map_info *dm_get_mapinfo(struct bio *bio);
union map_info *dm_get_rq_mapinfo(struct request *rq);
+struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
+
/*
* Geometry functions.
*/
int dm_get_geometry(struct mapped_device *md, struct hd_geometry *geo);
int dm_set_geometry(struct mapped_device *md, struct hd_geometry *geo);
-
/*-----------------------------------------------------------------
* Functions for manipulating device-mapper tables.
*---------------------------------------------------------------*/
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 8136c6d99037..40e9c9d76d1e 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -393,10 +393,12 @@ struct hid_report {
struct hid_device *device; /* associated device */
};
+#define HID_MAX_IDS 256
+
struct hid_report_enum {
unsigned numbered;
struct list_head report_list;
- struct hid_report *report_id_hash[256];
+ struct hid_report *report_id_hash[HID_MAX_IDS];
};
#define HID_REPORT_TYPES 3
@@ -744,9 +746,14 @@ struct hid_field *hidinput_get_led_field(struct hid_device *hid);
unsigned int hidinput_count_leds(struct hid_device *hid);
__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code);
void hid_output_report(struct hid_report *report, __u8 *data);
+u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags);
struct hid_device *hid_allocate_device(void);
struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
+struct hid_report *hid_validate_values(struct hid_device *hid,
+ unsigned int type, unsigned int id,
+ unsigned int field_index,
+ unsigned int report_counts);
int hid_open_report(struct hid_device *device);
int hid_check_keys_pressed(struct hid_device *hid);
int hid_connect(struct hid_device *hid, unsigned int connect_mask);
diff --git a/include/linux/ion.h b/include/linux/ion.h
index 8414a6d9c39b..5771f8c3d3ac 100644
--- a/include/linux/ion.h
+++ b/include/linux/ion.h
@@ -19,7 +19,8 @@
#include <linux/types.h>
-struct ion_handle;
+typedef int ion_user_handle_t;
+
/**
* enum ion_heap_types - list of all possible types of heaps
* @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc
@@ -27,6 +28,7 @@ struct ion_handle;
* @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved
* carveout heap, allocations are physically
* contiguous
+ * @ION_HEAP_TYPE_DMA: memory allocated via DMA API
* @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask
* is used to identify the heaps, so only 32
* total heap types are supported
@@ -36,6 +38,7 @@ enum ion_heap_type {
ION_HEAP_TYPE_SYSTEM_CONTIG,
ION_HEAP_TYPE_CARVEOUT,
ION_HEAP_TYPE_CHUNK,
+ ION_HEAP_TYPE_DMA,
ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
are at the end of this enum */
ION_NUM_HEAPS = 16,
@@ -44,6 +47,7 @@ enum ion_heap_type {
#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM)
#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT)
+#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA)
#define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8
@@ -60,6 +64,7 @@ enum ion_heap_type {
caches must be managed manually */
#ifdef __KERNEL__
+struct ion_handle;
struct ion_device;
struct ion_heap;
struct ion_mapper;
@@ -105,7 +110,7 @@ struct ion_platform_heap {
*/
struct ion_platform_data {
int nr;
- struct ion_platform_heap heaps[];
+ struct ion_platform_heap *heaps;
};
/**
@@ -265,7 +270,7 @@ struct ion_allocation_data {
size_t align;
unsigned int heap_id_mask;
unsigned int flags;
- struct ion_handle *handle;
+ ion_user_handle_t handle;
};
/**
@@ -279,7 +284,7 @@ struct ion_allocation_data {
* provides the file descriptor and the kernel returns the handle.
*/
struct ion_fd_data {
- struct ion_handle *handle;
+ ion_user_handle_t handle;
int fd;
};
@@ -288,7 +293,7 @@ struct ion_fd_data {
* @handle: a handle
*/
struct ion_handle_data {
- struct ion_handle *handle;
+ ion_user_handle_t handle;
};
/**
diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h
index c4d870b0d5e6..19c19a5eee29 100644
--- a/include/linux/ipc_namespace.h
+++ b/include/linux/ipc_namespace.h
@@ -22,7 +22,7 @@ struct ipc_ids {
int in_use;
unsigned short seq;
unsigned short seq_max;
- struct rw_semaphore rw_mutex;
+ struct rw_semaphore rwsem;
struct idr ipcs_idr;
int next_id;
};
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
index 09c2300ddb37..cb358355ef43 100644
--- a/include/linux/miscdevice.h
+++ b/include/linux/miscdevice.h
@@ -45,6 +45,7 @@
#define MAPPER_CTRL_MINOR 236
#define LOOP_CTRL_MINOR 237
#define VHOST_NET_MINOR 238
+#define UHID_MINOR 239
#define MISC_DYNAMIC_MINOR 255
struct device;
diff --git a/include/linux/mm.h b/include/linux/mm.h
index bd5679ddcd3f..01eb01df9225 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1486,7 +1486,7 @@ extern int vma_adjust(struct vm_area_struct *vma, unsigned long start,
extern struct vm_area_struct *vma_merge(struct mm_struct *,
struct vm_area_struct *prev, unsigned long addr, unsigned long end,
unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t,
- struct mempolicy *);
+ struct mempolicy *, const char __user *);
extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *);
extern int split_vma(struct mm_struct *,
struct vm_area_struct *, unsigned long addr, int new_below);
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 4a189ba6b128..93ddd4eb910b 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -255,6 +255,10 @@ struct vm_area_struct {
* For areas with an address space and backing store,
* linkage into the address_space->i_mmap interval tree, or
* linkage of vma in the address_space->i_mmap_nonlinear list.
+ *
+ * For private anonymous mappings, a pointer to a null terminated string
+ * in the user process containing the name given to the vma, or NULL
+ * if unnamed.
*/
union {
struct {
@@ -262,6 +266,7 @@ struct vm_area_struct {
unsigned long rb_subtree_last;
} linear;
struct list_head nonlinear;
+ const char __user *anon_name;
} shared;
/*
@@ -457,4 +462,14 @@ static inline cpumask_t *mm_cpumask(struct mm_struct *mm)
return mm->cpu_vm_mask_var;
}
+
+/* Return the name for an anonymous mapping or NULL for a file-backed mapping */
+static inline const char __user *vma_get_anon_name(struct vm_area_struct *vma)
+{
+ if (vma->vm_file)
+ return NULL;
+
+ return vma->shared.anon_name;
+}
+
#endif /* _LINUX_MM_TYPES_H */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index c12916248469..6c7bb35ad6d1 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -518,6 +518,8 @@
#define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303
#define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304
#define PCI_DEVICE_ID_AMD_15H_M10H_F3 0x1403
+#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F3 0x141d
+#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F4 0x141e
#define PCI_DEVICE_ID_AMD_15H_NB_F0 0x1600
#define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601
#define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602
diff --git a/include/linux/platform_data/android_battery.h b/include/linux/platform_data/android_battery.h
deleted file mode 100644
index f6c8298fd885..000000000000
--- a/include/linux/platform_data/android_battery.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * android_battery.h
- *
- * Copyright (C) 2012 Samsung Electronics
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _LINUX_ANDROID_BATTERY_H
-#define _LINUX_ANDROID_BATTERY_H
-
-enum {
- CHARGE_SOURCE_NONE = 0,
- CHARGE_SOURCE_AC,
- CHARGE_SOURCE_USB,
-};
-
-struct android_bat_callbacks {
- void (*charge_source_changed)
- (struct android_bat_callbacks *, int);
- void (*battery_set_full)(struct android_bat_callbacks *);
-};
-
-struct android_bat_platform_data {
- void (*register_callbacks)(struct android_bat_callbacks *);
- void (*unregister_callbacks)(void);
- void (*set_charging_current) (int);
- void (*set_charging_enable) (int);
- int (*poll_charge_source) (void);
- int (*get_capacity) (void);
- int (*get_temperature) (int *);
- int (*get_voltage_now)(void);
- int (*get_current_now)(int *);
-
- int temp_high_threshold;
- int temp_high_recovery;
- int temp_low_recovery;
- int temp_low_threshold;
-
- unsigned long full_charging_time;
- unsigned long recharging_time;
- unsigned int recharging_voltage;
-};
-
-#endif
diff --git a/include/linux/random.h b/include/linux/random.h
index 3b9377d6b7a5..6312dd9ba449 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -17,6 +17,7 @@ extern void add_interrupt_randomness(int irq, int irq_flags);
extern void get_random_bytes(void *buf, int nbytes);
extern void get_random_bytes_arch(void *buf, int nbytes);
void generate_random_uuid(unsigned char uuid_out[16]);
+extern int random_int_secret_init(void);
#ifndef MODULE
extern const struct file_operations random_fops, urandom_fops;
diff --git a/include/linux/rculist.h b/include/linux/rculist.h
index f4b1001a4676..4106721c4e5e 100644
--- a/include/linux/rculist.h
+++ b/include/linux/rculist.h
@@ -267,8 +267,9 @@ static inline void list_splice_init_rcu(struct list_head *list,
*/
#define list_first_or_null_rcu(ptr, type, member) \
({struct list_head *__ptr = (ptr); \
- struct list_head __rcu *__next = list_next_rcu(__ptr); \
- likely(__ptr != __next) ? container_of(__next, type, member) : NULL; \
+ struct list_head *__next = ACCESS_ONCE(__ptr->next); \
+ likely(__ptr != __next) ? \
+ list_entry_rcu(__next, type, member) : NULL; \
})
/**
diff --git a/include/linux/sem.h b/include/linux/sem.h
index 53d42650b193..976ce3a19f1b 100644
--- a/include/linux/sem.h
+++ b/include/linux/sem.h
@@ -12,10 +12,12 @@ struct task_struct;
struct sem_array {
struct kern_ipc_perm ____cacheline_aligned_in_smp
sem_perm; /* permissions .. see ipc.h */
- time_t sem_otime; /* last semop time */
time_t sem_ctime; /* last change time */
struct sem *sem_base; /* ptr to first semaphore in array */
- struct list_head sem_pending; /* pending operations to be processed */
+ struct list_head pending_alter; /* pending operations */
+ /* that alter the array */
+ struct list_head pending_const; /* pending complex operations */
+ /* that do not alter semvals */
struct list_head list_id; /* undo requests on this array */
int sem_nsems; /* no. of semaphores in array */
int complex_count; /* pending complex operations */
diff --git a/include/linux/signal.h b/include/linux/signal.h
index d897484730c0..2ac423bdb676 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -434,6 +434,14 @@ void signals_init(void);
int restore_altstack(const stack_t __user *);
int __save_altstack(stack_t __user *, unsigned long);
+#define save_altstack_ex(uss, sp) do { \
+ stack_t __user *__uss = uss; \
+ struct task_struct *t = current; \
+ put_user_ex((void __user *)t->sas_ss_sp, &__uss->ss_sp); \
+ put_user_ex(sas_ss_flags(sp), &__uss->ss_flags); \
+ put_user_ex(t->sas_ss_size, &__uss->ss_size); \
+} while (0);
+
#ifdef CONFIG_PROC_FS
struct seq_file;
extern void render_sigset_t(struct seq_file *, const char *, sigset_t *);
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index dec1748cd002..eaf602781635 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -1308,6 +1308,11 @@ static inline int skb_pagelen(const struct sk_buff *skb)
return len + skb_headlen(skb);
}
+static inline bool skb_has_frags(const struct sk_buff *skb)
+{
+ return skb_shinfo(skb)->nr_frags;
+}
+
/**
* __skb_fill_page_desc - initialise a paged fragment in an skb
* @skb: buffer containing fragment to be initialised
diff --git a/include/linux/timex.h b/include/linux/timex.h
index b3726e61368e..dd3edd7dfc94 100644
--- a/include/linux/timex.h
+++ b/include/linux/timex.h
@@ -141,6 +141,7 @@ extern int do_adjtimex(struct timex *);
extern void hardpps(const struct timespec *, const struct timespec *);
int read_current_timer(unsigned long *timer_val);
+void ntp_notify_cmos_timer(void);
/* The clock frequency of the i8253/i8254 PIT */
#define PIT_TICK_RATE 1193182ul
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index f5f5c7dfda90..0fdff28d5015 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -410,7 +410,7 @@ extern int usb_hcd_pci_probe(struct pci_dev *dev,
extern void usb_hcd_pci_remove(struct pci_dev *dev);
extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
#endif
#endif /* CONFIG_PCI */
diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
index bf99cd01be20..630356866030 100644
--- a/include/linux/usb_usual.h
+++ b/include/linux/usb_usual.h
@@ -66,7 +66,9 @@
US_FLAG(INITIAL_READ10, 0x00100000) \
/* Initial READ(10) (and others) must be retried */ \
US_FLAG(WRITE_CACHE, 0x00200000) \
- /* Write Cache status is not available */
+ /* Write Cache status is not available */ \
+ US_FLAG(NEEDS_CAP16, 0x00400000)
+ /* cannot handle READ_CAPACITY_10 */
#define US_FLAG(name, value) US_FL_##name = value ,
enum { US_DO_ALL_FLAGS };
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 7343a27fe819..47ada23345a1 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -22,6 +22,7 @@
#define _V4L2_CTRLS_H
#include <linux/list.h>
+#include <linux/mutex.h>
#include <linux/videodev2.h>
/* forward references */
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 01b1a1ad77d2..25100687babb 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,6 +73,10 @@ extern int ipv6_chk_home_addr(struct net *net,
const struct in6_addr *addr);
#endif
+bool ipv6_chk_custom_prefix(const struct in6_addr *addr,
+ const unsigned int prefix_len,
+ struct net_device *dev);
+
extern int ipv6_chk_prefix(const struct in6_addr *addr,
struct net_device *dev);
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index d84ec07b3097..db43501b7599 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -104,6 +104,7 @@ enum {
enum {
HCI_SETUP,
HCI_AUTO_OFF,
+ HCI_RFKILLED,
HCI_MGMT,
HCI_PAIRABLE,
HCI_SERVICE_CACHE,
diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h
index a7a683e30b64..a8c2ef6d3b93 100644
--- a/include/net/cipso_ipv4.h
+++ b/include/net/cipso_ipv4.h
@@ -290,6 +290,7 @@ static inline int cipso_v4_validate(const struct sk_buff *skb,
unsigned char err_offset = 0;
u8 opt_len = opt[1];
u8 opt_iter;
+ u8 tag_len;
if (opt_len < 8) {
err_offset = 1;
@@ -302,11 +303,12 @@ static inline int cipso_v4_validate(const struct sk_buff *skb,
}
for (opt_iter = 6; opt_iter < opt_len;) {
- if (opt[opt_iter + 1] > (opt_len - opt_iter)) {
+ tag_len = opt[opt_iter + 1];
+ if ((tag_len == 0) || (opt[opt_iter + 1] > (opt_len - opt_iter))) {
err_offset = opt_iter + 1;
goto out;
}
- opt_iter += opt[opt_iter + 1];
+ opt_iter += tag_len;
}
out:
diff --git a/include/net/dst.h b/include/net/dst.h
index 1f8fd109e225..e0c97f5a57cf 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -477,10 +477,22 @@ static inline struct dst_entry *xfrm_lookup(struct net *net,
{
return dst_orig;
}
+
+static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst)
+{
+ return NULL;
+}
+
#else
extern struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
const struct flowi *fl, struct sock *sk,
int flags);
+
+/* skb attached with this dst needs transformation if dst->xfrm is valid */
+static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst)
+{
+ return dst->xfrm;
+}
#endif
#endif /* _NET_DST_H */
diff --git a/include/net/ip.h b/include/net/ip.h
index a68f838a132c..edfa59174d9a 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -254,9 +254,11 @@ int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
extern void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more);
-static inline void ip_select_ident(struct iphdr *iph, struct dst_entry *dst, struct sock *sk)
+static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk)
{
- if (iph->frag_off & htons(IP_DF)) {
+ struct iphdr *iph = ip_hdr(skb);
+
+ if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) {
/* This is only to work around buggy Windows95/2000
* VJ compression implementations. If the ID field
* does not change, they drop every other packet in
@@ -268,9 +270,11 @@ static inline void ip_select_ident(struct iphdr *iph, struct dst_entry *dst, str
__ip_select_ident(iph, dst, 0);
}
-static inline void ip_select_ident_more(struct iphdr *iph, struct dst_entry *dst, struct sock *sk, int more)
+static inline void ip_select_ident_more(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk, int more)
{
- if (iph->frag_off & htons(IP_DF)) {
+ struct iphdr *iph = ip_hdr(skb);
+
+ if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) {
if (sk && inet_sk(sk)->inet_daddr) {
iph->id = htons(inet_sk(sk)->inet_id);
inet_sk(sk)->inet_id += 1 + more;
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index 2a601e7da1bf..665e0cee59bd 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -165,6 +165,7 @@ static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst)
static inline void rt6_clean_expires(struct rt6_info *rt)
{
rt->rt6i_flags &= ~RTF_EXPIRES;
+ rt->dst.expires = 0;
}
static inline void rt6_set_expires(struct rt6_info *rt, unsigned long expires)
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 260f83f16bcf..b906f4a131a4 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -194,11 +194,9 @@ static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
}
-static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt, struct in6_addr *dest)
+static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt)
{
- if (rt->rt6i_flags & RTF_GATEWAY)
- return &rt->rt6i_gateway;
- return dest;
+ return &rt->rt6i_gateway;
}
#endif
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index a9942e1faefb..7ac7f91f0242 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -113,7 +113,7 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
__be32 key);
int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
- const struct tnl_ptk_info *tpi, bool log_ecn_error);
+ const struct tnl_ptk_info *tpi, int hdr_len, bool log_ecn_error);
int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[],
struct ip_tunnel_parm *p);
int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[],
diff --git a/include/net/secure_seq.h b/include/net/secure_seq.h
index 6ca975bebd37..c2e542b27a5a 100644
--- a/include/net/secure_seq.h
+++ b/include/net/secure_seq.h
@@ -3,7 +3,6 @@
#include <linux/types.h>
-extern void net_secret_init(void);
extern __u32 secure_ip_id(__be32 daddr);
extern __u32 secure_ipv6_id(const __be32 daddr[4]);
extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
diff --git a/include/net/sock.h b/include/net/sock.h
index 66772cf8c3c5..cec4c723db9a 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -230,6 +230,7 @@ struct cg_proto;
* @sk_wmem_queued: persistent queue size
* @sk_forward_alloc: space allocated forward
* @sk_allocation: allocation mode
+ * @sk_pacing_rate: Pacing rate (if supported by transport/packet scheduler)
* @sk_sndbuf: size of send buffer in bytes
* @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE,
* %SO_OOBINLINE settings, %SO_TIMESTAMPING settings
@@ -355,6 +356,7 @@ struct sock {
kmemcheck_bitfield_end(flags);
int sk_wmem_queued;
gfp_t sk_allocation;
+ u32 sk_pacing_rate; /* bytes per second */
netdev_features_t sk_route_caps;
netdev_features_t sk_route_nocaps;
int sk_gso_type;
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 1ed71a993d65..a67601f2cb50 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -287,6 +287,7 @@ extern int sysctl_tcp_thin_dupack;
extern int sysctl_tcp_early_retrans;
extern int sysctl_tcp_limit_output_bytes;
extern int sysctl_tcp_challenge_ack_limit;
+extern int sysctl_tcp_min_tso_segs;
extern atomic_long_t tcp_memory_allocated;
extern struct percpu_counter tcp_sockets_allocated;
diff --git a/include/trace/events/arm-ipi.h b/include/trace/events/arm-ipi.h
new file mode 100644
index 000000000000..5d3bd21827be
--- /dev/null
+++ b/include/trace/events/arm-ipi.h
@@ -0,0 +1,100 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM arm-ipi
+
+#if !defined(_TRACE_ARM_IPI_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_ARM_IPI_H
+
+#include <linux/tracepoint.h>
+
+#define show_arm_ipi_name(val) \
+ __print_symbolic(val, \
+ { 0, "IPI_WAKEUP" }, \
+ { 1, "IPI_TIMER" }, \
+ { 2, "IPI_RESCHEDULE" }, \
+ { 3, "IPI_CALL_FUNC" }, \
+ { 4, "IPI_CALL_FUNC_SINGLE" }, \
+ { 5, "IPI_CPU_STOP" }, \
+ { 6, "IPI_COMPLETION" }, \
+ { 7, "IPI_CPU_BACKTRACE" })
+
+DECLARE_EVENT_CLASS(arm_ipi,
+
+ TP_PROTO(unsigned int ipi_nr),
+
+ TP_ARGS(ipi_nr),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, ipi )
+ ),
+
+ TP_fast_assign(
+ __entry->ipi = ipi_nr;
+ ),
+
+ TP_printk("ipi=%u [action=%s]", __entry->ipi,
+ show_arm_ipi_name(__entry->ipi))
+);
+
+/**
+ * arm_ipi_entry - called in the arm-generic ipi handler immediately before
+ * entering ipi-type handler
+ * @ipi_nr: ipi number
+ *
+ * When used in combination with the arm_ipi_exit tracepoint
+ * we can determine the ipi handler runtine.
+ */
+DEFINE_EVENT(arm_ipi, arm_ipi_entry,
+
+ TP_PROTO(unsigned int ipi_nr),
+
+ TP_ARGS(ipi_nr)
+);
+
+/**
+ * arm_ipi_exit - called in the arm-generic ipi handler immediately
+ * after the ipi-type handler returns
+ * @ipi_nr: ipi number
+ *
+ * When used in combination with the arm_ipi_entry tracepoint
+ * we can determine the ipi handler runtine.
+ */
+DEFINE_EVENT(arm_ipi, arm_ipi_exit,
+
+ TP_PROTO(unsigned int ipi_nr),
+
+ TP_ARGS(ipi_nr)
+);
+
+/**
+ * arm_ipi_send - called as the ipi target mask is built, immediately
+ * before the register is written
+ * @ipi_nr: ipi number
+ * @dest: cpu to send to
+ *
+ * When used in combination with the arm_ipi_entry tracepoint
+ * we can determine the ipi raise to run latency.
+ */
+TRACE_EVENT(arm_ipi_send,
+
+ TP_PROTO(unsigned int ipi_nr, int dest),
+
+ TP_ARGS(ipi_nr, dest),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, ipi )
+ __field( int , dest )
+ ),
+
+ TP_fast_assign(
+ __entry->ipi = ipi_nr;
+ __entry->dest = dest;
+ ),
+
+ TP_printk("dest=%d ipi=%u [action=%s]", __entry->dest,
+ __entry->ipi, show_arm_ipi_name(__entry->ipi))
+);
+
+#endif /* _TRACE_ARM_IPI_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index 203e8e9933ba..2afcb71857fd 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -530,6 +530,29 @@ TRACE_EVENT(sched_rq_runnable_load,
__entry->load)
);
+TRACE_EVENT(sched_rq_nr_running,
+
+ TP_PROTO(int cpu, unsigned int nr_running, int nr_iowait),
+
+ TP_ARGS(cpu, nr_running, nr_iowait),
+
+ TP_STRUCT__entry(
+ __field(int, cpu)
+ __field(unsigned int, nr_running)
+ __field(int, nr_iowait)
+ ),
+
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->nr_running = nr_running;
+ __entry->nr_iowait = nr_iowait;
+ ),
+
+ TP_printk("cpu=%d nr_running=%u nr_iowait=%d",
+ __entry->cpu,
+ __entry->nr_running, __entry->nr_iowait)
+);
+
/*
* Tracepoint for showing tracked task cpu usage ratio [0..1023].
*/
@@ -557,8 +580,61 @@ TRACE_EVENT(sched_task_usage_ratio,
);
/*
+ * Tracepoint for HMP (CONFIG_SCHED_HMP) task migrations,
+ * marking the forced transition of runnable or running tasks.
+ */
+TRACE_EVENT(sched_hmp_migrate_force_running,
+
+ TP_PROTO(struct task_struct *tsk, int running),
+
+ TP_ARGS(tsk, running),
+
+ TP_STRUCT__entry(
+ __array(char, comm, TASK_COMM_LEN)
+ __field(int, running)
+ ),
+
+ TP_fast_assign(
+ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
+ __entry->running = running;
+ ),
+
+ TP_printk("running=%d comm=%s",
+ __entry->running, __entry->comm)
+);
+
+/*
+ * Tracepoint for HMP (CONFIG_SCHED_HMP) task migrations,
+ * marking the forced transition of runnable or running
+ * tasks when a task is about to go idle.
+ */
+TRACE_EVENT(sched_hmp_migrate_idle_running,
+
+ TP_PROTO(struct task_struct *tsk, int running),
+
+ TP_ARGS(tsk, running),
+
+ TP_STRUCT__entry(
+ __array(char, comm, TASK_COMM_LEN)
+ __field(int, running)
+ ),
+
+ TP_fast_assign(
+ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
+ __entry->running = running;
+ ),
+
+ TP_printk("running=%d comm=%s",
+ __entry->running, __entry->comm)
+);
+
+/*
* Tracepoint for HMP (CONFIG_SCHED_HMP) task migrations.
*/
+#define HMP_MIGRATE_WAKEUP 0
+#define HMP_MIGRATE_FORCE 1
+#define HMP_MIGRATE_OFFLOAD 2
+#define HMP_MIGRATE_IDLE_PULL 3
TRACE_EVENT(sched_hmp_migrate,
TP_PROTO(struct task_struct *tsk, int dest, int force),
@@ -583,6 +659,51 @@ TRACE_EVENT(sched_hmp_migrate,
__entry->comm, __entry->pid,
__entry->dest, __entry->force)
);
+
+TRACE_EVENT(sched_hmp_offload_abort,
+
+ TP_PROTO(int cpu, int data, char *label),
+
+ TP_ARGS(cpu,data,label),
+
+ TP_STRUCT__entry(
+ __array(char, label, 64)
+ __field(int, cpu)
+ __field(int, data)
+ ),
+
+ TP_fast_assign(
+ strncpy(__entry->label, label, 64);
+ __entry->cpu = cpu;
+ __entry->data = data;
+ ),
+
+ TP_printk("cpu=%d data=%d label=%63s",
+ __entry->cpu, __entry->data,
+ __entry->label)
+);
+
+TRACE_EVENT(sched_hmp_offload_succeed,
+
+ TP_PROTO(int cpu, int dest_cpu),
+
+ TP_ARGS(cpu,dest_cpu),
+
+ TP_STRUCT__entry(
+ __field(int, cpu)
+ __field(int, dest_cpu)
+ ),
+
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->dest_cpu = dest_cpu;
+ ),
+
+ TP_printk("cpu=%d dest=%d",
+ __entry->cpu,
+ __entry->dest_cpu)
+);
+
#endif /* _TRACE_SCHED_H */
/* This part must be outside protection */
diff --git a/include/trace/events/smp.h b/include/trace/events/smp.h
new file mode 100644
index 000000000000..da0baf27a39a
--- /dev/null
+++ b/include/trace/events/smp.h
@@ -0,0 +1,90 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM smp
+
+#if !defined(_TRACE_SMP_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_SMP_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(smp_call_class,
+
+ TP_PROTO(void * fnc),
+
+ TP_ARGS(fnc),
+
+ TP_STRUCT__entry(
+ __field( void *, func )
+ ),
+
+ TP_fast_assign(
+ __entry->func = fnc;
+ ),
+
+ TP_printk("func=%pf", __entry->func)
+);
+
+/**
+ * smp_call_func_entry - called in the generic smp-cross-call-handler
+ * immediately before calling the destination
+ * function
+ * @func: function pointer
+ *
+ * When used in combination with the smp_call_func_exit tracepoint
+ * we can determine the cross-call runtime.
+ */
+DEFINE_EVENT(smp_call_class, smp_call_func_entry,
+
+ TP_PROTO(void * fnc),
+
+ TP_ARGS(fnc)
+);
+
+/**
+ * smp_call_func_exit - called in the generic smp-cross-call-handler
+ * immediately after the destination function
+ * returns
+ * @func: function pointer
+ *
+ * When used in combination with the smp_call_entry tracepoint
+ * we can determine the cross-call runtime.
+ */
+DEFINE_EVENT(smp_call_class, smp_call_func_exit,
+
+ TP_PROTO(void * fnc),
+
+ TP_ARGS(fnc)
+);
+
+/**
+ * smp_call_func_send - called as destination function is set
+ * in the per-cpu storage
+ * @func: function pointer
+ * @dest: cpu to send to
+ *
+ * When used in combination with the smp_cross_call_entry tracepoint
+ * we can determine the call-to-run latency.
+ */
+TRACE_EVENT(smp_call_func_send,
+
+ TP_PROTO(void * func, int dest),
+
+ TP_ARGS(func, dest),
+
+ TP_STRUCT__entry(
+ __field( void * , func )
+ __field( int , dest )
+ ),
+
+ TP_fast_assign(
+ __entry->func = func;
+ __entry->dest = dest;
+ ),
+
+ TP_printk("dest=%d func=%pf", __entry->dest,
+ __entry->func)
+);
+
+#endif /* _TRACE_SMP_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 090e5331ab7e..cc2e00eac2f1 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -223,6 +223,8 @@ struct drm_mode_get_connector {
__u32 connection;
__u32 mm_width, mm_height; /**< HxW in millimeters */
__u32 subpixel;
+
+ __u32 pad;
};
#define DRM_MODE_PROP_PENDING (1<<0)
diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h
index 321d4ac5c512..fa8b3adf9ffb 100644
--- a/include/uapi/drm/radeon_drm.h
+++ b/include/uapi/drm/radeon_drm.h
@@ -979,6 +979,8 @@ struct drm_radeon_cs {
#define RADEON_INFO_RING_WORKING 0x15
/* SI tile mode array */
#define RADEON_INFO_SI_TILE_MODE_ARRAY 0x16
+/* query if CP DMA is supported on the compute ring */
+#define RADEON_INFO_SI_CP_DMA_COMPUTE 0x17
struct drm_radeon_info {
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
index fb104e51496e..9e59950f55cf 100644
--- a/include/uapi/linux/perf_event.h
+++ b/include/uapi/linux/perf_event.h
@@ -425,13 +425,15 @@ struct perf_event_mmap_page {
/*
* Control data for the mmap() data buffer.
*
- * User-space reading the @data_head value should issue an rmb(), on
- * SMP capable platforms, after reading this value -- see
- * perf_event_wakeup().
+ * User-space reading the @data_head value should issue an smp_rmb(),
+ * after reading this value.
*
* When the mapping is PROT_WRITE the @data_tail value should be
- * written by userspace to reflect the last read data. In this case
- * the kernel will not over-write unread data.
+ * written by userspace to reflect the last read data, after issueing
+ * an smp_mb() to separate the data read from the ->data_tail store.
+ * In this case the kernel will not over-write unread data.
+ *
+ * See perf_output_put_handle() for the data ordering.
*/
__u64 data_head; /* head in the data section */
__u64 data_tail; /* user-space written tail */
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 289760f424aa..253856a2a8ad 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -149,4 +149,7 @@
#define PR_GET_TID_ADDRESS 40
+#define PR_SET_VMA 0x53564d41
+# define PR_SET_VMA_ANON_NAME 0
+
#endif /* _LINUX_PRCTL_H */
diff --git a/include/uapi/video/adf.h b/include/uapi/video/adf.h
new file mode 100644
index 000000000000..2ba345ca458b
--- /dev/null
+++ b/include/uapi/video/adf.h
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_VIDEO_ADF_H_
+#define _UAPI_VIDEO_ADF_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_mode.h>
+
+#define ADF_NAME_LEN 32
+#define ADF_MAX_CUSTOM_DATA_SIZE PAGE_SIZE
+
+enum adf_interface_type {
+ ADF_INTF_DSI = 0,
+ ADF_INTF_eDP = 1,
+ ADF_INTF_DPI = 2,
+ ADF_INTF_VGA = 3,
+ ADF_INTF_DVI = 4,
+ ADF_INTF_HDMI = 5,
+ ADF_INTF_MEMORY = 6,
+ ADF_INTF_TYPE_DEVICE_CUSTOM = 128,
+ ADF_INTF_TYPE_MAX = (~(__u32)0),
+};
+
+#define ADF_INTF_FLAG_PRIMARY (1 << 0)
+#define ADF_INTF_FLAG_EXTERNAL (1 << 1)
+
+enum adf_event_type {
+ ADF_EVENT_VSYNC = 0,
+ ADF_EVENT_HOTPLUG = 1,
+ ADF_EVENT_DEVICE_CUSTOM = 128,
+ ADF_EVENT_TYPE_MAX = 255,
+};
+
+/**
+ * struct adf_set_event - start or stop subscribing to ADF events
+ *
+ * @type: the type of event to (un)subscribe
+ * @enabled: subscribe or unsubscribe
+ *
+ * After subscribing to an event, userspace may poll() the ADF object's fd
+ * to wait for events or read() to consume the event's data.
+ *
+ * ADF reserves event types 0 to %ADF_EVENT_DEVICE_CUSTOM-1 for its own events.
+ * Devices may use event types %ADF_EVENT_DEVICE_CUSTOM to %ADF_EVENT_TYPE_MAX-1
+ * for driver-private events.
+ */
+struct adf_set_event {
+ __u8 type;
+ __u8 enabled;
+};
+
+/**
+ * struct adf_event - common header for ADF event data
+ *
+ * @type: event type
+ * @length: total size of event data, header inclusive
+ */
+struct adf_event {
+ __u8 type;
+ __u32 length;
+};
+
+/**
+ * struct adf_vsync_event - ADF vsync event
+ *
+ * @base: event header (see &struct adf_event)
+ * @timestamp: time of vsync event, in nanoseconds
+ */
+struct adf_vsync_event {
+ struct adf_event base;
+ __u64 timestamp;
+};
+
+/**
+ * struct adf_vsync_event - ADF display hotplug event
+ *
+ * @base: event header (see &struct adf_event)
+ * @connected: whether a display is now connected to the interface
+ */
+struct adf_hotplug_event {
+ struct adf_event base;
+ __u8 connected;
+};
+
+#define ADF_MAX_PLANES 4
+/**
+ * struct adf_buffer_config - description of buffer displayed by adf_post_config
+ *
+ * @overlay_engine: id of the target overlay engine
+ * @w: width of display region in pixels
+ * @h: height of display region in pixels
+ * @format: DRM-style fourcc, see drm_fourcc.h for standard formats
+ * @fd: dma_buf fd for each plane
+ * @offset: location of first pixel to scan out, in bytes
+ * @pitch: stride (i.e. length of a scanline including padding) in bytes
+ * @n_planes: number of planes in buffer
+ * @acquire_fence: sync_fence fd which will clear when the buffer is
+ * ready for display, or <0 if the buffer is already ready
+ */
+struct adf_buffer_config {
+ __u32 overlay_engine;
+
+ __u32 w;
+ __u32 h;
+ __u32 format;
+
+ __s64 fd[ADF_MAX_PLANES];
+ __u32 offset[ADF_MAX_PLANES];
+ __u32 pitch[ADF_MAX_PLANES];
+ __u8 n_planes;
+
+ __s64 acquire_fence;
+};
+#define ADF_MAX_BUFFERS (PAGE_SIZE / sizeof(struct adf_buffer_config))
+
+/**
+ * struct adf_post_config - request to flip to a new set of buffers
+ *
+ * @n_interfaces: number of interfaces targeted by the flip (input)
+ * @interfaces: ids of interfaces targeted by the flip (input)
+ * @n_bufs: number of buffers displayed (input)
+ * @bufs: description of buffers displayed (input)
+ * @custom_data_size: size of driver-private data (input)
+ * @custom_data: driver-private data (input)
+ * @complete_fence: sync_fence fd which will clear when this
+ * configuration has left the screen (output)
+ */
+struct adf_post_config {
+ size_t n_interfaces;
+ __u32 __user *interfaces;
+
+ size_t n_bufs;
+ struct adf_buffer_config __user *bufs;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+
+ __s64 complete_fence;
+};
+#define ADF_MAX_INTERFACES (PAGE_SIZE / sizeof(__u32))
+
+/**
+ * struct adf_simple_buffer_allocate - request to allocate a "simple" buffer
+ *
+ * @w: width of buffer in pixels (input)
+ * @h: height of buffer in pixels (input)
+ * @format: DRM-style fourcc (input)
+ *
+ * @fd: dma_buf fd (output)
+ * @offset: location of first pixel, in bytes (output)
+ * @pitch: length of a scanline including padding, in bytes (output)
+ *
+ * Simple buffers are analogous to DRM's "dumb" buffers. They have a single
+ * plane of linear RGB data which can be allocated and scanned out without
+ * any driver-private ioctls or data.
+ *
+ * @format must be a standard RGB format defined in drm_fourcc.h.
+ *
+ * ADF clients must NOT assume that an interface can scan out a simple buffer
+ * allocated by a different ADF interface, even if the two interfaces belong to
+ * the same ADF device.
+ */
+struct adf_simple_buffer_alloc {
+ __u16 w;
+ __u16 h;
+ __u32 format;
+
+ __s64 fd;
+ __u32 offset;
+ __u32 pitch;
+};
+
+/**
+ * struct adf_simple_post_config - request to flip to a single buffer without
+ * driver-private data
+ *
+ * @buf: description of buffer displayed (input)
+ * @complete_fence: sync_fence fd which will clear when this buffer has left the
+ * screen (output)
+ */
+struct adf_simple_post_config {
+ struct adf_buffer_config buf;
+ __s64 complete_fence;
+};
+
+/**
+ * struct adf_attachment_config - description of attachment between an overlay
+ * engine and an interface
+ *
+ * @overlay_engine: id of the overlay engine
+ * @interface: id of the interface
+ */
+struct adf_attachment_config {
+ __u32 overlay_engine;
+ __u32 interface;
+};
+
+/**
+ * struct adf_device_data - describes a display device
+ *
+ * @name: display device's name
+ * @n_attachments: the number of current attachments
+ * @attachments: list of current attachments
+ * @n_allowed_attachments: the number of allowed attachments
+ * @allowed_attachments: list of allowed attachments
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_device_data {
+ char name[ADF_NAME_LEN];
+
+ size_t n_attachments;
+ struct adf_attachment_config __user *attachments;
+
+ size_t n_allowed_attachments;
+ struct adf_attachment_config __user *allowed_attachments;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_ATTACHMENTS (PAGE_SIZE / sizeof(struct adf_attachment))
+
+/**
+ * struct adf_device_data - describes a display interface
+ *
+ * @name: display interface's name
+ * @type: interface type (see enum @adf_interface_type)
+ * @id: which interface of type @type;
+ * e.g. interface DSI.1 -> @type=@ADF_INTF_TYPE_DSI, @id=1
+ * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values)
+ * @dpms_state: DPMS state (one of @DRM_MODE_DPMS_* defined in drm_mode.h)
+ * @hotplug_detect: whether a display is plugged in
+ * @width_mm: screen width in millimeters, or 0 if unknown
+ * @height_mm: screen height in millimeters, or 0 if unknown
+ * @current_mode: current display mode
+ * @n_available_modes: the number of hardware display modes
+ * @available_modes: list of hardware display modes
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_interface_data {
+ char name[ADF_NAME_LEN];
+
+ __u32 type;
+ __u32 id;
+ /* e.g. type=ADF_INTF_TYPE_DSI, id=1 => DSI.1 */
+ __u32 flags;
+
+ __u8 dpms_state;
+ __u8 hotplug_detect;
+ __u16 width_mm;
+ __u16 height_mm;
+
+ struct drm_mode_modeinfo current_mode;
+ size_t n_available_modes;
+ struct drm_mode_modeinfo __user *available_modes;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_MODES (PAGE_SIZE / sizeof(struct drm_mode_modeinfo))
+
+/**
+ * struct adf_overlay_engine_data - describes an overlay engine
+ *
+ * @name: overlay engine's name
+ * @n_supported_formats: number of supported formats
+ * @supported_formats: list of supported formats
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_overlay_engine_data {
+ char name[ADF_NAME_LEN];
+
+ size_t n_supported_formats;
+ __u32 __user *supported_formats;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_SUPPORTED_FORMATS (PAGE_SIZE / sizeof(__u32))
+
+#define ADF_SET_EVENT _IOW('D', 0, struct adf_set_event)
+#define ADF_BLANK _IOW('D', 1, __u8)
+#define ADF_POST_CONFIG _IOW('D', 2, struct adf_post_config)
+#define ADF_SET_MODE _IOW('D', 3, struct drm_mode_modeinfo)
+#define ADF_GET_DEVICE_DATA _IOR('D', 4, struct adf_device_data)
+#define ADF_GET_INTERFACE_DATA _IOR('D', 5, struct adf_interface_data)
+#define ADF_GET_OVERLAY_ENGINE_DATA \
+ _IOR('D', 6, struct adf_overlay_engine_data)
+#define ADF_SIMPLE_POST_CONFIG _IOW('D', 7, struct adf_simple_post_config)
+#define ADF_SIMPLE_BUFFER_ALLOC _IOW('D', 8, struct adf_simple_buffer_alloc)
+#define ADF_ATTACH _IOW('D', 9, struct adf_attachment_config)
+#define ADF_DETACH _IOW('D', 10, struct adf_attachment_config)
+
+#endif /* _UAPI_VIDEO_ADF_H_ */
diff --git a/include/video/adf.h b/include/video/adf.h
new file mode 100644
index 000000000000..2b742ab463dd
--- /dev/null
+++ b/include/video/adf.h
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIDEO_ADF_H
+#define _VIDEO_ADF_H
+
+#include <linux/device.h>
+#include <linux/dma-buf.h>
+#include <linux/idr.h>
+#include <linux/kref.h>
+#include <linux/kthread.h>
+#include <linux/ktime.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <uapi/video/adf.h>
+#include "sync.h"
+
+struct adf_obj;
+struct adf_obj_ops;
+struct adf_device;
+struct adf_device_ops;
+struct adf_interface;
+struct adf_interface_ops;
+struct adf_overlay_engine;
+struct adf_overlay_engine_ops;
+
+/**
+ * struct adf_buffer - buffer displayed by adf_post
+ *
+ * @overlay_engine: target overlay engine
+ * @w: width of display region in pixels
+ * @h: height of display region in pixels
+ * @format: DRM-style fourcc, see drm_fourcc.h for standard formats
+ * @dma_bufs: dma_buf for each plane
+ * @offset: location of first pixel to scan out, in bytes
+ * @pitch: length of a scanline including padding, in bytes
+ * @n_planes: number of planes in buffer
+ * @acquire_fence: sync_fence which will clear when the buffer is
+ * ready for display
+ *
+ * &struct adf_buffer is the in-kernel counterpart to the userspace-facing
+ * &struct adf_buffer_config.
+ */
+struct adf_buffer {
+ struct adf_overlay_engine *overlay_engine;
+
+ u32 w;
+ u32 h;
+ u32 format;
+
+ struct dma_buf *dma_bufs[ADF_MAX_PLANES];
+ u32 offset[ADF_MAX_PLANES];
+ u32 pitch[ADF_MAX_PLANES];
+ u8 n_planes;
+
+ struct sync_fence *acquire_fence;
+};
+
+/**
+ * struct adf_buffer_mapping - state for mapping a &struct adf_buffer into the
+ * display device
+ *
+ * @attachments: dma-buf attachment for each plane
+ * @sg_tables: SG tables for each plane
+ */
+struct adf_buffer_mapping {
+ struct dma_buf_attachment *attachments[ADF_MAX_PLANES];
+ struct sg_table *sg_tables[ADF_MAX_PLANES];
+};
+
+/**
+ * struct adf_post - request to flip to a new set of buffers
+ *
+ * @n_bufs: number of buffers displayed
+ * @bufs: buffers displayed
+ * @mappings: in-device mapping state for each buffer
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ *
+ * &struct adf_post is the in-kernel counterpart to the userspace-facing
+ * &struct adf_post_config.
+ */
+struct adf_post {
+ size_t n_bufs;
+ struct adf_buffer *bufs;
+ struct adf_buffer_mapping *mappings;
+
+ size_t custom_data_size;
+ void *custom_data;
+};
+
+/**
+ * struct adf_attachment - description of attachment between an overlay engine
+ * and an interface
+ *
+ * @overlay_engine: the overlay engine
+ * @interface: the interface
+ *
+ * &struct adf_attachment is the in-kernel counterpart to the userspace-facing
+ * &struct adf_attachment_config.
+ */
+struct adf_attachment {
+ struct adf_overlay_engine *overlay_engine;
+ struct adf_interface *interface;
+};
+
+struct adf_pending_post {
+ struct list_head head;
+ struct adf_post config;
+ void *state;
+};
+
+enum adf_obj_type {
+ ADF_OBJ_OVERLAY_ENGINE = 0,
+ ADF_OBJ_INTERFACE = 1,
+ ADF_OBJ_DEVICE = 2,
+};
+
+/**
+ * struct adf_obj_ops - common ADF object implementation ops
+ *
+ * @open: handle opening the object's device node
+ * @release: handle releasing an open file
+ * @ioctl: handle custom ioctls
+ *
+ * @supports_event: return whether the object supports generating events of type
+ * @type
+ * @set_event: enable or disable events of type @type
+ * @event_type_str: return a string representation of custom event @type
+ * (@type >= %ADF_EVENT_DEVICE_CUSTOM).
+ *
+ * @custom_data: copy up to %ADF_MAX_CUSTOM_DATA_SIZE bytes of driver-private
+ * data into @data (allocated by ADF) and return the number of copied bytes
+ * in @size. Return 0 on success or an error code (<0) on failure.
+ */
+struct adf_obj_ops {
+ /* optional */
+ int (*open)(struct adf_obj *obj, struct inode *inode,
+ struct file *file);
+ /* optional */
+ void (*release)(struct adf_obj *obj, struct inode *inode,
+ struct file *file);
+ /* optional */
+ long (*ioctl)(struct adf_obj *obj, unsigned int cmd, unsigned long arg);
+
+ /* optional */
+ bool (*supports_event)(struct adf_obj *obj, enum adf_event_type type);
+ /* required if supports_event is implemented */
+ void (*set_event)(struct adf_obj *obj, enum adf_event_type type,
+ bool enabled);
+ /* optional */
+ const char *(*event_type_str)(struct adf_obj *obj,
+ enum adf_event_type type);
+
+ /* optional */
+ int (*custom_data)(struct adf_obj *obj, void *data, size_t *size);
+};
+
+struct adf_obj {
+ enum adf_obj_type type;
+ char name[ADF_NAME_LEN];
+ struct adf_device *parent;
+
+ const struct adf_obj_ops *ops;
+
+ struct device dev;
+
+ struct spinlock file_lock;
+ struct list_head file_list;
+
+ struct mutex event_lock;
+ struct rb_root event_refcount;
+
+ int id;
+ int minor;
+};
+
+/**
+ * struct adf_device_ops - display device implementation ops
+ *
+ * @owner: device's module
+ * @base: common operations (see &struct adf_obj_ops)
+ *
+ * @attach: attach overlay engine @eng to interface @intf. Return 0 on success
+ * or error code (<0) on failure.
+ * @detach: detach overlay engine @eng from interface @intf. Return 0 on
+ * success or error code (<0) on failure.
+ *
+ * @validate_custom_format: validate the number and size of planes
+ * in buffers with a custom format (i.e., not one of the @DRM_FORMAT_*
+ * types defined in drm/drm_fourcc.h). Return 0 if the buffer is valid or
+ * an error code (<0) otherwise.
+ *
+ * @validate: validate that the proposed configuration @cfg is legal. The
+ * driver may optionally allocate and return some driver-private state in
+ * @driver_state, which will be passed to the corresponding post(). The
+ * driver may NOT commit any changes to hardware. Return 0 if @cfg is
+ * valid or an error code (<0) otherwise.
+ * @complete_fence: create a hardware-backed sync fence to be signaled when
+ * @cfg is removed from the screen. If unimplemented, ADF automatically
+ * creates an sw_sync fence. Return the sync fence on success or a
+ * PTR_ERR() on failure.
+ * @post: flip @cfg onto the screen. Wait for the display to begin scanning out
+ * @cfg before returning.
+ * @advance_timeline: signal the sync fence for the last configuration to leave
+ * the display. If unimplemented, ADF automatically advances an sw_sync
+ * timeline.
+ * @state_free: free driver-private state allocated during validate()
+ */
+struct adf_device_ops {
+ /* required */
+ struct module *owner;
+ const struct adf_obj_ops base;
+
+ /* optional */
+ int (*attach)(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+ /* optional */
+ int (*detach)(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+
+ /* required if any of the device's overlay engines supports at least one
+ custom format */
+ int (*validate_custom_format)(struct adf_device *dev,
+ struct adf_buffer *buf);
+
+ /* required */
+ int (*validate)(struct adf_device *dev, struct adf_post *cfg,
+ void **driver_state);
+ /* optional */
+ struct sync_fence *(*complete_fence)(struct adf_device *dev,
+ struct adf_post *cfg, void *driver_state);
+ /* required */
+ void (*post)(struct adf_device *dev, struct adf_post *cfg,
+ void *driver_state);
+ /* required if complete_fence is implemented */
+ void (*advance_timeline)(struct adf_device *dev,
+ struct adf_post *cfg, void *driver_state);
+ /* required if validate allocates driver state */
+ void (*state_free)(struct adf_device *dev, void *driver_state);
+};
+
+struct adf_attachment_list {
+ struct adf_attachment attachment;
+ struct list_head head;
+};
+
+struct adf_device {
+ struct adf_obj base;
+ struct device *dev;
+
+ const struct adf_device_ops *ops;
+
+ struct mutex client_lock;
+
+ struct idr interfaces;
+ size_t n_interfaces;
+ struct idr overlay_engines;
+
+ struct list_head post_list;
+ struct mutex post_lock;
+ struct kthread_worker post_worker;
+ struct task_struct *post_thread;
+ struct kthread_work post_work;
+
+ struct list_head attached;
+ size_t n_attached;
+ struct list_head attach_allowed;
+ size_t n_attach_allowed;
+
+ struct adf_pending_post *onscreen;
+
+ struct sw_sync_timeline *timeline;
+ int timeline_max;
+};
+
+/**
+ * struct adf_interface_ops - display interface implementation ops
+ *
+ * @base: common operations (see &struct adf_obj_ops)
+ *
+ * @blank: change the display's DPMS state. Return 0 on success or error
+ * code (<0) on failure.
+ *
+ * @alloc_simple_buffer: allocate a buffer with the specified @w, @h, and
+ * @format. @format will be a standard RGB format (i.e.,
+ * adf_format_is_rgb(@format) == true). Return 0 on success or error code
+ * (<0) on failure. On success, return the buffer, offset, and pitch in
+ * @dma_buf, @offset, and @pitch respectively.
+ * @describe_simple_post: provide driver-private data needed to post a single
+ * buffer @buf. Copy up to ADF_MAX_CUSTOM_DATA_SIZE bytes into @data
+ * (allocated by ADF) and return the number of bytes in @size. Return 0 on
+ * success or error code (<0) on failure.
+ *
+ * @modeset: change the interface's mode. @mode is not necessarily part of the
+ * modelist passed to adf_hotplug_notify_connected(); the driver may
+ * accept or reject custom modes at its discretion. Return 0 on success or
+ * error code (<0) if the mode could not be set.
+ *
+ * @screen_size: copy the screen dimensions in millimeters into @width_mm
+ * and @height_mm. Return 0 on success or error code (<0) if the display
+ * dimensions are unknown.
+ *
+ * @type_str: return a string representation of custom @intf->type
+ * (@intf->type >= @ADF_INTF_TYPE_DEVICE_CUSTOM).
+ */
+struct adf_interface_ops {
+ const struct adf_obj_ops base;
+
+ /* optional */
+ int (*blank)(struct adf_interface *intf, u8 state);
+
+ /* optional */
+ int (*alloc_simple_buffer)(struct adf_interface *intf,
+ u16 w, u16 h, u32 format,
+ struct dma_buf **dma_buf, u32 *offset, u32 *pitch);
+ /* optional */
+ int (*describe_simple_post)(struct adf_interface *intf,
+ struct adf_buffer *fb, void *data, size_t *size);
+
+ /* optional */
+ int (*modeset)(struct adf_interface *intf,
+ struct drm_mode_modeinfo *mode);
+
+ /* optional */
+ int (*screen_size)(struct adf_interface *intf, u16 *width_mm,
+ u16 *height_mm);
+
+ /* optional */
+ const char *(*type_str)(struct adf_interface *intf);
+};
+
+struct adf_interface {
+ struct adf_obj base;
+ const struct adf_interface_ops *ops;
+
+ struct drm_mode_modeinfo current_mode;
+
+ enum adf_interface_type type;
+ u32 idx;
+ u32 flags;
+
+ wait_queue_head_t vsync_wait;
+ ktime_t vsync_timestamp;
+ rwlock_t vsync_lock;
+
+ u8 dpms_state;
+
+ bool hotplug_detect;
+ struct drm_mode_modeinfo *modelist;
+ size_t n_modes;
+ rwlock_t hotplug_modelist_lock;
+};
+
+/**
+ * struct adf_interface_ops - overlay engine implementation ops
+ *
+ * @base: common operations (see &struct adf_obj_ops)
+ *
+ * @supported_formats: list of fourccs the overlay engine can scan out
+ * @n_supported_formats: length of supported_formats, up to
+ * ADF_MAX_SUPPORTED_FORMATS
+ */
+struct adf_overlay_engine_ops {
+ const struct adf_obj_ops base;
+
+ /* required */
+ const u32 *supported_formats;
+ /* required */
+ const size_t n_supported_formats;
+};
+
+struct adf_overlay_engine {
+ struct adf_obj base;
+
+ const struct adf_overlay_engine_ops *ops;
+};
+
+#define adf_obj_to_device(ptr) \
+ container_of((ptr), struct adf_device, base)
+
+#define adf_obj_to_interface(ptr) \
+ container_of((ptr), struct adf_interface, base)
+
+#define adf_obj_to_overlay_engine(ptr) \
+ container_of((ptr), struct adf_overlay_engine, base)
+
+int __printf(4, 5) adf_device_init(struct adf_device *dev,
+ struct device *parent, const struct adf_device_ops *ops,
+ const char *fmt, ...);
+void adf_device_destroy(struct adf_device *dev);
+int __printf(7, 8) adf_interface_init(struct adf_interface *intf,
+ struct adf_device *dev, enum adf_interface_type type, u32 idx,
+ u32 flags, const struct adf_interface_ops *ops, const char *fmt,
+ ...);
+void adf_interface_destroy(struct adf_interface *intf);
+static inline struct adf_device *adf_interface_parent(
+ struct adf_interface *intf)
+{
+ return intf->base.parent;
+}
+int __printf(4, 5) adf_overlay_engine_init(struct adf_overlay_engine *eng,
+ struct adf_device *dev,
+ const struct adf_overlay_engine_ops *ops, const char *fmt, ...);
+void adf_overlay_engine_destroy(struct adf_overlay_engine *eng);
+static inline struct adf_device *adf_overlay_engine_parent(
+ struct adf_overlay_engine *eng)
+{
+ return eng->base.parent;
+}
+
+int adf_attachment_allow(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+
+const char *adf_obj_type_str(enum adf_obj_type type);
+const char *adf_interface_type_str(struct adf_interface *intf);
+const char *adf_event_type_str(struct adf_obj *obj, enum adf_event_type type);
+
+#define ADF_FORMAT_STR_SIZE 5
+void adf_format_str(u32 format, char buf[ADF_FORMAT_STR_SIZE]);
+int adf_format_validate_yuv(struct adf_device *dev, struct adf_buffer *buf,
+ u8 num_planes, u8 hsub, u8 vsub, u8 cpp[]);
+/**
+ * adf_format_validate_rgb - validate the number and size of planes in buffers
+ * with a custom RGB format.
+ *
+ * @dev: ADF device performing the validation
+ * @buf: buffer to validate
+ * @cpp: expected bytes per pixel
+ *
+ * adf_format_validate_rgb() is intended to be called as a helper from @dev's
+ * validate_custom_format() op. @buf must have a single RGB plane.
+ *
+ * Returns 0 if @buf has a single plane with sufficient size, or -EINVAL
+ * otherwise.
+ */
+static inline int adf_format_validate_rgb(struct adf_device *dev,
+ struct adf_buffer *buf, u8 cpp)
+{
+ return adf_format_validate_yuv(dev, buf, 1, 1, 1, &cpp);
+}
+
+int adf_event_get(struct adf_obj *obj, enum adf_event_type type);
+int adf_event_put(struct adf_obj *obj, enum adf_event_type type);
+int adf_event_notify(struct adf_obj *obj, struct adf_event *event);
+
+static inline void adf_vsync_get(struct adf_interface *intf)
+{
+ adf_event_get(&intf->base, ADF_EVENT_VSYNC);
+}
+
+static inline void adf_vsync_put(struct adf_interface *intf)
+{
+ adf_event_put(&intf->base, ADF_EVENT_VSYNC);
+}
+
+int adf_vsync_wait(struct adf_interface *intf, long timeout);
+void adf_vsync_notify(struct adf_interface *intf, ktime_t timestamp);
+
+int adf_hotplug_notify_connected(struct adf_interface *intf,
+ struct drm_mode_modeinfo *modelist, size_t n_modes);
+void adf_hotplug_notify_disconnected(struct adf_interface *intf);
+
+void adf_modeinfo_set_name(struct drm_mode_modeinfo *mode);
+void adf_modeinfo_set_vrefresh(struct drm_mode_modeinfo *mode);
+
+#endif /* _VIDEO_ADF_H */
diff --git a/include/video/adf_client.h b/include/video/adf_client.h
new file mode 100644
index 000000000000..983f2b6a5890
--- /dev/null
+++ b/include/video/adf_client.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIDEO_ADF_CLIENT_H_
+#define _VIDEO_ADF_CLIENT_H_
+
+#include <video/adf.h>
+
+int adf_interface_blank(struct adf_interface *intf, u8 state);
+u8 adf_interface_dpms_state(struct adf_interface *intf);
+
+void adf_interface_current_mode(struct adf_interface *intf,
+ struct drm_mode_modeinfo *mode);
+size_t adf_interface_modelist(struct adf_interface *intf,
+ struct drm_mode_modeinfo *modelist, size_t n_modes);
+int adf_interface_set_mode(struct adf_interface *intf,
+ struct drm_mode_modeinfo *mode);
+int adf_interface_get_screen_size(struct adf_interface *intf, u16 *width,
+ u16 *height);
+int adf_interface_simple_buffer_alloc(struct adf_interface *intf, u16 w, u16 h,
+ u32 format, struct dma_buf **dma_buf, u32 *offset, u32 *pitch);
+struct sync_fence *adf_interface_simple_post(struct adf_interface *intf,
+ struct adf_buffer *buf);
+
+bool adf_overlay_engine_supports_format(struct adf_overlay_engine *eng,
+ u32 format);
+
+size_t adf_device_attachments(struct adf_device *dev,
+ struct adf_attachment *attachments, size_t n_attachments);
+size_t adf_device_attachments_allowed(struct adf_device *dev,
+ struct adf_attachment *attachments, size_t n_attachments);
+bool adf_device_attached(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+bool adf_device_attach_allowed(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf);
+int adf_device_attach(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+int adf_device_detach(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+
+struct sync_fence *adf_device_post(struct adf_device *dev,
+ struct adf_interface **intfs, size_t n_intfs,
+ struct adf_buffer *bufs, size_t n_bufs, void *custom_data,
+ size_t custom_data_size);
+struct sync_fence *adf_device_post_nocopy(struct adf_device *dev,
+ struct adf_interface **intfs, size_t n_intfs,
+ struct adf_buffer *bufs, size_t n_bufs, void *custom_data,
+ size_t custom_data_size);
+
+#endif /* _VIDEO_ADF_CLIENT_H_ */
diff --git a/include/video/adf_fbdev.h b/include/video/adf_fbdev.h
new file mode 100644
index 000000000000..9c349144b5cd
--- /dev/null
+++ b/include/video/adf_fbdev.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIDEO_ADF_FBDEV_H_
+#define _VIDEO_ADF_FBDEV_H_
+
+#include <linux/fb.h>
+#include <video/adf.h>
+
+struct adf_fbdev {
+ struct adf_interface *intf;
+ struct adf_overlay_engine *eng;
+ struct fb_info *info;
+ u32 pseudo_palette[16];
+
+ bool open;
+
+ struct dma_buf *dma_buf;
+ u32 offset;
+ u32 pitch;
+ void *vaddr;
+ u32 format;
+
+ u16 default_xres_virtual;
+ u16 default_yres_virtual;
+ u32 default_format;
+};
+
+void adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo *mode,
+ struct fb_videomode *vmode);
+void adf_modeinfo_from_fb_videomode(const struct fb_videomode *vmode,
+ struct drm_mode_modeinfo *mode);
+
+int adf_fbdev_init(struct adf_fbdev *fbdev, struct adf_interface *interface,
+ struct adf_overlay_engine *eng,
+ u16 xres_virtual, u16 yres_virtual, u32 format,
+ struct fb_ops *fbops, const char *fmt, ...);
+void adf_fbdev_destroy(struct adf_fbdev *fbdev);
+
+int adf_fbdev_open(struct fb_info *info, int user);
+int adf_fbdev_release(struct fb_info *info, int user);
+int adf_fbdev_check_var(struct fb_var_screeninfo *var, struct fb_info *info);
+int adf_fbdev_set_par(struct fb_info *info);
+int adf_fbdev_blank(int blank, struct fb_info *info);
+int adf_fbdev_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
+int adf_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma);
+
+#endif /* _VIDEO_ADF_FBDEV_H_ */
diff --git a/include/video/adf_format.h b/include/video/adf_format.h
new file mode 100644
index 000000000000..e03182cdcb06
--- /dev/null
+++ b/include/video/adf_format.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIDEO_ADF_FORMAT_H
+#define _VIDEO_ADF_FORMAT_H
+
+bool adf_format_is_standard(u32 format);
+bool adf_format_is_rgb(u32 format);
+u8 adf_format_num_planes(u32 format);
+u8 adf_format_bpp(u32 format);
+u8 adf_format_plane_cpp(u32 format, int plane);
+u8 adf_format_horz_chroma_subsampling(u32 format);
+u8 adf_format_vert_chroma_subsampling(u32 format);
+
+#endif /* _VIDEO_ADF_FORMAT_H */
diff --git a/include/video/adf_memblock.h b/include/video/adf_memblock.h
new file mode 100644
index 000000000000..6256e0eebcc9
--- /dev/null
+++ b/include/video/adf_memblock.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIDEO_ADF_MEMBLOCK_H_
+#define _VIDEO_ADF_MEMBLOCK_H_
+
+struct dma_buf *adf_memblock_export(phys_addr_t base, size_t size, int flags);
+
+#endif /* _VIDEO_ADF_MEMBLOCK_H_ */
diff --git a/init/main.c b/init/main.c
index 9484f4ba88d0..e83ac04fda97 100644
--- a/init/main.c
+++ b/init/main.c
@@ -74,6 +74,7 @@
#include <linux/ptrace.h>
#include <linux/blkdev.h>
#include <linux/elevator.h>
+#include <linux/random.h>
#include <asm/io.h>
#include <asm/bugs.h>
@@ -777,6 +778,7 @@ static void __init do_basic_setup(void)
do_ctors();
usermodehelper_enable();
do_initcalls();
+ random_int_secret_init();
}
static void __init do_pre_smp_initcalls(void)
diff --git a/ipc/msg.c b/ipc/msg.c
index f8fbe2c095ce..558aa91186b6 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -70,8 +70,6 @@ struct msg_sender {
#define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS])
-#define msg_unlock(msq) ipc_unlock(&(msq)->q_perm)
-
static void freeque(struct ipc_namespace *, struct kern_ipc_perm *);
static int newque(struct ipc_namespace *, struct ipc_params *);
#ifdef CONFIG_PROC_FS
@@ -141,27 +139,23 @@ void __init msg_init(void)
IPC_MSG_IDS, sysvipc_msg_proc_show);
}
-/*
- * msg_lock_(check_) routines are called in the paths where the rw_mutex
- * is not held.
- */
-static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id)
+static inline struct msg_queue *msq_obtain_object(struct ipc_namespace *ns, int id)
{
- struct kern_ipc_perm *ipcp = ipc_lock(&msg_ids(ns), id);
+ struct kern_ipc_perm *ipcp = ipc_obtain_object(&msg_ids(ns), id);
if (IS_ERR(ipcp))
- return (struct msg_queue *)ipcp;
+ return ERR_CAST(ipcp);
return container_of(ipcp, struct msg_queue, q_perm);
}
-static inline struct msg_queue *msg_lock_check(struct ipc_namespace *ns,
- int id)
+static inline struct msg_queue *msq_obtain_object_check(struct ipc_namespace *ns,
+ int id)
{
- struct kern_ipc_perm *ipcp = ipc_lock_check(&msg_ids(ns), id);
+ struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&msg_ids(ns), id);
if (IS_ERR(ipcp))
- return (struct msg_queue *)ipcp;
+ return ERR_CAST(ipcp);
return container_of(ipcp, struct msg_queue, q_perm);
}
@@ -171,12 +165,21 @@ static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
ipc_rmid(&msg_ids(ns), &s->q_perm);
}
+static void msg_rcu_free(struct rcu_head *head)
+{
+ struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
+ struct msg_queue *msq = ipc_rcu_to_struct(p);
+
+ security_msg_queue_free(msq);
+ ipc_rcu_free(head);
+}
+
/**
* newque - Create a new msg queue
* @ns: namespace
* @params: ptr to the structure that contains the key and msgflg
*
- * Called with msg_ids.rw_mutex held (writer)
+ * Called with msg_ids.rwsem held (writer)
*/
static int newque(struct ipc_namespace *ns, struct ipc_params *params)
{
@@ -195,17 +198,14 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
msq->q_perm.security = NULL;
retval = security_msg_queue_alloc(msq);
if (retval) {
- ipc_rcu_putref(msq);
+ ipc_rcu_putref(msq, ipc_rcu_free);
return retval;
}
- /*
- * ipc_addid() locks msq
- */
+ /* ipc_addid() locks msq upon success. */
id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
if (id < 0) {
- security_msg_queue_free(msq);
- ipc_rcu_putref(msq);
+ ipc_rcu_putref(msq, msg_rcu_free);
return id;
}
@@ -218,7 +218,8 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
INIT_LIST_HEAD(&msq->q_receivers);
INIT_LIST_HEAD(&msq->q_senders);
- msg_unlock(msq);
+ ipc_unlock_object(&msq->q_perm);
+ rcu_read_unlock();
return msq->q_perm.id;
}
@@ -264,8 +265,8 @@ static void expunge_all(struct msg_queue *msq, int res)
* removes the message queue from message queue ID IDR, and cleans up all the
* messages associated with this queue.
*
- * msg_ids.rw_mutex (writer) and the spinlock for this message queue are held
- * before freeque() is called. msg_ids.rw_mutex remains locked on exit.
+ * msg_ids.rwsem (writer) and the spinlock for this message queue are held
+ * before freeque() is called. msg_ids.rwsem remains locked on exit.
*/
static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
{
@@ -275,19 +276,19 @@ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
expunge_all(msq, -EIDRM);
ss_wakeup(&msq->q_senders, 1);
msg_rmid(ns, msq);
- msg_unlock(msq);
+ ipc_unlock_object(&msq->q_perm);
+ rcu_read_unlock();
list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) {
atomic_dec(&ns->msg_hdrs);
free_msg(msg);
}
atomic_sub(msq->q_cbytes, &ns->msg_bytes);
- security_msg_queue_free(msq);
- ipc_rcu_putref(msq);
+ ipc_rcu_putref(msq, msg_rcu_free);
}
/*
- * Called with msg_ids.rw_mutex and ipcp locked.
+ * Called with msg_ids.rwsem and ipcp locked.
*/
static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg)
{
@@ -391,9 +392,9 @@ copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version)
}
/*
- * This function handles some msgctl commands which require the rw_mutex
+ * This function handles some msgctl commands which require the rwsem
* to be held in write mode.
- * NOTE: no locks must be held, the rw_mutex is taken inside this function.
+ * NOTE: no locks must be held, the rwsem is taken inside this function.
*/
static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
struct msqid_ds __user *buf, int version)
@@ -408,31 +409,39 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
return -EFAULT;
}
- ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd,
- &msqid64.msg_perm, msqid64.msg_qbytes);
- if (IS_ERR(ipcp))
- return PTR_ERR(ipcp);
+ down_write(&msg_ids(ns).rwsem);
+ rcu_read_lock();
+
+ ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd,
+ &msqid64.msg_perm, msqid64.msg_qbytes);
+ if (IS_ERR(ipcp)) {
+ err = PTR_ERR(ipcp);
+ goto out_unlock1;
+ }
msq = container_of(ipcp, struct msg_queue, q_perm);
err = security_msg_queue_msgctl(msq, cmd);
if (err)
- goto out_unlock;
+ goto out_unlock1;
switch (cmd) {
case IPC_RMID:
+ ipc_lock_object(&msq->q_perm);
+ /* freeque unlocks the ipc object and rcu */
freeque(ns, ipcp);
goto out_up;
case IPC_SET:
if (msqid64.msg_qbytes > ns->msg_ctlmnb &&
!capable(CAP_SYS_RESOURCE)) {
err = -EPERM;
- goto out_unlock;
+ goto out_unlock1;
}
+ ipc_lock_object(&msq->q_perm);
err = ipc_update_perm(&msqid64.msg_perm, ipcp);
if (err)
- goto out_unlock;
+ goto out_unlock0;
msq->q_qbytes = msqid64.msg_qbytes;
@@ -448,25 +457,23 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
break;
default:
err = -EINVAL;
+ goto out_unlock1;
}
-out_unlock:
- msg_unlock(msq);
+
+out_unlock0:
+ ipc_unlock_object(&msq->q_perm);
+out_unlock1:
+ rcu_read_unlock();
out_up:
- up_write(&msg_ids(ns).rw_mutex);
+ up_write(&msg_ids(ns).rwsem);
return err;
}
-SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
+static int msgctl_nolock(struct ipc_namespace *ns, int msqid,
+ int cmd, int version, void __user *buf)
{
+ int err;
struct msg_queue *msq;
- int err, version;
- struct ipc_namespace *ns;
-
- if (msqid < 0 || cmd < 0)
- return -EINVAL;
-
- version = ipc_parse_version(&cmd);
- ns = current->nsproxy->ipc_ns;
switch (cmd) {
case IPC_INFO:
@@ -477,6 +484,7 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
if (!buf)
return -EFAULT;
+
/*
* We must not return kernel stack data.
* due to padding, it's not enough
@@ -492,7 +500,7 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
msginfo.msgmnb = ns->msg_ctlmnb;
msginfo.msgssz = MSGSSZ;
msginfo.msgseg = MSGSEG;
- down_read(&msg_ids(ns).rw_mutex);
+ down_read(&msg_ids(ns).rwsem);
if (cmd == MSG_INFO) {
msginfo.msgpool = msg_ids(ns).in_use;
msginfo.msgmap = atomic_read(&ns->msg_hdrs);
@@ -503,12 +511,13 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
msginfo.msgtql = MSGTQL;
}
max_id = ipc_get_maxid(&msg_ids(ns));
- up_read(&msg_ids(ns).rw_mutex);
+ up_read(&msg_ids(ns).rwsem);
if (copy_to_user(buf, &msginfo, sizeof(struct msginfo)))
return -EFAULT;
return (max_id < 0) ? 0 : max_id;
}
- case MSG_STAT: /* msqid is an index rather than a msg queue id */
+
+ case MSG_STAT:
case IPC_STAT:
{
struct msqid64_ds tbuf;
@@ -517,17 +526,25 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
if (!buf)
return -EFAULT;
+ memset(&tbuf, 0, sizeof(tbuf));
+
+ rcu_read_lock();
if (cmd == MSG_STAT) {
- msq = msg_lock(ns, msqid);
- if (IS_ERR(msq))
- return PTR_ERR(msq);
+ msq = msq_obtain_object(ns, msqid);
+ if (IS_ERR(msq)) {
+ err = PTR_ERR(msq);
+ goto out_unlock;
+ }
success_return = msq->q_perm.id;
} else {
- msq = msg_lock_check(ns, msqid);
- if (IS_ERR(msq))
- return PTR_ERR(msq);
+ msq = msq_obtain_object_check(ns, msqid);
+ if (IS_ERR(msq)) {
+ err = PTR_ERR(msq);
+ goto out_unlock;
+ }
success_return = 0;
}
+
err = -EACCES;
if (ipcperms(ns, &msq->q_perm, S_IRUGO))
goto out_unlock;
@@ -536,8 +553,6 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
if (err)
goto out_unlock;
- memset(&tbuf, 0, sizeof(tbuf));
-
kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm);
tbuf.msg_stime = msq->q_stime;
tbuf.msg_rtime = msq->q_rtime;
@@ -547,24 +562,48 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
tbuf.msg_qbytes = msq->q_qbytes;
tbuf.msg_lspid = msq->q_lspid;
tbuf.msg_lrpid = msq->q_lrpid;
- msg_unlock(msq);
+ rcu_read_unlock();
+
if (copy_msqid_to_user(buf, &tbuf, version))
return -EFAULT;
return success_return;
}
- case IPC_SET:
- case IPC_RMID:
- err = msgctl_down(ns, msqid, cmd, buf, version);
- return err;
+
default:
- return -EINVAL;
+ return -EINVAL;
}
+ return err;
out_unlock:
- msg_unlock(msq);
+ rcu_read_unlock();
return err;
}
+SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
+{
+ int version;
+ struct ipc_namespace *ns;
+
+ if (msqid < 0 || cmd < 0)
+ return -EINVAL;
+
+ version = ipc_parse_version(&cmd);
+ ns = current->nsproxy->ipc_ns;
+
+ switch (cmd) {
+ case IPC_INFO:
+ case MSG_INFO:
+ case MSG_STAT: /* msqid is an index rather than a msg queue id */
+ case IPC_STAT:
+ return msgctl_nolock(ns, msqid, cmd, version, buf);
+ case IPC_SET:
+ case IPC_RMID:
+ return msgctl_down(ns, msqid, cmd, buf, version);
+ default:
+ return -EINVAL;
+ }
+}
+
static int testmsg(struct msg_msg *msg, long type, int mode)
{
switch(mode)
@@ -640,22 +679,31 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
msg->m_type = mtype;
msg->m_ts = msgsz;
- msq = msg_lock_check(ns, msqid);
+ rcu_read_lock();
+ msq = msq_obtain_object_check(ns, msqid);
if (IS_ERR(msq)) {
err = PTR_ERR(msq);
- goto out_free;
+ goto out_unlock1;
}
+ ipc_lock_object(&msq->q_perm);
+
for (;;) {
struct msg_sender s;
err = -EACCES;
if (ipcperms(ns, &msq->q_perm, S_IWUGO))
- goto out_unlock_free;
+ goto out_unlock0;
+
+ /* raced with RMID? */
+ if (msq->q_perm.deleted) {
+ err = -EIDRM;
+ goto out_unlock0;
+ }
err = security_msg_queue_msgsnd(msq, msg, msgflg);
if (err)
- goto out_unlock_free;
+ goto out_unlock0;
if (msgsz + msq->q_cbytes <= msq->q_qbytes &&
1 + msq->q_qnum <= msq->q_qbytes) {
@@ -665,32 +713,37 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
/* queue full, wait: */
if (msgflg & IPC_NOWAIT) {
err = -EAGAIN;
- goto out_unlock_free;
+ goto out_unlock0;
}
+
ss_add(msq, &s);
if (!ipc_rcu_getref(msq)) {
err = -EIDRM;
- goto out_unlock_free;
+ goto out_unlock0;
}
- msg_unlock(msq);
+ ipc_unlock_object(&msq->q_perm);
+ rcu_read_unlock();
schedule();
- ipc_lock_by_ptr(&msq->q_perm);
- ipc_rcu_putref(msq);
+ rcu_read_lock();
+ ipc_lock_object(&msq->q_perm);
+
+ ipc_rcu_putref(msq, ipc_rcu_free);
if (msq->q_perm.deleted) {
err = -EIDRM;
- goto out_unlock_free;
+ goto out_unlock0;
}
+
ss_del(&s);
if (signal_pending(current)) {
err = -ERESTARTNOHAND;
- goto out_unlock_free;
+ goto out_unlock0;
}
- }
+ }
msq->q_lspid = task_tgid_vnr(current);
msq->q_stime = get_seconds();
@@ -706,9 +759,10 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
err = 0;
msg = NULL;
-out_unlock_free:
- msg_unlock(msq);
-out_free:
+out_unlock0:
+ ipc_unlock_object(&msq->q_perm);
+out_unlock1:
+ rcu_read_unlock();
if (msg != NULL)
free_msg(msg);
return err;
@@ -817,21 +871,19 @@ static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode)
return found ?: ERR_PTR(-EAGAIN);
}
-
-long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
- int msgflg,
+long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg,
long (*msg_handler)(void __user *, struct msg_msg *, size_t))
{
- struct msg_queue *msq;
- struct msg_msg *msg;
int mode;
+ struct msg_queue *msq;
struct ipc_namespace *ns;
- struct msg_msg *copy = NULL;
+ struct msg_msg *msg, *copy = NULL;
ns = current->nsproxy->ipc_ns;
if (msqid < 0 || (long) bufsz < 0)
return -EINVAL;
+
if (msgflg & MSG_COPY) {
copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax));
if (IS_ERR(copy))
@@ -839,8 +891,10 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
}
mode = convert_mode(&msgtyp, msgflg);
- msq = msg_lock_check(ns, msqid);
+ rcu_read_lock();
+ msq = msq_obtain_object_check(ns, msqid);
if (IS_ERR(msq)) {
+ rcu_read_unlock();
free_copy(copy);
return PTR_ERR(msq);
}
@@ -850,10 +904,17 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
msg = ERR_PTR(-EACCES);
if (ipcperms(ns, &msq->q_perm, S_IRUGO))
- goto out_unlock;
+ goto out_unlock1;
- msg = find_msg(msq, &msgtyp, mode);
+ ipc_lock_object(&msq->q_perm);
+
+ /* raced with RMID? */
+ if (msq->q_perm.deleted) {
+ msg = ERR_PTR(-EIDRM);
+ goto out_unlock0;
+ }
+ msg = find_msg(msq, &msgtyp, mode);
if (!IS_ERR(msg)) {
/*
* Found a suitable message.
@@ -861,7 +922,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
*/
if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {
msg = ERR_PTR(-E2BIG);
- goto out_unlock;
+ goto out_unlock0;
}
/*
* If we are copying, then do not unlink message and do
@@ -869,8 +930,9 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
*/
if (msgflg & MSG_COPY) {
msg = copy_msg(msg, copy);
- goto out_unlock;
+ goto out_unlock0;
}
+
list_del(&msg->m_list);
msq->q_qnum--;
msq->q_rtime = get_seconds();
@@ -879,14 +941,16 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
atomic_sub(msg->m_ts, &ns->msg_bytes);
atomic_dec(&ns->msg_hdrs);
ss_wakeup(&msq->q_senders, 0);
- msg_unlock(msq);
- break;
+
+ goto out_unlock0;
}
+
/* No message waiting. Wait for a message */
if (msgflg & IPC_NOWAIT) {
msg = ERR_PTR(-ENOMSG);
- goto out_unlock;
+ goto out_unlock0;
}
+
list_add_tail(&msr_d.r_list, &msq->q_receivers);
msr_d.r_tsk = current;
msr_d.r_msgtype = msgtyp;
@@ -897,8 +961,9 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
msr_d.r_maxsize = bufsz;
msr_d.r_msg = ERR_PTR(-EAGAIN);
current->state = TASK_INTERRUPTIBLE;
- msg_unlock(msq);
+ ipc_unlock_object(&msq->q_perm);
+ rcu_read_unlock();
schedule();
/* Lockless receive, part 1:
@@ -909,7 +974,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
* Prior to destruction, expunge_all(-EIRDM) changes r_msg.
* Thus if r_msg is -EAGAIN, then the queue not yet destroyed.
* rcu_read_lock() prevents preemption between reading r_msg
- * and the spin_lock() inside ipc_lock_by_ptr().
+ * and acquiring the q_perm.lock in ipc_lock_object().
*/
rcu_read_lock();
@@ -928,32 +993,34 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
* If there is a message or an error then accept it without
* locking.
*/
- if (msg != ERR_PTR(-EAGAIN)) {
- rcu_read_unlock();
- break;
- }
+ if (msg != ERR_PTR(-EAGAIN))
+ goto out_unlock1;
/* Lockless receive, part 3:
* Acquire the queue spinlock.
*/
- ipc_lock_by_ptr(&msq->q_perm);
- rcu_read_unlock();
+ ipc_lock_object(&msq->q_perm);
/* Lockless receive, part 4:
* Repeat test after acquiring the spinlock.
*/
msg = (struct msg_msg*)msr_d.r_msg;
if (msg != ERR_PTR(-EAGAIN))
- goto out_unlock;
+ goto out_unlock0;
list_del(&msr_d.r_list);
if (signal_pending(current)) {
msg = ERR_PTR(-ERESTARTNOHAND);
-out_unlock:
- msg_unlock(msq);
- break;
+ goto out_unlock0;
}
+
+ ipc_unlock_object(&msq->q_perm);
}
+
+out_unlock0:
+ ipc_unlock_object(&msq->q_perm);
+out_unlock1:
+ rcu_read_unlock();
if (IS_ERR(msg)) {
free_copy(copy);
return PTR_ERR(msg);
diff --git a/ipc/namespace.c b/ipc/namespace.c
index 7ee61bf44933..aba9a586c667 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -81,7 +81,7 @@ void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
int next_id;
int total, in_use;
- down_write(&ids->rw_mutex);
+ down_write(&ids->rwsem);
in_use = ids->in_use;
@@ -89,11 +89,12 @@ void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
perm = idr_find(&ids->ipcs_idr, next_id);
if (perm == NULL)
continue;
- ipc_lock_by_ptr(perm);
+ rcu_read_lock();
+ ipc_lock_object(perm);
free(ns, perm);
total++;
}
- up_write(&ids->rw_mutex);
+ up_write(&ids->rwsem);
}
static void free_ipc_ns(struct ipc_namespace *ns)
diff --git a/ipc/sem.c b/ipc/sem.c
index 70480a3aa698..8c4f59b0204a 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -95,8 +95,12 @@ struct sem {
int semval; /* current value */
int sempid; /* pid of last operation */
spinlock_t lock; /* spinlock for fine-grained semtimedop */
- struct list_head sem_pending; /* pending single-sop operations */
-};
+ struct list_head pending_alter; /* pending single-sop operations */
+ /* that alter the semaphore */
+ struct list_head pending_const; /* pending single-sop operations */
+ /* that do not alter the semaphore*/
+ time_t sem_otime; /* candidate for sem_otime */
+} ____cacheline_aligned_in_smp;
/* One queue for each sleeping process in the system. */
struct sem_queue {
@@ -150,12 +154,15 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
#define SEMOPM_FAST 64 /* ~ 372 bytes on stack */
/*
- * linked list protection:
+ * Locking:
* sem_undo.id_next,
- * sem_array.sem_pending{,last},
- * sem_array.sem_undo: sem_lock() for read/write
+ * sem_array.complex_count,
+ * sem_array.pending{_alter,_cont},
+ * sem_array.sem_undo: global sem_lock() for read/write
* sem_undo.proc_next: only "current" is allowed to read/write that field.
*
+ * sem_array.sem_base[i].pending_{const,alter}:
+ * global or semaphore sem_lock() for read/write
*/
#define sc_semmsl sem_ctls[0]
@@ -189,77 +196,176 @@ void __init sem_init (void)
IPC_SEM_IDS, sysvipc_sem_proc_show);
}
+/**
+ * unmerge_queues - unmerge queues, if possible.
+ * @sma: semaphore array
+ *
+ * The function unmerges the wait queues if complex_count is 0.
+ * It must be called prior to dropping the global semaphore array lock.
+ */
+static void unmerge_queues(struct sem_array *sma)
+{
+ struct sem_queue *q, *tq;
+
+ /* complex operations still around? */
+ if (sma->complex_count)
+ return;
+ /*
+ * We will switch back to simple mode.
+ * Move all pending operation back into the per-semaphore
+ * queues.
+ */
+ list_for_each_entry_safe(q, tq, &sma->pending_alter, list) {
+ struct sem *curr;
+ curr = &sma->sem_base[q->sops[0].sem_num];
+
+ list_add_tail(&q->list, &curr->pending_alter);
+ }
+ INIT_LIST_HEAD(&sma->pending_alter);
+}
+
+/**
+ * merge_queues - Merge single semop queues into global queue
+ * @sma: semaphore array
+ *
+ * This function merges all per-semaphore queues into the global queue.
+ * It is necessary to achieve FIFO ordering for the pending single-sop
+ * operations when a multi-semop operation must sleep.
+ * Only the alter operations must be moved, the const operations can stay.
+ */
+static void merge_queues(struct sem_array *sma)
+{
+ int i;
+ for (i = 0; i < sma->sem_nsems; i++) {
+ struct sem *sem = sma->sem_base + i;
+
+ list_splice_init(&sem->pending_alter, &sma->pending_alter);
+ }
+}
+
+static void sem_rcu_free(struct rcu_head *head)
+{
+ struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
+ struct sem_array *sma = ipc_rcu_to_struct(p);
+
+ security_sem_free(sma);
+ ipc_rcu_free(head);
+}
+
+/*
+ * Wait until all currently ongoing simple ops have completed.
+ * Caller must own sem_perm.lock.
+ * New simple ops cannot start, because simple ops first check
+ * that sem_perm.lock is free.
+ * that a) sem_perm.lock is free and b) complex_count is 0.
+ */
+static void sem_wait_array(struct sem_array *sma)
+{
+ int i;
+ struct sem *sem;
+
+ if (sma->complex_count) {
+ /* The thread that increased sma->complex_count waited on
+ * all sem->lock locks. Thus we don't need to wait again.
+ */
+ return;
+ }
+
+ for (i = 0; i < sma->sem_nsems; i++) {
+ sem = sma->sem_base + i;
+ spin_unlock_wait(&sem->lock);
+ }
+}
+
/*
* If the request contains only one semaphore operation, and there are
* no complex transactions pending, lock only the semaphore involved.
* Otherwise, lock the entire semaphore array, since we either have
* multiple semaphores in our own semops, or we need to look at
* semaphores from other pending complex operations.
- *
- * Carefully guard against sma->complex_count changing between zero
- * and non-zero while we are spinning for the lock. The value of
- * sma->complex_count cannot change while we are holding the lock,
- * so sem_unlock should be fine.
- *
- * The global lock path checks that all the local locks have been released,
- * checking each local lock once. This means that the local lock paths
- * cannot start their critical sections while the global lock is held.
*/
static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
int nsops)
{
- int locknum;
- again:
- if (nsops == 1 && !sma->complex_count) {
- struct sem *sem = sma->sem_base + sops->sem_num;
+ struct sem *sem;
- /* Lock just the semaphore we are interested in. */
- spin_lock(&sem->lock);
+ if (nsops != 1) {
+ /* Complex operation - acquire a full lock */
+ ipc_lock_object(&sma->sem_perm);
- /*
- * If sma->complex_count was set while we were spinning,
- * we may need to look at things we did not lock here.
+ /* And wait until all simple ops that are processed
+ * right now have dropped their locks.
*/
- if (unlikely(sma->complex_count)) {
- spin_unlock(&sem->lock);
- goto lock_array;
- }
+ sem_wait_array(sma);
+ return -1;
+ }
+
+ /*
+ * Only one semaphore affected - try to optimize locking.
+ * The rules are:
+ * - optimized locking is possible if no complex operation
+ * is either enqueued or processed right now.
+ * - The test for enqueued complex ops is simple:
+ * sma->complex_count != 0
+ * - Testing for complex ops that are processed right now is
+ * a bit more difficult. Complex ops acquire the full lock
+ * and first wait that the running simple ops have completed.
+ * (see above)
+ * Thus: If we own a simple lock and the global lock is free
+ * and complex_count is now 0, then it will stay 0 and
+ * thus just locking sem->lock is sufficient.
+ */
+ sem = sma->sem_base + sops->sem_num;
+ if (sma->complex_count == 0) {
/*
- * Another process is holding the global lock on the
- * sem_array; we cannot enter our critical section,
- * but have to wait for the global lock to be released.
+ * It appears that no complex operation is around.
+ * Acquire the per-semaphore lock.
*/
- if (unlikely(spin_is_locked(&sma->sem_perm.lock))) {
- spin_unlock(&sem->lock);
- spin_unlock_wait(&sma->sem_perm.lock);
- goto again;
+ spin_lock(&sem->lock);
+
+ /* Then check that the global lock is free */
+ if (!spin_is_locked(&sma->sem_perm.lock)) {
+ /* spin_is_locked() is not a memory barrier */
+ smp_mb();
+
+ /* Now repeat the test of complex_count:
+ * It can't change anymore until we drop sem->lock.
+ * Thus: if is now 0, then it will stay 0.
+ */
+ if (sma->complex_count == 0) {
+ /* fast path successful! */
+ return sops->sem_num;
+ }
}
+ spin_unlock(&sem->lock);
+ }
+
+ /* slow path: acquire the full lock */
+ ipc_lock_object(&sma->sem_perm);
- locknum = sops->sem_num;
+ if (sma->complex_count == 0) {
+ /* False alarm:
+ * There is no complex operation, thus we can switch
+ * back to the fast path.
+ */
+ spin_lock(&sem->lock);
+ ipc_unlock_object(&sma->sem_perm);
+ return sops->sem_num;
} else {
- int i;
- /*
- * Lock the semaphore array, and wait for all of the
- * individual semaphore locks to go away. The code
- * above ensures no new single-lock holders will enter
- * their critical section while the array lock is held.
+ /* Not a false alarm, thus complete the sequence for a
+ * full lock.
*/
- lock_array:
- spin_lock(&sma->sem_perm.lock);
- for (i = 0; i < sma->sem_nsems; i++) {
- struct sem *sem = sma->sem_base + i;
- spin_unlock_wait(&sem->lock);
- }
- locknum = -1;
+ sem_wait_array(sma);
+ return -1;
}
- return locknum;
}
static inline void sem_unlock(struct sem_array *sma, int locknum)
{
if (locknum == -1) {
- spin_unlock(&sma->sem_perm.lock);
+ unmerge_queues(sma);
+ ipc_unlock_object(&sma->sem_perm);
} else {
struct sem *sem = sma->sem_base + locknum;
spin_unlock(&sem->lock);
@@ -267,7 +373,7 @@ static inline void sem_unlock(struct sem_array *sma, int locknum)
}
/*
- * sem_lock_(check_) routines are called in the paths where the rw_mutex
+ * sem_lock_(check_) routines are called in the paths where the rwsem
* is not held.
*
* The caller holds the RCU read lock.
@@ -319,12 +425,7 @@ static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns
static inline void sem_lock_and_putref(struct sem_array *sma)
{
sem_lock(sma, NULL, -1);
- ipc_rcu_putref(sma);
-}
-
-static inline void sem_putref(struct sem_array *sma)
-{
- ipc_rcu_putref(sma);
+ ipc_rcu_putref(sma, ipc_rcu_free);
}
static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
@@ -337,7 +438,7 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
* Without the check/retry algorithm a lockless wakeup is possible:
* - queue.status is initialized to -EINTR before blocking.
* - wakeup is performed by
- * * unlinking the queue entry from sma->sem_pending
+ * * unlinking the queue entry from the pending list
* * setting queue.status to IN_WAKEUP
* This is the notification for the blocked thread that a
* result value is imminent.
@@ -371,7 +472,7 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
* @ns: namespace
* @params: ptr to the structure that contains key, semflg and nsems
*
- * Called with sem_ids.rw_mutex held (as a writer)
+ * Called with sem_ids.rwsem held (as a writer)
*/
static int newary(struct ipc_namespace *ns, struct ipc_params *params)
@@ -403,14 +504,13 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
sma->sem_perm.security = NULL;
retval = security_sem_alloc(sma);
if (retval) {
- ipc_rcu_putref(sma);
+ ipc_rcu_putref(sma, ipc_rcu_free);
return retval;
}
id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
if (id < 0) {
- security_sem_free(sma);
- ipc_rcu_putref(sma);
+ ipc_rcu_putref(sma, sem_rcu_free);
return id;
}
ns->used_sems += nsems;
@@ -418,12 +518,14 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
sma->sem_base = (struct sem *) &sma[1];
for (i = 0; i < nsems; i++) {
- INIT_LIST_HEAD(&sma->sem_base[i].sem_pending);
+ INIT_LIST_HEAD(&sma->sem_base[i].pending_alter);
+ INIT_LIST_HEAD(&sma->sem_base[i].pending_const);
spin_lock_init(&sma->sem_base[i].lock);
}
sma->complex_count = 0;
- INIT_LIST_HEAD(&sma->sem_pending);
+ INIT_LIST_HEAD(&sma->pending_alter);
+ INIT_LIST_HEAD(&sma->pending_const);
INIT_LIST_HEAD(&sma->list_id);
sma->sem_nsems = nsems;
sma->sem_ctime = get_seconds();
@@ -435,7 +537,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
/*
- * Called with sem_ids.rw_mutex and ipcp locked.
+ * Called with sem_ids.rwsem and ipcp locked.
*/
static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg)
{
@@ -446,7 +548,7 @@ static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg)
}
/*
- * Called with sem_ids.rw_mutex and ipcp locked.
+ * Called with sem_ids.rwsem and ipcp locked.
*/
static inline int sem_more_checks(struct kern_ipc_perm *ipcp,
struct ipc_params *params)
@@ -482,12 +584,19 @@ SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
}
-/*
- * Determine whether a sequence of semaphore operations would succeed
- * all at once. Return 0 if yes, 1 if need to sleep, else return error code.
+/** perform_atomic_semop - Perform (if possible) a semaphore operation
+ * @sma: semaphore array
+ * @sops: array with operations that should be checked
+ * @nsems: number of sops
+ * @un: undo array
+ * @pid: pid that did the change
+ *
+ * Returns 0 if the operation was possible.
+ * Returns 1 if the operation is impossible, the caller must sleep.
+ * Negative values are error codes.
*/
-static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops,
+static int perform_atomic_semop(struct sem_array *sma, struct sembuf *sops,
int nsops, struct sem_undo *un, int pid)
{
int result, sem_op;
@@ -609,60 +718,132 @@ static void unlink_queue(struct sem_array *sma, struct sem_queue *q)
* update_queue is O(N^2) when it restarts scanning the whole queue of
* waiting operations. Therefore this function checks if the restart is
* really necessary. It is called after a previously waiting operation
- * was completed.
+ * modified the array.
+ * Note that wait-for-zero operations are handled without restart.
*/
static int check_restart(struct sem_array *sma, struct sem_queue *q)
{
- struct sem *curr;
- struct sem_queue *h;
-
- /* if the operation didn't modify the array, then no restart */
- if (q->alter == 0)
- return 0;
-
- /* pending complex operations are too difficult to analyse */
- if (sma->complex_count)
+ /* pending complex alter operations are too difficult to analyse */
+ if (!list_empty(&sma->pending_alter))
return 1;
/* we were a sleeping complex operation. Too difficult */
if (q->nsops > 1)
return 1;
- curr = sma->sem_base + q->sops[0].sem_num;
+ /* It is impossible that someone waits for the new value:
+ * - complex operations always restart.
+ * - wait-for-zero are handled seperately.
+ * - q is a previously sleeping simple operation that
+ * altered the array. It must be a decrement, because
+ * simple increments never sleep.
+ * - If there are older (higher priority) decrements
+ * in the queue, then they have observed the original
+ * semval value and couldn't proceed. The operation
+ * decremented to value - thus they won't proceed either.
+ */
+ return 0;
+}
- /* No-one waits on this queue */
- if (list_empty(&curr->sem_pending))
- return 0;
+/**
+ * wake_const_ops(sma, semnum, pt) - Wake up non-alter tasks
+ * @sma: semaphore array.
+ * @semnum: semaphore that was modified.
+ * @pt: list head for the tasks that must be woken up.
+ *
+ * wake_const_ops must be called after a semaphore in a semaphore array
+ * was set to 0. If complex const operations are pending, wake_const_ops must
+ * be called with semnum = -1, as well as with the number of each modified
+ * semaphore.
+ * The tasks that must be woken up are added to @pt. The return code
+ * is stored in q->pid.
+ * The function returns 1 if at least one operation was completed successfully.
+ */
+static int wake_const_ops(struct sem_array *sma, int semnum,
+ struct list_head *pt)
+{
+ struct sem_queue *q;
+ struct list_head *walk;
+ struct list_head *pending_list;
+ int semop_completed = 0;
- /* the new semaphore value */
- if (curr->semval) {
- /* It is impossible that someone waits for the new value:
- * - q is a previously sleeping simple operation that
- * altered the array. It must be a decrement, because
- * simple increments never sleep.
- * - The value is not 0, thus wait-for-zero won't proceed.
- * - If there are older (higher priority) decrements
- * in the queue, then they have observed the original
- * semval value and couldn't proceed. The operation
- * decremented to value - thus they won't proceed either.
+ if (semnum == -1)
+ pending_list = &sma->pending_const;
+ else
+ pending_list = &sma->sem_base[semnum].pending_const;
+
+ walk = pending_list->next;
+ while (walk != pending_list) {
+ int error;
+
+ q = container_of(walk, struct sem_queue, list);
+ walk = walk->next;
+
+ error = perform_atomic_semop(sma, q->sops, q->nsops,
+ q->undo, q->pid);
+
+ if (error <= 0) {
+ /* operation completed, remove from queue & wakeup */
+
+ unlink_queue(sma, q);
+
+ wake_up_sem_queue_prepare(pt, q, error);
+ if (error == 0)
+ semop_completed = 1;
+ }
+ }
+ return semop_completed;
+}
+
+/**
+ * do_smart_wakeup_zero(sma, sops, nsops, pt) - wakeup all wait for zero tasks
+ * @sma: semaphore array
+ * @sops: operations that were performed
+ * @nsops: number of operations
+ * @pt: list head of the tasks that must be woken up.
+ *
+ * do_smart_wakeup_zero() checks all required queue for wait-for-zero
+ * operations, based on the actual changes that were performed on the
+ * semaphore array.
+ * The function returns 1 if at least one operation was completed successfully.
+ */
+static int do_smart_wakeup_zero(struct sem_array *sma, struct sembuf *sops,
+ int nsops, struct list_head *pt)
+{
+ int i;
+ int semop_completed = 0;
+ int got_zero = 0;
+
+ /* first: the per-semaphore queues, if known */
+ if (sops) {
+ for (i = 0; i < nsops; i++) {
+ int num = sops[i].sem_num;
+
+ if (sma->sem_base[num].semval == 0) {
+ got_zero = 1;
+ semop_completed |= wake_const_ops(sma, num, pt);
+ }
+ }
+ } else {
+ /*
+ * No sops means modified semaphores not known.
+ * Assume all were changed.
*/
- BUG_ON(q->sops[0].sem_op >= 0);
- return 0;
+ for (i = 0; i < sma->sem_nsems; i++) {
+ if (sma->sem_base[i].semval == 0) {
+ got_zero = 1;
+ semop_completed |= wake_const_ops(sma, i, pt);
+ }
+ }
}
/*
- * semval is 0. Check if there are wait-for-zero semops.
- * They must be the first entries in the per-semaphore queue
+ * If one of the modified semaphores got 0,
+ * then check the global queue, too.
*/
- h = list_first_entry(&curr->sem_pending, struct sem_queue, list);
- BUG_ON(h->nsops != 1);
- BUG_ON(h->sops[0].sem_num != q->sops[0].sem_num);
+ if (got_zero)
+ semop_completed |= wake_const_ops(sma, -1, pt);
- /* Yes, there is a wait-for-zero semop. Restart */
- if (h->sops[0].sem_op == 0)
- return 1;
-
- /* Again - no-one is waiting for the new value. */
- return 0;
+ return semop_completed;
}
@@ -678,6 +859,8 @@ static int check_restart(struct sem_array *sma, struct sem_queue *q)
* semaphore.
* The tasks that must be woken up are added to @pt. The return code
* is stored in q->pid.
+ * The function internally checks if const operations can now succeed.
+ *
* The function return 1 if at least one semop was completed successfully.
*/
static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt)
@@ -688,9 +871,9 @@ static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt)
int semop_completed = 0;
if (semnum == -1)
- pending_list = &sma->sem_pending;
+ pending_list = &sma->pending_alter;
else
- pending_list = &sma->sem_base[semnum].sem_pending;
+ pending_list = &sma->sem_base[semnum].pending_alter;
again:
walk = pending_list->next;
@@ -702,16 +885,15 @@ again:
/* If we are scanning the single sop, per-semaphore list of
* one semaphore and that semaphore is 0, then it is not
- * necessary to scan the "alter" entries: simple increments
+ * necessary to scan further: simple increments
* that affect only one entry succeed immediately and cannot
* be in the per semaphore pending queue, and decrements
* cannot be successful if the value is already 0.
*/
- if (semnum != -1 && sma->sem_base[semnum].semval == 0 &&
- q->alter)
+ if (semnum != -1 && sma->sem_base[semnum].semval == 0)
break;
- error = try_atomic_semop(sma, q->sops, q->nsops,
+ error = perform_atomic_semop(sma, q->sops, q->nsops,
q->undo, q->pid);
/* Does q->sleeper still need to sleep? */
@@ -724,6 +906,7 @@ again:
restart = 0;
} else {
semop_completed = 1;
+ do_smart_wakeup_zero(sma, q->sops, q->nsops, pt);
restart = check_restart(sma, q);
}
@@ -735,6 +918,24 @@ again:
}
/**
+ * set_semotime(sma, sops) - set sem_otime
+ * @sma: semaphore array
+ * @sops: operations that modified the array, may be NULL
+ *
+ * sem_otime is replicated to avoid cache line trashing.
+ * This function sets one instance to the current time.
+ */
+static void set_semotime(struct sem_array *sma, struct sembuf *sops)
+{
+ if (sops == NULL) {
+ sma->sem_base[0].sem_otime = get_seconds();
+ } else {
+ sma->sem_base[sops[0].sem_num].sem_otime =
+ get_seconds();
+ }
+}
+
+/**
* do_smart_update(sma, sops, nsops, otime, pt) - optimized update_queue
* @sma: semaphore array
* @sops: operations that were performed
@@ -742,8 +943,8 @@ again:
* @otime: force setting otime
* @pt: list head of the tasks that must be woken up.
*
- * do_smart_update() does the required called to update_queue, based on the
- * actual changes that were performed on the semaphore array.
+ * do_smart_update() does the required calls to update_queue and wakeup_zero,
+ * based on the actual changes that were performed on the semaphore array.
* Note that the function does not do the actual wake-up: the caller is
* responsible for calling wake_up_sem_queue_do(@pt).
* It is safe to perform this call after dropping all locks.
@@ -752,52 +953,42 @@ static void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsop
int otime, struct list_head *pt)
{
int i;
- int progress;
-
- progress = 1;
-retry_global:
- if (sma->complex_count) {
- if (update_queue(sma, -1, pt)) {
- progress = 1;
- otime = 1;
- sops = NULL;
- }
- }
- if (!progress)
- goto done;
- if (!sops) {
- /* No semops; something special is going on. */
- for (i = 0; i < sma->sem_nsems; i++) {
- if (update_queue(sma, i, pt)) {
- otime = 1;
- progress = 1;
- }
- }
- goto done_checkretry;
- }
+ otime |= do_smart_wakeup_zero(sma, sops, nsops, pt);
- /* Check the semaphores that were modified. */
- for (i = 0; i < nsops; i++) {
- if (sops[i].sem_op > 0 ||
- (sops[i].sem_op < 0 &&
- sma->sem_base[sops[i].sem_num].semval == 0))
- if (update_queue(sma, sops[i].sem_num, pt)) {
- otime = 1;
- progress = 1;
+ if (!list_empty(&sma->pending_alter)) {
+ /* semaphore array uses the global queue - just process it. */
+ otime |= update_queue(sma, -1, pt);
+ } else {
+ if (!sops) {
+ /*
+ * No sops, thus the modified semaphores are not
+ * known. Check all.
+ */
+ for (i = 0; i < sma->sem_nsems; i++)
+ otime |= update_queue(sma, i, pt);
+ } else {
+ /*
+ * Check the semaphores that were increased:
+ * - No complex ops, thus all sleeping ops are
+ * decrease.
+ * - if we decreased the value, then any sleeping
+ * semaphore ops wont be able to run: If the
+ * previous value was too small, then the new
+ * value will be too small, too.
+ */
+ for (i = 0; i < nsops; i++) {
+ if (sops[i].sem_op > 0) {
+ otime |= update_queue(sma,
+ sops[i].sem_num, pt);
+ }
}
+ }
}
-done_checkretry:
- if (progress) {
- progress = 0;
- goto retry_global;
- }
-done:
if (otime)
- sma->sem_otime = get_seconds();
+ set_semotime(sma, sops);
}
-
/* The following counts are associated to each semaphore:
* semncnt number of tasks waiting on semval being nonzero
* semzcnt number of tasks waiting on semval being zero
@@ -813,14 +1004,14 @@ static int count_semncnt (struct sem_array * sma, ushort semnum)
struct sem_queue * q;
semncnt = 0;
- list_for_each_entry(q, &sma->sem_base[semnum].sem_pending, list) {
+ list_for_each_entry(q, &sma->sem_base[semnum].pending_alter, list) {
struct sembuf * sops = q->sops;
BUG_ON(sops->sem_num != semnum);
if ((sops->sem_op < 0) && !(sops->sem_flg & IPC_NOWAIT))
semncnt++;
}
- list_for_each_entry(q, &sma->sem_pending, list) {
+ list_for_each_entry(q, &sma->pending_alter, list) {
struct sembuf * sops = q->sops;
int nsops = q->nsops;
int i;
@@ -839,14 +1030,14 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum)
struct sem_queue * q;
semzcnt = 0;
- list_for_each_entry(q, &sma->sem_base[semnum].sem_pending, list) {
+ list_for_each_entry(q, &sma->sem_base[semnum].pending_const, list) {
struct sembuf * sops = q->sops;
BUG_ON(sops->sem_num != semnum);
if ((sops->sem_op == 0) && !(sops->sem_flg & IPC_NOWAIT))
semzcnt++;
}
- list_for_each_entry(q, &sma->sem_pending, list) {
+ list_for_each_entry(q, &sma->pending_const, list) {
struct sembuf * sops = q->sops;
int nsops = q->nsops;
int i;
@@ -859,8 +1050,8 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum)
return semzcnt;
}
-/* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked
- * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex
+/* Free a semaphore set. freeary() is called with sem_ids.rwsem locked
+ * as a writer and the spinlock for this semaphore set hold. sem_ids.rwsem
* remains locked on exit.
*/
static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
@@ -872,7 +1063,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
int i;
/* Free the existing undo structures for this semaphore set. */
- assert_spin_locked(&sma->sem_perm.lock);
+ ipc_assert_locked_object(&sma->sem_perm);
list_for_each_entry_safe(un, tu, &sma->list_id, list_id) {
list_del(&un->list_id);
spin_lock(&un->ulp->lock);
@@ -884,13 +1075,22 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
/* Wake up all pending processes and let them fail with EIDRM. */
INIT_LIST_HEAD(&tasks);
- list_for_each_entry_safe(q, tq, &sma->sem_pending, list) {
+ list_for_each_entry_safe(q, tq, &sma->pending_const, list) {
+ unlink_queue(sma, q);
+ wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
+ }
+
+ list_for_each_entry_safe(q, tq, &sma->pending_alter, list) {
unlink_queue(sma, q);
wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
}
for (i = 0; i < sma->sem_nsems; i++) {
struct sem *sem = sma->sem_base + i;
- list_for_each_entry_safe(q, tq, &sem->sem_pending, list) {
+ list_for_each_entry_safe(q, tq, &sem->pending_const, list) {
+ unlink_queue(sma, q);
+ wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
+ }
+ list_for_each_entry_safe(q, tq, &sem->pending_alter, list) {
unlink_queue(sma, q);
wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
}
@@ -903,8 +1103,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
wake_up_sem_queue_do(&tasks);
ns->used_sems -= sma->sem_nsems;
- security_sem_free(sma);
- ipc_rcu_putref(sma);
+ ipc_rcu_putref(sma, sem_rcu_free);
}
static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version)
@@ -931,6 +1130,21 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in,
}
}
+static time_t get_semotime(struct sem_array *sma)
+{
+ int i;
+ time_t res;
+
+ res = sma->sem_base[0].sem_otime;
+ for (i = 1; i < sma->sem_nsems; i++) {
+ time_t to = sma->sem_base[i].sem_otime;
+
+ if (to > res)
+ res = to;
+ }
+ return res;
+}
+
static int semctl_nolock(struct ipc_namespace *ns, int semid,
int cmd, int version, void __user *p)
{
@@ -957,7 +1171,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
seminfo.semmnu = SEMMNU;
seminfo.semmap = SEMMAP;
seminfo.semume = SEMUME;
- down_read(&sem_ids(ns).rw_mutex);
+ down_read(&sem_ids(ns).rwsem);
if (cmd == SEM_INFO) {
seminfo.semusz = sem_ids(ns).in_use;
seminfo.semaem = ns->used_sems;
@@ -966,7 +1180,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
seminfo.semaem = SEMAEM;
}
max_id = ipc_get_maxid(&sem_ids(ns));
- up_read(&sem_ids(ns).rw_mutex);
+ up_read(&sem_ids(ns).rwsem);
if (copy_to_user(p, &seminfo, sizeof(struct seminfo)))
return -EFAULT;
return (max_id < 0) ? 0: max_id;
@@ -1004,9 +1218,9 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
goto out_unlock;
kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
- tbuf.sem_otime = sma->sem_otime;
- tbuf.sem_ctime = sma->sem_ctime;
- tbuf.sem_nsems = sma->sem_nsems;
+ tbuf.sem_otime = get_semotime(sma);
+ tbuf.sem_ctime = sma->sem_ctime;
+ tbuf.sem_nsems = sma->sem_nsems;
rcu_read_unlock();
if (copy_semid_to_user(p, &tbuf, version))
return -EFAULT;
@@ -1070,7 +1284,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
curr = &sma->sem_base[semnum];
- assert_spin_locked(&sma->sem_perm.lock);
+ ipc_assert_locked_object(&sma->sem_perm);
list_for_each_entry(un, &sma->list_id, list_id)
un->semadj[semnum] = 0;
@@ -1133,7 +1347,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
rcu_read_unlock();
sem_io = ipc_alloc(sizeof(ushort)*nsems);
if(sem_io == NULL) {
- sem_putref(sma);
+ ipc_rcu_putref(sma, ipc_rcu_free);
return -ENOMEM;
}
@@ -1169,20 +1383,20 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
if(nsems > SEMMSL_FAST) {
sem_io = ipc_alloc(sizeof(ushort)*nsems);
if(sem_io == NULL) {
- sem_putref(sma);
+ ipc_rcu_putref(sma, ipc_rcu_free);
return -ENOMEM;
}
}
if (copy_from_user (sem_io, p, nsems*sizeof(ushort))) {
- sem_putref(sma);
+ ipc_rcu_putref(sma, ipc_rcu_free);
err = -EFAULT;
goto out_free;
}
for (i = 0; i < nsems; i++) {
if (sem_io[i] > SEMVMX) {
- sem_putref(sma);
+ ipc_rcu_putref(sma, ipc_rcu_free);
err = -ERANGE;
goto out_free;
}
@@ -1199,7 +1413,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
for (i = 0; i < nsems; i++)
sma->sem_base[i].semval = sem_io[i];
- assert_spin_locked(&sma->sem_perm.lock);
+ ipc_assert_locked_object(&sma->sem_perm);
list_for_each_entry(un, &sma->list_id, list_id) {
for (i = 0; i < nsems; i++)
un->semadj[i] = 0;
@@ -1272,9 +1486,9 @@ copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version)
}
/*
- * This function handles some semctl commands which require the rw_mutex
+ * This function handles some semctl commands which require the rwsem
* to be held in write mode.
- * NOTE: no locks must be held, the rw_mutex is taken inside this function.
+ * NOTE: no locks must be held, the rwsem is taken inside this function.
*/
static int semctl_down(struct ipc_namespace *ns, int semid,
int cmd, int version, void __user *p)
@@ -1289,42 +1503,46 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
return -EFAULT;
}
+ down_write(&sem_ids(ns).rwsem);
+ rcu_read_lock();
+
ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd,
&semid64.sem_perm, 0);
- if (IS_ERR(ipcp))
- return PTR_ERR(ipcp);
+ if (IS_ERR(ipcp)) {
+ err = PTR_ERR(ipcp);
+ goto out_unlock1;
+ }
sma = container_of(ipcp, struct sem_array, sem_perm);
err = security_sem_semctl(sma, cmd);
- if (err) {
- rcu_read_unlock();
- goto out_up;
- }
+ if (err)
+ goto out_unlock1;
- switch(cmd){
+ switch (cmd) {
case IPC_RMID:
sem_lock(sma, NULL, -1);
+ /* freeary unlocks the ipc object and rcu */
freeary(ns, ipcp);
goto out_up;
case IPC_SET:
sem_lock(sma, NULL, -1);
err = ipc_update_perm(&semid64.sem_perm, ipcp);
if (err)
- goto out_unlock;
+ goto out_unlock0;
sma->sem_ctime = get_seconds();
break;
default:
- rcu_read_unlock();
err = -EINVAL;
- goto out_up;
+ goto out_unlock1;
}
-out_unlock:
+out_unlock0:
sem_unlock(sma, -1);
+out_unlock1:
rcu_read_unlock();
out_up:
- up_write(&sem_ids(ns).rw_mutex);
+ up_write(&sem_ids(ns).rwsem);
return err;
}
@@ -1466,7 +1684,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
/* step 2: allocate new undo structure */
new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
if (!new) {
- sem_putref(sma);
+ ipc_rcu_putref(sma, ipc_rcu_free);
return ERR_PTR(-ENOMEM);
}
@@ -1496,7 +1714,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
new->semid = semid;
assert_spin_locked(&ulp->lock);
list_add_rcu(&new->list_proc, &ulp->list_proc);
- assert_spin_locked(&sma->sem_perm.lock);
+ ipc_assert_locked_object(&sma->sem_perm);
list_add(&new->list_id, &sma->list_id);
un = new;
@@ -1533,7 +1751,6 @@ static int get_queue_result(struct sem_queue *q)
return error;
}
-
SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
unsigned, nsops, const struct timespec __user *, timeout)
{
@@ -1631,13 +1848,19 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
if (un && un->semid == -1)
goto out_unlock_free;
- error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current));
- if (error <= 0) {
- if (alter && error == 0)
+ error = perform_atomic_semop(sma, sops, nsops, un,
+ task_tgid_vnr(current));
+ if (error == 0) {
+ /* If the operation was successful, then do
+ * the required updates.
+ */
+ if (alter)
do_smart_update(sma, sops, nsops, 1, &tasks);
-
- goto out_unlock_free;
+ else
+ set_semotime(sma, sops);
}
+ if (error <= 0)
+ goto out_unlock_free;
/* We need to sleep on this operation, so we put the current
* task into the pending queue and go to sleep.
@@ -1653,15 +1876,27 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
struct sem *curr;
curr = &sma->sem_base[sops->sem_num];
- if (alter)
- list_add_tail(&queue.list, &curr->sem_pending);
- else
- list_add(&queue.list, &curr->sem_pending);
+ if (alter) {
+ if (sma->complex_count) {
+ list_add_tail(&queue.list,
+ &sma->pending_alter);
+ } else {
+
+ list_add_tail(&queue.list,
+ &curr->pending_alter);
+ }
+ } else {
+ list_add_tail(&queue.list, &curr->pending_const);
+ }
} else {
+ if (!sma->complex_count)
+ merge_queues(sma);
+
if (alter)
- list_add_tail(&queue.list, &sma->sem_pending);
+ list_add_tail(&queue.list, &sma->pending_alter);
else
- list_add(&queue.list, &sma->sem_pending);
+ list_add_tail(&queue.list, &sma->pending_const);
+
sma->complex_count++;
}
@@ -1833,7 +2068,7 @@ void exit_sem(struct task_struct *tsk)
}
/* remove un from the linked lists */
- assert_spin_locked(&sma->sem_perm.lock);
+ ipc_assert_locked_object(&sma->sem_perm);
list_del(&un->list_id);
spin_lock(&ulp->lock);
@@ -1882,6 +2117,17 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
{
struct user_namespace *user_ns = seq_user_ns(s);
struct sem_array *sma = it;
+ time_t sem_otime;
+
+ /*
+ * The proc interface isn't aware of sem_lock(), it calls
+ * ipc_lock_object() directly (in sysvipc_find_ipc).
+ * In order to stay compatible with sem_lock(), we must wait until
+ * all simple semop() calls have left their critical regions.
+ */
+ sem_wait_array(sma);
+
+ sem_otime = get_semotime(sma);
return seq_printf(s,
"%10d %10d %4o %10u %5u %5u %5u %5u %10lu %10lu\n",
@@ -1893,7 +2139,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
from_kgid_munged(user_ns, sma->sem_perm.gid),
from_kuid_munged(user_ns, sma->sem_perm.cuid),
from_kgid_munged(user_ns, sma->sem_perm.cgid),
- sma->sem_otime,
+ sem_otime,
sma->sem_ctime);
}
#endif
diff --git a/ipc/shm.c b/ipc/shm.c
index 7e199fa1960f..7b87bea5245b 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -19,6 +19,9 @@
* namespaces support
* OpenVZ, SWsoft Inc.
* Pavel Emelianov <xemul@openvz.org>
+ *
+ * Better ipc lock (kern_ipc_perm.lock) handling
+ * Davidlohr Bueso <davidlohr.bueso@hp.com>, June 2013.
*/
#include <linux/slab.h>
@@ -80,8 +83,8 @@ void shm_init_ns(struct ipc_namespace *ns)
}
/*
- * Called with shm_ids.rw_mutex (writer) and the shp structure locked.
- * Only shm_ids.rw_mutex remains locked on exit.
+ * Called with shm_ids.rwsem (writer) and the shp structure locked.
+ * Only shm_ids.rwsem remains locked on exit.
*/
static void do_shm_rmid(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
{
@@ -124,8 +127,28 @@ void __init shm_init (void)
IPC_SHM_IDS, sysvipc_shm_proc_show);
}
+static inline struct shmid_kernel *shm_obtain_object(struct ipc_namespace *ns, int id)
+{
+ struct kern_ipc_perm *ipcp = ipc_obtain_object(&shm_ids(ns), id);
+
+ if (IS_ERR(ipcp))
+ return ERR_CAST(ipcp);
+
+ return container_of(ipcp, struct shmid_kernel, shm_perm);
+}
+
+static inline struct shmid_kernel *shm_obtain_object_check(struct ipc_namespace *ns, int id)
+{
+ struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&shm_ids(ns), id);
+
+ if (IS_ERR(ipcp))
+ return ERR_CAST(ipcp);
+
+ return container_of(ipcp, struct shmid_kernel, shm_perm);
+}
+
/*
- * shm_lock_(check_) routines are called in the paths where the rw_mutex
+ * shm_lock_(check_) routines are called in the paths where the rwsem
* is not necessarily held.
*/
static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id)
@@ -141,18 +164,16 @@ static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id)
static inline void shm_lock_by_ptr(struct shmid_kernel *ipcp)
{
rcu_read_lock();
- spin_lock(&ipcp->shm_perm.lock);
+ ipc_lock_object(&ipcp->shm_perm);
}
-static inline struct shmid_kernel *shm_lock_check(struct ipc_namespace *ns,
- int id)
+static void shm_rcu_free(struct rcu_head *head)
{
- struct kern_ipc_perm *ipcp = ipc_lock_check(&shm_ids(ns), id);
-
- if (IS_ERR(ipcp))
- return (struct shmid_kernel *)ipcp;
+ struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
+ struct shmid_kernel *shp = ipc_rcu_to_struct(p);
- return container_of(ipcp, struct shmid_kernel, shm_perm);
+ security_shm_free(shp);
+ ipc_rcu_free(head);
}
static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s)
@@ -182,7 +203,7 @@ static void shm_open(struct vm_area_struct *vma)
* @ns: namespace
* @shp: struct to free
*
- * It has to be called with shp and shm_ids.rw_mutex (writer) locked,
+ * It has to be called with shp and shm_ids.rwsem (writer) locked,
* but returns with shp unlocked and freed.
*/
static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
@@ -196,8 +217,7 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
user_shm_unlock(file_inode(shp->shm_file)->i_size,
shp->mlock_user);
fput (shp->shm_file);
- security_shm_free(shp);
- ipc_rcu_putref(shp);
+ ipc_rcu_putref(shp, shm_rcu_free);
}
/*
@@ -230,7 +250,7 @@ static void shm_close(struct vm_area_struct *vma)
struct shmid_kernel *shp;
struct ipc_namespace *ns = sfd->ns;
- down_write(&shm_ids(ns).rw_mutex);
+ down_write(&shm_ids(ns).rwsem);
/* remove from the list of attaches of the shm segment */
shp = shm_lock(ns, sfd->id);
BUG_ON(IS_ERR(shp));
@@ -241,10 +261,10 @@ static void shm_close(struct vm_area_struct *vma)
shm_destroy(ns, shp);
else
shm_unlock(shp);
- up_write(&shm_ids(ns).rw_mutex);
+ up_write(&shm_ids(ns).rwsem);
}
-/* Called with ns->shm_ids(ns).rw_mutex locked */
+/* Called with ns->shm_ids(ns).rwsem locked */
static int shm_try_destroy_current(int id, void *p, void *data)
{
struct ipc_namespace *ns = data;
@@ -275,7 +295,7 @@ static int shm_try_destroy_current(int id, void *p, void *data)
return 0;
}
-/* Called with ns->shm_ids(ns).rw_mutex locked */
+/* Called with ns->shm_ids(ns).rwsem locked */
static int shm_try_destroy_orphaned(int id, void *p, void *data)
{
struct ipc_namespace *ns = data;
@@ -286,7 +306,7 @@ static int shm_try_destroy_orphaned(int id, void *p, void *data)
* We want to destroy segments without users and with already
* exit'ed originating process.
*
- * As shp->* are changed under rw_mutex, it's safe to skip shp locking.
+ * As shp->* are changed under rwsem, it's safe to skip shp locking.
*/
if (shp->shm_creator != NULL)
return 0;
@@ -300,10 +320,10 @@ static int shm_try_destroy_orphaned(int id, void *p, void *data)
void shm_destroy_orphaned(struct ipc_namespace *ns)
{
- down_write(&shm_ids(ns).rw_mutex);
+ down_write(&shm_ids(ns).rwsem);
if (shm_ids(ns).in_use)
idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_orphaned, ns);
- up_write(&shm_ids(ns).rw_mutex);
+ up_write(&shm_ids(ns).rwsem);
}
@@ -315,10 +335,10 @@ void exit_shm(struct task_struct *task)
return;
/* Destroy all already created segments, but not mapped yet */
- down_write(&shm_ids(ns).rw_mutex);
+ down_write(&shm_ids(ns).rwsem);
if (shm_ids(ns).in_use)
idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_current, ns);
- up_write(&shm_ids(ns).rw_mutex);
+ up_write(&shm_ids(ns).rwsem);
}
static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
@@ -452,7 +472,7 @@ static const struct vm_operations_struct shm_vm_ops = {
* @ns: namespace
* @params: ptr to the structure that contains key, size and shmflg
*
- * Called with shm_ids.rw_mutex held as a writer.
+ * Called with shm_ids.rwsem held as a writer.
*/
static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
@@ -485,7 +505,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
shp->shm_perm.security = NULL;
error = security_shm_alloc(shp);
if (error) {
- ipc_rcu_putref(shp);
+ ipc_rcu_putref(shp, ipc_rcu_free);
return error;
}
@@ -535,6 +555,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
shp->shm_nattch = 0;
shp->shm_file = file;
shp->shm_creator = current;
+
/*
* shmid gets reported as "inode#" in /proc/pid/maps.
* proc-ps tools use this. Changing this will break them.
@@ -543,7 +564,9 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
ns->shm_tot += numpages;
error = shp->shm_perm.id;
- shm_unlock(shp);
+
+ ipc_unlock_object(&shp->shm_perm);
+ rcu_read_unlock();
return error;
no_id:
@@ -551,13 +574,12 @@ no_id:
user_shm_unlock(size, shp->mlock_user);
fput(file);
no_file:
- security_shm_free(shp);
- ipc_rcu_putref(shp);
+ ipc_rcu_putref(shp, shm_rcu_free);
return error;
}
/*
- * Called with shm_ids.rw_mutex and ipcp locked.
+ * Called with shm_ids.rwsem and ipcp locked.
*/
static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg)
{
@@ -568,7 +590,7 @@ static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg)
}
/*
- * Called with shm_ids.rw_mutex and ipcp locked.
+ * Called with shm_ids.rwsem and ipcp locked.
*/
static inline int shm_more_checks(struct kern_ipc_perm *ipcp,
struct ipc_params *params)
@@ -681,7 +703,7 @@ static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminf
/*
* Calculate and add used RSS and swap pages of a shm.
- * Called with shm_ids.rw_mutex held as a reader
+ * Called with shm_ids.rwsem held as a reader
*/
static void shm_add_rss_swap(struct shmid_kernel *shp,
unsigned long *rss_add, unsigned long *swp_add)
@@ -708,7 +730,7 @@ static void shm_add_rss_swap(struct shmid_kernel *shp,
}
/*
- * Called with shm_ids.rw_mutex held as a reader
+ * Called with shm_ids.rwsem held as a reader
*/
static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
unsigned long *swp)
@@ -737,9 +759,9 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
}
/*
- * This function handles some shmctl commands which require the rw_mutex
+ * This function handles some shmctl commands which require the rwsem
* to be held in write mode.
- * NOTE: no locks must be held, the rw_mutex is taken inside this function.
+ * NOTE: no locks must be held, the rwsem is taken inside this function.
*/
static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
struct shmid_ds __user *buf, int version)
@@ -754,59 +776,67 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
return -EFAULT;
}
- ipcp = ipcctl_pre_down(ns, &shm_ids(ns), shmid, cmd,
- &shmid64.shm_perm, 0);
- if (IS_ERR(ipcp))
- return PTR_ERR(ipcp);
+ down_write(&shm_ids(ns).rwsem);
+ rcu_read_lock();
+
+ ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd,
+ &shmid64.shm_perm, 0);
+ if (IS_ERR(ipcp)) {
+ err = PTR_ERR(ipcp);
+ goto out_unlock1;
+ }
shp = container_of(ipcp, struct shmid_kernel, shm_perm);
err = security_shm_shmctl(shp, cmd);
if (err)
- goto out_unlock;
+ goto out_unlock1;
+
switch (cmd) {
case IPC_RMID:
+ ipc_lock_object(&shp->shm_perm);
+ /* do_shm_rmid unlocks the ipc object and rcu */
do_shm_rmid(ns, ipcp);
goto out_up;
case IPC_SET:
+ ipc_lock_object(&shp->shm_perm);
err = ipc_update_perm(&shmid64.shm_perm, ipcp);
if (err)
- goto out_unlock;
+ goto out_unlock0;
shp->shm_ctim = get_seconds();
break;
default:
err = -EINVAL;
+ goto out_unlock1;
}
-out_unlock:
- shm_unlock(shp);
+
+out_unlock0:
+ ipc_unlock_object(&shp->shm_perm);
+out_unlock1:
+ rcu_read_unlock();
out_up:
- up_write(&shm_ids(ns).rw_mutex);
+ up_write(&shm_ids(ns).rwsem);
return err;
}
-SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
+static int shmctl_nolock(struct ipc_namespace *ns, int shmid,
+ int cmd, int version, void __user *buf)
{
+ int err;
struct shmid_kernel *shp;
- int err, version;
- struct ipc_namespace *ns;
- if (cmd < 0 || shmid < 0) {
- err = -EINVAL;
- goto out;
+ /* preliminary security checks for *_INFO */
+ if (cmd == IPC_INFO || cmd == SHM_INFO) {
+ err = security_shm_shmctl(NULL, cmd);
+ if (err)
+ return err;
}
- version = ipc_parse_version(&cmd);
- ns = current->nsproxy->ipc_ns;
-
- switch (cmd) { /* replace with proc interface ? */
+ switch (cmd) {
case IPC_INFO:
{
struct shminfo64 shminfo;
- err = security_shm_shmctl(NULL, cmd);
- if (err)
- return err;
-
memset(&shminfo, 0, sizeof(shminfo));
shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni;
shminfo.shmmax = ns->shm_ctlmax;
@@ -816,9 +846,9 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
if(copy_shminfo_to_user (buf, &shminfo, version))
return -EFAULT;
- down_read(&shm_ids(ns).rw_mutex);
+ down_read(&shm_ids(ns).rwsem);
err = ipc_get_maxid(&shm_ids(ns));
- up_read(&shm_ids(ns).rw_mutex);
+ up_read(&shm_ids(ns).rwsem);
if(err<0)
err = 0;
@@ -828,19 +858,15 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
{
struct shm_info shm_info;
- err = security_shm_shmctl(NULL, cmd);
- if (err)
- return err;
-
memset(&shm_info, 0, sizeof(shm_info));
- down_read(&shm_ids(ns).rw_mutex);
+ down_read(&shm_ids(ns).rwsem);
shm_info.used_ids = shm_ids(ns).in_use;
shm_get_stat (ns, &shm_info.shm_rss, &shm_info.shm_swp);
shm_info.shm_tot = ns->shm_tot;
shm_info.swap_attempts = 0;
shm_info.swap_successes = 0;
err = ipc_get_maxid(&shm_ids(ns));
- up_read(&shm_ids(ns).rw_mutex);
+ up_read(&shm_ids(ns).rwsem);
if (copy_to_user(buf, &shm_info, sizeof(shm_info))) {
err = -EFAULT;
goto out;
@@ -855,27 +881,31 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
struct shmid64_ds tbuf;
int result;
+ rcu_read_lock();
if (cmd == SHM_STAT) {
- shp = shm_lock(ns, shmid);
+ shp = shm_obtain_object(ns, shmid);
if (IS_ERR(shp)) {
err = PTR_ERR(shp);
- goto out;
+ goto out_unlock;
}
result = shp->shm_perm.id;
} else {
- shp = shm_lock_check(ns, shmid);
+ shp = shm_obtain_object_check(ns, shmid);
if (IS_ERR(shp)) {
err = PTR_ERR(shp);
- goto out;
+ goto out_unlock;
}
result = 0;
}
+
err = -EACCES;
if (ipcperms(ns, &shp->shm_perm, S_IRUGO))
goto out_unlock;
+
err = security_shm_shmctl(shp, cmd);
if (err)
goto out_unlock;
+
memset(&tbuf, 0, sizeof(tbuf));
kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm);
tbuf.shm_segsz = shp->shm_segsz;
@@ -885,43 +915,76 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
tbuf.shm_cpid = shp->shm_cprid;
tbuf.shm_lpid = shp->shm_lprid;
tbuf.shm_nattch = shp->shm_nattch;
- shm_unlock(shp);
- if(copy_shmid_to_user (buf, &tbuf, version))
+ rcu_read_unlock();
+
+ if (copy_shmid_to_user(buf, &tbuf, version))
err = -EFAULT;
else
err = result;
goto out;
}
+ default:
+ return -EINVAL;
+ }
+
+out_unlock:
+ rcu_read_unlock();
+out:
+ return err;
+}
+
+SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
+{
+ struct shmid_kernel *shp;
+ int err, version;
+ struct ipc_namespace *ns;
+
+ if (cmd < 0 || shmid < 0)
+ return -EINVAL;
+
+ version = ipc_parse_version(&cmd);
+ ns = current->nsproxy->ipc_ns;
+
+ switch (cmd) {
+ case IPC_INFO:
+ case SHM_INFO:
+ case SHM_STAT:
+ case IPC_STAT:
+ return shmctl_nolock(ns, shmid, cmd, version, buf);
+ case IPC_RMID:
+ case IPC_SET:
+ return shmctl_down(ns, shmid, cmd, buf, version);
case SHM_LOCK:
case SHM_UNLOCK:
{
struct file *shm_file;
- shp = shm_lock_check(ns, shmid);
+ rcu_read_lock();
+ shp = shm_obtain_object_check(ns, shmid);
if (IS_ERR(shp)) {
err = PTR_ERR(shp);
- goto out;
+ goto out_unlock1;
}
audit_ipc_obj(&(shp->shm_perm));
+ err = security_shm_shmctl(shp, cmd);
+ if (err)
+ goto out_unlock1;
+ ipc_lock_object(&shp->shm_perm);
if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
kuid_t euid = current_euid();
err = -EPERM;
if (!uid_eq(euid, shp->shm_perm.uid) &&
!uid_eq(euid, shp->shm_perm.cuid))
- goto out_unlock;
+ goto out_unlock0;
if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK))
- goto out_unlock;
+ goto out_unlock0;
}
- err = security_shm_shmctl(shp, cmd);
- if (err)
- goto out_unlock;
-
shm_file = shp->shm_file;
if (is_file_hugepages(shm_file))
- goto out_unlock;
+ goto out_unlock0;
if (cmd == SHM_LOCK) {
struct user_struct *user = current_user();
@@ -930,32 +993,31 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
shp->shm_perm.mode |= SHM_LOCKED;
shp->mlock_user = user;
}
- goto out_unlock;
+ goto out_unlock0;
}
/* SHM_UNLOCK */
if (!(shp->shm_perm.mode & SHM_LOCKED))
- goto out_unlock;
+ goto out_unlock0;
shmem_lock(shm_file, 0, shp->mlock_user);
shp->shm_perm.mode &= ~SHM_LOCKED;
shp->mlock_user = NULL;
get_file(shm_file);
- shm_unlock(shp);
+ ipc_unlock_object(&shp->shm_perm);
+ rcu_read_unlock();
shmem_unlock_mapping(shm_file->f_mapping);
+
fput(shm_file);
- goto out;
- }
- case IPC_RMID:
- case IPC_SET:
- err = shmctl_down(ns, shmid, cmd, buf, version);
return err;
+ }
default:
return -EINVAL;
}
-out_unlock:
- shm_unlock(shp);
-out:
+out_unlock0:
+ ipc_unlock_object(&shp->shm_perm);
+out_unlock1:
+ rcu_read_unlock();
return err;
}
@@ -1023,10 +1085,11 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr,
* additional creator id...
*/
ns = current->nsproxy->ipc_ns;
- shp = shm_lock_check(ns, shmid);
+ rcu_read_lock();
+ shp = shm_obtain_object_check(ns, shmid);
if (IS_ERR(shp)) {
err = PTR_ERR(shp);
- goto out;
+ goto out_unlock;
}
err = -EACCES;
@@ -1037,24 +1100,31 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr,
if (err)
goto out_unlock;
+ ipc_lock_object(&shp->shm_perm);
path = shp->shm_file->f_path;
path_get(&path);
shp->shm_nattch++;
size = i_size_read(path.dentry->d_inode);
- shm_unlock(shp);
+ ipc_unlock_object(&shp->shm_perm);
+ rcu_read_unlock();
err = -ENOMEM;
sfd = kzalloc(sizeof(*sfd), GFP_KERNEL);
- if (!sfd)
- goto out_put_dentry;
+ if (!sfd) {
+ path_put(&path);
+ goto out_nattch;
+ }
file = alloc_file(&path, f_mode,
is_file_hugepages(shp->shm_file) ?
&shm_file_operations_huge :
&shm_file_operations);
err = PTR_ERR(file);
- if (IS_ERR(file))
- goto out_free;
+ if (IS_ERR(file)) {
+ kfree(sfd);
+ path_put(&path);
+ goto out_nattch;
+ }
file->private_data = sfd;
file->f_mapping = shp->shm_file->f_mapping;
@@ -1080,7 +1150,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr,
addr > current->mm->start_stack - size - PAGE_SIZE * 5)
goto invalid;
}
-
+
addr = do_mmap_pgoff(file, addr, size, prot, flags, 0, &populate);
*raddr = addr;
err = 0;
@@ -1095,7 +1165,7 @@ out_fput:
fput(file);
out_nattch:
- down_write(&shm_ids(ns).rw_mutex);
+ down_write(&shm_ids(ns).rwsem);
shp = shm_lock(ns, shmid);
BUG_ON(IS_ERR(shp));
shp->shm_nattch--;
@@ -1103,20 +1173,13 @@ out_nattch:
shm_destroy(ns, shp);
else
shm_unlock(shp);
- up_write(&shm_ids(ns).rw_mutex);
-
-out:
+ up_write(&shm_ids(ns).rwsem);
return err;
out_unlock:
- shm_unlock(shp);
- goto out;
-
-out_free:
- kfree(sfd);
-out_put_dentry:
- path_put(&path);
- goto out_nattch;
+ rcu_read_unlock();
+out:
+ return err;
}
SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg)
@@ -1221,8 +1284,7 @@ SYSCALL_DEFINE1(shmdt, char __user *, shmaddr)
#else /* CONFIG_MMU */
/* under NOMMU conditions, the exact address to be destroyed must be
* given */
- retval = -EINVAL;
- if (vma->vm_start == addr && vma->vm_ops == &shm_vm_ops) {
+ if (vma && vma->vm_start == addr && vma->vm_ops == &shm_vm_ops) {
do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start);
retval = 0;
}
diff --git a/ipc/util.c b/ipc/util.c
index 809ec5ec8122..fdb8ae740775 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -15,6 +15,14 @@
* Jun 2006 - namespaces ssupport
* OpenVZ, SWsoft Inc.
* Pavel Emelianov <xemul@openvz.org>
+ *
+ * General sysv ipc locking scheme:
+ * when doing ipc id lookups, take the ids->rwsem
+ * rcu_read_lock()
+ * obtain the ipc object (kern_ipc_perm)
+ * perform security, capabilities, auditing and permission checks, etc.
+ * acquire the ipc lock (kern_ipc_perm.lock) throught ipc_lock_object()
+ * perform data updates (ie: SET, RMID, LOCK/UNLOCK commands)
*/
#include <linux/mm.h>
@@ -119,7 +127,7 @@ __initcall(ipc_init);
void ipc_init_ids(struct ipc_ids *ids)
{
- init_rwsem(&ids->rw_mutex);
+ init_rwsem(&ids->rwsem);
ids->in_use = 0;
ids->seq = 0;
@@ -174,7 +182,7 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
* @ids: Identifier set
* @key: The key to find
*
- * Requires ipc_ids.rw_mutex locked.
+ * Requires ipc_ids.rwsem locked.
* Returns the LOCKED pointer to the ipc structure if found or NULL
* if not.
* If key is found ipc points to the owning ipc structure
@@ -197,7 +205,8 @@ static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
continue;
}
- ipc_lock_by_ptr(ipc);
+ rcu_read_lock();
+ ipc_lock_object(ipc);
return ipc;
}
@@ -208,7 +217,7 @@ static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
* ipc_get_maxid - get the last assigned id
* @ids: IPC identifier set
*
- * Called with ipc_ids.rw_mutex held.
+ * Called with ipc_ids.rwsem held.
*/
int ipc_get_maxid(struct ipc_ids *ids)
@@ -246,9 +255,8 @@ int ipc_get_maxid(struct ipc_ids *ids)
* is returned. The 'new' entry is returned in a locked state on success.
* On failure the entry is not locked and a negative err-code is returned.
*
- * Called with ipc_ids.rw_mutex held as a writer.
+ * Called with writer ipc_ids.rwsem held.
*/
-
int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
{
kuid_t euid;
@@ -313,9 +321,9 @@ static int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids,
{
int err;
- down_write(&ids->rw_mutex);
+ down_write(&ids->rwsem);
err = ops->getnew(ns, params);
- up_write(&ids->rw_mutex);
+ up_write(&ids->rwsem);
return err;
}
@@ -332,7 +340,7 @@ static int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids,
*
* On success, the IPC id is returned.
*
- * It is called with ipc_ids.rw_mutex and ipcp->lock held.
+ * It is called with ipc_ids.rwsem and ipcp->lock held.
*/
static int ipc_check_perms(struct ipc_namespace *ns,
struct kern_ipc_perm *ipcp,
@@ -377,7 +385,7 @@ static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
* Take the lock as a writer since we are potentially going to add
* a new entry + read locks are not "upgradable"
*/
- down_write(&ids->rw_mutex);
+ down_write(&ids->rwsem);
ipcp = ipc_findkey(ids, params->key);
if (ipcp == NULL) {
/* key not used */
@@ -403,7 +411,7 @@ static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
}
ipc_unlock(ipcp);
}
- up_write(&ids->rw_mutex);
+ up_write(&ids->rwsem);
return err;
}
@@ -414,7 +422,7 @@ static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
* @ids: IPC identifier set
* @ipcp: ipc perm structure containing the identifier to remove
*
- * ipc_ids.rw_mutex (as a writer) and the spinlock for this ID are held
+ * ipc_ids.rwsem (as a writer) and the spinlock for this ID are held
* before this function is called, and remain locked on the exit.
*/
@@ -466,13 +474,6 @@ void ipc_free(void* ptr, int size)
kfree(ptr);
}
-struct ipc_rcu {
- struct rcu_head rcu;
- atomic_t refcount;
- /* "void *" makes sure alignment of following data is sane. */
- void *data[0];
-};
-
/**
* ipc_rcu_alloc - allocate ipc and rcu space
* @size: size desired
@@ -489,35 +490,34 @@ void *ipc_rcu_alloc(int size)
if (unlikely(!out))
return NULL;
atomic_set(&out->refcount, 1);
- return out->data;
+ return out + 1;
}
int ipc_rcu_getref(void *ptr)
{
- return atomic_inc_not_zero(&container_of(ptr, struct ipc_rcu, data)->refcount);
-}
+ struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
-/**
- * ipc_schedule_free - free ipc + rcu space
- * @head: RCU callback structure for queued work
- */
-static void ipc_schedule_free(struct rcu_head *head)
-{
- vfree(container_of(head, struct ipc_rcu, rcu));
+ return atomic_inc_not_zero(&p->refcount);
}
-void ipc_rcu_putref(void *ptr)
+void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head))
{
- struct ipc_rcu *p = container_of(ptr, struct ipc_rcu, data);
+ struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
if (!atomic_dec_and_test(&p->refcount))
return;
- if (is_vmalloc_addr(ptr)) {
- call_rcu(&p->rcu, ipc_schedule_free);
- } else {
- kfree_rcu(p, rcu);
- }
+ call_rcu(&p->rcu, func);
+}
+
+void ipc_rcu_free(struct rcu_head *head)
+{
+ struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
+
+ if (is_vmalloc_addr(p))
+ vfree(p);
+ else
+ kfree(p);
}
/**
@@ -622,7 +622,7 @@ struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id)
}
/**
- * ipc_lock - Lock an ipc structure without rw_mutex held
+ * ipc_lock - Lock an ipc structure without rwsem held
* @ids: IPC identifier set
* @id: ipc id to look for
*
@@ -678,22 +678,6 @@ out:
return out;
}
-struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id)
-{
- struct kern_ipc_perm *out;
-
- out = ipc_lock(ids, id);
- if (IS_ERR(out))
- return out;
-
- if (ipc_checkid(out, id)) {
- ipc_unlock(out);
- return ERR_PTR(-EIDRM);
- }
-
- return out;
-}
-
/**
* ipcget - Common sys_*get() code
* @ns : namsepace
@@ -734,7 +718,7 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)
}
/**
- * ipcctl_pre_down - retrieve an ipc and check permissions for some IPC_XXX cmd
+ * ipcctl_pre_down_nolock - retrieve an ipc and check permissions for some IPC_XXX cmd
* @ns: the ipc namespace
* @ids: the table of ids where to look for the ipc
* @id: the id of the ipc to retrieve
@@ -747,39 +731,22 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)
* It must be called without any lock held and
* - retrieves the ipc with the given id in the given table.
* - performs some audit and permission check, depending on the given cmd
- * - returns the ipc with both ipc and rw_mutex locks held in case of success
- * or an err-code without any lock held otherwise.
+ * - returns a pointer to the ipc object or otherwise, the corresponding error.
+ *
+ * Call holding the both the rwsem and the rcu read lock.
*/
-struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
- struct ipc_ids *ids, int id, int cmd,
- struct ipc64_perm *perm, int extra_perm)
-{
- struct kern_ipc_perm *ipcp;
-
- ipcp = ipcctl_pre_down_nolock(ns, ids, id, cmd, perm, extra_perm);
- if (IS_ERR(ipcp))
- goto out;
-
- spin_lock(&ipcp->lock);
-out:
- return ipcp;
-}
-
struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
- struct ipc_ids *ids, int id, int cmd,
- struct ipc64_perm *perm, int extra_perm)
+ struct ipc_ids *ids, int id, int cmd,
+ struct ipc64_perm *perm, int extra_perm)
{
kuid_t euid;
int err = -EPERM;
struct kern_ipc_perm *ipcp;
- down_write(&ids->rw_mutex);
- rcu_read_lock();
-
ipcp = ipc_obtain_object_check(ids, id);
if (IS_ERR(ipcp)) {
err = PTR_ERR(ipcp);
- goto out_up;
+ goto err;
}
audit_ipc_obj(ipcp);
@@ -790,16 +757,8 @@ struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
euid = current_euid();
if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid) ||
ns_capable(ns->user_ns, CAP_SYS_ADMIN))
- return ipcp;
-
-out_up:
- /*
- * Unsuccessful lookup, unlock and return
- * the corresponding error.
- */
- rcu_read_unlock();
- up_write(&ids->rw_mutex);
-
+ return ipcp; /* successful lookup */
+err:
return ERR_PTR(err);
}
@@ -856,7 +815,8 @@ static struct kern_ipc_perm *sysvipc_find_ipc(struct ipc_ids *ids, loff_t pos,
ipc = idr_find(&ids->ipcs_idr, pos);
if (ipc != NULL) {
*new_pos = pos + 1;
- ipc_lock_by_ptr(ipc);
+ rcu_read_lock();
+ ipc_lock_object(ipc);
return ipc;
}
}
@@ -894,7 +854,7 @@ static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos)
* Take the lock - this will be released by the corresponding
* call to stop().
*/
- down_read(&ids->rw_mutex);
+ down_read(&ids->rwsem);
/* pos < 0 is invalid */
if (*pos < 0)
@@ -921,7 +881,7 @@ static void sysvipc_proc_stop(struct seq_file *s, void *it)
ids = &iter->ns->ids[iface->ids];
/* Release the lock we took in start() */
- up_read(&ids->rw_mutex);
+ up_read(&ids->rwsem);
}
static int sysvipc_proc_show(struct seq_file *s, void *it)
diff --git a/ipc/util.h b/ipc/util.h
index 2b0bdd5d92ce..f2f5036f2eed 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -47,6 +47,13 @@ static inline void msg_exit_ns(struct ipc_namespace *ns) { }
static inline void shm_exit_ns(struct ipc_namespace *ns) { }
#endif
+struct ipc_rcu {
+ struct rcu_head rcu;
+ atomic_t refcount;
+} ____cacheline_aligned_in_smp;
+
+#define ipc_rcu_to_struct(p) ((void *)(p+1))
+
/*
* Structure that holds the parameters needed by the ipc operations
* (see after)
@@ -94,10 +101,10 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
#define ipcid_to_idx(id) ((id) % SEQ_MULTIPLIER)
#define ipcid_to_seqx(id) ((id) / SEQ_MULTIPLIER)
-/* must be called with ids->rw_mutex acquired for writing */
+/* must be called with ids->rwsem acquired for writing */
int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int);
-/* must be called with ids->rw_mutex acquired for reading */
+/* must be called with ids->rwsem acquired for reading */
int ipc_get_maxid(struct ipc_ids *);
/* must be called with both locks acquired. */
@@ -120,7 +127,8 @@ void ipc_free(void* ptr, int size);
*/
void* ipc_rcu_alloc(int size);
int ipc_rcu_getref(void *ptr);
-void ipc_rcu_putref(void *ptr);
+void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head));
+void ipc_rcu_free(struct rcu_head *head);
struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int);
struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id);
@@ -131,9 +139,6 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out);
struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
struct ipc_ids *ids, int id, int cmd,
struct ipc64_perm *perm, int extra_perm);
-struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
- struct ipc_ids *ids, int id, int cmd,
- struct ipc64_perm *perm, int extra_perm);
#ifndef CONFIG_ARCH_WANT_IPC_PARSE_VERSION
/* On IA-64, we always use the "64-bit version" of the IPC structures. */
@@ -159,24 +164,27 @@ static inline int ipc_checkid(struct kern_ipc_perm *ipcp, int uid)
return uid / SEQ_MULTIPLIER != ipcp->seq;
}
-static inline void ipc_lock_by_ptr(struct kern_ipc_perm *perm)
+static inline void ipc_lock_object(struct kern_ipc_perm *perm)
{
- rcu_read_lock();
spin_lock(&perm->lock);
}
-static inline void ipc_unlock(struct kern_ipc_perm *perm)
+static inline void ipc_unlock_object(struct kern_ipc_perm *perm)
{
spin_unlock(&perm->lock);
- rcu_read_unlock();
}
-static inline void ipc_lock_object(struct kern_ipc_perm *perm)
+static inline void ipc_assert_locked_object(struct kern_ipc_perm *perm)
{
- spin_lock(&perm->lock);
+ assert_spin_locked(&perm->lock);
+}
+
+static inline void ipc_unlock(struct kern_ipc_perm *perm)
+{
+ ipc_unlock_object(perm);
+ rcu_read_unlock();
}
-struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id);
struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id);
int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
struct ipc_ops *ops, struct ipc_params *params);
diff --git a/kernel/audit.c b/kernel/audit.c
index 91e53d04b6a9..7b0e23a740ce 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1117,9 +1117,10 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
sleep_time = timeout_start + audit_backlog_wait_time -
jiffies;
- if ((long)sleep_time > 0)
+ if ((long)sleep_time > 0) {
wait_for_auditd(sleep_time);
- continue;
+ continue;
+ }
}
if (audit_rate_check() && printk_ratelimit())
printk(KERN_WARNING
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 770c43a05c13..90b9aaa4b289 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -1995,7 +1995,7 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk,
/* @tsk either already exited or can't exit until the end */
if (tsk->flags & PF_EXITING)
- continue;
+ goto next;
/* as per above, nr_threads may decrease, but not increase. */
BUG_ON(i >= group_size);
@@ -2003,7 +2003,7 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk,
ent.cgrp = task_cgroup_from_root(tsk, root);
/* nothing to do if this task is already in the cgroup */
if (ent.cgrp == cgrp)
- continue;
+ goto next;
/*
* saying GFP_ATOMIC has no effect here because we did prealloc
* earlier, but it's good form to communicate our expectations.
@@ -2011,7 +2011,7 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk,
retval = flex_array_put(group, i, &ent, GFP_ATOMIC);
BUG_ON(retval != 0);
i++;
-
+ next:
if (!threadgroup)
break;
} while_each_thread(leader, tsk);
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index cd55144270b5..9c2ddfbf4525 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -87,10 +87,31 @@ again:
goto out;
/*
- * Publish the known good head. Rely on the full barrier implied
- * by atomic_dec_and_test() order the rb->head read and this
- * write.
+ * Since the mmap() consumer (userspace) can run on a different CPU:
+ *
+ * kernel user
+ *
+ * READ ->data_tail READ ->data_head
+ * smp_mb() (A) smp_rmb() (C)
+ * WRITE $data READ $data
+ * smp_wmb() (B) smp_mb() (D)
+ * STORE ->data_head WRITE ->data_tail
+ *
+ * Where A pairs with D, and B pairs with C.
+ *
+ * I don't think A needs to be a full barrier because we won't in fact
+ * write data until we see the store from userspace. So we simply don't
+ * issue the data WRITE until we observe it. Be conservative for now.
+ *
+ * OTOH, D needs to be a full barrier since it separates the data READ
+ * from the tail WRITE.
+ *
+ * For B a WMB is sufficient since it separates two WRITEs, and for C
+ * an RMB is sufficient since it separates two READs.
+ *
+ * See perf_output_begin().
*/
+ smp_wmb();
rb->user_page->data_head = head;
/*
@@ -154,9 +175,11 @@ int perf_output_begin(struct perf_output_handle *handle,
* Userspace could choose to issue a mb() before updating the
* tail pointer. So that all reads will be completed before the
* write is issued.
+ *
+ * See perf_output_put_handle().
*/
tail = ACCESS_ONCE(rb->user_page->data_tail);
- smp_rmb();
+ smp_mb();
offset = head = local_read(&rb->head);
head += size;
if (unlikely(!perf_output_space(rb, tail, offset, head)))
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index f3569747d629..ad8e1bdca70e 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1682,12 +1682,10 @@ static bool handle_trampoline(struct pt_regs *regs)
tmp = ri;
ri = ri->next;
kfree(tmp);
+ utask->depth--;
if (!chained)
break;
-
- utask->depth--;
-
BUG_ON(!ri);
}
diff --git a/kernel/fork.c b/kernel/fork.c
index b394c2036e4f..f9a15a265444 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1188,10 +1188,11 @@ static struct task_struct *copy_process(unsigned long clone_flags,
return ERR_PTR(-EINVAL);
/*
- * If the new process will be in a different pid namespace
- * don't allow the creation of threads.
+ * If the new process will be in a different pid namespace don't
+ * allow it to share a thread group or signal handlers with the
+ * forking task.
*/
- if ((clone_flags & (CLONE_VM|CLONE_NEWPID)) &&
+ if ((clone_flags & (CLONE_SIGHAND | CLONE_NEWPID)) &&
(task_active_pid_ns(current) != current->nsproxy->pid_ns))
return ERR_PTR(-EINVAL);
diff --git a/kernel/pid.c b/kernel/pid.c
index 0db3e791a06d..0eb6d8e8b1da 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -264,6 +264,7 @@ void free_pid(struct pid *pid)
struct pid_namespace *ns = upid->ns;
hlist_del_rcu(&upid->pid_chain);
switch(--ns->nr_hashed) {
+ case 2:
case 1:
/* When all that is left in the pid namespace
* is the reaper wake up the reaper. The reaper
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index b5ccba22603b..1101d92635c3 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -558,7 +558,7 @@ static void cputime_adjust(struct task_cputime *curr,
struct cputime *prev,
cputime_t *ut, cputime_t *st)
{
- cputime_t rtime, stime, utime, total;
+ cputime_t rtime, stime, utime;
if (vtime_accounting_enabled()) {
*ut = curr->utime;
@@ -566,9 +566,6 @@ static void cputime_adjust(struct task_cputime *curr,
return;
}
- stime = curr->stime;
- total = stime + curr->utime;
-
/*
* Tick based cputime accounting depend on random scheduling
* timeslices of a task to be interrupted or not by the timer.
@@ -589,13 +586,19 @@ static void cputime_adjust(struct task_cputime *curr,
if (prev->stime + prev->utime >= rtime)
goto out;
- if (total) {
+ stime = curr->stime;
+ utime = curr->utime;
+
+ if (utime == 0) {
+ stime = rtime;
+ } else if (stime == 0) {
+ utime = rtime;
+ } else {
+ cputime_t total = stime + utime;
+
stime = scale_stime((__force u64)stime,
(__force u64)rtime, (__force u64)total);
utime = rtime - stime;
- } else {
- stime = rtime;
- utime = 0;
}
/*
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 5f277f2d34d1..3e242209bc3f 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -31,7 +31,6 @@
#include <linux/task_work.h>
#include <trace/events/sched.h>
-#ifdef CONFIG_HMP_VARIABLE_SCALE
#include <linux/sysfs.h>
#include <linux/vmalloc.h>
#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE
@@ -40,7 +39,6 @@
*/
#include <linux/cpufreq.h>
#endif /* CONFIG_HMP_FREQUENCY_INVARIANT_SCALE */
-#endif /* CONFIG_HMP_VARIABLE_SCALE */
#include "sched.h"
@@ -1212,8 +1210,7 @@ static u32 __compute_runnable_contrib(u64 n)
return contrib + runnable_avg_yN_sum[n];
}
-#ifdef CONFIG_HMP_VARIABLE_SCALE
-
+#ifdef CONFIG_SCHED_HMP
#define HMP_VARIABLE_SCALE_SHIFT 16ULL
struct hmp_global_attr {
struct attribute attr;
@@ -1224,13 +1221,10 @@ struct hmp_global_attr {
int *value;
int (*to_sysfs)(int);
int (*from_sysfs)(int);
+ ssize_t (*to_sysfs_text)(char *buf, int buf_size);
};
-#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE
-#define HMP_DATA_SYSFS_MAX 4
-#else
-#define HMP_DATA_SYSFS_MAX 3
-#endif
+#define HMP_DATA_SYSFS_MAX 8
struct hmp_data_struct {
#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE
@@ -1298,7 +1292,7 @@ struct cpufreq_extents {
static struct cpufreq_extents freq_scale[CONFIG_NR_CPUS];
#endif /* CONFIG_HMP_FREQUENCY_INVARIANT_SCALE */
-#endif /* CONFIG_HMP_VARIABLE_SCALE */
+#endif /* CONFIG_SCHED_HMP */
/* We can represent the historical contribution to runnable average as the
* coefficients of a geometric series. To do this we sub-divide our runnable
@@ -1344,7 +1338,7 @@ static __always_inline int __update_entity_runnable_avg(u64 now,
#endif /* CONFIG_HMP_FREQUENCY_INVARIANT_SCALE */
delta = now - sa->last_runnable_update;
-#ifdef CONFIG_HMP_VARIABLE_SCALE
+#ifdef CONFIG_SCHED_HMP
delta = hmp_variable_scale_convert(delta);
#endif
/*
@@ -1688,6 +1682,7 @@ static inline void update_rq_runnable_avg(struct rq *rq, int runnable)
__update_tg_runnable_avg(&rq->avg, &rq->cfs);
trace_sched_rq_runnable_ratio(cpu_of(rq), rq->avg.load_avg_ratio);
trace_sched_rq_runnable_load(cpu_of(rq), rq->cfs.runnable_load_avg);
+ trace_sched_rq_nr_running(cpu_of(rq), rq->nr_running, rq->nr_iowait.counter);
}
/* Add the load generated by se into cfs_rq's child load-average */
@@ -3664,25 +3659,46 @@ static struct sched_entity *hmp_get_lightest_task(
* Migration thresholds should be in the range [0..1023]
* hmp_up_threshold: min. load required for migrating tasks to a faster cpu
* hmp_down_threshold: max. load allowed for tasks migrating to a slower cpu
- * The default values (512, 256) offer good responsiveness, but may need
- * tweaking suit particular needs.
*
* hmp_up_prio: Only up migrate task with high priority (<hmp_up_prio)
* hmp_next_up_threshold: Delay before next up migration (1024 ~= 1 ms)
* hmp_next_down_threshold: Delay before next down migration (1024 ~= 1 ms)
+ *
+ * Small Task Packing:
+ * We can choose to fill the littlest CPUs in an HMP system rather than
+ * the typical spreading mechanic. This behavior is controllable using
+ * two variables.
+ * hmp_packing_enabled: runtime control over pack/spread
+ * hmp_full_threshold: Consider a CPU with this much unweighted load full
*/
-unsigned int hmp_up_threshold = 512;
-unsigned int hmp_down_threshold = 256;
+unsigned int hmp_up_threshold = 700;
+unsigned int hmp_down_threshold = 512;
#ifdef CONFIG_SCHED_HMP_PRIO_FILTER
unsigned int hmp_up_prio = NICE_TO_PRIO(CONFIG_SCHED_HMP_PRIO_FILTER_VAL);
#endif
unsigned int hmp_next_up_threshold = 4096;
unsigned int hmp_next_down_threshold = 4096;
+#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING
+unsigned int hmp_packing_enabled = 1;
+#ifndef CONFIG_ARCH_VEXPRESS_TC2
+unsigned int hmp_full_threshold = (NICE_0_LOAD * 9) / 8;
+#else
+/* TC2 has a sharp consumption curve @ around 800Mhz, so
+ we aim to spread the load around that frequency. */
+unsigned int hmp_full_threshold = 650; /* 80% of the 800Mhz freq * NICE_0_LOAD */
+#endif
+#endif
+
static unsigned int hmp_up_migration(int cpu, int *target_cpu, struct sched_entity *se);
static unsigned int hmp_down_migration(int cpu, struct sched_entity *se);
static inline unsigned int hmp_domain_min_load(struct hmp_domain *hmpd,
- int *min_cpu);
+ int *min_cpu, struct cpumask *affinity);
+
+static inline struct hmp_domain *hmp_smallest_domain(void)
+{
+ return list_entry(hmp_domains.prev, struct hmp_domain, hmp_domains);
+}
/* Check if cpu is in fastest hmp_domain */
static inline unsigned int hmp_cpu_is_fastest(int cpu)
@@ -3722,22 +3738,23 @@ static inline struct hmp_domain *hmp_faster_domain(int cpu)
/*
* Selects a cpu in previous (faster) hmp_domain
- * Note that cpumask_any_and() returns the first cpu in the cpumask
*/
static inline unsigned int hmp_select_faster_cpu(struct task_struct *tsk,
int cpu)
{
int lowest_cpu=NR_CPUS;
- __always_unused int lowest_ratio = hmp_domain_min_load(hmp_faster_domain(cpu), &lowest_cpu);
- /*
- * If the lowest-loaded CPU in the domain is allowed by the task affinity
- * select that one, otherwise select one which is allowed
- */
- if(lowest_cpu != NR_CPUS && cpumask_test_cpu(lowest_cpu,tsk_cpus_allowed(tsk)))
- return lowest_cpu;
+ __always_unused int lowest_ratio;
+ struct hmp_domain *hmp;
+
+ if (hmp_cpu_is_fastest(cpu))
+ hmp = hmp_cpu_domain(cpu);
else
- return cpumask_any_and(&hmp_faster_domain(cpu)->cpus,
- tsk_cpus_allowed(tsk));
+ hmp = hmp_faster_domain(cpu);
+
+ lowest_ratio = hmp_domain_min_load(hmp, &lowest_cpu,
+ tsk_cpus_allowed(tsk));
+
+ return lowest_cpu;
}
/*
@@ -3756,18 +3773,54 @@ static inline unsigned int hmp_select_slower_cpu(struct task_struct *tsk,
else
hmp = hmp_slower_domain(cpu);
- lowest_ratio = hmp_domain_min_load(hmp, &lowest_cpu);
- /*
- * If the lowest-loaded CPU in the domain is allowed by the task affinity
- * select that one, otherwise select one which is allowed
- */
- if(lowest_cpu != NR_CPUS && cpumask_test_cpu(lowest_cpu,tsk_cpus_allowed(tsk)))
- return lowest_cpu;
- else
- return cpumask_any_and(&hmp_slower_domain(cpu)->cpus,
- tsk_cpus_allowed(tsk));
+ lowest_ratio = hmp_domain_min_load(hmp, &lowest_cpu,
+ tsk_cpus_allowed(tsk));
+
+ return lowest_cpu;
}
+#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING
+/*
+ * Select the 'best' candidate little CPU to wake up on.
+ * Implements a packing strategy which examines CPU in
+ * logical CPU order, and selects the first which will
+ * have at least 10% capacity available, according to
+ * both tracked load of the runqueue and the task.
+ */
+static inline unsigned int hmp_best_little_cpu(struct task_struct *tsk,
+ int cpu) {
+ int tmp_cpu;
+ unsigned long estimated_load;
+ struct hmp_domain *hmp;
+ struct sched_avg *avg;
+ struct cpumask allowed_hmp_cpus;
+
+ if(!hmp_packing_enabled ||
+ tsk->se.avg.load_avg_ratio > ((NICE_0_LOAD * 90)/100))
+ return hmp_select_slower_cpu(tsk, cpu);
+
+ if (hmp_cpu_is_slowest(cpu))
+ hmp = hmp_cpu_domain(cpu);
+ else
+ hmp = hmp_slower_domain(cpu);
+ /* respect affinity */
+ cpumask_and(&allowed_hmp_cpus, &hmp->cpus,
+ tsk_cpus_allowed(tsk));
+
+ for_each_cpu_mask(tmp_cpu, allowed_hmp_cpus) {
+ avg = &cpu_rq(tmp_cpu)->avg;
+ /* estimate new rq load if we add this task */
+ estimated_load = avg->load_avg_ratio +
+ tsk->se.avg.load_avg_ratio;
+ if (estimated_load <= hmp_full_threshold) {
+ cpu = tmp_cpu;
+ break;
+ }
+ }
+ /* if no match was found, the task uses the initial value */
+ return cpu;
+}
+#endif
static inline void hmp_next_up_delay(struct sched_entity *se, int cpu)
{
/* hack - always use clock from first online CPU */
@@ -3788,7 +3841,6 @@ static inline void hmp_next_down_delay(struct sched_entity *se, int cpu)
cpu_rq(cpu)->avg.hmp_last_up_migration = 0;
}
-#ifdef CONFIG_HMP_VARIABLE_SCALE
/*
* Heterogenous multiprocessor (HMP) optimizations
*
@@ -3821,27 +3873,35 @@ static inline void hmp_next_down_delay(struct sched_entity *se, int cpu)
* The scale factor hmp_data.multiplier is a fixed point
* number: (32-HMP_VARIABLE_SCALE_SHIFT).HMP_VARIABLE_SCALE_SHIFT
*/
-static u64 hmp_variable_scale_convert(u64 delta)
+static inline u64 hmp_variable_scale_convert(u64 delta)
{
+#ifdef CONFIG_HMP_VARIABLE_SCALE
u64 high = delta >> 32ULL;
u64 low = delta & 0xffffffffULL;
low *= hmp_data.multiplier;
high *= hmp_data.multiplier;
return (low >> HMP_VARIABLE_SCALE_SHIFT)
+ (high << (32ULL - HMP_VARIABLE_SCALE_SHIFT));
+#else
+ return delta;
+#endif
}
static ssize_t hmp_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
- ssize_t ret = 0;
struct hmp_global_attr *hmp_attr =
container_of(attr, struct hmp_global_attr, attr);
- int temp = *(hmp_attr->value);
+ int temp;
+
+ if (hmp_attr->to_sysfs_text != NULL)
+ return hmp_attr->to_sysfs_text(buf, PAGE_SIZE);
+
+ temp = *(hmp_attr->value);
if (hmp_attr->to_sysfs != NULL)
temp = hmp_attr->to_sysfs(temp);
- ret = sprintf(buf, "%d\n", temp);
- return ret;
+
+ return (ssize_t)sprintf(buf, "%d\n", temp);
}
static ssize_t hmp_store(struct kobject *a, struct attribute *attr,
@@ -3870,11 +3930,31 @@ static ssize_t hmp_store(struct kobject *a, struct attribute *attr,
return ret;
}
+static ssize_t hmp_print_domains(char *outbuf, int outbufsize)
+{
+ char buf[64];
+ const char nospace[] = "%s", space[] = " %s";
+ const char *fmt = nospace;
+ struct hmp_domain *domain;
+ struct list_head *pos;
+ int outpos = 0;
+ list_for_each(pos, &hmp_domains) {
+ domain = list_entry(pos, struct hmp_domain, hmp_domains);
+ if (cpumask_scnprintf(buf, 64, &domain->possible_cpus)) {
+ outpos += sprintf(outbuf+outpos, fmt, buf);
+ fmt = space;
+ }
+ }
+ strcat(outbuf, "\n");
+ return outpos+1;
+}
+
+#ifdef CONFIG_HMP_VARIABLE_SCALE
static int hmp_period_tofrom_sysfs(int value)
{
return (LOAD_AVG_PERIOD << HMP_VARIABLE_SCALE_SHIFT) / value;
}
-
+#endif
/* max value for threshold is 1024 */
static int hmp_theshold_from_sysfs(int value)
{
@@ -3882,20 +3962,32 @@ static int hmp_theshold_from_sysfs(int value)
return -1;
return value;
}
-#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE
-/* freqinvar control is only 0,1 off/on */
-static int hmp_freqinvar_from_sysfs(int value)
+#if defined(CONFIG_SCHED_HMP_LITTLE_PACKING) || \
+ defined(CONFIG_HMP_FREQUENCY_INVARIANT_SCALE)
+/* toggle control is only 0,1 off/on */
+static int hmp_toggle_from_sysfs(int value)
{
if (value < 0 || value > 1)
return -1;
return value;
}
#endif
+#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING
+/* packing value must be non-negative */
+static int hmp_packing_from_sysfs(int value)
+{
+ if (value < 0)
+ return -1;
+ return value;
+}
+#endif
static void hmp_attr_add(
const char *name,
int *value,
int (*to_sysfs)(int),
- int (*from_sysfs)(int))
+ int (*from_sysfs)(int),
+ ssize_t (*to_sysfs_text)(char *, int),
+ umode_t mode)
{
int i = 0;
while (hmp_data.attributes[i] != NULL) {
@@ -3903,13 +3995,17 @@ static void hmp_attr_add(
if (i >= HMP_DATA_SYSFS_MAX)
return;
}
- hmp_data.attr[i].attr.mode = 0644;
+ if (mode)
+ hmp_data.attr[i].attr.mode = mode;
+ else
+ hmp_data.attr[i].attr.mode = 0644;
hmp_data.attr[i].show = hmp_show;
hmp_data.attr[i].store = hmp_store;
hmp_data.attr[i].attr.name = name;
hmp_data.attr[i].value = value;
hmp_data.attr[i].to_sysfs = to_sysfs;
hmp_data.attr[i].from_sysfs = from_sysfs;
+ hmp_data.attr[i].to_sysfs_text = to_sysfs_text;
hmp_data.attributes[i] = &hmp_data.attr[i].attr;
hmp_data.attributes[i + 1] = NULL;
}
@@ -3918,30 +4014,59 @@ static int hmp_attr_init(void)
{
int ret;
memset(&hmp_data, sizeof(hmp_data), 0);
+ hmp_attr_add("hmp_domains",
+ NULL,
+ NULL,
+ NULL,
+ hmp_print_domains,
+ 0444);
+ hmp_attr_add("up_threshold",
+ &hmp_up_threshold,
+ NULL,
+ hmp_theshold_from_sysfs,
+ NULL,
+ 0);
+ hmp_attr_add("down_threshold",
+ &hmp_down_threshold,
+ NULL,
+ hmp_theshold_from_sysfs,
+ NULL,
+ 0);
+#ifdef CONFIG_HMP_VARIABLE_SCALE
/* by default load_avg_period_ms == LOAD_AVG_PERIOD
* meaning no change
*/
hmp_data.multiplier = hmp_period_tofrom_sysfs(LOAD_AVG_PERIOD);
-
hmp_attr_add("load_avg_period_ms",
&hmp_data.multiplier,
hmp_period_tofrom_sysfs,
- hmp_period_tofrom_sysfs);
- hmp_attr_add("up_threshold",
- &hmp_up_threshold,
- NULL,
- hmp_theshold_from_sysfs);
- hmp_attr_add("down_threshold",
- &hmp_down_threshold,
+ hmp_period_tofrom_sysfs,
NULL,
- hmp_theshold_from_sysfs);
+ 0);
+#endif
#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE
/* default frequency-invariant scaling ON */
hmp_data.freqinvar_load_scale_enabled = 1;
hmp_attr_add("frequency_invariant_load_scale",
&hmp_data.freqinvar_load_scale_enabled,
NULL,
- hmp_freqinvar_from_sysfs);
+ hmp_toggle_from_sysfs,
+ NULL,
+ 0);
+#endif
+#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING
+ hmp_attr_add("packing_enable",
+ &hmp_packing_enabled,
+ NULL,
+ hmp_toggle_from_sysfs,
+ NULL,
+ 0);
+ hmp_attr_add("packing_limit",
+ &hmp_full_threshold,
+ NULL,
+ hmp_packing_from_sysfs,
+ NULL,
+ 0);
#endif
hmp_data.attr_group.name = "hmp";
hmp_data.attr_group.attrs = hmp_data.attributes;
@@ -3950,10 +4075,24 @@ static int hmp_attr_init(void)
return 0;
}
late_initcall(hmp_attr_init);
-#endif /* CONFIG_HMP_VARIABLE_SCALE */
-
+/*
+ * return the load of the lowest-loaded CPU in a given HMP domain
+ * min_cpu optionally points to an int to receive the CPU.
+ * affinity optionally points to a cpumask containing the
+ * CPUs to be considered. note:
+ * + min_cpu = NR_CPUS only if no CPUs are in the set of
+ * affinity && hmp_domain cpus
+ * + min_cpu will always otherwise equal one of the CPUs in
+ * the hmp domain
+ * + when more than one CPU has the same load, the one which
+ * is least-recently-disturbed by an HMP migration will be
+ * selected
+ * + if all CPUs are equally loaded or idle and the times are
+ * all the same, the first in the set will be used
+ * + if affinity is not set, cpu_online_mask is used
+ */
static inline unsigned int hmp_domain_min_load(struct hmp_domain *hmpd,
- int *min_cpu)
+ int *min_cpu, struct cpumask *affinity)
{
int cpu;
int min_cpu_runnable_temp = NR_CPUS;
@@ -3962,8 +4101,15 @@ static inline unsigned int hmp_domain_min_load(struct hmp_domain *hmpd,
unsigned long min_runnable_load = INT_MAX;
unsigned long contrib;
struct sched_avg *avg;
+ struct cpumask temp_cpumask;
+ /*
+ * only look at CPUs allowed if specified,
+ * otherwise look at all online CPUs in the
+ * right HMP domain
+ */
+ cpumask_and(&temp_cpumask, &hmpd->cpus, affinity ? affinity : cpu_online_mask);
- for_each_cpu_mask(cpu, hmpd->cpus) {
+ for_each_cpu_mask(cpu, temp_cpumask) {
avg = &cpu_rq(cpu)->avg;
/* used for both up and down migration */
curr_last_migration = avg->hmp_last_up_migration ?
@@ -4025,27 +4171,36 @@ static inline unsigned int hmp_offload_down(int cpu, struct sched_entity *se)
return NR_CPUS;
/* Is there an idle CPU in the current domain */
- min_usage = hmp_domain_min_load(hmp_cpu_domain(cpu), NULL);
- if (min_usage == 0)
+ min_usage = hmp_domain_min_load(hmp_cpu_domain(cpu), NULL, NULL);
+ if (min_usage == 0) {
+ trace_sched_hmp_offload_abort(cpu, min_usage, "load");
return NR_CPUS;
+ }
/* Is the task alone on the cpu? */
- if (cpu_rq(cpu)->cfs.h_nr_running < 2)
+ if (cpu_rq(cpu)->cfs.h_nr_running < 2) {
+ trace_sched_hmp_offload_abort(cpu,
+ cpu_rq(cpu)->cfs.h_nr_running, "nr_running");
return NR_CPUS;
+ }
/* Is the task actually starving? */
/* >=25% ratio running/runnable = starving */
- if (hmp_task_starvation(se) > 768)
+ if (hmp_task_starvation(se) > 768) {
+ trace_sched_hmp_offload_abort(cpu, hmp_task_starvation(se),
+ "starvation");
return NR_CPUS;
+ }
/* Does the slower domain have any idle CPUs? */
- min_usage = hmp_domain_min_load(hmp_slower_domain(cpu), &dest_cpu);
- if (min_usage > 0)
- return NR_CPUS;
+ min_usage = hmp_domain_min_load(hmp_slower_domain(cpu), &dest_cpu,
+ tsk_cpus_allowed(task_of(se)));
- if (cpumask_test_cpu(dest_cpu, &hmp_slower_domain(cpu)->cpus))
+ if (min_usage == 0) {
+ trace_sched_hmp_offload_succeed(cpu, dest_cpu);
return dest_cpu;
-
+ } else
+ trace_sched_hmp_offload_abort(cpu,min_usage,"slowdomain");
return NR_CPUS;
}
#endif /* CONFIG_SCHED_HMP */
@@ -4077,30 +4232,13 @@ select_task_rq_fair(struct task_struct *p, int sd_flag, int wake_flags)
#ifdef CONFIG_SCHED_HMP
/* always put non-kernel forking tasks on a big domain */
if (p->mm && (sd_flag & SD_BALANCE_FORK)) {
- if(hmp_cpu_is_fastest(prev_cpu)) {
- struct hmp_domain *hmpdom = list_entry(&hmp_cpu_domain(prev_cpu)->hmp_domains, struct hmp_domain, hmp_domains);
- __always_unused int lowest_ratio = hmp_domain_min_load(hmpdom, &new_cpu);
- if (new_cpu != NR_CPUS &&
- cpumask_test_cpu(new_cpu,
- tsk_cpus_allowed(p))) {
- hmp_next_up_delay(&p->se, new_cpu);
- return new_cpu;
- } else {
- new_cpu = cpumask_any_and(
- &hmp_faster_domain(cpu)->cpus,
- tsk_cpus_allowed(p));
- if (new_cpu < nr_cpu_ids) {
- hmp_next_up_delay(&p->se, new_cpu);
- return new_cpu;
- }
- }
- } else {
- new_cpu = hmp_select_faster_cpu(p, prev_cpu);
- if (new_cpu != NR_CPUS) {
- hmp_next_up_delay(&p->se, new_cpu);
- return new_cpu;
- }
+ new_cpu = hmp_select_faster_cpu(p, prev_cpu);
+ if (new_cpu != NR_CPUS) {
+ hmp_next_up_delay(&p->se, new_cpu);
+ return new_cpu;
}
+ /* failed to perform HMP fork balance, use normal balance */
+ new_cpu = cpu;
}
#endif
@@ -4179,16 +4317,24 @@ unlock:
rcu_read_unlock();
#ifdef CONFIG_SCHED_HMP
+ prev_cpu = task_cpu(p);
+
if (hmp_up_migration(prev_cpu, &new_cpu, &p->se)) {
hmp_next_up_delay(&p->se, new_cpu);
- trace_sched_hmp_migrate(p, new_cpu, 0);
+ trace_sched_hmp_migrate(p, new_cpu, HMP_MIGRATE_WAKEUP);
return new_cpu;
}
if (hmp_down_migration(prev_cpu, &p->se)) {
+#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING
+ new_cpu = hmp_best_little_cpu(p, prev_cpu);
+#else
new_cpu = hmp_select_slower_cpu(p, prev_cpu);
- hmp_next_down_delay(&p->se, new_cpu);
- trace_sched_hmp_migrate(p, new_cpu, 0);
- return new_cpu;
+#endif
+ if (new_cpu != prev_cpu) {
+ hmp_next_down_delay(&p->se, new_cpu);
+ trace_sched_hmp_migrate(p, new_cpu, HMP_MIGRATE_WAKEUP);
+ return new_cpu;
+ }
}
/* Make sure that the task stays in its previous hmp domain */
if (!cpumask_test_cpu(new_cpu, &hmp_cpu_domain(prev_cpu)->cpus))
@@ -6155,16 +6301,49 @@ static struct {
unsigned long next_balance; /* in jiffy units */
} nohz ____cacheline_aligned;
+#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING
+/*
+ * Decide if the tasks on the busy CPUs in the
+ * littlest domain would benefit from an idle balance
+ */
+static int hmp_packing_ilb_needed(int cpu)
+{
+ struct hmp_domain *hmp;
+ /* always allow ilb on non-slowest domain */
+ if (!hmp_cpu_is_slowest(cpu))
+ return 1;
+
+ hmp = hmp_cpu_domain(cpu);
+ for_each_cpu_and(cpu, &hmp->cpus, nohz.idle_cpus_mask) {
+ /* only idle balance if a CPU is loaded over threshold */
+ if (cpu_rq(cpu)->avg.load_avg_ratio > hmp_full_threshold)
+ return 1;
+ }
+ return 0;
+}
+#endif
+
static inline int find_new_ilb(int call_cpu)
{
int ilb = cpumask_first(nohz.idle_cpus_mask);
#ifdef CONFIG_SCHED_HMP
+ int ilb_needed = 1;
+
/* restrict nohz balancing to occur in the same hmp domain */
ilb = cpumask_first_and(nohz.idle_cpus_mask,
&((struct hmp_domain *)hmp_cpu_domain(call_cpu))->cpus);
+
+#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING
+ if (ilb < nr_cpu_ids)
+ ilb_needed = hmp_packing_ilb_needed(ilb);
#endif
+
+ if (ilb_needed && ilb < nr_cpu_ids && idle_cpu(ilb))
+ return ilb;
+#else
if (ilb < nr_cpu_ids && idle_cpu(ilb))
return ilb;
+#endif
return nr_cpu_ids;
}
@@ -6490,11 +6669,9 @@ static void nohz_idle_balance(int this_cpu, enum cpu_idle_type idle) { }
static unsigned int hmp_up_migration(int cpu, int *target_cpu, struct sched_entity *se)
{
struct task_struct *p = task_of(se);
+ int temp_target_cpu;
u64 now;
- if (target_cpu)
- *target_cpu = NR_CPUS;
-
if (hmp_cpu_is_fastest(cpu))
return 0;
@@ -6517,13 +6694,12 @@ static unsigned int hmp_up_migration(int cpu, int *target_cpu, struct sched_enti
* idle CPU or 1023 for any partly-busy one.
* Be explicit about requirement for an idle CPU.
*/
- if (hmp_domain_min_load(hmp_faster_domain(cpu), target_cpu) != 0)
- return 0;
-
- if (cpumask_intersects(&hmp_faster_domain(cpu)->cpus,
- tsk_cpus_allowed(p)))
+ if (hmp_domain_min_load(hmp_faster_domain(cpu), &temp_target_cpu,
+ tsk_cpus_allowed(p)) == 0 && temp_target_cpu != NR_CPUS) {
+ if(target_cpu)
+ *target_cpu = temp_target_cpu;
return 1;
-
+ }
return 0;
}
@@ -6533,8 +6709,14 @@ static unsigned int hmp_down_migration(int cpu, struct sched_entity *se)
struct task_struct *p = task_of(se);
u64 now;
- if (hmp_cpu_is_slowest(cpu))
+ if (hmp_cpu_is_slowest(cpu)) {
+#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING
+ if(hmp_packing_enabled)
+ return 1;
+ else
+#endif
return 0;
+ }
#ifdef CONFIG_SCHED_HMP_PRIO_FILTER
/* Filter by task priority */
@@ -6703,6 +6885,7 @@ static int hmp_active_task_migration_cpu_stop(void *data)
rcu_read_unlock();
double_unlock_balance(busiest_rq, target_rq);
out_unlock:
+ put_task_struct(p);
busiest_rq->active_balance = 0;
raw_spin_unlock_irq(&busiest_rq->lock);
return 0;
@@ -6776,11 +6959,75 @@ static int hmp_idle_pull_cpu_stop(void *data)
rcu_read_unlock();
double_unlock_balance(busiest_rq, target_rq);
out_unlock:
+ put_task_struct(p);
busiest_rq->active_balance = 0;
raw_spin_unlock_irq(&busiest_rq->lock);
return 0;
}
+/*
+ * Move task in a runnable state to another CPU.
+ *
+ * Tailored on 'active_load_balance_stop_cpu' with slight
+ * modification to locking and pre-transfer checks. Note
+ * rq->lock must be held before calling.
+ */
+static void hmp_migrate_runnable_task(struct rq *rq)
+{
+ struct sched_domain *sd;
+ int src_cpu = cpu_of(rq);
+ struct rq *src_rq = rq;
+ int dst_cpu = rq->push_cpu;
+ struct rq *dst_rq = cpu_rq(dst_cpu);
+ struct task_struct *p = rq->migrate_task;
+ /*
+ * One last check to make sure nobody else is playing
+ * with the source rq.
+ */
+ if (src_rq->active_balance)
+ return;
+
+ if (src_rq->nr_running <= 1)
+ return;
+
+ if (task_rq(p) != src_rq)
+ return;
+ /*
+ * Not sure if this applies here but one can never
+ * be too cautious
+ */
+ BUG_ON(src_rq == dst_rq);
+
+ double_lock_balance(src_rq, dst_rq);
+
+ rcu_read_lock();
+ for_each_domain(dst_cpu, sd) {
+ if (cpumask_test_cpu(src_cpu, sched_domain_span(sd)))
+ break;
+ }
+
+ if (likely(sd)) {
+ struct lb_env env = {
+ .sd = sd,
+ .dst_cpu = dst_cpu,
+ .dst_rq = dst_rq,
+ .src_cpu = src_cpu,
+ .src_rq = src_rq,
+ .idle = CPU_IDLE,
+ };
+
+ schedstat_inc(sd, alb_count);
+
+ if (move_specific_task(&env, p))
+ schedstat_inc(sd, alb_pushed);
+ else
+ schedstat_inc(sd, alb_failed);
+ }
+
+ rcu_read_unlock();
+ double_unlock_balance(src_rq, dst_rq);
+}
+
static DEFINE_SPINLOCK(hmp_force_migration);
/*
@@ -6793,13 +7040,14 @@ static void hmp_force_up_migration(int this_cpu)
struct sched_entity *curr, *orig;
struct rq *target;
unsigned long flags;
- unsigned int force;
+ unsigned int force, got_target;
struct task_struct *p;
if (!spin_trylock(&hmp_force_migration))
return;
for_each_online_cpu(cpu) {
force = 0;
+ got_target = 0;
target = cpu_rq(cpu);
raw_spin_lock_irqsave(&target->lock, flags);
curr = target->cfs.curr;
@@ -6821,31 +7069,48 @@ static void hmp_force_up_migration(int this_cpu)
p = task_of(curr);
if (hmp_up_migration(cpu, &target_cpu, curr)) {
if (!target->active_balance) {
- target->active_balance = 1;
+ get_task_struct(p);
target->push_cpu = target_cpu;
target->migrate_task = p;
- force = 1;
- trace_sched_hmp_migrate(p, target->push_cpu, 1);
+ got_target = 1;
+ trace_sched_hmp_migrate(p, target->push_cpu, HMP_MIGRATE_FORCE);
hmp_next_up_delay(&p->se, target->push_cpu);
}
}
- if (!force && !target->active_balance) {
+ if (!got_target && !target->active_balance) {
/*
* For now we just check the currently running task.
* Selecting the lightest task for offloading will
* require extensive book keeping.
*/
curr = hmp_get_lightest_task(orig, 1);
+ p = task_of(curr);
target->push_cpu = hmp_offload_down(cpu, curr);
if (target->push_cpu < NR_CPUS) {
- target->active_balance = 1;
+ get_task_struct(p);
target->migrate_task = p;
- force = 1;
- trace_sched_hmp_migrate(p, target->push_cpu, 2);
+ got_target = 1;
+ trace_sched_hmp_migrate(p, target->push_cpu, HMP_MIGRATE_OFFLOAD);
hmp_next_down_delay(&p->se, target->push_cpu);
}
}
+ /*
+ * We have a target with no active_balance. If the task
+ * is not currently running move it, otherwise let the
+ * CPU stopper take care of it.
+ */
+ if (got_target && !target->active_balance) {
+ if (!task_running(target, p)) {
+ trace_sched_hmp_migrate_force_running(p, 0);
+ hmp_migrate_runnable_task(target);
+ } else {
+ target->active_balance = 1;
+ force = 1;
+ }
+ }
+
raw_spin_unlock_irqrestore(&target->lock, flags);
+
if (force)
stop_one_cpu_nowait(cpu_of(target),
hmp_active_task_migration_cpu_stop,
@@ -6865,7 +7130,7 @@ static unsigned int hmp_idle_pull(int this_cpu)
int cpu;
struct sched_entity *curr, *orig;
struct hmp_domain *hmp_domain = NULL;
- struct rq *target, *rq;
+ struct rq *target = NULL, *rq;
unsigned long flags, ratio = 0;
unsigned int force = 0;
struct task_struct *p = NULL;
@@ -6916,14 +7181,26 @@ static unsigned int hmp_idle_pull(int this_cpu)
/* now we have a candidate */
raw_spin_lock_irqsave(&target->lock, flags);
if (!target->active_balance && task_rq(p) == target) {
- target->active_balance = 1;
+ get_task_struct(p);
target->push_cpu = this_cpu;
target->migrate_task = p;
- force = 1;
- trace_sched_hmp_migrate(p, target->push_cpu, 3);
+ trace_sched_hmp_migrate(p, target->push_cpu, HMP_MIGRATE_IDLE_PULL);
hmp_next_up_delay(&p->se, target->push_cpu);
+ /*
+ * if the task isn't running move it right away.
+ * Otherwise setup the active_balance mechanic and let
+ * the CPU stopper do its job.
+ */
+ if (!task_running(target, p)) {
+ trace_sched_hmp_migrate_idle_running(p, 0);
+ hmp_migrate_runnable_task(target);
+ } else {
+ target->active_balance = 1;
+ force = 1;
+ }
}
raw_spin_unlock_irqrestore(&target->lock, flags);
+
if (force) {
stop_one_cpu_nowait(cpu_of(target),
hmp_idle_pull_cpu_stop,
@@ -7040,11 +7317,15 @@ static void task_fork_fair(struct task_struct *p)
cfs_rq = task_cfs_rq(current);
curr = cfs_rq->curr;
- if (unlikely(task_cpu(p) != this_cpu)) {
- rcu_read_lock();
- __set_task_cpu(p, this_cpu);
- rcu_read_unlock();
- }
+ /*
+ * Not only the cpu but also the task_group of the parent might have
+ * been changed after parent->se.parent,cfs_rq were copied to
+ * child->se.parent,cfs_rq. So call __set_task_cpu() to make those
+ * of child point to valid ones.
+ */
+ rcu_read_lock();
+ __set_task_cpu(p, this_cpu);
+ rcu_read_unlock();
update_curr(cfs_rq);
diff --git a/kernel/smp.c b/kernel/smp.c
index 4dba0f7b72ad..23ccc67dcbb2 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -12,6 +12,8 @@
#include <linux/gfp.h>
#include <linux/smp.h>
#include <linux/cpu.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/smp.h>
#include "smpboot.h"
@@ -159,8 +161,10 @@ void generic_exec_single(int cpu, struct call_single_data *csd, int wait)
* locking and barrier primitives. Generic code isn't really
* equipped to do the right thing...
*/
- if (ipi)
+ if (ipi) {
+ trace_smp_call_func_send(csd->func, cpu);
arch_send_call_function_single_ipi(cpu);
+ }
if (wait)
csd_lock_wait(csd);
@@ -197,8 +201,9 @@ void generic_smp_call_function_single_interrupt(void)
* so save them away before making the call:
*/
csd_flags = csd->flags;
-
+ trace_smp_call_func_entry(csd->func);
csd->func(csd->info);
+ trace_smp_call_func_exit(csd->func);
/*
* Unlocked CSDs are valid through generic_exec_single():
@@ -228,6 +233,7 @@ int smp_call_function_single(int cpu, smp_call_func_t func, void *info,
int this_cpu;
int err = 0;
+ trace_smp_call_func_send(func, cpu);
/*
* prevent preemption and reschedule on another processor,
* as well as CPU removal
@@ -245,7 +251,9 @@ int smp_call_function_single(int cpu, smp_call_func_t func, void *info,
if (cpu == this_cpu) {
local_irq_save(flags);
+ trace_smp_call_func_entry(func);
func(info);
+ trace_smp_call_func_exit(func);
local_irq_restore(flags);
} else {
if ((unsigned)cpu < nr_cpu_ids && cpu_online(cpu)) {
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 3d6833f125d3..787b3a032429 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -330,10 +330,19 @@ void irq_enter(void)
static inline void invoke_softirq(void)
{
- if (!force_irqthreads)
- __do_softirq();
- else
+ if (!force_irqthreads) {
+ /*
+ * We can safely execute softirq on the current stack if
+ * it is the irq stack, because it should be near empty
+ * at this stage. But we have no way to know if the arch
+ * calls irq_exit() on the irq stack. So call softirq
+ * in its own stack to prevent from any overrun on top
+ * of a potentially deep task stack.
+ */
+ do_softirq();
+ } else {
wakeup_softirqd();
+ }
}
static inline void tick_irq_exit(void)
diff --git a/kernel/sys.c b/kernel/sys.c
index 2bbd9a73b54c..1c9090bc6746 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -42,6 +42,8 @@
#include <linux/syscore_ops.h>
#include <linux/version.h>
#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/mempolicy.h>
#include <linux/compat.h>
#include <linux/syscalls.h>
@@ -2099,6 +2101,146 @@ static int prctl_get_tid_address(struct task_struct *me, int __user **tid_addr)
}
#endif
+
+static int prctl_update_vma_anon_name(struct vm_area_struct *vma,
+ struct vm_area_struct **prev,
+ unsigned long start, unsigned long end,
+ const char __user *name_addr)
+{
+ struct mm_struct * mm = vma->vm_mm;
+ int error = 0;
+ pgoff_t pgoff;
+
+ if (name_addr == vma_get_anon_name(vma)) {
+ *prev = vma;
+ goto out;
+ }
+
+ pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
+ *prev = vma_merge(mm, *prev, start, end, vma->vm_flags, vma->anon_vma,
+ vma->vm_file, pgoff, vma_policy(vma),
+ name_addr);
+ if (*prev) {
+ vma = *prev;
+ goto success;
+ }
+
+ *prev = vma;
+
+ if (start != vma->vm_start) {
+ error = split_vma(mm, vma, start, 1);
+ if (error)
+ goto out;
+ }
+
+ if (end != vma->vm_end) {
+ error = split_vma(mm, vma, end, 0);
+ if (error)
+ goto out;
+ }
+
+success:
+ if (!vma->vm_file)
+ vma->shared.anon_name = name_addr;
+
+out:
+ if (error == -ENOMEM)
+ error = -EAGAIN;
+ return error;
+}
+
+static int prctl_set_vma_anon_name(unsigned long start, unsigned long end,
+ unsigned long arg)
+{
+ unsigned long tmp;
+ struct vm_area_struct * vma, *prev;
+ int unmapped_error = 0;
+ int error = -EINVAL;
+
+ /*
+ * If the interval [start,end) covers some unmapped address
+ * ranges, just ignore them, but return -ENOMEM at the end.
+ * - this matches the handling in madvise.
+ */
+ vma = find_vma_prev(current->mm, start, &prev);
+ if (vma && start > vma->vm_start)
+ prev = vma;
+
+ for (;;) {
+ /* Still start < end. */
+ error = -ENOMEM;
+ if (!vma)
+ return error;
+
+ /* Here start < (end|vma->vm_end). */
+ if (start < vma->vm_start) {
+ unmapped_error = -ENOMEM;
+ start = vma->vm_start;
+ if (start >= end)
+ return error;
+ }
+
+ /* Here vma->vm_start <= start < (end|vma->vm_end) */
+ tmp = vma->vm_end;
+ if (end < tmp)
+ tmp = end;
+
+ /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */
+ error = prctl_update_vma_anon_name(vma, &prev, start, end,
+ (const char __user *)arg);
+ if (error)
+ return error;
+ start = tmp;
+ if (prev && start < prev->vm_end)
+ start = prev->vm_end;
+ error = unmapped_error;
+ if (start >= end)
+ return error;
+ if (prev)
+ vma = prev->vm_next;
+ else /* madvise_remove dropped mmap_sem */
+ vma = find_vma(current->mm, start);
+ }
+}
+
+static int prctl_set_vma(unsigned long opt, unsigned long start,
+ unsigned long len_in, unsigned long arg)
+{
+ struct mm_struct *mm = current->mm;
+ int error;
+ unsigned long len;
+ unsigned long end;
+
+ if (start & ~PAGE_MASK)
+ return -EINVAL;
+ len = (len_in + ~PAGE_MASK) & PAGE_MASK;
+
+ /* Check to see whether len was rounded up from small -ve to zero */
+ if (len_in && !len)
+ return -EINVAL;
+
+ end = start + len;
+ if (end < start)
+ return -EINVAL;
+
+ if (end == start)
+ return 0;
+
+ down_write(&mm->mmap_sem);
+
+ switch (opt) {
+ case PR_SET_VMA_ANON_NAME:
+ error = prctl_set_vma_anon_name(start, end, arg);
+ break;
+ default:
+ error = -EINVAL;
+ }
+
+ up_write(&mm->mmap_sem);
+
+ return error;
+}
+
SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
unsigned long, arg4, unsigned long, arg5)
{
@@ -2226,6 +2368,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
else
return -EINVAL;
break;
+ case PR_SET_VMA:
+ error = prctl_set_vma(arg2, arg3, arg4, arg5);
+ break;
default:
return -EINVAL;
}
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 1a07798c9146..6f562baf5f4a 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -105,6 +105,7 @@ extern char core_pattern[];
extern unsigned int core_pipe_limit;
#endif
extern int pid_max;
+extern int extra_free_kbytes;
extern int min_free_order_shift;
extern int pid_max_min, pid_max_max;
extern int percpu_pagelist_fraction;
@@ -1267,6 +1268,14 @@ static struct ctl_table vm_table[] = {
.extra1 = &zero,
},
{
+ .procname = "extra_free_kbytes",
+ .data = &extra_free_kbytes,
+ .maxlen = sizeof(extra_free_kbytes),
+ .mode = 0644,
+ .proc_handler = min_free_kbytes_sysctl_handler,
+ .extra1 = &zero,
+ },
+ {
.procname = "min_free_order_shift",
.data = &min_free_order_shift,
.maxlen = sizeof(min_free_order_shift),
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index c6d6400ee137..6a23c6c556c3 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -30,29 +30,64 @@ static RAW_NOTIFIER_HEAD(clockevents_chain);
/* Protection for the above */
static DEFINE_RAW_SPINLOCK(clockevents_lock);
-/**
- * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds
- * @latch: value to convert
- * @evt: pointer to clock event device descriptor
- *
- * Math helper, returns latch value converted to nanoseconds (bound checked)
- */
-u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
+static u64 cev_delta2ns(unsigned long latch, struct clock_event_device *evt,
+ bool ismax)
{
u64 clc = (u64) latch << evt->shift;
+ u64 rnd;
if (unlikely(!evt->mult)) {
evt->mult = 1;
WARN_ON(1);
}
+ rnd = (u64) evt->mult - 1;
+
+ /*
+ * Upper bound sanity check. If the backwards conversion is
+ * not equal latch, we know that the above shift overflowed.
+ */
+ if ((clc >> evt->shift) != (u64)latch)
+ clc = ~0ULL;
+
+ /*
+ * Scaled math oddities:
+ *
+ * For mult <= (1 << shift) we can safely add mult - 1 to
+ * prevent integer rounding loss. So the backwards conversion
+ * from nsec to device ticks will be correct.
+ *
+ * For mult > (1 << shift), i.e. device frequency is > 1GHz we
+ * need to be careful. Adding mult - 1 will result in a value
+ * which when converted back to device ticks can be larger
+ * than latch by up to (mult - 1) >> shift. For the min_delta
+ * calculation we still want to apply this in order to stay
+ * above the minimum device ticks limit. For the upper limit
+ * we would end up with a latch value larger than the upper
+ * limit of the device, so we omit the add to stay below the
+ * device upper boundary.
+ *
+ * Also omit the add if it would overflow the u64 boundary.
+ */
+ if ((~0ULL - clc > rnd) &&
+ (!ismax || evt->mult <= (1U << evt->shift)))
+ clc += rnd;
do_div(clc, evt->mult);
- if (clc < 1000)
- clc = 1000;
- if (clc > KTIME_MAX)
- clc = KTIME_MAX;
- return clc;
+ /* Deltas less than 1usec are pointless noise */
+ return clc > 1000 ? clc : 1000;
+}
+
+/**
+ * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds
+ * @latch: value to convert
+ * @evt: pointer to clock event device descriptor
+ *
+ * Math helper, returns latch value converted to nanoseconds (bound checked)
+ */
+u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
+{
+ return cev_delta2ns(latch, evt, false);
}
EXPORT_SYMBOL_GPL(clockevent_delta2ns);
@@ -317,8 +352,8 @@ void clockevents_config(struct clock_event_device *dev, u32 freq)
sec = 600;
clockevents_calc_mult_shift(dev, freq, sec);
- dev->min_delta_ns = clockevent_delta2ns(dev->min_delta_ticks, dev);
- dev->max_delta_ns = clockevent_delta2ns(dev->max_delta_ticks, dev);
+ dev->min_delta_ns = cev_delta2ns(dev->min_delta_ticks, dev, false);
+ dev->max_delta_ns = cev_delta2ns(dev->max_delta_ticks, dev, true);
}
/**
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index 8f5b3b98577b..bb2215174f05 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -516,13 +516,13 @@ static void sync_cmos_clock(struct work_struct *work)
schedule_delayed_work(&sync_cmos_work, timespec_to_jiffies(&next));
}
-static void notify_cmos_timer(void)
+void ntp_notify_cmos_timer(void)
{
schedule_delayed_work(&sync_cmos_work, 0);
}
#else
-static inline void notify_cmos_timer(void) { }
+void ntp_notify_cmos_timer(void) { }
#endif
@@ -687,8 +687,6 @@ int __do_adjtimex(struct timex *txc, struct timespec *ts, s32 *time_tai)
if (!(time_status & STA_NANO))
txc->time.tv_usec /= NSEC_PER_USEC;
- notify_cmos_timer();
-
return result;
}
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index baeeb5c87cf1..fcc261c5df29 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1682,6 +1682,8 @@ int do_adjtimex(struct timex *txc)
write_seqcount_end(&timekeeper_seq);
raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
+ ntp_notify_cmos_timer();
+
return ret;
}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index c431a16f1866..3abfc7b31afb 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -827,9 +827,12 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
if (isspace(ch)) {
parser->buffer[parser->idx] = 0;
parser->cont = false;
- } else {
+ } else if (parser->idx < parser->size - 1) {
parser->cont = true;
parser->buffer[parser->idx++] = ch;
+ } else {
+ ret = -EINVAL;
+ goto out;
}
*ppos += read;
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index a1cf8cae60e7..3e7df38067ae 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -529,7 +529,8 @@ void sg_miter_stop(struct sg_mapping_iter *miter)
miter->__offset += miter->consumed;
miter->__remaining -= miter->consumed;
- if (miter->__flags & SG_MITER_TO_SG)
+ if ((miter->__flags & SG_MITER_TO_SG) &&
+ !PageSlab(miter->page))
flush_kernel_dcache_page(miter->page);
if (miter->__flags & SG_MITER_ATOMIC) {
diff --git a/mm/bounce.c b/mm/bounce.c
index c9f0a4339a7d..5a7d58fb883b 100644
--- a/mm/bounce.c
+++ b/mm/bounce.c
@@ -204,6 +204,8 @@ static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig,
struct bio_vec *to, *from;
unsigned i;
+ if (force)
+ goto bounce;
bio_for_each_segment(from, *bio_orig, i)
if (page_to_pfn(from->bv_page) > queue_bounce_pfn(q))
goto bounce;
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 362c329b83fe..c403a74e4bee 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1288,64 +1288,90 @@ out:
int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long addr, pmd_t pmd, pmd_t *pmdp)
{
+ struct anon_vma *anon_vma = NULL;
struct page *page;
unsigned long haddr = addr & HPAGE_PMD_MASK;
+ int page_nid = -1, this_nid = numa_node_id();
int target_nid;
- int current_nid = -1;
- bool migrated;
+ bool page_locked;
+ bool migrated = false;
spin_lock(&mm->page_table_lock);
if (unlikely(!pmd_same(pmd, *pmdp)))
goto out_unlock;
page = pmd_page(pmd);
- get_page(page);
- current_nid = page_to_nid(page);
+ page_nid = page_to_nid(page);
count_vm_numa_event(NUMA_HINT_FAULTS);
- if (current_nid == numa_node_id())
+ if (page_nid == this_nid)
count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL);
+ /*
+ * Acquire the page lock to serialise THP migrations but avoid dropping
+ * page_table_lock if at all possible
+ */
+ page_locked = trylock_page(page);
target_nid = mpol_misplaced(page, vma, haddr);
if (target_nid == -1) {
- put_page(page);
- goto clear_pmdnuma;
+ /* If the page was locked, there are no parallel migrations */
+ if (page_locked)
+ goto clear_pmdnuma;
+
+ /*
+ * Otherwise wait for potential migrations and retry. We do
+ * relock and check_same as the page may no longer be mapped.
+ * As the fault is being retried, do not account for it.
+ */
+ spin_unlock(&mm->page_table_lock);
+ wait_on_page_locked(page);
+ page_nid = -1;
+ goto out;
}
- /* Acquire the page lock to serialise THP migrations */
+ /* Page is misplaced, serialise migrations and parallel THP splits */
+ get_page(page);
spin_unlock(&mm->page_table_lock);
- lock_page(page);
+ if (!page_locked)
+ lock_page(page);
+ anon_vma = page_lock_anon_vma_read(page);
/* Confirm the PTE did not while locked */
spin_lock(&mm->page_table_lock);
if (unlikely(!pmd_same(pmd, *pmdp))) {
unlock_page(page);
put_page(page);
+ page_nid = -1;
goto out_unlock;
}
- spin_unlock(&mm->page_table_lock);
- /* Migrate the THP to the requested node */
+ /*
+ * Migrate the THP to the requested node, returns with page unlocked
+ * and pmd_numa cleared.
+ */
+ spin_unlock(&mm->page_table_lock);
migrated = migrate_misplaced_transhuge_page(mm, vma,
pmdp, pmd, addr, page, target_nid);
- if (!migrated)
- goto check_same;
-
- task_numa_fault(target_nid, HPAGE_PMD_NR, true);
- return 0;
+ if (migrated)
+ page_nid = target_nid;
-check_same:
- spin_lock(&mm->page_table_lock);
- if (unlikely(!pmd_same(pmd, *pmdp)))
- goto out_unlock;
+ goto out;
clear_pmdnuma:
+ BUG_ON(!PageLocked(page));
pmd = pmd_mknonnuma(pmd);
set_pmd_at(mm, haddr, pmdp, pmd);
VM_BUG_ON(pmd_numa(*pmdp));
update_mmu_cache_pmd(vma, addr, pmdp);
+ unlock_page(page);
out_unlock:
spin_unlock(&mm->page_table_lock);
- if (current_nid != -1)
- task_numa_fault(current_nid, HPAGE_PMD_NR, false);
+
+out:
+ if (anon_vma)
+ page_unlock_anon_vma_read(anon_vma);
+
+ if (page_nid != -1)
+ task_numa_fault(page_nid, HPAGE_PMD_NR, migrated);
+
return 0;
}
@@ -2286,6 +2312,8 @@ static void collapse_huge_page(struct mm_struct *mm,
goto out;
vma = find_vma(mm, address);
+ if (!vma)
+ goto out;
hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
hend = vma->vm_end & HPAGE_PMD_MASK;
if (address < hstart || address + HPAGE_PMD_SIZE > hend)
@@ -2697,6 +2725,7 @@ void __split_huge_page_pmd(struct vm_area_struct *vma, unsigned long address,
mmun_start = haddr;
mmun_end = haddr + HPAGE_PMD_SIZE;
+again:
mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
spin_lock(&mm->page_table_lock);
if (unlikely(!pmd_trans_huge(*pmd))) {
@@ -2719,7 +2748,14 @@ void __split_huge_page_pmd(struct vm_area_struct *vma, unsigned long address,
split_huge_page(page);
put_page(page);
- BUG_ON(pmd_trans_huge(*pmd));
+
+ /*
+ * We don't always have down_write of mmap_sem here: a racing
+ * do_huge_pmd_wp_page() might have copied-on-write to another
+ * huge page before our split_huge_page() got the anon_vma lock.
+ */
+ if (unlikely(pmd_trans_huge(*pmd)))
+ goto again;
}
void split_huge_page_pmd_mm(struct mm_struct *mm, unsigned long address,
diff --git a/mm/madvise.c b/mm/madvise.c
index 7055883e6e25..85d1a5427c27 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -102,7 +102,8 @@ static long madvise_behavior(struct vm_area_struct * vma,
pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
*prev = vma_merge(mm, *prev, start, end, new_flags, vma->anon_vma,
- vma->vm_file, pgoff, vma_policy(vma));
+ vma->vm_file, pgoff, vma_policy(vma),
+ vma_get_anon_name(vma));
if (*prev) {
vma = *prev;
goto success;
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 82a187aea4c0..905ce72c8c4e 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -5584,7 +5584,13 @@ static int compare_thresholds(const void *a, const void *b)
const struct mem_cgroup_threshold *_a = a;
const struct mem_cgroup_threshold *_b = b;
- return _a->threshold - _b->threshold;
+ if (_a->threshold > _b->threshold)
+ return 1;
+
+ if (_a->threshold < _b->threshold)
+ return -1;
+
+ return 0;
}
static int mem_cgroup_oom_notify_cb(struct mem_cgroup *memcg)
diff --git a/mm/memory.c b/mm/memory.c
index 5a35443c01ad..4b60011907d7 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3525,12 +3525,12 @@ static int do_nonlinear_fault(struct mm_struct *mm, struct vm_area_struct *vma,
}
int numa_migrate_prep(struct page *page, struct vm_area_struct *vma,
- unsigned long addr, int current_nid)
+ unsigned long addr, int page_nid)
{
get_page(page);
count_vm_numa_event(NUMA_HINT_FAULTS);
- if (current_nid == numa_node_id())
+ if (page_nid == numa_node_id())
count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL);
return mpol_misplaced(page, vma, addr);
@@ -3541,7 +3541,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
{
struct page *page = NULL;
spinlock_t *ptl;
- int current_nid = -1;
+ int page_nid = -1;
int target_nid;
bool migrated = false;
@@ -3571,15 +3571,10 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
return 0;
}
- current_nid = page_to_nid(page);
- target_nid = numa_migrate_prep(page, vma, addr, current_nid);
+ page_nid = page_to_nid(page);
+ target_nid = numa_migrate_prep(page, vma, addr, page_nid);
pte_unmap_unlock(ptep, ptl);
if (target_nid == -1) {
- /*
- * Account for the fault against the current node if it not
- * being replaced regardless of where the page is located.
- */
- current_nid = numa_node_id();
put_page(page);
goto out;
}
@@ -3587,11 +3582,11 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
/* Migrate to the requested node */
migrated = migrate_misplaced_page(page, target_nid);
if (migrated)
- current_nid = target_nid;
+ page_nid = target_nid;
out:
- if (current_nid != -1)
- task_numa_fault(current_nid, 1, migrated);
+ if (page_nid != -1)
+ task_numa_fault(page_nid, 1, migrated);
return 0;
}
@@ -3606,7 +3601,6 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long offset;
spinlock_t *ptl;
bool numa = false;
- int local_nid = numa_node_id();
spin_lock(&mm->page_table_lock);
pmd = *pmdp;
@@ -3629,9 +3623,10 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
for (addr = _addr + offset; addr < _addr + PMD_SIZE; pte++, addr += PAGE_SIZE) {
pte_t pteval = *pte;
struct page *page;
- int curr_nid = local_nid;
+ int page_nid = -1;
int target_nid;
- bool migrated;
+ bool migrated = false;
+
if (!pte_present(pteval))
continue;
if (!pte_numa(pteval))
@@ -3653,25 +3648,19 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
if (unlikely(page_mapcount(page) != 1))
continue;
- /*
- * Note that the NUMA fault is later accounted to either
- * the node that is currently running or where the page is
- * migrated to.
- */
- curr_nid = local_nid;
- target_nid = numa_migrate_prep(page, vma, addr,
- page_to_nid(page));
- if (target_nid == -1) {
+ page_nid = page_to_nid(page);
+ target_nid = numa_migrate_prep(page, vma, addr, page_nid);
+ pte_unmap_unlock(pte, ptl);
+ if (target_nid != -1) {
+ migrated = migrate_misplaced_page(page, target_nid);
+ if (migrated)
+ page_nid = target_nid;
+ } else {
put_page(page);
- continue;
}
- /* Migrate to the requested node */
- pte_unmap_unlock(pte, ptl);
- migrated = migrate_misplaced_page(page, target_nid);
- if (migrated)
- curr_nid = target_nid;
- task_numa_fault(curr_nid, 1, migrated);
+ if (page_nid != -1)
+ task_numa_fault(page_nid, 1, migrated);
pte = pte_offset_map_lock(mm, pmdp, addr, &ptl);
}
@@ -4074,6 +4063,7 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
return len;
}
+EXPORT_SYMBOL_GPL(generic_access_phys);
#endif
/*
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 4baf12e534d1..0b34b3fd0940 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -728,7 +728,7 @@ static int mbind_range(struct mm_struct *mm, unsigned long start,
((vmstart - vma->vm_start) >> PAGE_SHIFT);
prev = vma_merge(mm, prev, vmstart, vmend, vma->vm_flags,
vma->anon_vma, vma->vm_file, pgoff,
- new_pol);
+ new_pol, vma_get_anon_name(name));
if (prev) {
vma = prev;
next = vma->vm_next;
diff --git a/mm/migrate.c b/mm/migrate.c
index 6f0c24438bba..bf436c15f055 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -103,7 +103,7 @@ void putback_movable_pages(struct list_head *l)
list_del(&page->lru);
dec_zone_page_state(page, NR_ISOLATED_ANON +
page_is_file_cache(page));
- if (unlikely(balloon_page_movable(page)))
+ if (unlikely(isolated_balloon_page(page)))
balloon_page_putback(page);
else
putback_lru_page(page);
@@ -1710,12 +1710,12 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
unlock_page(new_page);
put_page(new_page); /* Free it */
- unlock_page(page);
+ /* Retake the callers reference and putback on LRU */
+ get_page(page);
putback_lru_page(page);
-
- count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR);
- isolated = 0;
- goto out;
+ mod_zone_page_state(page_zone(page),
+ NR_ISOLATED_ANON + page_lru, -HPAGE_PMD_NR);
+ goto out_fail;
}
/*
@@ -1732,9 +1732,9 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);
entry = pmd_mkhuge(entry);
- page_add_new_anon_rmap(new_page, vma, haddr);
-
+ pmdp_clear_flush(vma, haddr, pmd);
set_pmd_at(mm, haddr, pmd, entry);
+ page_add_new_anon_rmap(new_page, vma, haddr);
update_mmu_cache_pmd(vma, address, &entry);
page_remove_rmap(page);
/*
@@ -1753,7 +1753,6 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
count_vm_events(PGMIGRATE_SUCCESS, HPAGE_PMD_NR);
count_vm_numa_events(NUMA_PAGE_MIGRATE, HPAGE_PMD_NR);
-out:
mod_zone_page_state(page_zone(page),
NR_ISOLATED_ANON + page_lru,
-HPAGE_PMD_NR);
@@ -1762,6 +1761,10 @@ out:
out_fail:
count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR);
out_dropref:
+ entry = pmd_mknonnuma(entry);
+ set_pmd_at(mm, haddr, pmd, entry);
+ update_mmu_cache_pmd(vma, address, &entry);
+
unlock_page(page);
put_page(page);
return 0;
diff --git a/mm/mlock.c b/mm/mlock.c
index 79b7cf7d1bca..33861c780070 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -287,7 +287,8 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev,
pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
*prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma,
- vma->vm_file, pgoff, vma_policy(vma));
+ vma->vm_file, pgoff, vma_policy(vma),
+ vma_get_anon_name(vma));
if (*prev) {
vma = *prev;
goto success;
diff --git a/mm/mmap.c b/mm/mmap.c
index 8d25fdc653be..e90411b9ebe5 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -893,7 +893,8 @@ again: remove_next = 1 + (end > next->vm_end);
* per-vma resources, so we don't attempt to merge those.
*/
static inline int is_mergeable_vma(struct vm_area_struct *vma,
- struct file *file, unsigned long vm_flags)
+ struct file *file, unsigned long vm_flags,
+ const char __user *anon_name)
{
if (vma->vm_flags ^ vm_flags)
return 0;
@@ -901,6 +902,8 @@ static inline int is_mergeable_vma(struct vm_area_struct *vma,
return 0;
if (vma->vm_ops && vma->vm_ops->close)
return 0;
+ if (vma_get_anon_name(vma) != anon_name)
+ return 0;
return 1;
}
@@ -931,9 +934,10 @@ static inline int is_mergeable_anon_vma(struct anon_vma *anon_vma1,
*/
static int
can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags,
- struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff)
+ struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff,
+ const char __user *anon_name)
{
- if (is_mergeable_vma(vma, file, vm_flags) &&
+ if (is_mergeable_vma(vma, file, vm_flags, anon_name) &&
is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) {
if (vma->vm_pgoff == vm_pgoff)
return 1;
@@ -950,9 +954,10 @@ can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags,
*/
static int
can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags,
- struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff)
+ struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff,
+ const char __user *anon_name)
{
- if (is_mergeable_vma(vma, file, vm_flags) &&
+ if (is_mergeable_vma(vma, file, vm_flags, anon_name) &&
is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) {
pgoff_t vm_pglen;
vm_pglen = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
@@ -963,9 +968,9 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags,
}
/*
- * Given a mapping request (addr,end,vm_flags,file,pgoff), figure out
- * whether that can be merged with its predecessor or its successor.
- * Or both (it neatly fills a hole).
+ * Given a mapping request (addr,end,vm_flags,file,pgoff,anon_name),
+ * figure out whether that can be merged with its predecessor or its
+ * successor. Or both (it neatly fills a hole).
*
* In most cases - when called for mmap, brk or mremap - [addr,end) is
* certain not to be mapped by the time vma_merge is called; but when
@@ -995,7 +1000,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
struct vm_area_struct *prev, unsigned long addr,
unsigned long end, unsigned long vm_flags,
struct anon_vma *anon_vma, struct file *file,
- pgoff_t pgoff, struct mempolicy *policy)
+ pgoff_t pgoff, struct mempolicy *policy,
+ const char __user *anon_name)
{
pgoff_t pglen = (end - addr) >> PAGE_SHIFT;
struct vm_area_struct *area, *next;
@@ -1021,15 +1027,15 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
*/
if (prev && prev->vm_end == addr &&
mpol_equal(vma_policy(prev), policy) &&
- can_vma_merge_after(prev, vm_flags,
- anon_vma, file, pgoff)) {
+ can_vma_merge_after(prev, vm_flags, anon_vma,
+ file, pgoff, anon_name)) {
/*
* OK, it can. Can we now merge in the successor as well?
*/
if (next && end == next->vm_start &&
mpol_equal(policy, vma_policy(next)) &&
- can_vma_merge_before(next, vm_flags,
- anon_vma, file, pgoff+pglen) &&
+ can_vma_merge_before(next, vm_flags, anon_vma,
+ file, pgoff+pglen, anon_name) &&
is_mergeable_anon_vma(prev->anon_vma,
next->anon_vma, NULL)) {
/* cases 1, 6 */
@@ -1049,8 +1055,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
*/
if (next && end == next->vm_start &&
mpol_equal(policy, vma_policy(next)) &&
- can_vma_merge_before(next, vm_flags,
- anon_vma, file, pgoff+pglen)) {
+ can_vma_merge_before(next, vm_flags, anon_vma,
+ file, pgoff+pglen, anon_name)) {
if (prev && addr < prev->vm_end) /* case 4 */
err = vma_adjust(prev, prev->vm_start,
addr, prev->vm_pgoff, NULL);
@@ -1519,7 +1525,8 @@ munmap_back:
/*
* Can we just expand an old mapping?
*/
- vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, NULL);
+ vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff,
+ NULL, NULL);
if (vma)
goto out;
@@ -2663,7 +2670,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)
/* Can we just expand an old private anonymous mapping? */
vma = vma_merge(mm, prev, addr, addr + len, flags,
- NULL, NULL, pgoff, NULL);
+ NULL, NULL, pgoff, NULL, NULL);
if (vma)
goto out;
@@ -2821,7 +2828,8 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
if (find_vma_links(mm, addr, addr + len, &prev, &rb_link, &rb_parent))
return NULL; /* should never get here */
new_vma = vma_merge(mm, prev, addr, addr + len, vma->vm_flags,
- vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma));
+ vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma),
+ vma_get_anon_name(vma));
if (new_vma) {
/*
* Source vma may have been merged into new_vma
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 94722a4d6b43..5d80adfabec8 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -145,7 +145,7 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma,
split_huge_page_pmd(vma, addr, pmd);
else if (change_huge_pmd(vma, pmd, addr, newprot,
prot_numa)) {
- pages += HPAGE_PMD_NR;
+ pages++;
continue;
}
/* fall through */
@@ -271,7 +271,8 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
*/
pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
*pprev = vma_merge(mm, *pprev, start, end, newflags,
- vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma));
+ vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma),
+ vma_get_anon_name(vma));
if (*pprev) {
vma = *pprev;
goto success;
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 4514ad7415c3..aca4364275b5 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -1104,11 +1104,11 @@ static unsigned long dirty_poll_interval(unsigned long dirty,
return 1;
}
-static long bdi_max_pause(struct backing_dev_info *bdi,
- unsigned long bdi_dirty)
+static unsigned long bdi_max_pause(struct backing_dev_info *bdi,
+ unsigned long bdi_dirty)
{
- long bw = bdi->avg_write_bandwidth;
- long t;
+ unsigned long bw = bdi->avg_write_bandwidth;
+ unsigned long t;
/*
* Limit pause time for small memory systems. If sleeping for too long
@@ -1120,7 +1120,7 @@ static long bdi_max_pause(struct backing_dev_info *bdi,
t = bdi_dirty / (1 + bw / roundup_pow_of_two(1 + HZ / 8));
t++;
- return min_t(long, t, MAX_PAUSE);
+ return min_t(unsigned long, t, MAX_PAUSE);
}
static long bdi_min_pause(struct backing_dev_info *bdi,
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 63858c4522b3..82e897fc291f 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -196,9 +196,21 @@ static char * const zone_names[MAX_NR_ZONES] = {
"Movable",
};
+/*
+ * Try to keep at least this much lowmem free. Do not allow normal
+ * allocations below this point, only high priority ones. Automatically
+ * tuned according to the amount of memory in the system.
+ */
int min_free_kbytes = 1024;
int min_free_order_shift = 1;
+/*
+ * Extra memory for the system to try freeing. Used to temporarily
+ * free memory, to make space for new workloads. Anyone can allocate
+ * down to the min watermarks controlled by min_free_kbytes above.
+ */
+int extra_free_kbytes = 0;
+
static unsigned long __meminitdata nr_kernel_pages;
static unsigned long __meminitdata nr_all_pages;
static unsigned long __meminitdata dma_reserve;
@@ -5321,6 +5333,7 @@ static void setup_per_zone_lowmem_reserve(void)
static void __setup_per_zone_wmarks(void)
{
unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10);
+ unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10);
unsigned long lowmem_pages = 0;
struct zone *zone;
unsigned long flags;
@@ -5332,11 +5345,14 @@ static void __setup_per_zone_wmarks(void)
}
for_each_zone(zone) {
- u64 tmp;
+ u64 min, low;
spin_lock_irqsave(&zone->lock, flags);
- tmp = (u64)pages_min * zone->managed_pages;
- do_div(tmp, lowmem_pages);
+ min = (u64)pages_min * zone->managed_pages;
+ do_div(min, lowmem_pages);
+ low = (u64)pages_low * zone->managed_pages;
+ do_div(low, vm_total_pages);
+
if (is_highmem(zone)) {
/*
* __GFP_HIGH and PF_MEMALLOC allocations usually don't
@@ -5357,11 +5373,13 @@ static void __setup_per_zone_wmarks(void)
* If it's a lowmem zone, reserve a number of pages
* proportionate to the zone's size.
*/
- zone->watermark[WMARK_MIN] = tmp;
+ zone->watermark[WMARK_MIN] = min;
}
- zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + (tmp >> 2);
- zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> 1);
+ zone->watermark[WMARK_LOW] = min_wmark_pages(zone) +
+ low + (min >> 2);
+ zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) +
+ low + (min >> 1);
setup_zone_migrate_reserve(zone);
spin_unlock_irqrestore(&zone->lock, flags);
@@ -5474,7 +5492,7 @@ module_init(init_per_zone_wmark_min)
/*
* min_free_kbytes_sysctl_handler - just a wrapper around proc_dointvec() so
* that we can call two helper functions whenever min_free_kbytes
- * changes.
+ * or extra_free_kbytes changes.
*/
int min_free_kbytes_sysctl_handler(ctl_table *table, int write,
void __user *buffer, size_t *length, loff_t *ppos)
diff --git a/mm/page_io.c b/mm/page_io.c
index a8a3ef45fed7..ba05b64e5d8d 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -21,6 +21,7 @@
#include <linux/writeback.h>
#include <linux/frontswap.h>
#include <linux/aio.h>
+#include <linux/blkdev.h>
#include <asm/pgtable.h>
static struct bio *get_swap_bio(gfp_t gfp_flags,
@@ -80,9 +81,54 @@ void end_swap_bio_read(struct bio *bio, int err)
imajor(bio->bi_bdev->bd_inode),
iminor(bio->bi_bdev->bd_inode),
(unsigned long long)bio->bi_sector);
- } else {
- SetPageUptodate(page);
+ goto out;
}
+
+ SetPageUptodate(page);
+
+ /*
+ * There is no guarantee that the page is in swap cache - the software
+ * suspend code (at least) uses end_swap_bio_read() against a non-
+ * swapcache page. So we must check PG_swapcache before proceeding with
+ * this optimization.
+ */
+ if (likely(PageSwapCache(page))) {
+ struct swap_info_struct *sis;
+
+ sis = page_swap_info(page);
+ if (sis->flags & SWP_BLKDEV) {
+ /*
+ * The swap subsystem performs lazy swap slot freeing,
+ * expecting that the page will be swapped out again.
+ * So we can avoid an unnecessary write if the page
+ * isn't redirtied.
+ * This is good for real swap storage because we can
+ * reduce unnecessary I/O and enhance wear-leveling
+ * if an SSD is used as the as swap device.
+ * But if in-memory swap device (eg zram) is used,
+ * this causes a duplicated copy between uncompressed
+ * data in VM-owned memory and compressed data in
+ * zram-owned memory. So let's free zram-owned memory
+ * and make the VM-owned decompressed page *dirty*,
+ * so the page should be swapped out somewhere again if
+ * we again wish to reclaim it.
+ */
+ struct gendisk *disk = sis->bdev->bd_disk;
+ if (disk->fops->swap_slot_free_notify) {
+ swp_entry_t entry;
+ unsigned long offset;
+
+ entry.val = page_private(page);
+ offset = swp_offset(entry);
+
+ SetPageDirty(page);
+ disk->fops->swap_slot_free_notify(sis->bdev,
+ offset);
+ }
+ }
+ }
+
+out:
unlock_page(page);
bio_put(bio);
}
diff --git a/mm/swap.c b/mm/swap.c
index dfd7d71d6841..9f2225f2b5b0 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -31,6 +31,7 @@
#include <linux/memcontrol.h>
#include <linux/gfp.h>
#include <linux/uio.h>
+#include <linux/hugetlb.h>
#include "internal.h"
@@ -78,6 +79,19 @@ static void __put_compound_page(struct page *page)
static void put_compound_page(struct page *page)
{
+ /*
+ * hugetlbfs pages cannot be split from under us. If this is a
+ * hugetlbfs page, check refcount on head page and release the page if
+ * the refcount becomes zero.
+ */
+ if (PageHuge(page)) {
+ page = compound_head(page);
+ if (put_page_testzero(page))
+ __put_compound_page(page);
+
+ return;
+ }
+
if (unlikely(PageTail(page))) {
/* __split_huge_page_refcount can run under us */
struct page *page_head = compound_trans_head(page);
@@ -181,38 +195,51 @@ bool __get_page_tail(struct page *page)
* proper PT lock that already serializes against
* split_huge_page().
*/
- unsigned long flags;
bool got = false;
- struct page *page_head = compound_trans_head(page);
+ struct page *page_head;
- if (likely(page != page_head && get_page_unless_zero(page_head))) {
+ /*
+ * If this is a hugetlbfs page it cannot be split under us. Simply
+ * increment refcount for the head page.
+ */
+ if (PageHuge(page)) {
+ page_head = compound_head(page);
+ atomic_inc(&page_head->_count);
+ got = true;
+ } else {
+ unsigned long flags;
+
+ page_head = compound_trans_head(page);
+ if (likely(page != page_head &&
+ get_page_unless_zero(page_head))) {
+
+ /* Ref to put_compound_page() comment. */
+ if (PageSlab(page_head)) {
+ if (likely(PageTail(page))) {
+ __get_page_tail_foll(page, false);
+ return true;
+ } else {
+ put_page(page_head);
+ return false;
+ }
+ }
- /* Ref to put_compound_page() comment. */
- if (PageSlab(page_head)) {
+ /*
+ * page_head wasn't a dangling pointer but it
+ * may not be a head page anymore by the time
+ * we obtain the lock. That is ok as long as it
+ * can't be freed from under us.
+ */
+ flags = compound_lock_irqsave(page_head);
+ /* here __split_huge_page_refcount won't run anymore */
if (likely(PageTail(page))) {
__get_page_tail_foll(page, false);
- return true;
- } else {
- put_page(page_head);
- return false;
+ got = true;
}
+ compound_unlock_irqrestore(page_head, flags);
+ if (unlikely(!got))
+ put_page(page_head);
}
-
- /*
- * page_head wasn't a dangling pointer but it
- * may not be a head page anymore by the time
- * we obtain the lock. That is ok as long as it
- * can't be freed from under us.
- */
- flags = compound_lock_irqsave(page_head);
- /* here __split_huge_page_refcount won't run anymore */
- if (likely(PageTail(page))) {
- __get_page_tail_foll(page, false);
- got = true;
- }
- compound_unlock_irqrestore(page_head, flags);
- if (unlikely(!got))
- put_page(page_head);
}
return got;
}
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index d365724feb05..d4565606cc96 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -388,12 +388,12 @@ nocache:
addr = ALIGN(first->va_end, align);
if (addr < vstart)
goto nocache;
- if (addr + size - 1 < addr)
+ if (addr + size < addr)
goto overflow;
} else {
addr = ALIGN(vstart, align);
- if (addr + size - 1 < addr)
+ if (addr + size < addr)
goto overflow;
n = vmap_area_root.rb_node;
@@ -420,7 +420,7 @@ nocache:
if (addr + cached_hole_size < first->va_start)
cached_hole_size = first->va_start - addr;
addr = ALIGN(first->va_end, align);
- if (addr + size - 1 < addr)
+ if (addr + size < addr)
goto overflow;
if (list_is_last(&first->list, &vmap_area_list))
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 5d41059e1e92..78baaa80eee6 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -49,6 +49,7 @@
#include <asm/div64.h>
#include <linux/swapops.h>
+#include <linux/balloon_compaction.h>
#include "internal.h"
@@ -1021,7 +1022,8 @@ unsigned long reclaim_clean_pages_from_list(struct zone *zone,
LIST_HEAD(clean_pages);
list_for_each_entry_safe(page, next, page_list, lru) {
- if (page_is_file_cache(page) && !PageDirty(page)) {
+ if (page_is_file_cache(page) && !PageDirty(page) &&
+ !isolated_balloon_page(page)) {
ClearPageActive(page);
list_move(&page->lru, &clean_pages);
}
diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c
index 309129732285..c7e634af8516 100644
--- a/net/8021q/vlan_netlink.c
+++ b/net/8021q/vlan_netlink.c
@@ -171,7 +171,7 @@ static size_t vlan_get_size(const struct net_device *dev)
return nla_total_size(2) + /* IFLA_VLAN_PROTOCOL */
nla_total_size(2) + /* IFLA_VLAN_ID */
- sizeof(struct ifla_vlan_flags) + /* IFLA_VLAN_FLAGS */
+ nla_total_size(sizeof(struct ifla_vlan_flags)) + /* IFLA_VLAN_FLAGS */
vlan_qos_map_size(vlan->nr_ingress_mappings) +
vlan_qos_map_size(vlan->nr_egress_mappings);
}
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 51aafd669cbb..f1cb1f56cda9 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -61,6 +61,7 @@ static int __init batadv_init(void)
batadv_recv_handler_init();
batadv_iv_init();
+ batadv_nc_init();
batadv_event_workqueue = create_singlethread_workqueue("bat_events");
@@ -138,7 +139,7 @@ int batadv_mesh_init(struct net_device *soft_iface)
if (ret < 0)
goto err;
- ret = batadv_nc_init(bat_priv);
+ ret = batadv_nc_mesh_init(bat_priv);
if (ret < 0)
goto err;
@@ -163,7 +164,7 @@ void batadv_mesh_free(struct net_device *soft_iface)
batadv_vis_quit(bat_priv);
batadv_gw_node_purge(bat_priv);
- batadv_nc_free(bat_priv);
+ batadv_nc_mesh_free(bat_priv);
batadv_dat_free(bat_priv);
batadv_bla_free(bat_priv);
diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c
index e84629ece9b7..f97aeee2201c 100644
--- a/net/batman-adv/network-coding.c
+++ b/net/batman-adv/network-coding.c
@@ -35,6 +35,20 @@ static int batadv_nc_recv_coded_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
/**
+ * batadv_nc_init - one-time initialization for network coding
+ */
+int __init batadv_nc_init(void)
+{
+ int ret;
+
+ /* Register our packet type */
+ ret = batadv_recv_handler_register(BATADV_CODED,
+ batadv_nc_recv_coded_packet);
+
+ return ret;
+}
+
+/**
* batadv_nc_start_timer - initialise the nc periodic worker
* @bat_priv: the bat priv with all the soft interface information
*/
@@ -45,10 +59,10 @@ static void batadv_nc_start_timer(struct batadv_priv *bat_priv)
}
/**
- * batadv_nc_init - initialise coding hash table and start house keeping
+ * batadv_nc_mesh_init - initialise coding hash table and start house keeping
* @bat_priv: the bat priv with all the soft interface information
*/
-int batadv_nc_init(struct batadv_priv *bat_priv)
+int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
{
bat_priv->nc.timestamp_fwd_flush = jiffies;
bat_priv->nc.timestamp_sniffed_purge = jiffies;
@@ -70,11 +84,6 @@ int batadv_nc_init(struct batadv_priv *bat_priv)
batadv_hash_set_lock_class(bat_priv->nc.coding_hash,
&batadv_nc_decoding_hash_lock_class_key);
- /* Register our packet type */
- if (batadv_recv_handler_register(BATADV_CODED,
- batadv_nc_recv_coded_packet) < 0)
- goto err;
-
INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker);
batadv_nc_start_timer(bat_priv);
@@ -1722,12 +1731,11 @@ free_nc_packet:
}
/**
- * batadv_nc_free - clean up network coding memory
+ * batadv_nc_mesh_free - clean up network coding memory
* @bat_priv: the bat priv with all the soft interface information
*/
-void batadv_nc_free(struct batadv_priv *bat_priv)
+void batadv_nc_mesh_free(struct batadv_priv *bat_priv)
{
- batadv_recv_handler_unregister(BATADV_CODED);
cancel_delayed_work_sync(&bat_priv->nc.work);
batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash, NULL);
diff --git a/net/batman-adv/network-coding.h b/net/batman-adv/network-coding.h
index 4fa6d0caddbd..bd4295fb960f 100644
--- a/net/batman-adv/network-coding.h
+++ b/net/batman-adv/network-coding.h
@@ -22,8 +22,9 @@
#ifdef CONFIG_BATMAN_ADV_NC
-int batadv_nc_init(struct batadv_priv *bat_priv);
-void batadv_nc_free(struct batadv_priv *bat_priv);
+int batadv_nc_init(void);
+int batadv_nc_mesh_init(struct batadv_priv *bat_priv);
+void batadv_nc_mesh_free(struct batadv_priv *bat_priv);
void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
struct batadv_orig_node *orig_neigh_node,
@@ -47,12 +48,17 @@ int batadv_nc_init_debugfs(struct batadv_priv *bat_priv);
#else /* ifdef CONFIG_BATMAN_ADV_NC */
-static inline int batadv_nc_init(struct batadv_priv *bat_priv)
+static inline int batadv_nc_init(void)
{
return 0;
}
-static inline void batadv_nc_free(struct batadv_priv *bat_priv)
+static inline int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
+{
+ return 0;
+}
+
+static inline void batadv_nc_mesh_free(struct batadv_priv *bat_priv)
{
return;
}
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index ace5e55fe5a3..7c88f5f83598 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1123,7 +1123,11 @@ int hci_dev_open(__u16 dev)
goto done;
}
- if (hdev->rfkill && rfkill_blocked(hdev->rfkill)) {
+ /* Check for rfkill but allow the HCI setup stage to proceed
+ * (which in itself doesn't cause any RF activity).
+ */
+ if (test_bit(HCI_RFKILLED, &hdev->dev_flags) &&
+ !test_bit(HCI_SETUP, &hdev->dev_flags)) {
ret = -ERFKILL;
goto done;
}
@@ -1545,10 +1549,13 @@ static int hci_rfkill_set_block(void *data, bool blocked)
BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
- if (!blocked)
- return 0;
-
- hci_dev_do_close(hdev);
+ if (blocked) {
+ set_bit(HCI_RFKILLED, &hdev->dev_flags);
+ if (!test_bit(HCI_SETUP, &hdev->dev_flags))
+ hci_dev_do_close(hdev);
+ } else {
+ clear_bit(HCI_RFKILLED, &hdev->dev_flags);
+}
return 0;
}
@@ -1570,9 +1577,13 @@ static void hci_power_on(struct work_struct *work)
return;
}
- if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+ if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) {
+ clear_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+ hci_dev_do_close(hdev);
+ } else if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
queue_delayed_work(hdev->req_workqueue, &hdev->power_off,
HCI_AUTO_OFF_TIMEOUT);
+ }
if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags))
mgmt_index_added(hdev);
@@ -2241,6 +2252,9 @@ int hci_register_dev(struct hci_dev *hdev)
}
}
+ if (hdev->rfkill && rfkill_blocked(hdev->rfkill))
+ set_bit(HCI_RFKILLED, &hdev->dev_flags);
+
set_bit(HCI_SETUP, &hdev->dev_flags);
if (hdev->dev_type != HCI_AMP)
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 1cd90753d1cb..49d5c941c9c7 100755
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3623,7 +3623,11 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
cp.handle = cpu_to_le16(conn->handle);
if (ltk->authenticated)
- conn->sec_level = BT_SECURITY_HIGH;
+ conn->pending_sec_level = BT_SECURITY_HIGH;
+ else
+ conn->pending_sec_level = BT_SECURITY_MEDIUM;
+
+ conn->enc_key_size = ltk->enc_size;
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 940f5acb6694..de030f50f72b 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -231,17 +231,22 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
static int hidp_send_report(struct hidp_session *session, struct hid_report *report)
{
- unsigned char buf[32], hdr;
- int rsize;
+ unsigned char hdr;
+ u8 *buf;
+ int rsize, ret;
- rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0);
- if (rsize > sizeof(buf))
+ buf = hid_alloc_report_buf(report, GFP_ATOMIC);
+ if (!buf)
return -EIO;
hid_output_report(report, buf);
hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
- return hidp_send_intr_message(session, hdr, buf, rsize);
+ rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+ ret = hidp_send_intr_message(session, hdr, buf, rsize);
+
+ kfree(buf);
+ return ret;
}
static int hidp_get_raw_report(struct hid_device *hid,
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index f66a03453a8e..06873e80a432 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -203,7 +203,7 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev, u32 filter_mask)
{
int err = 0;
- struct net_bridge_port *port = br_port_get_rcu(dev);
+ struct net_bridge_port *port = br_port_get_rtnl(dev);
/* not a bridge port and */
if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN))
@@ -443,7 +443,7 @@ static size_t br_get_link_af_size(const struct net_device *dev)
struct net_port_vlans *pv;
if (br_port_exists(dev))
- pv = nbp_get_vlan_info(br_port_get_rcu(dev));
+ pv = nbp_get_vlan_info(br_port_get_rtnl(dev));
else if (dev->priv_flags & IFF_EBRIDGE)
pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev));
else
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index d2c043a857b6..e696833a31b5 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -183,13 +183,10 @@ struct net_bridge_port
static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev)
{
- struct net_bridge_port *port =
- rcu_dereference_rtnl(dev->rx_handler_data);
-
- return br_port_exists(dev) ? port : NULL;
+ return rcu_dereference(dev->rx_handler_data);
}
-static inline struct net_bridge_port *br_port_get_rtnl(struct net_device *dev)
+static inline struct net_bridge_port *br_port_get_rtnl(const struct net_device *dev)
{
return br_port_exists(dev) ?
rtnl_dereference(dev->rx_handler_data) : NULL;
@@ -714,6 +711,7 @@ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
extern void br_init_port(struct net_bridge_port *p);
extern void br_become_designated_port(struct net_bridge_port *p);
+extern void __br_set_forward_delay(struct net_bridge *br, unsigned long t);
extern int br_set_forward_delay(struct net_bridge *br, unsigned long x);
extern int br_set_hello_time(struct net_bridge *br, unsigned long x);
extern int br_set_max_age(struct net_bridge *br, unsigned long x);
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 1c0a50f13229..3c86f0538cbb 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -209,7 +209,7 @@ static void br_record_config_information(struct net_bridge_port *p,
p->designated_age = jiffies - bpdu->message_age;
mod_timer(&p->message_age_timer, jiffies
- + (p->br->max_age - bpdu->message_age));
+ + (bpdu->max_age - bpdu->message_age));
}
/* called under bridge lock */
@@ -544,18 +544,27 @@ int br_set_max_age(struct net_bridge *br, unsigned long val)
}
+void __br_set_forward_delay(struct net_bridge *br, unsigned long t)
+{
+ br->bridge_forward_delay = t;
+ if (br_is_root_bridge(br))
+ br->forward_delay = br->bridge_forward_delay;
+}
+
int br_set_forward_delay(struct net_bridge *br, unsigned long val)
{
unsigned long t = clock_t_to_jiffies(val);
+ int err = -ERANGE;
+ spin_lock_bh(&br->lock);
if (br->stp_enabled != BR_NO_STP &&
(t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY))
- return -ERANGE;
+ goto unlock;
- spin_lock_bh(&br->lock);
- br->bridge_forward_delay = t;
- if (br_is_root_bridge(br))
- br->forward_delay = br->bridge_forward_delay;
+ __br_set_forward_delay(br, t);
+ err = 0;
+
+unlock:
spin_unlock_bh(&br->lock);
- return 0;
+ return err;
}
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index d45e760141bb..656a6f3e40de 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -129,6 +129,14 @@ static void br_stp_start(struct net_bridge *br)
char *envp[] = { NULL };
r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC);
+
+ spin_lock_bh(&br->lock);
+
+ if (br->bridge_forward_delay < BR_MIN_FORWARD_DELAY)
+ __br_set_forward_delay(br, BR_MIN_FORWARD_DELAY);
+ else if (br->bridge_forward_delay > BR_MAX_FORWARD_DELAY)
+ __br_set_forward_delay(br, BR_MAX_FORWARD_DELAY);
+
if (r == 0) {
br->stp_enabled = BR_USER_STP;
br_debug(br, "userspace STP started\n");
@@ -137,10 +145,10 @@ static void br_stp_start(struct net_bridge *br)
br_debug(br, "using kernel STP\n");
/* To start timers on any ports left in blocking */
- spin_lock_bh(&br->lock);
br_port_state_selection(br);
- spin_unlock_bh(&br->lock);
}
+
+ spin_unlock_bh(&br->lock);
}
static void br_stp_stop(struct net_bridge *br)
diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c
index 2bd4b58f4372..0f455227da83 100644
--- a/net/caif/cfctrl.c
+++ b/net/caif/cfctrl.c
@@ -293,9 +293,10 @@ int cfctrl_linkup_request(struct cflayer *layer,
count = cfctrl_cancel_req(&cfctrl->serv.layer,
user_layer);
- if (count != 1)
+ if (count != 1) {
pr_err("Could not remove request (%d)", count);
return -ENODEV;
+ }
}
return 0;
}
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 3a246a6cab47..46ec7672cccc 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -2130,6 +2130,8 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc,
dout("osdc_start_request failed map, "
" will retry %lld\n", req->r_tid);
rc = 0;
+ } else {
+ __unregister_request(osdc, req);
}
goto out_unlock;
}
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index 603ddd92db19..dbd9a4792427 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -1129,7 +1129,7 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid,
/* pg_temp? */
pgid.seed = ceph_stable_mod(pgid.seed, pool->pg_num,
- pool->pgp_num_mask);
+ pool->pg_num_mask);
pg = __lookup_pg_mapping(&osdmap->pg_temp, pgid);
if (pg) {
*num = pg->len;
diff --git a/net/compat.c b/net/compat.c
index f0a1ba6c8086..89032580bd1d 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -71,6 +71,8 @@ int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg)
__get_user(kmsg->msg_controllen, &umsg->msg_controllen) ||
__get_user(kmsg->msg_flags, &umsg->msg_flags))
return -EFAULT;
+ if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
+ return -EINVAL;
kmsg->msg_name = compat_ptr(tmp1);
kmsg->msg_iov = compat_ptr(tmp2);
kmsg->msg_control = compat_ptr(tmp3);
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index c99cc371bbd7..f97101b4d373 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -40,7 +40,7 @@ again:
struct iphdr _iph;
ip:
iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
- if (!iph)
+ if (!iph || iph->ihl < 5)
return false;
if (ip_is_fragment(iph))
@@ -149,8 +149,8 @@ ipv6:
if (poff >= 0) {
__be32 *ports, _ports;
- nhoff += poff;
- ports = skb_header_pointer(skb, nhoff, sizeof(_ports), &_ports);
+ ports = skb_header_pointer(skb, nhoff + poff,
+ sizeof(_ports), &_ports);
if (ports)
flow->ports = *ports;
}
@@ -347,7 +347,7 @@ u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb)
if (queue_index != new_index && sk &&
rcu_access_pointer(sk->sk_dst_cache))
- sk_tx_queue_set(sk, queue_index);
+ sk_tx_queue_set(sk, new_index);
queue_index = new_index;
}
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index cec074be8c43..b04f73847eda 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -550,7 +550,7 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo
return;
proto = ntohs(eth_hdr(skb)->h_proto);
- if (proto == ETH_P_IP) {
+ if (proto == ETH_P_ARP) {
struct arphdr *arp;
unsigned char *arp_ptr;
/* No arp on this interface */
@@ -1289,15 +1289,14 @@ EXPORT_SYMBOL_GPL(__netpoll_free_async);
void netpoll_cleanup(struct netpoll *np)
{
- if (!np->dev)
- return;
-
rtnl_lock();
+ if (!np->dev)
+ goto out;
__netpoll_cleanup(np);
- rtnl_unlock();
-
dev_put(np->dev);
np->dev = NULL;
+out:
+ rtnl_unlock();
}
EXPORT_SYMBOL(netpoll_cleanup);
diff --git a/net/core/scm.c b/net/core/scm.c
index 03795d0147f2..b4da80b1cc07 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -54,7 +54,7 @@ static __inline__ int scm_check_creds(struct ucred *creds)
return -EINVAL;
if ((creds->pid == task_tgid_vnr(current) ||
- ns_capable(current->nsproxy->pid_ns->user_ns, CAP_SYS_ADMIN)) &&
+ ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) &&
((uid_eq(uid, cred->uid) || uid_eq(uid, cred->euid) ||
uid_eq(uid, cred->suid)) || nsown_capable(CAP_SETUID)) &&
((gid_eq(gid, cred->gid) || gid_eq(gid, cred->egid) ||
diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c
index 6a2f13cee86a..8d9d05edd2eb 100644
--- a/net/core/secure_seq.c
+++ b/net/core/secure_seq.c
@@ -10,12 +10,27 @@
#include <net/secure_seq.h>
-static u32 net_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned;
+#if IS_ENABLED(CONFIG_IPV6) || IS_ENABLED(CONFIG_INET)
+#define NET_SECRET_SIZE (MD5_MESSAGE_BYTES / 4)
-void net_secret_init(void)
+static u32 net_secret[NET_SECRET_SIZE] ____cacheline_aligned;
+
+static void net_secret_init(void)
{
- get_random_bytes(net_secret, sizeof(net_secret));
+ u32 tmp;
+ int i;
+
+ if (likely(net_secret[0]))
+ return;
+
+ for (i = NET_SECRET_SIZE; i > 0;) {
+ do {
+ get_random_bytes(&tmp, sizeof(tmp));
+ } while (!tmp);
+ cmpxchg(&net_secret[--i], 0, tmp);
+ }
}
+#endif
#ifdef CONFIG_INET
static u32 seq_scale(u32 seq)
@@ -42,6 +57,7 @@ __u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr,
u32 hash[MD5_DIGEST_WORDS];
u32 i;
+ net_secret_init();
memcpy(hash, saddr, 16);
for (i = 0; i < 4; i++)
secret[i] = net_secret[i] + (__force u32)daddr[i];
@@ -63,6 +79,7 @@ u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
u32 hash[MD5_DIGEST_WORDS];
u32 i;
+ net_secret_init();
memcpy(hash, saddr, 16);
for (i = 0; i < 4; i++)
secret[i] = net_secret[i] + (__force u32) daddr[i];
@@ -82,6 +99,7 @@ __u32 secure_ip_id(__be32 daddr)
{
u32 hash[MD5_DIGEST_WORDS];
+ net_secret_init();
hash[0] = (__force __u32) daddr;
hash[1] = net_secret[13];
hash[2] = net_secret[14];
@@ -96,6 +114,7 @@ __u32 secure_ipv6_id(const __be32 daddr[4])
{
__u32 hash[4];
+ net_secret_init();
memcpy(hash, daddr, 16);
md5_transform(hash, net_secret);
@@ -107,6 +126,7 @@ __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
{
u32 hash[MD5_DIGEST_WORDS];
+ net_secret_init();
hash[0] = (__force u32)saddr;
hash[1] = (__force u32)daddr;
hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
@@ -121,6 +141,7 @@ u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
{
u32 hash[MD5_DIGEST_WORDS];
+ net_secret_init();
hash[0] = (__force u32)saddr;
hash[1] = (__force u32)daddr;
hash[2] = (__force u32)dport ^ net_secret[14];
@@ -140,6 +161,7 @@ u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
u32 hash[MD5_DIGEST_WORDS];
u64 seq;
+ net_secret_init();
hash[0] = (__force u32)saddr;
hash[1] = (__force u32)daddr;
hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
@@ -164,6 +186,7 @@ u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
u64 seq;
u32 i;
+ net_secret_init();
memcpy(hash, saddr, 16);
for (i = 0; i < 4; i++)
secret[i] = net_secret[i] + daddr[i];
diff --git a/net/core/sock.c b/net/core/sock.c
index d6d024cfaaaf..6565431b0e6d 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2271,6 +2271,7 @@ void sock_init_data(struct socket *sock, struct sock *sk)
sk->sk_stamp = ktime_set(-1L, 0);
+ sk->sk_pacing_rate = ~0U;
/*
* Before updating sk_refcnt, we must commit prior changes to memory
* (Documentation/RCU/rculist_nulls.txt for details)
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 9c61f9c02fdb..6cf9f7782ad4 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -135,6 +135,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (dst)
dst->ops->redirect(dst, sk, skb);
+ goto out;
}
if (type == ICMPV6_PKT_TOOBIG) {
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 109017e82266..f022e0e97dc5 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -276,10 +276,8 @@ void build_ehash_secret(void)
get_random_bytes(&rnd, sizeof(rnd));
} while (rnd == 0);
- if (cmpxchg(&inet_ehash_secret, 0, rnd) == 0) {
+ if (cmpxchg(&inet_ehash_secret, 0, rnd) == 0)
get_random_bytes(&ipv6_hash_secret, sizeof(ipv6_hash_secret));
- net_secret_init();
- }
}
EXPORT_SYMBOL(build_ehash_secret);
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index d8c232794bcb..089b4af4fecc 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -343,7 +343,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
pip->saddr = fl4.saddr;
pip->protocol = IPPROTO_IGMP;
pip->tot_len = 0; /* filled in later */
- ip_select_ident(pip, &rt->dst, NULL);
+ ip_select_ident(skb, &rt->dst, NULL);
((u8 *)&pip[1])[0] = IPOPT_RA;
((u8 *)&pip[1])[1] = 4;
((u8 *)&pip[1])[2] = 0;
@@ -687,7 +687,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
iph->daddr = dst;
iph->saddr = fl4.saddr;
iph->protocol = IPPROTO_IGMP;
- ip_select_ident(iph, &rt->dst, NULL);
+ ip_select_ident(skb, &rt->dst, NULL);
((u8 *)&iph[1])[0] = IPOPT_RA;
((u8 *)&iph[1])[1] = 4;
((u8 *)&iph[1])[2] = 0;
@@ -709,7 +709,7 @@ static void igmp_gq_timer_expire(unsigned long data)
in_dev->mr_gq_running = 0;
igmpv3_send_report(in_dev, NULL);
- __in_dev_put(in_dev);
+ in_dev_put(in_dev);
}
static void igmp_ifc_timer_expire(unsigned long data)
@@ -721,7 +721,7 @@ static void igmp_ifc_timer_expire(unsigned long data)
in_dev->mr_ifc_count--;
igmp_ifc_start_timer(in_dev, IGMP_Unsolicited_Report_Interval);
}
- __in_dev_put(in_dev);
+ in_dev_put(in_dev);
}
static void igmp_ifc_event(struct in_device *in_dev)
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 6af375afeeef..c95848d00039 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -287,7 +287,7 @@ begintw:
if (unlikely(!INET_TW_MATCH(sk, net, acookie,
saddr, daddr, ports,
dif))) {
- sock_put(sk);
+ inet_twsk_put(inet_twsk(sk));
goto begintw;
}
goto out;
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index 000e3d239d64..33d5537881ed 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -32,8 +32,8 @@
* At the moment of writing this notes identifier of IP packets is generated
* to be unpredictable using this code only for packets subjected
* (actually or potentially) to defragmentation. I.e. DF packets less than
- * PMTU in size uses a constant ID and do not use this code (see
- * ip_select_ident() in include/net/ip.h).
+ * PMTU in size when local fragmentation is disabled use a constant ID and do
+ * not use this code (see ip_select_ident() in include/net/ip.h).
*
* Route cache entries hold references to our nodes.
* New cache entries get references via lookup by destination IP address in
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index c52fee0976da..64e4e98c8786 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -335,7 +335,7 @@ static int ipgre_rcv(struct sk_buff *skb)
iph->saddr, iph->daddr, tpi.key);
if (tunnel) {
- ip_tunnel_rcv(tunnel, skb, &tpi, log_ecn_error);
+ ip_tunnel_rcv(tunnel, skb, &tpi, hdr_len, log_ecn_error);
return 0;
}
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 4bcabf3ab4ca..6ca5873d6175 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -148,7 +148,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
iph->daddr = (opt && opt->opt.srr ? opt->opt.faddr : daddr);
iph->saddr = saddr;
iph->protocol = sk->sk_protocol;
- ip_select_ident(iph, &rt->dst, sk);
+ ip_select_ident(skb, &rt->dst, sk);
if (opt && opt->opt.optlen) {
iph->ihl += opt->opt.optlen>>2;
@@ -394,7 +394,7 @@ packet_routed:
ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0);
}
- ip_select_ident_more(iph, &rt->dst, sk,
+ ip_select_ident_more(skb, &rt->dst, sk,
(skb_shinfo(skb)->gso_segs ?: 1) - 1);
skb->priority = sk->sk_priority;
@@ -844,7 +844,7 @@ static int __ip_append_data(struct sock *sk,
csummode = CHECKSUM_PARTIAL;
cork->length += length;
- if (((length > mtu) || (skb && skb_is_gso(skb))) &&
+ if (((length > mtu) || (skb && skb_has_frags(skb))) &&
(sk->sk_protocol == IPPROTO_UDP) &&
(rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len) {
err = ip_ufo_append_data(sk, queue, getfrag, from, length,
@@ -1324,7 +1324,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
else
ttl = ip_select_ttl(inet, &rt->dst);
- iph = (struct iphdr *)skb->data;
+ iph = ip_hdr(skb);
iph->version = 4;
iph->ihl = 5;
iph->tos = inet->tos;
@@ -1332,7 +1332,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
iph->ttl = ttl;
iph->protocol = sk->sk_protocol;
ip_copy_addrs(iph, fl4);
- ip_select_ident(iph, &rt->dst, sk);
+ ip_select_ident(skb, &rt->dst, sk);
if (opt) {
iph->ihl += opt->optlen>>2;
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index b7a4c21c06ec..46dcf32c012e 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -402,7 +402,7 @@ static struct ip_tunnel *ip_tunnel_create(struct net *net,
}
int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
- const struct tnl_ptk_info *tpi, bool log_ecn_error)
+ const struct tnl_ptk_info *tpi, int hdr_len, bool log_ecn_error)
{
struct pcpu_tstats *tstats;
const struct iphdr *iph = ip_hdr(skb);
@@ -413,7 +413,7 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
skb->protocol = tpi->proto;
skb->mac_header = skb->network_header;
- __pskb_pull(skb, tunnel->hlen);
+ __pskb_pull(skb, hdr_len);
skb_postpull_rcsum(skb, skb_transport_header(skb), tunnel->hlen);
#ifdef CONFIG_NET_IPGRE_BROADCAST
if (ipv4_is_multicast(iph->daddr)) {
@@ -659,13 +659,13 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct iphdr)
+ rt->dst.header_len;
- if (max_headroom > dev->needed_headroom) {
+ if (max_headroom > dev->needed_headroom)
dev->needed_headroom = max_headroom;
- if (skb_cow_head(skb, dev->needed_headroom)) {
- dev->stats.tx_dropped++;
- dev_kfree_skb(skb);
- return;
- }
+
+ if (skb_cow_head(skb, dev->needed_headroom)) {
+ dev->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return;
}
skb_dst_drop(skb);
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 17cc0ffa8c0d..065604127418 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -285,8 +285,17 @@ static int vti_rcv(struct sk_buff *skb)
tunnel = vti_tunnel_lookup(dev_net(skb->dev), iph->saddr, iph->daddr);
if (tunnel != NULL) {
struct pcpu_tstats *tstats;
+ u32 oldmark = skb->mark;
+ int ret;
- if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
+
+ /* temporarily mark the skb with the tunnel o_key, to
+ * only match policies with this mark.
+ */
+ skb->mark = be32_to_cpu(tunnel->parms.o_key);
+ ret = xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb);
+ skb->mark = oldmark;
+ if (!ret)
return -1;
tstats = this_cpu_ptr(tunnel->dev->tstats);
@@ -295,7 +304,6 @@ static int vti_rcv(struct sk_buff *skb)
tstats->rx_bytes += skb->len;
u64_stats_update_end(&tstats->syncp);
- skb->mark = 0;
secpath_reset(skb);
skb->dev = tunnel->dev;
return 1;
@@ -327,7 +335,7 @@ static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
memset(&fl4, 0, sizeof(fl4));
flowi4_init_output(&fl4, tunnel->parms.link,
- be32_to_cpu(tunnel->parms.i_key), RT_TOS(tos),
+ be32_to_cpu(tunnel->parms.o_key), RT_TOS(tos),
RT_SCOPE_UNIVERSE,
IPPROTO_IPIP, 0,
dst, tiph->saddr, 0, 0);
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 7cfc45624b6d..f5cc7b331511 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -195,7 +195,7 @@ static int ipip_rcv(struct sk_buff *skb)
if (tunnel) {
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;
- return ip_tunnel_rcv(tunnel, skb, &tpi, log_ecn_error);
+ return ip_tunnel_rcv(tunnel, skb, &tpi, 0, log_ecn_error);
}
return -1;
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 9d9610ae7855..2c581da1fcab 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1658,7 +1658,7 @@ static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
iph->protocol = IPPROTO_IPIP;
iph->ihl = 5;
iph->tot_len = htons(skb->len);
- ip_select_ident(iph, skb_dst(skb), NULL);
+ ip_select_ident(skb, skb_dst(skb), NULL);
ip_send_check(iph);
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 61e60d67adca..6fb233772f79 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -387,7 +387,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
iph->check = 0;
iph->tot_len = htons(length);
if (!iph->id)
- ip_select_ident(iph, &rt->dst, NULL);
+ ip_select_ident(skb, &rt->dst, NULL);
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
}
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index d35bbf0cf404..d11e73ce9365 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -2020,7 +2020,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
RT_SCOPE_LINK);
goto make_route;
}
- if (fl4->saddr) {
+ if (!fl4->saddr) {
if (ipv4_is_multicast(fl4->daddr))
fl4->saddr = inet_select_addr(dev_out, 0,
fl4->flowi4_scope);
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 3f25e75ae692..90b26beb84d4 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -29,6 +29,7 @@
static int zero;
static int one = 1;
static int four = 4;
+static int gso_max_segs = GSO_MAX_SEGS;
static int tcp_retr1_max = 255;
static int ip_local_port_range_min[] = { 1, 1 };
static int ip_local_port_range_max[] = { 65535, 65535 };
@@ -753,6 +754,15 @@ static struct ctl_table ipv4_table[] = {
.extra2 = &four,
},
{
+ .procname = "tcp_min_tso_segs",
+ .data = &sysctl_tcp_min_tso_segs,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &gso_max_segs,
+ },
+ {
.procname = "udp_mem",
.data = &sysctl_udp_mem,
.maxlen = sizeof(sysctl_udp_mem),
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index acc91d6c2c75..ce657df38cba 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -286,6 +286,8 @@
int sysctl_tcp_fin_timeout __read_mostly = TCP_FIN_TIMEOUT;
+int sysctl_tcp_min_tso_segs __read_mostly = 2;
+
struct percpu_counter tcp_orphan_count;
EXPORT_SYMBOL_GPL(tcp_orphan_count);
@@ -790,12 +792,28 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now,
xmit_size_goal = mss_now;
if (large_allowed && sk_can_gso(sk)) {
- xmit_size_goal = ((sk->sk_gso_max_size - 1) -
- inet_csk(sk)->icsk_af_ops->net_header_len -
- inet_csk(sk)->icsk_ext_hdr_len -
- tp->tcp_header_len);
+ u32 gso_size, hlen;
+
+ /* Maybe we should/could use sk->sk_prot->max_header here ? */
+ hlen = inet_csk(sk)->icsk_af_ops->net_header_len +
+ inet_csk(sk)->icsk_ext_hdr_len +
+ tp->tcp_header_len;
+
+ /* Goal is to send at least one packet per ms,
+ * not one big TSO packet every 100 ms.
+ * This preserves ACK clocking and is consistent
+ * with tcp_tso_should_defer() heuristic.
+ */
+ gso_size = sk->sk_pacing_rate / (2 * MSEC_PER_SEC);
+ gso_size = max_t(u32, gso_size,
+ sysctl_tcp_min_tso_segs * mss_now);
+
+ xmit_size_goal = min_t(u32, gso_size,
+ sk->sk_gso_max_size - 1 - hlen);
- /* TSQ : try to have two TSO segments in flight */
+ /* TSQ : try to have at least two segments in flight
+ * (one in NIC TX ring, another in Qdisc)
+ */
xmit_size_goal = min_t(u32, xmit_size_goal,
sysctl_tcp_limit_output_bytes >> 1);
@@ -2460,10 +2478,11 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
case TCP_THIN_DUPACK:
if (val < 0 || val > 1)
err = -EINVAL;
- else
+ else {
tp->thin_dupack = val;
if (tp->thin_dupack)
tcp_disable_early_retrans(tp);
+ }
break;
case TCP_REPAIR:
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 4b75aad14b04..e15d330919af 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -699,6 +699,34 @@ static void tcp_rtt_estimator(struct sock *sk, const __u32 mrtt)
}
}
+/* Set the sk_pacing_rate to allow proper sizing of TSO packets.
+ * Note: TCP stack does not yet implement pacing.
+ * FQ packet scheduler can be used to implement cheap but effective
+ * TCP pacing, to smooth the burst on large writes when packets
+ * in flight is significantly lower than cwnd (or rwin)
+ */
+static void tcp_update_pacing_rate(struct sock *sk)
+{
+ const struct tcp_sock *tp = tcp_sk(sk);
+ u64 rate;
+
+ /* set sk_pacing_rate to 200 % of current rate (mss * cwnd / srtt) */
+ rate = (u64)tp->mss_cache * 2 * (HZ << 3);
+
+ rate *= max(tp->snd_cwnd, tp->packets_out);
+
+ /* Correction for small srtt : minimum srtt being 8 (1 jiffy << 3),
+ * be conservative and assume srtt = 1 (125 us instead of 1.25 ms)
+ * We probably need usec resolution in the future.
+ * Note: This also takes care of possible srtt=0 case,
+ * when tcp_rtt_estimator() was not yet called.
+ */
+ if (tp->srtt > 8 + 2)
+ do_div(rate, tp->srtt);
+
+ sk->sk_pacing_rate = min_t(u64, rate, ~0U);
+}
+
/* Calculate rto without backoff. This is the second half of Van Jacobson's
* routine referred to above.
*/
@@ -1264,7 +1292,10 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
tp->lost_cnt_hint -= tcp_skb_pcount(prev);
}
- TCP_SKB_CB(skb)->tcp_flags |= TCP_SKB_CB(prev)->tcp_flags;
+ TCP_SKB_CB(prev)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags;
+ if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
+ TCP_SKB_CB(prev)->end_seq++;
+
if (skb == tcp_highest_sack(sk))
tcp_advance_highest_sack(sk, skb);
@@ -3314,7 +3345,7 @@ static void tcp_process_tlp_ack(struct sock *sk, u32 ack, int flag)
tcp_init_cwnd_reduction(sk, true);
tcp_set_ca_state(sk, TCP_CA_CWR);
tcp_end_cwnd_reduction(sk);
- tcp_set_ca_state(sk, TCP_CA_Open);
+ tcp_try_keep_open(sk);
NET_INC_STATS_BH(sock_net(sk),
LINUX_MIB_TCPLOSSPROBERECOVERY);
}
@@ -3330,7 +3361,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
u32 ack_seq = TCP_SKB_CB(skb)->seq;
u32 ack = TCP_SKB_CB(skb)->ack_seq;
bool is_dupack = false;
- u32 prior_in_flight;
+ u32 prior_in_flight, prior_cwnd = tp->snd_cwnd, prior_rtt = tp->srtt;
u32 prior_fackets;
int prior_packets = tp->packets_out;
int prior_sacked = tp->sacked_out;
@@ -3438,6 +3469,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
if (icsk->icsk_pending == ICSK_TIME_RETRANS)
tcp_schedule_loss_probe(sk);
+ if (tp->srtt != prior_rtt || tp->snd_cwnd != prior_cwnd)
+ tcp_update_pacing_rate(sk);
return 1;
no_queue:
@@ -5736,6 +5769,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
} else
tcp_init_metrics(sk);
+ tcp_update_pacing_rate(sk);
+
/* Prevent spurious tcp_cwnd_restart() on
* first data packet.
*/
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 0145ce7e6098..cd16eb06bebf 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -887,8 +887,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
skb_orphan(skb);
skb->sk = sk;
- skb->destructor = (sysctl_tcp_limit_output_bytes > 0) ?
- tcp_wfree : sock_wfree;
+ skb->destructor = tcp_wfree;
atomic_add(skb->truesize, &sk->sk_wmem_alloc);
/* Build TCP header and checksum it. */
@@ -977,6 +976,9 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
unsigned int mss_now)
{
+ /* Make sure we own this skb before messing gso_size/gso_segs */
+ WARN_ON_ONCE(skb_cloned(skb));
+
if (skb->len <= mss_now || !sk_can_gso(sk) ||
skb->ip_summed == CHECKSUM_NONE) {
/* Avoid the costly divide in the normal
@@ -1058,9 +1060,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
if (nsize < 0)
nsize = 0;
- if (skb_cloned(skb) &&
- skb_is_nonlinear(skb) &&
- pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+ if (skb_unclone(skb, GFP_ATOMIC))
return -ENOMEM;
/* Get a new skb... force flag on. */
@@ -1623,7 +1623,7 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb)
/* If a full-sized TSO skb can be sent, do it. */
if (limit >= min_t(unsigned int, sk->sk_gso_max_size,
- sk->sk_gso_max_segs * tp->mss_cache))
+ tp->xmit_size_goal_segs * tp->mss_cache))
goto send_now;
/* Middle in queue won't get any more data, full sendable already? */
@@ -1832,7 +1832,6 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
while ((skb = tcp_send_head(sk))) {
unsigned int limit;
-
tso_segs = tcp_init_tso_segs(sk, skb, mss_now);
BUG_ON(!tso_segs);
@@ -1861,13 +1860,20 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
break;
}
- /* TSQ : sk_wmem_alloc accounts skb truesize,
- * including skb overhead. But thats OK.
+ /* TCP Small Queues :
+ * Control number of packets in qdisc/devices to two packets / or ~1 ms.
+ * This allows for :
+ * - better RTT estimation and ACK scheduling
+ * - faster recovery
+ * - high rates
*/
- if (atomic_read(&sk->sk_wmem_alloc) >= sysctl_tcp_limit_output_bytes) {
+ limit = max(skb->truesize, sk->sk_pacing_rate >> 10);
+
+ if (atomic_read(&sk->sk_wmem_alloc) > limit) {
set_bit(TSQ_THROTTLED, &tp->tsq_flags);
break;
}
+
limit = mss_now;
if (tso_segs > 1 && !tcp_urg_mode(tp))
limit = tcp_mss_split_point(sk, skb, mss_now,
@@ -2329,6 +2335,8 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
int oldpcount = tcp_skb_pcount(skb);
if (unlikely(oldpcount > 1)) {
+ if (skb_unclone(skb, GFP_ATOMIC))
+ return -ENOMEM;
tcp_init_tso_segs(sk, skb, cur_mss);
tcp_adjust_pcount(sk, skb, oldpcount - tcp_skb_pcount(skb));
}
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
index eb1dd4d643f2..b5663c37f089 100644
--- a/net/ipv4/xfrm4_mode_tunnel.c
+++ b/net/ipv4/xfrm4_mode_tunnel.c
@@ -117,7 +117,7 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF));
- ip_select_ident(top_iph, dst->child, NULL);
+ ip_select_ident(skb, dst->child, NULL);
top_iph->ttl = ip4_dst_hoplimit(dst->child);
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d3057f9b04a1..b78a3ee93d52 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1533,6 +1533,33 @@ static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr,
return false;
}
+/* Compares an address/prefix_len with addresses on device @dev.
+ * If one is found it returns true.
+ */
+bool ipv6_chk_custom_prefix(const struct in6_addr *addr,
+ const unsigned int prefix_len, struct net_device *dev)
+{
+ struct inet6_dev *idev;
+ struct inet6_ifaddr *ifa;
+ bool ret = false;
+
+ rcu_read_lock();
+ idev = __in6_dev_get(dev);
+ if (idev) {
+ read_lock_bh(&idev->lock);
+ list_for_each_entry(ifa, &idev->addr_list, if_list) {
+ ret = ipv6_prefix_equal(addr, &ifa->addr, prefix_len);
+ if (ret)
+ break;
+ }
+ read_unlock_bh(&idev->lock);
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL(ipv6_chk_custom_prefix);
+
int ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev)
{
struct inet6_dev *idev;
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 07a7d65a7cb6..8d67900aa003 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -162,12 +162,6 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb)
off += optlen;
len -= optlen;
}
- /* This case will not be caught by above check since its padding
- * length is smaller than 7:
- * 1 byte NH + 1 byte Length + 6 bytes Padding
- */
- if ((padlen == 6) && ((off - skb_network_header_len(skb)) == 8))
- goto bad;
if (len == 0)
return true;
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index 32b4a1675d82..066640e0ba8e 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -116,7 +116,7 @@ begintw:
}
if (unlikely(!INET6_TW_MATCH(sk, net, saddr, daddr,
ports, dif))) {
- sock_put(sk);
+ inet_twsk_put(inet_twsk(sk));
goto begintw;
}
goto out;
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 2221ff6a308b..9c06ecb6556e 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -825,9 +825,9 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst),
allow_create, replace_required);
-
if (IS_ERR(fn)) {
err = PTR_ERR(fn);
+ fn = NULL;
goto out;
}
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index ecd60733e5e2..1f9a1a5b61f4 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -620,7 +620,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
struct ip6_tnl *tunnel = netdev_priv(dev);
struct net_device *tdev; /* Device to other host */
struct ipv6hdr *ipv6h; /* Our new IP header */
- unsigned int max_headroom; /* The extra header space needed */
+ unsigned int max_headroom = 0; /* The extra header space needed */
int gre_hlen;
struct ipv6_tel_txoption opt;
int mtu;
@@ -693,7 +693,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
tunnel->err_count = 0;
}
- max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len;
+ max_headroom += LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len;
if (skb_headroom(skb) < max_headroom || skb_shared(skb) ||
(skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 6e3ddf806ec2..878f8027ebf6 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -130,7 +130,7 @@ static int ip6_finish_output2(struct sk_buff *skb)
}
rcu_read_lock_bh();
- nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr);
+ nexthop = rt6_nexthop((struct rt6_info *)dst);
neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
if (unlikely(!neigh))
neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
@@ -898,7 +898,7 @@ static int ip6_dst_lookup_tail(struct sock *sk,
*/
rt = (struct rt6_info *) *dst;
rcu_read_lock_bh();
- n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt, &fl6->daddr));
+ n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt));
err = n && !(n->nud_state & NUD_VALID) ? -EINVAL : 0;
rcu_read_unlock_bh();
@@ -1039,6 +1039,8 @@ static inline int ip6_ufo_append_data(struct sock *sk,
* udp datagram
*/
if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) {
+ struct frag_hdr fhdr;
+
skb = sock_alloc_send_skb(sk,
hh_len + fragheaderlen + transhdrlen + 20,
(flags & MSG_DONTWAIT), &err);
@@ -1059,12 +1061,6 @@ static inline int ip6_ufo_append_data(struct sock *sk,
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum = 0;
- }
-
- err = skb_append_datato_frags(sk,skb, getfrag, from,
- (length - transhdrlen));
- if (!err) {
- struct frag_hdr fhdr;
/* Specify the length of each IPv6 datagram fragment.
* It has to be a multiple of 8.
@@ -1075,15 +1071,10 @@ static inline int ip6_ufo_append_data(struct sock *sk,
ipv6_select_ident(&fhdr, rt);
skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
__skb_queue_tail(&sk->sk_write_queue, skb);
-
- return 0;
}
- /* There is not enough support do UPD LSO,
- * so follow normal path
- */
- kfree_skb(skb);
- return err;
+ return skb_append_datato_frags(sk, skb, getfrag, from,
+ (length - transhdrlen));
}
static inline struct ipv6_opt_hdr *ip6_opt_dup(struct ipv6_opt_hdr *src,
@@ -1250,27 +1241,27 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
* --yoshfuji
*/
- cork->length += length;
- if (length > mtu) {
- int proto = sk->sk_protocol;
- if (dontfrag && (proto == IPPROTO_UDP || proto == IPPROTO_RAW)){
- ipv6_local_rxpmtu(sk, fl6, mtu-exthdrlen);
- return -EMSGSIZE;
- }
-
- if (proto == IPPROTO_UDP &&
- (rt->dst.dev->features & NETIF_F_UFO)) {
+ if ((length > mtu) && dontfrag && (sk->sk_protocol == IPPROTO_UDP ||
+ sk->sk_protocol == IPPROTO_RAW)) {
+ ipv6_local_rxpmtu(sk, fl6, mtu-exthdrlen);
+ return -EMSGSIZE;
+ }
- err = ip6_ufo_append_data(sk, getfrag, from, length,
- hh_len, fragheaderlen,
- transhdrlen, mtu, flags, rt);
- if (err)
- goto error;
- return 0;
- }
+ skb = skb_peek_tail(&sk->sk_write_queue);
+ cork->length += length;
+ if (((length > mtu) ||
+ (skb && skb_has_frags(skb))) &&
+ (sk->sk_protocol == IPPROTO_UDP) &&
+ (rt->dst.dev->features & NETIF_F_UFO)) {
+ err = ip6_ufo_append_data(sk, getfrag, from, length,
+ hh_len, fragheaderlen,
+ transhdrlen, mtu, flags, rt);
+ if (err)
+ goto error;
+ return 0;
}
- if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
+ if (!skb)
goto alloc_new_skb;
while (length > 0) {
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 1e55866cead7..0516ebbea80b 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1646,9 +1646,9 @@ static int ip6_tnl_fill_info(struct sk_buff *skb, const struct net_device *dev)
if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
nla_put(skb, IFLA_IPTUN_LOCAL, sizeof(struct in6_addr),
- &parm->raddr) ||
- nla_put(skb, IFLA_IPTUN_REMOTE, sizeof(struct in6_addr),
&parm->laddr) ||
+ nla_put(skb, IFLA_IPTUN_REMOTE, sizeof(struct in6_addr),
+ &parm->raddr) ||
nla_put_u8(skb, IFLA_IPTUN_TTL, parm->hop_limit) ||
nla_put_u8(skb, IFLA_IPTUN_ENCAP_LIMIT, parm->encap_limit) ||
nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) ||
@@ -1732,6 +1732,7 @@ static int __net_init ip6_tnl_init_net(struct net *net)
if (!ip6n->fb_tnl_dev)
goto err_alloc_dev;
dev_net_set(ip6n->fb_tnl_dev, net);
+ ip6n->fb_tnl_dev->rtnl_link_ops = &ip6_link_ops;
err = ip6_fb_tnl_dev_init(ip6n->fb_tnl_dev);
if (err < 0)
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index c3998c2bbc5a..952eaed38808 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -2158,7 +2158,7 @@ static void mld_gq_timer_expire(unsigned long data)
idev->mc_gq_running = 0;
mld_send_report(idev, NULL);
- __in6_dev_put(idev);
+ in6_dev_put(idev);
}
static void mld_ifc_timer_expire(unsigned long data)
@@ -2171,7 +2171,7 @@ static void mld_ifc_timer_expire(unsigned long data)
if (idev->mc_ifc_count)
mld_ifc_start_timer(idev, idev->mc_maxdelay);
}
- __in6_dev_put(idev);
+ in6_dev_put(idev);
}
static void mld_ifc_event(struct inet6_dev *idev)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index bacce6c08644..548a1f7c1a29 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -473,6 +473,24 @@ out:
}
#ifdef CONFIG_IPV6_ROUTER_PREF
+struct __rt6_probe_work {
+ struct work_struct work;
+ struct in6_addr target;
+ struct net_device *dev;
+};
+
+static void rt6_probe_deferred(struct work_struct *w)
+{
+ struct in6_addr mcaddr;
+ struct __rt6_probe_work *work =
+ container_of(w, struct __rt6_probe_work, work);
+
+ addrconf_addr_solict_mult(&work->target, &mcaddr);
+ ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL);
+ dev_put(work->dev);
+ kfree(w);
+}
+
static void rt6_probe(struct rt6_info *rt)
{
struct neighbour *neigh;
@@ -496,17 +514,23 @@ static void rt6_probe(struct rt6_info *rt)
if (!neigh ||
time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
- struct in6_addr mcaddr;
- struct in6_addr *target;
+ struct __rt6_probe_work *work;
+
+ work = kmalloc(sizeof(*work), GFP_ATOMIC);
- if (neigh) {
+ if (neigh && work)
neigh->updated = jiffies;
+
+ if (neigh)
write_unlock(&neigh->lock);
- }
- target = (struct in6_addr *)&rt->rt6i_gateway;
- addrconf_addr_solict_mult(target, &mcaddr);
- ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
+ if (work) {
+ INIT_WORK(&work->work, rt6_probe_deferred);
+ work->target = rt->rt6i_gateway;
+ dev_hold(rt->dst.dev);
+ work->dev = rt->dst.dev;
+ schedule_work(&work->work);
+ }
} else {
out:
write_unlock(&neigh->lock);
@@ -848,7 +872,6 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
if (ort->rt6i_dst.plen != 128 &&
ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
rt->rt6i_flags |= RTF_ANYCAST;
- rt->rt6i_gateway = *daddr;
}
rt->rt6i_flags |= RTF_CACHE;
@@ -1061,10 +1084,13 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev)))
return NULL;
- if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
- return dst;
+ if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
+ return NULL;
- return NULL;
+ if (rt6_check_expired(rt))
+ return NULL;
+
+ return dst;
}
static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
@@ -1245,6 +1271,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
rt->dst.flags |= DST_HOST;
rt->dst.output = ip6_output;
atomic_set(&rt->dst.__refcnt, 1);
+ rt->rt6i_gateway = fl6->daddr;
rt->rt6i_dst.addr = fl6->daddr;
rt->rt6i_dst.plen = 128;
rt->rt6i_idev = idev;
@@ -1801,7 +1828,10 @@ static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
in6_dev_hold(rt->rt6i_idev);
rt->dst.lastuse = jiffies;
- rt->rt6i_gateway = ort->rt6i_gateway;
+ if (ort->rt6i_flags & RTF_GATEWAY)
+ rt->rt6i_gateway = ort->rt6i_gateway;
+ else
+ rt->rt6i_gateway = *dest;
rt->rt6i_flags = ort->rt6i_flags;
if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
(RTF_DEFAULT | RTF_ADDRCONF))
@@ -2088,6 +2118,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
else
rt->rt6i_flags |= RTF_LOCAL;
+ rt->rt6i_gateway = *addr;
rt->rt6i_dst.addr = *addr;
rt->rt6i_dst.plen = 128;
rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 60df36d15390..0491264b8bfc 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -566,6 +566,70 @@ static inline bool is_spoofed_6rd(struct ip_tunnel *tunnel, const __be32 v4addr,
return false;
}
+/* Checks if an address matches an address on the tunnel interface.
+ * Used to detect the NAT of proto 41 packets and let them pass spoofing test.
+ * Long story:
+ * This function is called after we considered the packet as spoofed
+ * in is_spoofed_6rd.
+ * We may have a router that is doing NAT for proto 41 packets
+ * for an internal station. Destination a.a.a.a/PREFIX:bbbb:bbbb
+ * will be translated to n.n.n.n/PREFIX:bbbb:bbbb. And is_spoofed_6rd
+ * function will return true, dropping the packet.
+ * But, we can still check if is spoofed against the IP
+ * addresses associated with the interface.
+ */
+static bool only_dnatted(const struct ip_tunnel *tunnel,
+ const struct in6_addr *v6dst)
+{
+ int prefix_len;
+
+#ifdef CONFIG_IPV6_SIT_6RD
+ prefix_len = tunnel->ip6rd.prefixlen + 32
+ - tunnel->ip6rd.relay_prefixlen;
+#else
+ prefix_len = 48;
+#endif
+ return ipv6_chk_custom_prefix(v6dst, prefix_len, tunnel->dev);
+}
+
+/* Returns true if a packet is spoofed */
+static bool packet_is_spoofed(struct sk_buff *skb,
+ const struct iphdr *iph,
+ struct ip_tunnel *tunnel)
+{
+ const struct ipv6hdr *ipv6h;
+
+ if (tunnel->dev->priv_flags & IFF_ISATAP) {
+ if (!isatap_chksrc(skb, iph, tunnel))
+ return true;
+
+ return false;
+ }
+
+ if (tunnel->dev->flags & IFF_POINTOPOINT)
+ return false;
+
+ ipv6h = ipv6_hdr(skb);
+
+ if (unlikely(is_spoofed_6rd(tunnel, iph->saddr, &ipv6h->saddr))) {
+ net_warn_ratelimited("Src spoofed %pI4/%pI6c -> %pI4/%pI6c\n",
+ &iph->saddr, &ipv6h->saddr,
+ &iph->daddr, &ipv6h->daddr);
+ return true;
+ }
+
+ if (likely(!is_spoofed_6rd(tunnel, iph->daddr, &ipv6h->daddr)))
+ return false;
+
+ if (only_dnatted(tunnel, &ipv6h->daddr))
+ return false;
+
+ net_warn_ratelimited("Dst spoofed %pI4/%pI6c -> %pI4/%pI6c\n",
+ &iph->saddr, &ipv6h->saddr,
+ &iph->daddr, &ipv6h->daddr);
+ return true;
+}
+
static int ipip6_rcv(struct sk_buff *skb)
{
const struct iphdr *iph = ip_hdr(skb);
@@ -584,19 +648,9 @@ static int ipip6_rcv(struct sk_buff *skb)
skb->protocol = htons(ETH_P_IPV6);
skb->pkt_type = PACKET_HOST;
- if (tunnel->dev->priv_flags & IFF_ISATAP) {
- if (!isatap_chksrc(skb, iph, tunnel)) {
- tunnel->dev->stats.rx_errors++;
- goto out;
- }
- } else if (!(tunnel->dev->flags&IFF_POINTOPOINT)) {
- if (is_spoofed_6rd(tunnel, iph->saddr,
- &ipv6_hdr(skb)->saddr) ||
- is_spoofed_6rd(tunnel, iph->daddr,
- &ipv6_hdr(skb)->daddr)) {
- tunnel->dev->stats.rx_errors++;
- goto out;
- }
+ if (packet_is_spoofed(skb, iph, tunnel)) {
+ tunnel->dev->stats.rx_errors++;
+ goto out;
}
__skb_tunnel_rx(skb, tunnel->dev);
@@ -713,7 +767,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr);
if (neigh == NULL) {
- net_dbg_ratelimited("sit: nexthop == NULL\n");
+ net_dbg_ratelimited("nexthop == NULL\n");
goto tx_error;
}
@@ -742,7 +796,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr);
if (neigh == NULL) {
- net_dbg_ratelimited("sit: nexthop == NULL\n");
+ net_dbg_ratelimited("nexthop == NULL\n");
goto tx_error;
}
@@ -865,7 +919,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
iph->ttl = iph6->hop_limit;
skb->ip_summed = CHECKSUM_NONE;
- ip_select_ident(iph, skb_dst(skb), NULL);
+ ip_select_ident(skb, skb_dst(skb), NULL);
iptunnel_xmit(skb, dev);
return NETDEV_TX_OK;
@@ -1507,6 +1561,7 @@ static int __net_init sit_init_net(struct net *net)
goto err_alloc_dev;
}
dev_net_set(sitn->fb_tunnel_dev, net);
+ sitn->fb_tunnel_dev->rtnl_link_ops = &sit_link_ops;
err = ipip6_fb_tunnel_init(sitn->fb_tunnel_dev);
if (err)
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 6984c3a353cd..8c27de2b4d5a 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -115,6 +115,11 @@ struct l2tp_net {
static void l2tp_session_set_header_len(struct l2tp_session *session, int version);
static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel);
+static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk)
+{
+ return sk->sk_user_data;
+}
+
static inline struct l2tp_net *l2tp_pernet(struct net *net)
{
BUG_ON(!net);
@@ -507,7 +512,7 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk,
return 0;
#if IS_ENABLED(CONFIG_IPV6)
- if (sk->sk_family == PF_INET6) {
+ if (sk->sk_family == PF_INET6 && !l2tp_tunnel(sk)->v4mapped) {
if (!uh->check) {
LIMIT_NETDEBUG(KERN_INFO "L2TP: IPv6: checksum is 0\n");
return 1;
@@ -1071,7 +1076,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
/* Queue the packet to IP for output */
skb->local_df = 1;
#if IS_ENABLED(CONFIG_IPV6)
- if (skb->sk->sk_family == PF_INET6)
+ if (skb->sk->sk_family == PF_INET6 && !tunnel->v4mapped)
error = inet6_csk_xmit(skb, NULL);
else
#endif
@@ -1198,7 +1203,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
/* Calculate UDP checksum if configured to do so */
#if IS_ENABLED(CONFIG_IPV6)
- if (sk->sk_family == PF_INET6)
+ if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
l2tp_xmit_ipv6_csum(sk, skb, udp_len);
else
#endif
@@ -1247,10 +1252,9 @@ EXPORT_SYMBOL_GPL(l2tp_xmit_skb);
*/
static void l2tp_tunnel_destruct(struct sock *sk)
{
- struct l2tp_tunnel *tunnel;
+ struct l2tp_tunnel *tunnel = l2tp_tunnel(sk);
struct l2tp_net *pn;
- tunnel = sk->sk_user_data;
if (tunnel == NULL)
goto end;
@@ -1618,7 +1622,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
}
/* Check if this socket has already been prepped */
- tunnel = (struct l2tp_tunnel *)sk->sk_user_data;
+ tunnel = l2tp_tunnel(sk);
if (tunnel != NULL) {
/* This socket has already been prepped */
err = -EBUSY;
@@ -1647,6 +1651,24 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
if (cfg != NULL)
tunnel->debug = cfg->debug;
+#if IS_ENABLED(CONFIG_IPV6)
+ if (sk->sk_family == PF_INET6) {
+ struct ipv6_pinfo *np = inet6_sk(sk);
+
+ if (ipv6_addr_v4mapped(&np->saddr) &&
+ ipv6_addr_v4mapped(&np->daddr)) {
+ struct inet_sock *inet = inet_sk(sk);
+
+ tunnel->v4mapped = true;
+ inet->inet_saddr = np->saddr.s6_addr32[3];
+ inet->inet_rcv_saddr = np->rcv_saddr.s6_addr32[3];
+ inet->inet_daddr = np->daddr.s6_addr32[3];
+ } else {
+ tunnel->v4mapped = false;
+ }
+ }
+#endif
+
/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
tunnel->encap = encap;
if (encap == L2TP_ENCAPTYPE_UDP) {
@@ -1655,7 +1677,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv;
udp_sk(sk)->encap_destroy = l2tp_udp_encap_destroy;
#if IS_ENABLED(CONFIG_IPV6)
- if (sk->sk_family == PF_INET6)
+ if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
udpv6_encap_enable();
else
#endif
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index 485a490fd990..2f89d43877d7 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -189,6 +189,9 @@ struct l2tp_tunnel {
struct sock *sock; /* Parent socket */
int fd; /* Parent fd, if tunnel socket
* was created by userspace */
+#if IS_ENABLED(CONFIG_IPV6)
+ bool v4mapped;
+#endif
struct work_struct del_work;
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 5ebee2ded9e9..8c46b271064a 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -353,7 +353,9 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
goto error_put_sess_tun;
}
+ local_bh_disable();
l2tp_xmit_skb(session, skb, session->hdr_len);
+ local_bh_enable();
sock_put(ps->tunnel_sock);
sock_put(sk);
@@ -422,7 +424,9 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
skb->data[0] = ppph[0];
skb->data[1] = ppph[1];
+ local_bh_disable();
l2tp_xmit_skb(session, skb, session->hdr_len);
+ local_bh_enable();
sock_put(sk_tun);
sock_put(sk);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index ae36f8e11ae4..5ab17b82605d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -3315,7 +3315,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
return -EINVAL;
}
band = chanctx_conf->def.chan->band;
- sta = sta_info_get(sdata, peer);
+ sta = sta_info_get_bss(sdata, peer);
if (sta) {
qos = test_sta_flag(sta, WLAN_STA_WME);
} else {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 9ca8e3278cc0..92ef04c72c51 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -842,6 +842,8 @@ struct tpt_led_trigger {
* that the scan completed.
* @SCAN_ABORTED: Set for our scan work function when the driver reported
* a scan complete for an aborted scan.
+ * @SCAN_HW_CANCELLED: Set for our scan work function when the scan is being
+ * cancelled.
*/
enum {
SCAN_SW_SCANNING,
@@ -849,6 +851,7 @@ enum {
SCAN_ONCHANNEL_SCANNING,
SCAN_COMPLETED,
SCAN_ABORTED,
+ SCAN_HW_CANCELLED,
};
/**
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 83f6d29202aa..ec09bcba9bae 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3002,6 +3002,9 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
case NL80211_IFTYPE_ADHOC:
if (!bssid)
return 0;
+ if (ether_addr_equal(sdata->vif.addr, hdr->addr2) ||
+ ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2))
+ return 0;
if (ieee80211_is_beacon(hdr->frame_control)) {
return 1;
} else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 99b103921a4b..eb03337b6545 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -202,6 +202,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
enum ieee80211_band band;
int i, ielen, n_chans;
+ if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
+ return false;
+
do {
if (local->hw_scan_band == IEEE80211_NUM_BANDS)
return false;
@@ -878,7 +881,23 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
if (!local->scan_req)
goto out;
+ /*
+ * We have a scan running and the driver already reported completion,
+ * but the worker hasn't run yet or is stuck on the mutex - mark it as
+ * cancelled.
+ */
+ if (test_bit(SCAN_HW_SCANNING, &local->scanning) &&
+ test_bit(SCAN_COMPLETED, &local->scanning)) {
+ set_bit(SCAN_HW_CANCELLED, &local->scanning);
+ goto out;
+ }
+
if (test_bit(SCAN_HW_SCANNING, &local->scanning)) {
+ /*
+ * Make sure that __ieee80211_scan_completed doesn't trigger a
+ * scan on another band.
+ */
+ set_bit(SCAN_HW_CANCELLED, &local->scanning);
if (local->ops->cancel_hw_scan)
drv_cancel_hw_scan(local,
rcu_dereference_protected(local->scan_sdata,
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 43439203f4e4..9e78206bd9bb 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -180,6 +180,9 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+ sta->last_rx = jiffies;
+
if (ieee80211_is_data_qos(mgmt->frame_control)) {
struct ieee80211_hdr *hdr = (void *) skb->data;
u8 *qc = ieee80211_get_qos_ctl(hdr);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 9972e07a2f96..e9d18c30071f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1100,7 +1100,8 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
tx->sta = rcu_dereference(sdata->u.vlan.sta);
if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)
return TX_DROP;
- } else if (info->flags & IEEE80211_TX_CTL_INJECTED ||
+ } else if (info->flags & (IEEE80211_TX_CTL_INJECTED |
+ IEEE80211_TX_INTFL_NL80211_FRAME_TX) ||
tx->sdata->control_port_protocol == tx->skb->protocol) {
tx->sta = sta_info_get_bss(sdata, hdr->addr1);
}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 72e6292955bb..5db8eb5d56cf 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -2174,6 +2174,10 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
}
rate = cfg80211_calculate_bitrate(&ri);
+ if (WARN_ONCE(!rate,
+ "Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n",
+ status->flag, status->rate_idx, status->vht_nss))
+ return 0;
/* rewind from end of MPDU */
if (status->flag & RX_FLAG_MACTIME_END)
diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h
index 57beb1762b2d..707bc520d629 100644
--- a/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/net/netfilter/ipset/ip_set_hash_gen.h
@@ -325,18 +325,22 @@ mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length)
static void
mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length)
{
- u8 i, j;
-
- for (i = 0; i < nets_length - 1 && h->nets[i].cidr != cidr; i++)
- ;
- h->nets[i].nets--;
-
- if (h->nets[i].nets != 0)
- return;
-
- for (j = i; j < nets_length - 1 && h->nets[j].nets; j++) {
- h->nets[j].cidr = h->nets[j + 1].cidr;
- h->nets[j].nets = h->nets[j + 1].nets;
+ u8 i, j, net_end = nets_length - 1;
+
+ for (i = 0; i < nets_length; i++) {
+ if (h->nets[i].cidr != cidr)
+ continue;
+ if (h->nets[i].nets > 1 || i == net_end ||
+ h->nets[i + 1].nets == 0) {
+ h->nets[i].nets--;
+ return;
+ }
+ for (j = i; j < net_end && h->nets[j].nets; j++) {
+ h->nets[j].cidr = h->nets[j + 1].cidr;
+ h->nets[j].nets = h->nets[j + 1].nets;
+ }
+ h->nets[j].nets = 0;
+ return;
}
}
#endif
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index b75ff6429a04..c47444e4cf8c 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -883,7 +883,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
iph->daddr = cp->daddr.ip;
iph->saddr = saddr;
iph->ttl = old_iph->ttl;
- ip_select_ident(iph, &rt->dst, NULL);
+ ip_select_ident(skb, &rt->dst, NULL);
/* Another hack: avoid icmp_send in ip_fragment */
skb->local_df = 1;
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
index bdebd03bc8cd..70866d192efc 100644
--- a/net/netfilter/nf_conntrack_h323_main.c
+++ b/net/netfilter/nf_conntrack_h323_main.c
@@ -778,8 +778,8 @@ static int callforward_do_filter(const union nf_inet_addr *src,
flowi6_to_flowi(&fl1), false)) {
if (!afinfo->route(&init_net, (struct dst_entry **)&rt2,
flowi6_to_flowi(&fl2), false)) {
- if (!memcmp(&rt1->rt6i_gateway, &rt2->rt6i_gateway,
- sizeof(rt1->rt6i_gateway)) &&
+ if (ipv6_addr_equal(rt6_nexthop(rt1),
+ rt6_nexthop(rt2)) &&
rt1->dst.dev == rt2->dst.dev)
ret = 1;
dst_release(&rt2->dst);
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index 910667cbc1e3..e09b074bb8a7 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -1476,7 +1476,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
psched_ratecfg_precompute(&cl->ceil, &hopt->ceil);
cl->buffer = PSCHED_TICKS2NS(hopt->buffer);
- cl->cbuffer = PSCHED_TICKS2NS(hopt->buffer);
+ cl->cbuffer = PSCHED_TICKS2NS(hopt->cbuffer);
sch_tree_unlock(sch);
diff --git a/net/sctp/input.c b/net/sctp/input.c
index 4b2c83146aa7..bd4fb459c63b 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -648,8 +648,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
break;
case ICMP_REDIRECT:
sctp_icmp_redirect(sk, transport, skb);
- err = 0;
- break;
+ /* Fall through to out_unlock. */
default:
goto out_unlock;
}
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 391a245d5203..422d8bdacc0d 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -189,7 +189,7 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
break;
case NDISC_REDIRECT:
sctp_icmp_redirect(sk, transport, skb);
- break;
+ goto out_unlock;
default:
break;
}
@@ -210,45 +210,24 @@ out:
in6_dev_put(idev);
}
-/* Based on tcp_v6_xmit() in tcp_ipv6.c. */
static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
{
struct sock *sk = skb->sk;
struct ipv6_pinfo *np = inet6_sk(sk);
- struct flowi6 fl6;
-
- memset(&fl6, 0, sizeof(fl6));
-
- fl6.flowi6_proto = sk->sk_protocol;
-
- /* Fill in the dest address from the route entry passed with the skb
- * and the source address from the transport.
- */
- fl6.daddr = transport->ipaddr.v6.sin6_addr;
- fl6.saddr = transport->saddr.v6.sin6_addr;
-
- fl6.flowlabel = np->flow_label;
- IP6_ECN_flow_xmit(sk, fl6.flowlabel);
- if (ipv6_addr_type(&fl6.saddr) & IPV6_ADDR_LINKLOCAL)
- fl6.flowi6_oif = transport->saddr.v6.sin6_scope_id;
- else
- fl6.flowi6_oif = sk->sk_bound_dev_if;
-
- if (np->opt && np->opt->srcrt) {
- struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
- fl6.daddr = *rt0->addr;
- }
+ struct flowi6 *fl6 = &transport->fl.u.ip6;
SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n",
__func__, skb, skb->len,
- &fl6.saddr, &fl6.daddr);
+ &fl6->saddr, &fl6->daddr);
- SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS);
+ IP6_ECN_flow_xmit(sk, fl6->flowlabel);
if (!(transport->param_flags & SPP_PMTUD_ENABLE))
skb->local_df = 1;
- return ip6_xmit(sk, skb, &fl6, np->opt, np->tclass);
+ SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS);
+
+ return ip6_xmit(sk, skb, fl6, np->opt, np->tclass);
}
/* Returns the dst cache entry for the given source and destination ip
@@ -261,10 +240,12 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
struct dst_entry *dst = NULL;
struct flowi6 *fl6 = &fl->u.ip6;
struct sctp_bind_addr *bp;
+ struct ipv6_pinfo *np = inet6_sk(sk);
struct sctp_sockaddr_entry *laddr;
union sctp_addr *baddr = NULL;
union sctp_addr *daddr = &t->ipaddr;
union sctp_addr dst_saddr;
+ struct in6_addr *final_p, final;
__u8 matchlen = 0;
__u8 bmatchlen;
sctp_scope_t scope;
@@ -287,7 +268,8 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr);
}
- dst = ip6_dst_lookup_flow(sk, fl6, NULL, false);
+ final_p = fl6_update_dst(fl6, np->opt, &final);
+ dst = ip6_dst_lookup_flow(sk, fl6, final_p, false);
if (!asoc || saddr)
goto out;
@@ -339,10 +321,12 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
}
}
rcu_read_unlock();
+
if (baddr) {
fl6->saddr = baddr->v6.sin6_addr;
fl6->fl6_sport = baddr->v6.sin6_port;
- dst = ip6_dst_lookup_flow(sk, fl6, NULL, false);
+ final_p = fl6_update_dst(fl6, np->opt, &final);
+ dst = ip6_dst_lookup_flow(sk, fl6, final_p, false);
}
out:
diff --git a/net/sctp/output.c b/net/sctp/output.c
index bbef4a7a9b56..0beb2f9c8a7c 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -547,7 +547,8 @@ int sctp_packet_transmit(struct sctp_packet *packet)
* by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
*/
if (!sctp_checksum_disable) {
- if (!(dst->dev->features & NETIF_F_SCTP_CSUM)) {
+ if (!(dst->dev->features & NETIF_F_SCTP_CSUM) ||
+ (dst_xfrm(dst) != NULL) || packet->ipfragok) {
__u32 crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len);
/* 3) Put the resultant value into the checksum field in the
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 6abb1caf9836..79bc251042ba 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -820,6 +820,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
goto skip_mkasconf;
}
+ if (laddr == NULL)
+ return -EINVAL;
+
/* We do not need RCU protection throughout this loop
* because this is done under a socket lock from the
* setsockopt call.
@@ -6193,7 +6196,7 @@ unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
/* Is there any exceptional events? */
if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
mask |= POLLERR |
- sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0;
+ (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
if (sk->sk_shutdown & RCV_SHUTDOWN)
mask |= POLLRDHUP | POLLIN | POLLRDNORM;
if (sk->sk_shutdown == SHUTDOWN_MASK)
diff --git a/net/socket.c b/net/socket.c
index 4ca1526db756..9c467b2afc84 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1956,6 +1956,16 @@ struct used_address {
unsigned int name_len;
};
+static int copy_msghdr_from_user(struct msghdr *kmsg,
+ struct msghdr __user *umsg)
+{
+ if (copy_from_user(kmsg, umsg, sizeof(struct msghdr)))
+ return -EFAULT;
+ if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
+ return -EINVAL;
+ return 0;
+}
+
static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
struct msghdr *msg_sys, unsigned int flags,
struct used_address *used_address)
@@ -1974,8 +1984,11 @@ static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
if (MSG_CMSG_COMPAT & flags) {
if (get_compat_msghdr(msg_sys, msg_compat))
return -EFAULT;
- } else if (copy_from_user(msg_sys, msg, sizeof(struct msghdr)))
- return -EFAULT;
+ } else {
+ err = copy_msghdr_from_user(msg_sys, msg);
+ if (err)
+ return err;
+ }
if (msg_sys->msg_iovlen > UIO_FASTIOV) {
err = -EMSGSIZE;
@@ -2183,8 +2196,11 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
if (MSG_CMSG_COMPAT & flags) {
if (get_compat_msghdr(msg_sys, msg_compat))
return -EFAULT;
- } else if (copy_from_user(msg_sys, msg, sizeof(struct msghdr)))
- return -EFAULT;
+ } else {
+ err = copy_msghdr_from_user(msg_sys, msg);
+ if (err)
+ return err;
+ }
if (msg_sys->msg_iovlen > UIO_FASTIOV) {
err = -EMSGSIZE;
diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c
index af7ffd447fee..f1eb0d16666c 100644
--- a/net/sunrpc/auth_gss/gss_rpc_upcall.c
+++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c
@@ -213,6 +213,26 @@ static int gssp_call(struct net *net, struct rpc_message *msg)
return status;
}
+static void gssp_free_receive_pages(struct gssx_arg_accept_sec_context *arg)
+{
+ int i;
+
+ for (i = 0; i < arg->npages && arg->pages[i]; i++)
+ __free_page(arg->pages[i]);
+}
+
+static int gssp_alloc_receive_pages(struct gssx_arg_accept_sec_context *arg)
+{
+ arg->npages = DIV_ROUND_UP(NGROUPS_MAX * 4, PAGE_SIZE);
+ arg->pages = kzalloc(arg->npages * sizeof(struct page *), GFP_KERNEL);
+ /*
+ * XXX: actual pages are allocated by xdr layer in
+ * xdr_partial_copy_from_skb.
+ */
+ if (!arg->pages)
+ return -ENOMEM;
+ return 0;
+}
/*
* Public functions
@@ -261,10 +281,16 @@ int gssp_accept_sec_context_upcall(struct net *net,
arg.context_handle = &ctxh;
res.output_token->len = GSSX_max_output_token_sz;
+ ret = gssp_alloc_receive_pages(&arg);
+ if (ret)
+ return ret;
+
/* use nfs/ for targ_name ? */
ret = gssp_call(net, &msg);
+ gssp_free_receive_pages(&arg);
+
/* we need to fetch all data even in case of error so
* that we can free special strctures is they have been allocated */
data->major_status = res.status.major_status;
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c
index 3c85d1c8a028..f0f78c5f1c7d 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c
@@ -166,14 +166,15 @@ static int dummy_dec_opt_array(struct xdr_stream *xdr,
return 0;
}
-static int get_s32(void **p, void *max, s32 *res)
+static int get_host_u32(struct xdr_stream *xdr, u32 *res)
{
- void *base = *p;
- void *next = (void *)((char *)base + sizeof(s32));
- if (unlikely(next > max || next < base))
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (!p)
return -EINVAL;
- memcpy(res, base, sizeof(s32));
- *p = next;
+ /* Contents of linux creds are all host-endian: */
+ memcpy(res, p, sizeof(u32));
return 0;
}
@@ -182,9 +183,9 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr,
{
u32 length;
__be32 *p;
- void *q, *end;
- s32 tmp;
- int N, i, err;
+ u32 tmp;
+ u32 N;
+ int i, err;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
@@ -192,33 +193,28 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr,
length = be32_to_cpup(p);
- /* FIXME: we do not want to use the scratch buffer for this one
- * may need to use functions that allows us to access an io vector
- * directly */
- p = xdr_inline_decode(xdr, length);
- if (unlikely(p == NULL))
+ if (length > (3 + NGROUPS_MAX) * sizeof(u32))
return -ENOSPC;
- q = p;
- end = q + length;
-
/* uid */
- err = get_s32(&q, end, &tmp);
+ err = get_host_u32(xdr, &tmp);
if (err)
return err;
creds->cr_uid = make_kuid(&init_user_ns, tmp);
/* gid */
- err = get_s32(&q, end, &tmp);
+ err = get_host_u32(xdr, &tmp);
if (err)
return err;
creds->cr_gid = make_kgid(&init_user_ns, tmp);
/* number of additional gid's */
- err = get_s32(&q, end, &tmp);
+ err = get_host_u32(xdr, &tmp);
if (err)
return err;
N = tmp;
+ if ((3 + N) * sizeof(u32) != length)
+ return -EINVAL;
creds->cr_group_info = groups_alloc(N);
if (creds->cr_group_info == NULL)
return -ENOMEM;
@@ -226,7 +222,7 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr,
/* gid's */
for (i = 0; i < N; i++) {
kgid_t kgid;
- err = get_s32(&q, end, &tmp);
+ err = get_host_u32(xdr, &tmp);
if (err)
goto out_free_groups;
err = -EINVAL;
@@ -784,6 +780,9 @@ void gssx_enc_accept_sec_context(struct rpc_rqst *req,
/* arg->options */
err = dummy_enc_opt_array(xdr, &arg->options);
+ xdr_inline_pages(&req->rq_rcv_buf,
+ PAGE_SIZE/2 /* pretty arbitrary */,
+ arg->pages, 0 /* page base */, arg->npages * PAGE_SIZE);
done:
if (err)
dprintk("RPC: gssx_enc_accept_sec_context: %d\n", err);
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.h b/net/sunrpc/auth_gss/gss_rpc_xdr.h
index 1c98b27d870c..685a688f3d8a 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.h
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.h
@@ -147,6 +147,8 @@ struct gssx_arg_accept_sec_context {
struct gssx_cb *input_cb;
u32 ret_deleg_cred;
struct gssx_option_array options;
+ struct page **pages;
+ unsigned int npages;
};
struct gssx_res_accept_sec_context {
@@ -240,7 +242,8 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
2 * GSSX_max_princ_sz + \
8 + 8 + 4 + 4 + 4)
#define GSSX_max_output_token_sz 1024
-#define GSSX_max_creds_sz (4 + 4 + 4 + NGROUPS_MAX * 4)
+/* grouplist not included; we allocate separate pages for that: */
+#define GSSX_max_creds_sz (4 + 4 + 4 /* + NGROUPS_MAX*4 */)
#define GSSX_RES_accept_sec_context_sz (GSSX_default_status_sz + \
GSSX_default_ctx_sz + \
GSSX_max_output_token_sz + \
diff --git a/net/sysctl_net.c b/net/sysctl_net.c
index 9bc6db04be3e..e7000be321b0 100644
--- a/net/sysctl_net.c
+++ b/net/sysctl_net.c
@@ -47,12 +47,12 @@ static int net_ctl_permissions(struct ctl_table_header *head,
/* Allow network administrator to have same access as root. */
if (ns_capable(net->user_ns, CAP_NET_ADMIN) ||
- uid_eq(root_uid, current_uid())) {
+ uid_eq(root_uid, current_euid())) {
int mode = (table->mode >> 6) & 7;
return (mode << 6) | (mode << 3) | mode;
}
/* Allow netns root group to have the same access as the root group */
- if (gid_eq(root_gid, current_gid())) {
+ if (in_egroup_p(root_gid)) {
int mode = (table->mode >> 3) & 7;
return (mode << 3) | mode;
}
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index c4ce243824bb..e64bbcf5fb2c 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1246,6 +1246,15 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb)
return 0;
}
+static void unix_sock_inherit_flags(const struct socket *old,
+ struct socket *new)
+{
+ if (test_bit(SOCK_PASSCRED, &old->flags))
+ set_bit(SOCK_PASSCRED, &new->flags);
+ if (test_bit(SOCK_PASSSEC, &old->flags))
+ set_bit(SOCK_PASSSEC, &new->flags);
+}
+
static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
{
struct sock *sk = sock->sk;
@@ -1280,6 +1289,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
/* attach accepted sock to socket */
unix_state_lock(tsk);
newsock->state = SS_CONNECTED;
+ unix_sock_inherit_flags(sock, newsock);
sock_graft(tsk, newsock);
unix_state_unlock(tsk);
return 0;
diff --git a/net/unix/diag.c b/net/unix/diag.c
index d591091603bf..86fa0f3b2caf 100644
--- a/net/unix/diag.c
+++ b/net/unix/diag.c
@@ -124,6 +124,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r
rep->udiag_family = AF_UNIX;
rep->udiag_type = sk->sk_type;
rep->udiag_state = sk->sk_state;
+ rep->pad = 0;
rep->udiag_ino = sk_ino;
sock_diag_save_cookie(sk, rep->udiag_cookie);
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index d80e47194d49..e62c1ad4e4c9 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -269,6 +269,8 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
if (chan->flags & IEEE80211_CHAN_DISABLED)
continue;
wdev->wext.ibss.chandef.chan = chan;
+ wdev->wext.ibss.chandef.center_freq1 =
+ chan->center_freq;
break;
}
@@ -353,6 +355,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
if (chan) {
wdev->wext.ibss.chandef.chan = chan;
wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+ wdev->wext.ibss.chandef.center_freq1 = freq;
wdev->wext.ibss.channel_fixed = true;
} else {
/* cfg80211_ibss_wext_join will pick one if needed */
diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c
index 7d604c06c3dc..a271c27fac77 100644
--- a/net/wireless/radiotap.c
+++ b/net/wireless/radiotap.c
@@ -97,6 +97,10 @@ int ieee80211_radiotap_iterator_init(
struct ieee80211_radiotap_header *radiotap_header,
int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
{
+ /* check the radiotap header can actually be present */
+ if (max_length < sizeof(struct ieee80211_radiotap_header))
+ return -EINVAL;
+
/* Linux only supports version 0 radiotap format */
if (radiotap_header->it_version)
return -EINVAL;
@@ -131,7 +135,8 @@ int ieee80211_radiotap_iterator_init(
*/
if ((unsigned long)iterator->_arg -
- (unsigned long)iterator->_rtheader >
+ (unsigned long)iterator->_rtheader +
+ sizeof(uint32_t) >
(unsigned long)iterator->_max_length)
return -EINVAL;
}
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 487ac6f37ca2..9a11f9f799f4 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -55,6 +55,7 @@ static struct sym_entry *table;
static unsigned int table_size, table_cnt;
static int all_symbols = 0;
static char symbol_prefix_char = '\0';
+static unsigned long long kernel_start_addr = 0;
int token_profit[0x10000];
@@ -65,7 +66,10 @@ unsigned char best_table_len[256];
static void usage(void)
{
- fprintf(stderr, "Usage: kallsyms [--all-symbols] [--symbol-prefix=<prefix char>] < in.map > out.S\n");
+ fprintf(stderr, "Usage: kallsyms [--all-symbols] "
+ "[--symbol-prefix=<prefix char>] "
+ "[--page-offset=<CONFIG_PAGE_OFFSET>] "
+ "< in.map > out.S\n");
exit(1);
}
@@ -194,6 +198,9 @@ static int symbol_valid(struct sym_entry *s)
int i;
int offset = 1;
+ if (s->addr < kernel_start_addr)
+ return 0;
+
/* skip prefix char */
if (symbol_prefix_char && *(s->sym + 1) == symbol_prefix_char)
offset++;
@@ -646,6 +653,9 @@ int main(int argc, char **argv)
if ((*p == '"' && *(p+2) == '"') || (*p == '\'' && *(p+2) == '\''))
p++;
symbol_prefix_char = *p;
+ } else if (strncmp(argv[i], "--page-offset=", 14) == 0) {
+ const char *p = &argv[i][14];
+ kernel_start_addr = strtoull(p, NULL, 16);
} else
usage();
}
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 014994936b1c..32b10f53d0b4 100644
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -82,6 +82,8 @@ kallsyms()
kallsymopt="${kallsymopt} --all-symbols"
fi
+ kallsymopt="${kallsymopt} --page-offset=$CONFIG_PAGE_OFFSET"
+
local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \
${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}"
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index 98969541cbcc..5863ba6dd12b 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -837,7 +837,8 @@ static int snd_compress_dev_disconnect(struct snd_device *device)
struct snd_compr *compr;
compr = device->device_data;
- snd_unregister_device(compr->direction, compr->card, compr->device);
+ snd_unregister_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card,
+ compr->device);
return 0;
}
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 17f45e8aa89c..e1e9e0c999fe 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -49,6 +49,8 @@ static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)
struct snd_pcm *pcm;
list_for_each_entry(pcm, &snd_pcm_devices, list) {
+ if (pcm->internal)
+ continue;
if (pcm->card == card && pcm->device == device)
return pcm;
}
@@ -60,6 +62,8 @@ static int snd_pcm_next(struct snd_card *card, int device)
struct snd_pcm *pcm;
list_for_each_entry(pcm, &snd_pcm_devices, list) {
+ if (pcm->internal)
+ continue;
if (pcm->card == card && pcm->device > device)
return pcm->device;
else if (pcm->card->number > card->number)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 55108b5fb291..31461ba32d3c 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -4789,8 +4789,8 @@ static void hda_power_work(struct work_struct *work)
spin_unlock(&codec->power_lock);
state = hda_call_codec_suspend(codec, true);
- codec->pm_down_notified = 0;
- if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) {
+ if (!codec->pm_down_notified &&
+ !bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) {
codec->pm_down_notified = 1;
hda_call_pm_notify(bus, false);
}
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index ad22decad02b..d0cc796f778a 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3474,7 +3474,7 @@ static int create_capture_mixers(struct hda_codec *codec)
if (!multi)
err = create_single_cap_vol_ctl(codec, n, vol, sw,
inv_dmic);
- else if (!multi_cap_vol)
+ else if (!multi_cap_vol && !inv_dmic)
err = create_bind_cap_vol_ctl(codec, n, vol, sw);
else
err = create_multi_cap_vol_ctl(codec);
@@ -4383,9 +4383,11 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
true, &spec->vmaster_mute.sw_kctl);
if (err < 0)
return err;
- if (spec->vmaster_mute.hook)
+ if (spec->vmaster_mute.hook) {
snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute,
spec->vmaster_mute_enum);
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+ }
}
free_kctls(spec); /* no longer needed */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index de18722c4873..624e6c044a44 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -3335,6 +3335,7 @@ static struct snd_pci_quirk msi_black_list[] = {
SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */
SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */
SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */
+ SND_PCI_QUIRK(0x1179, 0xfb44, "Toshiba Satellite C870", 0), /* AMD Hudson */
SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */
SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */
{}
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index b314d3e6d7fa..c96e1945059d 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -3225,6 +3225,7 @@ enum {
CXT_PINCFG_LEMOTE_A1205,
CXT_FIXUP_STEREO_DMIC,
CXT_FIXUP_INC_MIC_BOOST,
+ CXT_FIXUP_GPIO1,
};
static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
@@ -3303,6 +3304,15 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt5066_increase_mic_boost,
},
+ [CXT_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 },
+ { 0x01, AC_VERB_SET_GPIO_DATA, 0x01 },
+ { }
+ },
+ },
};
static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -3312,6 +3322,7 @@ static const struct snd_pci_quirk cxt5051_fixups[] = {
static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_GPIO1),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 5bc419452198..b93799201578 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -67,6 +67,8 @@ struct hdmi_spec_per_pin {
struct delayed_work work;
struct snd_kcontrol *eld_ctl;
int repoll_count;
+ bool setup; /* the stream has been set up by prepare callback */
+ int channels; /* current number of channels */
bool non_pcm;
bool chmap_set; /* channel-map override by ALSA API? */
unsigned char chmap[8]; /* ALSA API channel-map */
@@ -551,6 +553,17 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
}
}
+ if (!ca) {
+ /* if there was no match, select the regular ALSA channel
+ * allocation with the matching number of channels */
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channels == channel_allocations[i].channels) {
+ ca = channel_allocations[i].ca_index;
+ break;
+ }
+ }
+ }
+
snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf));
snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
ca, channels, buf);
@@ -725,9 +738,10 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
{
int i;
+ int ordered_ca = get_channel_allocation_order(ca);
for (i = 0; i < 8; i++) {
- if (i < channel_allocations[ca].channels)
- map[i] = from_cea_slot((hdmi_channel_mapping[ca][i] >> 4) & 0x0f);
+ if (i < channel_allocations[ordered_ca].channels)
+ map[i] = from_cea_slot(hdmi_channel_mapping[ca][i] & 0x0f);
else
map[i] = 0;
}
@@ -868,18 +882,19 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
return true;
}
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
- bool non_pcm,
- struct snd_pcm_substream *substream)
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool non_pcm)
{
- struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
- int channels = substream->runtime->channels;
+ int channels = per_pin->channels;
struct hdmi_eld *eld;
int ca;
union audio_infoframe ai;
+ if (!channels)
+ return;
+
eld = &per_pin->sink_eld;
if (!eld->monitor_present)
return;
@@ -916,6 +931,14 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
}
/*
+ * always configure channel mapping, it may have been changed by the
+ * user in the meantime
+ */
+ hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
+ channels, per_pin->chmap,
+ per_pin->chmap_set);
+
+ /*
* sizeof(ai) is used instead of sizeof(*hdmi_ai) or
* sizeof(*dp_ai) to avoid partial match/update problems when
* the user switches between HDMI/DP monitors.
@@ -926,20 +949,10 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
"pin=%d channels=%d\n",
pin_nid,
channels);
- hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
- channels, per_pin->chmap,
- per_pin->chmap_set);
hdmi_stop_infoframe_trans(codec, pin_nid);
hdmi_fill_audio_infoframe(codec, pin_nid,
ai.bytes, sizeof(ai));
hdmi_start_infoframe_trans(codec, pin_nid);
- } else {
- /* For non-pcm audio switch, setup new channel mapping
- * accordingly */
- if (per_pin->non_pcm != non_pcm)
- hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
- channels, per_pin->chmap,
- per_pin->chmap_set);
}
per_pin->non_pcm = non_pcm;
@@ -1263,6 +1276,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
eld_changed = true;
}
if (update_eld) {
+ bool old_eld_valid = pin_eld->eld_valid;
pin_eld->eld_valid = eld->eld_valid;
eld_changed = pin_eld->eld_size != eld->eld_size ||
memcmp(pin_eld->eld_buffer, eld->eld_buffer,
@@ -1272,6 +1286,18 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
eld->eld_size);
pin_eld->eld_size = eld->eld_size;
pin_eld->info = eld->info;
+
+ /* Haswell-specific workaround: re-setup when the transcoder is
+ * changed during the stream playback
+ */
+ if (codec->vendor_id == 0x80862807 &&
+ eld->eld_valid && !old_eld_valid && per_pin->setup) {
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ hdmi_setup_audio_infoframe(codec, per_pin,
+ per_pin->non_pcm);
+ }
}
mutex_unlock(&pin_eld->lock);
@@ -1444,14 +1470,17 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
hda_nid_t cvt_nid = hinfo->nid;
struct hdmi_spec *spec = codec->spec;
int pin_idx = hinfo_to_pin_index(spec, hinfo);
- hda_nid_t pin_nid = get_pin(spec, pin_idx)->pin_nid;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hda_nid_t pin_nid = per_pin->pin_nid;
bool non_pcm;
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
+ per_pin->channels = substream->runtime->channels;
+ per_pin->setup = true;
hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
- hdmi_setup_audio_infoframe(codec, pin_idx, non_pcm, substream);
+ hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
}
@@ -1491,6 +1520,9 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
snd_hda_spdif_ctls_unassign(codec, pin_idx);
per_pin->chmap_set = false;
memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
+
+ per_pin->setup = false;
+ per_pin->channels = 0;
}
return 0;
@@ -1626,8 +1658,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
per_pin->chmap_set = true;
memcpy(per_pin->chmap, chmap, sizeof(chmap));
if (prepared)
- hdmi_setup_audio_infoframe(codec, pin_idx, per_pin->non_pcm,
- substream);
+ hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
return 0;
}
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 458cf89b1643..4496e0ab693d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3200,6 +3200,15 @@ static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec,
}
}
+static void alc290_fixup_mono_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ /* Remove DAC node 0x03, as it seems to be
+ giving mono output */
+ snd_hda_override_wcaps(codec, 0x03, 0);
+}
+
enum {
ALC269_FIXUP_SONY_VAIO,
ALC275_FIXUP_SONY_VAIO_GPIO2,
@@ -3223,9 +3232,12 @@ enum {
ALC269_FIXUP_HP_GPIO_LED,
ALC269_FIXUP_INV_DMIC,
ALC269_FIXUP_LENOVO_DOCK,
+ ALC286_FIXUP_SONY_MIC_NO_PRESENCE,
ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT,
ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+ ALC290_FIXUP_MONO_SPEAKERS,
ALC269_FIXUP_HEADSET_MODE,
ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC,
ALC269_FIXUP_ASUS_X101_FUNC,
@@ -3412,6 +3424,15 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
},
+ [ALC269_FIXUP_DELL3_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
[ALC269_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode,
@@ -3420,6 +3441,13 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode_no_hp_mic,
},
+ [ALC286_FIXUP_SONY_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ },
[ALC269_FIXUP_ASUS_X101_FUNC] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_x101_headset_mic,
@@ -3477,6 +3505,12 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_limit_int_mic_boost,
},
+ [ALC290_FIXUP_MONO_SPEAKERS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc290_fixup_mono_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -3511,6 +3545,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_MONO_SPEAKERS),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -3529,6 +3564,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101),
+ SND_PCI_QUIRK(0x104d, 0x90b6, "Sony VAIO Pro 13", ALC286_FIXUP_SONY_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2),
SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
@@ -4216,6 +4252,8 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
+ SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_ASUS_MODE4),
+ SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_ASUS_MODE4),
SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2),
SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index 60159c07448d..6fd174be3bdf 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -351,6 +351,9 @@ static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol,
val = ucontrol->value.integer.value[0];
val2 = ucontrol->value.integer.value[1];
+ if (val >= ARRAY_SIZE(st_table) || val2 >= ARRAY_SIZE(st_table))
+ return -EINVAL;
+
err = snd_soc_update_bits(codec, reg, 0x3f, st_table[val].m);
if (err < 0)
return err;
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index a153b168129b..bce45c197e1d 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -1225,13 +1225,18 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
struct device *dev = codec->dev;
bool apply_fir, apply_iir;
- int req, status;
+ unsigned int req;
+ int status;
dev_dbg(dev, "%s: Enter.\n", __func__);
mutex_lock(&drvdata->anc_lock);
req = ucontrol->value.integer.value[0];
+ if (req >= ARRAY_SIZE(enum_anc_state)) {
+ status = -EINVAL;
+ goto cleanup;
+ }
if (req != ANC_APPLY_FIR_IIR && req != ANC_APPLY_FIR &&
req != ANC_APPLY_IIR) {
dev_err(dev, "%s: ERROR: Unsupported status to set '%s'!\n",
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 41cdd1642970..8dbcacd44e6a 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -1863,7 +1863,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
struct max98095_pdata *pdata = max98095->pdata;
int channel = max98095_get_eq_channel(kcontrol->id.name);
struct max98095_cdata *cdata;
- int sel = ucontrol->value.integer.value[0];
+ unsigned int sel = ucontrol->value.integer.value[0];
struct max98095_eq_cfg *coef_set;
int fs, best, best_val, i;
int regmask, regsave;
@@ -2016,7 +2016,7 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
struct max98095_pdata *pdata = max98095->pdata;
int channel = max98095_get_bq_channel(codec, kcontrol->id.name);
struct max98095_cdata *cdata;
- int sel = ucontrol->value.integer.value[0];
+ unsigned int sel = ucontrol->value.integer.value[0];
struct max98095_biquad_cfg *coef_set;
int fs, best, best_val, i;
int regmask, regsave;
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index 5402dfbbb716..8a8d9364e87f 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -126,6 +126,10 @@ static int mc13783_write(struct snd_soc_codec *codec,
ret = mc13xxx_reg_write(priv->mc13xxx, reg, value);
+ /* include errata fix for spi audio problems */
+ if (reg == MC13783_AUDIO_CODEC || reg == MC13783_AUDIO_DAC)
+ ret = mc13xxx_reg_write(priv->mc13xxx, reg, value);
+
mc13xxx_unlock(priv->mc13xxx);
return ret;
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 0a4ffdd1d2a7..5e5af898f7f8 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -857,9 +857,9 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
if (pll_div.k) {
reg |= 0x20;
- snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
- snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
- snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
+ snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff);
+ snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff);
+ snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff);
}
snd_soc_write(codec, WM8960_PLL1, reg);
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index f5d81b948759..7a0466eb7ede 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -530,6 +530,7 @@ static int hp_supply_event(struct snd_soc_dapm_widget *w,
hubs->hp_startup_mode);
break;
}
+ break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 360638362e98..c2ecb4e01597 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -1797,7 +1797,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
w->active ? "active" : "inactive");
list_for_each_entry(p, &w->sources, list_sink) {
- if (p->connected && !p->connected(w, p->sink))
+ if (p->connected && !p->connected(w, p->source))
continue;
if (p->connect)
diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c
index d0323a693ba2..999550bbad40 100644
--- a/sound/usb/usx2y/us122l.c
+++ b/sound/usb/usx2y/us122l.c
@@ -262,7 +262,9 @@ static int usb_stream_hwdep_mmap(struct snd_hwdep *hw,
}
area->vm_ops = &usb_stream_hwdep_vm_ops;
- area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+ area->vm_flags |= VM_DONTDUMP;
+ if (!read)
+ area->vm_flags |= VM_DONTEXPAND;
area->vm_private_data = us122l;
atomic_inc(&us122l->mmap_count);
out:
diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
index 0ce903375eaf..cd69a80b5ca9 100644
--- a/sound/usb/usx2y/usbusx2yaudio.c
+++ b/sound/usb/usx2y/usbusx2yaudio.c
@@ -299,19 +299,6 @@ static void usX2Y_error_urb_status(struct usX2Ydev *usX2Y,
usX2Y_clients_stop(usX2Y);
}
-static void usX2Y_error_sequence(struct usX2Ydev *usX2Y,
- struct snd_usX2Y_substream *subs, struct urb *urb)
-{
- snd_printk(KERN_ERR
-"Sequence Error!(hcd_frame=%i ep=%i%s;wait=%i,frame=%i).\n"
-"Most probably some urb of usb-frame %i is still missing.\n"
-"Cause could be too long delays in usb-hcd interrupt handling.\n",
- usb_get_current_frame_number(usX2Y->dev),
- subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
- usX2Y->wait_iso_frame, urb->start_frame, usX2Y->wait_iso_frame);
- usX2Y_clients_stop(usX2Y);
-}
-
static void i_usX2Y_urb_complete(struct urb *urb)
{
struct snd_usX2Y_substream *subs = urb->context;
@@ -328,12 +315,9 @@ static void i_usX2Y_urb_complete(struct urb *urb)
usX2Y_error_urb_status(usX2Y, subs, urb);
return;
}
- if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF)))
- subs->completed_urb = urb;
- else {
- usX2Y_error_sequence(usX2Y, subs, urb);
- return;
- }
+
+ subs->completed_urb = urb;
+
{
struct snd_usX2Y_substream *capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE],
*playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index f2a1acdc4d83..814d0e887c62 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -244,13 +244,8 @@ static void i_usX2Y_usbpcm_urb_complete(struct urb *urb)
usX2Y_error_urb_status(usX2Y, subs, urb);
return;
}
- if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF)))
- subs->completed_urb = urb;
- else {
- usX2Y_error_sequence(usX2Y, subs, urb);
- return;
- }
+ subs->completed_urb = urb;
capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
diff --git a/tools/gator/daemon/Android.mk b/tools/gator/daemon/Android.mk
new file mode 100644
index 000000000000..a0429712fa82
--- /dev/null
+++ b/tools/gator/daemon/Android.mk
@@ -0,0 +1,52 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h configuration_xml.h)
+
+LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors
+
+LOCAL_SRC_FILES := \
+ Buffer.cpp \
+ CapturedXML.cpp \
+ Child.cpp \
+ Collector.cpp \
+ ConfigurationXML.cpp \
+ Driver.cpp \
+ EventsXML.cpp \
+ Fifo.cpp \
+ Hwmon.cpp \
+ KMod.cpp \
+ LocalCapture.cpp \
+ Logging.cpp \
+ main.cpp \
+ OlySocket.cpp \
+ OlyUtility.cpp \
+ Sender.cpp \
+ SessionData.cpp \
+ SessionXML.cpp \
+ StreamlineSetup.cpp \
+ libsensors/access.c \
+ libsensors/conf-lex.c \
+ libsensors/conf-parse.c \
+ libsensors/data.c \
+ libsensors/error.c \
+ libsensors/general.c \
+ libsensors/init.c \
+ libsensors/sysfs.c \
+ mxml/mxml-attr.c \
+ mxml/mxml-entity.c \
+ mxml/mxml-file.c \
+ mxml/mxml-get.c \
+ mxml/mxml-index.c \
+ mxml/mxml-node.c \
+ mxml/mxml-private.c \
+ mxml/mxml-search.c \
+ mxml/mxml-set.c \
+ mxml/mxml-string.c
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
+
+LOCAL_MODULE := gatord
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/tools/gator/daemon/Buffer.cpp b/tools/gator/daemon/Buffer.cpp
new file mode 100644
index 000000000000..c7abbf3a1820
--- /dev/null
+++ b/tools/gator/daemon/Buffer.cpp
@@ -0,0 +1,217 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "Buffer.h"
+
+#include "Logging.h"
+#include "Sender.h"
+#include "SessionData.h"
+
+#define mask (size - 1)
+
+Buffer::Buffer (const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : core(core), buftype(buftype), size(size), readPos(0), writePos(0), commitPos(0), available(true), done(false), buf(new char[size]), commitTime(gSessionData->mLiveRate), readerSem(readerSem) {
+ if ((size & mask) != 0) {
+ logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2");
+ handleException();
+ }
+ frame();
+}
+
+Buffer::~Buffer () {
+ delete [] buf;
+}
+
+void Buffer::write (Sender * const sender) {
+ if (!commitReady()) {
+ return;
+ }
+
+ // determine the size of two halves
+ int length1 = commitPos - readPos;
+ char * buffer1 = buf + readPos;
+ int length2 = 0;
+ char * buffer2 = buf;
+ if (length1 < 0) {
+ length1 = size - readPos;
+ length2 = commitPos;
+ }
+
+ logg->logMessage("Sending data length1: %i length2: %i", length1, length2);
+
+ // start, middle or end
+ if (length1 > 0) {
+ sender->writeData(buffer1, length1, RESPONSE_APC_DATA);
+ }
+
+ // possible wrap around
+ if (length2 > 0) {
+ sender->writeData(buffer2, length2, RESPONSE_APC_DATA);
+ }
+
+ readPos = commitPos;
+}
+
+bool Buffer::commitReady () const {
+ return commitPos != readPos;
+}
+
+int Buffer::bytesAvailable () const {
+ int filled = writePos - readPos;
+ if (filled < 0) {
+ filled += size;
+ }
+
+ int remaining = size - filled;
+
+ if (available) {
+ // Give some extra room; also allows space to insert the overflow error packet
+ remaining -= 200;
+ } else {
+ // Hysteresis, prevents multiple overflow messages
+ remaining -= 2000;
+ }
+
+ return remaining;
+}
+
+bool Buffer::checkSpace (const int bytes) {
+ const int remaining = bytesAvailable();
+
+ if (remaining < bytes) {
+ available = false;
+ } else {
+ available = true;
+ }
+
+ return available;
+}
+
+void Buffer::commit (const uint64_t time) {
+ // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
+ const int typeLength = gSessionData->mLocalCapture ? 0 : 1;
+ int length = writePos - commitPos;
+ if (length < 0) {
+ length += size;
+ }
+ length = length - typeLength - sizeof(int32_t);
+ for (size_t byte = 0; byte < sizeof(int32_t); byte++) {
+ buf[(commitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF;
+ }
+
+ logg->logMessage("Committing data readPos: %i writePos: %i commitPos: %i", readPos, writePos, commitPos);
+ commitPos = writePos;
+
+ if (gSessionData->mLiveRate > 0) {
+ while (time > commitTime) {
+ commitTime += gSessionData->mLiveRate;
+ }
+ }
+
+ if (!done) {
+ frame();
+ }
+
+ // send a notification that data is ready
+ sem_post(readerSem);
+}
+
+void Buffer::check (const uint64_t time) {
+ int filled = writePos - commitPos;
+ if (filled < 0) {
+ filled += size;
+ }
+ if (filled >= ((size * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= commitTime)) {
+ commit(time);
+ }
+}
+
+void Buffer::packInt (int32_t x) {
+ int packedBytes = 0;
+ int more = true;
+ while (more) {
+ // low order 7 bits of x
+ char b = x & 0x7f;
+ x >>= 7;
+
+ if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) {
+ more = false;
+ } else {
+ b |= 0x80;
+ }
+
+ buf[(writePos + packedBytes) & mask] = b;
+ packedBytes++;
+ }
+
+ writePos = (writePos + packedBytes) & mask;
+}
+
+void Buffer::packInt64 (int64_t x) {
+ int packedBytes = 0;
+ int more = true;
+ while (more) {
+ // low order 7 bits of x
+ char b = x & 0x7f;
+ x >>= 7;
+
+ if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) {
+ more = false;
+ } else {
+ b |= 0x80;
+ }
+
+ buf[(writePos + packedBytes) & mask] = b;
+ packedBytes++;
+ }
+
+ writePos = (writePos + packedBytes) & mask;
+}
+
+void Buffer::frame () {
+ if (!gSessionData->mLocalCapture) {
+ packInt(RESPONSE_APC_DATA);
+ }
+ // Reserve space for the length
+ writePos += sizeof(int32_t);
+ packInt(buftype);
+ packInt(core);
+}
+
+bool Buffer::eventHeader (const uint64_t curr_time) {
+ bool retval = false;
+ if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) {
+ packInt(0); // key of zero indicates a timestamp
+ packInt64(curr_time);
+ retval = true;
+ }
+
+ return retval;
+}
+
+void Buffer::event (const int32_t key, const int32_t value) {
+ if (checkSpace(2 * MAXSIZE_PACK32)) {
+ packInt(key);
+ packInt(value);
+ }
+}
+
+void Buffer::event64 (const int64_t key, const int64_t value) {
+ if (checkSpace(2 * MAXSIZE_PACK64)) {
+ packInt64(key);
+ packInt64(value);
+ }
+}
+
+void Buffer::setDone () {
+ done = true;
+ commit(0);
+}
+
+bool Buffer::isDone () const {
+ return done && readPos == commitPos && commitPos == writePos;
+}
diff --git a/tools/gator/daemon/Buffer.h b/tools/gator/daemon/Buffer.h
new file mode 100644
index 000000000000..f820cfd851e3
--- /dev/null
+++ b/tools/gator/daemon/Buffer.h
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <semaphore.h>
+
+class Sender;
+
+class Buffer {
+public:
+ static const size_t MAXSIZE_PACK32 = 5;
+ static const size_t MAXSIZE_PACK64 = 10;
+
+ Buffer (int32_t core, int32_t buftype, const int size, sem_t *const readerSem);
+ ~Buffer ();
+
+ void write (Sender * sender);
+
+ int bytesAvailable () const;
+ void commit (const uint64_t time);
+ void check (const uint64_t time);
+
+ void frame ();
+
+ bool eventHeader (uint64_t curr_time);
+ void event (int32_t key, int32_t value);
+ void event64 (int64_t key, int64_t value);
+
+ void setDone ();
+ bool isDone () const;
+
+private:
+ bool commitReady () const;
+ bool checkSpace (int bytes);
+
+ void packInt (int32_t x);
+ void packInt64 (int64_t x);
+
+ const int32_t core;
+ const int32_t buftype;
+ const int size;
+ int readPos;
+ int writePos;
+ int commitPos;
+ bool available;
+ bool done;
+ char *const buf;
+ uint64_t commitTime;
+ sem_t *const readerSem;
+};
+
+#endif // BUFFER_H
diff --git a/tools/gator/daemon/CapturedXML.cpp b/tools/gator/daemon/CapturedXML.cpp
new file mode 100644
index 000000000000..30c4c44c5d92
--- /dev/null
+++ b/tools/gator/daemon/CapturedXML.cpp
@@ -0,0 +1,134 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include "SessionData.h"
+#include "CapturedXML.h"
+#include "Logging.h"
+#include "OlyUtility.h"
+
+CapturedXML::CapturedXML() {
+}
+
+CapturedXML::~CapturedXML() {
+}
+
+mxml_node_t* CapturedXML::getTree(bool includeTime) {
+ mxml_node_t *xml;
+ mxml_node_t *captured;
+ mxml_node_t *target;
+ int x;
+
+ xml = mxmlNewXML("1.0");
+
+ captured = mxmlNewElement(xml, "captured");
+ mxmlElementSetAttr(captured, "version", "1");
+ mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION);
+ if (includeTime) { // Send the following only after the capture is complete
+ if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010)
+ mxmlElementSetAttrf(captured, "created", "%lu", time(NULL)); // Valid until the year 2038
+ }
+ }
+
+ target = mxmlNewElement(captured, "target");
+ mxmlElementSetAttr(target, "name", gSessionData->mCoreName);
+ mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate);
+ mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores);
+ mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mCpuId);
+
+ if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) {
+ mxmlElementSetAttr(target, "supports_live", "yes");
+ }
+
+ if (gSessionData->mLocalCapture) {
+ mxmlElementSetAttr(target, "local_capture", "yes");
+ }
+
+ mxml_node_t *counters = NULL;
+ for (x = 0; x < MAX_PERFORMANCE_COUNTERS; x++) {
+ const Counter & counter = gSessionData->mCounters[x];
+ if (counter.isEnabled()) {
+ if (counters == NULL) {
+ counters = mxmlNewElement(captured, "counters");
+ }
+ mxml_node_t *const node = mxmlNewElement(counters, "counter");
+ mxmlElementSetAttrf(node, "key", "0x%x", counter.getKey());
+ mxmlElementSetAttr(node, "type", counter.getType());
+ mxmlElementSetAttrf(node, "event", "0x%x", counter.getEvent());
+ if (counter.getCount() > 0) {
+ mxmlElementSetAttrf(node, "count", "%d", counter.getCount());
+ }
+ }
+ }
+
+ return xml;
+}
+
+char* CapturedXML::getXML(bool includeTime) {
+ char* xml_string;
+ mxml_node_t *xml = getTree(includeTime);
+ xml_string = mxmlSaveAllocString(xml, mxmlWhitespaceCB);
+ mxmlDelete(xml);
+ return xml_string;
+}
+
+void CapturedXML::write(char* path) {
+ char file[PATH_MAX];
+
+ // Set full path
+ snprintf(file, PATH_MAX, "%s/captured.xml", path);
+
+ char* xml = getXML(true);
+ if (util->writeToDisk(file, xml) < 0) {
+ logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file);
+ handleException();
+ }
+
+ free(xml);
+}
+
+// whitespace callback utility function used with mini-xml
+const char * mxmlWhitespaceCB(mxml_node_t *node, int loc) {
+ const char *name;
+
+ name = mxmlGetElement(node);
+
+ if (loc == MXML_WS_BEFORE_OPEN) {
+ // Single indentation
+ if (!strcmp(name, "target") || !strcmp(name, "counters"))
+ return("\n ");
+
+ // Double indentation
+ if (!strcmp(name, "counter"))
+ return("\n ");
+
+ // Avoid a carriage return on the first line of the xml file
+ if (!strncmp(name, "?xml", 4))
+ return(NULL);
+
+ // Default - no indentation
+ return("\n");
+ }
+
+ if (loc == MXML_WS_BEFORE_CLOSE) {
+ // No indentation
+ if (!strcmp(name, "captured"))
+ return("\n");
+
+ // Single indentation
+ if (!strcmp(name, "counters"))
+ return("\n ");
+
+ // Default - no carriage return
+ return(NULL);
+ }
+
+ return(NULL);
+}
diff --git a/tools/gator/daemon/CapturedXML.h b/tools/gator/daemon/CapturedXML.h
new file mode 100644
index 000000000000..b0482f593c6f
--- /dev/null
+++ b/tools/gator/daemon/CapturedXML.h
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CAPTURED_XML_H__
+#define __CAPTURED_XML_H__
+
+#include "mxml/mxml.h"
+
+class CapturedXML {
+public:
+ CapturedXML();
+ ~CapturedXML();
+ char* getXML(bool includeTime); // the string should be freed by the caller
+ void write(char* path);
+private:
+ mxml_node_t* getTree(bool includeTime);
+};
+
+const char * mxmlWhitespaceCB(mxml_node_t *node, int where);
+
+#endif //__CAPTURED_XML_H__
diff --git a/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp
new file mode 100644
index 000000000000..c0540762698f
--- /dev/null
+++ b/tools/gator/daemon/Child.cpp
@@ -0,0 +1,408 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+#include "Logging.h"
+#include "CapturedXML.h"
+#include "SessionData.h"
+#include "Child.h"
+#include "LocalCapture.h"
+#include "Collector.h"
+#include "Sender.h"
+#include "OlyUtility.h"
+#include "StreamlineSetup.h"
+#include "ConfigurationXML.h"
+#include "Driver.h"
+#include "Fifo.h"
+#include "Buffer.h"
+
+#define NS_PER_S ((uint64_t)1000000000)
+#define NS_PER_US 1000
+
+static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads
+static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads
+static Buffer* buffer = NULL;
+static Sender* sender = NULL; // Shared by Child.cpp and spawned threads
+static Collector* collector = NULL;
+Child* child = NULL; // shared by Child.cpp and main.cpp
+
+extern void cleanUp();
+void handleException() {
+ if (child && child->numExceptions++ > 0) {
+ // it is possible one of the below functions itself can cause an exception, thus allow only one exception
+ logg->logMessage("Received multiple exceptions, terminating the child");
+ exit(1);
+ }
+ fprintf(stderr, "%s", logg->getLastError());
+
+ if (child && child->socket) {
+ if (sender) {
+ // send the error, regardless of the command sent by Streamline
+ sender->writeData(logg->getLastError(), strlen(logg->getLastError()), RESPONSE_ERROR);
+
+ // cannot close the socket before Streamline issues the command, so wait for the command before exiting
+ if (gSessionData->mWaitingOnCommand) {
+ char discard;
+ child->socket->receiveNBytes(&discard, 1);
+ }
+
+ // Ensure all data is flushed
+ child->socket->shutdownConnection();
+
+ // this indirectly calls close socket which will ensure the data has been sent
+ delete sender;
+ }
+ }
+
+ if (gSessionData->mLocalCapture)
+ cleanUp();
+
+ exit(1);
+}
+
+// CTRL C Signal Handler for child process
+static void child_handler(int signum) {
+ static bool beenHere = false;
+ if (beenHere == true) {
+ logg->logMessage("Gator is being forced to shut down.");
+ exit(1);
+ }
+ beenHere = true;
+ logg->logMessage("Gator is shutting down.");
+ if (signum == SIGALRM || !collector) {
+ exit(1);
+ } else {
+ child->endSession();
+ alarm(5); // Safety net in case endSession does not complete within 5 seconds
+ }
+}
+
+static void* durationThread(void* pVoid) {
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-duration", 0, 0, 0);
+ sem_wait(&startProfile);
+ if (gSessionData->mSessionIsActive) {
+ // Time out after duration seconds
+ // Add a second for host-side filtering
+ sleep(gSessionData->mDuration + 1);
+ if (gSessionData->mSessionIsActive) {
+ logg->logMessage("Duration expired.");
+ child->endSession();
+ }
+ }
+ logg->logMessage("Exit duration thread");
+ return 0;
+}
+
+static void* stopThread(void* pVoid) {
+ OlySocket* socket = child->socket;
+
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-stopper", 0, 0, 0);
+ while (gSessionData->mSessionIsActive) {
+ // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected
+ unsigned char header[5];
+ const int result = socket->receiveNBytes((char*)&header, sizeof(header));
+ const char type = header[0];
+ const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24);
+ if (result == -1) {
+ child->endSession();
+ } else if (result > 0) {
+ if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) {
+ logg->logMessage("INVESTIGATE: Received unknown command type %d", type);
+ } else {
+ // verify a length of zero
+ if (length == 0) {
+ if (type == COMMAND_APC_STOP) {
+ logg->logMessage("Stop command received.");
+ child->endSession();
+ } else {
+ // Ping is used to make sure gator is alive and requires an ACK as the response
+ logg->logMessage("Ping command received.");
+ sender->writeData(NULL, 0, RESPONSE_ACK);
+ }
+ } else {
+ logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length);
+ }
+ }
+ }
+ }
+
+ logg->logMessage("Exit stop thread");
+ return 0;
+}
+
+void* countersThread(void* pVoid) {
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0);
+
+ gSessionData->hwmon.start();
+
+ int64_t monotonic_started = 0;
+ while (monotonic_started <= 0) {
+ usleep(10);
+
+ if (Collector::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) {
+ logg->logError(__FILE__, __LINE__, "Error reading gator driver start time");
+ handleException();
+ }
+ }
+
+ uint64_t next_time = 0;
+ while (gSessionData->mSessionIsActive) {
+ struct timespec ts;
+#ifndef CLOCK_MONOTONIC_RAW
+ // Android doesn't have this defined but it was added in Linux 2.6.28
+#define CLOCK_MONOTONIC_RAW 4
+#endif
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) {
+ logg->logError(__FILE__, __LINE__, "Failed to get uptime");
+ handleException();
+ }
+ const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started;
+ // Sample ten times a second ignoring gSessionData->mSampleRate
+ next_time += NS_PER_S/10;//gSessionData->mSampleRate;
+ if (next_time < curr_time) {
+ logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time);
+ next_time = curr_time;
+ }
+
+ if (buffer->eventHeader(curr_time)) {
+ gSessionData->hwmon.read(buffer);
+ // Only check after writing all counters so that time and corresponding counters appear in the same frame
+ buffer->check(curr_time);
+ }
+
+ if (buffer->bytesAvailable() <= 0) {
+ logg->logMessage("One shot (counters)");
+ child->endSession();
+ }
+
+ usleep((next_time - curr_time)/NS_PER_US);
+ }
+
+ buffer->setDone();
+
+ return NULL;
+}
+
+static void* senderThread(void* pVoid) {
+ int length = 1;
+ char* data;
+ char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
+
+ sem_post(&senderThreadStarted);
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0);
+ sem_wait(&haltPipeline);
+
+ while (length > 0 || !buffer->isDone()) {
+ sem_wait(&senderSem);
+ data = collectorFifo->read(&length);
+ if (data != NULL) {
+ sender->writeData(data, length, RESPONSE_APC_DATA);
+ collectorFifo->release();
+ }
+ if (!buffer->isDone()) {
+ buffer->write(sender);
+ }
+ }
+
+ // write end-of-capture sequence
+ if (!gSessionData->mLocalCapture) {
+ sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA);
+ }
+
+ logg->logMessage("Exit sender thread");
+ return 0;
+}
+
+Child::Child() {
+ initialization();
+ gSessionData->mLocalCapture = true;
+}
+
+Child::Child(OlySocket* sock, int conn) {
+ initialization();
+ socket = sock;
+ mNumConnections = conn;
+}
+
+Child::~Child() {
+}
+
+void Child::initialization() {
+ // Set up different handlers for signals
+ gSessionData->mSessionIsActive = true;
+ signal(SIGINT, child_handler);
+ signal(SIGTERM, child_handler);
+ signal(SIGABRT, child_handler);
+ signal(SIGALRM, child_handler);
+ socket = NULL;
+ numExceptions = 0;
+ mNumConnections = 0;
+
+ // Initialize semaphores
+ sem_init(&senderThreadStarted, 0, 0);
+ sem_init(&startProfile, 0, 0);
+ sem_init(&senderSem, 0, 0);
+}
+
+void Child::endSession() {
+ gSessionData->mSessionIsActive = false;
+ collector->stop();
+ sem_post(&haltPipeline);
+}
+
+void Child::run() {
+ char* collectBuffer;
+ int bytesCollected = 0;
+ LocalCapture* localCapture = NULL;
+ pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID;
+
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
+
+ // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually
+ mxmlSetWrapMargin(0);
+
+ // Instantiate the Sender - must be done first, after which error messages can be sent
+ sender = new Sender(socket);
+
+ if (mNumConnections > 1) {
+ logg->logError(__FILE__, __LINE__, "Session already in progress");
+ handleException();
+ }
+
+ // Populate gSessionData with the configuration
+ { ConfigurationXML configuration; }
+
+ // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
+ collector = new Collector();
+
+ // Initialize all drivers
+ for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
+ driver->resetCounters();
+ }
+
+ // Set up counters using the associated driver's setup function
+ for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
+ Counter & counter = gSessionData->mCounters[i];
+ if (counter.isEnabled()) {
+ counter.getDriver()->setupCounter(counter);
+ }
+ }
+
+ // Start up and parse session xml
+ if (socket) {
+ // Respond to Streamline requests
+ StreamlineSetup ss(socket);
+ } else {
+ char* xmlString;
+ xmlString = util->readFromDisk(gSessionData->mSessionXMLPath);
+ if (xmlString == 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath);
+ handleException();
+ }
+ gSessionData->parseSessionXML(xmlString);
+ localCapture = new LocalCapture();
+ localCapture->createAPCDirectory(gSessionData->mTargetPath);
+ localCapture->copyImages(gSessionData->mImages);
+ localCapture->write(xmlString);
+ sender->createDataFile(gSessionData->mAPCDir);
+ free(xmlString);
+ }
+
+ // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length
+ logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize());
+ collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem);
+
+ // Get the initial pointer to the collect buffer
+ collectBuffer = collectorFifo->start();
+
+ // Create a new Block Counter Buffer
+ buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem);
+
+ // Sender thread shall be halted until it is signaled for one shot mode
+ sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
+
+ // Create the duration, stop, and sender threads
+ bool thread_creation_success = true;
+ if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) {
+ thread_creation_success = false;
+ } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) {
+ thread_creation_success = false;
+ } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)){
+ thread_creation_success = false;
+ }
+
+ if (gSessionData->hwmon.countersEnabled()) {
+ if (pthread_create(&countersThreadID, NULL, countersThread, this)) {
+ thread_creation_success = false;
+ }
+ } else {
+ // Let senderThread know there is no buffer data to send
+ buffer->setDone();
+ }
+
+ if (!thread_creation_success) {
+ logg->logError(__FILE__, __LINE__, "Failed to create gator threads");
+ handleException();
+ }
+
+ // Wait until thread has started
+ sem_wait(&senderThreadStarted);
+
+ // Start profiling
+ logg->logMessage("********** Profiling started **********");
+ collector->start();
+ sem_post(&startProfile);
+
+ // Collect Data
+ do {
+ // This command will stall until data is received from the driver
+ bytesCollected = collector->collect(collectBuffer);
+
+ // In one shot mode, stop collection once all the buffers are filled
+ if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
+ if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) {
+ logg->logMessage("One shot");
+ endSession();
+ }
+ }
+ collectBuffer = collectorFifo->write(bytesCollected);
+ } while (bytesCollected > 0);
+ logg->logMessage("Exit collect data loop");
+
+ if (gSessionData->hwmon.countersEnabled()) {
+ pthread_join(countersThreadID, NULL);
+ }
+
+ // Wait for the other threads to exit
+ pthread_join(senderThreadID, NULL);
+
+ // Shutting down the connection should break the stop thread which is stalling on the socket recv() function
+ if (socket) {
+ logg->logMessage("Waiting on stop thread");
+ socket->shutdownConnection();
+ pthread_join(stopThreadID, NULL);
+ }
+
+ // Write the captured xml file
+ if (gSessionData->mLocalCapture) {
+ CapturedXML capturedXML;
+ capturedXML.write(gSessionData->mAPCDir);
+ }
+
+ logg->logMessage("Profiling ended.");
+
+ delete buffer;
+ delete collectorFifo;
+ delete sender;
+ delete collector;
+ delete localCapture;
+}
diff --git a/tools/gator/daemon/Child.h b/tools/gator/daemon/Child.h
new file mode 100644
index 000000000000..e39d18276407
--- /dev/null
+++ b/tools/gator/daemon/Child.h
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CHILD_H__
+#define __CHILD_H__
+
+#include <pthread.h>
+
+class OlySocket;
+
+class Child {
+public:
+ Child();
+ Child(OlySocket* sock, int numConnections);
+ ~Child();
+ void run();
+ OlySocket *socket;
+ void endSession();
+ int numExceptions;
+private:
+ int mNumConnections;
+
+ void initialization();
+};
+
+#endif //__CHILD_H__
diff --git a/tools/gator/daemon/Collector.cpp b/tools/gator/daemon/Collector.cpp
new file mode 100644
index 000000000000..bf73534692a0
--- /dev/null
+++ b/tools/gator/daemon/Collector.cpp
@@ -0,0 +1,221 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define __STDC_FORMAT_MACROS
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <inttypes.h>
+#include "Collector.h"
+#include "SessionData.h"
+#include "Logging.h"
+#include "Sender.h"
+
+// Driver initialization independent of session settings
+Collector::Collector() {
+ mBufferFD = 0;
+
+ checkVersion();
+
+ int enable = -1;
+ if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) {
+ logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress.");
+ handleException();
+ }
+
+ readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores);
+ if (gSessionData->mCores == 0) {
+ gSessionData->mCores = 1;
+ }
+
+ mBufferSize = 0;
+ if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size");
+ handleException();
+ }
+}
+
+Collector::~Collector() {
+ // Write zero for safety, as a zero should have already been written
+ writeDriver("/dev/gator/enable", "0");
+
+ // Calls event_buffer_release in the driver
+ if (mBufferFD) {
+ close(mBufferFD);
+ }
+}
+
+void Collector::checkVersion() {
+ int driver_version = 0;
+
+ if (readIntDriver("/dev/gator/version", &driver_version) == -1) {
+ logg->logError(__FILE__, __LINE__, "Error reading gator driver version");
+ handleException();
+ }
+
+ // Verify the driver version matches the daemon version
+ if (driver_version != PROTOCOL_VERSION) {
+ if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) {
+ // One of the mismatched versions is development version
+ logg->logError(__FILE__, __LINE__,
+ "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n"
+ ">> The following must be synchronized from engineering repository:\n"
+ ">> * gator driver\n"
+ ">> * gator daemon\n"
+ ">> * Streamline", driver_version, PROTOCOL_VERSION);
+ handleException();
+ } else {
+ // Release version mismatch
+ logg->logError(__FILE__, __LINE__,
+ "gator driver version \"%d\" is different than gator daemon version \"%d\".\n"
+ ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION);
+ handleException();
+ }
+ }
+}
+
+void Collector::start() {
+ // Set the maximum backtrace depth
+ if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) {
+ logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth");
+ handleException();
+ }
+
+ // open the buffer which calls userspace_buffer_open() in the driver
+ mBufferFD = open("/dev/gator/buffer", O_RDONLY);
+ if (mBufferFD < 0) {
+ logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure.");
+ handleException();
+ }
+
+ // set the tick rate of the profiling timer
+ if (writeReadDriver("/dev/gator/tick", &gSessionData->mSampleRate) != 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to set the driver tick");
+ handleException();
+ }
+
+ // notify the kernel of the response type
+ int response_type = gSessionData->mLocalCapture ? 0 : RESPONSE_APC_DATA;
+ if (writeDriver("/dev/gator/response_type", response_type)) {
+ logg->logError(__FILE__, __LINE__, "Unable to write the response type");
+ handleException();
+ }
+
+ // Set the live rate
+ if (writeReadDriver("/dev/gator/live_rate", &gSessionData->mLiveRate)) {
+ logg->logError(__FILE__, __LINE__, "Unable to set the driver live rate");
+ handleException();
+ }
+
+ logg->logMessage("Start the driver");
+
+ // This command makes the driver start profiling by calling gator_op_start() in the driver
+ if (writeDriver("/dev/gator/enable", "1") != 0) {
+ logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure.");
+ handleException();
+ }
+
+ lseek(mBufferFD, 0, SEEK_SET);
+}
+
+// These commands should cause the read() function in collect() to return
+void Collector::stop() {
+ // This will stop the driver from profiling
+ if (writeDriver("/dev/gator/enable", "0") != 0) {
+ logg->logMessage("Stopping kernel failed");
+ }
+}
+
+int Collector::collect(char* buffer) {
+ // Calls event_buffer_read in the driver
+ int bytesRead;
+
+ errno = 0;
+ bytesRead = read(mBufferFD, buffer, mBufferSize);
+
+ // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data
+ if (bytesRead == -1 && errno == EINTR) {
+ bytesRead = read(mBufferFD, buffer, mBufferSize);
+ }
+
+ // return the total bytes written
+ logg->logMessage("Driver read of %d bytes", bytesRead);
+ return bytesRead;
+}
+
+int Collector::readIntDriver(const char* fullpath, int* value) {
+ FILE* file = fopen(fullpath, "r");
+ if (file == NULL) {
+ return -1;
+ }
+ if (fscanf(file, "%u", value) != 1) {
+ fclose(file);
+ logg->logMessage("Invalid value in file %s", fullpath);
+ return -1;
+ }
+ fclose(file);
+ return 0;
+}
+
+int Collector::readInt64Driver(const char* fullpath, int64_t* value) {
+ FILE* file = fopen(fullpath, "r");
+ if (file == NULL) {
+ return -1;
+ }
+ if (fscanf(file, "%" SCNi64, value) != 1) {
+ fclose(file);
+ logg->logMessage("Invalid value in file %s", fullpath);
+ return -1;
+ }
+ fclose(file);
+ return 0;
+}
+
+int Collector::writeDriver(const char* path, int value) {
+ char data[40]; // Sufficiently large to hold any integer
+ snprintf(data, sizeof(data), "%d", value);
+ return writeDriver(path, data);
+}
+
+int Collector::writeDriver(const char* path, int64_t value) {
+ char data[40]; // Sufficiently large to hold any integer
+ snprintf(data, sizeof(data), "%" PRIi64, value);
+ return writeDriver(path, data);
+}
+
+int Collector::writeDriver(const char* fullpath, const char* data) {
+ int fd = open(fullpath, O_WRONLY);
+ if (fd < 0) {
+ return -1;
+ }
+ if (write(fd, data, strlen(data)) < 0) {
+ close(fd);
+ logg->logMessage("Opened but could not write to %s", fullpath);
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
+int Collector::writeReadDriver(const char* path, int* value) {
+ if (writeDriver(path, *value) || readIntDriver(path, value)) {
+ return -1;
+ }
+ return 0;
+}
+
+int Collector::writeReadDriver(const char* path, int64_t* value) {
+ if (writeDriver(path, *value) || readInt64Driver(path, value)) {
+ return -1;
+ }
+ return 0;
+}
diff --git a/tools/gator/daemon/Collector.h b/tools/gator/daemon/Collector.h
new file mode 100644
index 000000000000..c5e9eac573dd
--- /dev/null
+++ b/tools/gator/daemon/Collector.h
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __COLLECTOR_H__
+#define __COLLECTOR_H__
+
+#include <stdio.h>
+
+class Collector {
+public:
+ Collector();
+ ~Collector();
+ void start();
+ void stop();
+ int collect(char* buffer);
+ int getBufferSize() {return mBufferSize;}
+
+ static int readIntDriver(const char* path, int* value);
+ static int readInt64Driver(const char* path, int64_t* value);
+ static int writeDriver(const char* path, int value);
+ static int writeDriver(const char* path, int64_t value);
+ static int writeDriver(const char* path, const char* data);
+ static int writeReadDriver(const char* path, int* value);
+ static int writeReadDriver(const char* path, int64_t* value);
+
+private:
+ int mBufferSize;
+ int mBufferFD;
+
+ void checkVersion();
+};
+
+#endif //__COLLECTOR_H__
diff --git a/tools/gator/daemon/ConfigurationXML.cpp b/tools/gator/daemon/ConfigurationXML.cpp
new file mode 100644
index 000000000000..2a5252a5bb04
--- /dev/null
+++ b/tools/gator/daemon/ConfigurationXML.cpp
@@ -0,0 +1,209 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include "ConfigurationXML.h"
+#include "Driver.h"
+#include "Logging.h"
+#include "OlyUtility.h"
+#include "SessionData.h"
+
+static const char* ATTR_COUNTER = "counter";
+static const char* ATTR_REVISION = "revision";
+static const char* ATTR_EVENT = "event";
+static const char* ATTR_COUNT = "count";
+
+ConfigurationXML::ConfigurationXML() {
+ const char * configuration_xml;
+ unsigned int configuration_xml_len;
+ getDefaultConfigurationXml(configuration_xml, configuration_xml_len);
+
+ char path[PATH_MAX];
+
+ getPath(path);
+ mConfigurationXML = util->readFromDisk(path);
+
+ for (int retryCount = 0; retryCount < 2; ++retryCount) {
+ if (mConfigurationXML == NULL) {
+ logg->logMessage("Unable to locate configuration.xml, using default in binary");
+ // null-terminate configuration_xml
+ mConfigurationXML = (char*)malloc(configuration_xml_len + 1);
+ memcpy(mConfigurationXML, (const void*)configuration_xml, configuration_xml_len);
+ mConfigurationXML[configuration_xml_len] = 0;
+ }
+
+ int ret = parse(mConfigurationXML);
+ if (ret == 1) {
+ remove();
+
+ // Free the current configuration and reload
+ free((void*)mConfigurationXML);
+ mConfigurationXML = NULL;
+ continue;
+ }
+
+ break;
+ }
+
+ validate();
+}
+
+ConfigurationXML::~ConfigurationXML() {
+ if (mConfigurationXML) {
+ free((void*)mConfigurationXML);
+ }
+}
+
+int ConfigurationXML::parse(const char* configurationXML) {
+ mxml_node_t *tree, *node;
+ int ret;
+
+ // clear counter overflow
+ gSessionData->mCounterOverflow = 0;
+ mIndex = 0;
+
+ // disable all counters prior to parsing the configuration xml
+ for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
+ gSessionData->mCounters[i].setEnabled(false);
+ }
+
+ tree = mxmlLoadString(NULL, configurationXML, MXML_NO_CALLBACK);
+
+ node = mxmlGetFirstChild(tree);
+ while (node && mxmlGetType(node) != MXML_ELEMENT)
+ node = mxmlWalkNext(node, tree, MXML_NO_DESCEND);
+
+ ret = configurationsTag(node);
+
+ node = mxmlGetFirstChild(node);
+ while (node) {
+ if (mxmlGetType(node) != MXML_ELEMENT) {
+ node = mxmlWalkNext(node, tree, MXML_NO_DESCEND);
+ continue;
+ }
+ configurationTag(node);
+ node = mxmlWalkNext(node, tree, MXML_NO_DESCEND);
+ }
+
+ mxmlDelete(tree);
+
+ return ret;
+}
+
+void ConfigurationXML::validate(void) {
+ for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
+ const Counter & counter = gSessionData->mCounters[i];
+ if (counter.isEnabled()) {
+ if (strcmp(counter.getType(), "") == 0) {
+ logg->logError(__FILE__, __LINE__, "Invalid required attribute in configuration.xml:\n counter=\"%s\"\n event=%d\n", counter.getType(), counter.getEvent());
+ handleException();
+ }
+
+ // iterate through the remaining enabled performance counters
+ for (int j = i + 1; j < MAX_PERFORMANCE_COUNTERS; j++) {
+ const Counter & counter2 = gSessionData->mCounters[j];
+ if (counter2.isEnabled()) {
+ // check if the types are the same
+ if (strcmp(counter.getType(), counter2.getType()) == 0) {
+ logg->logError(__FILE__, __LINE__, "Duplicate performance counter type in configuration.xml: %s", counter.getType());
+ handleException();
+ }
+ }
+ }
+ }
+ }
+}
+
+#define CONFIGURATION_REVISION 3
+int ConfigurationXML::configurationsTag(mxml_node_t *node) {
+ const char* revision_string;
+
+ revision_string = mxmlElementGetAttr(node, ATTR_REVISION);
+ if (!revision_string) {
+ return 1; //revision issue;
+ }
+
+ int revision = strtol(revision_string, NULL, 10);
+ if (revision < CONFIGURATION_REVISION) {
+ return 1; // revision issue
+ }
+
+ // A revision >= CONFIGURATION_REVISION is okay
+ // Greater than can occur when Streamline is newer than gator
+
+ return 0;
+}
+
+void ConfigurationXML::configurationTag(mxml_node_t *node) {
+ // handle all other performance counters
+ if (mIndex >= MAX_PERFORMANCE_COUNTERS) {
+ mIndex++;
+ gSessionData->mCounterOverflow = mIndex;
+ return;
+ }
+
+ // read attributes
+ Counter & counter = gSessionData->mCounters[mIndex];
+ counter.clear();
+ if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER));
+ if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16));
+ if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10));
+ counter.setEnabled(true);
+
+ // Associate a driver with each counter
+ for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
+ if (driver->claimCounter(counter)) {
+ if (counter.getDriver() != NULL) {
+ logg->logError(__FILE__, __LINE__, "More than one driver has claimed %s:%i", counter.getType(), counter.getEvent());
+ handleException();
+ }
+ counter.setDriver(driver);
+ }
+ }
+
+ // If no driver is associated with the counter, disable it
+ if (counter.getDriver() == NULL) {
+ logg->logMessage("No driver has claimed %s:%i", counter.getType(), counter.getEvent());
+ counter.setEnabled(false);
+ }
+
+ if (counter.isEnabled()) {
+ // update counter index
+ mIndex++;
+ }
+}
+
+void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) {
+#include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len
+ xml = (const char *)configuration_xml;
+ len = configuration_xml_len;
+}
+
+void ConfigurationXML::getPath(char* path) {
+ if (gSessionData->mConfigurationXMLPath) {
+ strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX);
+ } else {
+ if (util->getApplicationFullPath(path, PATH_MAX) != 0) {
+ logg->logMessage("Unable to determine the full path of gatord, the cwd will be used");
+ }
+ strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1);
+ }
+}
+
+void ConfigurationXML::remove() {
+ char path[PATH_MAX];
+ getPath(path);
+
+ if (::remove(path) != 0) {
+ logg->logError(__FILE__, __LINE__, "Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk");
+ handleException();
+ }
+ logg->logMessage("Invalid configuration.xml file detected and removed");
+}
diff --git a/tools/gator/daemon/ConfigurationXML.h b/tools/gator/daemon/ConfigurationXML.h
new file mode 100644
index 000000000000..eba7dc4bac46
--- /dev/null
+++ b/tools/gator/daemon/ConfigurationXML.h
@@ -0,0 +1,34 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef COUNTERS_H
+#define COUNTERS_H
+
+#include "mxml/mxml.h"
+
+class ConfigurationXML {
+public:
+ static void getDefaultConfigurationXml(const char * & xml, unsigned int & len);
+ static void getPath(char* path);
+ static void remove();
+
+ ConfigurationXML();
+ ~ConfigurationXML();
+ const char* getConfigurationXML() {return mConfigurationXML;}
+ void validate(void);
+
+private:
+ char* mConfigurationXML;
+ int mIndex;
+
+ int parse(const char* xmlFile);
+ int configurationsTag(mxml_node_t *node);
+ void configurationTag(mxml_node_t *node);
+};
+
+#endif // COUNTERS_H
diff --git a/tools/gator/daemon/Counter.h b/tools/gator/daemon/Counter.h
new file mode 100644
index 000000000000..231a85d6e3b5
--- /dev/null
+++ b/tools/gator/daemon/Counter.h
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef COUNTER_H
+#define COUNTER_H
+
+#include <string.h>
+
+class Driver;
+
+class Counter {
+public:
+ static const size_t MAX_STRING_LEN = 80;
+ static const size_t MAX_DESCRIPTION_LEN = 400;
+
+ Counter () {
+ clear();
+ }
+
+ void clear () {
+ mType[0] = '\0';
+ mEnabled = false;
+ mEvent = 0;
+ mCount = 0;
+ mKey = 0;
+ mDriver = NULL;
+ }
+
+ void setType(const char *const type) { strncpy(mType, type, sizeof(mType)); mType[sizeof(mType) - 1] = '\0'; }
+ void setEnabled(const bool enabled) { mEnabled = enabled; }
+ void setEvent(const int event) { mEvent = event; }
+ void setCount(const int count) { mCount = count; }
+ void setKey(const int key) { mKey = key; }
+ void setDriver(Driver *const driver) { mDriver = driver; }
+
+ const char *getType() const { return mType;}
+ bool isEnabled() const { return mEnabled; }
+ int getEvent() const { return mEvent; }
+ int getCount() const { return mCount; }
+ int getKey() const { return mKey; }
+ Driver *getDriver() const { return mDriver; }
+
+private:
+ // Intentionally unimplemented
+ Counter(const Counter &);
+ Counter & operator=(const Counter &);
+
+ char mType[MAX_STRING_LEN];
+ bool mEnabled;
+ int mEvent;
+ int mCount;
+ int mKey;
+ Driver *mDriver;
+};
+
+#endif // COUNTER_H
diff --git a/tools/gator/daemon/Driver.cpp b/tools/gator/daemon/Driver.cpp
new file mode 100644
index 000000000000..c262467dc219
--- /dev/null
+++ b/tools/gator/daemon/Driver.cpp
@@ -0,0 +1,15 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "Driver.h"
+
+Driver *Driver::head = NULL;
+
+Driver::Driver() : next(head) {
+ head = this;
+}
diff --git a/tools/gator/daemon/Driver.h b/tools/gator/daemon/Driver.h
new file mode 100644
index 000000000000..dd1dc27d1cdb
--- /dev/null
+++ b/tools/gator/daemon/Driver.h
@@ -0,0 +1,44 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef DRIVER_H
+#define DRIVER_H
+
+#include "mxml/mxml.h"
+
+class Counter;
+
+class Driver {
+public:
+ static Driver *getHead() { return head; }
+
+ virtual ~Driver() {}
+
+ // Returns true if this driver can manage the counter
+ virtual bool claimCounter(const Counter &counter) const = 0;
+ // Clears and disables all counters
+ virtual void resetCounters() = 0;
+ // Enables and prepares the counter for capture
+ virtual void setupCounter(Counter &counter) = 0;
+
+ // Emits available counters
+ virtual void writeCounters(mxml_node_t *root) const = 0;
+ // Emits possible dynamically generated events/counters
+ virtual void writeEvents(mxml_node_t *root) const {}
+
+ Driver *getNext() const { return next; }
+
+protected:
+ Driver ();
+
+private:
+ static Driver *head;
+ Driver *next;
+};
+
+#endif // DRIVER_H
diff --git a/tools/gator/daemon/EventsXML.cpp b/tools/gator/daemon/EventsXML.cpp
new file mode 100644
index 000000000000..2a80482e0b8d
--- /dev/null
+++ b/tools/gator/daemon/EventsXML.cpp
@@ -0,0 +1,70 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "EventsXML.h"
+
+#include "CapturedXML.h"
+#include "Logging.h"
+#include "OlyUtility.h"
+#include "SessionData.h"
+
+char* EventsXML::getXML() {
+#include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len
+ char path[PATH_MAX];
+ mxml_node_t *xml;
+ FILE *fl;
+
+ // Avoid unused variable warning
+ (void)events_xml_len;
+
+ // Load the provided or default events xml
+ if (gSessionData->mEventsXMLPath) {
+ strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX);
+ } else {
+ util->getApplicationFullPath(path, PATH_MAX);
+ strncat(path, "events.xml", PATH_MAX - strlen(path) - 1);
+ }
+ fl = fopen(path, "r");
+ if (fl) {
+ xml = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK);
+ fclose(fl);
+ } else {
+ logg->logMessage("Unable to locate events.xml, using default");
+ xml = mxmlLoadString(NULL, (char *)events_xml, MXML_NO_CALLBACK);
+ }
+
+ // Add dynamic events from the drivers
+ mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND);
+ if (!events) {
+ logg->logMessage("Unable to find <events> node in the events.xml");
+ handleException();
+ }
+ for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
+ driver->writeEvents(events);
+ }
+
+ char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB);
+ mxmlDelete(xml);
+
+ return string;
+}
+
+void EventsXML::write(const char* path) {
+ char file[PATH_MAX];
+
+ // Set full path
+ snprintf(file, PATH_MAX, "%s/events.xml", path);
+
+ char* buf = getXML();
+ if (util->writeToDisk(file, buf) < 0) {
+ logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file);
+ handleException();
+ }
+
+ free(buf);
+}
diff --git a/tools/gator/daemon/EventsXML.h b/tools/gator/daemon/EventsXML.h
new file mode 100644
index 000000000000..8e693efab202
--- /dev/null
+++ b/tools/gator/daemon/EventsXML.h
@@ -0,0 +1,18 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef EVENTS_XML
+#define EVENTS_XML
+
+class EventsXML {
+public:
+ char* getXML();
+ void write(const char* path);
+};
+
+#endif // EVENTS_XML
diff --git a/tools/gator/daemon/Fifo.cpp b/tools/gator/daemon/Fifo.cpp
new file mode 100644
index 000000000000..250a4d023bf2
--- /dev/null
+++ b/tools/gator/daemon/Fifo.cpp
@@ -0,0 +1,130 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "Fifo.h"
+
+#include <stdlib.h>
+#ifdef WIN32
+#define valloc malloc
+#endif
+
+#include "Logging.h"
+
+// bufferSize is the amount of data to be filled
+// singleBufferSize is the maximum size that may be filled during a single write
+// (bufferSize + singleBufferSize) will be allocated
+Fifo::Fifo(int singleBufferSize, int bufferSize, sem_t* readerSem) {
+ mWrite = mRead = mReadCommit = mRaggedEnd = 0;
+ mWrapThreshold = bufferSize;
+ mSingleBufferSize = singleBufferSize;
+ mReaderSem = readerSem;
+ mBuffer = (char*)valloc(bufferSize + singleBufferSize);
+ mEnd = false;
+
+ if (mBuffer == NULL) {
+ logg->logError(__FILE__, __LINE__, "failed to allocate %d bytes", bufferSize + singleBufferSize);
+ handleException();
+ }
+
+ if (sem_init(&mWaitForSpaceSem, 0, 0)) {
+ logg->logError(__FILE__, __LINE__, "sem_init() failed");
+ handleException();
+ }
+}
+
+Fifo::~Fifo() {
+ free(mBuffer);
+ sem_destroy(&mWaitForSpaceSem);
+}
+
+int Fifo::numBytesFilled() const {
+ return mWrite - mRead + mRaggedEnd;
+}
+
+char* Fifo::start() const {
+ return mBuffer;
+}
+
+bool Fifo::isEmpty() const {
+ return mRead == mWrite && mRaggedEnd == 0;
+}
+
+bool Fifo::isFull() const {
+ return willFill(0);
+}
+
+// Determines if the buffer will fill assuming 'additional' bytes will be added to the buffer
+// 'full' means there is less than singleBufferSize bytes available contiguously; it does not mean there are zero bytes available
+bool Fifo::willFill(int additional) const {
+ if (mWrite > mRead) {
+ if (numBytesFilled() + additional < mWrapThreshold) {
+ return false;
+ }
+ } else {
+ if (numBytesFilled() + additional < mWrapThreshold - mSingleBufferSize) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// This function will stall until contiguous singleBufferSize bytes are available
+char* Fifo::write(int length) {
+ if (length <= 0) {
+ length = 0;
+ mEnd = true;
+ }
+
+ // update the write pointer
+ mWrite += length;
+
+ // handle the wrap-around
+ if (mWrite >= mWrapThreshold) {
+ mRaggedEnd = mWrite;
+ mWrite = 0;
+ }
+
+ // send a notification that data is ready
+ sem_post(mReaderSem);
+
+ // wait for space
+ while (isFull()) {
+ sem_wait(&mWaitForSpaceSem);
+ }
+
+ return &mBuffer[mWrite];
+}
+
+void Fifo::release() {
+ // update the read pointer now that the data has been handled
+ mRead = mReadCommit;
+
+ // handle the wrap-around
+ if (mRead >= mWrapThreshold) {
+ mRaggedEnd = mRead = mReadCommit = 0;
+ }
+
+ // send a notification that data is free (space is available)
+ sem_post(&mWaitForSpaceSem);
+}
+
+// This function will return null if no data is available
+char* Fifo::read(int *const length) {
+ // wait for data
+ if (isEmpty() && !mEnd) {
+ return NULL;
+ }
+
+ // obtain the length
+ do {
+ mReadCommit = mRaggedEnd ? mRaggedEnd : mWrite;
+ *length = mReadCommit - mRead;
+ } while (*length < 0); // plugs race condition without using semaphores
+
+ return &mBuffer[mRead];
+}
diff --git a/tools/gator/daemon/Fifo.h b/tools/gator/daemon/Fifo.h
new file mode 100644
index 000000000000..ada42b9fb584
--- /dev/null
+++ b/tools/gator/daemon/Fifo.h
@@ -0,0 +1,44 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __FIFO_H__
+#define __FIFO_H__
+
+#ifdef WIN32
+#include <windows.h>
+#define sem_t HANDLE
+#define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, INFINITE, NULL)) == NULL)
+#define sem_wait(sem) WaitForSingleObject(*(sem), INFINITE)
+#define sem_post(sem) ReleaseSemaphore(*(sem), 1, NULL)
+#define sem_destroy(sem) CloseHandle(*(sem))
+#else
+#include <semaphore.h>
+#endif
+
+class Fifo {
+public:
+ Fifo(int singleBufferSize, int totalBufferSize, sem_t* readerSem);
+ ~Fifo();
+ int numBytesFilled() const;
+ bool isEmpty() const;
+ bool isFull() const;
+ bool willFill(int additional) const;
+ char* start() const;
+ char* write(int length);
+ void release();
+ char* read(int *const length);
+
+private:
+ int mSingleBufferSize, mWrite, mRead, mReadCommit, mRaggedEnd, mWrapThreshold;
+ sem_t mWaitForSpaceSem;
+ sem_t* mReaderSem;
+ char* mBuffer;
+ bool mEnd;
+};
+
+#endif //__FIFO_H__
diff --git a/tools/gator/daemon/Hwmon.cpp b/tools/gator/daemon/Hwmon.cpp
new file mode 100644
index 000000000000..07925680c1f6
--- /dev/null
+++ b/tools/gator/daemon/Hwmon.cpp
@@ -0,0 +1,326 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "Hwmon.h"
+
+#include "libsensors/sensors.h"
+
+#include "Buffer.h"
+#include "Counter.h"
+#include "Logging.h"
+#include "SessionData.h"
+
+class HwmonCounter {
+public:
+ HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature);
+ ~HwmonCounter();
+
+ HwmonCounter *getNext() const { return next; }
+ int getKey() const { return key; }
+ bool isEnabled() const { return enabled; }
+ const char *getName() const { return name; }
+ const char *getLabel() const { return label; }
+ const char *getTitle() const { return title; }
+ bool isDuplicate() const { return duplicate; }
+ const char *getDisplay() const { return display; }
+ const char *getUnit() const { return unit; }
+ int getModifier() const { return modifier; }
+
+ void setEnabled(const bool enabled) {
+ this->enabled = enabled;
+ // canRead will clear enabled if the counter is not readable
+ canRead();
+ }
+
+ double read();
+ bool canRead();
+
+private:
+ void init(const sensors_chip_name *chip, const sensors_feature *feature);
+
+ HwmonCounter *const next;
+ const int key;
+ int polled : 1,
+ readable : 1,
+ enabled : 1,
+ monotonic: 1,
+ duplicate : 1;
+
+ const sensors_chip_name *chip;
+ const sensors_feature *feature;
+
+ char *name;
+ char *label;
+ const char *title;
+ const char *display;
+ const char *unit;
+ int modifier;
+ double previous_value;
+
+ sensors_subfeature_type input;
+};
+
+HwmonCounter::HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(key), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) {
+
+ int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1;
+ char *chip_name = new char[len];
+ sensors_snprintf_chip_name(chip_name, len, chip);
+
+ len = snprintf(NULL, 0, "hwmon_%s_%d", chip_name, feature->number) + 1;
+ name = new char[len];
+ snprintf(name, len, "hwmon_%s_%d", chip_name, feature->number);
+
+ delete [] chip_name;
+
+ label = sensors_get_label(chip, feature);
+
+ switch (feature->type) {
+ case SENSORS_FEATURE_IN:
+ title = "Voltage";
+ input = SENSORS_SUBFEATURE_IN_INPUT;
+ display = "average";
+ unit = "V";
+ modifier = 1000;
+ monotonic = false;
+ break;
+ case SENSORS_FEATURE_FAN:
+ title = "Fan";
+ input = SENSORS_SUBFEATURE_FAN_INPUT;
+ display = "average";
+ unit = "RPM";
+ modifier = 1;
+ monotonic = false;
+ break;
+ case SENSORS_FEATURE_TEMP:
+ title = "Temperature";
+ input = SENSORS_SUBFEATURE_TEMP_INPUT;
+ display = "maximum";
+ unit = "°C";
+ modifier = 1000;
+ monotonic = false;
+ break;
+ case SENSORS_FEATURE_POWER:
+ title = "Power";
+ input = SENSORS_SUBFEATURE_POWER_INPUT;
+ display = "average";
+ unit = "W";
+ modifier = 1000000;
+ monotonic = false;
+ break;
+ case SENSORS_FEATURE_ENERGY:
+ title = "Energy";
+ input = SENSORS_SUBFEATURE_ENERGY_INPUT;
+ display = "accumulate";
+ unit = "J";
+ modifier = 1000000;
+ monotonic = true;
+ break;
+ case SENSORS_FEATURE_CURR:
+ title = "Current";
+ input = SENSORS_SUBFEATURE_CURR_INPUT;
+ display = "average";
+ unit = "A";
+ modifier = 1000;
+ monotonic = false;
+ break;
+ case SENSORS_FEATURE_HUMIDITY:
+ title = "Humidity";
+ input = SENSORS_SUBFEATURE_HUMIDITY_INPUT;
+ display = "average";
+ unit = "%";
+ modifier = 1000;
+ monotonic = false;
+ break;
+ default:
+ logg->logError(__FILE__, __LINE__, "Unsupported hwmon feature %i", feature->type);
+ handleException();
+ }
+
+ for (HwmonCounter * counter = next; counter != NULL; counter = counter->getNext()) {
+ if (strcmp(label, counter->getLabel()) == 0 && strcmp(title, counter->getTitle()) == 0) {
+ duplicate = true;
+ counter->duplicate = true;
+ break;
+ }
+ }
+}
+
+HwmonCounter::~HwmonCounter() {
+ free((void *)label);
+ delete [] name;
+}
+
+double HwmonCounter::read() {
+ double value;
+ double result;
+ const sensors_subfeature *subfeature;
+
+ // Keep in sync with canRead
+ subfeature = sensors_get_subfeature(chip, feature, input);
+ if (!subfeature) {
+ logg->logError(__FILE__, __LINE__, "No input value for hwmon sensor %s", label);
+ handleException();
+ }
+
+ if (sensors_get_value(chip, subfeature->number, &value) != 0) {
+ logg->logError(__FILE__, __LINE__, "Can't get input value for hwmon sensor %s", label);
+ handleException();
+ }
+
+ result = (monotonic ? value - previous_value : value);
+ previous_value = value;
+
+ return result;
+}
+
+bool HwmonCounter::canRead() {
+ if (!polled) {
+ double value;
+ const sensors_subfeature *subfeature;
+ bool result = true;
+
+ subfeature = sensors_get_subfeature(chip, feature, input);
+ if (!subfeature) {
+ result = false;
+ } else {
+ result = sensors_get_value(chip, subfeature->number, &value) == 0;
+ }
+
+ polled = true;
+ readable = result;
+ }
+
+ enabled &= readable;
+
+ return readable;
+}
+
+Hwmon::Hwmon() : counters(NULL) {
+ int err = sensors_init(NULL);
+ if (err) {
+ logg->logMessage("Failed to initialize libsensors! (%d)", err);
+ return;
+ }
+ sensors_sysfs_no_scaling = 1;
+
+ int chip_nr = 0;
+ const sensors_chip_name *chip;
+ while ((chip = sensors_get_detected_chips(NULL, &chip_nr))) {
+ int feature_nr = 0;
+ const sensors_feature *feature;
+ while ((feature = sensors_get_features(chip, &feature_nr))) {
+ counters = new HwmonCounter(counters, getEventKey(), chip, feature);
+ }
+ }
+}
+
+Hwmon::~Hwmon() {
+ while (counters != NULL) {
+ HwmonCounter * counter = counters;
+ counters = counter->getNext();
+ delete counter;
+ }
+ sensors_cleanup();
+}
+
+HwmonCounter *Hwmon::findCounter(const Counter &counter) const {
+ for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) {
+ if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) {
+ return hwmonCounter;
+ }
+ }
+
+ return NULL;
+}
+
+bool Hwmon::claimCounter(const Counter &counter) const {
+ return findCounter(counter) != NULL;
+}
+
+bool Hwmon::countersEnabled() const {
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (counter->isEnabled()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Hwmon::resetCounters() {
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ counter->setEnabled(false);
+ }
+}
+
+void Hwmon::setupCounter(Counter &counter) {
+ HwmonCounter *const hwmonCounter = findCounter(counter);
+ if (hwmonCounter == NULL) {
+ counter.setEnabled(false);
+ return;
+ }
+ hwmonCounter->setEnabled(true);
+ counter.setKey(hwmonCounter->getKey());
+}
+
+void Hwmon::writeCounters(mxml_node_t *root) const {
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (!counter->canRead()) {
+ continue;
+ }
+ mxml_node_t *node = mxmlNewElement(root, "counter");
+ mxmlElementSetAttr(node, "name", counter->getName());
+ }
+}
+
+void Hwmon::writeEvents(mxml_node_t *root) const {
+ root = mxmlNewElement(root, "category");
+ mxmlElementSetAttr(root, "name", "hwmon");
+
+ char buf[1024];
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (!counter->canRead()) {
+ continue;
+ }
+ mxml_node_t *node = mxmlNewElement(root, "event");
+ mxmlElementSetAttr(node, "counter", counter->getName());
+ mxmlElementSetAttr(node, "title", counter->getTitle());
+ if (counter->isDuplicate()) {
+ mxmlElementSetAttrf(node, "name", "%s (0x%x)", counter->getLabel(), counter->getKey());
+ } else {
+ mxmlElementSetAttr(node, "name", counter->getLabel());
+ }
+ mxmlElementSetAttr(node, "display", counter->getDisplay());
+ mxmlElementSetAttr(node, "units", counter->getUnit());
+ if (counter->getModifier() != 1) {
+ mxmlElementSetAttrf(node, "modifier", "%d", counter->getModifier());
+ }
+ if (strcmp(counter->getDisplay(), "average") == 0 || strcmp(counter->getDisplay(), "maximum") == 0) {
+ mxmlElementSetAttr(node, "average_selection", "yes");
+ }
+ snprintf(buf, sizeof(buf), "libsensors %s sensor %s (%s)", counter->getTitle(), counter->getLabel(), counter->getName());
+ mxmlElementSetAttr(node, "description", buf);
+ }
+}
+
+void Hwmon::start() {
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (!counter->isEnabled()) {
+ continue;
+ }
+ counter->read();
+ }
+}
+
+void Hwmon::read(Buffer * const buffer) {
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (!counter->isEnabled()) {
+ continue;
+ }
+ buffer->event(counter->getKey(), counter->read());
+ }
+}
diff --git a/tools/gator/daemon/Hwmon.h b/tools/gator/daemon/Hwmon.h
new file mode 100644
index 000000000000..35981dc3d9ad
--- /dev/null
+++ b/tools/gator/daemon/Hwmon.h
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef HWMON_H
+#define HWMON_H
+
+#include "Driver.h"
+
+class Buffer;
+class HwmonCounter;
+
+class Hwmon : public Driver {
+public:
+ Hwmon();
+ ~Hwmon();
+
+ bool claimCounter(const Counter &counter) const;
+ bool countersEnabled() const;
+ void resetCounters();
+ void setupCounter(Counter &counter);
+
+ void writeCounters(mxml_node_t *root) const;
+ void writeEvents(mxml_node_t *root) const;
+
+ void start();
+ void read(Buffer * buffer);
+
+private:
+ HwmonCounter *findCounter(const Counter &counter) const;
+
+ HwmonCounter *counters;
+};
+
+#endif // HWMON_H
diff --git a/tools/gator/daemon/KMod.cpp b/tools/gator/daemon/KMod.cpp
new file mode 100644
index 000000000000..559297fe2274
--- /dev/null
+++ b/tools/gator/daemon/KMod.cpp
@@ -0,0 +1,102 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "KMod.h"
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#include "Collector.h"
+#include "ConfigurationXML.h"
+#include "Counter.h"
+#include "Logging.h"
+
+// Claim all the counters in /dev/gator/events
+bool KMod::claimCounter(const Counter &counter) const {
+ char text[128];
+ snprintf(text, sizeof(text), "/dev/gator/events/%s", counter.getType());
+ return access(text, F_OK) == 0;
+}
+
+void KMod::resetCounters() {
+ char base[128];
+ char text[128];
+
+ // Initialize all perf counters in the driver, i.e. set enabled to zero
+ struct dirent *ent;
+ DIR* dir = opendir("/dev/gator/events");
+ if (dir) {
+ while ((ent = readdir(dir)) != NULL) {
+ // skip hidden files, current dir, and parent dir
+ if (ent->d_name[0] == '.')
+ continue;
+ snprintf(base, sizeof(base), "/dev/gator/events/%s", ent->d_name);
+ snprintf(text, sizeof(text), "%s/enabled", base);
+ Collector::writeDriver(text, 0);
+ snprintf(text, sizeof(text), "%s/count", base);
+ Collector::writeDriver(text, 0);
+ }
+ closedir(dir);
+ }
+}
+
+void KMod::setupCounter(Counter &counter) {
+ char base[128];
+ char text[128];
+ snprintf(base, sizeof(base), "/dev/gator/events/%s", counter.getType());
+
+ snprintf(text, sizeof(text), "%s/enabled", base);
+ int enabled = true;
+ if (Collector::writeReadDriver(text, &enabled) || !enabled) {
+ counter.setEnabled(false);
+ return;
+ }
+
+ snprintf(text, sizeof(text), "%s/key", base);
+ int key = 0;
+ Collector::readIntDriver(text, &key);
+ counter.setKey(key);
+
+ snprintf(text, sizeof(text), "%s/event", base);
+ Collector::writeDriver(text, counter.getEvent());
+ snprintf(text, sizeof(text), "%s/count", base);
+ if (access(text, F_OK) == 0) {
+ int count = counter.getCount();
+ if (Collector::writeReadDriver(text, &count) && counter.getCount() > 0) {
+ logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount());
+ handleException();
+ }
+ counter.setCount(count);
+ } else if (counter.getCount() > 0) {
+ ConfigurationXML::remove();
+ logg->logError(__FILE__, __LINE__, "Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y. The invalid configuration.xml has been removed.\n");
+ handleException();
+ }
+}
+
+void KMod::writeCounters(mxml_node_t *root) const {
+ struct dirent *ent;
+ mxml_node_t *counter;
+
+ // counters.xml is simply a file listing of /dev/gator/events
+ DIR* dir = opendir("/dev/gator/events");
+ if (dir == NULL) {
+ logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events");
+ handleException();
+ }
+
+ while ((ent = readdir(dir)) != NULL) {
+ // skip hidden files, current dir, and parent dir
+ if (ent->d_name[0] == '.')
+ continue;
+ counter = mxmlNewElement(root, "counter");
+ mxmlElementSetAttr(counter, "name", ent->d_name);
+ }
+ closedir(dir);
+}
diff --git a/tools/gator/daemon/KMod.h b/tools/gator/daemon/KMod.h
new file mode 100644
index 000000000000..797426290dfc
--- /dev/null
+++ b/tools/gator/daemon/KMod.h
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) ARM Limited 2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef KMOD_H
+#define KMOD_H
+
+#include "Driver.h"
+
+// Driver for the gator kernel module
+class KMod : public Driver {
+public:
+ KMod() {}
+ ~KMod() {}
+
+ bool claimCounter(const Counter &counter) const;
+ void resetCounters();
+ void setupCounter(Counter &counter);
+
+ void writeCounters(mxml_node_t *root) const;
+};
+
+#endif // KMOD_H
diff --git a/tools/gator/daemon/LocalCapture.cpp b/tools/gator/daemon/LocalCapture.cpp
new file mode 100644
index 000000000000..3235a34ae9c7
--- /dev/null
+++ b/tools/gator/daemon/LocalCapture.cpp
@@ -0,0 +1,129 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "LocalCapture.h"
+#include "SessionData.h"
+#include "Logging.h"
+#include "OlyUtility.h"
+#include "EventsXML.h"
+
+LocalCapture::LocalCapture() {}
+
+LocalCapture::~LocalCapture() {}
+
+void LocalCapture::createAPCDirectory(char* target_path) {
+ gSessionData->mAPCDir = createUniqueDirectory(target_path, ".apc");
+ if ((removeDirAndAllContents(gSessionData->mAPCDir) != 0 || mkdir(gSessionData->mAPCDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)) {
+ logg->logError(__FILE__, __LINE__, "Unable to create directory %s", gSessionData->mAPCDir);
+ handleException();
+ }
+}
+
+void LocalCapture::write(char* string) {
+ char file[PATH_MAX];
+
+ // Set full path
+ snprintf(file, PATH_MAX, "%s/session.xml", gSessionData->mAPCDir);
+
+ // Write the file
+ if (util->writeToDisk(file, string) < 0) {
+ logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file);
+ handleException();
+ }
+
+ // Write events XML
+ EventsXML eventsXML;
+ eventsXML.write(gSessionData->mAPCDir);
+}
+
+char* LocalCapture::createUniqueDirectory(const char* initialPath, const char* ending) {
+ char* output;
+ char path[PATH_MAX];
+
+ // Ensure the path is an absolute path, i.e. starts with a slash
+ if (initialPath == 0 || strlen(initialPath) == 0) {
+ logg->logError(__FILE__, __LINE__, "Missing -o command line option required for a local capture.");
+ handleException();
+ } else if (initialPath[0] != '/') {
+ if (getcwd(path, PATH_MAX) == 0) {
+ logg->logMessage("Unable to retrieve the current working directory");
+ }
+ strncat(path, "/", PATH_MAX - strlen(path) - 1);
+ strncat(path, initialPath, PATH_MAX - strlen(path) - 1);
+ } else {
+ strncpy(path, initialPath, PATH_MAX);
+ path[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string
+ }
+
+ // Add ending if it is not already there
+ if (strcmp(&path[strlen(path) - strlen(ending)], ending) != 0) {
+ strncat(path, ending, PATH_MAX - strlen(path) - 1);
+ }
+
+ output = strdup(path);
+
+ return output;
+}
+
+int LocalCapture::removeDirAndAllContents(char* path) {
+ int error = 0;
+ struct stat mFileInfo;
+ // Does the path exist?
+ if (stat(path, &mFileInfo) == 0) {
+ // Is it a directory?
+ if (mFileInfo.st_mode & S_IFDIR) {
+ DIR * dir = opendir(path);
+ dirent* entry = readdir(dir);
+ while (entry) {
+ if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
+ char* newpath = (char*)malloc(strlen(path) + strlen(entry->d_name) + 2);
+ sprintf(newpath, "%s/%s", path, entry->d_name);
+ error = removeDirAndAllContents(newpath);
+ free(newpath);
+ if (error) {
+ break;
+ }
+ }
+ entry = readdir(dir);
+ }
+ closedir(dir);
+ if (error == 0) {
+ error = rmdir(path);
+ }
+ } else {
+ error = remove(path);
+ }
+ }
+ return error;
+}
+
+void LocalCapture::copyImages(ImageLinkList* ptr) {
+ char dstfilename[PATH_MAX];
+
+ while (ptr) {
+ strncpy(dstfilename, gSessionData->mAPCDir, PATH_MAX);
+ dstfilename[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string
+ if (gSessionData->mAPCDir[strlen(gSessionData->mAPCDir) - 1] != '/') {
+ strncat(dstfilename, "/", PATH_MAX - strlen(dstfilename) - 1);
+ }
+ strncat(dstfilename, util->getFilePart(ptr->path), PATH_MAX - strlen(dstfilename) - 1);
+ if (util->copyFile(ptr->path, dstfilename)) {
+ logg->logMessage("copied file %s to %s", ptr->path, dstfilename);
+ } else {
+ logg->logMessage("copy of file %s to %s failed", ptr->path, dstfilename);
+ }
+
+ ptr = ptr->next;
+ }
+}
diff --git a/tools/gator/daemon/LocalCapture.h b/tools/gator/daemon/LocalCapture.h
new file mode 100644
index 000000000000..8042d6a8dc37
--- /dev/null
+++ b/tools/gator/daemon/LocalCapture.h
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LOCAL_CAPTURE_H__
+#define __LOCAL_CAPTURE_H__
+
+struct ImageLinkList;
+
+class LocalCapture {
+public:
+ LocalCapture();
+ ~LocalCapture();
+ void write(char* string);
+ void copyImages(ImageLinkList* ptr);
+ void createAPCDirectory(char* target_path);
+private:
+ char* createUniqueDirectory(const char* path, const char* ending);
+ int removeDirAndAllContents(char* path);
+};
+
+#endif //__LOCAL_CAPTURE_H__
diff --git a/tools/gator/daemon/Logging.cpp b/tools/gator/daemon/Logging.cpp
new file mode 100644
index 000000000000..5fd45b54f90a
--- /dev/null
+++ b/tools/gator/daemon/Logging.cpp
@@ -0,0 +1,78 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#ifdef WIN32
+#define MUTEX_INIT() mLoggingMutex = CreateMutex(NULL, false, NULL);
+#define MUTEX_LOCK() WaitForSingleObject(mLoggingMutex, 0xFFFFFFFF);
+#define MUTEX_UNLOCK() ReleaseMutex(mLoggingMutex);
+#define snprintf _snprintf
+#else
+#include <pthread.h>
+#define MUTEX_INIT() pthread_mutex_init(&mLoggingMutex, NULL)
+#define MUTEX_LOCK() pthread_mutex_lock(&mLoggingMutex)
+#define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex)
+#endif
+
+#include "Logging.h"
+
+// Global thread-safe logging
+Logging* logg = NULL;
+
+Logging::Logging(bool debug) {
+ mDebug = debug;
+ MUTEX_INIT();
+
+ strcpy(mErrBuf, "Unknown Error");
+ strcpy(mLogBuf, "Unknown Message");
+}
+
+Logging::~Logging() {
+}
+
+void Logging::logError(const char* file, int line, const char* fmt, ...) {
+ va_list args;
+
+ MUTEX_LOCK();
+ if (mDebug) {
+ snprintf(mErrBuf, sizeof(mErrBuf), "ERROR[%s:%d]: ", file, line);
+ } else {
+ mErrBuf[0] = 0;
+ }
+
+ va_start(args, fmt);
+ vsnprintf(mErrBuf + strlen(mErrBuf), sizeof(mErrBuf) - 2 - strlen(mErrBuf), fmt, args); // subtract 2 for \n and \0
+ va_end(args);
+
+ if (strlen(mErrBuf) > 0) {
+ strcat(mErrBuf, "\n");
+ }
+ MUTEX_UNLOCK();
+}
+
+void Logging::logMessage(const char* fmt, ...) {
+ if (mDebug) {
+ va_list args;
+
+ MUTEX_LOCK();
+ strcpy(mLogBuf, "INFO: ");
+
+ va_start(args, fmt);
+ vsnprintf(mLogBuf + strlen(mLogBuf), sizeof(mLogBuf) - 2 - strlen(mLogBuf), fmt, args); // subtract 2 for \n and \0
+ va_end(args);
+ strcat(mLogBuf, "\n");
+
+ fprintf(stdout, "%s", mLogBuf);
+ fflush(stdout);
+ MUTEX_UNLOCK();
+ }
+}
diff --git a/tools/gator/daemon/Logging.h b/tools/gator/daemon/Logging.h
new file mode 100644
index 000000000000..8f960de27bf3
--- /dev/null
+++ b/tools/gator/daemon/Logging.h
@@ -0,0 +1,47 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LOGGING_H__
+#define __LOGGING_H__
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+#define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord"
+
+class Logging {
+public:
+ Logging(bool debug);
+ ~Logging();
+ void logError(const char* file, int line, const char* fmt, ...);
+ void logMessage(const char* fmt, ...);
+ char* getLastError() {return mErrBuf;}
+ char* getLastMessage() {return mLogBuf;}
+
+private:
+ char mErrBuf[4096]; // Arbitrarily large buffer to hold a string
+ char mLogBuf[4096]; // Arbitrarily large buffer to hold a string
+ bool mDebug;
+#ifdef WIN32
+ HANDLE mLoggingMutex;
+#else
+ pthread_mutex_t mLoggingMutex;
+#endif
+};
+
+extern Logging* logg;
+
+extern void handleException() __attribute__ ((noreturn));
+
+#endif //__LOGGING_H__
diff --git a/tools/gator/daemon/Makefile b/tools/gator/daemon/Makefile
new file mode 100644
index 000000000000..24ee94045470
--- /dev/null
+++ b/tools/gator/daemon/Makefile
@@ -0,0 +1,25 @@
+#
+# Makefile for ARM Streamline - Gator Daemon
+#
+
+# Uncomment and define CROSS_COMPILE if it is not already defined
+# CROSS_COMPILE=/path/to/cross-compiler/arm-linux-gnueabihf-
+# NOTE: This toolchain uses the hardfloat abi by default. For non-hardfloat
+# targets run 'make SOFTFLOAT=1 SYSROOT=/path/to/sysroot', see
+# README_Streamline.txt for more details
+
+CPP = $(CROSS_COMPILE)g++
+GCC = $(CROSS_COMPILE)gcc
+
+# -mthumb-interwork is required for interworking to ARM or Thumb stdlibc
+CFLAGS += -mthumb-interwork
+
+ifeq ($(SOFTFLOAT),1)
+ CFLAGS += -marm -march=armv4t -mfloat-abi=soft
+ LDFLAGS += -marm -march=armv4t -mfloat-abi=soft
+endif
+ifneq ($(SYSROOT),)
+ LDFLAGS += --sysroot=$(SYSROOT)
+endif
+
+include common.mk
diff --git a/tools/gator/daemon/Makefile_aarch64 b/tools/gator/daemon/Makefile_aarch64
new file mode 100644
index 000000000000..10b4b4a71ab1
--- /dev/null
+++ b/tools/gator/daemon/Makefile_aarch64
@@ -0,0 +1,15 @@
+#
+# Makefile for ARM Streamline - Gator Daemon
+# make -f Makefile_aarch64
+#
+
+# Uncomment and define CROSS_COMPILE if it is not already defined
+# CROSS_COMPILE=/path/to/cross-compiler/arm-linux-gnueabihf-
+# NOTE: This toolchain uses the hardfloat abi by default. For non-hardfloat
+# targets it is necessary to add options
+# '-marm -march=armv4t -mfloat-abi=soft'.
+
+CPP = $(CROSS_COMPILE)g++
+GCC = $(CROSS_COMPILE)gcc
+
+include common.mk
diff --git a/tools/gator/daemon/OlySocket.cpp b/tools/gator/daemon/OlySocket.cpp
new file mode 100644
index 000000000000..132510df584a
--- /dev/null
+++ b/tools/gator/daemon/OlySocket.cpp
@@ -0,0 +1,261 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "OlySocket.h"
+
+#include <stdio.h>
+#ifdef WIN32
+#include <Winsock2.h>
+#else
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#endif
+
+#include "Logging.h"
+
+#ifdef WIN32
+#define CLOSE_SOCKET(x) closesocket(x)
+#define SHUTDOWN_RX_TX SD_BOTH
+#define snprintf _snprintf
+#else
+#define CLOSE_SOCKET(x) close(x)
+#define SHUTDOWN_RX_TX SHUT_RDWR
+#endif
+
+OlySocket::OlySocket(int port, bool multiple) {
+#ifdef WIN32
+ WSADATA wsaData;
+ if (WSAStartup(0x0202, &wsaData) != 0) {
+ logg->logError(__FILE__, __LINE__, "Windows socket initialization failed");
+ handleException();
+ }
+#endif
+
+ if (multiple) {
+ createServerSocket(port);
+ } else {
+ createSingleServerConnection(port);
+ }
+}
+
+OlySocket::OlySocket(int port, char* host) {
+ mFDServer = 0;
+ createClientSocket(host, port);
+}
+
+OlySocket::~OlySocket() {
+ if (mSocketID > 0) {
+ CLOSE_SOCKET(mSocketID);
+ }
+}
+
+void OlySocket::shutdownConnection() {
+ // Shutdown is primarily used to unblock other threads that are blocking on send/receive functions
+ shutdown(mSocketID, SHUTDOWN_RX_TX);
+}
+
+void OlySocket::closeSocket() {
+ // Used for closing an accepted socket but keeping the server socket active
+ if (mSocketID > 0) {
+ CLOSE_SOCKET(mSocketID);
+ mSocketID = -1;
+ }
+}
+
+void OlySocket::closeServerSocket() {
+ if (CLOSE_SOCKET(mFDServer) != 0) {
+ logg->logError(__FILE__, __LINE__, "Failed to close server socket.");
+ handleException();
+ }
+ mFDServer = 0;
+}
+
+void OlySocket::createClientSocket(char* hostname, int portno) {
+#ifdef WIN32
+ // TODO: Implement for Windows
+#else
+ char buf[32];
+ struct addrinfo hints, *res, *res0;
+
+ snprintf(buf, sizeof(buf), "%d", portno);
+ mSocketID = -1;
+ memset((void*)&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ if (getaddrinfo(hostname, buf, &hints, &res0)) {
+ logg->logError(__FILE__, __LINE__, "Client socket failed to get address info for %s", hostname);
+ handleException();
+ }
+ for (res=res0; res!=NULL; res = res->ai_next) {
+ if ( res->ai_family != PF_INET || res->ai_socktype != SOCK_STREAM ) {
+ continue;
+ }
+ mSocketID = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (mSocketID < 0) {
+ continue;
+ }
+ if (connect(mSocketID, res->ai_addr, res->ai_addrlen) < 0) {
+ close(mSocketID);
+ mSocketID = -1;
+ }
+ if (mSocketID > 0) {
+ break;
+ }
+ }
+ freeaddrinfo(res0);
+ if (mSocketID <= 0) {
+ logg->logError(__FILE__, __LINE__, "Could not connect to client socket. Ensure ARM Streamline is running.");
+ handleException();
+ }
+#endif
+}
+
+void OlySocket::createSingleServerConnection(int port) {
+ createServerSocket(port);
+
+ mSocketID = acceptConnection();
+ closeServerSocket();
+}
+
+void OlySocket::createServerSocket(int port) {
+ // Create socket
+ mFDServer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (mFDServer < 0) {
+ logg->logError(__FILE__, __LINE__, "Error creating server socket");
+ handleException();
+ }
+
+ // Enable address reuse, another solution would be to create the server socket once and only close it when the object exits
+ int on = 1;
+ if (setsockopt(mFDServer, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) {
+ logg->logError(__FILE__, __LINE__, "Setting server socket options failed");
+ handleException();
+ }
+
+ // Create sockaddr_in structure, ensuring non-populated fields are zero
+ struct sockaddr_in sockaddr;
+ memset((void*)&sockaddr, 0, sizeof(struct sockaddr_in));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(port);
+ sockaddr.sin_addr.s_addr = INADDR_ANY;
+
+ // Bind the socket to an address
+ if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
+ logg->logError(__FILE__, __LINE__, "Binding of server socket failed.\nIs an instance already running?");
+ handleException();
+ }
+
+ // Listen for connections on this socket
+ if (listen(mFDServer, 1) < 0) {
+ logg->logError(__FILE__, __LINE__, "Listening of server socket failed");
+ handleException();
+ }
+}
+
+// mSocketID is always set to the most recently accepted connection
+// The user of this class should maintain the different socket connections, e.g. by forking the process
+int OlySocket::acceptConnection() {
+ if (mFDServer <= 0) {
+ logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket");
+ handleException();
+ }
+
+ // Accept a connection, note that this call blocks until a client connects
+ mSocketID = accept(mFDServer, NULL, NULL);
+ if (mSocketID < 0) {
+ logg->logError(__FILE__, __LINE__, "Socket acceptance failed");
+ handleException();
+ }
+ return mSocketID;
+}
+
+void OlySocket::send(char* buffer, int size) {
+ if (size <= 0 || buffer == NULL) {
+ return;
+ }
+
+ while (size > 0) {
+ int n = ::send(mSocketID, buffer, size, 0);
+ if (n < 0) {
+ logg->logError(__FILE__, __LINE__, "Socket send error");
+ handleException();
+ }
+ size -= n;
+ buffer += n;
+ }
+}
+
+// Returns the number of bytes received
+int OlySocket::receive(char* buffer, int size) {
+ if (size <= 0 || buffer == NULL) {
+ return 0;
+ }
+
+ int bytes = recv(mSocketID, buffer, size, 0);
+ if (bytes < 0) {
+ logg->logError(__FILE__, __LINE__, "Socket receive error");
+ handleException();
+ } else if (bytes == 0) {
+ logg->logMessage("Socket disconnected");
+ return -1;
+ }
+ return bytes;
+}
+
+// Receive exactly size bytes of data. Note, this function will block until all bytes are received
+int OlySocket::receiveNBytes(char* buffer, int size) {
+ int bytes = 0;
+ while (size > 0 && buffer != NULL) {
+ bytes = recv(mSocketID, buffer, size, 0);
+ if (bytes < 0) {
+ logg->logError(__FILE__, __LINE__, "Socket receive error");
+ handleException();
+ } else if (bytes == 0) {
+ logg->logMessage("Socket disconnected");
+ return -1;
+ }
+ buffer += bytes;
+ size -= bytes;
+ }
+ return bytes;
+}
+
+// Receive data until a carriage return, line feed, or null is encountered, or the buffer fills
+int OlySocket::receiveString(char* buffer, int size) {
+ int bytes_received = 0;
+ bool found = false;
+
+ if (buffer == 0) {
+ return 0;
+ }
+
+ while (!found && bytes_received < size) {
+ // Receive a single character
+ int bytes = recv(mSocketID, &buffer[bytes_received], 1, 0);
+ if (bytes < 0) {
+ logg->logError(__FILE__, __LINE__, "Socket receive error");
+ handleException();
+ } else if (bytes == 0) {
+ logg->logMessage("Socket disconnected");
+ return -1;
+ }
+
+ // Replace carriage returns and line feeds with zero
+ if (buffer[bytes_received] == '\n' || buffer[bytes_received] == '\r' || buffer[bytes_received] == '\0') {
+ buffer[bytes_received] = '\0';
+ found = true;
+ }
+
+ bytes_received++;
+ }
+
+ return bytes_received;
+}
diff --git a/tools/gator/daemon/OlySocket.h b/tools/gator/daemon/OlySocket.h
new file mode 100644
index 000000000000..5bab7d1f4cc4
--- /dev/null
+++ b/tools/gator/daemon/OlySocket.h
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __OLY_SOCKET_H__
+#define __OLY_SOCKET_H__
+
+#include <string.h>
+
+class OlySocket {
+public:
+ OlySocket(int port, bool multipleConnections = false);
+ OlySocket(int port, char* hostname);
+ ~OlySocket();
+ int acceptConnection();
+ void closeSocket();
+ void closeServerSocket();
+ void shutdownConnection();
+ void send(char* buffer, int size);
+ void sendString(const char* string) {send((char*)string, strlen(string));}
+ int receive(char* buffer, int size);
+ int receiveNBytes(char* buffer, int size);
+ int receiveString(char* buffer, int size);
+ int getSocketID() {return mSocketID;}
+private:
+ int mSocketID, mFDServer;
+ void createClientSocket(char* hostname, int port);
+ void createSingleServerConnection(int port);
+ void createServerSocket(int port);
+};
+
+#endif //__OLY_SOCKET_H__
diff --git a/tools/gator/daemon/OlyUtility.cpp b/tools/gator/daemon/OlyUtility.cpp
new file mode 100644
index 000000000000..0b22d6ebd027
--- /dev/null
+++ b/tools/gator/daemon/OlyUtility.cpp
@@ -0,0 +1,227 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "OlyUtility.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#if defined(WIN32)
+#include <windows.h>
+#elif defined(__linux__)
+#include <unistd.h>
+#elif defined(DARWIN)
+#include <mach-o/dyld.h>
+#endif
+
+OlyUtility* util = NULL;
+
+bool OlyUtility::stringToBool(const char* string, bool defValue) {
+ char value[32];
+
+ if (string == NULL) {
+ return defValue;
+ }
+
+ strncpy(value, string, sizeof(value));
+ if (value[0] == 0) {
+ return defValue;
+ }
+ value[sizeof(value) - 1] = 0; // strncpy does not guarantee a null-terminated string
+
+ // Convert to lowercase
+ int i = 0;
+ while (value[i]) {
+ value[i] = tolower(value[i]);
+ i++;
+ }
+
+ if (strcmp(value, "true") == 0 || strcmp(value, "yes") == 0 || strcmp(value, "1") == 0 || strcmp(value, "on") == 0) {
+ return true;
+ } else if (strcmp(value, "false") == 0 || strcmp(value, "no") == 0 || strcmp(value, "0") == 0 || strcmp(value, "off") == 0) {
+ return false;
+ } else {
+ return defValue;
+ }
+}
+
+void OlyUtility::stringToLower(char* string) {
+ if (string == NULL) {
+ return;
+ }
+
+ while (*string) {
+ *string = tolower(*string);
+ string++;
+ }
+}
+
+// Modifies fullpath with the path part including the trailing path separator
+int OlyUtility::getApplicationFullPath(char* fullpath, int sizeOfPath) {
+ memset(fullpath, 0, sizeOfPath);
+#if defined(WIN32)
+ int length = GetModuleFileName(NULL, fullpath, sizeOfPath);
+#elif defined(__linux__)
+ int length = readlink("/proc/self/exe", fullpath, sizeOfPath);
+#elif defined(DARWIN)
+ uint32_t length_u = (uint32_t)sizeOfPath;
+ int length = sizeOfPath;
+ if (_NSGetExecutablePath(fullpath, &length_u) == 0) {
+ length = strlen(fullpath);
+ }
+#endif
+
+ if (length == sizeOfPath) {
+ return -1;
+ }
+
+ fullpath[length] = 0;
+ getPathPart(fullpath);
+
+ return 0;
+}
+
+char* OlyUtility::readFromDisk(const char* file, unsigned int *size, bool appendNull) {
+ // Open the file
+ FILE* pFile = fopen(file, "rb");
+ if (pFile==NULL) {
+ return NULL;
+ }
+
+ // Obtain file size
+ fseek(pFile , 0 , SEEK_END);
+ unsigned int lSize = ftell(pFile);
+ rewind(pFile);
+
+ // Allocate memory to contain the whole file
+ char* buffer = (char*)malloc(lSize + (int)appendNull);
+ if (buffer == NULL) {
+ fclose(pFile);
+ return NULL;
+ }
+
+ // Copy the file into the buffer
+ if (fread(buffer, 1, lSize, pFile) != lSize) {
+ free(buffer);
+ fclose(pFile);
+ return NULL;
+ }
+
+ // Terminate
+ fclose(pFile);
+
+ if (appendNull) {
+ buffer[lSize] = 0;
+ }
+
+ if (size) {
+ *size = lSize;
+ }
+
+ return buffer;
+}
+
+int OlyUtility::writeToDisk(const char* path, const char* data) {
+ // Open the file
+ FILE* pFile = fopen(path, "wb");
+ if (pFile == NULL) {
+ return -1;
+ }
+
+ // Write the data to disk
+ if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) {
+ fclose(pFile);
+ return -1;
+ }
+
+ // Terminate
+ fclose(pFile);
+ return 0;
+}
+
+int OlyUtility::appendToDisk(const char* path, const char* data) {
+ // Open the file
+ FILE* pFile = fopen(path, "a");
+ if (pFile == NULL) {
+ return -1;
+ }
+
+ // Write the data to disk
+ if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) {
+ fclose(pFile);
+ return -1;
+ }
+
+ // Terminate
+ fclose(pFile);
+ return 0;
+}
+
+/**
+ * Copies the srcFile into dstFile in 1kB chunks.
+ * The dstFile will be overwritten if it exists.
+ * 0 is returned on an error; otherwise 1.
+ */
+#define TRANSFER_SIZE 1024
+int OlyUtility::copyFile(const char* srcFile, const char* dstFile) {
+ char buffer[TRANSFER_SIZE];
+ FILE * f_src = fopen(srcFile,"rb");
+ if (!f_src) {
+ return 0;
+ }
+ FILE * f_dst = fopen(dstFile,"wb");
+ if (!f_dst) {
+ fclose(f_src);
+ return 0;
+ }
+ while (!feof(f_src)) {
+ int num_bytes_read = fread(buffer, 1, TRANSFER_SIZE, f_src);
+ if (num_bytes_read < TRANSFER_SIZE && !feof(f_src)) {
+ fclose(f_src);
+ fclose(f_dst);
+ return 0;
+ }
+ int num_bytes_written = fwrite(buffer, 1, num_bytes_read, f_dst);
+ if (num_bytes_written != num_bytes_read) {
+ fclose(f_src);
+ fclose(f_dst);
+ return 0;
+ }
+ }
+ fclose(f_src);
+ fclose(f_dst);
+ return 1;
+}
+
+const char* OlyUtility::getFilePart(const char* path) {
+ const char* last_sep = strrchr(path, PATH_SEPARATOR);
+
+ // in case path is not a full path
+ if (last_sep == NULL) {
+ return path;
+ }
+
+ return last_sep++;
+}
+
+// getPathPart may modify the contents of path
+// returns the path including the trailing path separator
+char* OlyUtility::getPathPart(char* path) {
+ char* last_sep = strrchr(path, PATH_SEPARATOR);
+
+ // in case path is not a full path
+ if (last_sep == NULL) {
+ return 0;
+ }
+ last_sep++;
+ *last_sep = 0;
+
+ return (path);
+}
diff --git a/tools/gator/daemon/OlyUtility.h b/tools/gator/daemon/OlyUtility.h
new file mode 100644
index 000000000000..abab0a510a7c
--- /dev/null
+++ b/tools/gator/daemon/OlyUtility.h
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef OLY_UTILITY_H
+#define OLY_UTILITY_H
+
+#include <stddef.h>
+
+#ifdef WIN32
+#define PATH_SEPARATOR '\\'
+#define CAIMAN_PATH_MAX MAX_PATH
+#define snprintf _snprintf
+#else
+#include <limits.h>
+#define PATH_SEPARATOR '/'
+#define CAIMAN_PATH_MAX PATH_MAX
+#endif
+
+class OlyUtility {
+public:
+ OlyUtility() {};
+ ~OlyUtility() {};
+ bool stringToBool(const char* string, bool defValue);
+ void stringToLower(char* string);
+ int getApplicationFullPath(char* path, int sizeOfPath);
+ char* readFromDisk(const char* file, unsigned int *size = NULL, bool appendNull = true);
+ int writeToDisk(const char* path, const char* file);
+ int appendToDisk(const char* path, const char* file);
+ int copyFile(const char* srcFile, const char* dstFile);
+ const char* getFilePart(const char* path);
+ char* getPathPart(char* path);
+private:
+};
+
+extern OlyUtility* util;
+
+#endif // OLY_UTILITY_H
diff --git a/tools/gator/daemon/Sender.cpp b/tools/gator/daemon/Sender.cpp
new file mode 100644
index 000000000000..8eb348ff3a06
--- /dev/null
+++ b/tools/gator/daemon/Sender.cpp
@@ -0,0 +1,135 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "Sender.h"
+#include "Logging.h"
+#include "OlySocket.h"
+#include "SessionData.h"
+
+Sender::Sender(OlySocket* socket) {
+ mDataFile = NULL;
+ mDataSocket = NULL;
+
+ // Set up the socket connection
+ if (socket) {
+ char streamline[64] = {0};
+ mDataSocket = socket;
+
+ // Receive magic sequence - can wait forever
+ // Streamline will send data prior to the magic sequence for legacy support, which should be ignored for v4+
+ while (strcmp("STREAMLINE", streamline) != 0) {
+ if (mDataSocket->receiveString(streamline, sizeof(streamline)) == -1) {
+ logg->logError(__FILE__, __LINE__, "Socket disconnected");
+ handleException();
+ }
+ }
+
+ // Send magic sequence - must be done first, after which error messages can be sent
+ char magic[32];
+ snprintf(magic, 32, "GATOR %i\n", PROTOCOL_VERSION);
+ mDataSocket->send(magic, strlen(magic));
+
+ gSessionData->mWaitingOnCommand = true;
+ logg->logMessage("Completed magic sequence");
+ }
+
+ pthread_mutex_init(&mSendMutex, NULL);
+}
+
+Sender::~Sender() {
+ delete mDataSocket;
+ mDataSocket = NULL;
+ if (mDataFile) {
+ fclose(mDataFile);
+ }
+}
+
+void Sender::createDataFile(char* apcDir) {
+ if (apcDir == NULL) {
+ return;
+ }
+
+ mDataFileName = (char*)malloc(strlen(apcDir) + 12);
+ sprintf(mDataFileName, "%s/0000000000", apcDir);
+ mDataFile = fopen(mDataFileName, "wb");
+ if (!mDataFile) {
+ logg->logError(__FILE__, __LINE__, "Failed to open binary file: %s", mDataFileName);
+ handleException();
+ }
+}
+
+template<typename T>
+inline T min(const T a, const T b) {
+ return (a < b ? a : b);
+}
+
+void Sender::writeData(const char* data, int length, int type) {
+ if (length < 0 || (data == NULL && length > 0)) {
+ return;
+ }
+
+ // Multiple threads call writeData()
+ pthread_mutex_lock(&mSendMutex);
+
+ // Send data over the socket connection
+ if (mDataSocket) {
+ // Start alarm
+ const int alarmDuration = 8;
+ alarm(alarmDuration);
+
+ // Send data over the socket, sending the type and size first
+ logg->logMessage("Sending data with length %d", length);
+ if (type != RESPONSE_APC_DATA) {
+ // type and length already added by the Collector for apc data
+ unsigned char header[5];
+ header[0] = type;
+ header[1] = (length >> 0) & 0xff;
+ header[2] = (length >> 8) & 0xff;
+ header[3] = (length >> 16) & 0xff;
+ header[4] = (length >> 24) & 0xff;
+ mDataSocket->send((char*)&header, sizeof(header));
+ }
+
+ // 100Kbits/sec * alarmDuration sec / 8 bits/byte
+ const int chunkSize = 100*1000 * alarmDuration / 8;
+ int pos = 0;
+ while (true) {
+ mDataSocket->send((char*)data + pos, min(length - pos, chunkSize));
+ pos += chunkSize;
+ if (pos >= length) {
+ break;
+ }
+
+ // Reset the alarm
+ alarm(alarmDuration);
+ logg->logMessage("Resetting the alarm");
+ }
+
+ // Stop alarm
+ alarm(0);
+ }
+
+ // Write data to disk as long as it is not meta data
+ if (mDataFile && type == RESPONSE_APC_DATA) {
+ logg->logMessage("Writing data with length %d", length);
+ // Send data to the data file
+ if (fwrite(data, 1, length, mDataFile) != (unsigned int)length) {
+ logg->logError(__FILE__, __LINE__, "Failed writing binary file %s", mDataFileName);
+ handleException();
+ }
+ }
+
+ pthread_mutex_unlock(&mSendMutex);
+}
diff --git a/tools/gator/daemon/Sender.h b/tools/gator/daemon/Sender.h
new file mode 100644
index 000000000000..8f23361a5def
--- /dev/null
+++ b/tools/gator/daemon/Sender.h
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __SENDER_H__
+#define __SENDER_H__
+
+#include <stdio.h>
+#include <pthread.h>
+
+class OlySocket;
+
+enum {
+ RESPONSE_XML = 1,
+ RESPONSE_APC_DATA = 3,
+ RESPONSE_ACK = 4,
+ RESPONSE_NAK = 5,
+ RESPONSE_ERROR = 0xFF
+};
+
+class Sender {
+public:
+ Sender(OlySocket* socket);
+ ~Sender();
+ void writeData(const char* data, int length, int type);
+ void createDataFile(char* apcDir);
+private:
+ OlySocket* mDataSocket;
+ FILE* mDataFile;
+ char* mDataFileName;
+ pthread_mutex_t mSendMutex;
+};
+
+#endif //__SENDER_H__
diff --git a/tools/gator/daemon/SessionData.cpp b/tools/gator/daemon/SessionData.cpp
new file mode 100644
index 000000000000..4068d4e957f0
--- /dev/null
+++ b/tools/gator/daemon/SessionData.cpp
@@ -0,0 +1,148 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <string.h>
+#include "SessionData.h"
+#include "SessionXML.h"
+#include "Logging.h"
+
+SessionData* gSessionData = NULL;
+
+SessionData::SessionData() {
+ initialize();
+}
+
+SessionData::~SessionData() {
+}
+
+void SessionData::initialize() {
+ mWaitingOnCommand = false;
+ mSessionIsActive = false;
+ mLocalCapture = false;
+ mOneShot = false;
+ readCpuInfo();
+ mConfigurationXMLPath = NULL;
+ mSessionXMLPath = NULL;
+ mEventsXMLPath = NULL;
+ mTargetPath = NULL;
+ mAPCDir = NULL;
+ mSampleRate = 0;
+ mLiveRate = 0;
+ mDuration = 0;
+ mBacktraceDepth = 0;
+ mTotalBufferSize = 0;
+ // sysconf(_SC_NPROCESSORS_CONF) is unreliable on 2.6 Android, get the value from the kernel module
+ mCores = 1;
+}
+
+void SessionData::parseSessionXML(char* xmlString) {
+ SessionXML session(xmlString);
+ session.parse();
+
+ // Set session data values
+ if (strcmp(session.parameters.sample_rate, "high") == 0) {
+ mSampleRate = 10000;
+ } else if (strcmp(session.parameters.sample_rate, "normal") == 0) {
+ mSampleRate = 1000;
+ } else if (strcmp(session.parameters.sample_rate, "low") == 0) {
+ mSampleRate = 100;
+ } else if (strcmp(session.parameters.sample_rate, "none") == 0) {
+ mSampleRate = 0;
+ } else {
+ logg->logError(__FILE__, __LINE__, "Invalid sample rate (%s) in session xml.", session.parameters.sample_rate);
+ handleException();
+ }
+ mBacktraceDepth = session.parameters.call_stack_unwinding == true ? 128 : 0;
+ mDuration = session.parameters.duration;
+
+ // Determine buffer size (in MB) based on buffer mode
+ mOneShot = true;
+ if (strcmp(session.parameters.buffer_mode, "streaming") == 0) {
+ mOneShot = false;
+ mTotalBufferSize = 1;
+ } else if (strcmp(session.parameters.buffer_mode, "small") == 0) {
+ mTotalBufferSize = 1;
+ } else if (strcmp(session.parameters.buffer_mode, "normal") == 0) {
+ mTotalBufferSize = 4;
+ } else if (strcmp(session.parameters.buffer_mode, "large") == 0) {
+ mTotalBufferSize = 16;
+ } else {
+ logg->logError(__FILE__, __LINE__, "Invalid value for buffer mode in session xml.");
+ handleException();
+ }
+
+ mImages = session.parameters.images;
+ // Convert milli- to nanoseconds
+ mLiveRate = session.parameters.live_rate * (int64_t)1000000;
+ if (mLiveRate > 0 && mLocalCapture) {
+ logg->logMessage("Local capture is not compatable with live, disabling live");
+ mLiveRate = 0;
+ }
+}
+
+void SessionData::readCpuInfo() {
+ char temp[256]; // arbitrarily large amount
+ strcpy(mCoreName, "unknown");
+ mCpuId = -1;
+
+ FILE* f = fopen("/proc/cpuinfo", "r");
+ if (f == NULL) {
+ logg->logMessage("Error opening /proc/cpuinfo\n"
+ "The core name in the captured xml file will be 'unknown'.");
+ return;
+ }
+
+ bool foundCoreName = false;
+ bool foundCpuId = false;
+ while (fgets(temp, sizeof(temp), f) && (!foundCoreName || !foundCpuId)) {
+ if (strlen(temp) > 0) {
+ temp[strlen(temp) - 1] = 0; // Replace the line feed with a null
+ }
+
+ const bool foundHardware = strstr(temp, "Hardware") != 0;
+ const bool foundCPUPart = strstr(temp, "CPU part") != 0;
+ if (foundHardware || foundCPUPart) {
+ char* position = strchr(temp, ':');
+ if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) {
+ logg->logMessage("Unknown format of /proc/cpuinfo\n"
+ "The core name in the captured xml file will be 'unknown'.");
+ return;
+ }
+ position += 2;
+
+ if (foundHardware) {
+ strncpy(mCoreName, position, sizeof(mCoreName));
+ mCoreName[sizeof(mCoreName) - 1] = 0; // strncpy does not guarantee a null-terminated string
+ foundCoreName = true;
+ }
+
+ if (foundCPUPart) {
+ int cpuId = strtol(position, NULL, 16);
+ if (cpuId > mCpuId) {
+ mCpuId = cpuId;
+ }
+ foundCpuId = true;
+ }
+ }
+ }
+
+ if (!foundCoreName) {
+ logg->logMessage("Could not determine core name from /proc/cpuinfo\n"
+ "The core name in the captured xml file will be 'unknown'.");
+ }
+ fclose(f);
+ }
+
+int getEventKey() {
+ // Start one after the gator.ko's value of 1
+ static int key = 2;
+
+ const int ret = key;
+ key += 2;
+ return ret;
+}
diff --git a/tools/gator/daemon/SessionData.h b/tools/gator/daemon/SessionData.h
new file mode 100644
index 000000000000..e72fa5d7c5ed
--- /dev/null
+++ b/tools/gator/daemon/SessionData.h
@@ -0,0 +1,71 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SESSION_DATA_H
+#define SESSION_DATA_H
+
+#include <stdint.h>
+
+#include "Counter.h"
+#include "Hwmon.h"
+
+#define MAX_PERFORMANCE_COUNTERS 50
+
+#define PROTOCOL_VERSION 16
+#define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions
+
+struct ImageLinkList {
+ char* path;
+ struct ImageLinkList *next;
+};
+
+class SessionData {
+public:
+ static const size_t MAX_STRING_LEN = 80;
+
+ SessionData();
+ ~SessionData();
+ void initialize();
+ void parseSessionXML(char* xmlString);
+
+ Hwmon hwmon;
+
+ char mCoreName[MAX_STRING_LEN];
+ struct ImageLinkList *mImages;
+ char* mConfigurationXMLPath;
+ char* mSessionXMLPath;
+ char* mEventsXMLPath;
+ char* mTargetPath;
+ char* mAPCDir;
+
+ bool mWaitingOnCommand;
+ bool mSessionIsActive;
+ bool mLocalCapture;
+ bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled
+
+ int mBacktraceDepth;
+ int mTotalBufferSize; // number of MB to use for the entire collection buffer
+ int mSampleRate;
+ int64_t mLiveRate;
+ int mDuration;
+ int mCores;
+ int mCpuId;
+
+ // PMU Counters
+ int mCounterOverflow;
+ Counter mCounters[MAX_PERFORMANCE_COUNTERS];
+
+private:
+ void readCpuInfo();
+};
+
+extern SessionData* gSessionData;
+
+int getEventKey();
+
+#endif // SESSION_DATA_H
diff --git a/tools/gator/daemon/SessionXML.cpp b/tools/gator/daemon/SessionXML.cpp
new file mode 100644
index 000000000000..0a0a02779176
--- /dev/null
+++ b/tools/gator/daemon/SessionXML.cpp
@@ -0,0 +1,109 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include "SessionXML.h"
+#include "Logging.h"
+#include "OlyUtility.h"
+#include "SessionData.h"
+
+static const char* TAG_SESSION = "session";
+static const char* TAG_IMAGE = "image";
+
+static const char* ATTR_VERSION = "version";
+static const char* ATTR_CALL_STACK_UNWINDING = "call_stack_unwinding";
+static const char* ATTR_BUFFER_MODE = "buffer_mode";
+static const char* ATTR_SAMPLE_RATE = "sample_rate";
+static const char* ATTR_DURATION = "duration";
+static const char* ATTR_PATH = "path";
+static const char* ATTR_LIVE_RATE = "live_rate";
+
+SessionXML::SessionXML(const char* str) {
+ parameters.buffer_mode[0] = 0;
+ parameters.sample_rate[0] = 0;
+ parameters.duration = 0;
+ parameters.call_stack_unwinding = false;
+ parameters.live_rate = 0;
+ parameters.images = NULL;
+ mPath = 0;
+ mSessionXML = (char*)str;
+ logg->logMessage(mSessionXML);
+}
+
+SessionXML::~SessionXML() {
+ if (mPath != 0) {
+ free(mSessionXML);
+ }
+}
+
+void SessionXML::parse() {
+ mxml_node_t *tree;
+ mxml_node_t *node;
+
+ tree = mxmlLoadString(NULL, mSessionXML, MXML_NO_CALLBACK);
+ node = mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND);
+
+ if (node) {
+ sessionTag(tree, node);
+ mxmlDelete(tree);
+ return;
+ }
+
+ logg->logError(__FILE__, __LINE__, "No session tag found in the session.xml file");
+ handleException();
+}
+
+void SessionXML::sessionTag(mxml_node_t *tree, mxml_node_t *node) {
+ int version = 0;
+ if (mxmlElementGetAttr(node, ATTR_VERSION)) version = strtol(mxmlElementGetAttr(node, ATTR_VERSION), NULL, 10);
+ if (version != 1) {
+ logg->logError(__FILE__, __LINE__, "Invalid session.xml version: %d", version);
+ handleException();
+ }
+
+ // copy to pre-allocated strings
+ if (mxmlElementGetAttr(node, ATTR_BUFFER_MODE)) {
+ strncpy(parameters.buffer_mode, mxmlElementGetAttr(node, ATTR_BUFFER_MODE), sizeof(parameters.buffer_mode));
+ parameters.buffer_mode[sizeof(parameters.buffer_mode) - 1] = 0; // strncpy does not guarantee a null-terminated string
+ }
+ if (mxmlElementGetAttr(node, ATTR_SAMPLE_RATE)) {
+ strncpy(parameters.sample_rate, mxmlElementGetAttr(node, ATTR_SAMPLE_RATE), sizeof(parameters.sample_rate));
+ parameters.sample_rate[sizeof(parameters.sample_rate) - 1] = 0; // strncpy does not guarantee a null-terminated string
+ }
+
+ // integers/bools
+ parameters.call_stack_unwinding = util->stringToBool(mxmlElementGetAttr(node, ATTR_CALL_STACK_UNWINDING), false);
+ if (mxmlElementGetAttr(node, ATTR_DURATION)) parameters.duration = strtol(mxmlElementGetAttr(node, ATTR_DURATION), NULL, 10);
+ if (mxmlElementGetAttr(node, ATTR_LIVE_RATE)) parameters.live_rate = strtol(mxmlElementGetAttr(node, ATTR_LIVE_RATE), NULL, 10);
+
+ // parse subtags
+ node = mxmlGetFirstChild(node);
+ while (node) {
+ if (mxmlGetType(node) != MXML_ELEMENT) {
+ node = mxmlWalkNext(node, tree, MXML_NO_DESCEND);
+ continue;
+ }
+ if (strcmp(TAG_IMAGE, mxmlGetElement(node)) == 0) {
+ sessionImage(node);
+ }
+ node = mxmlWalkNext(node, tree, MXML_NO_DESCEND);
+ }
+}
+
+void SessionXML::sessionImage(mxml_node_t *node) {
+ int length = strlen(mxmlElementGetAttr(node, ATTR_PATH));
+ struct ImageLinkList *image;
+
+ image = (struct ImageLinkList *)malloc(sizeof(struct ImageLinkList));
+ image->path = (char*)malloc(length + 1);
+ image->path = strdup(mxmlElementGetAttr(node, ATTR_PATH));
+ image->next = parameters.images;
+ parameters.images = image;
+}
diff --git a/tools/gator/daemon/SessionXML.h b/tools/gator/daemon/SessionXML.h
new file mode 100644
index 000000000000..c7e3798d6950
--- /dev/null
+++ b/tools/gator/daemon/SessionXML.h
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SESSION_XML_H
+#define SESSION_XML_H
+
+#include "mxml/mxml.h"
+
+struct ImageLinkList;
+
+struct ConfigParameters {
+ char buffer_mode[64]; // buffer mode, "streaming", "low", "normal", "high" defines oneshot and buffer size
+ char sample_rate[64]; // capture mode, "high", "normal", or "low"
+ int duration; // length of profile in seconds
+ bool call_stack_unwinding; // whether stack unwinding is performed
+ int live_rate;
+ struct ImageLinkList *images; // linked list of image strings
+};
+
+class SessionXML {
+public:
+ SessionXML(const char* str);
+ ~SessionXML();
+ void parse();
+ ConfigParameters parameters;
+private:
+ char* mSessionXML;
+ char* mPath;
+ void sessionTag(mxml_node_t *tree, mxml_node_t *node);
+ void sessionImage(mxml_node_t *node);
+};
+
+#endif // SESSION_XML_H
diff --git a/tools/gator/daemon/StreamlineSetup.cpp b/tools/gator/daemon/StreamlineSetup.cpp
new file mode 100644
index 000000000000..2faada23f842
--- /dev/null
+++ b/tools/gator/daemon/StreamlineSetup.cpp
@@ -0,0 +1,272 @@
+/**
+ * Copyright (C) ARM Limited 2011-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "Sender.h"
+#include "Logging.h"
+#include "OlyUtility.h"
+#include "SessionData.h"
+#include "CapturedXML.h"
+#include "StreamlineSetup.h"
+#include "ConfigurationXML.h"
+#include "Driver.h"
+#include "EventsXML.h"
+
+static const char* TAG_SESSION = "session";
+static const char* TAG_REQUEST = "request";
+static const char* TAG_CONFIGURATIONS = "configurations";
+
+static const char* ATTR_TYPE = "type";
+static const char* VALUE_EVENTS = "events";
+static const char* VALUE_CONFIGURATION = "configuration";
+static const char* VALUE_COUNTERS = "counters";
+static const char* VALUE_CAPTURED = "captured";
+static const char* VALUE_DEFAULTS = "defaults";
+
+StreamlineSetup::StreamlineSetup(OlySocket* s) {
+ bool ready = false;
+ char* data = NULL;
+ int type;
+
+ mSocket = s;
+
+ // Receive commands from Streamline (master)
+ while (!ready) {
+ // receive command over socket
+ gSessionData->mWaitingOnCommand = true;
+ data = readCommand(&type);
+
+ // parse and handle data
+ switch (type) {
+ case COMMAND_REQUEST_XML:
+ handleRequest(data);
+ break;
+ case COMMAND_DELIVER_XML:
+ handleDeliver(data);
+ break;
+ case COMMAND_APC_START:
+ logg->logMessage("Received apc start request");
+ ready = true;
+ break;
+ case COMMAND_APC_STOP:
+ logg->logMessage("Received apc stop request before apc start request");
+ exit(0);
+ break;
+ case COMMAND_DISCONNECT:
+ logg->logMessage("Received disconnect command");
+ exit(0);
+ break;
+ case COMMAND_PING:
+ logg->logMessage("Received ping command");
+ sendData(NULL, 0, RESPONSE_ACK);
+ break;
+ default:
+ logg->logError(__FILE__, __LINE__, "Target error: Unknown command type, %d", type);
+ handleException();
+ }
+
+ free(data);
+ }
+
+ if (gSessionData->mCounterOverflow > 0) {
+ logg->logError(__FILE__, __LINE__, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow);
+ handleException();
+ }
+}
+
+StreamlineSetup::~StreamlineSetup() {
+}
+
+char* StreamlineSetup::readCommand(int* command) {
+ unsigned char header[5];
+ char* data;
+ int response;
+
+ // receive type and length
+ response = mSocket->receiveNBytes((char*)&header, sizeof(header));
+
+ // After receiving a single byte, we are no longer waiting on a command
+ gSessionData->mWaitingOnCommand = false;
+
+ if (response < 0) {
+ logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
+ handleException();
+ }
+
+ const char type = header[0];
+ const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24);
+
+ // add artificial limit
+ if ((length < 0) || length > 1024 * 1024) {
+ logg->logError(__FILE__, __LINE__, "Target error: Invalid length received, %d", length);
+ handleException();
+ }
+
+ // allocate memory to contain the xml file, size of zero returns a zero size object
+ data = (char*)calloc(length + 1, 1);
+ if (data == NULL) {
+ logg->logError(__FILE__, __LINE__, "Unable to allocate memory for xml");
+ handleException();
+ }
+
+ // receive data
+ response = mSocket->receiveNBytes(data, length);
+ if (response < 0) {
+ logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
+ handleException();
+ }
+
+ // null terminate the data for string parsing
+ if (length > 0) {
+ data[length] = 0;
+ }
+
+ *command = type;
+ return data;
+}
+
+void StreamlineSetup::handleRequest(char* xml) {
+ mxml_node_t *tree, *node;
+ const char * attr = NULL;
+
+ tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK);
+ node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_TYPE, NULL, MXML_DESCEND_FIRST);
+ if (node) {
+ attr = mxmlElementGetAttr(node, ATTR_TYPE);
+ }
+ if (attr && strcmp(attr, VALUE_EVENTS) == 0) {
+ sendEvents();
+ logg->logMessage("Sent events xml response");
+ } else if (attr && strcmp(attr, VALUE_CONFIGURATION) == 0) {
+ sendConfiguration();
+ logg->logMessage("Sent configuration xml response");
+ } else if (attr && strcmp(attr, VALUE_COUNTERS) == 0) {
+ sendCounters();
+ logg->logMessage("Sent counters xml response");
+ } else if (attr && strcmp(attr, VALUE_CAPTURED) == 0) {
+ CapturedXML capturedXML;
+ char* capturedText = capturedXML.getXML(false);
+ sendData(capturedText, strlen(capturedText), RESPONSE_XML);
+ free(capturedText);
+ logg->logMessage("Sent captured xml response");
+ } else if (attr && strcmp(attr, VALUE_DEFAULTS) == 0) {
+ sendDefaults();
+ logg->logMessage("Sent default configuration xml response");
+ } else {
+ char error[] = "Unknown request";
+ sendData(error, strlen(error), RESPONSE_NAK);
+ logg->logMessage("Received unknown request:\n%s", xml);
+ }
+
+ mxmlDelete(tree);
+}
+
+void StreamlineSetup::handleDeliver(char* xml) {
+ mxml_node_t *tree;
+
+ // Determine xml type
+ tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK);
+ if (mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND_FIRST)) {
+ // Session XML
+ gSessionData->parseSessionXML(xml);
+ sendData(NULL, 0, RESPONSE_ACK);
+ logg->logMessage("Received session xml");
+ } else if (mxmlFindElement(tree, tree, TAG_CONFIGURATIONS, NULL, NULL, MXML_DESCEND_FIRST)) {
+ // Configuration XML
+ writeConfiguration(xml);
+ sendData(NULL, 0, RESPONSE_ACK);
+ logg->logMessage("Received configuration xml");
+ } else {
+ // Unknown XML
+ logg->logMessage("Received unknown XML delivery type");
+ sendData(NULL, 0, RESPONSE_NAK);
+ }
+
+ mxmlDelete(tree);
+}
+
+void StreamlineSetup::sendData(const char* data, uint32_t length, char type) {
+ unsigned char header[5];
+ header[0] = type;
+ header[1] = (length >> 0) & 0xff;
+ header[2] = (length >> 8) & 0xff;
+ header[3] = (length >> 16) & 0xff;
+ header[4] = (length >> 24) & 0xff;
+ mSocket->send((char*)&header, sizeof(header));
+ mSocket->send((char*)data, length);
+}
+
+void StreamlineSetup::sendEvents() {
+ EventsXML eventsXML;
+ char* string = eventsXML.getXML();
+ sendString(string, RESPONSE_XML);
+ free(string);
+}
+
+void StreamlineSetup::sendConfiguration() {
+ ConfigurationXML xml;
+
+ const char* string = xml.getConfigurationXML();
+ sendData(string, strlen(string), RESPONSE_XML);
+}
+
+void StreamlineSetup::sendDefaults() {
+ // Send the config built into the binary
+ const char* xml;
+ unsigned int size;
+ ConfigurationXML::getDefaultConfigurationXml(xml, size);
+
+ // Artificial size restriction
+ if (size > 1024*1024) {
+ logg->logError(__FILE__, __LINE__, "Corrupt default configuration file");
+ handleException();
+ }
+
+ sendData(xml, size, RESPONSE_XML);
+}
+
+void StreamlineSetup::sendCounters() {
+ mxml_node_t *xml;
+ mxml_node_t *counters;
+
+ xml = mxmlNewXML("1.0");
+ counters = mxmlNewElement(xml, "counters");
+ for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
+ driver->writeCounters(counters);
+ }
+
+ char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB);
+ sendString(string, RESPONSE_XML);
+
+ free(string);
+ mxmlDelete(xml);
+}
+
+void StreamlineSetup::writeConfiguration(char* xml) {
+ char path[PATH_MAX];
+
+ ConfigurationXML::getPath(path);
+
+ if (util->writeToDisk(path, xml) < 0) {
+ logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify write permissions to this path.", path);
+ handleException();
+ }
+
+ // Re-populate gSessionData with the configuration, as it has now changed
+ { ConfigurationXML configuration; }
+
+ if (gSessionData->mCounterOverflow > 0) {
+ logg->logError(__FILE__, __LINE__, "Only %i performance counters counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow);
+ handleException();
+ }
+}
diff --git a/tools/gator/daemon/StreamlineSetup.h b/tools/gator/daemon/StreamlineSetup.h
new file mode 100644
index 000000000000..092d956ec99f
--- /dev/null
+++ b/tools/gator/daemon/StreamlineSetup.h
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __STREAMLINE_SETUP_H__
+#define __STREAMLINE_SETUP_H__
+
+#include "OlySocket.h"
+
+// Commands from Streamline
+enum {
+ COMMAND_REQUEST_XML = 0,
+ COMMAND_DELIVER_XML = 1,
+ COMMAND_APC_START = 2,
+ COMMAND_APC_STOP = 3,
+ COMMAND_DISCONNECT = 4,
+ COMMAND_PING = 5
+};
+
+class StreamlineSetup {
+public:
+ StreamlineSetup(OlySocket *socket);
+ ~StreamlineSetup();
+private:
+ OlySocket* mSocket;
+
+ char* readCommand(int*);
+ void handleRequest(char* xml);
+ void handleDeliver(char* xml);
+ void sendData(const char* data, uint32_t length, char type);
+ void sendString(const char* string, int type) {sendData(string, strlen(string), type);}
+ void sendEvents();
+ void sendConfiguration();
+ void sendDefaults();
+ void sendCounters();
+ void writeConfiguration(char* xml);
+};
+
+#endif //__STREAMLINE_SETUP_H__
diff --git a/tools/gator/daemon/common.mk b/tools/gator/daemon/common.mk
new file mode 100644
index 000000000000..ee2415b8825c
--- /dev/null
+++ b/tools/gator/daemon/common.mk
@@ -0,0 +1,50 @@
+# -g produces debugging information
+# -O3 maximum optimization
+# -O0 no optimization, used for debugging
+# -Wall enables most warnings
+# -Werror treats warnings as errors
+# -std=c++0x is the planned new c++ standard
+# -std=c++98 is the 1998 c++ standard
+CFLAGS += -O3 -Wall -fno-exceptions -pthread -MMD -DETCDIR=\"/etc\" -Ilibsensors
+CXXFLAGS += -fno-rtti
+ifeq ($(WERROR),1)
+ CFLAGS += -Werror
+endif
+# -s strips the binary of debug info
+LDFLAGS += -s
+TARGET = gatord
+C_SRC = $(wildcard mxml/*.c) $(wildcard libsensors/*.c)
+CPP_SRC = $(wildcard *.cpp)
+
+all: $(TARGET)
+
+events.xml: events_header.xml $(wildcard events-*.xml) events_footer.xml
+ cat $^ > $@
+
+include $(wildcard *.d)
+include $(wildcard mxml/*.d)
+
+EventsXML.cpp: events_xml.h
+ConfigurationXML.cpp: configuration_xml.h
+
+# Don't regenerate conf-lex.c or conf-parse.c
+libsensors/conf-lex.c: ;
+libsensors/conf-parse.c: ;
+
+%_xml.h: %.xml escape
+ ./escape $< > $@
+
+%.o: %.c
+ $(GCC) -c $(CFLAGS) -o $@ $<
+
+%.o: %.cpp
+ $(CPP) -c $(CFLAGS) $(CXXFLAGS) -o $@ $<
+
+$(TARGET): $(CPP_SRC:%.cpp=%.o) $(C_SRC:%.c=%.o)
+ $(CPP) $(LDFLAGS) -o $@ $^ -lrt -pthread
+
+escape: escape.c
+ gcc $^ -o $@
+
+clean:
+ rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h configuration_xml.h
diff --git a/tools/gator/daemon/configuration.xml b/tools/gator/daemon/configuration.xml
new file mode 100644
index 000000000000..b44c00a79e88
--- /dev/null
+++ b/tools/gator/daemon/configuration.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configurations revision="3">
+ <configuration counter="ARM_ARM11_ccnt" event="0xff"/>
+ <configuration counter="ARM_ARM11_cnt0" event="0x7"/>
+ <configuration counter="ARM_ARM11_cnt1" event="0xb"/>
+ <configuration counter="ARM_ARM11MPCore_ccnt" event="0xff"/>
+ <configuration counter="ARM_ARM11MPCore_cnt0" event="0x08"/>
+ <configuration counter="ARM_ARM11MPCore_cnt1" event="0x0b"/>
+ <configuration counter="ARM_Cortex-A5_ccnt" event="0xff"/>
+ <configuration counter="ARM_Cortex-A5_cnt0" event="0x8"/>
+ <configuration counter="ARM_Cortex-A5_cnt1" event="0x1"/>
+ <configuration counter="ARM_Cortex-A7_ccnt" event="0xff"/>
+ <configuration counter="ARM_Cortex-A7_cnt0" event="0x08"/>
+ <configuration counter="ARM_Cortex-A7_cnt1" event="0x10"/>
+ <configuration counter="ARM_Cortex-A7_cnt2" event="0x16"/>
+ <configuration counter="ARM_Cortex-A8_ccnt" event="0xff"/>
+ <configuration counter="ARM_Cortex-A8_cnt0" event="0x8"/>
+ <configuration counter="ARM_Cortex-A8_cnt1" event="0x44"/>
+ <configuration counter="ARM_Cortex-A8_cnt2" event="0x43"/>
+ <configuration counter="ARM_Cortex-A8_cnt3" event="0x10"/>
+ <configuration counter="ARM_Cortex-A9_ccnt" event="0xff"/>
+ <configuration counter="ARM_Cortex-A9_cnt0" event="0x68"/>
+ <configuration counter="ARM_Cortex-A9_cnt1" event="0x06"/>
+ <configuration counter="ARM_Cortex-A9_cnt2" event="0x07"/>
+ <configuration counter="ARM_Cortex-A9_cnt3" event="0x03"/>
+ <configuration counter="ARM_Cortex-A9_cnt4" event="0x04"/>
+ <configuration counter="ARM_Cortex-A15_ccnt" event="0xff"/>
+ <configuration counter="ARM_Cortex-A15_cnt0" event="0x8"/>
+ <configuration counter="ARM_Cortex-A15_cnt1" event="0x16"/>
+ <configuration counter="ARM_Cortex-A15_cnt2" event="0x10"/>
+ <configuration counter="ARM_Cortex-A15_cnt3" event="0x19"/>
+ <configuration counter="ARM_Cortex-A53_ccnt" event="0x11"/>
+ <configuration counter="ARM_Cortex-A53_cnt0" event="0x8"/>
+ <configuration counter="ARM_Cortex-A53_cnt1" event="0x16"/>
+ <configuration counter="ARM_Cortex-A53_cnt2" event="0x10"/>
+ <configuration counter="ARM_Cortex-A53_cnt3" event="0x19"/>
+ <configuration counter="ARM_Cortex-A57_ccnt" event="0x11"/>
+ <configuration counter="ARM_Cortex-A57_cnt0" event="0x8"/>
+ <configuration counter="ARM_Cortex-A57_cnt1" event="0x16"/>
+ <configuration counter="ARM_Cortex-A57_cnt2" event="0x10"/>
+ <configuration counter="ARM_Cortex-A57_cnt3" event="0x19"/>
+ <configuration counter="Scorpion_ccnt" event="0xff"/>
+ <configuration counter="Scorpion_cnt0" event="0x08"/>
+ <configuration counter="Scorpion_cnt1" event="0x10"/>
+ <configuration counter="ScorpionMP_ccnt" event="0xff"/>
+ <configuration counter="ScorpionMP_cnt0" event="0x08"/>
+ <configuration counter="ScorpionMP_cnt1" event="0x10"/>
+ <configuration counter="Krait_ccnt" event="0xff"/>
+ <configuration counter="Krait_cnt0" event="0x08"/>
+ <configuration counter="Krait_cnt1" event="0x10"/>
+ <configuration counter="Linux_block_rq_wr"/>
+ <configuration counter="Linux_block_rq_rd"/>
+ <configuration counter="Linux_meminfo_memused"/>
+ <configuration counter="Linux_meminfo_memfree"/>
+ <configuration counter="Linux_power_cpu_freq"/>
+ <configuration counter="L2C-310_cnt0" event="0x1"/>
+</configurations>
diff --git a/tools/gator/daemon/escape.c b/tools/gator/daemon/escape.c
new file mode 100644
index 000000000000..3eec1f8d38d3
--- /dev/null
+++ b/tools/gator/daemon/escape.c
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * The Makefile in the daemon folder builds and executes 'escape'
+ * 'escape' creates configuration_xml.h from configuration.xml and events_xml.h from events-*.xml
+ * these genereated xml files are then #included and built as part of the gatord binary
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+static void print_escaped_path(char *path) {
+ if (isdigit(*path)) {
+ printf("__");
+ }
+ for (; *path != '\0'; ++path) {
+ printf("%c", isalnum(*path) ? *path : '_');
+ }
+}
+
+int main(int argc, char *argv[]) {
+ int i;
+ char *path;
+ FILE *in = NULL;
+ int ch;
+ unsigned int len = 0;
+
+ for (i = 1; i < argc && argv[i][0] == '-'; ++i) ;
+ if (i == argc) {
+ fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ path = argv[i];
+
+ errno = 0;
+ if ((in = fopen(path, "r")) == NULL) {
+ fprintf(stderr, "Unable to open '%s': %s\n", path, strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ printf("static const unsigned char ");
+ print_escaped_path(path);
+ printf("[] = {");
+ for (;;) {
+ ch = fgetc(in);
+ if (len != 0) {
+ printf(",");
+ }
+ if (len % 12 == 0) {
+ printf("\n ");
+ }
+ // Write out a null character after the contents of the file but do not increment len
+ printf(" 0x%.2x", (ch == EOF ? 0 : ch));
+ if (ch == EOF) {
+ break;
+ }
+ ++len;
+ }
+ printf("\n};\nstatic const unsigned int ");
+ print_escaped_path(path);
+ printf("_len = %i;\n", len);
+
+ fclose(in);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tools/gator/daemon/events-ARM11.xml b/tools/gator/daemon/events-ARM11.xml
new file mode 100644
index 000000000000..57e323546314
--- /dev/null
+++ b/tools/gator/daemon/events-ARM11.xml
@@ -0,0 +1,39 @@
+ <counter_set name="ARM_ARM11_cnt" count="3"/>
+ <category name="ARM11" counter_set="ARM_ARM11_cnt" per_cpu="yes">
+ <event counter="ARM_ARM11_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Cache" name="Inst miss" description="Instruction cache miss to a cacheable location, which requires a fetch from external memory"/>
+ <event event="0x01" title="Pipeline" name="Instruction stall" description="Stall because instruction buffer cannot deliver an instruction"/>
+ <event event="0x02" title="Pipeline" name="Data stall" description="Stall because of a data dependency"/>
+ <event event="0x03" title="Cache" name="Inst micro TLB miss" description="Instruction MicroTLB miss (unused on ARM1156)"/>
+ <event event="0x04" title="Cache" name="Data micro TLB miss" description="Data MicroTLB miss (unused on ARM1156)"/>
+ <event event="0x05" title="Branch" name="Instruction executed" description="Branch instruction executed, branch might or might not have changed program flow"/>
+ <event event="0x06" title="Branch" name="Mispredicted" description="Branch mis-predicted"/>
+ <event event="0x07" title="Instruction" name="Executed" description="Instructions executed"/>
+ <event event="0x09" title="Cache" name="Data access" description="Data cache access, not including Cache operations"/>
+ <event event="0x0a" title="Cache" name="Data all access" description="Data cache access, not including Cache Operations regardless of whether or not the location is cacheable"/>
+ <event event="0x0b" title="Cache" name="Data miss" description="Data cache miss, not including Cache Operations"/>
+ <event event="0x0c" title="Cache" name="Write-back" description="Data cache write-back"/>
+ <event event="0x0d" title="Program Counter" name="SW change" description="Software changed the PC"/>
+ <event event="0x0f" title="Cache " name="TLB miss" description="Main TLB miss (unused on ARM1156)"/>
+ <event event="0x10" title="External" name="Access" description="Explicit external data or peripheral access"/>
+ <event event="0x11" title="Cache" name="Data miss" description="Stall because of Load Store Unit request queue being full"/>
+ <event event="0x12" title="Write Buffer" name="Drains" description="The number of times the Write Buffer was drained because of a Data Synchronization Barrier command or Strongly Ordered operation"/>
+ <event event="0x13" title="Disable Interrupts" name="FIQ" description="The number of cycles which FIQ interrupts are disabled (ARM1156 only)"/>
+ <event event="0x14" title="Disable Interrupts" name="IRQ" description="The number of cycles which IRQ interrupts are disabled (ARM1156 only)"/>
+ <event event="0x20" title="ETM" name="ETMEXTOUT[0]" description="ETMEXTOUT[0] signal was asserted for a cycle"/>
+ <event event="0x21" title="ETM" name="ETMEXTOUT[1]" description="ETMEXTOUT[1] signal was asserted for a cycle"/>
+ <event event="0x22" title="ETM" name="ETMEXTOUT[0,1]" description="ETMEXTOUT[0] or ETMEXTOUT[1] was asserted"/>
+ <event event="0x23" title="Procedure" name="Calls" description="Procedure call instruction executed"/>
+ <event event="0x24" title="Procedure" name="Returns" description="Procedure return instruction executed"/>
+ <event event="0x25" title="Procedure" name="Return and predicted" description="Procedure return instruction executed and return address predicted"/>
+ <event event="0x26" title="Procedure" name="Return and mispredicted" description="Procedure return instruction executed and return address predicted incorrectly"/>
+ <event event="0x30" title="Cache" name="Inst tag or parity error" description="Instruction cache Tag or Valid RAM parity error (ARM1156 only)"/>
+ <event event="0x31" title="Cache" name="Inst parity error" description="Instruction cache RAM parity error (ARM1156 only)"/>
+ <event event="0x32" title="Cache" name="Data tag or parity error" description="Data cache Tag or Valid RAM parity error (ARM1156 only)"/>
+ <event event="0x33" title="Cache" name="Data parity error" description="Data cache RAM parity error (ARM1156 only)"/>
+ <event event="0x34" title="ITCM" name="Error" description="ITCM error (ARM1156 only)"/>
+ <event event="0x35" title="DTCM" name="Error" description="DTCM error (ARM1156 only)"/>
+ <event event="0x36" title="Procedure" name="Return address pop" description="Procedure return address popped off the return stack (ARM1156 only)"/>
+ <event event="0x37" title="Procedure" name="Return address misprediction" description="Procedure return address popped off the return stack has been incorrectly predicted by the PFU (ARM1156 only)"/>
+ <event event="0x38" title="Cache" name="Data dirty parity error" description="Data cache Dirty RAM parity error (ARM1156 only)"/>
+ </category>
diff --git a/tools/gator/daemon/events-ARM11MPCore.xml b/tools/gator/daemon/events-ARM11MPCore.xml
new file mode 100644
index 000000000000..2d5c5e199e66
--- /dev/null
+++ b/tools/gator/daemon/events-ARM11MPCore.xml
@@ -0,0 +1,26 @@
+ <counter_set name="ARM_ARM11MPCore_cnt" count="3"/>
+ <category name="ARM11MPCore" counter_set="ARM_ARM11MPCore_cnt" per_cpu="yes">
+ <event counter="ARM_ARM11MPCore_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Cache" name="Inst miss" description="Instruction cache miss to a cacheable location, which requires a fetch from external memory"/>
+ <event event="0x01" title="Pipeline" name="Instruction stall" description="Stall because instruction buffer cannot deliver an instruction"/>
+ <event event="0x02" title="Pipeline" name="Data stall" description="Stall because of a data dependency"/>
+ <event event="0x03" title="Cache" name="Inst micro TLB miss" description="Instruction MicroTLB miss (unused on ARM1156)"/>
+ <event event="0x04" title="Cache" name="Data micro TLB miss" description="Data MicroTLB miss (unused on ARM1156)"/>
+ <event event="0x05" title="Branch" name="Instruction executed" description="Branch instructions executed, branch might or might not have changed program flow"/>
+ <event event="0x06" title="Branch" name="Not predicted" description="Branch not predicted"/>
+ <event event="0x07" title="Branch" name="Mispredicted" description="Branch mispredicted"/>
+ <event event="0x08" title="Core" name="Instructions" description="Instructions executed"/>
+ <event event="0x09" title="Core" name="Folded Instructions" description="Folded instructions executed"/>
+ <event event="0x0a" title="Cache" name="Data read access" description="Data cache read access, not including cache operations"/>
+ <event event="0x0b" title="Cache" name="Data read miss" description="Data cache miss, not including Cache Operations"/>
+ <event event="0x0c" title="Cache" name="Data write access" description="Data cache write access"/>
+ <event event="0x0d" title="Cache" name="Data write miss" description="Data cache write miss"/>
+ <event event="0x0e" title="Cache" name="Data line eviction" description="Data cache line eviction, not including cache operations"/>
+ <event event="0x0f" title="Branch" name="PC change w/o mode change" description="Software changed the PC and there is not a mode change"/>
+ <event event="0x10" title="Cache " name="TLB miss" description="Main TLB miss"/>
+ <event event="0x11" title="External" name="External Memory request" description="External memory request (cache refill, noncachable, write-back)"/>
+ <event event="0x12" title="Cache" name="Stall" description="Stall because of Load Store Unit request queue being full"/>
+ <event event="0x13" title="Write Buffer" name="Drains" description="The number of times the Write Buffer was drained because of LSU ordering constraints or CP15 operations (Data Synchronization Barrier command) or Strongly Ordered operation"/>
+ <event event="0x14" title="Write Buffer" name="Write Merges" description="Buffered write merged in a store buffer slot"/>
+ <event event="0xFF" title="Core" name="Cycle counter" description="An increment each cycle"/>
+ </category>
diff --git a/tools/gator/daemon/events-CCI-400.xml b/tools/gator/daemon/events-CCI-400.xml
new file mode 100644
index 000000000000..86db2087e1f5
--- /dev/null
+++ b/tools/gator/daemon/events-CCI-400.xml
@@ -0,0 +1,47 @@
+ <counter_set name="cci-400_cnt" count="4"/>
+ <category name="CCI-400" counter_set="cci-400_cnt" per_cpu="no" supports_event_based_sampling="yes">
+ <event counter="cci-400_ccnt" event="0xff" title="CCI-400 Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" description="The number of core clock cycles"/>
+
+ <option_set name="Slave">
+ <option event_delta="0x00" name="S0" description="Slave interface 0"/>
+ <option event_delta="0x20" name="S1" description="Slave interface 1"/>
+ <option event_delta="0x40" name="S2" description="Slave interface 2"/>
+ <option event_delta="0x60" name="S3" description="Slave interface 3"/>
+ <option event_delta="0x80" name="S4" description="Slave interface 4"/>
+ </option_set>
+
+ <event event="0x00" option_set="Slave" title="CCI-400" name="Read: any" description="Read request handshake: any"/>
+ <event event="0x01" option_set="Slave" title="CCI-400" name="Read: transaction" description="Read request handshake: device transaction"/>
+ <event event="0x02" option_set="Slave" title="CCI-400" name="Read: normal" description="Read request handshake: normal, non-shareable or system-shareable, but not barrier or cache maintenance operation"/>
+ <event event="0x03" option_set="Slave" title="CCI-400" name="Read: shareable" description="Read request handshake: inner- or outer-shareable, but not barrier, DVM message or cache maintenance operation"/>
+ <event event="0x04" option_set="Slave" title="CCI-400" name="Read: cache" description="Read request handshake: cache maintenance operation, CleanInvalid, CleanShared, MakeInvalid"/>
+ <event event="0x05" option_set="Slave" title="CCI-400" name="Read: memory barrier" description="Read request handshake: memory barrier"/>
+ <event event="0x06" option_set="Slave" title="CCI-400" name="Read: sync barrier" description="Read request handshake: synchronization barrier"/>
+ <event event="0x07" option_set="Slave" title="CCI-400" name="Read: DVM message, no sync" description="Read request handshake: DVM message, no synchronization"/>
+ <event event="0x08" option_set="Slave" title="CCI-400" name="Read: DVM message, sync" description="Read request handshake: DVM message, synchronization"/>
+ <event event="0x09" option_set="Slave" title="CCI-400" name="Read: stall" description="Read request stall cycle because the transaction tracker is full. Increase SIx_R_MAX to avoid this stall"/>
+ <event event="0x0a" option_set="Slave" title="CCI-400" name="Read data last handshake" description="Read data last handshake: data returned from the snoop instead of from downstream"/>
+ <event event="0x0b" option_set="Slave" title="CCI-400" name="Read data stall cycle" description="Read data stall cycle: RVALIDS is HIGH, RREADYS is LOW"/>
+ <event event="0x0c" option_set="Slave" title="CCI-400" name="Write: any" description="Write request handshake: any"/>
+ <event event="0x0d" option_set="Slave" title="CCI-400" name="Write: transaction" description="Write request handshake: device transaction"/>
+ <event event="0x0e" option_set="Slave" title="CCI-400" name="Write: normal" description="Write request handshake: normal, non-shareable, or system-shareable, but not barrier"/>
+ <event event="0x0f" option_set="Slave" title="CCI-400" name="Write: shareable" description="Write request handshake: inner- or outer-shareable, WriteBack or WriteClean"/>
+ <event event="0x10" option_set="Slave" title="CCI-400" name="Write: WriteUnique" description="Write request handshake: WriteUnique"/>
+ <event event="0x11" option_set="Slave" title="CCI-400" name="Write: WriteLineUnique" description="Write request handshake: WriteLineUnique"/>
+ <event event="0x12" option_set="Slave" title="CCI-400" name="Write: Evict" description="Write request handshake: Evict"/>
+ <event event="0x13" option_set="Slave" title="CCI-400" name="Write stall: tracker full" description="Write request stall cycle because the transaction tracker is full. Increase SIx_W_MAX to avoid this stall"/>
+
+ <option_set name="Master">
+ <option event_delta="0xa0" name="M0" description="Master interface 0"/>
+ <option event_delta="0xc0" name="M1" description="Master interface 1"/>
+ <option event_delta="0xe0" name="M2" description="Master interface 2"/>
+ </option_set>
+
+ <event event="0x14" option_set="Master" title="CCI-400" name="Retry fetch" description="RETRY of speculative fetch transaction"/>
+ <event event="0x15" option_set="Master" title="CCI-400" name="Read stall: address hazard" description="Read request stall cycle because of an address hazard"/>
+ <event event="0x16" option_set="Master" title="CCI-400" name="Read stall: ID hazard" description="Read request stall cycle because of an ID hazard"/>
+ <event event="0x17" option_set="Master" title="CCI-400" name="Read stall: tracker full" description="Read request stall cycle because the transaction tracker is full. Increase MIx_R_MAX to avoid this stall. See the CoreLink CCI-400 Cache Coherent Interconnect Integration Manual"/>
+ <event event="0x18" option_set="Master" title="CCI-400" name="Read stall: barrier hazard" description="Read request stall cycle because of a barrier hazard"/>
+ <event event="0x19" option_set="Master" title="CCI-400" name="Write stall: barrier hazard" description="Write request stall cycle because of a barrier hazard"/>
+ <event event="0x1a" option_set="Master" title="CCI-400" name="Write stall: tracker full" description="Write request stall cycle because the transaction tracker is full. Increase MIx_W_MAX to avoid this stall. See the CoreLink CCI-400 Cache Coherent Interconnect Integration Manual"/>
+ </category>
diff --git a/tools/gator/daemon/events-CCN-504.xml b/tools/gator/daemon/events-CCN-504.xml
new file mode 100644
index 000000000000..cfabf65949ed
--- /dev/null
+++ b/tools/gator/daemon/events-CCN-504.xml
@@ -0,0 +1,122 @@
+ <counter_set name="CCN-504_cnt" count="4"/>
+ <category name="CCN-504" counter_set="CCN-504_cnt">
+ <event counter="CCN-504_ccnt" title="CCN-504 Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" description="The number of core clock cycles"/>
+
+ <option_set name="XP_Region">
+ <option event_delta="0x400000" name="XP 0" description="Crosspoint 0"/>
+ <option event_delta="0x410000" name="XP 1" description="Crosspoint 1"/>
+ <option event_delta="0x420000" name="XP 2" description="Crosspoint 2"/>
+ <option event_delta="0x430000" name="XP 3" description="Crosspoint 3"/>
+ <option event_delta="0x440000" name="XP 4" description="Crosspoint 4"/>
+ <option event_delta="0x450000" name="XP 5" description="Crosspoint 5"/>
+ <option event_delta="0x460000" name="XP 6" description="Crosspoint 6"/>
+ <option event_delta="0x470000" name="XP 7" description="Crosspoint 7"/>
+ <option event_delta="0x480000" name="XP 8" description="Crosspoint 8"/>
+ <option event_delta="0x490000" name="XP 9" description="Crosspoint 9"/>
+ <option event_delta="0x4A0000" name="XP 10" description="Crosspoint 10"/>
+ </option_set>
+
+ <event event="0x0801" option_set="XP_Region" title="CCN-504" name="Bus 0: REQ: H-bit" description="Bus 0: REQ: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x0802" option_set="XP_Region" title="CCN-504" name="Bus 0: REQ: S-bit" description="Bus 0: REQ: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x0803" option_set="XP_Region" title="CCN-504" name="Bus 0: REQ: P-Cnt" description="Bus 0: REQ: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x0804" option_set="XP_Region" title="CCN-504" name="Bus 0: REQ: TknV" description="Bus 0: REQ: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0809" option_set="XP_Region" title="CCN-504" name="Bus 1: REQ: H-bit" description="Bus 1: REQ: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x080A" option_set="XP_Region" title="CCN-504" name="Bus 1: REQ: S-bit" description="Bus 1: REQ: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x080B" option_set="XP_Region" title="CCN-504" name="Bus 1: REQ: P-Cnt" description="Bus 1: REQ: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x080C" option_set="XP_Region" title="CCN-504" name="Bus 1: REQ: TknV" description="Bus 1: REQ: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0811" option_set="XP_Region" title="CCN-504" name="Bus 0: RSP: H-bit" description="Bus 0: RSP: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x0812" option_set="XP_Region" title="CCN-504" name="Bus 0: RSP: S-bit" description="Bus 0: RSP: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x0813" option_set="XP_Region" title="CCN-504" name="Bus 0: RSP: P-Cnt" description="Bus 0: RSP: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x0814" option_set="XP_Region" title="CCN-504" name="Bus 0: RSP: TknV" description="Bus 0: RSP: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0819" option_set="XP_Region" title="CCN-504" name="Bus 1: RSP: H-bit" description="Bus 1: RSP: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x081A" option_set="XP_Region" title="CCN-504" name="Bus 1: RSP: S-bit" description="Bus 1: RSP: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x081B" option_set="XP_Region" title="CCN-504" name="Bus 1: RSP: P-Cnt" description="Bus 1: RSP: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x081C" option_set="XP_Region" title="CCN-504" name="Bus 1: RSP: TknV" description="Bus 1: RSP: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0821" option_set="XP_Region" title="CCN-504" name="Bus 0: SNP: H-bit" description="Bus 0: SNP: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x0822" option_set="XP_Region" title="CCN-504" name="Bus 0: SNP: S-bit" description="Bus 0: SNP: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x0823" option_set="XP_Region" title="CCN-504" name="Bus 0: SNP: P-Cnt" description="Bus 0: SNP: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x0824" option_set="XP_Region" title="CCN-504" name="Bus 0: SNP: TknV" description="Bus 0: SNP: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0829" option_set="XP_Region" title="CCN-504" name="Bus 1: SNP: H-bit" description="Bus 1: SNP: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x082A" option_set="XP_Region" title="CCN-504" name="Bus 1: SNP: S-bit" description="Bus 1: SNP: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x082B" option_set="XP_Region" title="CCN-504" name="Bus 1: SNP: P-Cnt" description="Bus 1: SNP: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x082C" option_set="XP_Region" title="CCN-504" name="Bus 1: SNP: TknV" description="Bus 1: SNP: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0831" option_set="XP_Region" title="CCN-504" name="Bus 0: DAT: H-bit" description="Bus 0: DAT: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x0832" option_set="XP_Region" title="CCN-504" name="Bus 0: DAT: S-bit" description="Bus 0: DAT: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x0833" option_set="XP_Region" title="CCN-504" name="Bus 0: DAT: P-Cnt" description="Bus 0: DAT: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x0834" option_set="XP_Region" title="CCN-504" name="Bus 0: DAT: TknV" description="Bus 0: DAT: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0839" option_set="XP_Region" title="CCN-504" name="Bus 1: DAT: H-bit" description="Bus 1: DAT: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x083A" option_set="XP_Region" title="CCN-504" name="Bus 1: DAT: S-bit" description="Bus 1: DAT: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x083B" option_set="XP_Region" title="CCN-504" name="Bus 1: DAT: P-Cnt" description="Bus 1: DAT: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x083C" option_set="XP_Region" title="CCN-504" name="Bus 1: DAT: TknV" description="Bus 1: DAT: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0871" option_set="XP_Region" title="CCN-504" name="Bus 0: DATB: H-bit" description="Bus 0: DATB: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x0872" option_set="XP_Region" title="CCN-504" name="Bus 0: DATB: S-bit" description="Bus 0: DATB: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x0873" option_set="XP_Region" title="CCN-504" name="Bus 0: DATB: P-Cnt" description="Bus 0: DATB: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x0874" option_set="XP_Region" title="CCN-504" name="Bus 0: DATB: TknV" description="Bus 0: DATB: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0879" option_set="XP_Region" title="CCN-504" name="Bus 1: DATB: H-bit" description="Bus 1: DATB: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x087A" option_set="XP_Region" title="CCN-504" name="Bus 1: DATB: S-bit" description="Bus 1: DATB: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x087B" option_set="XP_Region" title="CCN-504" name="Bus 1: DATB: P-Cnt" description="Bus 1: DATB: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x087C" option_set="XP_Region" title="CCN-504" name="Bus 1: DATB: TknV" description="Bus 1: DATB: No TknV, signaled when this XP transmits a valid packet."/>
+
+ <option_set name="HN-F_Region">
+ <option event_delta="0x200000" name="HN-F 3" description="Fully-coherent Home Node 3"/>
+ <option event_delta="0x210000" name="HN-F 5" description="Fully-coherent Home Node 5"/>
+ <option event_delta="0x220000" name="HN-F 7" description="Fully-coherent Home Node 7"/>
+ <option event_delta="0x230000" name="HN-F 8" description="Fully-coherent Home Node 8"/>
+ <option event_delta="0x240000" name="HN-F 13" description="Fully-coherent Home Node 13"/>
+ <option event_delta="0x250000" name="HN-F 15" description="Fully-coherent Home Node 15"/>
+ <option event_delta="0x260000" name="HN-F 17" description="Fully-coherent Home Node 17"/>
+ <option event_delta="0x270000" name="HN-F 18" description="Fully-coherent Home Node 18"/>
+ </option_set>
+
+ <event event="0x0401" option_set="HN-F_Region" title="CCN-504" name="Cache Miss" description="Counts the total cache misses. This is the first time lookup result, and is high priority."/>
+ <event event="0x0402" option_set="HN-F_Region" title="CCN-504" name="L3 SF Cache Access" description="Counts the number of cache accesses. This is the first time access, and is high priority."/>
+ <event event="0x0403" option_set="HN-F_Region" title="CCN-504" name="Cache Fill" description="Counts the total allocations in the HN L3 cache, and all cache line allocations to the L3 cache."/>
+ <event event="0x0404" option_set="HN-F_Region" title="CCN-504" name="POCQ Retry" description="Counts the number of requests that have been retried."/>
+ <event event="0x0405" option_set="HN-F_Region" title="CCN-504" name="POCQ Reqs Recvd" description="Counts the number of requests received by HN."/>
+ <event event="0x0406" option_set="HN-F_Region" title="CCN-504" name="SF Hit" description="Counts the number of snoop filter hits."/>
+ <event event="0x0407" option_set="HN-F_Region" title="CCN-504" name="SF Evictions" description="Counts the number of snoop filter evictions. Cache invalidations are initiated."/>
+ <event event="0x0408" option_set="HN-F_Region" title="CCN-504" name="Snoops Sent" description="Counts the number of snoops sent. Does not differentiate between broadcast or directed snoops."/>
+ <event event="0x0409" option_set="HN-F_Region" title="CCN-504" name="Snoops Broadcast" description="Counts the number of snoop broadcasts sent."/>
+ <event event="0x040A" option_set="HN-F_Region" title="CCN-504" name="L3 Eviction" description="Counts the number of L3 evictions."/>
+ <event event="0x040B" option_set="HN-F_Region" title="CCN-504" name="L3 Fill Invalid Way" description="Counts the number of L3 fills to an invalid way."/>
+ <event event="0x040C" option_set="HN-F_Region" title="CCN-504" name="MC Retries" description="Counts the number of transactions retried by the memory controller."/>
+ <event event="0x040D" option_set="HN-F_Region" title="CCN-504" name="MC Reqs" description="Counts the number of requests to the memory controller."/>
+ <event event="0x040E" option_set="HN-F_Region" title="CCN-504" name="QOS HH Retry" description="Counts the number of times a highest-priority QoS class was retried at the HN-F."/>
+
+ <option_set name="RN-I_Region">
+ <option event_delta="0x800000" name="RN-I 0" description="I/O-coherent Requesting Node 0"/>
+ <option event_delta="0x820000" name="RN-I 2" description="I/O-coherent Requesting Node 2"/>
+ <option event_delta="0x860000" name="RN-I 6" description="I/O-coherent Requesting Node 6"/>
+ <option event_delta="0x8C0000" name="RN-I 12" description="I/O-coherent Requesting Node 12"/>
+ <option event_delta="0x900000" name="RN-I 16" description="I/O-coherent Requesting Node 16"/>
+ <option event_delta="0x940000" name="RN-I 20" description="I/O-coherent Requesting Node 20"/>
+ </option_set>
+
+ <event event="0x1601" option_set="RN-I_Region" title="CCN-504" name="S0 RDataBeats" description="S0 RDataBeats."/>
+ <event event="0x1602" option_set="RN-I_Region" title="CCN-504" name="S1 RDataBeats" description="S1 RDataBeats."/>
+ <event event="0x1603" option_set="RN-I_Region" title="CCN-504" name="S2 RDataBeats" description="S2 RDataBeats."/>
+ <event event="0x1604" option_set="RN-I_Region" title="CCN-504" name="RXDAT Flits received" description="RXDAT Flits received."/>
+ <event event="0x1605" option_set="RN-I_Region" title="CCN-504" name="TXDAT Flits sent" description="TXDAT Flits sent."/>
+ <event event="0x1606" option_set="RN-I_Region" title="CCN-504" name="Total TXREQ Flits sent" description="Total TXREQ Flits sent."/>
+ <event event="0x1607" option_set="RN-I_Region" title="CCN-504" name="Retried TXREQ Flits sent" description="Retried TXREQ Flits sent."/>
+ <event event="0x1608" option_set="RN-I_Region" title="CCN-504" name="RRT full" description="RRT full."/>
+ <event event="0x1609" option_set="RN-I_Region" title="CCN-504" name="WRT full" description="WRT full."/>
+ <event event="0x160A" option_set="RN-I_Region" title="CCN-504" name="Replayed TXREQ Flits" description="Replayed TXREQ Flits."/>
+
+ <option_set name="SBAS_Region">
+ <option event_delta="0x810000" name="SBAS 1" description="ACE master to CHI protocol bridge 1"/>
+ <option event_delta="0x890000" name="SBAS 9" description="ACE master to CHI protocol bridge 9"/>
+ <option event_delta="0x8B0000" name="SBAS 11" description="ACE master to CHI protocol bridge 11"/>
+ <option event_delta="0x930000" name="SBAS 19" description="ACE master to CHI protocol bridge 19"/>
+ </option_set>
+
+ <event event="0x1001" option_set="SBAS_Region" title="CCN-504" name="S0 RDataBeats" description="S0 RDataBeats."/>
+ <event event="0x1004" option_set="SBAS_Region" title="CCN-504" name="RXDAT Flits received" description="RXDAT Flits received."/>
+ <event event="0x1005" option_set="SBAS_Region" title="CCN-504" name="TXDAT Flits sent" description="TXDAT Flits sent."/>
+ <event event="0x1006" option_set="SBAS_Region" title="CCN-504" name="Total TXREQ Flits sent" description="Total TXREQ Flits sent."/>
+ <event event="0x1007" option_set="SBAS_Region" title="CCN-504" name="Retried TXREQ Flits sent" description="Retried TXREQ Flits sent."/>
+ <event event="0x1008" option_set="SBAS_Region" title="CCN-504" name="RRT full" description="RRT full."/>
+ <event event="0x1009" option_set="SBAS_Region" title="CCN-504" name="WRT full" description="WRT full."/>
+ <event event="0x100A" option_set="SBAS_Region" title="CCN-504" name="Replayed TXREQ Flits" description="Replayed TXREQ Flits."/>
+
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A12.xml b/tools/gator/daemon/events-Cortex-A12.xml
new file mode 100644
index 000000000000..20a4772c4587
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A12.xml
@@ -0,0 +1,86 @@
+ <counter_set name="ARM_Cortex-A12_cnt" count="6"/>
+ <category name="Cortex-A12" counter_set="ARM_Cortex-A12_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARM_Cortex-A12_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <event event="0x14" title="Cache" name="L1 inst access" description="Instruction cache access"/>
+ <event event="0x15" title="Cache" name="L1 data write" description="Level 1 data cache Write-Back"/>
+ <event event="0x16" title="Cache" name="L2 data access" description="Level 2 data cache access"/>
+ <event event="0x17" title="Cache" name="L2 data refill" description="Level 2 data cache refill"/>
+ <event event="0x18" title="Cache" name="L2 data write" description="Level 2 data cache Write-Back"/>
+ <event event="0x19" title="Bus" name="Access" description="Bus - Access"/>
+ <event event="0x1b" title="Instruction" name="Speculative" description="Instruction speculatively executed"/>
+ <event event="0x1c" title="Memory" name="Translation table" description="Write to translation table base architecturally executed"/>
+ <event event="0x1d" title="Bus" name="Cycle" description="Bus - Cycle"/>
+ <event event="0x40" title="Cache" name="L1 data read" description="Level 1 data cache access - Read"/>
+ <event event="0x41" title="Cache" name="L1 data access write" description="Level 1 data cache access - Write"/>
+ <event event="0x50" title="Cache" name="L2 data read" description="Level 2 data cache access - Read"/>
+ <event event="0x51" title="Cache" name="L2 data access write" description="Level 2 data cache access - Write"/>
+ <event event="0x56" title="Cache" name="L2 data victim" description="Level 2 data cache Write-Back - Victim"/>
+ <event event="0x57" title="Cache" name="L2 data clean" description="Level 2 data cache Write-Back - Cleaning and coherency"/>
+ <event event="0x58" title="Cache" name="L2 data invalidate" description="Level 2 data cache invalidate"/>
+ <event event="0x60" title="Bus" name="Read" description="Bus access - Read"/>
+ <event event="0x62" title="Bus" name="Access shared" description="Bus access - Normal"/>
+ <event event="0x63" title="Bus" name="Access not shared" description="Bus access - Not normal"/>
+ <event event="0x64" title="Bus" name="Access normal" description="Bus access - Normal"/>
+ <event event="0x65" title="Bus" name="Peripheral" description="Bus access - Peripheral"/>
+ <event event="0x66" title="Memory" name="Read" description="Data memory access - Read"/>
+ <event event="0x67" title="Memory" name="Write" description="Data memory access - Write"/>
+ <event event="0x68" title="Memory" name="Unaligned Read" description="Unaligned access - Read"/>
+ <event event="0x69" title="Memory" name="Unaligned Write" description="Unaligned access - Write"/>
+ <event event="0x6a" title="Memory" name="Unaligned" description="Unaligned access"/>
+ <event event="0x6c" title="Intrinsic" name="LDREX" description="Exclusive instruction speculatively executed - LDREX"/>
+ <event event="0x6e" title="Intrinsic" name="STREX fail" description="Exclusive instruction speculatively executed - STREX fail"/>
+ <event event="0x6f" title="Intrinsic" name="STREX" description="Exclusive instruction speculatively executed - STREX"/>
+ <event event="0x70" title="Instruction" name="Load" description="Instruction speculatively executed - Load"/>
+ <event event="0x71" title="Instruction" name="Store" description="Instruction speculatively executed - Store"/>
+ <event event="0x72" title="Instruction" name="Load/Store" description="Instruction speculatively executed - Load or store"/>
+ <event event="0x73" title="Instruction" name="Integer" description="Instruction speculatively executed - Integer data processing"/>
+ <event event="0x74" title="Instruction" name="Advanced SIMD" description="Instruction speculatively executed - Advanced SIMD"/>
+ <event event="0x75" title="Instruction" name="VFP" description="Instruction speculatively executed - VFP"/>
+ <event event="0x76" title="Instruction" name="Software change" description="Instruction speculatively executed - Software change of the PC"/>
+ <event event="0x78" title="Instruction" name="Immediate branch" description="Branch speculatively executed - Immediate branch"/>
+ <event event="0x79" title="Instruction" name="Procedure return" description="Branch speculatively executed - Procedure return"/>
+ <event event="0x7a" title="Instruction" name="Indirect branch" description="Branch speculatively executed - Indirect branch"/>
+ <event event="0x7c" title="Instruction" name="ISB" description="Barrier speculatively executed - ISB"/>
+ <event event="0x7d" title="Instruction" name="DSB" description="Barrier speculatively executed - DSB"/>
+ <event event="0x7e" title="Instruction" name="DMB" description="Barrier speculatively executed - DMB"/>
+ <event event="0x81" title="Exception" name="Undefined" description="Exception taken, other synchronous"/>
+ <event event="0x8a" title="Exception" name="Hypervisor call" description="Exception taken, Hypervisor Call"/>
+ <event event="0xc0" title="Instruction" name="Stalled Linefill" description="Instruction side stalled due to a Linefill"/>
+ <event event="0xc1" title="Instruction" name="Stalled Page Table Walk" description="Instruction Side stalled due to a Page Table Walk"/>
+ <event event="0xc2" title="Cache" name="4 Ways Read" description="Number of set of 4 ways read in the instruction cache - Tag RAM"/>
+ <event event="0xc3" title="Cache" name="Ways Read" description="Number of ways read in the instruction cache - Data RAM"/>
+ <event event="0xc4" title="Cache" name="BATC Read" description="Number of ways read in the instruction BTAC RAM"/>
+ <event event="0xca" title="Memory" name="Snoop" description="Data snooped from other processor. This event counts memory-read operations that read data from another processor within the local Cortex-A12 cluster, rather than accessing the L2 cache or issuing an external read. It increments on each transaction, rather than on each beat of data"/>
+ <event event="0xd3" title="Slots" name="Load-Store Unit" description="Duration during which all slots in the Load-Store Unit are busy"/>
+ <event event="0xd8" title="Slots" name="Load-Store Issue Queue" description="Duration during which all slots in the Load-Store Issue queue are busy"/>
+ <event event="0xd9" title="Slots" name="Data Processing Issue Queue" description="Duration during which all slots in the Data Processing issue queue are busy"/>
+ <event event="0xda" title="Slots" name="Data Engine Issue Queue" description="Duration during which all slots in the Data Engine issue queue are busy"/>
+ <event event="0xdb" title="NEON" name="Flush" description="Number of NEON instruction which fail their condition code and lead to a flush of the DE pipe"/>
+ <event event="0xdc" title="Hypervisor" name="Traps" description="Number of Trap to hypervisor"/>
+ <event event="0xde" title="PTM" name="EXTOUT 0" description="PTM EXTOUT 0"/>
+ <event event="0xdf" title="PTM" name="EXTOUT 1" description="PTM EXTOUT 1"/>
+ <event event="0xe0" title="MMU" name="Table Walk" description="Duration during which the MMU handle a Page table walk"/>
+ <event event="0xe1" title="MMU" name="Stage1 Table Walk" description="Duration during which the MMU handle a Stage1 Page table walk"/>
+ <event event="0xe2" title="MMU" name="Stage2 Table Walk" description="Duration during which the MMU handle a Stage2 Page table walk"/>
+ <event event="0xe3" title="MMU" name="LSU Table Walk" description="Duration during which the MMU handle a Page table walk requested by the Load Store Unit"/>
+ <event event="0xe4" title="MMU" name="Instruction Table Walk" description="Duration during which the MMU handle a Page table walk requested by the Instruction side"/>
+ <event event="0xe5" title="MMU" name="Preload Table Walk" description="Duration during which the MMU handle a Page table walk requested by a Preload instruction or Prefetch request"/>
+ <event event="0xe6" title="MMU" name="cp15 Table Walk" description="Duration during which the MMU handle a Page table walk requested by a cp15 operation (maintenance by MVA and VA-to-PA operation)"/>
+ <event event="0xe7" title="Cache" name="L1 PLD TLB refill" description="Level 1 PLD TLB refill"/>
+ <event event="0xe8" title="Cache" name="L1 CP15 TLB refill" description="Level 1 CP15 TLB refill"/>
+ <event event="0xe9" title="Cache" name="L1 TLB flush" description="Level 1 TLB flush"/>
+ <event event="0xea" title="Cache" name="L2 TLB access" description="Level 2 TLB access"/>
+ <event event="0xeb" title="Cache" name="L2 TLB miss" description="Level 2 TLB miss"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A15.xml b/tools/gator/daemon/events-Cortex-A15.xml
new file mode 100644
index 000000000000..faa8b1cbcfb2
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A15.xml
@@ -0,0 +1,68 @@
+ <counter_set name="ARM_Cortex-A15_cnt" count="6"/>
+ <category name="Cortex-A15" counter_set="ARM_Cortex-A15_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARM_Cortex-A15_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <event event="0x14" title="Cache" name="L1 inst access" description="Instruction cache access"/>
+ <event event="0x15" title="Cache" name="L1 data write" description="Level 1 data cache Write-Back"/>
+ <event event="0x16" title="Cache" name="L2 data access" description="Level 2 data cache access"/>
+ <event event="0x17" title="Cache" name="L2 data refill" description="Level 2 data cache refill"/>
+ <event event="0x18" title="Cache" name="L2 data write" description="Level 2 data cache Write-Back"/>
+ <event event="0x19" title="Bus" name="Access" description="Bus - Access"/>
+ <event event="0x1a" title="Memory" name="Error" description="Local memory error"/>
+ <event event="0x1b" title="Instruction" name="Speculative" description="Instruction speculatively executed"/>
+ <event event="0x1c" title="Memory" name="Translation table" description="Write to translation table base architecturally executed"/>
+ <event event="0x1d" title="Bus" name="Cycle" description="Bus - Cycle"/>
+ <event event="0x40" title="Cache" name="L1 data read" description="Level 1 data cache access - Read"/>
+ <event event="0x41" title="Cache" name="L1 data access write" description="Level 1 data cache access - Write"/>
+ <event event="0x42" title="Cache" name="L1 data refill read" description="Level 1 data cache refill - Read"/>
+ <event event="0x43" title="Cache" name="L1 data refill write" description="Level 1 data cache refill - Write"/>
+ <event event="0x46" title="Cache" name="L1 data victim" description="Level 1 data cache Write-Back - Victim"/>
+ <event event="0x47" title="Cache" name="L1 data clean" description="Level 1 data cache Write-Back - Cleaning and coherency"/>
+ <event event="0x48" title="Cache" name="L1 data invalidate" description="Level 1 data cache invalidate"/>
+ <event event="0x4c" title="TLB" name="L1 data refill read" description="Level 1 data TLB refill - Read"/>
+ <event event="0x4d" title="TLB" name="L1 data refill write" description="Level 1 data TLB refill - Write"/>
+ <event event="0x50" title="Cache" name="L2 data read" description="Level 2 data cache access - Read"/>
+ <event event="0x51" title="Cache" name="L2 data access write" description="Level 2 data cache access - Write"/>
+ <event event="0x52" title="Cache" name="L2 data refill read" description="Level 2 data cache refill - Read"/>
+ <event event="0x53" title="Cache" name="L2 data refill write" description="Level 2 data cache refill - Write"/>
+ <event event="0x56" title="Cache" name="L2 data victim" description="Level 2 data cache Write-Back - Victim"/>
+ <event event="0x57" title="Cache" name="L2 data clean" description="Level 2 data cache Write-Back - Cleaning and coherency"/>
+ <event event="0x58" title="Cache" name="L2 data invalidate" description="Level 2 data cache invalidate"/>
+ <event event="0x60" title="Bus" name="Read" description="Bus access - Read"/>
+ <event event="0x61" title="Bus" name="Write" description="Bus access - Write"/>
+ <event event="0x64" title="Bus" name="Access normal" description="Bus access - Normal"/>
+ <event event="0x65" title="Bus" name="Peripheral" description="Bus access - Peripheral"/>
+ <event event="0x66" title="Memory" name="Read" description="Data memory access - Read"/>
+ <event event="0x67" title="Memory" name="Write" description="Data memory access - Write"/>
+ <event event="0x68" title="Memory" name="Unaligned Read" description="Unaligned access - Read"/>
+ <event event="0x69" title="Memory" name="Unaligned Write" description="Unaligned access - Write"/>
+ <event event="0x6a" title="Memory" name="Unaligned" description="Unaligned access"/>
+ <event event="0x6c" title="Intrinsic" name="LDREX" description="Exclusive instruction speculatively executed - LDREX"/>
+ <event event="0x6d" title="Intrinsic" name="STREX pass" description="Exclusive instruction speculatively executed - STREX pass"/>
+ <event event="0x6e" title="Intrinsic" name="STREX fail" description="Exclusive instruction speculatively executed - STREX fail"/>
+ <event event="0x70" title="Instruction" name="Load" description="Instruction speculatively executed - Load"/>
+ <event event="0x71" title="Instruction" name="Store" description="Instruction speculatively executed - Store"/>
+ <event event="0x72" title="Instruction" name="Load/Store" description="Instruction speculatively executed - Load or store"/>
+ <event event="0x73" title="Instruction" name="Integer" description="Instruction speculatively executed - Integer data processing"/>
+ <event event="0x74" title="Instruction" name="Advanced SIMD" description="Instruction speculatively executed - Advanced SIMD"/>
+ <event event="0x75" title="Instruction" name="VFP" description="Instruction speculatively executed - VFP"/>
+ <event event="0x76" title="Instruction" name="Software change" description="Instruction speculatively executed - Software change of the PC"/>
+ <event event="0x78" title="Instruction" name="Immediate branch" description="Branch speculatively executed - Immediate branch"/>
+ <event event="0x79" title="Instruction" name="Procedure return" description="Branch speculatively executed - Procedure return"/>
+ <event event="0x7a" title="Instruction" name="Indirect branch" description="Branch speculatively executed - Indirect branch"/>
+ <event event="0x7c" title="Instruction" name="ISB" description="Barrier speculatively executed - ISB"/>
+ <event event="0x7d" title="Instruction" name="DSB" description="Barrier speculatively executed - DSB"/>
+ <event event="0x7e" title="Instruction" name="DMB" description="Barrier speculatively executed - DMB"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A5.xml b/tools/gator/daemon/events-Cortex-A5.xml
new file mode 100644
index 000000000000..a5b15466be52
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A5.xml
@@ -0,0 +1,36 @@
+ <counter_set name="ARM_Cortex-A5_cnt" count="2"/>
+ <category name="Cortex-A5" counter_set="ARM_Cortex-A5_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARM_Cortex-A5_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Branch" name="PC change" description="Software change of the Program Counter, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0e" title="Procedure" name="Return" description="Procedure return, other than exception return, architecturally executed"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <event event="0x14" title="Cache" name="Instruction access" description="Instruction cache access"/>
+ <event event="0x15" title="Cache" name="Data eviction" description="Data cache eviction"/>
+ <event event="0x86" title="Interrupts" name="IRQ" description="IRQ exception taken"/>
+ <event event="0x87" title="Interrupts" name="FIQ" description="FIQ exception taken"/>
+ <event event="0xC0" title="Memory" name="External request" description="External memory request"/>
+ <event event="0xC1" title="Memory" name="Non-cacheable ext req" description="Non-cacheable external memory request"/>
+ <event event="0xC2" title="Cache" name="Linefill" description="Linefill because of prefetch"/>
+ <event event="0xC3" title="Cache" name="Linefill dropped" description="Prefetch linefill dropped"/>
+ <event event="0xC4" title="Cache" name="Allocate mode enter" description="Entering read allocate mode"/>
+ <event event="0xC5" title="Cache" name="Allocate mode" description="Read allocate mode"/>
+ <event event="0xC7" title="ETM" name="ETM Ext Out[0]" description="ETM - ETM Ext Out[0]"/>
+ <event event="0xC8" title="ETM" name="ETM Ext Out[1]" description="ETM - ETM Ext Out[1]"/>
+ <event event="0xC9" title="Instruction" name="Pipeline stall" description="Data Write operation that stalls the pipeline because the store buffer is full"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A53.xml b/tools/gator/daemon/events-Cortex-A53.xml
new file mode 100644
index 000000000000..577dcd94185e
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A53.xml
@@ -0,0 +1,171 @@
+ <counter_set name="ARM_Cortex-A53_cnt" count="6"/>
+ <category name="Cortex-A53" counter_set="ARM_Cortex-A53_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <!-- 0x11 CPU_CYCLES - Cycle -->
+ <event counter="ARM_Cortex-A53_ccnt" event="0x11" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <!-- 0x00 SW_INCR - Instruction architecturally executed (condition check pass) - Software increment -->
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <!-- 0x01 L1I_CACHE_REFILL - Level 1 instruction cache refill -->
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <!-- 0x02 L1I_TLB_REFILL - Level 1 instruction TLB refill -->
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <!-- 0x03 L1D_CACHE_REFILL - Level 1 data cache refill -->
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <!-- 0x04 L1D_CACHE - Level 1 data cache access -->
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <!-- 0x05 L1D_TLB_REFILL - Level 1 data TLB refill -->
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <!-- 0x08 INST_RETIRED - Instruction architecturally executed -->
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <!-- 0x09 EXC_TAKEN - Exception taken -->
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <!-- 0x0A EXC_RETURN - Instruction architecturally executed (condition check pass) - Exception return -->
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <!-- 0x0B CID_WRITE_RETIRED - Instruction architecturally executed (condition check pass) - Write to CONTEXTIDR -->
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <!-- 0x10 BR_MIS_PRED - Mispredicted or not predicted branch speculatively executed -->
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <!-- 0x12 BR_PRED - Predictable branch speculatively executed -->
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <!-- 0x13 MEM_ACCESS - Data memory access -->
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <!-- 0x14 L1I_CACHE - Level 1 instruction cache access -->
+ <event event="0x14" title="Cache" name="L1 inst access" description="Level 1 instruction cache access"/>
+ <!-- 0x15 L1D_CACHE_WB - Level 1 data cache Write-Back -->
+ <event event="0x15" title="Cache" name="L1 data write" description="Level 1 data cache Write-Back"/>
+ <!-- 0x16 L2D_CACHE - Level 2 data cache access -->
+ <event event="0x16" title="Cache" name="L2 data access" description="Level 2 data cache access"/>
+ <!-- 0x17 L2D_CACHE_REFILL - Level 2 data cache refill -->
+ <event event="0x17" title="Cache" name="L2 data refill" description="Level 2 data cache refill"/>
+ <!-- 0x18 L2D_CACHE_WB - Level 2 data cache Write-Back -->
+ <event event="0x18" title="Cache" name="L2 data write" description="Level 2 data cache Write-Back"/>
+ <!-- 0x19 BUS_ACCESS - Bus access -->
+ <event event="0x19" title="Bus" name="Access" description="Bus access"/>
+ <!-- 0x1A MEMORY_ERROR - Local memory error -->
+ <event event="0x1A" title="Memory" name="Error" description="Local memory error"/>
+ <!-- 0x1B INST_SPEC - Operation speculatively executed -->
+ <event event="0x1B" title="Instruction" name="Speculative" description="Operation speculatively executed"/>
+ <!-- 0x1C TTBR_WRITE_RETIRED - Instruction architecturally executed (condition check pass) - Write to translation table base -->
+ <event event="0x1C" title="Memory" name="Translation table" description="Instruction architecturally executed (condition check pass) - Write to translation table base"/>
+ <!-- 0x1D BUS_CYCLES - Bus cycle -->
+ <event event="0x1D" title="Bus" name="Cycle" description="Bus cycle"/>
+ <!-- 0x1E CHAIN - Odd performance counter chain mode -->
+ <event event="0x1E" title="Counter chain" name="Odd Performance" description="Odd performance counter chain mode"/>
+ <!-- 0x40 L1D_CACHE_LD - Level 1 data cache access - Read -->
+ <event event="0x40" title="Cache" name="L1 data read" description="Level 1 data cache access - Read"/>
+ <!-- 0x41 L1D_CACHE_ST - Level 1 data cache access - Write -->
+ <event event="0x41" title="Cache" name="L1 data access write" description="Level 1 data cache access - Write"/>
+ <!-- 0x42 L1D_CACHE_REFILL_LD - Level 1 data cache refill - Read -->
+ <event event="0x42" title="Cache" name="L1 data refill read" description="Level 1 data cache refill - Read"/>
+ <!-- 0x43 L1D_CACHE_REFILL_ST - Level 1 data cache refill - Write -->
+ <event event="0x43" title="Cache" name="L1 data refill write" description="Level 1 data cache refill - Write"/>
+ <!-- 0x46 L1D_CACHE_WB_VICTIM - Level 1 data cache Write-back - Victim -->
+ <event event="0x46" title="Cache" name="L1 data victim" description="Level 1 data cache Write-back - Victim"/>
+ <!-- 0x47 L1D_CACHE_WB_CLEAN - Level 1 data cache Write-back - Cleaning and coherency -->
+ <event event="0x47" title="Cache" name="L1 data clean" description="Level 1 data cache Write-back - Cleaning and coherency"/>
+ <!-- 0x48 L1D_CACHE_INVAL - Level 1 data cache invalidate -->
+ <event event="0x48" title="Cache" name="L1 data invalidate" description="Level 1 data cache invalidate"/>
+ <!-- 0x4C L1D_TLB_REFILL_LD - Level 1 data TLB refill - Read -->
+ <event event="0x4C" title="Cache" name="L1 data refill read" description="Level 1 data TLB refill - Read"/>
+ <!-- 0x4D L1D_TLB_REFILL_ST - Level 1 data TLB refill - Write -->
+ <event event="0x4D" title="Cache" name="L1 data refill write" description="Level 1 data TLB refill - Write"/>
+ <!-- 0x50 L2D_CACHE_LD - Level 2 data cache access - Read -->
+ <event event="0x50" title="Cache" name="L2 data read" description="Level 2 data cache access - Read"/>
+ <!-- 0x51 L2D_CACHE_ST - Level 2 data cache access - Write -->
+ <event event="0x51" title="Cache" name="L2 data access write" description="Level 2 data cache access - Write"/>
+ <!-- 0x52 L2D_CACHE_REFILL_LD - Level 2 data cache refill - Read -->
+ <event event="0x52" title="Cache" name="L2 data refill read" description="Level 2 data cache refill - Read"/>
+ <!-- 0x53 L2D_CACHE_REFILL_ST - Level 2 data cache refill - Write -->
+ <event event="0x53" title="Cache" name="L2 data refill write" description="Level 2 data cache refill - Write"/>
+ <!-- 0x56 L2D_CACHE_WB_VICTIM - Level 2 data cache Write-back - Victim -->
+ <event event="0x56" title="Cache" name="L2 data victim" description="Level 2 data cache Write-back - Victim"/>
+ <!-- 0x57 L2D_CACHE_WB_CLEAN - Level 2 data cache Write-back - Cleaning and coherency -->
+ <event event="0x57" title="Cache" name="L2 data clean" description="Level 2 data cache Write-back - Cleaning and coherency"/>
+ <!-- 0x58 L2D_CACHE_INVAL - Level 2 data cache invalidate -->
+ <event event="0x58" title="Cache" name="L2 data invalidate" description="Level 2 data cache invalidate"/>
+ <!-- 0x60 BUS_ACCESS_LD - Bus access - Read -->
+ <event event="0x60" title="Bus" name="Read" description="Bus access - Read"/>
+ <!-- 0x61 BUS_ACCESS_ST - Bus access - Write -->
+ <event event="0x61" title="Bus" name="Write" description="Bus access - Write"/>
+ <!-- 0x62 BUS_ACCESS_SHARED - Bus access - Normal -->
+ <event event="0x62" title="Bus" name="Access shared" description="Bus access - Normal"/>
+ <!-- 0x63 BUS_ACCESS_NOT_SHARED - Bus access - Not normal -->
+ <event event="0x63" title="Bus" name="Access not shared" description="Bus access - Not normal"/>
+ <!-- 0x64 BUS_ACCESS_NORMAL - Bus access - Normal -->
+ <event event="0x64" title="Bus" name="Access normal" description="Bus access - Normal"/>
+ <!-- 0x65 BUS_ACCESS_PERIPH - Bus access - Peripheral -->
+ <event event="0x65" title="Bus" name="Peripheral" description="Bus access - Peripheral"/>
+ <!-- 0x66 MEM_ACCESS_LD - Data memory access - Read -->
+ <event event="0x66" title="Memory" name="Read" description="Data memory access - Read"/>
+ <!-- 0x67 MEM_ACCESS_ST - Data memory access - Write -->
+ <event event="0x67" title="Memory" name="Write" description="Data memory access - Write"/>
+ <!-- 0x68 UNALIGNED_LD_SPEC - Unaligned access - Read -->
+ <event event="0x68" title="Memory" name="Unaligned Read" description="Unaligned access - Read"/>
+ <!-- 0x69 UNALIGNED_ST_SPEC - Unaligned access - Write -->
+ <event event="0x69" title="Memory" name="Unaligned Write" description="Unaligned access - Write"/>
+ <!-- 0x6A UNALIGNED_LDST_SPEC - Unaligned access -->
+ <event event="0x6A" title="Memory" name="Unaligned" description="Unaligned access"/>
+ <!-- 0x6C LDREX_SPEC - Exclusive operation speculatively executed - LDREX -->
+ <event event="0x6C" title="Intrinsic" name="LDREX" description="Exclusive operation speculatively executed - LDREX"/>
+ <!-- 0x6D STREX_PASS_SPEC - Exclusive instruction speculatively executed - STREX pass -->
+ <event event="0x6D" title="Intrinsic" name="STREX pass" description="Exclusive instruction speculatively executed - STREX pass"/>
+ <!-- 0x6E STREX_FAIL_SPEC - Exclusive operation speculatively executed - STREX fail -->
+ <event event="0x6E" title="Intrinsic" name="STREX fail" description="Exclusive operation speculatively executed - STREX fail"/>
+ <!-- 0x70 LD_SPEC - Operation speculatively executed - Load -->
+ <event event="0x70" title="Instruction" name="Load" description="Operation speculatively executed - Load"/>
+ <!-- 0x71 ST_SPEC - Operation speculatively executed - Store -->
+ <event event="0x71" title="Instruction" name="Store" description="Operation speculatively executed - Store"/>
+ <!-- 0x72 LDST_SPEC - Operation speculatively executed - Load or store -->
+ <event event="0x72" title="Instruction" name="Load/Store" description="Operation speculatively executed - Load or store"/>
+ <!-- 0x73 DP_SPEC - Operation speculatively executed - Integer data processing -->
+ <event event="0x73" title="Instruction" name="Integer" description="Operation speculatively executed - Integer data processing"/>
+ <!-- 0x74 ASE_SPEC - Operation speculatively executed - Advanced SIMD -->
+ <event event="0x74" title="Instruction" name="Advanced SIMD" description="Operation speculatively executed - Advanced SIMD"/>
+ <!-- 0x75 VFP_SPEC - Operation speculatively executed - VFP -->
+ <event event="0x75" title="Instruction" name="VFP" description="Operation speculatively executed - VFP"/>
+ <!-- 0x76 PC_WRITE_SPEC - Operation speculatively executed - Software change of the PC -->
+ <event event="0x76" title="Instruction" name="Software change" description="Operation speculatively executed - Software change of the PC"/>
+ <!-- 0x77 CRYPTO_SPEC - Operation speculatively executed, crypto data processing -->
+ <event event="0x77" title="Instruction" name="Crypto" description="Operation speculatively executed, crypto data processing"/>
+ <!-- 0x78 BR_IMMED_SPEC - Branch speculatively executed - Immediate branch -->
+ <event event="0x78" title="Instruction" name="Immediate branch" description="Branch speculatively executed - Immediate branch"/>
+ <!-- 0x79 BR_RETURN_SPEC - Branch speculatively executed - Procedure return -->
+ <event event="0x79" title="Instruction" name="Procedure return" description="Branch speculatively executed - Procedure return"/>
+ <!-- 0x7A BR_INDIRECT_SPEC - Branch speculatively executed - Indirect branch -->
+ <event event="0x7A" title="Instruction" name="Indirect branch" description="Branch speculatively executed - Indirect branch"/>
+ <!-- 0x7C ISB_SPEC - Barrier speculatively executed - ISB -->
+ <event event="0x7C" title="Instruction" name="ISB" description="Barrier speculatively executed - ISB"/>
+ <!-- 0x7D DSB_SPEC - Barrier speculatively executed - DSB -->
+ <event event="0x7D" title="Instruction" name="DSB" description="Barrier speculatively executed - DSB"/>
+ <!-- 0x7E DMB_SPEC - Barrier speculatively executed - DMB -->
+ <event event="0x7E" title="Instruction" name="DMB" description="Barrier speculatively executed - DMB"/>
+ <!-- 0x81 EXC_UNDEF - Exception taken, other synchronous -->
+ <event event="0x81" title="Exception" name="Undefined" description="Exception taken, other synchronous"/>
+ <!-- 0x82 EXC_SVC - Exception taken, Supervisor Call -->
+ <event event="0x82" title="Exception" name="Supervisor" description="Exception taken, Supervisor Call"/>
+ <!-- 0x83 EXC_PABORT - Exception taken, Instruction Abort -->
+ <event event="0x83" title="Exception" name="Instruction abort" description="Exception taken, Instruction Abort"/>
+ <!-- 0x84 EXC_DABORT - Exception taken, Data Abort or SError -->
+ <event event="0x84" title="Exception" name="Data abort" description="Exception taken, Data Abort or SError"/>
+ <!-- 0x86 EXC_IRQ - Exception taken, IRQ -->
+ <event event="0x86" title="Interrupts" name="IRQ" description="Exception taken, IRQ"/>
+ <!-- 0x87 EXC_FIQ - Exception taken, FIQ -->
+ <event event="0x87" title="Interrupts" name="FIQ" description="Exception taken, FIQ"/>
+ <!-- 0x88 EXC_SMC - Exception taken, Secure Monitor Call -->
+ <event event="0x88" title="Exception" name="Secure monitor call" description="Exception taken, Secure Monitor Call"/>
+ <!-- 0x8A EXC_HVC - Exception taken, Hypervisor Call -->
+ <event event="0x8A" title="Exception" name="Hypervisor call" description="Exception taken, Hypervisor Call"/>
+ <!-- 0x8B EXC_TRAP_PABORT - Exception taken, Instruction Abort not taken locally -->
+ <event event="0x8B" title="Exception" name="Instruction abort non-local" description="Exception taken, Instruction Abort not taken locally"/>
+ <!-- 0x8C EXC_TRAP_DABORT - Exception taken, Data Abort or SError not taken locally -->
+ <event event="0x8C" title="Exception" name="Data abort non-local" description="Exception taken, Data Abort or SError not taken locally"/>
+ <!-- 0x8D EXC_TRAP_OTHER - Exception taken - Other traps not taken locally -->
+ <event event="0x8D" title="Exception" name="Other non-local" description="Exception taken - Other traps not taken locally"/>
+ <!-- 0x8E EXC_TRAP_IRQ - Exception taken, IRQ not taken locally -->
+ <event event="0x8E" title="Exception" name="IRQ non-local" description="Exception taken, IRQ not taken locally"/>
+ <!-- 0x8F EXC_TRAP_FIQ - Exception taken, FIQ not taken locally -->
+ <event event="0x8F" title="Exception" name="FIQ non-local" description="Exception taken, FIQ not taken locally"/>
+ <!-- 0x90 RC_LD_SPEC - Release consistency instruction speculatively executed - Load Acquire -->
+ <event event="0x90" title="Release Consistency" name="Load" description="Release consistency instruction speculatively executed - Load Acquire"/>
+ <!-- 0x91 RC_ST_SPEC - Release consistency instruction speculatively executed - Store Release -->
+ <event event="0x91" title="Release Consistency" name="Store" description="Release consistency instruction speculatively executed - Store Release"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A57.xml b/tools/gator/daemon/events-Cortex-A57.xml
new file mode 100644
index 000000000000..b7178c0c7427
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A57.xml
@@ -0,0 +1,171 @@
+ <counter_set name="ARM_Cortex-A57_cnt" count="6"/>
+ <category name="Cortex-A57" counter_set="ARM_Cortex-A57_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <!-- 0x11 CPU_CYCLES - Cycle -->
+ <event counter="ARM_Cortex-A57_ccnt" event="0x11" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <!-- 0x00 SW_INCR - Instruction architecturally executed (condition check pass) - Software increment -->
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <!-- 0x01 L1I_CACHE_REFILL - Level 1 instruction cache refill -->
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <!-- 0x02 L1I_TLB_REFILL - Level 1 instruction TLB refill -->
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <!-- 0x03 L1D_CACHE_REFILL - Level 1 data cache refill -->
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <!-- 0x04 L1D_CACHE - Level 1 data cache access -->
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <!-- 0x05 L1D_TLB_REFILL - Level 1 data TLB refill -->
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <!-- 0x08 INST_RETIRED - Instruction architecturally executed -->
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <!-- 0x09 EXC_TAKEN - Exception taken -->
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <!-- 0x0A EXC_RETURN - Instruction architecturally executed (condition check pass) - Exception return -->
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <!-- 0x0B CID_WRITE_RETIRED - Instruction architecturally executed (condition check pass) - Write to CONTEXTIDR -->
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <!-- 0x10 BR_MIS_PRED - Mispredicted or not predicted branch speculatively executed -->
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <!-- 0x12 BR_PRED - Predictable branch speculatively executed -->
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <!-- 0x13 MEM_ACCESS - Data memory access -->
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <!-- 0x14 L1I_CACHE - Level 1 instruction cache access -->
+ <event event="0x14" title="Cache" name="L1 inst access" description="Level 1 instruction cache access"/>
+ <!-- 0x15 L1D_CACHE_WB - Level 1 data cache Write-Back -->
+ <event event="0x15" title="Cache" name="L1 data write" description="Level 1 data cache Write-Back"/>
+ <!-- 0x16 L2D_CACHE - Level 2 data cache access -->
+ <event event="0x16" title="Cache" name="L2 data access" description="Level 2 data cache access"/>
+ <!-- 0x17 L2D_CACHE_REFILL - Level 2 data cache refill -->
+ <event event="0x17" title="Cache" name="L2 data refill" description="Level 2 data cache refill"/>
+ <!-- 0x18 L2D_CACHE_WB - Level 2 data cache Write-Back -->
+ <event event="0x18" title="Cache" name="L2 data write" description="Level 2 data cache Write-Back"/>
+ <!-- 0x19 BUS_ACCESS - Bus access -->
+ <event event="0x19" title="Bus" name="Access" description="Bus access"/>
+ <!-- 0x1A MEMORY_ERROR - Local memory error -->
+ <event event="0x1A" title="Memory" name="Error" description="Local memory error"/>
+ <!-- 0x1B INST_SPEC - Operation speculatively executed -->
+ <event event="0x1B" title="Instruction" name="Speculative" description="Operation speculatively executed"/>
+ <!-- 0x1C TTBR_WRITE_RETIRED - Instruction architecturally executed (condition check pass) - Write to translation table base -->
+ <event event="0x1C" title="Memory" name="Translation table" description="Instruction architecturally executed (condition check pass) - Write to translation table base"/>
+ <!-- 0x1D BUS_CYCLES - Bus cycle -->
+ <event event="0x1D" title="Bus" name="Cycle" description="Bus cycle"/>
+ <!-- 0x1E CHAIN - Odd performance counter chain mode -->
+ <event event="0x1E" title="Counter chain" name="Odd Performance" description="Odd performance counter chain mode"/>
+ <!-- 0x40 L1D_CACHE_LD - Level 1 data cache access - Read -->
+ <event event="0x40" title="Cache" name="L1 data read" description="Level 1 data cache access - Read"/>
+ <!-- 0x41 L1D_CACHE_ST - Level 1 data cache access - Write -->
+ <event event="0x41" title="Cache" name="L1 data access write" description="Level 1 data cache access - Write"/>
+ <!-- 0x42 L1D_CACHE_REFILL_LD - Level 1 data cache refill - Read -->
+ <event event="0x42" title="Cache" name="L1 data refill read" description="Level 1 data cache refill - Read"/>
+ <!-- 0x43 L1D_CACHE_REFILL_ST - Level 1 data cache refill - Write -->
+ <event event="0x43" title="Cache" name="L1 data refill write" description="Level 1 data cache refill - Write"/>
+ <!-- 0x46 L1D_CACHE_WB_VICTIM - Level 1 data cache Write-back - Victim -->
+ <event event="0x46" title="Cache" name="L1 data victim" description="Level 1 data cache Write-back - Victim"/>
+ <!-- 0x47 L1D_CACHE_WB_CLEAN - Level 1 data cache Write-back - Cleaning and coherency -->
+ <event event="0x47" title="Cache" name="L1 data clean" description="Level 1 data cache Write-back - Cleaning and coherency"/>
+ <!-- 0x48 L1D_CACHE_INVAL - Level 1 data cache invalidate -->
+ <event event="0x48" title="Cache" name="L1 data invalidate" description="Level 1 data cache invalidate"/>
+ <!-- 0x4C L1D_TLB_REFILL_LD - Level 1 data TLB refill - Read -->
+ <event event="0x4C" title="Cache" name="L1 data refill read" description="Level 1 data TLB refill - Read"/>
+ <!-- 0x4D L1D_TLB_REFILL_ST - Level 1 data TLB refill - Write -->
+ <event event="0x4D" title="Cache" name="L1 data refill write" description="Level 1 data TLB refill - Write"/>
+ <!-- 0x50 L2D_CACHE_LD - Level 2 data cache access - Read -->
+ <event event="0x50" title="Cache" name="L2 data read" description="Level 2 data cache access - Read"/>
+ <!-- 0x51 L2D_CACHE_ST - Level 2 data cache access - Write -->
+ <event event="0x51" title="Cache" name="L2 data access write" description="Level 2 data cache access - Write"/>
+ <!-- 0x52 L2D_CACHE_REFILL_LD - Level 2 data cache refill - Read -->
+ <event event="0x52" title="Cache" name="L2 data refill read" description="Level 2 data cache refill - Read"/>
+ <!-- 0x53 L2D_CACHE_REFILL_ST - Level 2 data cache refill - Write -->
+ <event event="0x53" title="Cache" name="L2 data refill write" description="Level 2 data cache refill - Write"/>
+ <!-- 0x56 L2D_CACHE_WB_VICTIM - Level 2 data cache Write-back - Victim -->
+ <event event="0x56" title="Cache" name="L2 data victim" description="Level 2 data cache Write-back - Victim"/>
+ <!-- 0x57 L2D_CACHE_WB_CLEAN - Level 2 data cache Write-back - Cleaning and coherency -->
+ <event event="0x57" title="Cache" name="L2 data clean" description="Level 2 data cache Write-back - Cleaning and coherency"/>
+ <!-- 0x58 L2D_CACHE_INVAL - Level 2 data cache invalidate -->
+ <event event="0x58" title="Cache" name="L2 data invalidate" description="Level 2 data cache invalidate"/>
+ <!-- 0x60 BUS_ACCESS_LD - Bus access - Read -->
+ <event event="0x60" title="Bus" name="Read" description="Bus access - Read"/>
+ <!-- 0x61 BUS_ACCESS_ST - Bus access - Write -->
+ <event event="0x61" title="Bus" name="Write" description="Bus access - Write"/>
+ <!-- 0x62 BUS_ACCESS_SHARED - Bus access - Normal -->
+ <event event="0x62" title="Bus" name="Access shared" description="Bus access - Normal"/>
+ <!-- 0x63 BUS_ACCESS_NOT_SHARED - Bus access - Not normal -->
+ <event event="0x63" title="Bus" name="Access not shared" description="Bus access - Not normal"/>
+ <!-- 0x64 BUS_ACCESS_NORMAL - Bus access - Normal -->
+ <event event="0x64" title="Bus" name="Access normal" description="Bus access - Normal"/>
+ <!-- 0x65 BUS_ACCESS_PERIPH - Bus access - Peripheral -->
+ <event event="0x65" title="Bus" name="Peripheral" description="Bus access - Peripheral"/>
+ <!-- 0x66 MEM_ACCESS_LD - Data memory access - Read -->
+ <event event="0x66" title="Memory" name="Read" description="Data memory access - Read"/>
+ <!-- 0x67 MEM_ACCESS_ST - Data memory access - Write -->
+ <event event="0x67" title="Memory" name="Write" description="Data memory access - Write"/>
+ <!-- 0x68 UNALIGNED_LD_SPEC - Unaligned access - Read -->
+ <event event="0x68" title="Memory" name="Unaligned Read" description="Unaligned access - Read"/>
+ <!-- 0x69 UNALIGNED_ST_SPEC - Unaligned access - Write -->
+ <event event="0x69" title="Memory" name="Unaligned Write" description="Unaligned access - Write"/>
+ <!-- 0x6A UNALIGNED_LDST_SPEC - Unaligned access -->
+ <event event="0x6A" title="Memory" name="Unaligned" description="Unaligned access"/>
+ <!-- 0x6C LDREX_SPEC - Exclusive operation speculatively executed - LDREX -->
+ <event event="0x6C" title="Intrinsic" name="LDREX" description="Exclusive operation speculatively executed - LDREX"/>
+ <!-- 0x6D STREX_PASS_SPEC - Exclusive instruction speculatively executed - STREX pass -->
+ <event event="0x6D" title="Intrinsic" name="STREX pass" description="Exclusive instruction speculatively executed - STREX pass"/>
+ <!-- 0x6E STREX_FAIL_SPEC - Exclusive operation speculatively executed - STREX fail -->
+ <event event="0x6E" title="Intrinsic" name="STREX fail" description="Exclusive operation speculatively executed - STREX fail"/>
+ <!-- 0x70 LD_SPEC - Operation speculatively executed - Load -->
+ <event event="0x70" title="Instruction" name="Load" description="Operation speculatively executed - Load"/>
+ <!-- 0x71 ST_SPEC - Operation speculatively executed - Store -->
+ <event event="0x71" title="Instruction" name="Store" description="Operation speculatively executed - Store"/>
+ <!-- 0x72 LDST_SPEC - Operation speculatively executed - Load or store -->
+ <event event="0x72" title="Instruction" name="Load/Store" description="Operation speculatively executed - Load or store"/>
+ <!-- 0x73 DP_SPEC - Operation speculatively executed - Integer data processing -->
+ <event event="0x73" title="Instruction" name="Integer" description="Operation speculatively executed - Integer data processing"/>
+ <!-- 0x74 ASE_SPEC - Operation speculatively executed - Advanced SIMD -->
+ <event event="0x74" title="Instruction" name="Advanced SIMD" description="Operation speculatively executed - Advanced SIMD"/>
+ <!-- 0x75 VFP_SPEC - Operation speculatively executed - VFP -->
+ <event event="0x75" title="Instruction" name="VFP" description="Operation speculatively executed - VFP"/>
+ <!-- 0x76 PC_WRITE_SPEC - Operation speculatively executed - Software change of the PC -->
+ <event event="0x76" title="Instruction" name="Software change" description="Operation speculatively executed - Software change of the PC"/>
+ <!-- 0x77 CRYPTO_SPEC - Operation speculatively executed, crypto data processing -->
+ <event event="0x77" title="Instruction" name="Crypto" description="Operation speculatively executed, crypto data processing"/>
+ <!-- 0x78 BR_IMMED_SPEC - Branch speculatively executed - Immediate branch -->
+ <event event="0x78" title="Instruction" name="Immediate branch" description="Branch speculatively executed - Immediate branch"/>
+ <!-- 0x79 BR_RETURN_SPEC - Branch speculatively executed - Procedure return -->
+ <event event="0x79" title="Instruction" name="Procedure return" description="Branch speculatively executed - Procedure return"/>
+ <!-- 0x7A BR_INDIRECT_SPEC - Branch speculatively executed - Indirect branch -->
+ <event event="0x7A" title="Instruction" name="Indirect branch" description="Branch speculatively executed - Indirect branch"/>
+ <!-- 0x7C ISB_SPEC - Barrier speculatively executed - ISB -->
+ <event event="0x7C" title="Instruction" name="ISB" description="Barrier speculatively executed - ISB"/>
+ <!-- 0x7D DSB_SPEC - Barrier speculatively executed - DSB -->
+ <event event="0x7D" title="Instruction" name="DSB" description="Barrier speculatively executed - DSB"/>
+ <!-- 0x7E DMB_SPEC - Barrier speculatively executed - DMB -->
+ <event event="0x7E" title="Instruction" name="DMB" description="Barrier speculatively executed - DMB"/>
+ <!-- 0x81 EXC_UNDEF - Exception taken, other synchronous -->
+ <event event="0x81" title="Exception" name="Undefined" description="Exception taken, other synchronous"/>
+ <!-- 0x82 EXC_SVC - Exception taken, Supervisor Call -->
+ <event event="0x82" title="Exception" name="Supervisor" description="Exception taken, Supervisor Call"/>
+ <!-- 0x83 EXC_PABORT - Exception taken, Instruction Abort -->
+ <event event="0x83" title="Exception" name="Instruction abort" description="Exception taken, Instruction Abort"/>
+ <!-- 0x84 EXC_DABORT - Exception taken, Data Abort or SError -->
+ <event event="0x84" title="Exception" name="Data abort" description="Exception taken, Data Abort or SError"/>
+ <!-- 0x86 EXC_IRQ - Exception taken, IRQ -->
+ <event event="0x86" title="Interrupts" name="IRQ" description="Exception taken, IRQ"/>
+ <!-- 0x87 EXC_FIQ - Exception taken, FIQ -->
+ <event event="0x87" title="Interrupts" name="FIQ" description="Exception taken, FIQ"/>
+ <!-- 0x88 EXC_SMC - Exception taken, Secure Monitor Call -->
+ <event event="0x88" title="Exception" name="Secure monitor call" description="Exception taken, Secure Monitor Call"/>
+ <!-- 0x8A EXC_HVC - Exception taken, Hypervisor Call -->
+ <event event="0x8A" title="Exception" name="Hypervisor call" description="Exception taken, Hypervisor Call"/>
+ <!-- 0x8B EXC_TRAP_PABORT - Exception taken, Instruction Abort not taken locally -->
+ <event event="0x8B" title="Exception" name="Instruction abort non-local" description="Exception taken, Instruction Abort not taken locally"/>
+ <!-- 0x8C EXC_TRAP_DABORT - Exception taken, Data Abort or SError not taken locally -->
+ <event event="0x8C" title="Exception" name="Data abort non-local" description="Exception taken, Data Abort or SError not taken locally"/>
+ <!-- 0x8D EXC_TRAP_OTHER - Exception taken - Other traps not taken locally -->
+ <event event="0x8D" title="Exception" name="Other non-local" description="Exception taken - Other traps not taken locally"/>
+ <!-- 0x8E EXC_TRAP_IRQ - Exception taken, IRQ not taken locally -->
+ <event event="0x8E" title="Exception" name="IRQ non-local" description="Exception taken, IRQ not taken locally"/>
+ <!-- 0x8F EXC_TRAP_FIQ - Exception taken, FIQ not taken locally -->
+ <event event="0x8F" title="Exception" name="FIQ non-local" description="Exception taken, FIQ not taken locally"/>
+ <!-- 0x90 RC_LD_SPEC - Release consistency instruction speculatively executed - Load Acquire -->
+ <event event="0x90" title="Release Consistency" name="Load" description="Release consistency instruction speculatively executed - Load Acquire"/>
+ <!-- 0x91 RC_ST_SPEC - Release consistency instruction speculatively executed - Store Release -->
+ <event event="0x91" title="Release Consistency" name="Store" description="Release consistency instruction speculatively executed - Store Release"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A7.xml b/tools/gator/daemon/events-Cortex-A7.xml
new file mode 100644
index 000000000000..54d7264bc08e
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A7.xml
@@ -0,0 +1,43 @@
+ <counter_set name="ARM_Cortex-A7_cnt" count="4"/>
+ <category name="Cortex-A7" counter_set="ARM_Cortex-A7_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARM_Cortex-A7_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Memory" name="Data Read" description="Data read architecturally executed"/>
+ <event event="0x07" title="Memory" name="Data Write" description="Data write architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Branch" name="PC change" description="Software change of the Program Counter, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <event event="0x14" title="Cache" name="L1 inst access" description="Instruction cache access"/>
+ <event event="0x15" title="Cache" name="L1 data eviction" description="Level 1 data cache eviction"/>
+ <event event="0x16" title="Cache" name="L2 data access" description="Level 2 data cache access"/>
+ <event event="0x17" title="Cache" name="L2 data refill" description="Level 2 data cache refill"/>
+ <event event="0x18" title="Cache" name="L2 data write" description="Level 2 data cache Write-Back"/>
+ <event event="0x19" title="Bus" name="Access" description="Bus - Access"/>
+ <event event="0x1d" title="Bus" name="Cycle" description="Bus - Cycle"/>
+ <event event="0x60" title="Bus" name="Read" description="Bus access - Read"/>
+ <event event="0x61" title="Bus" name="Write" description="Bus access - Write"/>
+ <event event="0x86" title="Exception" name="IRQ" description="IRQ exception taken"/>
+ <event event="0x87" title="Exception" name="FIQ" description="FIQ exception taken"/>
+ <event event="0xC0" title="Memory" name="External request" description="External memory request"/>
+ <event event="0xC1" title="Memory" name="Non-cacheable ext req" description="Non-cacheable external memory request"/>
+ <event event="0xC2" title="Cache" name="Linefill" description="Linefill because of prefetch"/>
+ <event event="0xC3" title="Cache" name="Linefill dropped" description="Prefetch linefill dropped"/>
+ <event event="0xC4" title="Cache" name="Allocate mode enter" description="Entering read allocate mode"/>
+ <event event="0xC5" title="Cache" name="Allocate mode" description="Read allocate mode"/>
+ <event event="0xC7" title="ETM" name="ETM Ext Out[0]" description="ETM - ETM Ext Out[0]"/>
+ <event event="0xC8" title="ETM" name="ETM Ext Out[1]" description="ETM - ETM Ext Out[1]"/>
+ <event event="0xC9" title="Instruction" name="Pipeline stall" description="Data Write operation that stalls the pipeline because the store buffer is full"/>
+ <event event="0xCA" title="Memory" name="Snoop" description="Data snooped from other processor. This event counts memory-read operations that read data from another processor within the local cluster, rather than accessing the L2 cache or issuing an external read."/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A8.xml b/tools/gator/daemon/events-Cortex-A8.xml
new file mode 100644
index 000000000000..f2518237983a
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A8.xml
@@ -0,0 +1,52 @@
+ <counter_set name="ARM_Cortex-A8_cnt" count="4"/>
+ <category name="Cortex-A8" counter_set="ARM_Cortex-A8_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARM_Cortex-A8_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Branch" name="PC change" description="Software change of the Program Counter, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0e" title="Procedure" name="Return" description="Procedure return, other than exception return, architecturally executed"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x40" title="Cache" name="Write buffer full" description="Any write buffer full cycle"/>
+ <event event="0x41" title="Cache" name="L2 store" description="Any store that is merged in the L2 memory system"/>
+ <event event="0x42" title="Cache" name="Bufferable transaction" description="Any bufferable store transaction from load/store to L2 cache, excluding eviction or cast out data"/>
+ <event event="0x43" title="Cache" name="L2 access" description="Any accesses to the L2 cache"/>
+ <event event="0x44" title="Cache" name="L2 miss" description="Any cacheable miss in the L2 cache"/>
+ <event event="0x45" title="AXI" name="Read" description="The number of AXI read data transfers"/>
+ <event event="0x46" title="AXI" name="Write" description="The number of AXI write data transfers"/>
+ <event event="0x47" title="Memory" name="Replay event" description="Any replay event in the memory system"/>
+ <event event="0x48" title="Memory" name="Unaligned access replay" description="Any unaligned memory access that results in a replay"/>
+ <event event="0x49" title="Cache" name="L1 data hash miss" description="Any L1 data memory access that misses in the cache as a result of the hashing algorithm"/>
+ <event event="0x4a" title="Cache" name="L1 inst hash miss" description="Any L1 instruction memory access that misses in the cache as a result of the hashing algorithm"/>
+ <event event="0x4b" title="Cache" name="L1 page coloring" description="Any L1 data memory access in which a page coloring alias occurs"/>
+ <event event="0x4c" title="NEON" name="L1 cache hit" description="Any NEON access that hits in the L1 data cache"/>
+ <event event="0x4d" title="NEON" name="L1 cache access" description="Any NEON cacheable data accesses for L1 data cache"/>
+ <event event="0x4e" title="NEON" name="L2 cache access" description="Any L2 cache accesses as a result of a NEON memory access"/>
+ <event event="0x4f" title="NEON" name="L2 cache hit" description="Any NEON hit in the L2 cache"/>
+ <event event="0x50" title="Cache" name="L1 inst access" description="Any L1 instruction cache access, excluding CP15 cache accesses"/>
+ <event event="0x51" title="Branch" name="Return stack misprediction" description="Any return stack misprediction because of incorrect target address for a taken return stack pop"/>
+ <event event="0x52" title="Branch" name="Direction misprediction" description="Branch direction misprediction"/>
+ <event event="0x53" title="Branch" name="Taken prediction" description="Any predictable branch that is predicted to be taken"/>
+ <event event="0x54" title="Branch" name="Executed and taken prediction" description="Any predictable branch that is executed and taken"/>
+ <event event="0x55" title="Core" name="Operations issued" description="Number of operations issued, where an operation is either: an instruction or one operation in a sequence of operations that make up a multi-cycle instruction"/>
+ <event event="0x56" title="Core" name="No issue cycles" description="Increment for every cycle that no instructions are available for issue"/>
+ <event event="0x57" title="Core" name="Issue cycles" description="For every cycle, this event counts the number of instructions issued in that cycle. Multi-cycle instructions are only counted once"/>
+ <event event="0x58" title="NEON" name="MRC data wait" description="Number of cycles the processor stalls waiting on MRC data from NEON"/>
+ <event event="0x59" title="NEON" name="Full queue" description="Number of cycles that the processor stalls as a result of a full NEON instruction queue or NEON load queue"/>
+ <event event="0x5a" title="NEON" name="Idle" description="Number of cycles that NEON and integer processors are both not idle"/>
+ <event event="0x70" title="External" name="PMUEXTIN[0]" description="Counts any event from external input source PMUEXTIN[0]"/>
+ <event event="0x71" title="External" name="PMUEXTIN[1]" description="Counts any event from external input source PMUEXTIN[1]"/>
+ <event event="0x72" title="External" name="PMUEXTIN[0,1]" description="Counts any event from both external input sources PMUEXTIN[0] and PMUEXTIN[1]"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A9.xml b/tools/gator/daemon/events-Cortex-A9.xml
new file mode 100644
index 000000000000..75f09c80425e
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A9.xml
@@ -0,0 +1,65 @@
+ <counter_set name="ARM_Cortex-A9_cnt" count="6"/>
+ <category name="Cortex-A9" counter_set="ARM_Cortex-A9_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARM_Cortex-A9_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Branch" name="PC change" description="Software change of the Program Counter, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x40" title="Java" name="Bytecode execute" description="Counts the number of Java bytecodes being decoded, including speculative ones"/>
+ <event event="0x41" title="Java" name="SW bytecode execute" description="Counts the number of software java bytecodes being decoded, including speculative ones"/>
+ <event event="0x42" title="Jazelle" name="Backward branch execute" description="Counts the number of Jazelle taken branches being executed"/>
+ <event event="0x50" title="Cache" name="Coherency miss" description="Counts the number of coherent linefill requests performed by the Cortex-A9 processor which also miss in all the other Cortex-A9 processors, meaning that the request is sent to the external memory"/>
+ <event event="0x51" title="Cache" name="Coherency hit" description="Counts the number of coherent linefill requests performed by the Cortex-A9 processor which hit in another Cortex-A9 processor, meaning that the linefill data is fetched directly from the relevant Cortex-A9 cache"/>
+ <event event="0x60" title="Cache" name="Inst dependent stall" description="Counts the number of cycles where the processor is ready to accept new instructions, but does not receive any because of the instruction side not being able to provide any and the instruction cache is currently performing at least one linefill"/>
+ <event event="0x61" title="Cache" name="Data dependent stall" description="Counts the number of cycles where the core has some instructions that it cannot issue to any pipeline, and the Load Store unit has at least one pending linefill request, and no pending TLB requests"/>
+ <event event="0x62" title="Cache" name="TLB stall" description="Counts the number of cycles where the processor is stalled waiting for the completion of translation table walks from the main TLB"/>
+ <event event="0x63" title="Intrinsic" name="STREX pass" description="Counts the number of STREX instructions architecturally executed and passed"/>
+ <event event="0x64" title="Intrinsic" name="STREX fail" description="Counts the number of STREX instructions architecturally executed and failed"/>
+ <event event="0x65" title="Cache" name="Data eviction" description="Counts the number of eviction requests because of a linefill in the data cache"/>
+ <event event="0x66" title="Pipeline" name="Issue stage no dispatch" description="Counts the number of cycles where the issue stage does not dispatch any instruction because it is empty or cannot dispatch any instructions"/>
+ <event event="0x67" title="Pipeline" name="Issue stage empty" description="Counts the number of cycles where the issue stage is empty"/>
+ <event event="0x68" title="Instruction" name="Executed" description="Counts the number of instructions going through the Register Renaming stage. This number is an approximate number of the total number of instructions speculatively executed, and even more approximate of the total number of instructions architecturally executed"/>
+ <event event="0x69" title="Cache" name="Data linefills" description="Counts the number of linefills performed on the external AXI bus"/>
+ <event event="0x6A" title="Cache" name="Prefetch linefills" description="Counts the number of data linefills caused by prefetcher requests"/>
+ <event event="0x6B" title="Cache" name="Prefetch hits" description="Counts the number of cache hits in a line that belongs to a stream followed by the prefetcher"/>
+ <event event="0x6E" title="Core" name="Functions" description="Counts the number of procedure returns whose condition codes do not fail, excluding all returns from exception"/>
+ <event event="0x70" title="Instruction" name="Main execution unit" description="Counts the number of instructions being executed in the main execution pipeline of the processor, the multiply pipeline and arithmetic logic unit pipeline"/>
+ <event event="0x71" title="Instruction" name="Second execution unit" description="Counts the number of instructions being executed in the processor second execution pipeline (ALU)"/>
+ <event event="0x72" title="Instruction" name="Load/Store" description="Counts the number of instructions being executed in the Load/Store unit"/>
+ <event event="0x73" title="Instruction" name="Floating point" description="Counts the number of Floating-point instructions going through the Register Rename stage"/>
+ <event event="0x74" title="Instruction" name="NEON" description="Counts the number of NEON instructions going through the Register Rename stage"/>
+ <event event="0x80" title="Stalls" name="PLD" description="Counts the number of cycles where the processor is stalled because PLD slots are all full"/>
+ <event event="0x81" title="Stalls" name="Memory write" description="Counts the number of cycles when the processor is stalled and the data side is stalled too because it is full and executing writes to the external memory"/>
+ <event event="0x82" title="Stalls" name="Inst main TLB miss" description="Counts the number of stall cycles because of main TLB misses on requests issued by the instruction side"/>
+ <event event="0x83" title="Stalls" name="Data main TLB miss" description="Counts the number of stall cycles because of main TLB misses on requests issued by the data side"/>
+ <event event="0x84" title="Stalls" name="Inst micro TLB miss" description="Counts the number of stall cycles because of micro TLB misses on the instruction side"/>
+ <event event="0x85" title="Stalls" name="Data micro TLB miss" description="Counts the number of stall cycles because of micro TLB misses on the data side"/>
+ <event event="0x86" title="Stalls" name="DMB" description="Counts the number of stall cycles because of the execution of a DMB memory barrier"/>
+ <event event="0x8A" title="Clock" name="Integer core" description="Counts the number of cycles during which the integer core clock is enabled"/>
+ <event event="0x8B" title="Clock" name="Data engine" description="Counts the number of cycles during which the Data Engine clock is enabled"/>
+ <event event="0x8C" title="Clock" name="NEON" description="Counts the number of cycles when the NEON SIMD clock is enabled"/>
+ <event event="0x8D" title="Memory" name="TLB inst allocations" description="Counts the number of TLB allocations because of Instruction requests"/>
+ <event event="0x8E" title="Memory" name="TLB data allocations" description="Counts the number of TLB allocations because of Data requests"/>
+ <event event="0x90" title="Instruction" name="ISB" description="Counts the number of ISB instructions architecturally executed"/>
+ <event event="0x91" title="Instruction" name="DSB" description="Counts the number of DSB instructions architecturally executed"/>
+ <event event="0x92" title="Instruction" name="DMB" description="Counts the number of DMB instructions speculatively executed"/>
+ <event event="0x93" title="External" name="Interrupts" description="Counts the number of external interrupts executed by the processor"/>
+ <event event="0xA0" title="PLE" name="Cache line rq completed" description="Counts the number of PLE cache line requests completed"/>
+ <event event="0xA1" title="PLE" name="Cache line rq skipped" description="Counts the number of PLE cache line requests skipped"/>
+ <event event="0xA2" title="PLE" name="FIFO flush" description="Counts the number of PLE FIFO flush requests"/>
+ <event event="0xA3" title="PLE" name="Request completed" description="Counts the number of PLE FIFO flush completed"/>
+ <event event="0xA4" title="PLE" name="FIFO overflow" description="Counts the number of PLE FIFO flush overflowed"/>
+ <event event="0xA5" title="PLE" name="Request programmed" description="Counts the number of PLE FIFO flush program requests"/>
+ </category>
diff --git a/tools/gator/daemon/events-Krait-architected.xml b/tools/gator/daemon/events-Krait-architected.xml
new file mode 100644
index 000000000000..b8d3bcb48de7
--- /dev/null
+++ b/tools/gator/daemon/events-Krait-architected.xml
@@ -0,0 +1,22 @@
+ <counter_set name="Krait_cnt" count="4"/>
+ <category name="Krait" counter_set="Krait_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="Krait_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Program Counter" name="SW change" description="Software change of PC, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0e" title="Branch" name="Procedure Return" description="Procedure return architecturally executed (not by exceptions)"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ </category>
diff --git a/tools/gator/daemon/events-L2C-310.xml b/tools/gator/daemon/events-L2C-310.xml
new file mode 100644
index 000000000000..4da4d1d63431
--- /dev/null
+++ b/tools/gator/daemon/events-L2C-310.xml
@@ -0,0 +1,18 @@
+ <counter_set name="L2C-310_cnt" count="2"/>
+ <category name="L2C-310" counter_set="L2C-310_cnt" per_cpu="no">
+ <event event="0x1" title="L2 Cache" name="CO" description="Eviction, CastOUT, of a line from the L2 cache"/>
+ <event event="0x2" title="L2 Cache" name="DRH" description="Data read hit"/>
+ <event event="0x3" title="L2 Cache" name="DRREQ" description="Data read request"/>
+ <event event="0x4" title="L2 Cache" name="DWHIT" description="Data write hit"/>
+ <event event="0x5" title="L2 Cache" name="DWREQ" description="Data write request"/>
+ <event event="0x6" title="L2 Cache" name="DWTREQ" description="Data write request with write-through attribute"/>
+ <event event="0x7" title="L2 Cache" name="IRHIT" description="Instruction read hit"/>
+ <event event="0x8" title="L2 Cache" name="IRREQ" description="Instruction read request"/>
+ <event event="0x9" title="L2 Cache" name="WA" description="Write allocate"/>
+ <event event="0xa" title="L2 Cache" name="IPFALLOC" description="Allocation of a prefetch generated by L2C-310 into the L2 cache"/>
+ <event event="0xb" title="L2 Cache" name="EPFHIT" description="Prefetch hint hits in the L2 cache"/>
+ <event event="0xc" title="L2 Cache" name="EPFALLOC" description="Prefetch hint allocated into the L2 cache"/>
+ <event event="0xd" title="L2 Cache" name="SRRCVD" description="Speculative read received"/>
+ <event event="0xe" title="L2 Cache" name="SRCONF" description="Speculative read confirmed"/>
+ <event event="0xf" title="L2 Cache" name="EPFRCVD" description="Prefetch hint received"/>
+ </category>
diff --git a/tools/gator/daemon/events-Linux.xml b/tools/gator/daemon/events-Linux.xml
new file mode 100644
index 000000000000..4a30ad6ec4e6
--- /dev/null
+++ b/tools/gator/daemon/events-Linux.xml
@@ -0,0 +1,17 @@
+ <category name="Linux">
+ <event counter="Linux_irq_softirq" title="Interrupts" name="SoftIRQ" per_cpu="yes" description="Linux SoftIRQ taken"/>
+ <event counter="Linux_irq_irq" title="Interrupts" name="IRQ" per_cpu="yes" description="Linux IRQ taken"/>
+ <event counter="Linux_block_rq_wr" title="Disk IO" name="Write" units="B" description="Disk IO Bytes Written"/>
+ <event counter="Linux_block_rq_rd" title="Disk IO" name="Read" units="B" description="Disk IO Bytes Read"/>
+ <event counter="Linux_net_rx" title="Network" name="Receive" units="B" description="Receive network traffic, including effect from Streamline"/>
+ <event counter="Linux_net_tx" title="Network" name="Transmit" units="B" description="Transmit network traffic, including effect from Streamline"/>
+ <event counter="Linux_sched_switch" title="Scheduler" name="Switch" per_cpu="yes" description="Context switch events"/>
+ <event counter="Linux_meminfo_memused" title="Memory" name="Used" display="maximum" units="B" average_selection="yes" description="Total used memory size"/>
+ <event counter="Linux_meminfo_memfree" title="Memory" name="Free" display="minimum" units="B" average_selection="yes" description="Available memory size"/>
+ <event counter="Linux_meminfo_bufferram" title="Memory" name="Buffer" display="maximum" units="B" average_selection="yes" description="Memory used by OS disk buffers"/>
+ <event counter="Linux_power_cpu_freq" title="Clock" name="Frequency" per_cpu="yes" display="maximum" units="Hz" series_composition="overlay" average_selection="yes" average_cores="yes" description="Frequency setting of the CPU"/>
+ <event counter="Linux_power_cpu_idle" title="Idle" name="State" per_cpu="yes" display="maximum" average_selection="yes" description="CPU Idle State + 1, set the Sample Rate to None to prevent the hrtimer from interrupting the system"/>
+ <event counter="Linux_cpu_wait_contention" title="CPU Contention" name="Wait" per_cpu="no" display="average" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on contended resource"/>
+ <event counter="Linux_cpu_wait_io" title="CPU I/O" name="Wait" per_cpu="no" display="average" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on I/O resource"/>
+ </category>
+
diff --git a/tools/gator/daemon/events-Mali-4xx.xml b/tools/gator/daemon/events-Mali-4xx.xml
new file mode 100644
index 000000000000..8772ce410b95
--- /dev/null
+++ b/tools/gator/daemon/events-Mali-4xx.xml
@@ -0,0 +1,251 @@
+ <counter_set name="ARM_Mali-4xx_VP_0_cnt" count="2"/>
+ <counter_set name="ARM_Mali-4xx_SW_cnt" count="0"/>
+ <counter_set name="ARM_Mali-4xx_Filmstrip_cnt" count="1"/>
+ <category name="Mali-4xx-VP" counter_set="ARM_Mali-4xx_VP_0_cnt" per_cpu="no">
+ <event event="0x01" title="Mali GPU Vertex Processor" name="Active cycles" description="Number of cycles per frame the MaliGP2 was active."/>
+ <event event="0x02" title="Mali GPU Vertex Processor" name="Active cycles, vertex shader" description="Number of cycles per frame the vertex shader unit was active."/>
+ <event event="0x03" title="Mali GPU Vertex Processor" name="Active cycles, vertex storer" description="Number of cycles per frame the vertex storer unit was active."/>
+ <event event="0x04" title="Mali GPU Vertex Processor" name="Active cycles, vertex loader" description="Number of cycles per frame the vertex loader unit was active."/>
+ <event event="0x05" title="Mali GPU Vertex Processor" name="Cycles vertex loader waiting for vertex shader" description="Number of cycles per frame the vertex loader was idle while waiting on the vertex shader."/>
+ <event event="0x06" title="Mali GPU Vertex Processor" name="Words read, system bus" description="Total number of 64 bit words read by the GP2 from the system bus per frame."/>
+ <event event="0x07" title="Mali GPU Vertex Processor" name="Words written, system bus" description="Total number of 64 bit words written by the GP2 to the system bus per frame."/>
+ <event event="0x08" title="Mali GPU Vertex Processor" name="Read bursts, system bus" description="Number of read bursts by the GP2 from the system bus per frame."/>
+ <event event="0x09" title="Mali GPU Vertex Processor" name="Write bursts, system bus" description="Number of write bursts from the MaliGP2 to the system bus per frame."/>
+ <event event="0x0a" title="Mali GPU Vertex Processor" name="Vertices processed" description="Number of vertices processed by the MaliGP2 per frame."/>
+ <event event="0x0b" title="Mali GPU Vertex Processor" name="Vertices fetched" description="Number of vertices fetched by the MaliGP2 per frame."/>
+ <event event="0x0c" title="Mali GPU Vertex Processor" name="Primitives fetched" description="Number of graphics primitives fetched by the MaliGP2 per frame."/>
+ <event event="0x0e" title="Mali GPU Vertex Processor" name="Primitives culled" description="Number of graphics primitives discarded per frame, because they were seen from the back or were offscreen."/>
+ <event event="0x0f" title="Mali GPU Vertex Processor" name="Commands written to tiles" description="Number of commands (8 Bytes, mainly primitives) written by GP2 to the PP input data structure per frame."/>
+ <event event="0x10" title="Mali GPU Vertex Processor" name="Memory blocks allocated" description="Number of overflow data blocks needed for outputting the PP input data structure per frame ."/>
+ <event event="0x13" title="Mali GPU Vertex Processor" name="Vertex loader cache misses" description="Number of cache misses for the vertex shader's vertex input unit per frame."/>
+ <event event="0x16" title="Mali GPU Vertex Processor" name="Active cycles, vertex shader command processor" description="Number of cycles per frame the GP2 vertex shader command processor was active. This includes time waiting for semaphores."/>
+ <event event="0x17" title="Mali GPU Vertex Processor" name="Active cycles, PLBU command processor" description="Number of cycles per frame the MaliGP2 PLBU command processor was active. This includes time waiting for semaphores."/>
+ <event event="0x18" title="Mali GPU Vertex Processor" name="MaliGP2 PLBU cycles per frame" description="Number of cycles per frame the MaliGP2 PLBU output unit was active. This includes time spent waiting on the bus."/>
+ <event event="0x19" title="Mali GPU Vertex Processor" name="Active cycles, PLBU geometry processing" description="Number of cycles per frame the MaliGP2 PLBU was active, excepting final data output. In other words: active cycles through the prepare list commands. This includes time spent waiting on the bus."/>
+ <event event="0x1b" title="Mali GPU Vertex Processor" name="Active cycles, PLBU primitive assembly" description="Number of active cycles per frame spent by the MaliGP2 PLBU doing primitive assembly. This does not include scissoring or final output. This includes time spent waiting on the bus."/>
+ <event event="0x1c" title="Mali GPU Vertex Processor" name="Active cycles, PLBU vertex fetcher" description="Number of active cycles per frame spent by the MaliGP2 PLBU fetching vertex data. This includes time spent waiting on the bus."/>
+ <event event="0x1e" title="Mali GPU Vertex Processor" name="Active cycles, Bounding-box and command generator" description="Number of active cycles per frame spent by the MaliGP2 PLBU setting up bounding boxes and commands (mainly graphics primitives). This includes time spent waiting on the bus."/>
+ <event event="0x20" title="Mali GPU Vertex Processor" name="Active cycles, Scissor tile iterator" description="Number of active cycles per frame spent by the MaliGP2 PLBU iterating over tiles to perform scissoring. This includes time spent waiting on the bus."/>
+ <event event="0x21" title="Mali GPU Vertex Processor" name="Active cycles, PLBU tile iterator" description="Number of active cycles per frame spent by the MaliGP2 PLBU iterating over the tiles in the bounding box generating commands (mainly graphics primitives). This includes time spent waiting on the bus."/>
+ </category>
+ <category name="Mali GPU Fragment Processor" per_cpu="no">
+ <counter_set name="ARM_Mali-4xx_FP_0_cnt" title="Mali-4xx FP0" description="Mali GPU Fragment Processor 0" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_1_cnt" title="Mali-4xx FP1" description="Mali GPU Fragment Processor 1" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_2_cnt" title="Mali-4xx FP2" description="Mali GPU Fragment Processor 2" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_3_cnt" title="Mali-4xx FP3" description="Mali GPU Fragment Processor 3" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_4_cnt" title="Mali-4xx FP4" description="Mali GPU Fragment Processor 4" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_5_cnt" title="Mali-4xx FP5" description="Mali GPU Fragment Processor 5" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_6_cnt" title="Mali-4xx FP6" description="Mali GPU Fragment Processor 6" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_7_cnt" title="Mali-4xx FP7" description="Mali GPU Fragment Processor 7" count="2"/>
+
+ <event event="0x00" title="Mali-4xx FP" name="Active clock cycles" description="Active clock cycles, between polygon start and IRQ."/>
+ <event event="0x02" title="Mali-4xx FP" name="Total bus reads" description="Total number of 64-bit words read from the bus."/>
+ <event event="0x03" title="Mali-4xx FP" name="Total bus writes" description="Total number of 64-bit words written to the bus."/>
+ <event event="0x04" title="Mali-4xx FP" name="Bus read request cycles" description="Number of cycles during which the bus read request signal was HIGH."/>
+ <event event="0x05" title="Mali-4xx FP" name="Bus write request cycles" description="Number of cycles during which the bus write request signal was HIGH."/>
+ <event event="0x06" title="Mali-4xx FP" name="Bus read transactions count" description="Number of read requests accepted by the bus."/>
+ <event event="0x07" title="Mali-4xx FP" name="Bus write transactions" description="Number of write requests accepted by the bus."/>
+ <event event="0x09" title="Mali-4xx FP" name="Tile writeback writes" description="64-bit words written to the bus by the writeback unit."/>
+ <event event="0x0a" title="Mali-4xx FP" name="Store unit writes" description="64-bit words written to the bus by the store unit."/>
+ <event event="0x0d" title="Mali-4xx FP" name="Texture cache uncompressed reads" description="Number of 64-bit words read from the bus into the uncompressed textures cache."/>
+ <event event="0x0e" title="Mali-4xx FP" name="Polygon list reads" description="Number of 64-bit words read from the bus by the polygon list reader."/>
+ <event event="0x0f" title="Mali-4xx FP" name="RSW reads" description="Number of 64-bit words read from the bus into the Render State Word register."/>
+ <event event="0x10" title="Mali-4xx FP" name="Vertex cache reads" description="Number of 64-bit words read from the bus into the vertex cache."/>
+ <event event="0x11" title="Mali-4xx FP" name="Uniform remapping reads" description="Number of 64-bit words read from the bus when reading from the uniform remapping table."/>
+ <event event="0x12" title="Mali-4xx FP" name="Program cache reads" description="Number of 64-bit words read from the bus into the fragment shader program cache."/>
+ <event event="0x13" title="Mali-4xx FP" name="Varying reads" description="Number of 64-bit words containing varyings generated by the vertex processing read from the bus."/>
+ <event event="0x14" title="Mali-4xx FP" name="Texture descriptors reads" description="Number of 64-bit words containing texture descriptors read from the bus."/>
+ <event event="0x15" title="Mali-4xx FP" name="Texture descriptor remapping reads" description="Number of 64-bit words read from the bus when reading from the texture descriptor remapping table."/>
+ <event event="0x17" title="Mali-4xx FP" name="Load unit reads" description="Number of 64-bit words read from the bus by the LOAD sub-instruction."/>
+ <event event="0x18" title="Mali-4xx FP" name="Polygon count" description="Number of triangles read from the polygon list."/>
+ <event event="0x19" title="Mali-4xx FP" name="Pixel rectangle count" description="Number of pixel rectangles read from the polygon list."/>
+ <event event="0x1a" title="Mali-4xx FP" name="Lines count" description="Number of lines read from the polygon list."/>
+ <event event="0x1b" title="Mali-4xx FP" name="Points count" description="Number of points read from the polygon list."/>
+ <event event="0x1c" title="Mali-4xx FP" name="Stall cycles PolygonListReader" description="Number of clock cycles the Polygon List Reader waited for output being collected."/>
+ <event event="0x1d" title="Mali-4xx FP" name="Stall cycles triangle setup" description="Number of clock cycles the TSC waited for input."/>
+ <event event="0x1e" title="Mali-4xx FP" name="Quad rasterized count" description="Number of 2x?2 quads output from rasterizer."/>
+ <event event="0x1f" title="Mali-4xx FP" name="Fragment rasterized count" description="Number of fragment rasterized. Fragments/(Quads*4) gives average actual fragments per quad."/>
+ <event event="0x20" title="Mali-4xx FP" name="Fragment rejected fragment-kill count" description="Number of fragments exiting the fragment shader as killed."/>
+ <event event="0x21" title="Mali-4xx FP" name="Fragment rejected fwd-fragment-kill count" description="Number of fragments killed by forward fragment kill."/>
+ <event event="0x22" title="Mali-4xx FP" name="Fragment passed z/stencil count" description="Number of fragments passing Z and stencil test."/>
+ <event event="0x23" title="Mali-4xx FP" name="Patches rejected early z/stencil count" description="Number of patches rejected by EarlyZ. A patch can be 8x8, 4x4 or 2x2 pixels."/>
+ <event event="0x24" title="Mali-4xx FP" name="Patches evaluated" description="Number of patches evaluated for EarlyZ rejection."/>
+ <event event="0x25" title="Mali-4xx FP" name="Instruction completed count" description="Number of fragment shader instruction words completed. It is a function of pixels processed and the length of the shader programs."/>
+ <event event="0x26" title="Mali-4xx FP" name="Instruction failed rendezvous count" description="Number of fragment shader instructions not completed because of failed Rendezvous."/>
+ <event event="0x27" title="Mali-4xx FP" name="Instruction failed varying-miss count" description="Number of fragment shader instructions not completed because of failed varying operation."/>
+ <event event="0x28" title="Mali-4xx FP" name="Instruction failed texture-miss count" description="Number of fragment shader instructions not completed because of failed texture operation."/>
+ <event event="0x29" title="Mali-4xx FP" name="Instruction failed load-miss count" description="Number of fragment shader instructions not completed because of failed load operation."/>
+ <event event="0x2a" title="Mali-4xx FP" name="Instruction failed tile read-miss count" description="Number of fragment shader instructions not completed because of failed read from the tilebuffer."/>
+ <event event="0x2b" title="Mali-4xx FP" name="Instruction failed store-miss count" description="Number of fragment shader instructions not completed because of failed store operation."/>
+ <event event="0x2c" title="Mali-4xx FP" name="Rendezvous breakage count" description="Number of Rendezvous breakages reported."/>
+ <event event="0x2d" title="Mali-4xx FP" name="Pipeline bubbles cycle count" description="Number of unused cycles in the fragment shader while rendering is active."/>
+ <event event="0x2e" title="Mali-4xx FP" name="Texture mapper multipass count" description="Number of texture operations looped because of more texture passes needed."/>
+ <event event="0x2f" title="Mali-4xx FP" name="Texture mapper cycle count" description="Number of texture operation cycles."/>
+ <event event="0x30" title="Mali-4xx FP" name="Vertex cache hit count" description="Number of times a requested vertex was found in the cache (Number of vertex cache hits)."/>
+ <event event="0x31" title="Mali-4xx FP" name="Vertex cache miss count" description="Number of times a requested vertex was not found in the cache (Number of vertex cache misses)."/>
+ <event event="0x32" title="Mali-4xx FP" name="Varying cache hit count" description="Number of times a requested varying was found in the cache (Number of varying cache hits)."/>
+ <event event="0x33" title="Mali-4xx FP" name="Varying cache miss count" description="Number of times a requested varying was not found in the cache (Number of varying cache misses)."/>
+ <event event="0x34" title="Mali-4xx FP" name="Varying cache conflict miss count" description="Number of times a requested varying was not in the cache and its value, retrieved from memory, must overwrite an older cache entry. This happens when an access pattern cannot be serviced by the cache."/>
+ <event event="0x35" title="Mali-4xx FP" name="Texture cache hit count" description="Number of times a requested texel was found in the texture cache (Number of texture cache hits)."/>
+ <event event="0x36" title="Mali-4xx FP" name="Texture cache miss count" description="Number of times a requested texel was not found in the texture cache (Number of texture cache misses)."/>
+ <event event="0x37" title="Mali-4xx FP" name="Texture cache conflict miss count" description="Number of times a requested texel was not in the cache and its value, retrieved from memory, must overwrite an older cache entry. This happens when an access pattern cannot be serviced by the cache."/>
+ <event event="0x38" title="Mali-4xx FP" name="Compressed texture cache hit count" description="Number of times a requested item was found in the cache."/>
+ <event event="0x39" title="Mali-4xx FP" name="Compressed texture cache miss count" description="Number of times a requested item was not found in the cache."/>
+ <event event="0x3a" title="Mali-4xx FP" name="Load/Store cache hit count" description="Number of hits in the load/store cache."/>
+ <event event="0x3b" title="Mali-4xx FP" name="Load/Store cache miss count" description="Number of misses in the load/store cache."/>
+ <event event="0x3c" title="Mali-4xx FP" name="Program cache hit count" description="Number of hits in the program cache."/>
+ <event event="0x3d" title="Mali-4xx FP" name="Program cache miss count" description="Number of misses in the program cache."/>
+ </category>
+ <counter_set name="ARM_Mali-4xx_L2_0_cnt" title="Mali-4xx L2 0" description="Mali GPU L2 Cache Core 0" count="2"/>
+ <category name="Mali-4xx-L2_0" counter_set="ARM_Mali-4xx_L2_0_cnt" per_cpu="no">
+ <event event="0x01" title="Mali L2 Cache" name="Total clock cycles" description="Total clock cycles"/>
+ <event event="0x02" title="Mali L2 Cache" name="Active clock cycles" description="Active clock cycles"/>
+
+ <option_set name="All">
+ <option event_delta="0x08" name="Master" description="Master"/>
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+
+ <option_set name="Slaves">
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+
+ <event event="0x00" option_set="All" title="Mali L2 Cache" name="Read transactions" description="Read transactions"/>
+ <event event="0x01" option_set="All" title="Mali L2 Cache" name="Write transactions" description="Write transactions"/>
+ <event event="0x02" option_set="All" title="Mali L2 Cache" name="Words read" description="Words read"/>
+ <event event="0x03" option_set="All" title="Mali L2 Cache" name="Words written" description="Words written"/>
+ <event event="0x04" option_set="Slaves" title="Mali L2 Cache" name="Read hits" description="Read hits"/>
+ <event event="0x05" option_set="Slaves" title="Mali L2 Cache" name="Read misses" description="Read misses"/>
+ <event event="0x06" option_set="Slaves" title="Mali L2 Cache" name="Write invalidates" description="Write invalidates"/>
+ <event event="0x07" option_set="Slaves" title="Mali L2 Cache" name="Read invalidates" description="Read invalidates"/>
+ <event event="0x08" option_set="Slaves" title="Mali L2 Cache" name="Cacheable read transactions" description="Cacheable read transactions"/>
+ </category>
+ <counter_set name="ARM_Mali-4xx_L2_1_cnt" title="Mali-4xx L2 1" description="Mali GPU L2 Cache Core 1" count="2"/>
+ <category name="Mali-4xx-L2_1" counter_set="ARM_Mali-4xx_L2_1_cnt" per_cpu="no">
+ <event event="0x01" title="Mali L2 Cache" name="Total clock cycles" description="Total clock cycles"/>
+ <event event="0x02" title="Mali L2 Cache" name="Active clock cycles" description="Active clock cycles"/>
+
+ <option_set name="All">
+ <option event_delta="0x08" name="Master" description="Master"/>
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+
+ <option_set name="Slaves">
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+
+ <event event="0x00" option_set="All" title="Mali L2 Cache" name="Read transactions" description="Read transactions"/>
+ <event event="0x01" option_set="All" title="Mali L2 Cache" name="Write transactions" description="Write transactions"/>
+ <event event="0x02" option_set="All" title="Mali L2 Cache" name="Words read" description="Words read"/>
+ <event event="0x03" option_set="All" title="Mali L2 Cache" name="Words written" description="Words written"/>
+ <event event="0x04" option_set="Slaves" title="Mali L2 Cache" name="Read hits" description="Read hits"/>
+ <event event="0x05" option_set="Slaves" title="Mali L2 Cache" name="Read misses" description="Read misses"/>
+ <event event="0x06" option_set="Slaves" title="Mali L2 Cache" name="Write invalidates" description="Write invalidates"/>
+ <event event="0x07" option_set="Slaves" title="Mali L2 Cache" name="Read invalidates" description="Read invalidates"/>
+ <event event="0x08" option_set="Slaves" title="Mali L2 Cache" name="Cacheable read transactions" description="Cacheable read transactions"/>
+ </category>
+ <counter_set name="ARM_Mali-4xx_L2_2_cnt" title="Mali-4xx L2 2" description="Mali GPU L2 Cache Core 2" count="2"/>
+ <category name="Mali-4xx-L2_2" counter_set="ARM_Mali-4xx_L2_2_cnt" per_cpu="no">
+ <event event="0x01" title="Mali L2 Cache" name="Total clock cycles" description="Total clock cycles"/>
+ <event event="0x02" title="Mali L2 Cache" name="Active clock cycles" description="Active clock cycles"/>
+
+ <option_set name="All">
+ <option event_delta="0x08" name="Master" description="Master"/>
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+
+ <option_set name="Slaves">
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+
+ <event event="0x00" option_set="All" title="Mali L2 Cache" name="Read transactions" description="Read transactions"/>
+ <event event="0x01" option_set="All" title="Mali L2 Cache" name="Write transactions" description="Write transactions"/>
+ <event event="0x02" option_set="All" title="Mali L2 Cache" name="Words read" description="Words read"/>
+ <event event="0x03" option_set="All" title="Mali L2 Cache" name="Words written" description="Words written"/>
+ <event event="0x04" option_set="Slaves" title="Mali L2 Cache" name="Read hits" description="Read hits"/>
+ <event event="0x05" option_set="Slaves" title="Mali L2 Cache" name="Read misses" description="Read misses"/>
+ <event event="0x06" option_set="Slaves" title="Mali L2 Cache" name="Write invalidates" description="Write invalidates"/>
+ <event event="0x07" option_set="Slaves" title="Mali L2 Cache" name="Read invalidates" description="Read invalidates"/>
+ <event event="0x08" option_set="Slaves" title="Mali L2 Cache" name="Cacheable read transactions" description="Cacheable read transactions"/>
+ </category>
+ <category name="ARM Mali-4xx Filmstrip" counter_set="ARM_Mali-4xx_Filmstrip_cnt" per_cpu="no">
+ <option_set name="fs">
+ <option event_delta="0x3c" name="1:60" description="captures every 60th frame"/>
+ <option event_delta="0x1e" name="1:30" description="captures every 30th frame"/>
+ <option event_delta="0xa" name="1:10" description="captures every 10th frame"/>
+ </option_set>
+ <event event="0x0400" option_set="fs" title="ARM Mali-4xx" name="Filmstrip" description="Scaled framebuffer"/>
+ </category>
+ <category name="ARM_Mali-4xx_Voltage" per_cpu="no">
+ <event counter="ARM_Mali-4xx_Voltage" title="Mali GPU Voltage" name="Voltage" display="average" average_selection="yes" units="mV" description="GPU core voltage."/>
+ </category>
+ <category name="ARM_Mali-4xx_Frequency" per_cpu="no">
+ <event counter="ARM_Mali-4xx_Frequency" title="Mali GPU Frequency" name="Frequency" display="average" average_selection="yes" units="MHz" description="GPU core frequency."/>
+ </category>
+ <category name="Mali-4xx-SW" counter_set="ARM_Mali-4xx_SW_cnt" per_cpu="no">
+ <!-- EGL Counters -->
+ <event counter="ARM_Mali-4xx_SW_0" title="Mali EGL Software Counters" name="Blit Time" description="Time spent blitting the framebuffer from video memory to framebuffer."/>
+ <!-- glDrawElements Counters -->
+ <event counter="ARM_Mali-4xx_SW_1" title="glDrawElements Statistics" name="Calls to glDrawElements" description="Number of calls to glDrawElements."/>
+ <event counter="ARM_Mali-4xx_SW_2" title="glDrawElements Statistics" name="Indices to glDrawElements" description="Number of indices to glDrawElements."/>
+ <event counter="ARM_Mali-4xx_SW_3" title="glDrawElements Statistics" name="Transformed by glDrawElements" description="Number of vertices transformed by glDrawElements."/>
+ <!-- glDrawArrays Counters -->
+ <event counter="ARM_Mali-4xx_SW_4" title="glDrawArrays Statistics" name="Calls to glDrawArrays" description="Number of calls to glDrawArrays."/>
+ <event counter="ARM_Mali-4xx_SW_5" title="glDrawArrays Statistics" name="Transformed by glDrawArrays" description="Number of vertices transformed by glDrawArrays."/>
+ <!-- Draw Call Counters -->
+ <event counter="ARM_Mali-4xx_SW_6" title="Drawcall Statistics" name="Points" description="Number of calls to glDraw* with parameter GL_POINTS."/>
+ <event counter="ARM_Mali-4xx_SW_7" title="Drawcall Statistics" name="Lines" description="Number of calls to glDraw* with parameter GL_LINES."/>
+ <event counter="ARM_Mali-4xx_SW_8" title="Drawcall Statistics" name="Lineloop" description="Number of calls to glDraw* with parameter GL_LINE_LOOP."/>
+ <event counter="ARM_Mali-4xx_SW_9" title="Drawcall Statistics" name="Linestrip" description="Number of calls to glDraw* with parameter GL_LINE_STRIP."/>
+ <event counter="ARM_Mali-4xx_SW_10" title="Drawcall Statistics" name="Triangles" description="Number of calls to glDraw* with parameter GL_TRIANGLES."/>
+ <event counter="ARM_Mali-4xx_SW_11" title="Drawcall Statistics" name="Trianglestrip" description="Number of calls to glDraw* with parameter GL_TRIANGLE_STRIP."/>
+ <event counter="ARM_Mali-4xx_SW_12" title="Drawcall Statistics" name="Trianglefan" description="Number of calls to glDraw* with parameter GL_TRIANGLE_FAN."/>
+ <event counter="ARM_Mali-4xx_SW_13" title="Drawcall Statistics" name="Vertex Upload Time (us)" description="Time spent uploading vertex attributes and faceindex data not present in a VBO."/>
+ <event counter="ARM_Mali-4xx_SW_14" title="Drawcall Statistics" name="Uniform Bytes Copied (bytes)" description="Number of bytes copied to Mali memory as a result of uniforms update."/>
+ <!-- Buffer Profiling Counters -->
+ <event counter="ARM_Mali-4xx_SW_15" title="Buffer Profiling" name="Texture Upload Time (ms)" description="Time spent uploading textures."/>
+ <event counter="ARM_Mali-4xx_SW_16" title="Buffer Profiling" name="VBO Upload Time (ms)" description="Time spent uploading vertex buffer objects."/>
+ <event counter="ARM_Mali-4xx_SW_17" title="Buffer Profiling" name="FBO Flushes" description="Number of flushed on framebuffer attachment."/>
+ <!-- OpenGL ES 1.1 Emulation -->
+ <event counter="ARM_Mali-4xx_SW_18" title="Fixed-function Emulation" name="# Vertex Shaders Generated" description="Number of vertex shaders generated."/>
+ <event counter="ARM_Mali-4xx_SW_19" title="Fixed-function Emulation" name="# Fragment Shaders Generated" description="Number of fragment shaders generated."/>
+ <!-- Geometry Statistics -->
+ <event counter="ARM_Mali-4xx_SW_33" title="Geometry Statistics" name="Triangles" description="The total number of triangles passed to GLES per-frame."/>
+ <event counter="ARM_Mali-4xx_SW_34" title="Geometry Statistics" name="Independent Triangles" description="Number of triangles passed to GLES using the mode GL_TRIANGLES."/>
+ <event counter="ARM_Mali-4xx_SW_35" title="Geometry Statistics" name="Strip Triangles" description="Number of triangles passed to GLES using the mode GL_TRIANGLE_STRIP."/>
+ <event counter="ARM_Mali-4xx_SW_36" title="Geometry Statistics" name="Fan Triangles" description="Number of triangles passed to GLES using the mode GL_TRIANGLE_FAN."/>
+ <event counter="ARM_Mali-4xx_SW_37" title="Geometry Statistics" name="Lines" description="Number of lines passed to GLES per-frame."/>
+ <event counter="ARM_Mali-4xx_SW_38" title="Geometry Statistics" name="Independent Lines" description="Number of lines passed to GLES using the mode GL_LINES."/>
+ <event counter="ARM_Mali-4xx_SW_39" title="Geometry Statistics" name="Strip Lines" description="Number of lines passed to GLES using the mode GL_LINE_STRIP."/>
+ <event counter="ARM_Mali-4xx_SW_40" title="Geometry Statistics" name="Loop Lines" description="Number of lines passed to GLES using the mode GL_LINE_LOOP."/>
+ </category>
diff --git a/tools/gator/daemon/events-Mali-T6xx.xml b/tools/gator/daemon/events-Mali-T6xx.xml
new file mode 100644
index 000000000000..647e3d5b0fcf
--- /dev/null
+++ b/tools/gator/daemon/events-Mali-T6xx.xml
@@ -0,0 +1,38 @@
+
+ <category name="Mali-T6xx-SW-counters" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_TOTAL_ALLOC_PAGES" title="Mali Total Alloc Pages" name="Total number of allocated pages" description="Mali total number of allocated pages."/>
+ </category>
+
+ <category name="Mali-T6xx-PMShader" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_PM_SHADER_0" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 0" description="Mali PM Shader: PM Shader Core 0."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_1" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 1" description="Mali PM Shader: PM Shader Core 1."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_2" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 2" description="Mali PM Shader: PM Shader Core 2."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_3" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 3" description="Mali PM Shader: PM Shader Core 3."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_4" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 4" description="Mali PM Shader: PM Shader Core 4."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_5" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 5" description="Mali PM Shader: PM Shader Core 5."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_6" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 6" description="Mali PM Shader: PM Shader Core 6."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_7" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 7" description="Mali PM Shader: PM Shader Core 7."/>
+ </category>
+
+ <category name="Mali-T6xx-PMTiler" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_PM_TILER_0" display="average" average_selection="yes" percentage="yes" title="Mali PM Tiler" name="PM Tiler Core 0" description="Mali PM Tiler: PM Tiler Core 0."/>
+ </category>
+
+ <category name="Mali-T6xx-PML2" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_PM_L2_0" display="average" average_selection="yes" percentage="yes" title="Mali PM L2" name="PM L2 Core 0" description="Mali PM L2: PM L2 Core 0."/>
+ <event counter="ARM_Mali-T6xx_PM_L2_1" display="average" average_selection="yes" percentage="yes" title="Mali PM L2" name="PM L2 Core 1" description="Mali PM L2: PM L2 Core 1."/>
+ </category>
+
+ <category name="Mali-T6xx-MMU_AS" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_MMU_AS_0" display="average" average_selection="yes" percentage="yes" title="Mali MMU Address Space" name="MMU Address Space 0" description="Mali MMU Address Space 0 usage."/>
+ <event counter="ARM_Mali-T6xx_MMU_AS_1" display="average" average_selection="yes" percentage="yes" title="Mali MMU Address Space" name="MMU Address Space 1" description="Mali MMU Address Space 1 usage."/>
+ <event counter="ARM_Mali-T6xx_MMU_AS_2" display="average" average_selection="yes" percentage="yes" title="Mali MMU Address Space" name="MMU Address Space 2" description="Mali MMU Address Space 2 usage."/>
+ <event counter="ARM_Mali-T6xx_MMU_AS_3" display="average" average_selection="yes" percentage="yes" title="Mali MMU Address Space" name="MMU Address Space 3" description="Mali MMU Address Space 3 usage."/>
+ </category>
+
+ <category name="Mali-T6xx-MMU_page_fault" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_MMU_PAGE_FAULT_0" title="Mali MMU Page Fault Add. Space" name="Mali MMU Page Fault Add. Space 0" description="Reports the number of newly allocated pages after a MMU page fault in address space 0."/>
+ <event counter="ARM_Mali-T6xx_MMU_PAGE_FAULT_1" title="Mali MMU Page Fault Add. Space" name="Mali MMU Page Fault Add. Space 1" description="Reports the number of newly allocated pages after a MMU page fault in address space 1."/>
+ <event counter="ARM_Mali-T6xx_MMU_PAGE_FAULT_2" title="Mali MMU Page Fault Add. Space" name="Mali MMU Page Fault Add. Space 2" description="Reports the number of newly allocated pages after a MMU page fault in address space 2."/>
+ <event counter="ARM_Mali-T6xx_MMU_PAGE_FAULT_3" title="Mali MMU Page Fault Add. Space" name="Mali MMU Page Fault Add. Space 3" description="Reports the number of newly allocated pages after a MMU page fault in address space 3."/>
+ </category>
diff --git a/tools/gator/daemon/events-Mali-T6xx_hw.xml b/tools/gator/daemon/events-Mali-T6xx_hw.xml
new file mode 100644
index 000000000000..8cfe7c3084d5
--- /dev/null
+++ b/tools/gator/daemon/events-Mali-T6xx_hw.xml
@@ -0,0 +1,113 @@
+
+ <category name="Mali-T6xx-JobManager" per_cpu="no">
+
+ <event counter="ARM_Mali-T6xx_GPU_ACTIVE" title="Mali Job Manager Cycles" name="GPU cycles" description="Number of cycles the GPU was active"/>
+ <event counter="ARM_Mali-T6xx_IRQ_ACTIVE" title="Mali Job Manager Cycles" name="IRQ cycles" description="Number of cycles the GPU had a pending interrupt"/>
+ <event counter="ARM_Mali-T6xx_JS0_ACTIVE" title="Mali Job Manager Cycles" name="JS0 cycles" description="Number of cycles JS0 (fragment) was active"/>
+ <event counter="ARM_Mali-T6xx_JS1_ACTIVE" title="Mali Job Manager Cycles" name="JS1 cycles" description="Number of cycles JS1 (vertex/tiler/compute) was active"/>
+ <event counter="ARM_Mali-T6xx_JS2_ACTIVE" title="Mali Job Manager Cycles" name="JS2 cycles" description="Number of cycles JS2 (vertex/compute) was active"/>
+
+ <event counter="ARM_Mali-T6xx_JS0_JOBS" title="Mali Job Manager Work" name="JS0 jobs" description="Number of Jobs (fragment) completed in JS0"/>
+ <event counter="ARM_Mali-T6xx_JS0_TASKS" title="Mali Job Manager Work" name="JS0 tasks" description="Number of Tasks completed in JS0"/>
+ <event counter="ARM_Mali-T6xx_JS1_JOBS" title="Mali Job Manager Work" name="JS1 jobs" description="Number of Jobs (vertex/tiler/compute) completed in JS1"/>
+ <event counter="ARM_Mali-T6xx_JS1_TASKS" title="Mali Job Manager Work" name="JS1 tasks" description="Number of Tasks completed in JS1"/>
+ <event counter="ARM_Mali-T6xx_JS2_TASKS" title="Mali Job Manager Work" name="JS2 tasks" description="Number of Tasks completed in JS2"/>
+ <event counter="ARM_Mali-T6xx_JS2_JOBS" title="Mali Job Manager Work" name="JS2 jobs" description="Number of Jobs (vertex/compute) completed in JS2"/>
+
+ </category>
+
+ <category name="Mali-T6xx-Tiler" per_cpu="no">
+
+ <event counter="ARM_Mali-T6xx_POLYGONS" title="Mali Tiler Primitives" name="Polygons" description="Number of polygons processed"/>
+ <event counter="ARM_Mali-T6xx_QUADS" title="Mali Tiler Primitives" name="Quads" description="Number of quads processed"/>
+ <event counter="ARM_Mali-T6xx_TRIANGLES" title="Mali Tiler Primitives" name="Triangles" description="Number of triangles processed"/>
+ <event counter="ARM_Mali-T6xx_LINES" title="Mali Tiler Primitives" name="Lines" description="Number of lines processed"/>
+ <event counter="ARM_Mali-T6xx_POINTS" title="Mali Tiler Primitives" name="Points" description="Number of points processed"/>
+
+ <event counter="ARM_Mali-T6xx_FRONT_FACING" title="Mali Tiler Culling" name="Front facing prims" description="Number of front facing primitives"/>
+ <event counter="ARM_Mali-T6xx_BACK_FACING" title="Mali Tiler Culling" name="Back facing prims" description="Number of back facing primitives"/>
+ <event counter="ARM_Mali-T6xx_PRIM_VISIBLE" title="Mali Tiler Culling" name="Visible prims" description="Number of visible primitives"/>
+ <event counter="ARM_Mali-T6xx_PRIM_CULLED" title="Mali Tiler Culling" name="Culled prims" description="Number of culled primitives"/>
+ <event counter="ARM_Mali-T6xx_PRIM_CLIPPED" title="Mali Tiler Culling" name="Clipped prims" description="Number of clipped primitives"/>
+
+ <event counter="ARM_Mali-T6xx_LEVEL0" title="Mali Tiler Hierarchy" name="L0 prims" description="Number of primitives in hierarchy level 0"/>
+ <event counter="ARM_Mali-T6xx_LEVEL1" title="Mali Tiler Hierarchy" name="L1 prims" description="Number of primitives in hierarchy level 1"/>
+ <event counter="ARM_Mali-T6xx_LEVEL2" title="Mali Tiler Hierarchy" name="L2 prims" description="Number of primitives in hierarchy level 2"/>
+ <event counter="ARM_Mali-T6xx_LEVEL3" title="Mali Tiler Hierarchy" name="L3 prims" description="Number of primitives in hierarchy level 3"/>
+ <event counter="ARM_Mali-T6xx_LEVEL4" title="Mali Tiler Hierarchy" name="L4 prims" description="Number of primitives in hierarchy level 4"/>
+ <event counter="ARM_Mali-T6xx_LEVEL5" title="Mali Tiler Hierarchy" name="L5 prims" description="Number of primitives in hierarchy level 5"/>
+ <event counter="ARM_Mali-T6xx_LEVEL6" title="Mali Tiler Hierarchy" name="L6 prims" description="Number of primitives in hierarchy level 6"/>
+ <event counter="ARM_Mali-T6xx_LEVEL7" title="Mali Tiler Hierarchy" name="L7 prims" description="Number of primitives in hierarchy level 7"/>
+
+ <event counter="ARM_Mali-T6xx_COMMAND_1" title="Mali Tiler Commands" name="Prims in 1 command" description="Number of primitives producing 1 command"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_2" title="Mali Tiler Commands" name="Prims in 2 command" description="Number of primitives producing 2 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_3" title="Mali Tiler Commands" name="Prims in 3 command" description="Number of primitives producing 3 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_4" title="Mali Tiler Commands" name="Prims in 4 command" description="Number of primitives producing 4 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_4_7" title="Mali Tiler Commands" name="Prims in 4-7 commands" description="Number of primitives producing 4-7 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_5_7" title="Mali Tiler Commands" name="Prims in 5-7 commands" description="Number of primitives producing 5-7 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_8_15" title="Mali Tiler Commands" name="Prims in 8-15 commands" description="Number of primitives producing 8-15 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_16_63" title="Mali Tiler Commands" name="Prims in 16-63 commands" description="Number of primitives producing 16-63 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_64" title="Mali Tiler Commands" name="Prims in &gt;= 64 commands" description="Number of primitives producing &gt;= 64 commands"/>
+
+ </category>
+
+ <category name="Mali-T6xx-ShaderCore" per_cpu="no">
+
+ <event counter="ARM_Mali-T6xx_TRIPIPE_ACTIVE" title="Mali Core Cycles" name="Tripipe cycles" description="Number of cycles the Tripipe was active"/>
+ <event counter="ARM_Mali-T6xx_FRAG_ACTIVE" title="Mali Core Cycles" name="Fragment cycles" description="Number of cycles fragment processing was active"/>
+ <event counter="ARM_Mali-T6xx_COMPUTE_ACTIVE" title="Mali Core Cycles" name="Compute cycles" description="Number of cycles vertex\compute processing was active"/>
+ <event counter="ARM_Mali-T6xx_FRAG_CYCLE_NO_TILE" title="Mali Core Cycles" name="Fragment cycles waiting for tile" description="Number of cycles spent waiting for a physical tile buffer"/>
+
+ <event counter="ARM_Mali-T6xx_FRAG_THREADS" title="Mali Core Threads" name="Fragment threads" description="Number of fragment threads started"/>
+ <event counter="ARM_Mali-T6xx_FRAG_DUMMY_THREADS" title="Mali Core Threads" name="Dummy fragment threads" description="Number of dummy fragment threads started"/>
+ <event counter="ARM_Mali-T6xx_COMPUTE_THREADS" title="Mali Core Threads" name="Compute threads" description="Number of vertex\compute threads started"/>
+ <event counter="ARM_Mali-T6xx_FRAG_QUADS_LZS_TEST" title="Mali Core Threads" name="Frag threads doing late ZS" description="Number of threads doing late ZS test"/>
+ <event counter="ARM_Mali-T6xx_FRAG_QUADS_LZS_KILLED" title="Mali Core Threads" name="Frag threads killed late ZS" description="Number of threads killed by late ZS test"/>
+ <event counter="ARM_Mali-T6xx_FRAG_THREADS_LZS_TEST" title="Mali Core Threads" name="Frag threads doing late ZS" description="Number of threads doing late ZS test"/>
+ <event counter="ARM_Mali-T6xx_FRAG_THREADS_LZS_KILLED" title="Mali Core Threads" name="Frag threads killed late ZS" description="Number of threads killed by late ZS test"/>
+
+ <event counter="ARM_Mali-T6xx_FRAG_PRIMATIVES" title="Mali Fragment Primitives" name="Primitives loaded" description="Number of primitives loaded from tiler"/>
+ <event counter="ARM_Mali-T6xx_FRAG_PRIMATIVES_DROPPED" title="Mali Fragment Primitives" name="Primitives dropped" description="Number of primitives dropped because out of tile"/>
+ <event counter="ARM_Mali-T6xx_FRAG_PRIMITIVES" title="Mali Fragment Primitives" name="Primitives loaded" description="Number of primitives loaded from tiler"/>
+ <event counter="ARM_Mali-T6xx_FRAG_PRIMITIVES_DROPPED" title="Mali Fragment Primitives" name="Primitives dropped" description="Number of primitives dropped because out of tile"/>
+
+ <event counter="ARM_Mali-T6xx_FRAG_QUADS_RAST" title="Mali Fragment Quads" name="Quads rasterized" description="Number of quads rasterized"/>
+ <event counter="ARM_Mali-T6xx_FRAG_QUADS_EZS_TEST" title="Mali Fragment Quads" name="Quads doing early ZS" description="Number of quads doing early ZS test"/>
+ <event counter="ARM_Mali-T6xx_FRAG_QUADS_EZS_KILLED" title="Mali Fragment Quads" name="Quads killed early Z" description="Number of quads killed by early ZS test"/>
+
+ <event counter="ARM_Mali-T6xx_FRAG_NUM_TILES" title="Mali Fragment Tasks" name="Tiles rendered" description="Number of tiles rendered"/>
+ <event counter="ARM_Mali-T6xx_FRAG_TRANS_ELIM" title="Mali Fragment Tasks" name="Tile writes killed by TE" description="Number of tile writes skipped by transaction elimination"/>
+
+ <event counter="ARM_Mali-T6xx_ARITH_WORDS" title="Mali Arithmetic Pipe" name="A instructions" description="Number of instructions completed by the the A-pipe (normalized per pipeline)"/>
+
+ <event counter="ARM_Mali-T6xx_LS_WORDS" title="Mali Load/Store Pipe" name="LS instructions" description="Number of instructions completed by the LS-pipe"/>
+ <event counter="ARM_Mali-T6xx_LS_ISSUES" title="Mali Load/Store Pipe" name="LS instruction issues" description="Number of instructions issued to the LS-pipe, including restarts"/>
+
+ <event counter="ARM_Mali-T6xx_TEX_WORDS" title="Mali Texture Pipe" name="T instructions" description="Number of instructions completed by the T-pipe"/>
+ <event counter="ARM_Mali-T6xx_TEX_THREADS" title="Mali Texture Pipe" name="T instruction issues" description="Number of instructions issused to the T-pipe, including restarts"/>
+ <event counter="ARM_Mali-T6xx_TEX_RECIRC_FMISS" title="Mali Texture Pipe" name="Cache misses" description="Number of instructions in the T-pipe, recirculated due to cache miss"/>
+
+ <event counter="ARM_Mali-T6xx_LSC_READ_HITS" title="Mali Load/Store Cache" name="Read hits" description="Number of read hits in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_READ_MISSES" title="Mali Load/Store Cache" name="Read misses" description="Number of read misses in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_WRITE_HITS" title="Mali Load/Store Cache" name="Write hits" description="Number of write hits in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_WRITE_MISSES" title="Mali Load/Store Cache" name="Write misses" description="Number of write misses in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_ATOMIC_HITS" title="Mali Load/Store Cache" name="Atomic hits" description="Number of atomic hits in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_ATOMIC_MISSES" title="Mali Load/Store Cache" name="Atomic misses" description="Number of atomic misses in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_LINE_FETCHES" title="Mali Load/Store Cache" name="Line fetches" description="Number of line fetches in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_DIRTY_LINE" title="Mali Load/Store Cache" name="Dirty line evictions" description="Number of dirty line evictions in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_SNOOPS" title="Mali Load/Store Cache" name="Snoops in to LSC" description="Number of coherent memory snoops in to the Load/Store cache"/>
+
+ </category>
+
+ <category name="Mali-T6xx-L2AndMMU" per_cpu="no">
+
+ <event counter="ARM_Mali-T6xx_L2_WRITE_BEATS" title="Mali L2 Cache" name="External write beats" description="Number of external bus write beats"/>
+ <event counter="ARM_Mali-T6xx_L2_READ_BEATS" title="Mali L2 Cache" name="External read beats" description="Number of external bus read beats"/>
+ <event counter="ARM_Mali-T6xx_L2_READ_SNOOP" title="Mali L2 Cache" name="Read snoops" description="Number of read transaction snoops"/>
+ <event counter="ARM_Mali-T6xx_L2_READ_HIT" title="Mali L2 Cache" name="L2 read hits" description="Number of reads hitting in the L2 cache"/>
+ <event counter="ARM_Mali-T6xx_L2_WRITE_SNOOP" title="Mali L2 Cache" name="Write snoops" description="Number of write transaction snoops"/>
+ <event counter="ARM_Mali-T6xx_L2_WRITE_HIT" title="Mali L2 Cache" name="L2 write hits" description="Number of writes hitting in the L2 cache"/>
+ <event counter="ARM_Mali-T6xx_L2_EXT_AR_STALL" title="Mali L2 Cache" name="External bus stalls (AR)" description="Number of cycles a valid read address (AR) is stalled by the external interconnect"/>
+ <event counter="ARM_Mali-T6xx_L2_EXT_W_STALL" title="Mali L2 Cache" name="External bus stalls (W)" description="Number of cycles a valid write data (W channel) is stalled by the external interconnect"/>
+
+ </category>
diff --git a/tools/gator/daemon/events-Scorpion.xml b/tools/gator/daemon/events-Scorpion.xml
new file mode 100644
index 000000000000..fa716fdc4840
--- /dev/null
+++ b/tools/gator/daemon/events-Scorpion.xml
@@ -0,0 +1,107 @@
+ <counter_set name="Scorpion_cnt" count="4"/>
+ <category name="Scorpion" counter_set="Scorpion_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="Scorpion_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Program Counter" name="SW change" description="Software change of PC, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0e" title="Branch" name="Procedure Return" description="Procedure return architecturally executed (not by exceptions)"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x4c" title="Scorpion" name="ICACHE_EXPL_INV" description="I-cache explicit invalidates"/>
+ <event event="0x4d" title="Scorpion" name="ICACHE_MISS" description="I-cache misses"/>
+ <event event="0x4e" title="Scorpion" name="ICACHE_ACCESS" description="I-cache accesses"/>
+ <event event="0x4f" title="Scorpion" name="ICACHE_CACHEREQ_L2" description="I-cache cacheable requests to L2"/>
+ <event event="0x50" title="Scorpion" name="ICACHE_NOCACHE_L2" description="I-cache non-cacheable requests to L2"/>
+ <event event="0x51" title="Scorpion" name="HIQUP_NOPED" description="Conditional instructions HIQUPs NOPed"/>
+ <event event="0x52" title="Scorpion" name="DATA_ABORT" description="Interrupts and Exceptions Data Abort"/>
+ <event event="0x53" title="Scorpion" name="IRQ" description="Interrupts and Exceptions IRQ"/>
+ <event event="0x54" title="Scorpion" name="FIQ" description="Interrupts and Exceptions FIQ"/>
+ <event event="0x55" title="Scorpion" name="ALL_EXCPT" description="Interrupts and Exceptions All interrupts"/>
+ <event event="0x56" title="Scorpion" name="UNDEF" description="Interrupts and Exceptions Undefined"/>
+ <event event="0x57" title="Scorpion" name="SVC" description="Interrupts and Exceptions SVC"/>
+ <event event="0x58" title="Scorpion" name="SMC" description="Interrupts and Exceptions SMC"/>
+ <event event="0x59" title="Scorpion" name="PREFETCH_ABORT" description="Interrupts and Exceptions Prefetch Abort"/>
+ <event event="0x5a" title="Scorpion" name="INDEX_CHECK" description="Interrupts and Exceptions Index Check"/>
+ <event event="0x5b" title="Scorpion" name="NULL_CHECK" description="Interrupts and Exceptions Null Check"/>
+ <event event="0x5c" title="Scorpion" name="EXPL_ICIALLU" description="I-cache and BTAC Invalidates Explicit ICIALLU"/>
+ <event event="0x5d" title="Scorpion" name="IMPL_ICIALLU" description="I-cache and BTAC Invalidates Implicit ICIALLU"/>
+ <event event="0x5e" title="Scorpion" name="NONICIALLU_BTAC_INV" description="I-cache and BTAC Invalidates Non-ICIALLU BTAC Invalidate"/>
+ <event event="0x5f" title="Scorpion" name="ICIMVAU_IMPL_ICIALLU" description="I-cache and BTAC Invalidates ICIMVAU-implied ICIALLU"/>
+ <event event="0x60" title="Scorpion" name="SPIPE_ONLY_CYCLES" description="Issue S-pipe only issue cycles"/>
+ <event event="0x61" title="Scorpion" name="XPIPE_ONLY_CYCLES" description="Issue X-pipe only issue cycles"/>
+ <event event="0x62" title="Scorpion" name="DUAL_CYCLES" description="Issue dual issue cycles"/>
+ <event event="0x63" title="Scorpion" name="DISPATCH_ANY_CYCLES" description="Dispatch any dispatch cycles"/>
+ <event event="0x64" title="Scorpion" name="FIFO_FULLBLK_CMT" description="Commits Trace FIFO full Blk CMT"/>
+ <event event="0x65" title="Scorpion" name="FAIL_COND_INST" description="Conditional instructions failing conditional instrs (excluding branches)"/>
+ <event event="0x66" title="Scorpion" name="PASS_COND_INST" description="Conditional instructions passing conditional instrs (excluding branches)"/>
+ <event event="0x67" title="Scorpion" name="ALLOW_VU_CLK" description="Unit Clock Gating Allow VU Clks"/>
+ <event event="0x68" title="Scorpion" name="VU_IDLE" description="Unit Clock Gating VU Idle"/>
+ <event event="0x69" title="Scorpion" name="ALLOW_L2_CLK" description="Unit Clock Gating Allow L2 Clks"/>
+ <event event="0x6a" title="Scorpion" name="L2_IDLE" description="Unit Clock Gating L2 Idle"/>
+ <event event="0x6b" title="Scorpion" name="DTLB_IMPL_INV_SCTLR_DACR" description="DTLB implicit invalidates writes to SCTLR and DACR"/>
+ <event event="0x6c" title="Scorpion" name="DTLB_EXPL_INV" description="DTLB explicit invalidates"/>
+ <event event="0x6d" title="Scorpion" name="DTLB_MISS" description="DTLB misses"/>
+ <event event="0x6e" title="Scorpion" name="DTLB_ACCESS" description="DTLB accesses"/>
+ <event event="0x6f" title="Scorpion" name="ITLB_MISS" description="ITLB misses"/>
+ <event event="0x70" title="Scorpion" name="ITLB_IMPL_INV" description="ITLB implicit ITLB invalidates"/>
+ <event event="0x71" title="Scorpion" name="ITLB_EXPL_INV" description="ITLB explicit ITLB invalidates"/>
+ <event event="0x72" title="Scorpion" name="UTLB_D_MISS" description="UTLB d-side misses"/>
+ <event event="0x73" title="Scorpion" name="UTLB_D_ACCESS" description="UTLB d-side accesses"/>
+ <event event="0x74" title="Scorpion" name="UTLB_I_MISS" description="UTLB i-side misses"/>
+ <event event="0x75" title="Scorpion" name="UTLB_I_ACCESS" description="UTLB i-side accesses"/>
+ <event event="0x76" title="Scorpion" name="UTLB_INV_ASID" description="UTLB invalidate by ASID"/>
+ <event event="0x77" title="Scorpion" name="UTLB_INV_MVA" description="UTLB invalidate by MVA"/>
+ <event event="0x78" title="Scorpion" name="UTLB_INV_ALL" description="UTLB invalidate all"/>
+ <event event="0x79" title="Scorpion" name="S2_HOLD_RDQ_UNAVAIL" description="S2 hold RDQ unavail"/>
+ <event event="0x7a" title="Scorpion" name="S2_HOLD" description="S2 hold"/>
+ <event event="0x7b" title="Scorpion" name="S2_HOLD_DEV_OP" description="S2 hold device op"/>
+ <event event="0x7c" title="Scorpion" name="S2_HOLD_ORDER" description="S2 hold strongly ordered op"/>
+ <event event="0x7d" title="Scorpion" name="S2_HOLD_BARRIER" description="S2 hold barrier"/>
+ <event event="0x7e" title="Scorpion" name="VIU_DUAL_CYCLE" description="Scorpion VIU dual cycle"/>
+ <event event="0x7f" title="Scorpion" name="VIU_SINGLE_CYCLE" description="Scorpion VIU single cycle"/>
+ <event event="0x80" title="Scorpion" name="VX_PIPE_WAR_STALL_CYCLES" description="Scorpion VX pipe WAR cycles"/>
+ <event event="0x81" title="Scorpion" name="VX_PIPE_WAW_STALL_CYCLES" description="Scorpion VX pipe WAW cycles"/>
+ <event event="0x82" title="Scorpion" name="VX_PIPE_RAW_STALL_CYCLES" description="Scorpion VX pipe RAW cycles"/>
+ <event event="0x83" title="Scorpion" name="VX_PIPE_LOAD_USE_STALL" description="Scorpion VX pipe load use stall"/>
+ <event event="0x84" title="Scorpion" name="VS_PIPE_WAR_STALL_CYCLES" description="Scorpion VS pipe WAR stall cycles"/>
+ <event event="0x85" title="Scorpion" name="VS_PIPE_WAW_STALL_CYCLES" description="Scorpion VS pipe WAW stall cycles"/>
+ <event event="0x86" title="Scorpion" name="VS_PIPE_RAW_STALL_CYCLES" description="Scorpion VS pipe RAW stall cycles"/>
+ <event event="0x87" title="Scorpion" name="EXCEPTIONS_INV_OPERATION" description="Scorpion invalid operation exceptions"/>
+ <event event="0x88" title="Scorpion" name="EXCEPTIONS_DIV_BY_ZERO" description="Scorpion divide by zero exceptions"/>
+ <event event="0x89" title="Scorpion" name="COND_INST_FAIL_VX_PIPE" description="Scorpion conditional instruction fail VX pipe"/>
+ <event event="0x8a" title="Scorpion" name="COND_INST_FAIL_VS_PIPE" description="Scorpion conditional instruction fail VS pipe"/>
+ <event event="0x8b" title="Scorpion" name="EXCEPTIONS_OVERFLOW" description="Scorpion overflow exceptions"/>
+ <event event="0x8c" title="Scorpion" name="EXCEPTIONS_UNDERFLOW" description="Scorpion underflow exceptions"/>
+ <event event="0x8d" title="Scorpion" name="EXCEPTIONS_DENORM" description="Scorpion denorm exceptions"/>
+ <event event="0x8e" title="Scorpion" name="BANK_AB_HIT" description="L2 hit rates bank A/B hits"/>
+ <event event="0x8f" title="Scorpion" name="BANK_AB_ACCESS" description="L2 hit rates bank A/B accesses"/>
+ <event event="0x90" title="Scorpion" name="BANK_CD_HIT" description="L2 hit rates bank C/D hits"/>
+ <event event="0x91" title="Scorpion" name="BANK_CD_ACCESS" description="L2 hit rates bank C/D accesses"/>
+ <event event="0x92" title="Scorpion" name="BANK_AB_DSIDE_HIT" description="L2 hit rates bank A/B d-side hits"/>
+ <event event="0x93" title="Scorpion" name="BANK_AB_DSIDE_ACCESS" description="L2 hit rates bank A/B d-side accesses"/>
+ <event event="0x94" title="Scorpion" name="BANK_CD_DSIDE_HIT" description="L2 hit rates bank C/D d-side hits"/>
+ <event event="0x95" title="Scorpion" name="BANK_CD_DSIDE_ACCESS" description="L2 hit rates bank C/D d-side accesses"/>
+ <event event="0x96" title="Scorpion" name="BANK_AB_ISIDE_HIT" description="L2 hit rates bank A/B i-side hits"/>
+ <event event="0x97" title="Scorpion" name="BANK_AB_ISIDE_ACCESS" description="L2 hit rates bank A/B i-side accesses"/>
+ <event event="0x98" title="Scorpion" name="BANK_CD_ISIDE_HIT" description="L2 hit rates bank C/D i-side hits"/>
+ <event event="0x99" title="Scorpion" name="BANK_CD_ISIDE_ACCESS" description="L2 hit rates bank C/D i-side accesses"/>
+ <event event="0x9a" title="Scorpion" name="ISIDE_RD_WAIT" description="fills and castouts cycles that i-side RD requests wait on data from bus"/>
+ <event event="0x9b" title="Scorpion" name="DSIDE_RD_WAIT" description="fills and castouts cycles that d-side RD requests wait on data from bus"/>
+ <event event="0x9c" title="Scorpion" name="BANK_BYPASS_WRITE" description="fills and castouts bank bypass writes"/>
+ <event event="0x9d" title="Scorpion" name="BANK_AB_NON_CASTOUT" description="fills and castouts bank A/B non-castout writes to bus"/>
+ <event event="0x9e" title="Scorpion" name="BANK_AB_L2_CASTOUT" description="fills and castouts bank A/B L2 castouts (granules)"/>
+ <event event="0x9f" title="Scorpion" name="BANK_CD_NON_CASTOUT" description="fills and castouts bank C/D non-castout writes to bus"/>
+ <event event="0xa0" title="Scorpion" name="BANK_CD_L2_CASTOUT" description="fills and castouts bank C/D L2 castouts (granules)"/>
+ </category>
diff --git a/tools/gator/daemon/events-ScorpionMP.xml b/tools/gator/daemon/events-ScorpionMP.xml
new file mode 100644
index 000000000000..c648ccefb287
--- /dev/null
+++ b/tools/gator/daemon/events-ScorpionMP.xml
@@ -0,0 +1,90 @@
+ <counter_set name="ScorpionMP_cnt" count="4"/>
+ <category name="ScorpionMP" counter_set="ScorpionMP_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ScorpionMP_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Program Counter" name="SW change" description="Software change of PC, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0e" title="Branch" name="Procedure Return" description="Procedure return architecturally executed (not by exceptions)"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x4c" title="Scorpion" name="ICACHE_EXPL_INV" description="I-cache explicit invalidates"/>
+ <event event="0x4d" title="Scorpion" name="ICACHE_MISS" description="I-cache misses"/>
+ <event event="0x4e" title="Scorpion" name="ICACHE_ACCESS" description="I-cache accesses"/>
+ <event event="0x4f" title="Scorpion" name="ICACHE_CACHEREQ_L2" description="I-cache cacheable requests to L2"/>
+ <event event="0x50" title="Scorpion" name="ICACHE_NOCACHE_L2" description="I-cache non-cacheable requests to L2"/>
+ <event event="0x51" title="Scorpion" name="HIQUP_NOPED" description="Conditional instructions HIQUPs NOPed"/>
+ <event event="0x52" title="Scorpion" name="DATA_ABORT" description="Interrupts and Exceptions Data Abort"/>
+ <event event="0x53" title="Scorpion" name="IRQ" description="Interrupts and Exceptions IRQ"/>
+ <event event="0x54" title="Scorpion" name="FIQ" description="Interrupts and Exceptions FIQ"/>
+ <event event="0x55" title="Scorpion" name="ALL_EXCPT" description="Interrupts and Exceptions All interrupts"/>
+ <event event="0x56" title="Scorpion" name="UNDEF" description="Interrupts and Exceptions Undefined"/>
+ <event event="0x57" title="Scorpion" name="SVC" description="Interrupts and Exceptions SVC"/>
+ <event event="0x58" title="Scorpion" name="SMC" description="Interrupts and Exceptions SMC"/>
+ <event event="0x59" title="Scorpion" name="PREFETCH_ABORT" description="Interrupts and Exceptions Prefetch Abort"/>
+ <event event="0x5a" title="Scorpion" name="INDEX_CHECK" description="Interrupts and Exceptions Index Check"/>
+ <event event="0x5b" title="Scorpion" name="NULL_CHECK" description="Interrupts and Exceptions Null Check"/>
+ <event event="0x5c" title="Scorpion" name="EXPL_ICIALLU" description="I-cache and BTAC Invalidates Explicit ICIALLU"/>
+ <event event="0x5d" title="Scorpion" name="IMPL_ICIALLU" description="I-cache and BTAC Invalidates Implicit ICIALLU"/>
+ <event event="0x5e" title="Scorpion" name="NONICIALLU_BTAC_INV" description="I-cache and BTAC Invalidates Non-ICIALLU BTAC Invalidate"/>
+ <event event="0x5f" title="Scorpion" name="ICIMVAU_IMPL_ICIALLU" description="I-cache and BTAC Invalidates ICIMVAU-implied ICIALLU"/>
+ <event event="0x60" title="Scorpion" name="SPIPE_ONLY_CYCLES" description="Issue S-pipe only issue cycles"/>
+ <event event="0x61" title="Scorpion" name="XPIPE_ONLY_CYCLES" description="Issue X-pipe only issue cycles"/>
+ <event event="0x62" title="Scorpion" name="DUAL_CYCLES" description="Issue dual issue cycles"/>
+ <event event="0x63" title="Scorpion" name="DISPATCH_ANY_CYCLES" description="Dispatch any dispatch cycles"/>
+ <event event="0x64" title="Scorpion" name="FIFO_FULLBLK_CMT" description="Commits Trace FIFO full Blk CMT"/>
+ <event event="0x65" title="Scorpion" name="FAIL_COND_INST" description="Conditional instructions failing conditional instrs (excluding branches)"/>
+ <event event="0x66" title="Scorpion" name="PASS_COND_INST" description="Conditional instructions passing conditional instrs (excluding branches)"/>
+ <event event="0x67" title="Scorpion" name="ALLOW_VU_CLK" description="Unit Clock Gating Allow VU Clks"/>
+ <event event="0x68" title="Scorpion" name="VU_IDLE" description="Unit Clock Gating VU Idle"/>
+ <event event="0x69" title="Scorpion" name="ALLOW_L2_CLK" description="Unit Clock Gating Allow L2 Clks"/>
+ <event event="0x6a" title="Scorpion" name="L2_IDLE" description="Unit Clock Gating L2 Idle"/>
+ <event event="0x6b" title="Scorpion" name="DTLB_IMPL_INV_SCTLR_DACR" description="DTLB implicit invalidates writes to SCTLR and DACR"/>
+ <event event="0x6c" title="Scorpion" name="DTLB_EXPL_INV" description="DTLB explicit invalidates"/>
+ <event event="0x6d" title="Scorpion" name="DTLB_MISS" description="DTLB misses"/>
+ <event event="0x6e" title="Scorpion" name="DTLB_ACCESS" description="DTLB accesses"/>
+ <event event="0x6f" title="Scorpion" name="ITLB_MISS" description="ITLB misses"/>
+ <event event="0x70" title="Scorpion" name="ITLB_IMPL_INV" description="ITLB implicit ITLB invalidates"/>
+ <event event="0x71" title="Scorpion" name="ITLB_EXPL_INV" description="ITLB explicit ITLB invalidates"/>
+ <event event="0x72" title="Scorpion" name="UTLB_D_MISS" description="UTLB d-side misses"/>
+ <event event="0x73" title="Scorpion" name="UTLB_D_ACCESS" description="UTLB d-side accesses"/>
+ <event event="0x74" title="Scorpion" name="UTLB_I_MISS" description="UTLB i-side misses"/>
+ <event event="0x75" title="Scorpion" name="UTLB_I_ACCESS" description="UTLB i-side accesses"/>
+ <event event="0x76" title="Scorpion" name="UTLB_INV_ASID" description="UTLB invalidate by ASID"/>
+ <event event="0x77" title="Scorpion" name="UTLB_INV_MVA" description="UTLB invalidate by MVA"/>
+ <event event="0x78" title="Scorpion" name="UTLB_INV_ALL" description="UTLB invalidate all"/>
+ <event event="0x79" title="Scorpion" name="S2_HOLD_RDQ_UNAVAIL" description="S2 hold RDQ unavail"/>
+ <event event="0x7a" title="Scorpion" name="S2_HOLD" description="S2 hold"/>
+ <event event="0x7b" title="Scorpion" name="S2_HOLD_DEV_OP" description="S2 hold device op"/>
+ <event event="0x7c" title="Scorpion" name="S2_HOLD_ORDER" description="S2 hold strongly ordered op"/>
+ <event event="0x7d" title="Scorpion" name="S2_HOLD_BARRIER" description="S2 hold barrier"/>
+ <event event="0x7e" title="Scorpion" name="VIU_DUAL_CYCLE" description="Scorpion VIU dual cycle"/>
+ <event event="0x7f" title="Scorpion" name="VIU_SINGLE_CYCLE" description="Scorpion VIU single cycle"/>
+ <event event="0x80" title="Scorpion" name="VX_PIPE_WAR_STALL_CYCLES" description="Scorpion VX pipe WAR cycles"/>
+ <event event="0x81" title="Scorpion" name="VX_PIPE_WAW_STALL_CYCLES" description="Scorpion VX pipe WAW cycles"/>
+ <event event="0x82" title="Scorpion" name="VX_PIPE_RAW_STALL_CYCLES" description="Scorpion VX pipe RAW cycles"/>
+ <event event="0x83" title="Scorpion" name="VX_PIPE_LOAD_USE_STALL" description="Scorpion VX pipe load use stall"/>
+ <event event="0x84" title="Scorpion" name="VS_PIPE_WAR_STALL_CYCLES" description="Scorpion VS pipe WAR stall cycles"/>
+ <event event="0x85" title="Scorpion" name="VS_PIPE_WAW_STALL_CYCLES" description="Scorpion VS pipe WAW stall cycles"/>
+ <event event="0x86" title="Scorpion" name="VS_PIPE_RAW_STALL_CYCLES" description="Scorpion VS pipe RAW stall cycles"/>
+ <event event="0x87" title="Scorpion" name="EXCEPTIONS_INV_OPERATION" description="Scorpion invalid operation exceptions"/>
+ <event event="0x88" title="Scorpion" name="EXCEPTIONS_DIV_BY_ZERO" description="Scorpion divide by zero exceptions"/>
+ <event event="0x89" title="Scorpion" name="COND_INST_FAIL_VX_PIPE" description="Scorpion conditional instruction fail VX pipe"/>
+ <event event="0x8a" title="Scorpion" name="COND_INST_FAIL_VS_PIPE" description="Scorpion conditional instruction fail VS pipe"/>
+ <event event="0x8b" title="Scorpion" name="EXCEPTIONS_OVERFLOW" description="Scorpion overflow exceptions"/>
+ <event event="0x8c" title="Scorpion" name="EXCEPTIONS_UNDERFLOW" description="Scorpion underflow exceptions"/>
+ <event event="0x8d" title="Scorpion" name="EXCEPTIONS_DENORM" description="Scorpion denorm exceptions"/>
+ <event event="0x8e" title="ScorpionMP" name="NUM_BARRIERS" description="Barriers"/>
+ <event event="0x8f" title="ScorpionMP" name="BARRIER_CYCLES" description="Barrier cycles"/>
+ </category>
diff --git a/tools/gator/daemon/events_footer.xml b/tools/gator/daemon/events_footer.xml
new file mode 100644
index 000000000000..cd2b44665bab
--- /dev/null
+++ b/tools/gator/daemon/events_footer.xml
@@ -0,0 +1 @@
+</events>
diff --git a/tools/gator/daemon/events_header.xml b/tools/gator/daemon/events_header.xml
new file mode 100644
index 000000000000..38ec4c03246e
--- /dev/null
+++ b/tools/gator/daemon/events_header.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<events>
diff --git a/tools/gator/daemon/libsensors/COPYING.LGPL b/tools/gator/daemon/libsensors/COPYING.LGPL
new file mode 100644
index 000000000000..4362b49151d7
--- /dev/null
+++ b/tools/gator/daemon/libsensors/COPYING.LGPL
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/tools/gator/daemon/libsensors/access.c b/tools/gator/daemon/libsensors/access.c
new file mode 100644
index 000000000000..8e227e2550db
--- /dev/null
+++ b/tools/gator/daemon/libsensors/access.c
@@ -0,0 +1,561 @@
+/*
+ access.c - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007-2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "access.h"
+#include "sensors.h"
+#include "data.h"
+#include "error.h"
+#include "sysfs.h"
+
+/* We watch the recursion depth for variables only, as an easy way to
+ detect cycles. */
+#define DEPTH_MAX 8
+
+static int sensors_eval_expr(const sensors_chip_features *chip_features,
+ const sensors_expr *expr,
+ double val, int depth, double *result);
+
+/* Compare two chips name descriptions, to see whether they could match.
+ Return 0 if it does not match, return 1 if it does match. */
+static int sensors_match_chip(const sensors_chip_name *chip1,
+ const sensors_chip_name *chip2)
+{
+ if ((chip1->prefix != SENSORS_CHIP_NAME_PREFIX_ANY) &&
+ (chip2->prefix != SENSORS_CHIP_NAME_PREFIX_ANY) &&
+ strcmp(chip1->prefix, chip2->prefix))
+ return 0;
+
+ if ((chip1->bus.type != SENSORS_BUS_TYPE_ANY) &&
+ (chip2->bus.type != SENSORS_BUS_TYPE_ANY) &&
+ (chip1->bus.type != chip2->bus.type))
+ return 0;
+
+ if ((chip1->bus.nr != SENSORS_BUS_NR_ANY) &&
+ (chip2->bus.nr != SENSORS_BUS_NR_ANY) &&
+ (chip1->bus.nr != chip2->bus.nr))
+ return 0;
+
+ if ((chip1->addr != chip2->addr) &&
+ (chip1->addr != SENSORS_CHIP_NAME_ADDR_ANY) &&
+ (chip2->addr != SENSORS_CHIP_NAME_ADDR_ANY))
+ return 0;
+
+ return 1;
+}
+
+/* Returns, one by one, a pointer to all sensor_chip structs of the
+ config file which match with the given chip name. Last should be
+ the value returned by the last call, or NULL if this is the first
+ call. Returns NULL if no more matches are found. Do not modify
+ the struct the return value points to!
+ Note that this visits the list of chips from last to first. Usually,
+ you want the match that was latest in the config file. */
+static sensors_chip *
+sensors_for_all_config_chips(const sensors_chip_name *name,
+ const sensors_chip *last)
+{
+ int nr, i;
+ sensors_chip_name_list chips;
+
+ for (nr = last ? last - sensors_config_chips - 1 :
+ sensors_config_chips_count - 1; nr >= 0; nr--) {
+
+ chips = sensors_config_chips[nr].chips;
+ for (i = 0; i < chips.fits_count; i++) {
+ if (sensors_match_chip(&chips.fits[i], name))
+ return sensors_config_chips + nr;
+ }
+ }
+ return NULL;
+}
+
+/* Look up a chip in the intern chip list, and return a pointer to it.
+ Do not modify the struct the return value points to! Returns NULL if
+ not found.*/
+static const sensors_chip_features *
+sensors_lookup_chip(const sensors_chip_name *name)
+{
+ int i;
+
+ for (i = 0; i < sensors_proc_chips_count; i++)
+ if (sensors_match_chip(&sensors_proc_chips[i].chip, name))
+ return &sensors_proc_chips[i];
+
+ return NULL;
+}
+
+/* Look up a subfeature of the given chip, and return a pointer to it.
+ Do not modify the struct the return value points to! Returns NULL if
+ not found.*/
+static const sensors_subfeature *
+sensors_lookup_subfeature_nr(const sensors_chip_features *chip,
+ int subfeat_nr)
+{
+ if (subfeat_nr < 0 ||
+ subfeat_nr >= chip->subfeature_count)
+ return NULL;
+ return chip->subfeature + subfeat_nr;
+}
+
+/* Look up a feature of the given chip, and return a pointer to it.
+ Do not modify the struct the return value points to! Returns NULL if
+ not found.*/
+static const sensors_feature *
+sensors_lookup_feature_nr(const sensors_chip_features *chip, int feat_nr)
+{
+ if (feat_nr < 0 ||
+ feat_nr >= chip->feature_count)
+ return NULL;
+ return chip->feature + feat_nr;
+}
+
+/* Look up a subfeature by name, and return a pointer to it.
+ Do not modify the struct the return value points to! Returns NULL if
+ not found.*/
+static const sensors_subfeature *
+sensors_lookup_subfeature_name(const sensors_chip_features *chip,
+ const char *name)
+{
+ int j;
+
+ for (j = 0; j < chip->subfeature_count; j++)
+ if (!strcmp(chip->subfeature[j].name, name))
+ return chip->subfeature + j;
+ return NULL;
+}
+
+/* Check whether the chip name is an 'absolute' name, which can only match
+ one chip, or whether it has wildcards. Returns 0 if it is absolute, 1
+ if there are wildcards. */
+int sensors_chip_name_has_wildcards(const sensors_chip_name *chip)
+{
+ if ((chip->prefix == SENSORS_CHIP_NAME_PREFIX_ANY) ||
+ (chip->bus.type == SENSORS_BUS_TYPE_ANY) ||
+ (chip->bus.nr == SENSORS_BUS_NR_ANY) ||
+ (chip->addr == SENSORS_CHIP_NAME_ADDR_ANY))
+ return 1;
+ else
+ return 0;
+}
+
+/* Look up the label for a given feature. Note that chip should not
+ contain wildcard values! The returned string is newly allocated (free it
+ yourself). On failure, NULL is returned.
+ If no label exists for this feature, its name is returned itself. */
+char *sensors_get_label(const sensors_chip_name *name,
+ const sensors_feature *feature)
+{
+ char *label;
+ const sensors_chip *chip;
+ char buf[PATH_MAX];
+ FILE *f;
+ int i;
+
+ if (sensors_chip_name_has_wildcards(name))
+ return NULL;
+
+ for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));)
+ for (i = 0; i < chip->labels_count; i++)
+ if (!strcmp(feature->name, chip->labels[i].name)) {
+ label = chip->labels[i].value;
+ goto sensors_get_label_exit;
+ }
+
+ /* No user specified label, check for a _label sysfs file */
+ snprintf(buf, PATH_MAX, "%s/%s_label", name->path, feature->name);
+
+ if ((f = fopen(buf, "r"))) {
+ i = fread(buf, 1, sizeof(buf), f);
+ fclose(f);
+ if (i > 0) {
+ /* i - 1 to strip the '\n' at the end */
+ buf[i - 1] = 0;
+ label = buf;
+ goto sensors_get_label_exit;
+ }
+ }
+
+ /* No label, return the feature name instead */
+ label = feature->name;
+
+sensors_get_label_exit:
+ label = strdup(label);
+ if (!label)
+ sensors_fatal_error(__func__, "Allocating label text");
+ return label;
+}
+
+/* Looks up whether a feature should be ignored. Returns
+ 1 if it should be ignored, 0 if not. */
+static int sensors_get_ignored(const sensors_chip_name *name,
+ const sensors_feature *feature)
+{
+ const sensors_chip *chip;
+ int i;
+
+ for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));)
+ for (i = 0; i < chip->ignores_count; i++)
+ if (!strcmp(feature->name, chip->ignores[i].name))
+ return 1;
+ return 0;
+}
+
+/* Read the value of a subfeature of a certain chip. Note that chip should not
+ contain wildcard values! This function will return 0 on success, and <0
+ on failure. */
+static int __sensors_get_value(const sensors_chip_name *name, int subfeat_nr,
+ int depth, double *result)
+{
+ const sensors_chip_features *chip_features;
+ const sensors_subfeature *subfeature;
+ const sensors_expr *expr = NULL;
+ double val;
+ int res, i;
+
+ if (depth >= DEPTH_MAX)
+ return -SENSORS_ERR_RECURSION;
+ if (sensors_chip_name_has_wildcards(name))
+ return -SENSORS_ERR_WILDCARDS;
+ if (!(chip_features = sensors_lookup_chip(name)))
+ return -SENSORS_ERR_NO_ENTRY;
+ if (!(subfeature = sensors_lookup_subfeature_nr(chip_features,
+ subfeat_nr)))
+ return -SENSORS_ERR_NO_ENTRY;
+ if (!(subfeature->flags & SENSORS_MODE_R))
+ return -SENSORS_ERR_ACCESS_R;
+
+ /* Apply compute statement if it exists */
+ if (subfeature->flags & SENSORS_COMPUTE_MAPPING) {
+ const sensors_feature *feature;
+ const sensors_chip *chip;
+
+ feature = sensors_lookup_feature_nr(chip_features,
+ subfeature->mapping);
+
+ chip = NULL;
+ while (!expr &&
+ (chip = sensors_for_all_config_chips(name, chip)))
+ for (i = 0; i < chip->computes_count; i++) {
+ if (!strcmp(feature->name,
+ chip->computes[i].name)) {
+ expr = chip->computes[i].from_proc;
+ break;
+ }
+ }
+ }
+
+ res = sensors_read_sysfs_attr(name, subfeature, &val);
+ if (res)
+ return res;
+ if (!expr)
+ *result = val;
+ else if ((res = sensors_eval_expr(chip_features, expr, val, depth,
+ result)))
+ return res;
+ return 0;
+}
+
+int sensors_get_value(const sensors_chip_name *name, int subfeat_nr,
+ double *result)
+{
+ return __sensors_get_value(name, subfeat_nr, 0, result);
+}
+
+/* Set the value of a subfeature of a certain chip. Note that chip should not
+ contain wildcard values! This function will return 0 on success, and <0
+ on failure. */
+int sensors_set_value(const sensors_chip_name *name, int subfeat_nr,
+ double value)
+{
+ const sensors_chip_features *chip_features;
+ const sensors_subfeature *subfeature;
+ const sensors_expr *expr = NULL;
+ int i, res;
+ double to_write;
+
+ if (sensors_chip_name_has_wildcards(name))
+ return -SENSORS_ERR_WILDCARDS;
+ if (!(chip_features = sensors_lookup_chip(name)))
+ return -SENSORS_ERR_NO_ENTRY;
+ if (!(subfeature = sensors_lookup_subfeature_nr(chip_features,
+ subfeat_nr)))
+ return -SENSORS_ERR_NO_ENTRY;
+ if (!(subfeature->flags & SENSORS_MODE_W))
+ return -SENSORS_ERR_ACCESS_W;
+
+ /* Apply compute statement if it exists */
+ if (subfeature->flags & SENSORS_COMPUTE_MAPPING) {
+ const sensors_feature *feature;
+ const sensors_chip *chip;
+
+ feature = sensors_lookup_feature_nr(chip_features,
+ subfeature->mapping);
+
+ chip = NULL;
+ while (!expr &&
+ (chip = sensors_for_all_config_chips(name, chip)))
+ for (i = 0; i < chip->computes_count; i++) {
+ if (!strcmp(feature->name,
+ chip->computes[i].name)) {
+ expr = chip->computes[i].to_proc;
+ break;
+ }
+ }
+ }
+
+ to_write = value;
+ if (expr)
+ if ((res = sensors_eval_expr(chip_features, expr,
+ value, 0, &to_write)))
+ return res;
+ return sensors_write_sysfs_attr(name, subfeature, to_write);
+}
+
+const sensors_chip_name *sensors_get_detected_chips(const sensors_chip_name
+ *match, int *nr)
+{
+ const sensors_chip_name *res;
+
+ while (*nr < sensors_proc_chips_count) {
+ res = &sensors_proc_chips[(*nr)++].chip;
+ if (!match || sensors_match_chip(res, match))
+ return res;
+ }
+ return NULL;
+}
+
+const char *sensors_get_adapter_name(const sensors_bus_id *bus)
+{
+ int i;
+
+ /* bus types with a single instance */
+ switch (bus->type) {
+ case SENSORS_BUS_TYPE_ISA:
+ return "ISA adapter";
+ case SENSORS_BUS_TYPE_PCI:
+ return "PCI adapter";
+ /* SPI should not be here, but for now SPI adapters have no name
+ so we don't have any custom string to return. */
+ case SENSORS_BUS_TYPE_SPI:
+ return "SPI adapter";
+ case SENSORS_BUS_TYPE_VIRTUAL:
+ return "Virtual device";
+ case SENSORS_BUS_TYPE_ACPI:
+ return "ACPI interface";
+ /* HID should probably not be there either, but I don't know if
+ HID buses have a name nor where to find it. */
+ case SENSORS_BUS_TYPE_HID:
+ return "HID adapter";
+ }
+
+ /* bus types with several instances */
+ for (i = 0; i < sensors_proc_bus_count; i++)
+ if (sensors_proc_bus[i].bus.type == bus->type &&
+ sensors_proc_bus[i].bus.nr == bus->nr)
+ return sensors_proc_bus[i].adapter;
+ return NULL;
+}
+
+const sensors_feature *
+sensors_get_features(const sensors_chip_name *name, int *nr)
+{
+ const sensors_chip_features *chip;
+
+ if (!(chip = sensors_lookup_chip(name)))
+ return NULL; /* No such chip */
+
+ while (*nr < chip->feature_count
+ && sensors_get_ignored(name, &chip->feature[*nr]))
+ (*nr)++;
+ if (*nr >= chip->feature_count)
+ return NULL;
+ return &chip->feature[(*nr)++];
+}
+
+const sensors_subfeature *
+sensors_get_all_subfeatures(const sensors_chip_name *name,
+ const sensors_feature *feature, int *nr)
+{
+ const sensors_chip_features *chip;
+ const sensors_subfeature *subfeature;
+
+ if (!(chip = sensors_lookup_chip(name)))
+ return NULL; /* No such chip */
+
+ /* Seek directly to the first subfeature */
+ if (*nr < feature->first_subfeature)
+ *nr = feature->first_subfeature;
+
+ if (*nr >= chip->subfeature_count)
+ return NULL; /* end of list */
+ subfeature = &chip->subfeature[(*nr)++];
+ if (subfeature->mapping == feature->number)
+ return subfeature;
+ return NULL; /* end of subfeature list */
+}
+
+const sensors_subfeature *
+sensors_get_subfeature(const sensors_chip_name *name,
+ const sensors_feature *feature,
+ sensors_subfeature_type type)
+{
+ const sensors_chip_features *chip;
+ int i;
+
+ if (!(chip = sensors_lookup_chip(name)))
+ return NULL; /* No such chip */
+
+ for (i = feature->first_subfeature; i < chip->subfeature_count &&
+ chip->subfeature[i].mapping == feature->number; i++) {
+ if (chip->subfeature[i].type == type)
+ return &chip->subfeature[i];
+ }
+ return NULL; /* No such subfeature */
+}
+
+/* Evaluate an expression */
+int sensors_eval_expr(const sensors_chip_features *chip_features,
+ const sensors_expr *expr,
+ double val, int depth, double *result)
+{
+ double res1, res2;
+ int res;
+ const sensors_subfeature *subfeature;
+
+ if (expr->kind == sensors_kind_val) {
+ *result = expr->data.val;
+ return 0;
+ }
+ if (expr->kind == sensors_kind_source) {
+ *result = val;
+ return 0;
+ }
+ if (expr->kind == sensors_kind_var) {
+ if (!(subfeature = sensors_lookup_subfeature_name(chip_features,
+ expr->data.var)))
+ return -SENSORS_ERR_NO_ENTRY;
+ return __sensors_get_value(&chip_features->chip,
+ subfeature->number, depth + 1,
+ result);
+ }
+ if ((res = sensors_eval_expr(chip_features, expr->data.subexpr.sub1,
+ val, depth, &res1)))
+ return res;
+ if (expr->data.subexpr.sub2 &&
+ (res = sensors_eval_expr(chip_features, expr->data.subexpr.sub2,
+ val, depth, &res2)))
+ return res;
+ switch (expr->data.subexpr.op) {
+ case sensors_add:
+ *result = res1 + res2;
+ return 0;
+ case sensors_sub:
+ *result = res1 - res2;
+ return 0;
+ case sensors_multiply:
+ *result = res1 * res2;
+ return 0;
+ case sensors_divide:
+ if (res2 == 0.0)
+ return -SENSORS_ERR_DIV_ZERO;
+ *result = res1 / res2;
+ return 0;
+ case sensors_negate:
+ *result = -res1;
+ return 0;
+ case sensors_exp:
+ *result = exp(res1);
+ return 0;
+ case sensors_log:
+ if (res1 < 0.0)
+ return -SENSORS_ERR_DIV_ZERO;
+ *result = log(res1);
+ return 0;
+ }
+ return 0;
+}
+
+/* Execute all set statements for this particular chip. The chip may not
+ contain wildcards! This function will return 0 on success, and <0 on
+ failure. */
+static int sensors_do_this_chip_sets(const sensors_chip_name *name)
+{
+ const sensors_chip_features *chip_features;
+ sensors_chip *chip;
+ double value;
+ int i;
+ int err = 0, res;
+ const sensors_subfeature *subfeature;
+
+ chip_features = sensors_lookup_chip(name); /* Can't fail */
+
+ for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));)
+ for (i = 0; i < chip->sets_count; i++) {
+ subfeature = sensors_lookup_subfeature_name(chip_features,
+ chip->sets[i].name);
+ if (!subfeature) {
+ sensors_parse_error_wfn("Unknown feature name",
+ chip->sets[i].line.filename,
+ chip->sets[i].line.lineno);
+ err = -SENSORS_ERR_NO_ENTRY;
+ continue;
+ }
+
+ res = sensors_eval_expr(chip_features,
+ chip->sets[i].value, 0,
+ 0, &value);
+ if (res) {
+ sensors_parse_error_wfn("Error parsing expression",
+ chip->sets[i].line.filename,
+ chip->sets[i].line.lineno);
+ err = res;
+ continue;
+ }
+ if ((res = sensors_set_value(name, subfeature->number,
+ value))) {
+ sensors_parse_error_wfn("Failed to set value",
+ chip->sets[i].line.filename,
+ chip->sets[i].line.lineno);
+ err = res;
+ continue;
+ }
+ }
+ return err;
+}
+
+/* Execute all set statements for this particular chip. The chip may contain
+ wildcards! This function will return 0 on success, and <0 on failure. */
+int sensors_do_chip_sets(const sensors_chip_name *name)
+{
+ int nr, this_res;
+ const sensors_chip_name *found_name;
+ int res = 0;
+
+ for (nr = 0; (found_name = sensors_get_detected_chips(name, &nr));) {
+ this_res = sensors_do_this_chip_sets(found_name);
+ if (this_res)
+ res = this_res;
+ }
+ return res;
+}
diff --git a/tools/gator/daemon/libsensors/access.h b/tools/gator/daemon/libsensors/access.h
new file mode 100644
index 000000000000..1d3784340757
--- /dev/null
+++ b/tools/gator/daemon/libsensors/access.h
@@ -0,0 +1,33 @@
+/*
+ access.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef LIB_SENSORS_ACCESS_H
+#define LIB_SENSORS_ACCESS_H
+
+#include "sensors.h"
+#include "data.h"
+
+/* Check whether the chip name is an 'absolute' name, which can only match
+ one chip, or whether it has wildcards. Returns 0 if it is absolute, 1
+ if there are wildcards. */
+int sensors_chip_name_has_wildcards(const sensors_chip_name *chip);
+
+#endif /* def LIB_SENSORS_ACCESS_H */
diff --git a/tools/gator/daemon/libsensors/conf-lex.c b/tools/gator/daemon/libsensors/conf-lex.c
new file mode 100644
index 000000000000..a54664b3d77b
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf-lex.c
@@ -0,0 +1,2881 @@
+
+#line 3 "<stdout>"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define yy_create_buffer sensors_yy_create_buffer
+#define yy_delete_buffer sensors_yy_delete_buffer
+#define yy_flex_debug sensors_yy_flex_debug
+#define yy_init_buffer sensors_yy_init_buffer
+#define yy_flush_buffer sensors_yy_flush_buffer
+#define yy_load_buffer_state sensors_yy_load_buffer_state
+#define yy_switch_to_buffer sensors_yy_switch_to_buffer
+#define yyin sensors_yyin
+#define yyleng sensors_yyleng
+#define yylex sensors_yylex
+#define yylineno sensors_yylineno
+#define yyout sensors_yyout
+#define yyrestart sensors_yyrestart
+#define yytext sensors_yytext
+#define yywrap sensors_yywrap
+#define yyalloc sensors_yyalloc
+#define yyrealloc sensors_yyrealloc
+#define yyfree sensors_yyfree
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE sensors_yyrestart(sensors_yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int sensors_yyleng;
+
+extern FILE *sensors_yyin, *sensors_yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up sensors_yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up sensors_yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via sensors_yyrestart()), so that the user can continue scanning by
+ * just pointing sensors_yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when sensors_yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int sensors_yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow sensors_yywrap()'s to do buffer switches
+ * instead of setting up a fresh sensors_yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void sensors_yyrestart (FILE *input_file );
+void sensors_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE sensors_yy_create_buffer (FILE *file,int size );
+void sensors_yy_delete_buffer (YY_BUFFER_STATE b );
+void sensors_yy_flush_buffer (YY_BUFFER_STATE b );
+void sensors_yypush_buffer_state (YY_BUFFER_STATE new_buffer );
+void sensors_yypop_buffer_state (void );
+
+static void sensors_yyensure_buffer_stack (void );
+static void sensors_yy_load_buffer_state (void );
+static void sensors_yy_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER sensors_yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE sensors_yy_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE sensors_yy_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE sensors_yy_scan_bytes (yyconst char *bytes,int len );
+
+void *sensors_yyalloc (yy_size_t );
+void *sensors_yyrealloc (void *,yy_size_t );
+void sensors_yyfree (void * );
+
+#define yy_new_buffer sensors_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ sensors_yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ sensors_yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define sensors_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *sensors_yyin = (FILE *) 0, *sensors_yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int sensors_yylineno;
+
+int sensors_yylineno = 1;
+
+extern char *sensors_yytext;
+#define yytext_ptr sensors_yytext
+static yyconst flex_int16_t yy_nxt[][39] =
+ {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
+ },
+
+ {
+ 9, 10, 11, 12, 10, 13, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 14, 15, 16, 14, 14, 14, 14, 14, 17, 18,
+ 14, 14, 14, 14, 14, 19, 14, 14, 14
+ },
+
+ {
+ 9, 10, 11, 12, 10, 13, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+
+ 14, 15, 16, 14, 14, 14, 14, 14, 17, 18,
+ 14, 14, 14, 14, 14, 19, 14, 14, 14
+ },
+
+ {
+ 9, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35
+ },
+
+ {
+ 9, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35
+
+ },
+
+ {
+ 9, 39, 39, 40, 41, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 42, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39
+ },
+
+ {
+ 9, 39, 39, 40, 41, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 42, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39
+ },
+
+ {
+ 9, 43, 43, 44, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
+
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43
+ },
+
+ {
+ 9, 43, 43, 44, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43
+ },
+
+ {
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9
+
+ },
+
+ {
+ 9, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10
+ },
+
+ {
+ 9, -11, 45, 46, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11
+ },
+
+ {
+ 9, -12, -12, -12, -12, -12, -12, -12, -12, -12,
+ -12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
+
+ -12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
+ -12, -12, -12, -12, -12, -12, -12, -12, -12
+ },
+
+ {
+ 9, 47, 47, 48, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47
+ },
+
+ {
+ 9, -14, -14, -14, -14, -14, -14, -14, -14, -14,
+ -14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -15, -15, -15, -15, -15, -15, -15, -15, -15,
+ -15, -15, -15, -15, -15, -15, -15, -15, -15, -15,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 50, 49
+ },
+
+ {
+ 9, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ 49, 49, 49, 49, 49, 49, 49, 51, 49, 49,
+ 49, 49, 52, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+
+ 49, 49, 49, 49, 49, 49, 53, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ 54, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ 49, 49, 49, 49, 55, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20
+ },
+
+ {
+ 9, -21, 56, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21
+ },
+
+ {
+ 9, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22
+ },
+
+ {
+ 9, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23, -23, -23
+ },
+
+ {
+ 9, 57, 57, 58, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57
+
+ },
+
+ {
+ 9, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25
+ },
+
+ {
+ 9, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26
+ },
+
+ {
+ 9, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27
+ },
+
+ {
+ 9, -28, -28, -28, -28, -28, -28, -28, -28, -28,
+ -28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
+ -28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
+ -28, -28, -28, -28, -28, -28, -28, -28, -28
+ },
+
+ {
+ 9, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29
+
+ },
+
+ {
+ 9, -30, -30, -30, -30, -30, -30, -30, -30, -30,
+ -30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
+ -30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
+ -30, -30, -30, -30, -30, -30, -30, -30, -30
+ },
+
+ {
+ 9, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, 59, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31
+ },
+
+ {
+ 9, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32
+ },
+
+ {
+ 9, -33, -33, -33, -33, -33, -33, -33, -33, -33,
+ -33, -33, 60, -33, 61, -33, 62, -33, -33, -33,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62
+ },
+
+ {
+ 9, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34
+
+ },
+
+ {
+ 9, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, 62, -35, 62, -35, -35, -35,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62
+ },
+
+ {
+ 9, -36, 63, 64, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36
+ },
+
+ {
+ 9, -37, -37, -37, -37, -37, -37, -37, -37, -37,
+ -37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
+
+ -37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
+ -37, -37, -37, -37, -37, -37, -37, -37, -37
+ },
+
+ {
+ 9, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38
+ },
+
+ {
+ 9, 65, 65, -39, -39, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, -39, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65
+
+ },
+
+ {
+ 9, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40
+ },
+
+ {
+ 9, -41, -41, -41, 66, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41
+ },
+
+ {
+ 9, 67, 67, 68, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+
+ 69, 70, 67, 67, 67, 71, 67, 67, 67, 67,
+ 67, 72, 67, 67, 73, 67, 74, 67, 75
+ },
+
+ {
+ 9, 76, 76, -43, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76
+ },
+
+ {
+ 9, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44
+
+ },
+
+ {
+ 9, -45, 45, 46, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45
+ },
+
+ {
+ 9, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46
+ },
+
+ {
+ 9, 47, 47, 48, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47
+ },
+
+ {
+ 9, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48
+ },
+
+ {
+ 9, -49, -49, -49, -49, -49, -49, -49, -49, -49,
+ -49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 77, 49, 49, 49
+ },
+
+ {
+ 9, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ 49, 49, 49, 49, 49, 49, 49, 49, 78, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -52, -52, -52, -52, -52, -52, -52, -52, -52,
+ -52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 79, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 80, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -54, -54, -54, -54, -54, -54, -54, -54, -54,
+ -54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
+ 49, 81, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -55, -55, -55, -55, -55, -55, -55, -55, -55,
+ -55, -55, -55, -55, -55, -55, -55, -55, -55, -55,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 82, 49, 49
+ },
+
+ {
+ 9, -56, 56, -56, -56, -56, -56, -56, -56, -56,
+ -56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
+ -56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
+ -56, -56, -56, -56, -56, -56, -56, -56, -56
+ },
+
+ {
+ 9, 57, 57, 58, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57
+ },
+
+ {
+ 9, -58, -58, -58, -58, -58, -58, -58, -58, -58,
+ -58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
+ -58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
+ -58, -58, -58, -58, -58, -58, -58, -58, -58
+ },
+
+ {
+ 9, -59, -59, -59, -59, -59, -59, -59, -59, -59,
+ -59, -59, -59, -59, 59, -59, -59, -59, -59, -59,
+ -59, -59, -59, -59, -59, -59, -59, -59, -59, -59,
+ -59, -59, -59, -59, -59, -59, -59, -59, -59
+
+ },
+
+ {
+ 9, -60, -60, -60, -60, -60, -60, -60, -60, -60,
+ -60, -60, -60, -60, 59, -60, -60, -60, -60, -60,
+ -60, -60, -60, -60, -60, -60, -60, -60, -60, -60,
+ -60, -60, -60, -60, -60, -60, -60, -60, -60
+ },
+
+ {
+ 9, -61, -61, -61, -61, -61, -61, -61, -61, -61,
+ -61, -61, 60, -61, 61, -61, 62, -61, -61, -61,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62
+ },
+
+ {
+ 9, -62, -62, -62, -62, -62, -62, -62, -62, -62,
+ -62, -62, -62, -62, 62, -62, 62, -62, -62, -62,
+
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62
+ },
+
+ {
+ 9, -63, 63, 64, -63, -63, -63, -63, -63, -63,
+ -63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
+ -63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
+ -63, -63, -63, -63, -63, -63, -63, -63, -63
+ },
+
+ {
+ 9, -64, -64, -64, -64, -64, -64, -64, -64, -64,
+ -64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
+ -64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
+ -64, -64, -64, -64, -64, -64, -64, -64, -64
+
+ },
+
+ {
+ 9, 65, 65, -65, -65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, -65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65
+ },
+
+ {
+ 9, -66, -66, -66, -66, -66, -66, -66, -66, -66,
+ -66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
+ -66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
+ -66, -66, -66, -66, -66, -66, -66, -66, -66
+ },
+
+ {
+ 9, -67, -67, -67, -67, -67, -67, -67, -67, -67,
+ -67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
+
+ -67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
+ -67, -67, -67, -67, -67, -67, -67, -67, -67
+ },
+
+ {
+ 9, -68, -68, -68, -68, -68, -68, -68, -68, -68,
+ -68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
+ -68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
+ -68, -68, -68, -68, -68, -68, -68, -68, -68
+ },
+
+ {
+ 9, -69, -69, -69, -69, -69, -69, -69, -69, -69,
+ -69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
+ -69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
+ -69, -69, -69, -69, -69, -69, -69, -69, -69
+
+ },
+
+ {
+ 9, -70, -70, -70, -70, -70, -70, -70, -70, -70,
+ -70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
+ -70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
+ -70, -70, -70, -70, -70, -70, -70, -70, -70
+ },
+
+ {
+ 9, -71, -71, -71, -71, -71, -71, -71, -71, -71,
+ -71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
+ -71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
+ -71, -71, -71, -71, -71, -71, -71, -71, -71
+ },
+
+ {
+ 9, -72, -72, -72, -72, -72, -72, -72, -72, -72,
+ -72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
+
+ -72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
+ -72, -72, -72, -72, -72, -72, -72, -72, -72
+ },
+
+ {
+ 9, -73, -73, -73, -73, -73, -73, -73, -73, -73,
+ -73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
+ -73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
+ -73, -73, -73, -73, -73, -73, -73, -73, -73
+ },
+
+ {
+ 9, -74, -74, -74, -74, -74, -74, -74, -74, -74,
+ -74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
+ -74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
+ -74, -74, -74, -74, -74, -74, -74, -74, -74
+
+ },
+
+ {
+ 9, -75, -75, -75, -75, -75, -75, -75, -75, -75,
+ -75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
+ -75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
+ -75, -75, -75, -75, -75, -75, -75, -75, -75
+ },
+
+ {
+ 9, 76, 76, -76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76
+ },
+
+ {
+ 9, -77, 83, -77, -77, -77, -77, -77, -77, -77,
+ -77, -77, -77, -77, -77, -77, -77, -77, -77, -77,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -78, -78, -78, -78, -78, -78, -78, -78, -78,
+ -78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 84, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -79, -79, -79, -79, -79, -79, -79, -79, -79,
+ -79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 85, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -80, -80, -80, -80, -80, -80, -80, -80, -80,
+ -80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 86, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -81, -81, -81, -81, -81, -81, -81, -81, -81,
+ -81, -81, -81, -81, -81, -81, -81, -81, -81, -81,
+ 49, 49, 49, 49, 87, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -82, 88, -82, -82, -82, -82, -82, -82, -82,
+ -82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -83, 83, -83, -83, -83, -83, -83, -83, -83,
+ -83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
+ -83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
+ -83, -83, -83, -83, -83, -83, -83, -83, -83
+ },
+
+ {
+ 9, -84, 89, -84, -84, -84, -84, -84, -84, -84,
+ -84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -85, -85, -85, -85, -85, -85, -85, -85, -85,
+ -85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 90, 49
+ },
+
+ {
+ 9, -86, -86, -86, -86, -86, -86, -86, -86, -86,
+ -86, -86, -86, -86, -86, -86, -86, -86, -86, -86,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 91, 49, 49, 49, 49
+ },
+
+ {
+ 9, -87, -87, -87, -87, -87, -87, -87, -87, -87,
+ -87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 92,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -88, 88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88
+ },
+
+ {
+ 9, -89, 89, -89, -89, -89, -89, -89, -89, -89,
+ -89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
+ -89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
+ -89, -89, -89, -89, -89, -89, -89, -89, -89
+
+ },
+
+ {
+ 9, -90, -90, -90, -90, -90, -90, -90, -90, -90,
+ -90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 93, 49, 49
+ },
+
+ {
+ 9, -91, -91, -91, -91, -91, -91, -91, -91, -91,
+ -91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
+ 49, 49, 49, 49, 94, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -92, 95, -92, -92, -92, -92, -92, -92, -92,
+ -92, -92, -92, -92, -92, -92, -92, -92, -92, -92,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -93, -93, -93, -93, -93, -93, -93, -93, -93,
+ -93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
+ 49, 49, 49, 49, 96, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -94, 97, -94, -94, -94, -94, -94, -94, -94,
+ -94, -94, -94, -94, -94, -94, -94, -94, -94, -94,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -95, 95, -95, -95, -95, -95, -95, -95, -95,
+ -95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
+ -95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
+ -95, -95, -95, -95, -95, -95, -95, -95, -95
+ },
+
+ {
+ 9, -96, 98, -96, -96, -96, -96, -96, -96, -96,
+ -96, -96, -96, -96, -96, -96, -96, -96, -96, -96,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -97, 97, -97, -97, -97, -97, -97, -97, -97,
+ -97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
+
+ -97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
+ -97, -97, -97, -97, -97, -97, -97, -97, -97
+ },
+
+ {
+ 9, -98, 98, -98, -98, -98, -98, -98, -98, -98,
+ -98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
+ -98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
+ -98, -98, -98, -98, -98, -98, -98, -98, -98
+ },
+
+ } ;
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up sensors_yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ sensors_yyleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 50
+#define YY_END_OF_BUFFER 51
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[99] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 13, 13, 51, 12,
+ 1, 2, 3, 11, 11, 11, 11, 11, 11, 33,
+ 15, 16, 31, 18, 25, 26, 23, 21, 27, 22,
+ 33, 24, 20, 28, 32, 33, 29, 30, 49, 36,
+ 39, 48, 13, 14, 1, 2, 3, 4, 11, 11,
+ 11, 11, 11, 11, 11, 15, 18, 19, 20, 34,
+ 20, 32, 35, 17, 49, 38, 47, 37, 40, 41,
+ 42, 43, 44, 45, 46, 13, 8, 11, 11, 11,
+ 11, 6, 8, 9, 11, 11, 11, 6, 9, 11,
+ 11, 5, 11, 10, 5, 7, 10, 7
+
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 4, 5, 1, 1, 1, 1, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 1, 1, 1,
+ 1, 1, 1, 15, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 1, 17, 1, 18, 16, 19, 20, 21, 22, 23,
+
+ 24, 25, 26, 27, 28, 23, 23, 29, 30, 31,
+ 32, 33, 23, 34, 35, 36, 37, 38, 23, 23,
+ 23, 23, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+extern int sensors_yy_flex_debug;
+int sensors_yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *sensors_yytext;
+#line 1 "lib/conf-lex.l"
+#line 2 "lib/conf-lex.l"
+/*
+ conf-lex.l - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "general.h"
+#include "data.h"
+#include "conf-parse.h"
+#include "error.h"
+#include "scanner.h"
+
+static int buffer_count;
+static int buffer_max;
+static char *buffer;
+
+char sensors_lex_error[100];
+
+const char *sensors_yyfilename;
+int sensors_yylineno;
+
+#define buffer_malloc() sensors_malloc_array(&buffer,&buffer_count,\
+ &buffer_max,1)
+#define buffer_free() sensors_free_array(&buffer,&buffer_count,\
+ &buffer_max)
+#define buffer_add_char(c) sensors_add_array_el(c,&buffer,\
+ &buffer_count,\
+ &buffer_max,1)
+#define buffer_add_string(s) sensors_add_array_els(s,strlen(s),\
+ &buffer, \
+ &buffer_count,&buffer_max,1)
+
+/* Scanner for configuration files */
+/* All states are exclusive */
+
+
+
+/* Any whitespace-like character */
+/* Note: `10', `10.4' and `.4' are valid, `10.' is not */
+/* Only positive whole numbers are recognized here */
+#line 1255 "<stdout>"
+
+#define INITIAL 0
+#define MIDDLE 1
+#define STRING 2
+#define ERR 3
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int sensors_yylex_destroy (void );
+
+int sensors_yyget_debug (void );
+
+void sensors_yyset_debug (int debug_flag );
+
+YY_EXTRA_TYPE sensors_yyget_extra (void );
+
+void sensors_yyset_extra (YY_EXTRA_TYPE user_defined );
+
+FILE *sensors_yyget_in (void );
+
+void sensors_yyset_in (FILE * in_str );
+
+FILE *sensors_yyget_out (void );
+
+void sensors_yyset_out (FILE * out_str );
+
+int sensors_yyget_leng (void );
+
+char *sensors_yyget_text (void );
+
+int sensors_yyget_lineno (void );
+
+void sensors_yyset_lineno (int line_number );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int sensors_yywrap (void );
+#else
+extern int sensors_yywrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( sensors_yytext, sensors_yyleng, 1, sensors_yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( sensors_yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( sensors_yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, sensors_yyin))==0 && ferror(sensors_yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(sensors_yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int sensors_yylex (void);
+
+#define YY_DECL int sensors_yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after sensors_yytext and sensors_yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 80 "lib/conf-lex.l"
+
+
+ /*
+ * STATE: INITIAL
+ */
+
+#line 1450 "<stdout>"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! sensors_yyin )
+ sensors_yyin = stdin;
+
+ if ( ! sensors_yyout )
+ sensors_yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ sensors_yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE );
+ }
+
+ sensors_yy_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of sensors_yytext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 )
+ ++yy_cp;
+
+ yy_current_state = -yy_current_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+
+case YY_STATE_EOF(INITIAL):
+#line 88 "lib/conf-lex.l"
+{ /* EOF from this state terminates */
+ return 0;
+ }
+ YY_BREAK
+case 1:
+YY_RULE_SETUP
+#line 92 "lib/conf-lex.l"
+; /* eat as many blanks as possible at once */
+ YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+#line 94 "lib/conf-lex.l"
+{ /* eat a bare newline (possibly preceded by blanks) */
+ sensors_yylineno++;
+ }
+ YY_BREAK
+/* comments */
+case 3:
+YY_RULE_SETUP
+#line 100 "lib/conf-lex.l"
+; /* eat the rest of the line after comment char */
+ YY_BREAK
+case 4:
+/* rule 4 can match eol */
+YY_RULE_SETUP
+#line 102 "lib/conf-lex.l"
+{ /* eat the rest of the line after comment char */
+ sensors_yylineno++;
+ }
+ YY_BREAK
+/*
+ * Keywords must be followed by whitespace - eat that too.
+ * If there isn't trailing whitespace, we still need to
+ * accept it as lexically correct (even though the parser
+ * will reject it anyway.)
+ */
+case 5:
+YY_RULE_SETUP
+#line 113 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return LABEL;
+ }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 120 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return SET;
+ }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 127 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return COMPUTE;
+ }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 134 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return BUS;
+ }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 141 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return CHIP;
+ }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 148 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return IGNORE;
+ }
+ YY_BREAK
+/* Anything else at the beginning of a line is an error */
+case 11:
+#line 158 "lib/conf-lex.l"
+case 12:
+YY_RULE_SETUP
+#line 158 "lib/conf-lex.l"
+{
+ BEGIN(ERR);
+ strcpy(sensors_lex_error,"Invalid keyword");
+ return ERROR;
+ }
+ YY_BREAK
+
+/*
+ * STATE: ERROR
+ */
+
+case 13:
+YY_RULE_SETUP
+#line 171 "lib/conf-lex.l"
+; /* eat whatever is left on this line */
+ YY_BREAK
+case 14:
+/* rule 14 can match eol */
+YY_RULE_SETUP
+#line 173 "lib/conf-lex.l"
+{
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+ YY_BREAK
+
+/*
+ * STATE: MIDDLE
+ */
+
+case 15:
+YY_RULE_SETUP
+#line 186 "lib/conf-lex.l"
+; /* eat as many blanks as possible at once */
+ YY_BREAK
+case 16:
+/* rule 16 can match eol */
+YY_RULE_SETUP
+#line 188 "lib/conf-lex.l"
+{ /* newline here sends EOL token to parser */
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+ YY_BREAK
+case YY_STATE_EOF(MIDDLE):
+#line 194 "lib/conf-lex.l"
+{ /* EOF here sends EOL token to parser also */
+ BEGIN(INITIAL);
+ return EOL;
+ }
+ YY_BREAK
+case 17:
+/* rule 17 can match eol */
+YY_RULE_SETUP
+#line 199 "lib/conf-lex.l"
+{ /* eat an escaped newline with no state change */
+ sensors_yylineno++;
+ }
+ YY_BREAK
+/* comments */
+case 18:
+YY_RULE_SETUP
+#line 205 "lib/conf-lex.l"
+; /* eat the rest of the line after comment char */
+ YY_BREAK
+case 19:
+/* rule 19 can match eol */
+YY_RULE_SETUP
+#line 207 "lib/conf-lex.l"
+{ /* eat the rest of the line after comment char */
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+ YY_BREAK
+/* A number */
+case 20:
+YY_RULE_SETUP
+#line 215 "lib/conf-lex.l"
+{
+ sensors_yylval.value = atof(sensors_yytext);
+ return FLOAT;
+ }
+ YY_BREAK
+/* Some operators */
+case 21:
+YY_RULE_SETUP
+#line 222 "lib/conf-lex.l"
+return '+';
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 223 "lib/conf-lex.l"
+return '-';
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 224 "lib/conf-lex.l"
+return '*';
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 225 "lib/conf-lex.l"
+return '/';
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 226 "lib/conf-lex.l"
+return '(';
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 227 "lib/conf-lex.l"
+return ')';
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 228 "lib/conf-lex.l"
+return ',';
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 229 "lib/conf-lex.l"
+return '@';
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 230 "lib/conf-lex.l"
+return '^';
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 231 "lib/conf-lex.l"
+return '`';
+ YY_BREAK
+/* Quoted string */
+case 31:
+YY_RULE_SETUP
+#line 235 "lib/conf-lex.l"
+{
+ buffer_malloc();
+ BEGIN(STRING);
+ }
+ YY_BREAK
+/* A normal, unquoted identifier */
+case 32:
+YY_RULE_SETUP
+#line 242 "lib/conf-lex.l"
+{
+ sensors_yylval.name = strdup(sensors_yytext);
+ if (! sensors_yylval.name)
+ sensors_fatal_error("conf-lex.l",
+ "Allocating a new string");
+
+ return NAME;
+ }
+ YY_BREAK
+/* anything else is bogus */
+case 33:
+#line 254 "lib/conf-lex.l"
+case 34:
+#line 255 "lib/conf-lex.l"
+case 35:
+YY_RULE_SETUP
+#line 255 "lib/conf-lex.l"
+{
+ BEGIN(ERR);
+ return ERROR;
+ }
+ YY_BREAK
+
+/*
+ * STATE: STRING
+ */
+
+/* Oops, newline or EOF while in a string is not good */
+case 36:
+/* rule 36 can match eol */
+#line 270 "lib/conf-lex.l"
+case 37:
+/* rule 37 can match eol */
+YY_RULE_SETUP
+#line 270 "lib/conf-lex.l"
+{
+ buffer_add_char("\0");
+ strcpy(sensors_lex_error,
+ "No matching double quote.");
+ buffer_free();
+ yyless(0);
+ BEGIN(ERR);
+ return ERROR;
+ }
+ YY_BREAK
+case YY_STATE_EOF(STRING):
+#line 280 "lib/conf-lex.l"
+{
+ strcpy(sensors_lex_error,
+ "Reached end-of-file without a matching double quote.");
+ buffer_free();
+ BEGIN(MIDDLE);
+ return ERROR;
+ }
+ YY_BREAK
+/* At the end */
+case 38:
+YY_RULE_SETUP
+#line 290 "lib/conf-lex.l"
+{
+ buffer_add_char("\0");
+ strcpy(sensors_lex_error,
+ "Quoted strings must be separated by whitespace.");
+ buffer_free();
+ BEGIN(ERR);
+ return ERROR;
+ }
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 299 "lib/conf-lex.l"
+{
+ buffer_add_char("\0");
+ sensors_yylval.name = strdup(buffer);
+ if (! sensors_yylval.name)
+ sensors_fatal_error("conf-lex.l",
+ "Allocating a new string");
+ buffer_free();
+ BEGIN(MIDDLE);
+ return NAME;
+ }
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 310 "lib/conf-lex.l"
+buffer_add_char("\a");
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 311 "lib/conf-lex.l"
+buffer_add_char("\b");
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 312 "lib/conf-lex.l"
+buffer_add_char("\f");
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 313 "lib/conf-lex.l"
+buffer_add_char("\n");
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 314 "lib/conf-lex.l"
+buffer_add_char("\r");
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 315 "lib/conf-lex.l"
+buffer_add_char("\t");
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 316 "lib/conf-lex.l"
+buffer_add_char("\v");
+ YY_BREAK
+/* Other escapes: just copy the character behind the slash */
+case 47:
+YY_RULE_SETUP
+#line 320 "lib/conf-lex.l"
+{
+ buffer_add_char(&sensors_yytext[1]);
+ }
+ YY_BREAK
+/* Anything else (including a bare '\' which may be followed by EOF) */
+case 48:
+#line 327 "lib/conf-lex.l"
+case 49:
+YY_RULE_SETUP
+#line 327 "lib/conf-lex.l"
+{
+ buffer_add_string(sensors_yytext);
+ }
+ YY_BREAK
+
+case 50:
+YY_RULE_SETUP
+#line 332 "lib/conf-lex.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+#line 1903 "<stdout>"
+ case YY_STATE_EOF(ERR):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed sensors_yyin at a new source and called
+ * sensors_yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = sensors_yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( sensors_yywrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * sensors_yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of sensors_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ sensors_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ sensors_yyrestart(sensors_yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) sensors_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+
+ yy_current_state = yy_nxt[yy_current_state][1];
+ yy_is_jam = (yy_current_state <= 0);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ sensors_yyrestart(sensors_yyin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( sensors_yywrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve sensors_yytext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void sensors_yyrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ sensors_yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE );
+ }
+
+ sensors_yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+ sensors_yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void sensors_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * sensors_yypop_buffer_state();
+ * sensors_yypush_buffer_state(new_buffer);
+ */
+ sensors_yyensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ sensors_yy_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (sensors_yywrap()) processing, but the only time this flag
+ * is looked at is after sensors_yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void sensors_yy_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ sensors_yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE sensors_yy_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) sensors_yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) sensors_yyalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ sensors_yy_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with sensors_yy_create_buffer()
+ *
+ */
+ void sensors_yy_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ sensors_yyfree((void *) b->yy_ch_buf );
+
+ sensors_yyfree((void *) b );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a sensors_yyrestart() or at EOF.
+ */
+ static void sensors_yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ sensors_yy_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then sensors_yy_init_buffer was _probably_
+ * called from sensors_yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void sensors_yy_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ sensors_yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void sensors_yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ sensors_yyensure_buffer_stack();
+
+ /* This block is copied from sensors_yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from sensors_yy_switch_to_buffer. */
+ sensors_yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void sensors_yypop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ sensors_yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ sensors_yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void sensors_yyensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)sensors_yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yyensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)sensors_yyrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE sensors_yy_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) sensors_yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ sensors_yy_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to sensors_yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * sensors_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE sensors_yy_scan_string (yyconst char * yystr )
+{
+
+ return sensors_yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to sensors_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE sensors_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) sensors_yyalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = sensors_yy_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in sensors_yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up sensors_yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ sensors_yytext[sensors_yyleng] = (yy_hold_char); \
+ (yy_c_buf_p) = sensors_yytext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ sensors_yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int sensors_yyget_lineno (void)
+{
+
+ return sensors_yylineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *sensors_yyget_in (void)
+{
+ return sensors_yyin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *sensors_yyget_out (void)
+{
+ return sensors_yyout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int sensors_yyget_leng (void)
+{
+ return sensors_yyleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *sensors_yyget_text (void)
+{
+ return sensors_yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void sensors_yyset_lineno (int line_number )
+{
+
+ sensors_yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see sensors_yy_switch_to_buffer
+ */
+void sensors_yyset_in (FILE * in_str )
+{
+ sensors_yyin = in_str ;
+}
+
+void sensors_yyset_out (FILE * out_str )
+{
+ sensors_yyout = out_str ;
+}
+
+int sensors_yyget_debug (void)
+{
+ return sensors_yy_flex_debug;
+}
+
+void sensors_yyset_debug (int bdebug )
+{
+ sensors_yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from sensors_yylex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ sensors_yyin = stdin;
+ sensors_yyout = stdout;
+#else
+ sensors_yyin = (FILE *) 0;
+ sensors_yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * sensors_yylex_init()
+ */
+ return 0;
+}
+
+/* sensors_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int sensors_yylex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ sensors_yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ sensors_yypop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ sensors_yyfree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * sensors_yylex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *sensors_yyalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *sensors_yyrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void sensors_yyfree (void * ptr )
+{
+ free( (char *) ptr ); /* see sensors_yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 332 "lib/conf-lex.l"
+
+
+
+/*
+ Do the buffer handling manually. This allows us to scan as many
+ config files as we need to, while cleaning up properly after each
+ one. The "BEGIN(0)" line ensures that we start in the default state,
+ even if e.g. the previous config file was syntactically broken.
+
+ Returns 0 if successful, !0 otherwise.
+*/
+
+static YY_BUFFER_STATE scan_buf = (YY_BUFFER_STATE)0;
+
+int sensors_scanner_init(FILE *input, const char *filename)
+{
+ BEGIN(0);
+ if (!(scan_buf = sensors_yy_create_buffer(input, YY_BUF_SIZE)))
+ return -1;
+
+ sensors_yy_switch_to_buffer(scan_buf);
+ sensors_yyfilename = filename;
+ sensors_yylineno = 1;
+ return 0;
+}
+
+void sensors_scanner_exit(void)
+{
+ sensors_yy_delete_buffer(scan_buf);
+ scan_buf = (YY_BUFFER_STATE)0;
+
+/* As of flex 2.5.9, sensors_yylex_destroy() must be called when done with the
+ scaller, otherwise we'll leak memory. */
+#if defined(YY_FLEX_MAJOR_VERSION) && defined(YY_FLEX_MINOR_VERSION) && defined(YY_FLEX_SUBMINOR_VERSION)
+#if YY_FLEX_MAJOR_VERSION > 2 || \
+ (YY_FLEX_MAJOR_VERSION == 2 && (YY_FLEX_MINOR_VERSION > 5 || \
+ (YY_FLEX_MINOR_VERSION == 5 && YY_FLEX_SUBMINOR_VERSION >= 9)))
+ sensors_yylex_destroy();
+#endif
+#endif
+}
+
+
diff --git a/tools/gator/daemon/libsensors/conf-lex.l b/tools/gator/daemon/libsensors/conf-lex.l
new file mode 100644
index 000000000000..43ddbd8f5bd5
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf-lex.l
@@ -0,0 +1,372 @@
+%{
+/*
+ conf-lex.l - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "general.h"
+#include "data.h"
+#include "conf-parse.h"
+#include "error.h"
+#include "scanner.h"
+
+static int buffer_count;
+static int buffer_max;
+static char *buffer;
+
+char sensors_lex_error[100];
+
+const char *sensors_yyfilename;
+int sensors_yylineno;
+
+#define buffer_malloc() sensors_malloc_array(&buffer,&buffer_count,\
+ &buffer_max,1)
+#define buffer_free() sensors_free_array(&buffer,&buffer_count,\
+ &buffer_max)
+#define buffer_add_char(c) sensors_add_array_el(c,&buffer,\
+ &buffer_count,\
+ &buffer_max,1)
+#define buffer_add_string(s) sensors_add_array_els(s,strlen(s),\
+ &buffer, \
+ &buffer_count,&buffer_max,1)
+
+%}
+
+ /* Scanner for configuration files */
+
+%option nodefault
+%option noyywrap
+%option nounput
+
+ /* All states are exclusive */
+
+%x MIDDLE
+%x STRING
+%x ERR
+
+ /* Any whitespace-like character */
+
+BLANK [ \f\r\t\v]
+
+IDCHAR [[:alnum:]_]
+
+ /* Note: `10', `10.4' and `.4' are valid, `10.' is not */
+
+FLOAT [[:digit:]]*\.?[[:digit:]]+
+
+ /* Only positive whole numbers are recognized here */
+
+NUM 0|([1-9][[:digit:]]*)
+
+
+%%
+
+ /*
+ * STATE: INITIAL
+ */
+
+<INITIAL>{
+
+<<EOF>> { /* EOF from this state terminates */
+ return 0;
+ }
+
+{BLANK}+ ; /* eat as many blanks as possible at once */
+
+{BLANK}*\n { /* eat a bare newline (possibly preceded by blanks) */
+ sensors_yylineno++;
+ }
+
+ /* comments */
+
+#.* ; /* eat the rest of the line after comment char */
+
+#.*\n { /* eat the rest of the line after comment char */
+ sensors_yylineno++;
+ }
+
+ /*
+ * Keywords must be followed by whitespace - eat that too.
+ * If there isn't trailing whitespace, we still need to
+ * accept it as lexically correct (even though the parser
+ * will reject it anyway.)
+ */
+
+label{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return LABEL;
+ }
+
+set{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return SET;
+ }
+
+compute{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return COMPUTE;
+ }
+
+bus{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return BUS;
+ }
+
+chip{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return CHIP;
+ }
+
+ignore{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return IGNORE;
+ }
+
+ /* Anything else at the beginning of a line is an error */
+
+[a-z]+ |
+. {
+ BEGIN(ERR);
+ strcpy(sensors_lex_error,"Invalid keyword");
+ return ERROR;
+ }
+}
+
+ /*
+ * STATE: ERROR
+ */
+
+<ERR>{
+
+.* ; /* eat whatever is left on this line */
+
+\n {
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+}
+
+ /*
+ * STATE: MIDDLE
+ */
+
+<MIDDLE>{
+
+{BLANK}+ ; /* eat as many blanks as possible at once */
+
+\n { /* newline here sends EOL token to parser */
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+
+<<EOF>> { /* EOF here sends EOL token to parser also */
+ BEGIN(INITIAL);
+ return EOL;
+ }
+
+\\{BLANK}*\n { /* eat an escaped newline with no state change */
+ sensors_yylineno++;
+ }
+
+ /* comments */
+
+#.* ; /* eat the rest of the line after comment char */
+
+#.*\n { /* eat the rest of the line after comment char */
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+
+ /* A number */
+
+{FLOAT} {
+ sensors_yylval.value = atof(sensors_yytext);
+ return FLOAT;
+ }
+
+ /* Some operators */
+
+"+" return '+';
+"-" return '-';
+"*" return '*';
+"/" return '/';
+"(" return '(';
+")" return ')';
+"," return ',';
+"@" return '@';
+"^" return '^';
+"`" return '`';
+
+ /* Quoted string */
+
+\" {
+ buffer_malloc();
+ BEGIN(STRING);
+ }
+
+ /* A normal, unquoted identifier */
+
+{IDCHAR}+ {
+ sensors_yylval.name = strdup(sensors_yytext);
+ if (! sensors_yylval.name)
+ sensors_fatal_error("conf-lex.l",
+ "Allocating a new string");
+
+ return NAME;
+ }
+
+ /* anything else is bogus */
+
+. |
+[[:digit:]]*\. |
+\\{BLANK}* {
+ BEGIN(ERR);
+ return ERROR;
+ }
+}
+
+ /*
+ * STATE: STRING
+ */
+
+<STRING>{
+
+ /* Oops, newline or EOF while in a string is not good */
+
+\n |
+\\\n {
+ buffer_add_char("\0");
+ strcpy(sensors_lex_error,
+ "No matching double quote.");
+ buffer_free();
+ yyless(0);
+ BEGIN(ERR);
+ return ERROR;
+ }
+
+<<EOF>> {
+ strcpy(sensors_lex_error,
+ "Reached end-of-file without a matching double quote.");
+ buffer_free();
+ BEGIN(MIDDLE);
+ return ERROR;
+ }
+
+ /* At the end */
+
+\"\" {
+ buffer_add_char("\0");
+ strcpy(sensors_lex_error,
+ "Quoted strings must be separated by whitespace.");
+ buffer_free();
+ BEGIN(ERR);
+ return ERROR;
+ }
+
+\" {
+ buffer_add_char("\0");
+ sensors_yylval.name = strdup(buffer);
+ if (! sensors_yylval.name)
+ sensors_fatal_error("conf-lex.l",
+ "Allocating a new string");
+ buffer_free();
+ BEGIN(MIDDLE);
+ return NAME;
+ }
+
+\\a buffer_add_char("\a");
+\\b buffer_add_char("\b");
+\\f buffer_add_char("\f");
+\\n buffer_add_char("\n");
+\\r buffer_add_char("\r");
+\\t buffer_add_char("\t");
+\\v buffer_add_char("\v");
+
+ /* Other escapes: just copy the character behind the slash */
+
+\\. {
+ buffer_add_char(&sensors_yytext[1]);
+ }
+
+ /* Anything else (including a bare '\' which may be followed by EOF) */
+
+\\ |
+[^\\\n\"]+ {
+ buffer_add_string(sensors_yytext);
+ }
+}
+
+%%
+
+/*
+ Do the buffer handling manually. This allows us to scan as many
+ config files as we need to, while cleaning up properly after each
+ one. The "BEGIN(0)" line ensures that we start in the default state,
+ even if e.g. the previous config file was syntactically broken.
+
+ Returns 0 if successful, !0 otherwise.
+*/
+
+static YY_BUFFER_STATE scan_buf = (YY_BUFFER_STATE)0;
+
+int sensors_scanner_init(FILE *input, const char *filename)
+{
+ BEGIN(0);
+ if (!(scan_buf = sensors_yy_create_buffer(input, YY_BUF_SIZE)))
+ return -1;
+
+ sensors_yy_switch_to_buffer(scan_buf);
+ sensors_yyfilename = filename;
+ sensors_yylineno = 1;
+ return 0;
+}
+
+void sensors_scanner_exit(void)
+{
+ sensors_yy_delete_buffer(scan_buf);
+ scan_buf = (YY_BUFFER_STATE)0;
+
+/* As of flex 2.5.9, yylex_destroy() must be called when done with the
+ scaller, otherwise we'll leak memory. */
+#if defined(YY_FLEX_MAJOR_VERSION) && defined(YY_FLEX_MINOR_VERSION) && defined(YY_FLEX_SUBMINOR_VERSION)
+#if YY_FLEX_MAJOR_VERSION > 2 || \
+ (YY_FLEX_MAJOR_VERSION == 2 && (YY_FLEX_MINOR_VERSION > 5 || \
+ (YY_FLEX_MINOR_VERSION == 5 && YY_FLEX_SUBMINOR_VERSION >= 9)))
+ sensors_yylex_destroy();
+#endif
+#endif
+}
+
diff --git a/tools/gator/daemon/libsensors/conf-parse.c b/tools/gator/daemon/libsensors/conf-parse.c
new file mode 100644
index 000000000000..fb775460a1c3
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf-parse.c
@@ -0,0 +1,2042 @@
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.5"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names. */
+#define yyparse sensors_yyparse
+#define yylex sensors_yylex
+#define yyerror sensors_yyerror
+#define yylval sensors_yylval
+#define yychar sensors_yychar
+#define yydebug sensors_yydebug
+#define yynerrs sensors_yynerrs
+
+
+/* Copy the first part of user declarations. */
+
+/* Line 268 of yacc.c */
+#line 1 "lib/conf-parse.y"
+
+/*
+ conf-parse.y - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#define YYERROR_VERBOSE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "data.h"
+#include "general.h"
+#include "error.h"
+#include "conf.h"
+#include "access.h"
+#include "init.h"
+
+static void sensors_yyerror(const char *err);
+static sensors_expr *malloc_expr(void);
+
+static sensors_chip *current_chip = NULL;
+
+#define bus_add_el(el) sensors_add_array_el(el,\
+ &sensors_config_busses,\
+ &sensors_config_busses_count,\
+ &sensors_config_busses_max,\
+ sizeof(sensors_bus))
+#define label_add_el(el) sensors_add_array_el(el,\
+ &current_chip->labels,\
+ &current_chip->labels_count,\
+ &current_chip->labels_max,\
+ sizeof(sensors_label));
+#define set_add_el(el) sensors_add_array_el(el,\
+ &current_chip->sets,\
+ &current_chip->sets_count,\
+ &current_chip->sets_max,\
+ sizeof(sensors_set));
+#define compute_add_el(el) sensors_add_array_el(el,\
+ &current_chip->computes,\
+ &current_chip->computes_count,\
+ &current_chip->computes_max,\
+ sizeof(sensors_compute));
+#define ignore_add_el(el) sensors_add_array_el(el,\
+ &current_chip->ignores,\
+ &current_chip->ignores_count,\
+ &current_chip->ignores_max,\
+ sizeof(sensors_ignore));
+#define chip_add_el(el) sensors_add_array_el(el,\
+ &sensors_config_chips,\
+ &sensors_config_chips_count,\
+ &sensors_config_chips_max,\
+ sizeof(sensors_chip));
+
+#define fits_add_el(el,list) sensors_add_array_el(el,\
+ &(list).fits,\
+ &(list).fits_count,\
+ &(list).fits_max, \
+ sizeof(sensors_chip_name));
+
+
+
+/* Line 268 of yacc.c */
+#line 158 "lib/conf-parse.c"
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ NEG = 258,
+ EOL = 259,
+ BUS = 260,
+ LABEL = 261,
+ SET = 262,
+ CHIP = 263,
+ COMPUTE = 264,
+ IGNORE = 265,
+ FLOAT = 266,
+ NAME = 267,
+ ERROR = 268
+ };
+#endif
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 293 of yacc.c */
+#line 79 "lib/conf-parse.y"
+
+ double value;
+ char *name;
+ void *nothing;
+ sensors_chip_name_list chips;
+ sensors_expr *expr;
+ sensors_bus_id bus;
+ sensors_chip_name chip;
+ sensors_config_line line;
+
+
+
+/* Line 293 of yacc.c */
+#line 220 "lib/conf-parse.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 343 of yacc.c */
+#line 232 "lib/conf-parse.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 2
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 58
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 24
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 16
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 34
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 63
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 268
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 22, 23, 5, 4, 10, 3, 2, 6, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 21, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 8, 2, 9, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 7, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 4, 7, 10, 13, 16, 19, 22,
+ 25, 28, 32, 36, 40, 46, 49, 52, 54, 57,
+ 59, 61, 63, 67, 71, 75, 79, 82, 86, 89,
+ 92, 94, 96, 98, 100
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 25, 0, -1, -1, 25, 26, -1, 27, 11, -1,
+ 28, 11, -1, 29, 11, -1, 32, 11, -1, 30,
+ 11, -1, 31, 11, -1, 1, 11, -1, 12, 35,
+ 36, -1, 13, 37, 38, -1, 14, 37, 34, -1,
+ 16, 37, 34, 10, 34, -1, 17, 37, -1, 15,
+ 33, -1, 39, -1, 33, 39, -1, 18, -1, 19,
+ -1, 21, -1, 34, 4, 34, -1, 34, 3, 34,
+ -1, 34, 5, 34, -1, 34, 6, 34, -1, 3,
+ 34, -1, 22, 34, 23, -1, 8, 34, -1, 9,
+ 34, -1, 19, -1, 19, -1, 19, -1, 19, -1,
+ 19, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 119, 119, 120, 123, 124, 125, 126, 127, 128,
+ 129, 132, 141, 156, 171, 188, 201, 219, 225, 231,
+ 236, 241, 245, 252, 259, 266, 273, 280, 282, 289,
+ 298, 308, 312, 316, 320
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "'-'", "'+'", "'*'", "'/'", "NEG", "'^'",
+ "'`'", "','", "EOL", "BUS", "LABEL", "SET", "CHIP", "COMPUTE", "IGNORE",
+ "FLOAT", "NAME", "ERROR", "'@'", "'('", "')'", "$accept", "input",
+ "line", "bus_statement", "label_statement", "set_statement",
+ "compute_statement", "ignore_statement", "chip_statement",
+ "chip_name_list", "expression", "bus_id", "adapter_name",
+ "function_name", "string", "chip_name", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 45, 43, 42, 47, 258, 94, 96,
+ 44, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 64, 40, 41
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 24, 25, 25, 26, 26, 26, 26, 26, 26,
+ 26, 27, 28, 29, 30, 31, 32, 33, 33, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 35, 36, 37, 38, 39
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 0, 2, 2, 2, 2, 2, 2, 2,
+ 2, 3, 3, 3, 5, 2, 2, 1, 2, 1,
+ 1, 1, 3, 3, 3, 3, 2, 3, 2, 2,
+ 1, 1, 1, 1, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 2, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0, 0, 10, 30, 0,
+ 32, 0, 0, 34, 16, 17, 0, 15, 4, 5,
+ 6, 8, 9, 7, 31, 11, 33, 12, 0, 0,
+ 0, 19, 20, 21, 0, 13, 18, 0, 26, 28,
+ 29, 0, 0, 0, 0, 0, 0, 27, 23, 22,
+ 24, 25, 14
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 1, 10, 11, 12, 13, 14, 15, 16, 24,
+ 45, 19, 35, 21, 37, 25
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -27
+static const yytype_int8 yypact[] =
+{
+ -27, 37, -27, -4, -3, 1, 1, 6, 1, 1,
+ -27, 8, 13, 20, 23, 32, 34, -27, -27, 29,
+ -27, 39, 14, -27, 6, -27, 14, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, 14, 14,
+ 14, -27, -27, -27, 14, 36, -27, 5, -27, -27,
+ -27, -2, 14, 14, 14, 14, 14, -27, 0, 0,
+ -27, -27, 36
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -26, -27, -27, 38, -27, 31
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 47, 52, 53, 54, 55, 54, 55, 17, 52, 53,
+ 54, 55, 48, 49, 50, 56, 18, 38, 51, 28,
+ 20, 57, 39, 40, 29, 23, 58, 59, 60, 61,
+ 62, 30, 41, 42, 31, 43, 44, 2, 3, 52,
+ 53, 54, 55, 32, 22, 33, 26, 27, 34, 4,
+ 5, 6, 7, 8, 9, 46, 0, 0, 36
+};
+
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-27))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
+static const yytype_int8 yycheck[] =
+{
+ 26, 3, 4, 5, 6, 5, 6, 11, 3, 4,
+ 5, 6, 38, 39, 40, 10, 19, 3, 44, 11,
+ 19, 23, 8, 9, 11, 19, 52, 53, 54, 55,
+ 56, 11, 18, 19, 11, 21, 22, 0, 1, 3,
+ 4, 5, 6, 11, 6, 11, 8, 9, 19, 12,
+ 13, 14, 15, 16, 17, 24, -1, -1, 19
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 25, 0, 1, 12, 13, 14, 15, 16, 17,
+ 26, 27, 28, 29, 30, 31, 32, 11, 19, 35,
+ 19, 37, 37, 19, 33, 39, 37, 37, 11, 11,
+ 11, 11, 11, 11, 19, 36, 19, 38, 3, 8,
+ 9, 18, 19, 21, 22, 34, 39, 34, 34, 34,
+ 34, 34, 3, 4, 5, 6, 10, 23, 34, 34,
+ 34, 34, 34
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
+
+#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* This macro is provided for backward compatibility. */
+
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = 0;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 11:
+
+/* Line 1806 of yacc.c */
+#line 133 "lib/conf-parse.y"
+ { sensors_bus new_el;
+ new_el.line = (yyvsp[(1) - (3)].line);
+ new_el.bus = (yyvsp[(2) - (3)].bus);
+ new_el.adapter = (yyvsp[(3) - (3)].name);
+ bus_add_el(&new_el);
+ }
+ break;
+
+ case 12:
+
+/* Line 1806 of yacc.c */
+#line 142 "lib/conf-parse.y"
+ { sensors_label new_el;
+ if (!current_chip) {
+ sensors_yyerror("Label statement before first chip statement");
+ free((yyvsp[(2) - (3)].name));
+ free((yyvsp[(3) - (3)].name));
+ YYERROR;
+ }
+ new_el.line = (yyvsp[(1) - (3)].line);
+ new_el.name = (yyvsp[(2) - (3)].name);
+ new_el.value = (yyvsp[(3) - (3)].name);
+ label_add_el(&new_el);
+ }
+ break;
+
+ case 13:
+
+/* Line 1806 of yacc.c */
+#line 157 "lib/conf-parse.y"
+ { sensors_set new_el;
+ if (!current_chip) {
+ sensors_yyerror("Set statement before first chip statement");
+ free((yyvsp[(2) - (3)].name));
+ sensors_free_expr((yyvsp[(3) - (3)].expr));
+ YYERROR;
+ }
+ new_el.line = (yyvsp[(1) - (3)].line);
+ new_el.name = (yyvsp[(2) - (3)].name);
+ new_el.value = (yyvsp[(3) - (3)].expr);
+ set_add_el(&new_el);
+ }
+ break;
+
+ case 14:
+
+/* Line 1806 of yacc.c */
+#line 172 "lib/conf-parse.y"
+ { sensors_compute new_el;
+ if (!current_chip) {
+ sensors_yyerror("Compute statement before first chip statement");
+ free((yyvsp[(2) - (5)].name));
+ sensors_free_expr((yyvsp[(3) - (5)].expr));
+ sensors_free_expr((yyvsp[(5) - (5)].expr));
+ YYERROR;
+ }
+ new_el.line = (yyvsp[(1) - (5)].line);
+ new_el.name = (yyvsp[(2) - (5)].name);
+ new_el.from_proc = (yyvsp[(3) - (5)].expr);
+ new_el.to_proc = (yyvsp[(5) - (5)].expr);
+ compute_add_el(&new_el);
+ }
+ break;
+
+ case 15:
+
+/* Line 1806 of yacc.c */
+#line 189 "lib/conf-parse.y"
+ { sensors_ignore new_el;
+ if (!current_chip) {
+ sensors_yyerror("Ignore statement before first chip statement");
+ free((yyvsp[(2) - (2)].name));
+ YYERROR;
+ }
+ new_el.line = (yyvsp[(1) - (2)].line);
+ new_el.name = (yyvsp[(2) - (2)].name);
+ ignore_add_el(&new_el);
+ }
+ break;
+
+ case 16:
+
+/* Line 1806 of yacc.c */
+#line 202 "lib/conf-parse.y"
+ { sensors_chip new_el;
+ new_el.line = (yyvsp[(1) - (2)].line);
+ new_el.labels = NULL;
+ new_el.sets = NULL;
+ new_el.computes = NULL;
+ new_el.ignores = NULL;
+ new_el.labels_count = new_el.labels_max = 0;
+ new_el.sets_count = new_el.sets_max = 0;
+ new_el.computes_count = new_el.computes_max = 0;
+ new_el.ignores_count = new_el.ignores_max = 0;
+ new_el.chips = (yyvsp[(2) - (2)].chips);
+ chip_add_el(&new_el);
+ current_chip = sensors_config_chips +
+ sensors_config_chips_count - 1;
+ }
+ break;
+
+ case 17:
+
+/* Line 1806 of yacc.c */
+#line 220 "lib/conf-parse.y"
+ {
+ (yyval.chips).fits = NULL;
+ (yyval.chips).fits_count = (yyval.chips).fits_max = 0;
+ fits_add_el(&(yyvsp[(1) - (1)].chip),(yyval.chips));
+ }
+ break;
+
+ case 18:
+
+/* Line 1806 of yacc.c */
+#line 226 "lib/conf-parse.y"
+ { (yyval.chips) = (yyvsp[(1) - (2)].chips);
+ fits_add_el(&(yyvsp[(2) - (2)].chip),(yyval.chips));
+ }
+ break;
+
+ case 19:
+
+/* Line 1806 of yacc.c */
+#line 232 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->data.val = (yyvsp[(1) - (1)].value);
+ (yyval.expr)->kind = sensors_kind_val;
+ }
+ break;
+
+ case 20:
+
+/* Line 1806 of yacc.c */
+#line 237 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->data.var = (yyvsp[(1) - (1)].name);
+ (yyval.expr)->kind = sensors_kind_var;
+ }
+ break;
+
+ case 21:
+
+/* Line 1806 of yacc.c */
+#line 242 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_source;
+ }
+ break;
+
+ case 22:
+
+/* Line 1806 of yacc.c */
+#line 246 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_add;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr);
+ (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr);
+ }
+ break;
+
+ case 23:
+
+/* Line 1806 of yacc.c */
+#line 253 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_sub;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr);
+ (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr);
+ }
+ break;
+
+ case 24:
+
+/* Line 1806 of yacc.c */
+#line 260 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_multiply;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr);
+ (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr);
+ }
+ break;
+
+ case 25:
+
+/* Line 1806 of yacc.c */
+#line 267 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_divide;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr);
+ (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr);
+ }
+ break;
+
+ case 26:
+
+/* Line 1806 of yacc.c */
+#line 274 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_negate;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr);
+ (yyval.expr)->data.subexpr.sub2 = NULL;
+ }
+ break;
+
+ case 27:
+
+/* Line 1806 of yacc.c */
+#line 281 "lib/conf-parse.y"
+ { (yyval.expr) = (yyvsp[(2) - (3)].expr); }
+ break;
+
+ case 28:
+
+/* Line 1806 of yacc.c */
+#line 283 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_exp;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr);
+ (yyval.expr)->data.subexpr.sub2 = NULL;
+ }
+ break;
+
+ case 29:
+
+/* Line 1806 of yacc.c */
+#line 290 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_log;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr);
+ (yyval.expr)->data.subexpr.sub2 = NULL;
+ }
+ break;
+
+ case 30:
+
+/* Line 1806 of yacc.c */
+#line 299 "lib/conf-parse.y"
+ { int res = sensors_parse_bus_id((yyvsp[(1) - (1)].name),&(yyval.bus));
+ free((yyvsp[(1) - (1)].name));
+ if (res) {
+ sensors_yyerror("Parse error in bus id");
+ YYERROR;
+ }
+ }
+ break;
+
+ case 31:
+
+/* Line 1806 of yacc.c */
+#line 309 "lib/conf-parse.y"
+ { (yyval.name) = (yyvsp[(1) - (1)].name); }
+ break;
+
+ case 32:
+
+/* Line 1806 of yacc.c */
+#line 313 "lib/conf-parse.y"
+ { (yyval.name) = (yyvsp[(1) - (1)].name); }
+ break;
+
+ case 33:
+
+/* Line 1806 of yacc.c */
+#line 317 "lib/conf-parse.y"
+ { (yyval.name) = (yyvsp[(1) - (1)].name); }
+ break;
+
+ case 34:
+
+/* Line 1806 of yacc.c */
+#line 321 "lib/conf-parse.y"
+ { int res = sensors_parse_chip_name((yyvsp[(1) - (1)].name),&(yyval.chip));
+ free((yyvsp[(1) - (1)].name));
+ if (res) {
+ sensors_yyerror("Parse error in chip name");
+ YYERROR;
+ }
+ }
+ break;
+
+
+
+/* Line 1806 of yacc.c */
+#line 1793 "lib/conf-parse.c"
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
+/* Line 2067 of yacc.c */
+#line 330 "lib/conf-parse.y"
+
+
+void sensors_yyerror(const char *err)
+{
+ if (sensors_lex_error[0]) {
+ sensors_parse_error_wfn(sensors_lex_error, sensors_yyfilename, sensors_yylineno);
+ sensors_lex_error[0] = '\0';
+ } else
+ sensors_parse_error_wfn(err, sensors_yyfilename, sensors_yylineno);
+}
+
+sensors_expr *malloc_expr(void)
+{
+ sensors_expr *res = malloc(sizeof(sensors_expr));
+ if (! res)
+ sensors_fatal_error(__func__, "Allocating a new expression");
+ return res;
+}
+
diff --git a/tools/gator/daemon/libsensors/conf-parse.h b/tools/gator/daemon/libsensors/conf-parse.h
new file mode 100644
index 000000000000..89c9c1a0d59f
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf-parse.h
@@ -0,0 +1,84 @@
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ NEG = 258,
+ EOL = 259,
+ BUS = 260,
+ LABEL = 261,
+ SET = 262,
+ CHIP = 263,
+ COMPUTE = 264,
+ IGNORE = 265,
+ FLOAT = 266,
+ NAME = 267,
+ ERROR = 268
+ };
+#endif
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 2068 of yacc.c */
+#line 79 "lib/conf-parse.y"
+
+ double value;
+ char *name;
+ void *nothing;
+ sensors_chip_name_list chips;
+ sensors_expr *expr;
+ sensors_bus_id bus;
+ sensors_chip_name chip;
+ sensors_config_line line;
+
+
+
+/* Line 2068 of yacc.c */
+#line 76 "lib/conf-parse.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE sensors_yylval;
+
+
diff --git a/tools/gator/daemon/libsensors/conf-parse.y b/tools/gator/daemon/libsensors/conf-parse.y
new file mode 100644
index 000000000000..1937f544dc66
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf-parse.y
@@ -0,0 +1,347 @@
+%{
+/*
+ conf-parse.y - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#define YYERROR_VERBOSE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "data.h"
+#include "general.h"
+#include "error.h"
+#include "conf.h"
+#include "access.h"
+#include "init.h"
+
+static void sensors_yyerror(const char *err);
+static sensors_expr *malloc_expr(void);
+
+static sensors_chip *current_chip = NULL;
+
+#define bus_add_el(el) sensors_add_array_el(el,\
+ &sensors_config_busses,\
+ &sensors_config_busses_count,\
+ &sensors_config_busses_max,\
+ sizeof(sensors_bus))
+#define label_add_el(el) sensors_add_array_el(el,\
+ &current_chip->labels,\
+ &current_chip->labels_count,\
+ &current_chip->labels_max,\
+ sizeof(sensors_label));
+#define set_add_el(el) sensors_add_array_el(el,\
+ &current_chip->sets,\
+ &current_chip->sets_count,\
+ &current_chip->sets_max,\
+ sizeof(sensors_set));
+#define compute_add_el(el) sensors_add_array_el(el,\
+ &current_chip->computes,\
+ &current_chip->computes_count,\
+ &current_chip->computes_max,\
+ sizeof(sensors_compute));
+#define ignore_add_el(el) sensors_add_array_el(el,\
+ &current_chip->ignores,\
+ &current_chip->ignores_count,\
+ &current_chip->ignores_max,\
+ sizeof(sensors_ignore));
+#define chip_add_el(el) sensors_add_array_el(el,\
+ &sensors_config_chips,\
+ &sensors_config_chips_count,\
+ &sensors_config_chips_max,\
+ sizeof(sensors_chip));
+
+#define fits_add_el(el,list) sensors_add_array_el(el,\
+ &(list).fits,\
+ &(list).fits_count,\
+ &(list).fits_max, \
+ sizeof(sensors_chip_name));
+
+%}
+
+%union {
+ double value;
+ char *name;
+ void *nothing;
+ sensors_chip_name_list chips;
+ sensors_expr *expr;
+ sensors_bus_id bus;
+ sensors_chip_name chip;
+ sensors_config_line line;
+}
+
+%left <nothing> '-' '+'
+%left <nothing> '*' '/'
+%left <nothing> NEG
+%right <nothing> '^' '`'
+
+%token <nothing> ','
+%token <nothing> EOL
+%token <line> BUS
+%token <line> LABEL
+%token <line> SET
+%token <line> CHIP
+%token <line> COMPUTE
+%token <line> IGNORE
+%token <value> FLOAT
+%token <name> NAME
+%token <nothing> ERROR
+
+%type <chips> chip_name_list
+%type <expr> expression
+%type <bus> bus_id
+%type <name> adapter_name
+%type <name> function_name
+%type <name> string
+%type <chip> chip_name
+
+%start input
+
+%%
+
+input: /* empty */
+ | input line
+;
+
+line: bus_statement EOL
+ | label_statement EOL
+ | set_statement EOL
+ | chip_statement EOL
+ | compute_statement EOL
+ | ignore_statement EOL
+ | error EOL
+;
+
+bus_statement: BUS bus_id adapter_name
+ { sensors_bus new_el;
+ new_el.line = $1;
+ new_el.bus = $2;
+ new_el.adapter = $3;
+ bus_add_el(&new_el);
+ }
+;
+
+label_statement: LABEL function_name string
+ { sensors_label new_el;
+ if (!current_chip) {
+ sensors_yyerror("Label statement before first chip statement");
+ free($2);
+ free($3);
+ YYERROR;
+ }
+ new_el.line = $1;
+ new_el.name = $2;
+ new_el.value = $3;
+ label_add_el(&new_el);
+ }
+;
+
+set_statement: SET function_name expression
+ { sensors_set new_el;
+ if (!current_chip) {
+ sensors_yyerror("Set statement before first chip statement");
+ free($2);
+ sensors_free_expr($3);
+ YYERROR;
+ }
+ new_el.line = $1;
+ new_el.name = $2;
+ new_el.value = $3;
+ set_add_el(&new_el);
+ }
+;
+
+compute_statement: COMPUTE function_name expression ',' expression
+ { sensors_compute new_el;
+ if (!current_chip) {
+ sensors_yyerror("Compute statement before first chip statement");
+ free($2);
+ sensors_free_expr($3);
+ sensors_free_expr($5);
+ YYERROR;
+ }
+ new_el.line = $1;
+ new_el.name = $2;
+ new_el.from_proc = $3;
+ new_el.to_proc = $5;
+ compute_add_el(&new_el);
+ }
+;
+
+ignore_statement: IGNORE function_name
+ { sensors_ignore new_el;
+ if (!current_chip) {
+ sensors_yyerror("Ignore statement before first chip statement");
+ free($2);
+ YYERROR;
+ }
+ new_el.line = $1;
+ new_el.name = $2;
+ ignore_add_el(&new_el);
+ }
+;
+
+chip_statement: CHIP chip_name_list
+ { sensors_chip new_el;
+ new_el.line = $1;
+ new_el.labels = NULL;
+ new_el.sets = NULL;
+ new_el.computes = NULL;
+ new_el.ignores = NULL;
+ new_el.labels_count = new_el.labels_max = 0;
+ new_el.sets_count = new_el.sets_max = 0;
+ new_el.computes_count = new_el.computes_max = 0;
+ new_el.ignores_count = new_el.ignores_max = 0;
+ new_el.chips = $2;
+ chip_add_el(&new_el);
+ current_chip = sensors_config_chips +
+ sensors_config_chips_count - 1;
+ }
+;
+
+chip_name_list: chip_name
+ {
+ $$.fits = NULL;
+ $$.fits_count = $$.fits_max = 0;
+ fits_add_el(&$1,$$);
+ }
+ | chip_name_list chip_name
+ { $$ = $1;
+ fits_add_el(&$2,$$);
+ }
+;
+
+expression: FLOAT
+ { $$ = malloc_expr();
+ $$->data.val = $1;
+ $$->kind = sensors_kind_val;
+ }
+ | NAME
+ { $$ = malloc_expr();
+ $$->data.var = $1;
+ $$->kind = sensors_kind_var;
+ }
+ | '@'
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_source;
+ }
+ | expression '+' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_add;
+ $$->data.subexpr.sub1 = $1;
+ $$->data.subexpr.sub2 = $3;
+ }
+ | expression '-' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_sub;
+ $$->data.subexpr.sub1 = $1;
+ $$->data.subexpr.sub2 = $3;
+ }
+ | expression '*' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_multiply;
+ $$->data.subexpr.sub1 = $1;
+ $$->data.subexpr.sub2 = $3;
+ }
+ | expression '/' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_divide;
+ $$->data.subexpr.sub1 = $1;
+ $$->data.subexpr.sub2 = $3;
+ }
+ | '-' expression %prec NEG
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_negate;
+ $$->data.subexpr.sub1 = $2;
+ $$->data.subexpr.sub2 = NULL;
+ }
+ | '(' expression ')'
+ { $$ = $2; }
+ | '^' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_exp;
+ $$->data.subexpr.sub1 = $2;
+ $$->data.subexpr.sub2 = NULL;
+ }
+ | '`' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_log;
+ $$->data.subexpr.sub1 = $2;
+ $$->data.subexpr.sub2 = NULL;
+ }
+;
+
+bus_id: NAME
+ { int res = sensors_parse_bus_id($1,&$$);
+ free($1);
+ if (res) {
+ sensors_yyerror("Parse error in bus id");
+ YYERROR;
+ }
+ }
+;
+
+adapter_name: NAME
+ { $$ = $1; }
+;
+
+function_name: NAME
+ { $$ = $1; }
+;
+
+string: NAME
+ { $$ = $1; }
+;
+
+chip_name: NAME
+ { int res = sensors_parse_chip_name($1,&$$);
+ free($1);
+ if (res) {
+ sensors_yyerror("Parse error in chip name");
+ YYERROR;
+ }
+ }
+;
+
+%%
+
+void sensors_yyerror(const char *err)
+{
+ if (sensors_lex_error[0]) {
+ sensors_parse_error_wfn(sensors_lex_error, sensors_yyfilename, sensors_yylineno);
+ sensors_lex_error[0] = '\0';
+ } else
+ sensors_parse_error_wfn(err, sensors_yyfilename, sensors_yylineno);
+}
+
+sensors_expr *malloc_expr(void)
+{
+ sensors_expr *res = malloc(sizeof(sensors_expr));
+ if (! res)
+ sensors_fatal_error(__func__, "Allocating a new expression");
+ return res;
+}
diff --git a/tools/gator/daemon/libsensors/conf.h b/tools/gator/daemon/libsensors/conf.h
new file mode 100644
index 000000000000..b7ce4f7ca833
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf.h
@@ -0,0 +1,34 @@
+/*
+ conf.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef LIB_SENSORS_CONF_H
+#define LIB_SENSORS_CONF_H
+
+/* This is defined in conf-lex.l */
+int sensors_yylex(void);
+extern char sensors_lex_error[];
+extern const char *sensors_yyfilename;
+extern int sensors_yylineno;
+extern FILE *sensors_yyin;
+
+/* This is defined in conf-parse.y */
+int sensors_yyparse(void);
+
+#endif /* LIB_SENSORS_CONF_H */
diff --git a/tools/gator/daemon/libsensors/data.c b/tools/gator/daemon/libsensors/data.c
new file mode 100644
index 000000000000..cac9c8dc6eb1
--- /dev/null
+++ b/tools/gator/daemon/libsensors/data.c
@@ -0,0 +1,278 @@
+/*
+ data.c - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007, 2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+/*** This file modified by ARM on Jan 23, 2013 to move version.h to the current directory. ***/
+
+/* this define needed for strndup() */
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "access.h"
+#include "error.h"
+#include "data.h"
+#include "sensors.h"
+#include "version.h"
+
+const char *libsensors_version = LM_VERSION;
+
+char **sensors_config_files = NULL;
+int sensors_config_files_count = 0;
+int sensors_config_files_max = 0;
+
+sensors_chip *sensors_config_chips = NULL;
+int sensors_config_chips_count = 0;
+int sensors_config_chips_subst = 0;
+int sensors_config_chips_max = 0;
+
+sensors_bus *sensors_config_busses = NULL;
+int sensors_config_busses_count = 0;
+int sensors_config_busses_max = 0;
+
+sensors_chip_features *sensors_proc_chips = NULL;
+int sensors_proc_chips_count = 0;
+int sensors_proc_chips_max = 0;
+
+sensors_bus *sensors_proc_bus = NULL;
+int sensors_proc_bus_count = 0;
+int sensors_proc_bus_max = 0;
+
+void sensors_free_chip_name(sensors_chip_name *chip)
+{
+ free(chip->prefix);
+}
+
+/*
+ Parse a chip name to the internal representation. These are valid names:
+
+ lm78-i2c-10-5e *-i2c-10-5e
+ lm78-i2c-10-* *-i2c-10-*
+ lm78-i2c-*-5e *-i2c-*-5e
+ lm78-i2c-*-* *-i2c-*-*
+ lm78-isa-10dd *-isa-10dd
+ lm78-isa-* *-isa-*
+ lm78-* *-*
+
+ Here 'lm78' can be any prefix. 'i2c' and 'isa' are
+ literal strings, just like all dashes '-' and wildcards '*'. '10' can
+ be any decimal i2c bus number. '5e' can be any hexadecimal i2c device
+ address, and '10dd' any hexadecimal isa address.
+
+ The 'prefix' part in the result is freshly allocated. All old contents
+ of res is overwritten. res itself is not allocated. In case of an error
+ return (ie. != 0), res is undefined, but all allocations are undone.
+*/
+
+int sensors_parse_chip_name(const char *name, sensors_chip_name *res)
+{
+ char *dash;
+
+ /* First, the prefix. It's either "*" or a real chip name. */
+ if (!strncmp(name, "*-", 2)) {
+ res->prefix = SENSORS_CHIP_NAME_PREFIX_ANY;
+ name += 2;
+ } else {
+ if (!(dash = strchr(name, '-')))
+ return -SENSORS_ERR_CHIP_NAME;
+ res->prefix = strndup(name, dash - name);
+ if (!res->prefix)
+ sensors_fatal_error(__func__,
+ "Allocating name prefix");
+ name = dash + 1;
+ }
+
+ /* Then we have either a sole "*" (all chips with this name) or a bus
+ type and an address. */
+ if (!strcmp(name, "*")) {
+ res->bus.type = SENSORS_BUS_TYPE_ANY;
+ res->bus.nr = SENSORS_BUS_NR_ANY;
+ res->addr = SENSORS_CHIP_NAME_ADDR_ANY;
+ return 0;
+ }
+
+ if (!(dash = strchr(name, '-')))
+ goto ERROR;
+ if (!strncmp(name, "i2c", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_I2C;
+ else if (!strncmp(name, "isa", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_ISA;
+ else if (!strncmp(name, "pci", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_PCI;
+ else if (!strncmp(name, "spi", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_SPI;
+ else if (!strncmp(name, "virtual", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_VIRTUAL;
+ else if (!strncmp(name, "acpi", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_ACPI;
+ else if (!strncmp(name, "hid", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_HID;
+ else
+ goto ERROR;
+ name = dash + 1;
+
+ /* Some bus types (i2c, spi) have an additional bus number.
+ For these, the next part is either a "*" (any bus of that type)
+ or a decimal number. */
+ switch (res->bus.type) {
+ case SENSORS_BUS_TYPE_I2C:
+ case SENSORS_BUS_TYPE_SPI:
+ case SENSORS_BUS_TYPE_HID:
+ if (!strncmp(name, "*-", 2)) {
+ res->bus.nr = SENSORS_BUS_NR_ANY;
+ name += 2;
+ break;
+ }
+
+ res->bus.nr = strtoul(name, &dash, 10);
+ if (*name == '\0' || *dash != '-' || res->bus.nr < 0)
+ goto ERROR;
+ name = dash + 1;
+ break;
+ default:
+ res->bus.nr = SENSORS_BUS_NR_ANY;
+ }
+
+ /* Last part is the chip address, or "*" for any address. */
+ if (!strcmp(name, "*")) {
+ res->addr = SENSORS_CHIP_NAME_ADDR_ANY;
+ } else {
+ res->addr = strtoul(name, &dash, 16);
+ if (*name == '\0' || *dash != '\0' || res->addr < 0)
+ goto ERROR;
+ }
+
+ return 0;
+
+ERROR:
+ free(res->prefix);
+ return -SENSORS_ERR_CHIP_NAME;
+}
+
+int sensors_snprintf_chip_name(char *str, size_t size,
+ const sensors_chip_name *chip)
+{
+ if (sensors_chip_name_has_wildcards(chip))
+ return -SENSORS_ERR_WILDCARDS;
+
+ switch (chip->bus.type) {
+ case SENSORS_BUS_TYPE_ISA:
+ return snprintf(str, size, "%s-isa-%04x", chip->prefix,
+ chip->addr);
+ case SENSORS_BUS_TYPE_PCI:
+ return snprintf(str, size, "%s-pci-%04x", chip->prefix,
+ chip->addr);
+ case SENSORS_BUS_TYPE_I2C:
+ return snprintf(str, size, "%s-i2c-%hd-%02x", chip->prefix,
+ chip->bus.nr, chip->addr);
+ case SENSORS_BUS_TYPE_SPI:
+ return snprintf(str, size, "%s-spi-%hd-%x", chip->prefix,
+ chip->bus.nr, chip->addr);
+ case SENSORS_BUS_TYPE_VIRTUAL:
+ return snprintf(str, size, "%s-virtual-%x", chip->prefix,
+ chip->addr);
+ case SENSORS_BUS_TYPE_ACPI:
+ return snprintf(str, size, "%s-acpi-%x", chip->prefix,
+ chip->addr);
+ case SENSORS_BUS_TYPE_HID:
+ return snprintf(str, size, "%s-hid-%hd-%x", chip->prefix,
+ chip->bus.nr, chip->addr);
+ }
+
+ return -SENSORS_ERR_CHIP_NAME;
+}
+
+int sensors_parse_bus_id(const char *name, sensors_bus_id *bus)
+{
+ char *endptr;
+
+ if (strncmp(name, "i2c-", 4)) {
+ return -SENSORS_ERR_BUS_NAME;
+ }
+ name += 4;
+ bus->type = SENSORS_BUS_TYPE_I2C;
+ bus->nr = strtoul(name, &endptr, 10);
+ if (*name == '\0' || *endptr != '\0' || bus->nr < 0)
+ return -SENSORS_ERR_BUS_NAME;
+ return 0;
+}
+
+static int sensors_substitute_chip(sensors_chip_name *name,
+ const char *filename, int lineno)
+{
+ int i, j;
+ for (i = 0; i < sensors_config_busses_count; i++)
+ if (sensors_config_busses[i].bus.type == name->bus.type &&
+ sensors_config_busses[i].bus.nr == name->bus.nr)
+ break;
+
+ if (i == sensors_config_busses_count) {
+ sensors_parse_error_wfn("Undeclared bus id referenced",
+ filename, lineno);
+ name->bus.nr = SENSORS_BUS_NR_IGNORE;
+ return -SENSORS_ERR_BUS_NAME;
+ }
+
+ /* Compare the adapter names */
+ for (j = 0; j < sensors_proc_bus_count; j++) {
+ if (!strcmp(sensors_config_busses[i].adapter,
+ sensors_proc_bus[j].adapter)) {
+ name->bus.nr = sensors_proc_bus[j].bus.nr;
+ return 0;
+ }
+ }
+
+ /* We did not find a matching bus name, simply ignore this chip
+ config entry. */
+ name->bus.nr = SENSORS_BUS_NR_IGNORE;
+ return 0;
+}
+
+/* Bus substitution is on a per-configuration file basis, so we keep
+ memory (in sensors_config_chips_subst) of which chip entries have been
+ already substituted. */
+int sensors_substitute_busses(void)
+{
+ int err, i, j, lineno;
+ sensors_chip_name_list *chips;
+ const char *filename;
+ int res = 0;
+
+ for (i = sensors_config_chips_subst;
+ i < sensors_config_chips_count; i++) {
+ filename = sensors_config_chips[i].line.filename;
+ lineno = sensors_config_chips[i].line.lineno;
+ chips = &sensors_config_chips[i].chips;
+ for (j = 0; j < chips->fits_count; j++) {
+ /* We can only substitute if a specific bus number
+ is given. */
+ if (chips->fits[j].bus.nr == SENSORS_BUS_NR_ANY)
+ continue;
+
+ err = sensors_substitute_chip(&chips->fits[j],
+ filename, lineno);
+ if (err)
+ res = err;
+ }
+ }
+ sensors_config_chips_subst = sensors_config_chips_count;
+ return res;
+}
diff --git a/tools/gator/daemon/libsensors/data.h b/tools/gator/daemon/libsensors/data.h
new file mode 100644
index 000000000000..4a23eabe1414
--- /dev/null
+++ b/tools/gator/daemon/libsensors/data.h
@@ -0,0 +1,184 @@
+/*
+ data.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007, 2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef LIB_SENSORS_DATA_H
+#define LIB_SENSORS_DATA_H
+
+#include "sensors.h"
+#include "general.h"
+
+/* This header file contains all kinds of data structures which are used
+ for the representation of the config file data and the sensors
+ data. */
+
+/* Kinds of expression operators recognized */
+typedef enum sensors_operation {
+ sensors_add, sensors_sub, sensors_multiply, sensors_divide,
+ sensors_negate, sensors_exp, sensors_log,
+} sensors_operation;
+
+/* An expression can have several forms */
+typedef enum sensors_expr_kind {
+ sensors_kind_val, sensors_kind_source, sensors_kind_var,
+ sensors_kind_sub
+} sensors_expr_kind;
+
+/* An expression. It is either a floating point value, a variable name,
+ an operation on subexpressions, or the special value 'sub' } */
+struct sensors_expr;
+
+typedef struct sensors_subexpr {
+ sensors_operation op;
+ struct sensors_expr *sub1;
+ struct sensors_expr *sub2;
+} sensors_subexpr;
+
+typedef struct sensors_expr {
+ sensors_expr_kind kind;
+ union {
+ double val;
+ char *var;
+ sensors_subexpr subexpr;
+ } data;
+} sensors_expr;
+
+/* Config file line reference */
+typedef struct sensors_config_line {
+ const char *filename;
+ int lineno;
+} sensors_config_line;
+
+/* Config file label declaration: a feature name, combined with the label
+ value */
+typedef struct sensors_label {
+ char *name;
+ char *value;
+ sensors_config_line line;
+} sensors_label;
+
+/* Config file set declaration: a subfeature name, combined with an
+ expression */
+typedef struct sensors_set {
+ char *name;
+ sensors_expr *value;
+ sensors_config_line line;
+} sensors_set;
+
+/* Config file compute declaration: a feature name, combined with two
+ expressions */
+typedef struct sensors_compute {
+ char *name;
+ sensors_expr *from_proc;
+ sensors_expr *to_proc;
+ sensors_config_line line;
+} sensors_compute;
+
+/* Config file ignore declaration: a feature name */
+typedef struct sensors_ignore {
+ char *name;
+ sensors_config_line line;
+} sensors_ignore;
+
+/* A list of chip names, used to represent a config file chips declaration */
+typedef struct sensors_chip_name_list {
+ sensors_chip_name *fits;
+ int fits_count;
+ int fits_max;
+} sensors_chip_name_list;
+
+/* A config file chip block */
+typedef struct sensors_chip {
+ sensors_chip_name_list chips;
+ sensors_label *labels;
+ int labels_count;
+ int labels_max;
+ sensors_set *sets;
+ int sets_count;
+ int sets_max;
+ sensors_compute *computes;
+ int computes_count;
+ int computes_max;
+ sensors_ignore *ignores;
+ int ignores_count;
+ int ignores_max;
+ sensors_config_line line;
+} sensors_chip;
+
+/* Config file bus declaration: the bus type and number, combined with adapter
+ name */
+typedef struct sensors_bus {
+ char *adapter;
+ sensors_bus_id bus;
+ sensors_config_line line;
+} sensors_bus;
+
+/* Internal data about all features and subfeatures of a chip */
+typedef struct sensors_chip_features {
+ struct sensors_chip_name chip;
+ struct sensors_feature *feature;
+ struct sensors_subfeature *subfeature;
+ int feature_count;
+ int subfeature_count;
+} sensors_chip_features;
+
+extern char **sensors_config_files;
+extern int sensors_config_files_count;
+extern int sensors_config_files_max;
+
+#define sensors_add_config_files(el) sensors_add_array_el( \
+ (el), &sensors_config_files, &sensors_config_files_count, \
+ &sensors_config_files_max, sizeof(char *))
+
+extern sensors_chip *sensors_config_chips;
+extern int sensors_config_chips_count;
+extern int sensors_config_chips_subst;
+extern int sensors_config_chips_max;
+
+extern sensors_bus *sensors_config_busses;
+extern int sensors_config_busses_count;
+extern int sensors_config_busses_max;
+
+extern sensors_chip_features *sensors_proc_chips;
+extern int sensors_proc_chips_count;
+extern int sensors_proc_chips_max;
+
+#define sensors_add_proc_chips(el) sensors_add_array_el( \
+ (el), &sensors_proc_chips, &sensors_proc_chips_count,\
+ &sensors_proc_chips_max, sizeof(struct sensors_chip_features))
+
+extern sensors_bus *sensors_proc_bus;
+extern int sensors_proc_bus_count;
+extern int sensors_proc_bus_max;
+
+#define sensors_add_proc_bus(el) sensors_add_array_el( \
+ (el), &sensors_proc_bus, &sensors_proc_bus_count,\
+ &sensors_proc_bus_max, sizeof(struct sensors_bus))
+
+/* Substitute configuration bus numbers with real-world bus numbers
+ in the chips lists */
+int sensors_substitute_busses(void);
+
+
+/* Parse a bus id into its components. Returns 0 on success, a value from
+ error.h on failure. */
+int sensors_parse_bus_id(const char *name, sensors_bus_id *bus);
+
+#endif /* def LIB_SENSORS_DATA_H */
diff --git a/tools/gator/daemon/libsensors/error.c b/tools/gator/daemon/libsensors/error.c
new file mode 100644
index 000000000000..55bde81295fb
--- /dev/null
+++ b/tools/gator/daemon/libsensors/error.c
@@ -0,0 +1,92 @@
+/*
+ error.c - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007-2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "error.h"
+#include "general.h"
+
+static void sensors_default_parse_error(const char *err, int lineno);
+static void sensors_default_parse_error_wfn(const char *err,
+ const char *filename, int lineno);
+static void sensors_default_fatal_error(const char *proc, const char *err);
+
+void (*sensors_parse_error) (const char *err, int lineno) =
+ sensors_default_parse_error;
+void (*sensors_parse_error_wfn) (const char *err, const char *filename,
+ int lineno) = sensors_default_parse_error_wfn;
+void (*sensors_fatal_error) (const char *proc, const char *err) =
+ sensors_default_fatal_error;
+
+static const char *errorlist[] = {
+ /* Invalid error code */ "Unknown error",
+ /* SENSORS_ERR_WILDCARDS */ "Wildcard found in chip name",
+ /* SENSORS_ERR_NO_ENTRY */ "No such subfeature known",
+ /* SENSORS_ERR_ACCESS_R */ "Can't read",
+ /* SENSORS_ERR_KERNEL */ "Kernel interface error",
+ /* SENSORS_ERR_DIV_ZERO */ "Divide by zero",
+ /* SENSORS_ERR_CHIP_NAME */ "Can't parse chip name",
+ /* SENSORS_ERR_BUS_NAME */ "Can't parse bus name",
+ /* SENSORS_ERR_PARSE */ "General parse error",
+ /* SENSORS_ERR_ACCESS_W */ "Can't write",
+ /* SENSORS_ERR_IO */ "I/O error",
+ /* SENSORS_ERR_RECURSION */ "Evaluation recurses too deep",
+};
+
+const char *sensors_strerror(int errnum)
+{
+ if (errnum < 0)
+ errnum = -errnum;
+ if (errnum >= ARRAY_SIZE(errorlist))
+ errnum = 0;
+ return errorlist[errnum];
+}
+
+void sensors_default_parse_error(const char *err, int lineno)
+{
+ if (lineno)
+ fprintf(stderr, "Error: Line %d: %s\n", lineno, err);
+ else
+ fprintf(stderr, "Error: %s\n", err);
+}
+
+void sensors_default_parse_error_wfn(const char *err,
+ const char *filename, int lineno)
+{
+ /* If application provided a custom parse error reporting function
+ but not the variant with the filename, fall back to the original
+ variant without the filename, for backwards compatibility. */
+ if (sensors_parse_error != sensors_default_parse_error ||
+ !filename)
+ return sensors_parse_error(err, lineno);
+
+ if (lineno)
+ fprintf(stderr, "Error: File %s, line %d: %s\n", filename,
+ lineno, err);
+ else
+ fprintf(stderr, "Error: File %s: %s\n", filename, err);
+}
+
+void sensors_default_fatal_error(const char *proc, const char *err)
+{
+ fprintf(stderr, "Fatal error in `%s': %s\n", proc, err);
+ exit(1);
+}
diff --git a/tools/gator/daemon/libsensors/error.h b/tools/gator/daemon/libsensors/error.h
new file mode 100644
index 000000000000..37cdc956151d
--- /dev/null
+++ b/tools/gator/daemon/libsensors/error.h
@@ -0,0 +1,74 @@
+/*
+ error.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007-2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef LIB_SENSORS_ERROR_H
+#define LIB_SENSORS_ERROR_H
+
+#define SENSORS_ERR_WILDCARDS 1 /* Wildcard found in chip name */
+#define SENSORS_ERR_NO_ENTRY 2 /* No such subfeature known */
+#define SENSORS_ERR_ACCESS_R 3 /* Can't read */
+#define SENSORS_ERR_KERNEL 4 /* Kernel interface error */
+#define SENSORS_ERR_DIV_ZERO 5 /* Divide by zero */
+#define SENSORS_ERR_CHIP_NAME 6 /* Can't parse chip name */
+#define SENSORS_ERR_BUS_NAME 7 /* Can't parse bus name */
+#define SENSORS_ERR_PARSE 8 /* General parse error */
+#define SENSORS_ERR_ACCESS_W 9 /* Can't write */
+#define SENSORS_ERR_IO 10 /* I/O error */
+#define SENSORS_ERR_RECURSION 11 /* Evaluation recurses too deep */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* This function returns a pointer to a string which describes the error.
+ errnum may be negative (the corresponding positive error is returned).
+ You may not modify the result! */
+const char *sensors_strerror(int errnum);
+
+/* These functions are called when a parse error is detected. Give them new
+ values, and your own functions are called instead of the default (which
+ print to stderr). These functions may terminate the program, but they
+ usually output an error and return. The first function is the original
+ one, the second one was added later when support for multiple
+ configuration files was added.
+ The library code now only calls the second function. However, for
+ backwards compatibility, if an application provides a custom handling
+ function for the first function but not the second, then all parse
+ errors will be reported using the first function (that is, the filename
+ is never reported.)
+ Note that filename can be NULL (if filename isn't known) and lineno
+ can be 0 (if the error occurs before the actual parsing starts.) */
+extern void (*sensors_parse_error) (const char *err, int lineno);
+extern void (*sensors_parse_error_wfn) (const char *err,
+ const char *filename, int lineno);
+
+/* This function is called when an immediately fatal error (like no
+ memory left) is detected. Give it a new value, and your own function
+ is called instead of the default (which prints to stderr and ends
+ the program). Never let it return! */
+extern void (*sensors_fatal_error) (const char *proc, const char *err);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* def LIB_SENSORS_ERROR_H */
diff --git a/tools/gator/daemon/libsensors/general.c b/tools/gator/daemon/libsensors/general.c
new file mode 100644
index 000000000000..f237e3b59c22
--- /dev/null
+++ b/tools/gator/daemon/libsensors/general.c
@@ -0,0 +1,85 @@
+/*
+ general.c - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#include "error.h"
+#include "general.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+#define A_BUNCH 16
+
+void sensors_malloc_array(void *list, int *num_el, int *max_el, int el_size)
+{
+ void **my_list = (void **)list;
+
+ *my_list = malloc(el_size*A_BUNCH);
+ if (! *my_list)
+ sensors_fatal_error(__func__, "Allocating new elements");
+ *max_el = A_BUNCH;
+ *num_el = 0;
+}
+
+void sensors_free_array(void *list, int *num_el, int *max_el)
+{
+ void **my_list = (void **)list;
+
+ free(*my_list);
+ *my_list = NULL;
+ *num_el = 0;
+ *max_el = 0;
+}
+
+void sensors_add_array_el(const void *el, void *list, int *num_el,
+ int *max_el, int el_size)
+{
+ int new_max_el;
+ void **my_list = (void *)list;
+ if (*num_el + 1 > *max_el) {
+ new_max_el = *max_el + A_BUNCH;
+ *my_list = realloc(*my_list, new_max_el * el_size);
+ if (! *my_list)
+ sensors_fatal_error(__func__,
+ "Allocating new elements");
+ *max_el = new_max_el;
+ }
+ memcpy(((char *) *my_list) + *num_el * el_size, el, el_size);
+ (*num_el) ++;
+}
+
+void sensors_add_array_els(const void *els, int nr_els, void *list,
+ int *num_el, int *max_el, int el_size)
+{
+ int new_max_el;
+ void **my_list = (void *)list;
+ if (*num_el + nr_els > *max_el) {
+ new_max_el = (*max_el + nr_els + A_BUNCH);
+ new_max_el -= new_max_el % A_BUNCH;
+ *my_list = realloc(*my_list, new_max_el * el_size);
+ if (! *my_list)
+ sensors_fatal_error(__func__,
+ "Allocating new elements");
+ *max_el = new_max_el;
+ }
+ memcpy(((char *)*my_list) + *num_el * el_size, els, el_size * nr_els);
+ *num_el += nr_els;
+}
diff --git a/tools/gator/daemon/libsensors/general.h b/tools/gator/daemon/libsensors/general.h
new file mode 100644
index 000000000000..a3971e02e1b7
--- /dev/null
+++ b/tools/gator/daemon/libsensors/general.h
@@ -0,0 +1,39 @@
+/*
+ general.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef LIB_SENSORS_GENERAL
+#define LIB_SENSORS_GENERAL
+
+/* These are general purpose functions. They allow you to use variable-
+ length arrays, which are extended automatically. A distinction is
+ made between the current number of elements and the maximum number.
+ You can only add elements at the end. Primitive, but very useful
+ for internal use. */
+void sensors_malloc_array(void *list, int *num_el, int *max_el,
+ int el_size);
+void sensors_free_array(void *list, int *num_el, int *max_el);
+void sensors_add_array_el(const void *el, void *list, int *num_el,
+ int *max_el, int el_size);
+void sensors_add_array_els(const void *els, int nr_els, void *list,
+ int *num_el, int *max_el, int el_size);
+
+#define ARRAY_SIZE(arr) (int)(sizeof(arr) / sizeof((arr)[0]))
+
+#endif /* LIB_SENSORS_GENERAL */
diff --git a/tools/gator/daemon/libsensors/init.c b/tools/gator/daemon/libsensors/init.c
new file mode 100644
index 000000000000..558046e76c24
--- /dev/null
+++ b/tools/gator/daemon/libsensors/init.c
@@ -0,0 +1,341 @@
+/*
+ init.c - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007, 2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+/*** This file modified by ARM on Jan 23, 2013 to cast alphasort to supress a warning as it's prototype is different on android. ***/
+
+/* Needed for scandir() and alphasort() */
+#define _BSD_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include "sensors.h"
+#include "data.h"
+#include "error.h"
+#include "access.h"
+#include "conf.h"
+#include "sysfs.h"
+#include "scanner.h"
+#include "init.h"
+
+#define DEFAULT_CONFIG_FILE ETCDIR "/sensors3.conf"
+#define ALT_CONFIG_FILE ETCDIR "/sensors.conf"
+#define DEFAULT_CONFIG_DIR ETCDIR "/sensors.d"
+
+/* Wrapper around sensors_yyparse(), which clears the locale so that
+ the decimal numbers are always parsed properly. */
+static int sensors_parse(void)
+{
+ int res;
+ char *locale;
+
+ /* Remember the current locale and clear it */
+ locale = setlocale(LC_ALL, NULL);
+ if (locale) {
+ locale = strdup(locale);
+ if (!locale)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ setlocale(LC_ALL, "C");
+ }
+
+ res = sensors_yyparse();
+
+ /* Restore the old locale */
+ if (locale) {
+ setlocale(LC_ALL, locale);
+ free(locale);
+ }
+
+ return res;
+}
+
+static void free_bus(sensors_bus *bus)
+{
+ free(bus->adapter);
+}
+
+static void free_config_busses(void)
+{
+ int i;
+
+ for (i = 0; i < sensors_config_busses_count; i++)
+ free_bus(&sensors_config_busses[i]);
+ free(sensors_config_busses);
+ sensors_config_busses = NULL;
+ sensors_config_busses_count = sensors_config_busses_max = 0;
+}
+
+static int parse_config(FILE *input, const char *name)
+{
+ int err;
+ char *name_copy;
+
+ if (name) {
+ /* Record configuration file name for error reporting */
+ name_copy = strdup(name);
+ if (!name_copy)
+ sensors_fatal_error(__func__, "Out of memory");
+ sensors_add_config_files(&name_copy);
+ } else
+ name_copy = NULL;
+
+ if (sensors_scanner_init(input, name_copy)) {
+ err = -SENSORS_ERR_PARSE;
+ goto exit_cleanup;
+ }
+ err = sensors_parse();
+ sensors_scanner_exit();
+ if (err) {
+ err = -SENSORS_ERR_PARSE;
+ goto exit_cleanup;
+ }
+
+ err = sensors_substitute_busses();
+
+exit_cleanup:
+ free_config_busses();
+ return err;
+}
+
+static int config_file_filter(const struct dirent *entry)
+{
+ return entry->d_name[0] != '.'; /* Skip hidden files */
+}
+
+static int add_config_from_dir(const char *dir)
+{
+ int count, res, i;
+ struct dirent **namelist;
+
+ count = scandir(dir, &namelist, config_file_filter, (int (*)(const struct dirent **, const struct dirent **))alphasort);
+ if (count < 0) {
+ /* Do not return an error if directory does not exist */
+ if (errno == ENOENT)
+ return 0;
+
+ sensors_parse_error_wfn(strerror(errno), NULL, 0);
+ return -SENSORS_ERR_PARSE;
+ }
+
+ for (res = 0, i = 0; !res && i < count; i++) {
+ int len;
+ char path[PATH_MAX];
+ FILE *input;
+ struct stat st;
+
+ len = snprintf(path, sizeof(path), "%s/%s", dir,
+ namelist[i]->d_name);
+ if (len < 0 || len >= (int)sizeof(path)) {
+ res = -SENSORS_ERR_PARSE;
+ continue;
+ }
+
+ /* Only accept regular files */
+ if (stat(path, &st) < 0 || !S_ISREG(st.st_mode))
+ continue;
+
+ input = fopen(path, "r");
+ if (input) {
+ res = parse_config(input, path);
+ fclose(input);
+ } else {
+ res = -SENSORS_ERR_PARSE;
+ sensors_parse_error_wfn(strerror(errno), path, 0);
+ }
+ }
+
+ /* Free memory allocated by scandir() */
+ for (i = 0; i < count; i++)
+ free(namelist[i]);
+ free(namelist);
+
+ return res;
+}
+
+int sensors_init(FILE *input)
+{
+ int res;
+
+ if (!sensors_init_sysfs())
+ return -SENSORS_ERR_KERNEL;
+ if ((res = sensors_read_sysfs_bus()) ||
+ (res = sensors_read_sysfs_chips()))
+ goto exit_cleanup;
+
+ if (input) {
+ res = parse_config(input, NULL);
+ if (res)
+ goto exit_cleanup;
+ } else {
+ const char* name;
+
+ /* No configuration provided, use default */
+ input = fopen(name = DEFAULT_CONFIG_FILE, "r");
+ if (!input && errno == ENOENT)
+ input = fopen(name = ALT_CONFIG_FILE, "r");
+ if (input) {
+ res = parse_config(input, name);
+ fclose(input);
+ if (res)
+ goto exit_cleanup;
+
+ } else if (errno != ENOENT) {
+ sensors_parse_error_wfn(strerror(errno), name, 0);
+ res = -SENSORS_ERR_PARSE;
+ goto exit_cleanup;
+ }
+
+ /* Also check for files in default directory */
+ res = add_config_from_dir(DEFAULT_CONFIG_DIR);
+ if (res)
+ goto exit_cleanup;
+ }
+
+ return 0;
+
+exit_cleanup:
+ sensors_cleanup();
+ return res;
+}
+
+static void free_chip_name(sensors_chip_name *name)
+{
+ free(name->prefix);
+ free(name->path);
+}
+
+static void free_chip_features(sensors_chip_features *features)
+{
+ int i;
+
+ for (i = 0; i < features->subfeature_count; i++)
+ free(features->subfeature[i].name);
+ free(features->subfeature);
+ for (i = 0; i < features->feature_count; i++)
+ free(features->feature[i].name);
+ free(features->feature);
+}
+
+static void free_label(sensors_label *label)
+{
+ free(label->name);
+ free(label->value);
+}
+
+void sensors_free_expr(sensors_expr *expr)
+{
+ if (expr->kind == sensors_kind_var)
+ free(expr->data.var);
+ else if (expr->kind == sensors_kind_sub) {
+ if (expr->data.subexpr.sub1)
+ sensors_free_expr(expr->data.subexpr.sub1);
+ if (expr->data.subexpr.sub2)
+ sensors_free_expr(expr->data.subexpr.sub2);
+ }
+ free(expr);
+}
+
+static void free_set(sensors_set *set)
+{
+ free(set->name);
+ sensors_free_expr(set->value);
+}
+
+static void free_compute(sensors_compute *compute)
+{
+ free(compute->name);
+ sensors_free_expr(compute->from_proc);
+ sensors_free_expr(compute->to_proc);
+}
+
+static void free_ignore(sensors_ignore *ignore)
+{
+ free(ignore->name);
+}
+
+static void free_chip(sensors_chip *chip)
+{
+ int i;
+
+ for (i = 0; i < chip->chips.fits_count; i++)
+ free_chip_name(&chip->chips.fits[i]);
+ free(chip->chips.fits);
+ chip->chips.fits_count = chip->chips.fits_max = 0;
+
+ for (i = 0; i < chip->labels_count; i++)
+ free_label(&chip->labels[i]);
+ free(chip->labels);
+ chip->labels_count = chip->labels_max = 0;
+
+ for (i = 0; i < chip->sets_count; i++)
+ free_set(&chip->sets[i]);
+ free(chip->sets);
+ chip->sets_count = chip->sets_max = 0;
+
+ for (i = 0; i < chip->computes_count; i++)
+ free_compute(&chip->computes[i]);
+ free(chip->computes);
+ chip->computes_count = chip->computes_max = 0;
+
+ for (i = 0; i < chip->ignores_count; i++)
+ free_ignore(&chip->ignores[i]);
+ free(chip->ignores);
+ chip->ignores_count = chip->ignores_max = 0;
+}
+
+void sensors_cleanup(void)
+{
+ int i;
+
+ for (i = 0; i < sensors_proc_chips_count; i++) {
+ free_chip_name(&sensors_proc_chips[i].chip);
+ free_chip_features(&sensors_proc_chips[i]);
+ }
+ free(sensors_proc_chips);
+ sensors_proc_chips = NULL;
+ sensors_proc_chips_count = sensors_proc_chips_max = 0;
+
+ for (i = 0; i < sensors_config_chips_count; i++)
+ free_chip(&sensors_config_chips[i]);
+ free(sensors_config_chips);
+ sensors_config_chips = NULL;
+ sensors_config_chips_count = sensors_config_chips_max = 0;
+ sensors_config_chips_subst = 0;
+
+ for (i = 0; i < sensors_proc_bus_count; i++)
+ free_bus(&sensors_proc_bus[i]);
+ free(sensors_proc_bus);
+ sensors_proc_bus = NULL;
+ sensors_proc_bus_count = sensors_proc_bus_max = 0;
+
+ for (i = 0; i < sensors_config_files_count; i++)
+ free(sensors_config_files[i]);
+ free(sensors_config_files);
+ sensors_config_files = NULL;
+ sensors_config_files_count = sensors_config_files_max = 0;
+}
diff --git a/tools/gator/daemon/libsensors/init.h b/tools/gator/daemon/libsensors/init.h
new file mode 100644
index 000000000000..47006a62db63
--- /dev/null
+++ b/tools/gator/daemon/libsensors/init.h
@@ -0,0 +1,28 @@
+/*
+ init.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (C) 2007 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef LIB_SENSORS_INIT_H
+#define LIB_SENSORS_INIT_H
+
+#include "data.h"
+
+void sensors_free_expr(sensors_expr *expr);
+
+#endif /* def LIB_SENSORS_INIT_H */
diff --git a/tools/gator/daemon/libsensors/scanner.h b/tools/gator/daemon/libsensors/scanner.h
new file mode 100644
index 000000000000..4c415167de5c
--- /dev/null
+++ b/tools/gator/daemon/libsensors/scanner.h
@@ -0,0 +1,32 @@
+/*
+ scanner.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 2006 Mark M. Hoffman <mhoffman@lightlink.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+/*** This file modified by ARM on Jan 23, 2013 to fix input defined but not used warning from conf-lex.c. ***/
+
+#ifndef LIB_SENSORS_SCANNER_H
+#define LIB_SENSORS_SCANNER_H
+
+int sensors_scanner_init(FILE *input, const char *filename);
+void sensors_scanner_exit(void);
+
+#define YY_NO_INPUT
+
+#endif
+
diff --git a/tools/gator/daemon/libsensors/sensors.h b/tools/gator/daemon/libsensors/sensors.h
new file mode 100644
index 000000000000..7874d028dc82
--- /dev/null
+++ b/tools/gator/daemon/libsensors/sensors.h
@@ -0,0 +1,311 @@
+/*
+ sensors.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007, 2010 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+/*** This file modified by ARM on Jan 23, 2013 to read non-scaled values. ***/
+
+#ifndef LIB_SENSORS_SENSORS_H
+#define LIB_SENSORS_SENSORS_H
+
+#include <stdio.h>
+#include <limits.h>
+
+/* Publicly accessible library functions */
+
+/* libsensors API version define, first digit is the major version (changed
+ when the API + ABI breaks), the third digit is incremented to track small
+ API additions like new flags / enum values. The second digit is for tracking
+ larger additions like new methods. */
+#define SENSORS_API_VERSION 0x432
+
+#define SENSORS_CHIP_NAME_PREFIX_ANY NULL
+#define SENSORS_CHIP_NAME_ADDR_ANY (-1)
+
+#define SENSORS_BUS_TYPE_ANY (-1)
+#define SENSORS_BUS_TYPE_I2C 0
+#define SENSORS_BUS_TYPE_ISA 1
+#define SENSORS_BUS_TYPE_PCI 2
+#define SENSORS_BUS_TYPE_SPI 3
+#define SENSORS_BUS_TYPE_VIRTUAL 4
+#define SENSORS_BUS_TYPE_ACPI 5
+#define SENSORS_BUS_TYPE_HID 6
+#define SENSORS_BUS_NR_ANY (-1)
+#define SENSORS_BUS_NR_IGNORE (-2)
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+extern const char *libsensors_version;
+
+extern int sensors_sysfs_no_scaling;
+
+typedef struct sensors_bus_id {
+ short type;
+ short nr;
+} sensors_bus_id;
+
+/* A chip name is encoded in this structure */
+typedef struct sensors_chip_name {
+ char *prefix;
+ sensors_bus_id bus;
+ int addr;
+ char *path;
+} sensors_chip_name;
+
+/* Load the configuration file and the detected chips list. If this
+ returns a value unequal to zero, you are in trouble; you can not
+ assume anything will be initialized properly. If you want to
+ reload the configuration file, call sensors_cleanup() below before
+ calling sensors_init() again. */
+int sensors_init(FILE *input);
+
+/* Clean-up function: You can't access anything after
+ this, until the next sensors_init() call! */
+void sensors_cleanup(void);
+
+/* Parse a chip name to the internal representation. Return 0 on success, <0
+ on error. */
+int sensors_parse_chip_name(const char *orig_name, sensors_chip_name *res);
+
+/* Free memory allocated for the internal representation of a chip name. */
+void sensors_free_chip_name(sensors_chip_name *chip);
+
+/* Print a chip name from its internal representation. Note that chip should
+ not contain wildcard values! Return the number of characters printed on
+ success (same as snprintf), <0 on error. */
+int sensors_snprintf_chip_name(char *str, size_t size,
+ const sensors_chip_name *chip);
+
+/* This function returns the adapter name of a bus,
+ as used within the sensors_chip_name structure. If it could not be found,
+ it returns NULL */
+const char *sensors_get_adapter_name(const sensors_bus_id *bus);
+
+typedef struct sensors_feature sensors_feature;
+
+/* Look up the label for a given feature. Note that chip should not
+ contain wildcard values! The returned string is newly allocated (free it
+ yourself). On failure, NULL is returned.
+ If no label exists for this feature, its name is returned itself. */
+char *sensors_get_label(const sensors_chip_name *name,
+ const sensors_feature *feature);
+
+/* Read the value of a subfeature of a certain chip. Note that chip should not
+ contain wildcard values! This function will return 0 on success, and <0
+ on failure. */
+int sensors_get_value(const sensors_chip_name *name, int subfeat_nr,
+ double *value);
+
+/* Set the value of a subfeature of a certain chip. Note that chip should not
+ contain wildcard values! This function will return 0 on success, and <0
+ on failure. */
+int sensors_set_value(const sensors_chip_name *name, int subfeat_nr,
+ double value);
+
+/* Execute all set statements for this particular chip. The chip may contain
+ wildcards! This function will return 0 on success, and <0 on failure. */
+int sensors_do_chip_sets(const sensors_chip_name *name);
+
+/* This function returns all detected chips that match a given chip name,
+ one by one. If no chip name is provided, all detected chips are returned.
+ To start at the beginning of the list, use 0 for nr; NULL is returned if
+ we are at the end of the list. Do not try to change these chip names, as
+ they point to internal structures! */
+const sensors_chip_name *sensors_get_detected_chips(const sensors_chip_name
+ *match, int *nr);
+
+/* These defines are used in the flags field of sensors_subfeature */
+#define SENSORS_MODE_R 1
+#define SENSORS_MODE_W 2
+#define SENSORS_COMPUTE_MAPPING 4
+
+typedef enum sensors_feature_type {
+ SENSORS_FEATURE_IN = 0x00,
+ SENSORS_FEATURE_FAN = 0x01,
+ SENSORS_FEATURE_TEMP = 0x02,
+ SENSORS_FEATURE_POWER = 0x03,
+ SENSORS_FEATURE_ENERGY = 0x04,
+ SENSORS_FEATURE_CURR = 0x05,
+ SENSORS_FEATURE_HUMIDITY = 0x06,
+ SENSORS_FEATURE_MAX_MAIN,
+ SENSORS_FEATURE_VID = 0x10,
+ SENSORS_FEATURE_INTRUSION = 0x11,
+ SENSORS_FEATURE_MAX_OTHER,
+ SENSORS_FEATURE_BEEP_ENABLE = 0x18,
+ SENSORS_FEATURE_UNKNOWN = INT_MAX,
+} sensors_feature_type;
+
+/* All the sensor types (in, fan, temp, vid) are a multiple of 0x100 apart,
+ and sensor subfeatures which have no compute mapping have bit 7 set. */
+typedef enum sensors_subfeature_type {
+ SENSORS_SUBFEATURE_IN_INPUT = SENSORS_FEATURE_IN << 8,
+ SENSORS_SUBFEATURE_IN_MIN,
+ SENSORS_SUBFEATURE_IN_MAX,
+ SENSORS_SUBFEATURE_IN_LCRIT,
+ SENSORS_SUBFEATURE_IN_CRIT,
+ SENSORS_SUBFEATURE_IN_AVERAGE,
+ SENSORS_SUBFEATURE_IN_LOWEST,
+ SENSORS_SUBFEATURE_IN_HIGHEST,
+ SENSORS_SUBFEATURE_IN_ALARM = (SENSORS_FEATURE_IN << 8) | 0x80,
+ SENSORS_SUBFEATURE_IN_MIN_ALARM,
+ SENSORS_SUBFEATURE_IN_MAX_ALARM,
+ SENSORS_SUBFEATURE_IN_BEEP,
+ SENSORS_SUBFEATURE_IN_LCRIT_ALARM,
+ SENSORS_SUBFEATURE_IN_CRIT_ALARM,
+
+ SENSORS_SUBFEATURE_FAN_INPUT = SENSORS_FEATURE_FAN << 8,
+ SENSORS_SUBFEATURE_FAN_MIN,
+ SENSORS_SUBFEATURE_FAN_MAX,
+ SENSORS_SUBFEATURE_FAN_ALARM = (SENSORS_FEATURE_FAN << 8) | 0x80,
+ SENSORS_SUBFEATURE_FAN_FAULT,
+ SENSORS_SUBFEATURE_FAN_DIV,
+ SENSORS_SUBFEATURE_FAN_BEEP,
+ SENSORS_SUBFEATURE_FAN_PULSES,
+ SENSORS_SUBFEATURE_FAN_MIN_ALARM,
+ SENSORS_SUBFEATURE_FAN_MAX_ALARM,
+
+ SENSORS_SUBFEATURE_TEMP_INPUT = SENSORS_FEATURE_TEMP << 8,
+ SENSORS_SUBFEATURE_TEMP_MAX,
+ SENSORS_SUBFEATURE_TEMP_MAX_HYST,
+ SENSORS_SUBFEATURE_TEMP_MIN,
+ SENSORS_SUBFEATURE_TEMP_CRIT,
+ SENSORS_SUBFEATURE_TEMP_CRIT_HYST,
+ SENSORS_SUBFEATURE_TEMP_LCRIT,
+ SENSORS_SUBFEATURE_TEMP_EMERGENCY,
+ SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST,
+ SENSORS_SUBFEATURE_TEMP_LOWEST,
+ SENSORS_SUBFEATURE_TEMP_HIGHEST,
+ SENSORS_SUBFEATURE_TEMP_ALARM = (SENSORS_FEATURE_TEMP << 8) | 0x80,
+ SENSORS_SUBFEATURE_TEMP_MAX_ALARM,
+ SENSORS_SUBFEATURE_TEMP_MIN_ALARM,
+ SENSORS_SUBFEATURE_TEMP_CRIT_ALARM,
+ SENSORS_SUBFEATURE_TEMP_FAULT,
+ SENSORS_SUBFEATURE_TEMP_TYPE,
+ SENSORS_SUBFEATURE_TEMP_OFFSET,
+ SENSORS_SUBFEATURE_TEMP_BEEP,
+ SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM,
+ SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM,
+
+ SENSORS_SUBFEATURE_POWER_AVERAGE = SENSORS_FEATURE_POWER << 8,
+ SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST,
+ SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST,
+ SENSORS_SUBFEATURE_POWER_INPUT,
+ SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST,
+ SENSORS_SUBFEATURE_POWER_INPUT_LOWEST,
+ SENSORS_SUBFEATURE_POWER_CAP,
+ SENSORS_SUBFEATURE_POWER_CAP_HYST,
+ SENSORS_SUBFEATURE_POWER_MAX,
+ SENSORS_SUBFEATURE_POWER_CRIT,
+ SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL = (SENSORS_FEATURE_POWER << 8) | 0x80,
+ SENSORS_SUBFEATURE_POWER_ALARM,
+ SENSORS_SUBFEATURE_POWER_CAP_ALARM,
+ SENSORS_SUBFEATURE_POWER_MAX_ALARM,
+ SENSORS_SUBFEATURE_POWER_CRIT_ALARM,
+
+ SENSORS_SUBFEATURE_ENERGY_INPUT = SENSORS_FEATURE_ENERGY << 8,
+
+ SENSORS_SUBFEATURE_CURR_INPUT = SENSORS_FEATURE_CURR << 8,
+ SENSORS_SUBFEATURE_CURR_MIN,
+ SENSORS_SUBFEATURE_CURR_MAX,
+ SENSORS_SUBFEATURE_CURR_LCRIT,
+ SENSORS_SUBFEATURE_CURR_CRIT,
+ SENSORS_SUBFEATURE_CURR_AVERAGE,
+ SENSORS_SUBFEATURE_CURR_LOWEST,
+ SENSORS_SUBFEATURE_CURR_HIGHEST,
+ SENSORS_SUBFEATURE_CURR_ALARM = (SENSORS_FEATURE_CURR << 8) | 0x80,
+ SENSORS_SUBFEATURE_CURR_MIN_ALARM,
+ SENSORS_SUBFEATURE_CURR_MAX_ALARM,
+ SENSORS_SUBFEATURE_CURR_BEEP,
+ SENSORS_SUBFEATURE_CURR_LCRIT_ALARM,
+ SENSORS_SUBFEATURE_CURR_CRIT_ALARM,
+
+ SENSORS_SUBFEATURE_HUMIDITY_INPUT = SENSORS_FEATURE_HUMIDITY << 8,
+
+ SENSORS_SUBFEATURE_VID = SENSORS_FEATURE_VID << 8,
+
+ SENSORS_SUBFEATURE_INTRUSION_ALARM = SENSORS_FEATURE_INTRUSION << 8,
+ SENSORS_SUBFEATURE_INTRUSION_BEEP,
+
+ SENSORS_SUBFEATURE_BEEP_ENABLE = SENSORS_FEATURE_BEEP_ENABLE << 8,
+
+ SENSORS_SUBFEATURE_UNKNOWN = INT_MAX,
+} sensors_subfeature_type;
+
+/* Data about a single chip feature (or category leader) */
+struct sensors_feature {
+ char *name;
+ int number;
+ sensors_feature_type type;
+ /* Members below are for libsensors internal use only */
+ int first_subfeature;
+ int padding1;
+};
+
+/* Data about a single chip subfeature:
+ name is the string name used to refer to this subfeature (in config files)
+ number is the internal subfeature number, used in many functions to refer
+ to this subfeature
+ type is the subfeature type
+ mapping is the number of a main feature this subfeature belongs to
+ (for example subfeatures fan1_input, fan1_min, fan1_div and fan1_alarm
+ are mapped to main feature fan1)
+ flags is a bitfield, its value is a combination of SENSORS_MODE_R (readable),
+ SENSORS_MODE_W (writable) and SENSORS_COMPUTE_MAPPING (affected by the
+ computation rules of the main feature) */
+typedef struct sensors_subfeature {
+ char *name;
+ int number;
+ sensors_subfeature_type type;
+ int mapping;
+ unsigned int flags;
+} sensors_subfeature;
+
+/* This returns all main features of a specific chip. nr is an internally
+ used variable. Set it to zero to start at the begin of the list. If no
+ more features are found NULL is returned.
+ Do not try to change the returned structure; you will corrupt internal
+ data structures. */
+const sensors_feature *
+sensors_get_features(const sensors_chip_name *name, int *nr);
+
+/* This returns all subfeatures of a given main feature. nr is an internally
+ used variable. Set it to zero to start at the begin of the list. If no
+ more features are found NULL is returned.
+ Do not try to change the returned structure; you will corrupt internal
+ data structures. */
+const sensors_subfeature *
+sensors_get_all_subfeatures(const sensors_chip_name *name,
+ const sensors_feature *feature, int *nr);
+
+/* This returns the subfeature of the given type for a given main feature,
+ if it exists, NULL otherwise.
+ Do not try to change the returned structure; you will corrupt internal
+ data structures. */
+const sensors_subfeature *
+sensors_get_subfeature(const sensors_chip_name *name,
+ const sensors_feature *feature,
+ sensors_subfeature_type type);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* def LIB_SENSORS_ERROR_H */
diff --git a/tools/gator/daemon/libsensors/sysfs.c b/tools/gator/daemon/libsensors/sysfs.c
new file mode 100644
index 000000000000..2b494c9975b4
--- /dev/null
+++ b/tools/gator/daemon/libsensors/sysfs.c
@@ -0,0 +1,926 @@
+/*
+ sysfs.c - Part of libsensors, a library for reading Linux sensor data
+ Copyright (c) 2005 Mark M. Hoffman <mhoffman@lightlink.com>
+ Copyright (C) 2007-2010 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+/*** This file modified by ARM on Jan 23, 2013 to improve performance by substituting calls to fread() with calls to read() and to read non-scaled values. ***/
+
+/* this define needed for strndup() */
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include "data.h"
+#include "error.h"
+#include "access.h"
+#include "general.h"
+#include "sysfs.h"
+
+
+/****************************************************************************/
+
+#define ATTR_MAX 128
+#define SYSFS_MAGIC 0x62656572
+
+int sensors_sysfs_no_scaling;
+
+/*
+ * Read an attribute from sysfs
+ * Returns a pointer to a freshly allocated string; free it yourself.
+ * If the file doesn't exist or can't be read, NULL is returned.
+ */
+static char *sysfs_read_attr(const char *device, const char *attr)
+{
+ char path[NAME_MAX];
+ char buf[ATTR_MAX], *p;
+ FILE *f;
+
+ snprintf(path, NAME_MAX, "%s/%s", device, attr);
+
+ if (!(f = fopen(path, "r")))
+ return NULL;
+ p = fgets(buf, ATTR_MAX, f);
+ fclose(f);
+ if (!p)
+ return NULL;
+
+ /* Last byte is a '\n'; chop that off */
+ p = strndup(buf, strlen(buf) - 1);
+ if (!p)
+ sensors_fatal_error(__func__, "Out of memory");
+ return p;
+}
+
+/*
+ * Call an arbitrary function for each class device of the given class
+ * Returns 0 on success (all calls returned 0), a positive errno for
+ * local errors, or a negative error value if any call fails.
+ */
+static int sysfs_foreach_classdev(const char *class_name,
+ int (*func)(const char *, const char *))
+{
+ char path[NAME_MAX];
+ int path_off, ret;
+ DIR *dir;
+ struct dirent *ent;
+
+ path_off = snprintf(path, NAME_MAX, "%s/class/%s",
+ sensors_sysfs_mount, class_name);
+ if (!(dir = opendir(path)))
+ return errno;
+
+ ret = 0;
+ while (!ret && (ent = readdir(dir))) {
+ if (ent->d_name[0] == '.') /* skip hidden entries */
+ continue;
+
+ snprintf(path + path_off, NAME_MAX - path_off, "/%s",
+ ent->d_name);
+ ret = func(path, ent->d_name);
+ }
+
+ closedir(dir);
+ return ret;
+}
+
+/*
+ * Call an arbitrary function for each device of the given bus type
+ * Returns 0 on success (all calls returned 0), a positive errno for
+ * local errors, or a negative error value if any call fails.
+ */
+static int sysfs_foreach_busdev(const char *bus_type,
+ int (*func)(const char *, const char *))
+{
+ char path[NAME_MAX];
+ int path_off, ret;
+ DIR *dir;
+ struct dirent *ent;
+
+ path_off = snprintf(path, NAME_MAX, "%s/bus/%s/devices",
+ sensors_sysfs_mount, bus_type);
+ if (!(dir = opendir(path)))
+ return errno;
+
+ ret = 0;
+ while (!ret && (ent = readdir(dir))) {
+ if (ent->d_name[0] == '.') /* skip hidden entries */
+ continue;
+
+ snprintf(path + path_off, NAME_MAX - path_off, "/%s",
+ ent->d_name);
+ ret = func(path, ent->d_name);
+ }
+
+ closedir(dir);
+ return ret;
+}
+
+/****************************************************************************/
+
+char sensors_sysfs_mount[NAME_MAX];
+
+#define MAX_MAIN_SENSOR_TYPES (SENSORS_FEATURE_MAX_MAIN - SENSORS_FEATURE_IN)
+#define MAX_OTHER_SENSOR_TYPES (SENSORS_FEATURE_MAX_OTHER - SENSORS_FEATURE_VID)
+#define MAX_SENSORS_PER_TYPE 24
+/* max_subfeatures is now computed dynamically */
+#define FEATURE_SIZE (max_subfeatures * 2)
+#define FEATURE_TYPE_SIZE (MAX_SENSORS_PER_TYPE * FEATURE_SIZE)
+
+/*
+ * Room for all 7 main types (in, fan, temp, power, energy, current, humidity)
+ * and 2 other types (VID, intrusion) with all their subfeatures + misc features
+ */
+#define SUB_OFFSET_OTHER (MAX_MAIN_SENSOR_TYPES * FEATURE_TYPE_SIZE)
+#define SUB_OFFSET_MISC (SUB_OFFSET_OTHER + \
+ MAX_OTHER_SENSOR_TYPES * FEATURE_TYPE_SIZE)
+#define ALL_POSSIBLE_SUBFEATURES (SUB_OFFSET_MISC + 1)
+
+static
+int get_type_scaling(sensors_subfeature_type type)
+{
+ /* Multipliers for subfeatures */
+ switch (type & 0xFF80) {
+ case SENSORS_SUBFEATURE_IN_INPUT:
+ case SENSORS_SUBFEATURE_TEMP_INPUT:
+ case SENSORS_SUBFEATURE_CURR_INPUT:
+ case SENSORS_SUBFEATURE_HUMIDITY_INPUT:
+ return 1000;
+ case SENSORS_SUBFEATURE_FAN_INPUT:
+ return 1;
+ case SENSORS_SUBFEATURE_POWER_AVERAGE:
+ case SENSORS_SUBFEATURE_ENERGY_INPUT:
+ return 1000000;
+ }
+
+ /* Multipliers for second class subfeatures
+ that need their own multiplier */
+ switch (type) {
+ case SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL:
+ case SENSORS_SUBFEATURE_VID:
+ case SENSORS_SUBFEATURE_TEMP_OFFSET:
+ return 1000;
+ default:
+ return 1;
+ }
+}
+
+static
+char *get_feature_name(sensors_feature_type ftype, char *sfname)
+{
+ char *name, *underscore;
+
+ switch (ftype) {
+ case SENSORS_FEATURE_IN:
+ case SENSORS_FEATURE_FAN:
+ case SENSORS_FEATURE_TEMP:
+ case SENSORS_FEATURE_POWER:
+ case SENSORS_FEATURE_ENERGY:
+ case SENSORS_FEATURE_CURR:
+ case SENSORS_FEATURE_HUMIDITY:
+ case SENSORS_FEATURE_INTRUSION:
+ underscore = strchr(sfname, '_');
+ name = strndup(sfname, underscore - sfname);
+ if (!name)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ break;
+ default:
+ name = strdup(sfname);
+ if (!name)
+ sensors_fatal_error(__func__, "Out of memory");
+ }
+
+ return name;
+}
+
+/* Static mappings for use by sensors_subfeature_get_type() */
+struct subfeature_type_match
+{
+ const char *name;
+ sensors_subfeature_type type;
+};
+
+struct feature_type_match
+{
+ const char *name;
+ const struct subfeature_type_match *submatches;
+};
+
+static const struct subfeature_type_match temp_matches[] = {
+ { "input", SENSORS_SUBFEATURE_TEMP_INPUT },
+ { "max", SENSORS_SUBFEATURE_TEMP_MAX },
+ { "max_hyst", SENSORS_SUBFEATURE_TEMP_MAX_HYST },
+ { "min", SENSORS_SUBFEATURE_TEMP_MIN },
+ { "crit", SENSORS_SUBFEATURE_TEMP_CRIT },
+ { "crit_hyst", SENSORS_SUBFEATURE_TEMP_CRIT_HYST },
+ { "lcrit", SENSORS_SUBFEATURE_TEMP_LCRIT },
+ { "emergency", SENSORS_SUBFEATURE_TEMP_EMERGENCY },
+ { "emergency_hyst", SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST },
+ { "lowest", SENSORS_SUBFEATURE_TEMP_LOWEST },
+ { "highest", SENSORS_SUBFEATURE_TEMP_HIGHEST },
+ { "alarm", SENSORS_SUBFEATURE_TEMP_ALARM },
+ { "min_alarm", SENSORS_SUBFEATURE_TEMP_MIN_ALARM },
+ { "max_alarm", SENSORS_SUBFEATURE_TEMP_MAX_ALARM },
+ { "crit_alarm", SENSORS_SUBFEATURE_TEMP_CRIT_ALARM },
+ { "emergency_alarm", SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM },
+ { "lcrit_alarm", SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM },
+ { "fault", SENSORS_SUBFEATURE_TEMP_FAULT },
+ { "type", SENSORS_SUBFEATURE_TEMP_TYPE },
+ { "offset", SENSORS_SUBFEATURE_TEMP_OFFSET },
+ { "beep", SENSORS_SUBFEATURE_TEMP_BEEP },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match in_matches[] = {
+ { "input", SENSORS_SUBFEATURE_IN_INPUT },
+ { "min", SENSORS_SUBFEATURE_IN_MIN },
+ { "max", SENSORS_SUBFEATURE_IN_MAX },
+ { "lcrit", SENSORS_SUBFEATURE_IN_LCRIT },
+ { "crit", SENSORS_SUBFEATURE_IN_CRIT },
+ { "average", SENSORS_SUBFEATURE_IN_AVERAGE },
+ { "lowest", SENSORS_SUBFEATURE_IN_LOWEST },
+ { "highest", SENSORS_SUBFEATURE_IN_HIGHEST },
+ { "alarm", SENSORS_SUBFEATURE_IN_ALARM },
+ { "min_alarm", SENSORS_SUBFEATURE_IN_MIN_ALARM },
+ { "max_alarm", SENSORS_SUBFEATURE_IN_MAX_ALARM },
+ { "lcrit_alarm", SENSORS_SUBFEATURE_IN_LCRIT_ALARM },
+ { "crit_alarm", SENSORS_SUBFEATURE_IN_CRIT_ALARM },
+ { "beep", SENSORS_SUBFEATURE_IN_BEEP },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match fan_matches[] = {
+ { "input", SENSORS_SUBFEATURE_FAN_INPUT },
+ { "min", SENSORS_SUBFEATURE_FAN_MIN },
+ { "max", SENSORS_SUBFEATURE_FAN_MAX },
+ { "div", SENSORS_SUBFEATURE_FAN_DIV },
+ { "pulses", SENSORS_SUBFEATURE_FAN_PULSES },
+ { "alarm", SENSORS_SUBFEATURE_FAN_ALARM },
+ { "min_alarm", SENSORS_SUBFEATURE_FAN_MIN_ALARM },
+ { "max_alarm", SENSORS_SUBFEATURE_FAN_MAX_ALARM },
+ { "fault", SENSORS_SUBFEATURE_FAN_FAULT },
+ { "beep", SENSORS_SUBFEATURE_FAN_BEEP },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match power_matches[] = {
+ { "average", SENSORS_SUBFEATURE_POWER_AVERAGE },
+ { "average_highest", SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST },
+ { "average_lowest", SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST },
+ { "input", SENSORS_SUBFEATURE_POWER_INPUT },
+ { "input_highest", SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST },
+ { "input_lowest", SENSORS_SUBFEATURE_POWER_INPUT_LOWEST },
+ { "cap", SENSORS_SUBFEATURE_POWER_CAP },
+ { "cap_hyst", SENSORS_SUBFEATURE_POWER_CAP_HYST },
+ { "cap_alarm", SENSORS_SUBFEATURE_POWER_CAP_ALARM },
+ { "alarm", SENSORS_SUBFEATURE_POWER_ALARM },
+ { "max", SENSORS_SUBFEATURE_POWER_MAX },
+ { "max_alarm", SENSORS_SUBFEATURE_POWER_MAX_ALARM },
+ { "crit", SENSORS_SUBFEATURE_POWER_CRIT },
+ { "crit_alarm", SENSORS_SUBFEATURE_POWER_CRIT_ALARM },
+ { "average_interval", SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match energy_matches[] = {
+ { "input", SENSORS_SUBFEATURE_ENERGY_INPUT },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match curr_matches[] = {
+ { "input", SENSORS_SUBFEATURE_CURR_INPUT },
+ { "min", SENSORS_SUBFEATURE_CURR_MIN },
+ { "max", SENSORS_SUBFEATURE_CURR_MAX },
+ { "lcrit", SENSORS_SUBFEATURE_CURR_LCRIT },
+ { "crit", SENSORS_SUBFEATURE_CURR_CRIT },
+ { "average", SENSORS_SUBFEATURE_CURR_AVERAGE },
+ { "lowest", SENSORS_SUBFEATURE_CURR_LOWEST },
+ { "highest", SENSORS_SUBFEATURE_CURR_HIGHEST },
+ { "alarm", SENSORS_SUBFEATURE_CURR_ALARM },
+ { "min_alarm", SENSORS_SUBFEATURE_CURR_MIN_ALARM },
+ { "max_alarm", SENSORS_SUBFEATURE_CURR_MAX_ALARM },
+ { "lcrit_alarm", SENSORS_SUBFEATURE_CURR_LCRIT_ALARM },
+ { "crit_alarm", SENSORS_SUBFEATURE_CURR_CRIT_ALARM },
+ { "beep", SENSORS_SUBFEATURE_CURR_BEEP },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match humidity_matches[] = {
+ { "input", SENSORS_SUBFEATURE_HUMIDITY_INPUT },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match cpu_matches[] = {
+ { "vid", SENSORS_SUBFEATURE_VID },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match intrusion_matches[] = {
+ { "alarm", SENSORS_SUBFEATURE_INTRUSION_ALARM },
+ { "beep", SENSORS_SUBFEATURE_INTRUSION_BEEP },
+ { NULL, 0 }
+};
+static struct feature_type_match matches[] = {
+ { "temp%d%c", temp_matches },
+ { "in%d%c", in_matches },
+ { "fan%d%c", fan_matches },
+ { "cpu%d%c", cpu_matches },
+ { "power%d%c", power_matches },
+ { "curr%d%c", curr_matches },
+ { "energy%d%c", energy_matches },
+ { "intrusion%d%c", intrusion_matches },
+ { "humidity%d%c", humidity_matches },
+};
+
+/* Return the subfeature type and channel number based on the subfeature
+ name */
+static
+sensors_subfeature_type sensors_subfeature_get_type(const char *name, int *nr)
+{
+ char c;
+ int i, count;
+ const struct subfeature_type_match *submatches;
+
+ /* Special case */
+ if (!strcmp(name, "beep_enable")) {
+ *nr = 0;
+ return SENSORS_SUBFEATURE_BEEP_ENABLE;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(matches); i++)
+ if ((count = sscanf(name, matches[i].name, nr, &c)))
+ break;
+
+ if (i == ARRAY_SIZE(matches) || count != 2 || c != '_')
+ return SENSORS_SUBFEATURE_UNKNOWN; /* no match */
+
+ submatches = matches[i].submatches;
+ name = strchr(name + 3, '_') + 1;
+ for (i = 0; submatches[i].name != NULL; i++)
+ if (!strcmp(name, submatches[i].name))
+ return submatches[i].type;
+
+ return SENSORS_SUBFEATURE_UNKNOWN;
+}
+
+static int sensors_compute_max(void)
+{
+ int i, j, max, offset;
+ const struct subfeature_type_match *submatches;
+ sensors_feature_type ftype;
+
+ max = 0;
+ for (i = 0; i < ARRAY_SIZE(matches); i++) {
+ submatches = matches[i].submatches;
+ for (j = 0; submatches[j].name != NULL; j++) {
+ ftype = submatches[j].type >> 8;
+
+ if (ftype < SENSORS_FEATURE_VID) {
+ offset = submatches[j].type & 0x7F;
+ if (offset >= max)
+ max = offset + 1;
+ } else {
+ offset = submatches[j].type & 0xFF;
+ if (offset >= max * 2)
+ max = ((offset + 1) + 1) / 2;
+ }
+ }
+ }
+
+ return max;
+}
+
+static int sensors_get_attr_mode(const char *device, const char *attr)
+{
+ char path[NAME_MAX];
+ struct stat st;
+ int mode = 0;
+
+ snprintf(path, NAME_MAX, "%s/%s", device, attr);
+ if (!stat(path, &st)) {
+ if (st.st_mode & S_IRUSR)
+ mode |= SENSORS_MODE_R;
+ if (st.st_mode & S_IWUSR)
+ mode |= SENSORS_MODE_W;
+ }
+ return mode;
+}
+
+static int sensors_read_dynamic_chip(sensors_chip_features *chip,
+ const char *dev_path)
+{
+ int i, fnum = 0, sfnum = 0, prev_slot;
+ static int max_subfeatures;
+ DIR *dir;
+ struct dirent *ent;
+ sensors_subfeature *all_subfeatures;
+ sensors_subfeature *dyn_subfeatures;
+ sensors_feature *dyn_features;
+ sensors_feature_type ftype;
+ sensors_subfeature_type sftype;
+
+ if (!(dir = opendir(dev_path)))
+ return -errno;
+
+ /* Dynamically figure out the max number of subfeatures */
+ if (!max_subfeatures)
+ max_subfeatures = sensors_compute_max();
+
+ /* We use a large sparse table at first to store all found
+ subfeatures, so that we can store them sorted at type and index
+ and then later create a dense sorted table. */
+ all_subfeatures = calloc(ALL_POSSIBLE_SUBFEATURES,
+ sizeof(sensors_subfeature));
+ if (!all_subfeatures)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ while ((ent = readdir(dir))) {
+ char *name;
+ int nr;
+
+ /* Skip directories and symlinks */
+ if (ent->d_type != DT_REG)
+ continue;
+
+ name = ent->d_name;
+
+ sftype = sensors_subfeature_get_type(name, &nr);
+ if (sftype == SENSORS_SUBFEATURE_UNKNOWN)
+ continue;
+ ftype = sftype >> 8;
+
+ /* Adjust the channel number */
+ switch (ftype) {
+ case SENSORS_FEATURE_FAN:
+ case SENSORS_FEATURE_TEMP:
+ case SENSORS_FEATURE_POWER:
+ case SENSORS_FEATURE_ENERGY:
+ case SENSORS_FEATURE_CURR:
+ case SENSORS_FEATURE_HUMIDITY:
+ nr--;
+ break;
+ default:
+ break;
+ }
+
+ if (nr < 0 || nr >= MAX_SENSORS_PER_TYPE) {
+ /* More sensors of one type than MAX_SENSORS_PER_TYPE,
+ we have to ignore it */
+#ifdef DEBUG
+ sensors_fatal_error(__func__,
+ "Increase MAX_SENSORS_PER_TYPE!");
+#endif
+ continue;
+ }
+
+ /* "calculate" a place to store the subfeature in our sparse,
+ sorted table */
+ switch (ftype) {
+ case SENSORS_FEATURE_VID:
+ case SENSORS_FEATURE_INTRUSION:
+ i = SUB_OFFSET_OTHER +
+ (ftype - SENSORS_FEATURE_VID) * FEATURE_TYPE_SIZE +
+ nr * FEATURE_SIZE + (sftype & 0xFF);
+ break;
+ case SENSORS_FEATURE_BEEP_ENABLE:
+ i = SUB_OFFSET_MISC +
+ (ftype - SENSORS_FEATURE_BEEP_ENABLE);
+ break;
+ default:
+ i = ftype * FEATURE_TYPE_SIZE +
+ nr * FEATURE_SIZE +
+ ((sftype & 0x80) >> 7) * max_subfeatures +
+ (sftype & 0x7F);
+ }
+
+ if (all_subfeatures[i].name) {
+#ifdef DEBUG
+ sensors_fatal_error(__func__, "Duplicate subfeature");
+#endif
+ continue;
+ }
+
+ /* fill in the subfeature members */
+ all_subfeatures[i].type = sftype;
+ all_subfeatures[i].name = strdup(name);
+ if (!all_subfeatures[i].name)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ /* Other and misc subfeatures are never scaled */
+ if (sftype < SENSORS_SUBFEATURE_VID && !(sftype & 0x80))
+ all_subfeatures[i].flags |= SENSORS_COMPUTE_MAPPING;
+ all_subfeatures[i].flags |= sensors_get_attr_mode(dev_path, name);
+
+ sfnum++;
+ }
+ closedir(dir);
+
+ if (!sfnum) { /* No subfeature */
+ chip->subfeature = NULL;
+ goto exit_free;
+ }
+
+ /* How many main features? */
+ prev_slot = -1;
+ for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
+ if (!all_subfeatures[i].name)
+ continue;
+
+ if (i >= SUB_OFFSET_MISC || i / FEATURE_SIZE != prev_slot) {
+ fnum++;
+ prev_slot = i / FEATURE_SIZE;
+ }
+ }
+
+ dyn_subfeatures = calloc(sfnum, sizeof(sensors_subfeature));
+ dyn_features = calloc(fnum, sizeof(sensors_feature));
+ if (!dyn_subfeatures || !dyn_features)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ /* Copy from the sparse array to the compact array */
+ sfnum = 0;
+ fnum = -1;
+ prev_slot = -1;
+ for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
+ if (!all_subfeatures[i].name)
+ continue;
+
+ /* New main feature? */
+ if (i >= SUB_OFFSET_MISC || i / FEATURE_SIZE != prev_slot) {
+ ftype = all_subfeatures[i].type >> 8;
+ fnum++;
+ prev_slot = i / FEATURE_SIZE;
+
+ dyn_features[fnum].name = get_feature_name(ftype,
+ all_subfeatures[i].name);
+ dyn_features[fnum].number = fnum;
+ dyn_features[fnum].first_subfeature = sfnum;
+ dyn_features[fnum].type = ftype;
+ }
+
+ dyn_subfeatures[sfnum] = all_subfeatures[i];
+ dyn_subfeatures[sfnum].number = sfnum;
+ /* Back to the feature */
+ dyn_subfeatures[sfnum].mapping = fnum;
+
+ sfnum++;
+ }
+
+ chip->subfeature = dyn_subfeatures;
+ chip->subfeature_count = sfnum;
+ chip->feature = dyn_features;
+ chip->feature_count = ++fnum;
+
+exit_free:
+ free(all_subfeatures);
+ return 0;
+}
+
+/* returns !0 if sysfs filesystem was found, 0 otherwise */
+int sensors_init_sysfs(void)
+{
+ struct statfs statfsbuf;
+
+ snprintf(sensors_sysfs_mount, NAME_MAX, "%s", "/sys");
+ if (statfs(sensors_sysfs_mount, &statfsbuf) < 0
+ || statfsbuf.f_type != SYSFS_MAGIC)
+ return 0;
+
+ return 1;
+}
+
+/* returns: number of devices added (0 or 1) if successful, <0 otherwise */
+static int sensors_read_one_sysfs_chip(const char *dev_path,
+ const char *dev_name,
+ const char *hwmon_path)
+{
+ int domain, bus, slot, fn, vendor, product, id;
+ int err = -SENSORS_ERR_KERNEL;
+ char *bus_attr;
+ char bus_path[NAME_MAX];
+ char linkpath[NAME_MAX];
+ char subsys_path[NAME_MAX], *subsys;
+ int sub_len;
+ sensors_chip_features entry;
+
+ /* ignore any device without name attribute */
+ if (!(entry.chip.prefix = sysfs_read_attr(hwmon_path, "name")))
+ return 0;
+
+ entry.chip.path = strdup(hwmon_path);
+ if (!entry.chip.path)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ if (dev_path == NULL) {
+ /* Virtual device */
+ entry.chip.bus.type = SENSORS_BUS_TYPE_VIRTUAL;
+ entry.chip.bus.nr = 0;
+ /* For now we assume that virtual devices are unique */
+ entry.chip.addr = 0;
+ goto done;
+ }
+
+ /* Find bus type */
+ snprintf(linkpath, NAME_MAX, "%s/subsystem", dev_path);
+ sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
+ if (sub_len < 0 && errno == ENOENT) {
+ /* Fallback to "bus" link for kernels <= 2.6.17 */
+ snprintf(linkpath, NAME_MAX, "%s/bus", dev_path);
+ sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
+ }
+ if (sub_len < 0) {
+ /* Older kernels (<= 2.6.11) have neither the subsystem
+ symlink nor the bus symlink */
+ if (errno == ENOENT)
+ subsys = NULL;
+ else
+ goto exit_free;
+ } else {
+ subsys_path[sub_len] = '\0';
+ subsys = strrchr(subsys_path, '/') + 1;
+ }
+
+ if ((!subsys || !strcmp(subsys, "i2c")) &&
+ sscanf(dev_name, "%hd-%x", &entry.chip.bus.nr,
+ &entry.chip.addr) == 2) {
+ /* find out if legacy ISA or not */
+ if (entry.chip.bus.nr == 9191) {
+ entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
+ entry.chip.bus.nr = 0;
+ } else {
+ entry.chip.bus.type = SENSORS_BUS_TYPE_I2C;
+ snprintf(bus_path, sizeof(bus_path),
+ "%s/class/i2c-adapter/i2c-%d/device",
+ sensors_sysfs_mount, entry.chip.bus.nr);
+
+ if ((bus_attr = sysfs_read_attr(bus_path, "name"))) {
+ if (!strncmp(bus_attr, "ISA ", 4)) {
+ entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
+ entry.chip.bus.nr = 0;
+ }
+
+ free(bus_attr);
+ }
+ }
+ } else
+ if ((!subsys || !strcmp(subsys, "spi")) &&
+ sscanf(dev_name, "spi%hd.%d", &entry.chip.bus.nr,
+ &entry.chip.addr) == 2) {
+ /* SPI */
+ entry.chip.bus.type = SENSORS_BUS_TYPE_SPI;
+ } else
+ if ((!subsys || !strcmp(subsys, "pci")) &&
+ sscanf(dev_name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) {
+ /* PCI */
+ entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn;
+ entry.chip.bus.type = SENSORS_BUS_TYPE_PCI;
+ entry.chip.bus.nr = 0;
+ } else
+ if ((!subsys || !strcmp(subsys, "platform") ||
+ !strcmp(subsys, "of_platform"))) {
+ /* must be new ISA (platform driver) */
+ if (sscanf(dev_name, "%*[a-z0-9_].%d", &entry.chip.addr) != 1)
+ entry.chip.addr = 0;
+ entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
+ entry.chip.bus.nr = 0;
+ } else if (subsys && !strcmp(subsys, "acpi")) {
+ entry.chip.bus.type = SENSORS_BUS_TYPE_ACPI;
+ /* For now we assume that acpi devices are unique */
+ entry.chip.bus.nr = 0;
+ entry.chip.addr = 0;
+ } else
+ if (subsys && !strcmp(subsys, "hid") &&
+ sscanf(dev_name, "%x:%x:%x.%x", &bus, &vendor, &product, &id) == 4) {
+ entry.chip.bus.type = SENSORS_BUS_TYPE_HID;
+ /* As of kernel 2.6.32, the hid device names don't look good */
+ entry.chip.bus.nr = bus;
+ entry.chip.addr = id;
+ } else {
+ /* Ignore unknown device */
+ err = 0;
+ goto exit_free;
+ }
+
+done:
+ if (sensors_read_dynamic_chip(&entry, hwmon_path) < 0)
+ goto exit_free;
+ if (!entry.subfeature) { /* No subfeature, discard chip */
+ err = 0;
+ goto exit_free;
+ }
+ sensors_add_proc_chips(&entry);
+
+ return 1;
+
+exit_free:
+ free(entry.chip.prefix);
+ free(entry.chip.path);
+ return err;
+}
+
+static int sensors_add_hwmon_device_compat(const char *path,
+ const char *dev_name)
+{
+ int err;
+
+ err = sensors_read_one_sysfs_chip(path, dev_name, path);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* returns 0 if successful, !0 otherwise */
+static int sensors_read_sysfs_chips_compat(void)
+{
+ int ret;
+
+ ret = sysfs_foreach_busdev("i2c", sensors_add_hwmon_device_compat);
+ if (ret && ret != ENOENT)
+ return -SENSORS_ERR_KERNEL;
+
+ return 0;
+}
+
+static int sensors_add_hwmon_device(const char *path, const char *classdev)
+{
+ char linkpath[NAME_MAX];
+ char device[NAME_MAX], *device_p;
+ int dev_len, err;
+ (void)classdev; /* hide warning */
+
+ snprintf(linkpath, NAME_MAX, "%s/device", path);
+ dev_len = readlink(linkpath, device, NAME_MAX - 1);
+ if (dev_len < 0) {
+ /* No device link? Treat as virtual */
+ err = sensors_read_one_sysfs_chip(NULL, NULL, path);
+ } else {
+ device[dev_len] = '\0';
+ device_p = strrchr(device, '/') + 1;
+
+ /* The attributes we want might be those of the hwmon class
+ device, or those of the device itself. */
+ err = sensors_read_one_sysfs_chip(linkpath, device_p, path);
+ if (err == 0)
+ err = sensors_read_one_sysfs_chip(linkpath, device_p,
+ linkpath);
+ }
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* returns 0 if successful, !0 otherwise */
+int sensors_read_sysfs_chips(void)
+{
+ int ret;
+
+ ret = sysfs_foreach_classdev("hwmon", sensors_add_hwmon_device);
+ if (ret == ENOENT) {
+ /* compatibility function for kernel 2.6.n where n <= 13 */
+ return sensors_read_sysfs_chips_compat();
+ }
+
+ if (ret > 0)
+ ret = -SENSORS_ERR_KERNEL;
+ return ret;
+}
+
+/* returns 0 if successful, !0 otherwise */
+static int sensors_add_i2c_bus(const char *path, const char *classdev)
+{
+ sensors_bus entry;
+
+ if (sscanf(classdev, "i2c-%hd", &entry.bus.nr) != 1 ||
+ entry.bus.nr == 9191) /* legacy ISA */
+ return 0;
+ entry.bus.type = SENSORS_BUS_TYPE_I2C;
+
+ /* Get the adapter name from the classdev "name" attribute
+ * (Linux 2.6.20 and later). If it fails, fall back to
+ * the device "name" attribute (for older kernels). */
+ entry.adapter = sysfs_read_attr(path, "name");
+ if (!entry.adapter)
+ entry.adapter = sysfs_read_attr(path, "device/name");
+ if (entry.adapter)
+ sensors_add_proc_bus(&entry);
+
+ return 0;
+}
+
+/* returns 0 if successful, !0 otherwise */
+int sensors_read_sysfs_bus(void)
+{
+ int ret;
+
+ ret = sysfs_foreach_classdev("i2c-adapter", sensors_add_i2c_bus);
+ if (ret == ENOENT)
+ ret = sysfs_foreach_busdev("i2c", sensors_add_i2c_bus);
+ if (ret && ret != ENOENT)
+ return -SENSORS_ERR_KERNEL;
+
+ return 0;
+}
+
+int sensors_read_sysfs_attr(const sensors_chip_name *name,
+ const sensors_subfeature *subfeature,
+ double *value)
+{
+ char n[NAME_MAX];
+ int f;
+
+ snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
+ if ((f = open(n, O_RDONLY)) != -1) {
+ int res, err = 0;
+ char buf[512];
+ int count;
+
+ errno = 0;
+ if ((count = read(f, buf, sizeof(buf) - 1)) == -1) {
+ if (errno == EIO)
+ err = -SENSORS_ERR_IO;
+ else
+ err = -SENSORS_ERR_ACCESS_R;
+ } else {
+ buf[count] = '\0';
+ errno = 0;
+ res = sscanf(buf, "%lf", value);
+ if (res == EOF && errno == EIO)
+ err = -SENSORS_ERR_IO;
+ else if (res != 1)
+ err = -SENSORS_ERR_ACCESS_R;
+ }
+ res = close(f);
+ if (err)
+ return err;
+
+ if (res != 0) {
+ if (errno == EIO)
+ return -SENSORS_ERR_IO;
+ else
+ return -SENSORS_ERR_ACCESS_R;
+ }
+ if (!sensors_sysfs_no_scaling)
+ *value /= get_type_scaling(subfeature->type);
+ } else
+ return -SENSORS_ERR_KERNEL;
+
+ return 0;
+}
+
+int sensors_write_sysfs_attr(const sensors_chip_name *name,
+ const sensors_subfeature *subfeature,
+ double value)
+{
+ char n[NAME_MAX];
+ FILE *f;
+
+ snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
+ if ((f = fopen(n, "w"))) {
+ int res, err = 0;
+
+ if (!sensors_sysfs_no_scaling)
+ value *= get_type_scaling(subfeature->type);
+ res = fprintf(f, "%d", (int) value);
+ if (res == -EIO)
+ err = -SENSORS_ERR_IO;
+ else if (res < 0)
+ err = -SENSORS_ERR_ACCESS_W;
+ res = fclose(f);
+ if (err)
+ return err;
+
+ if (res == EOF) {
+ if (errno == EIO)
+ return -SENSORS_ERR_IO;
+ else
+ return -SENSORS_ERR_ACCESS_W;
+ }
+ } else
+ return -SENSORS_ERR_KERNEL;
+
+ return 0;
+}
diff --git a/tools/gator/daemon/libsensors/sysfs.h b/tools/gator/daemon/libsensors/sysfs.h
new file mode 100644
index 000000000000..38584afd028e
--- /dev/null
+++ b/tools/gator/daemon/libsensors/sysfs.h
@@ -0,0 +1,43 @@
+/*
+ sysfs.h - part of libsensors, a library for reading Linux sensor data
+ Copyright (C) Mark M. Hoffman <mhoffman@lightlink.com>
+ Copyright (C) 2007 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef SENSORS_LIB_SYSFS_H
+#define SENSORS_LIB_SYSFS_H
+
+extern char sensors_sysfs_mount[];
+
+int sensors_init_sysfs(void);
+
+int sensors_read_sysfs_chips(void);
+
+int sensors_read_sysfs_bus(void);
+
+/* Read a value out of a sysfs attribute file */
+int sensors_read_sysfs_attr(const sensors_chip_name *name,
+ const sensors_subfeature *subfeature,
+ double *value);
+
+/* Write a value to a sysfs attribute file */
+int sensors_write_sysfs_attr(const sensors_chip_name *name,
+ const sensors_subfeature *subfeature,
+ double value);
+
+#endif /* !SENSORS_LIB_SYSFS_H */
diff --git a/tools/gator/daemon/libsensors/version.h b/tools/gator/daemon/libsensors/version.h
new file mode 100644
index 000000000000..76ceb08c29d0
--- /dev/null
+++ b/tools/gator/daemon/libsensors/version.h
@@ -0,0 +1 @@
+#define LM_VERSION "3.3.2"
diff --git a/tools/gator/daemon/main.cpp b/tools/gator/daemon/main.cpp
new file mode 100644
index 000000000000..d1b0913aa78f
--- /dev/null
+++ b/tools/gator/daemon/main.cpp
@@ -0,0 +1,480 @@
+/**
+ * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include "Child.h"
+#include "SessionData.h"
+#include "OlySocket.h"
+#include "Logging.h"
+#include "OlyUtility.h"
+#include "KMod.h"
+
+#define DEBUG false
+
+extern Child* child;
+static int shutdownFilesystem();
+static pthread_mutex_t numSessions_mutex;
+static int numSessions = 0;
+static OlySocket* sock = NULL;
+static bool driverRunningAtStart = false;
+static bool driverMountedAtStart = false;
+
+struct cmdline_t {
+ int port;
+ char* module;
+};
+
+#define DEFAULT_PORT 8080
+
+void cleanUp() {
+ if (shutdownFilesystem() == -1) {
+ logg->logMessage("Error shutting down gator filesystem");
+ }
+ delete sock;
+ delete util;
+ delete logg;
+}
+
+// CTRL C Signal Handler
+static void handler(int signum) {
+ logg->logMessage("Received signal %d, gator daemon exiting", signum);
+
+ // Case 1: both child and parent receive the signal
+ if (numSessions > 0) {
+ // Arbitrary sleep of 1 second to give time for the child to exit;
+ // if something bad happens, continue the shutdown process regardless
+ sleep(1);
+ }
+
+ // Case 2: only the parent received the signal
+ if (numSessions > 0) {
+ // Kill child threads - the first signal exits gracefully
+ logg->logMessage("Killing process group as %d child was running when signal was received", numSessions);
+ kill(0, SIGINT);
+
+ // Give time for the child to exit
+ sleep(1);
+
+ if (numSessions > 0) {
+ // The second signal force kills the child
+ logg->logMessage("Force kill the child");
+ kill(0, SIGINT);
+ // Again, sleep for 1 second
+ sleep(1);
+
+ if (numSessions > 0) {
+ // Something bad has really happened; the child is not exiting and therefore may hold the /dev/gator resource open
+ printf("Unable to kill the gatord child process, thus gator.ko may still be loaded.\n");
+ }
+ }
+ }
+
+ cleanUp();
+ exit(0);
+}
+
+// Child exit Signal Handler
+static void child_exit(int signum) {
+ int status;
+ int pid = wait(&status);
+ if (pid != -1) {
+ pthread_mutex_lock(&numSessions_mutex);
+ numSessions--;
+ pthread_mutex_unlock(&numSessions_mutex);
+ logg->logMessage("Child process %d exited with status %d", pid, status);
+ }
+}
+
+static int udpPort(int port) {
+ int s;
+ struct sockaddr_in sockaddr;
+ int on;
+
+ s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (s == -1) {
+ logg->logError(__FILE__, __LINE__, "socket failed");
+ handleException();
+ }
+
+ on = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) {
+ logg->logError(__FILE__, __LINE__, "setsockopt failed");
+ handleException();
+ }
+
+ memset((void*)&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(port);
+ sockaddr.sin_addr.s_addr = INADDR_ANY;
+ if (bind(s, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) {
+ logg->logError(__FILE__, __LINE__, "socket failed");
+ handleException();
+ }
+
+ return s;
+}
+
+#define UDP_ANS_PORT 30000
+#define UDP_REQ_PORT 30001
+
+typedef struct {
+ char rviHeader[8];
+ uint32_t messageID;
+ uint8_t ethernetAddress[8];
+ uint32_t ethernetType;
+ uint32_t dhcp;
+ char dhcpName[40];
+ uint32_t ipAddress;
+ uint32_t defaultGateway;
+ uint32_t subnetMask;
+ uint32_t activeConnections;
+} RVIConfigureInfo;
+
+static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 };
+
+static void* answerThread(void* pVoid) {
+ const struct cmdline_t * const cmdline = (struct cmdline_t *)pVoid;
+ RVIConfigureInfo dstAns;
+ int req = udpPort(UDP_REQ_PORT);
+ int ans = udpPort(UDP_ANS_PORT);
+
+ // Format the answer buffer
+ memset(&dstAns, 0, sizeof(dstAns));
+ memcpy(dstAns.rviHeader, "STR_ANS ", sizeof(dstAns.rviHeader));
+ if (gethostname(dstAns.dhcpName, sizeof(dstAns.dhcpName) - 1) != 0) {
+ logg->logError(__FILE__, __LINE__, "gethostname failed");
+ handleException();
+ }
+ // Subvert the defaultGateway field for the port number
+ if (cmdline->port != DEFAULT_PORT) {
+ dstAns.defaultGateway = cmdline->port;
+ }
+ // Subvert the subnetMask field for the protocol version
+ dstAns.subnetMask = PROTOCOL_VERSION;
+
+ for (;;) {
+ char buf[128];
+ struct sockaddr_in sockaddr;
+ socklen_t addrlen;
+ int read;
+ addrlen = sizeof(sockaddr);
+ read = recvfrom(req, &buf, sizeof(buf), 0, (struct sockaddr *)&sockaddr, &addrlen);
+ if (read < 0) {
+ logg->logError(__FILE__, __LINE__, "recvfrom failed");
+ handleException();
+ } else if ((read == 12) && (memcmp(buf, DST_REQ, sizeof(DST_REQ)) == 0)) {
+ if (sendto(ans, &dstAns, sizeof(dstAns), 0, (struct sockaddr *)&sockaddr, addrlen) != sizeof(dstAns)) {
+ logg->logError(__FILE__, __LINE__, "sendto failed");
+ handleException();
+ }
+ }
+ }
+}
+
+// retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted
+static int mountGatorFS() {
+ // If already mounted,
+ if (access("/dev/gator/buffer", F_OK) == 0) {
+ return 0;
+ }
+
+ // else, mount the filesystem
+ mkdir("/dev/gator", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ if (mount("nodev", "/dev/gator", "gatorfs", 0, NULL) != 0) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+static bool init_module (const char * const location) {
+ bool ret(false);
+ const int fd = open(location, O_RDONLY);
+ if (fd >= 0) {
+ struct stat st;
+ if (fstat(fd, &st) == 0) {
+ void * const p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (p != MAP_FAILED) {
+ if (syscall(__NR_init_module, p, st.st_size, "") == 0) {
+ ret = true;
+ }
+ munmap(p, st.st_size);
+ }
+ }
+ close(fd);
+ }
+
+ return ret;
+}
+
+static int setupFilesystem(char* module) {
+ int retval;
+
+ // Verify root permissions
+ uid_t euid = geteuid();
+ if (euid) {
+ logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges");
+ handleException();
+ }
+
+ if (module) {
+ // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running
+ shutdownFilesystem();
+
+ // if still mounted
+ if (access("/dev/gator/buffer", F_OK) == 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to remove the running gator.ko. Manually remove the module or use the running module by not specifying one on the commandline");
+ handleException();
+ }
+ }
+
+ retval = mountGatorFS();
+ if (retval == 1) {
+ logg->logMessage("Driver already running at startup");
+ driverRunningAtStart = true;
+ } else if (retval == 0) {
+ logg->logMessage("Driver already mounted at startup");
+ driverRunningAtStart = driverMountedAtStart = true;
+ } else {
+ char command[256]; // arbitrarily large amount
+ char location[256]; // arbitrarily large amount
+
+ if (module) {
+ strncpy(location, module, sizeof(location));
+ } else {
+ // Is the driver co-located in the same directory?
+ if (util->getApplicationFullPath(location, sizeof(location)) != 0) { // allow some buffer space
+ logg->logMessage("Unable to determine the full path of gatord, the cwd will be used");
+ }
+ strncat(location, "gator.ko", sizeof(location) - strlen(location) - 1);
+ }
+
+ if (access(location, F_OK) == -1) {
+ logg->logError(__FILE__, __LINE__, "Unable to locate gator.ko driver:\n >>> gator.ko should be co-located with gatord in the same directory\n >>> OR insmod gator.ko prior to launching gatord\n >>> OR specify the location of gator.ko on the command line");
+ handleException();
+ }
+
+ // Load driver
+ bool success = init_module(location);
+ if (!success) {
+ logg->logMessage("init_module failed, trying insmod");
+ snprintf(command, sizeof(command), "insmod %s >/dev/null 2>&1", location);
+ if (system(command) != 0) {
+ logg->logMessage("Unable to load gator.ko driver with command: %s", command);
+ logg->logError(__FILE__, __LINE__, "Unable to load (insmod) gator.ko driver:\n >>> gator.ko must be built against the current kernel version & configuration\n >>> See dmesg for more details");
+ handleException();
+ }
+ }
+
+ if (mountGatorFS() == -1) {
+ logg->logError(__FILE__, __LINE__, "Unable to mount the gator filesystem needed for profiling.");
+ handleException();
+ }
+ }
+
+ return 0;
+}
+
+static int shutdownFilesystem() {
+ if (driverMountedAtStart == false) {
+ umount("/dev/gator");
+ }
+ if (driverRunningAtStart == false) {
+ if (syscall(__NR_delete_module, "gator", O_NONBLOCK) != 0) {
+ logg->logMessage("delete_module failed, trying rmmod");
+ if (system("rmmod gator >/dev/null 2>&1") != 0) {
+ return -1;
+ }
+ }
+ }
+
+ return 0; // success
+}
+
+static struct cmdline_t parseCommandLine(int argc, char** argv) {
+ struct cmdline_t cmdline;
+ cmdline.port = DEFAULT_PORT;
+ cmdline.module = NULL;
+ char version_string[256]; // arbitrary length to hold the version information
+ int c;
+
+ // build the version string
+ if (PROTOCOL_VERSION < PROTOCOL_DEV) {
+ snprintf(version_string, sizeof(version_string), "Streamline gatord version %d (DS-5 v5.%d)", PROTOCOL_VERSION, PROTOCOL_VERSION);
+ } else {
+ snprintf(version_string, sizeof(version_string), "Streamline gatord development version %d", PROTOCOL_VERSION);
+ }
+
+ while ((c = getopt(argc, argv, "hvp:s:c:e:m:o:")) != -1) {
+ switch(c) {
+ case 'c':
+ gSessionData->mConfigurationXMLPath = optarg;
+ break;
+ case 'e':
+ gSessionData->mEventsXMLPath = optarg;
+ break;
+ case 'm':
+ cmdline.module = optarg;
+ break;
+ case 'p':
+ cmdline.port = strtol(optarg, NULL, 10);
+ break;
+ case 's':
+ gSessionData->mSessionXMLPath = optarg;
+ break;
+ case 'o':
+ gSessionData->mTargetPath = optarg;
+ break;
+ case 'h':
+ case '?':
+ logg->logError(__FILE__, __LINE__,
+ "%s. All parameters are optional:\n"
+ "-c config_xml path and filename of the configuration.xml to use\n"
+ "-e events_xml path and filename of the events.xml to use\n"
+ "-h this help page\n"
+ "-m module path and filename of gator.ko\n"
+ "-p port_number port upon which the server listens; default is 8080\n"
+ "-s session_xml path and filename of a session xml used for local capture\n"
+ "-o apc_dir path and name of the output for a local capture\n"
+ "-v version information\n"
+ , version_string);
+ handleException();
+ break;
+ case 'v':
+ logg->logError(__FILE__, __LINE__, version_string);
+ handleException();
+ break;
+ }
+ }
+
+ // Error checking
+ if (cmdline.port != DEFAULT_PORT && gSessionData->mSessionXMLPath != NULL) {
+ logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both");
+ handleException();
+ }
+
+ if (gSessionData->mTargetPath != NULL && gSessionData->mSessionXMLPath == NULL) {
+ logg->logError(__FILE__, __LINE__, "Missing -s command line option required for a local capture.");
+ handleException();
+ }
+
+ if (optind < argc) {
+ logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]);
+ handleException();
+ }
+
+ return cmdline;
+}
+
+// Gator data flow: collector -> collector fifo -> sender
+int main(int argc, char** argv, char* envp[]) {
+ // Ensure proper signal handling by making gatord the process group leader
+ // e.g. it may not be the group leader when launched as 'sudo gatord'
+ setsid();
+
+ logg = new Logging(DEBUG); // Set up global thread-safe logging
+ gSessionData = new SessionData(); // Global data class
+ util = new OlyUtility(); // Set up global utility class
+
+ // Initialize drivers
+ new KMod();
+
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-main", 0, 0, 0);
+ pthread_mutex_init(&numSessions_mutex, NULL);
+
+ signal(SIGINT, handler);
+ signal(SIGTERM, handler);
+ signal(SIGABRT, handler);
+
+ // Set to high priority
+ if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) {
+ logg->logMessage("setpriority() failed");
+ }
+
+ // Parse the command line parameters
+ struct cmdline_t cmdline = parseCommandLine(argc, argv);
+
+ // Call before setting up the SIGCHLD handler, as system() spawns child processes
+ setupFilesystem(cmdline.module);
+
+ // Handle child exit codes
+ signal(SIGCHLD, child_exit);
+
+ // Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal
+ // Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler
+ signal(SIGPIPE, SIG_IGN);
+
+ // If the command line argument is a session xml file, no need to open a socket
+ if (gSessionData->mSessionXMLPath) {
+ child = new Child();
+ child->run();
+ delete child;
+ } else {
+ pthread_t answerThreadID;
+ if (pthread_create(&answerThreadID, NULL, answerThread, &cmdline)) {
+ logg->logError(__FILE__, __LINE__, "Failed to create answer thread");
+ handleException();
+ }
+ sock = new OlySocket(cmdline.port, true);
+ // Forever loop, can be exited via a signal or exception
+ while (1) {
+ logg->logMessage("Waiting on connection...");
+ sock->acceptConnection();
+
+ int pid = fork();
+ if (pid < 0) {
+ // Error
+ logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists.");
+ } else if (pid == 0) {
+ // Child
+ sock->closeServerSocket();
+ child = new Child(sock, numSessions + 1);
+ child->run();
+ delete child;
+ exit(0);
+ } else {
+ // Parent
+ sock->closeSocket();
+
+ pthread_mutex_lock(&numSessions_mutex);
+ numSessions++;
+ pthread_mutex_unlock(&numSessions_mutex);
+
+ // Maximum number of connections is 2
+ int wait = 0;
+ while (numSessions > 1) {
+ // Throttle until one of the children exits before continuing to accept another socket connection
+ logg->logMessage("%d sessions active!", numSessions);
+ if (wait++ >= 10) { // Wait no more than 10 seconds
+ // Kill last created child
+ kill(pid, SIGALRM);
+ break;
+ }
+ sleep(1);
+ }
+ }
+ }
+ }
+
+ cleanUp();
+ return 0;
+}
diff --git a/tools/gator/daemon/mxml/COPYING b/tools/gator/daemon/mxml/COPYING
new file mode 100644
index 000000000000..4d0aa78af224
--- /dev/null
+++ b/tools/gator/daemon/mxml/COPYING
@@ -0,0 +1,507 @@
+ Mini-XML License
+ September 18, 2010
+
+
+The Mini-XML library and included programs are provided under the
+terms of the GNU Library General Public License version 2 (LGPL2)
+with the following exceptions:
+
+ 1. Static linking of applications to the Mini-XML library
+does not constitute a derivative work and does not require
+the author to provide source code for the application, use
+the shared Mini-XML libraries, or link their applications
+against a user-supplied version of Mini-XML.
+
+If you link the application to a modified version of
+Mini-XML, then the changes to Mini-XML must be provided
+under the terms of the LGPL2 in sections 1, 2, and 4.
+
+ 2. You do not have to provide a copy of the Mini-XML license
+with programs that are linked to the Mini-XML library, nor
+do you have to identify the Mini-XML license in your
+program or documentation as required by section 6 of the
+LGPL2.
+
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ [This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/tools/gator/daemon/mxml/config.h b/tools/gator/daemon/mxml/config.h
new file mode 100644
index 000000000000..1f59ba34a474
--- /dev/null
+++ b/tools/gator/daemon/mxml/config.h
@@ -0,0 +1,96 @@
+/* config.h. Generated from config.h.in by configure. */
+/*
+ * "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $"
+ *
+ * Configuration file for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+
+/*
+ * Version number...
+ */
+
+#define MXML_VERSION "Mini-XML v2.7"
+
+
+/*
+ * Inline function support...
+ */
+
+#define inline
+
+
+/*
+ * Long long support...
+ */
+
+#define HAVE_LONG_LONG 1
+
+
+/*
+ * Do we have the snprintf() and vsnprintf() functions?
+ */
+
+#define HAVE_SNPRINTF 1
+#define HAVE_VSNPRINTF 1
+
+
+/*
+ * Do we have the strXXX() functions?
+ */
+
+#define HAVE_STRDUP 1
+
+
+/*
+ * Do we have threading support?
+ */
+
+#define HAVE_PTHREAD_H 1
+
+
+/*
+ * Define prototypes for string functions as needed...
+ */
+
+# ifndef HAVE_STRDUP
+extern char *_mxml_strdup(const char *);
+# define strdup _mxml_strdup
+# endif /* !HAVE_STRDUP */
+
+extern char *_mxml_strdupf(const char *, ...);
+extern char *_mxml_vstrdupf(const char *, va_list);
+
+# ifndef HAVE_SNPRINTF
+extern int _mxml_snprintf(char *, size_t, const char *, ...);
+# define snprintf _mxml_snprintf
+# endif /* !HAVE_SNPRINTF */
+
+# ifndef HAVE_VSNPRINTF
+extern int _mxml_vsnprintf(char *, size_t, const char *, va_list);
+# define vsnprintf _mxml_vsnprintf
+# endif /* !HAVE_VSNPRINTF */
+
+/*
+ * End of "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-attr.c b/tools/gator/daemon/mxml/mxml-attr.c
new file mode 100644
index 000000000000..c9950f5fb732
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-attr.c
@@ -0,0 +1,319 @@
+/*
+ * "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $"
+ *
+ * Attribute support code for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlElementDeleteAttr() - Delete an attribute.
+ * mxmlElementGetAttr() - Get an attribute.
+ * mxmlElementSetAttr() - Set an attribute.
+ * mxmlElementSetAttrf() - Set an attribute with a formatted value.
+ * mxml_set_attr() - Set or add an attribute name/value pair.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int mxml_set_attr(mxml_node_t *node, const char *name,
+ char *value);
+
+
+/*
+ * 'mxmlElementDeleteAttr()' - Delete an attribute.
+ *
+ * @since Mini-XML 2.4@
+ */
+
+void
+mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */
+ const char *name)/* I - Attribute name */
+{
+ int i; /* Looping var */
+ mxml_attr_t *attr; /* Cirrent attribute */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlElementDeleteAttr(node=%p, name=\"%s\")\n",
+ node, name ? name : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT || !name)
+ return;
+
+ /*
+ * Look for the attribute...
+ */
+
+ for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
+ i > 0;
+ i --, attr ++)
+ {
+#ifdef DEBUG
+ printf(" %s=\"%s\"\n", attr->name, attr->value);
+#endif /* DEBUG */
+
+ if (!strcmp(attr->name, name))
+ {
+ /*
+ * Delete this attribute...
+ */
+
+ free(attr->name);
+ free(attr->value);
+
+ i --;
+ if (i > 0)
+ memmove(attr, attr + 1, i * sizeof(mxml_attr_t));
+
+ node->value.element.num_attrs --;
+ return;
+ }
+ }
+}
+
+
+/*
+ * 'mxmlElementGetAttr()' - Get an attribute.
+ *
+ * This function returns NULL if the node is not an element or the
+ * named attribute does not exist.
+ */
+
+const char * /* O - Attribute value or NULL */
+mxmlElementGetAttr(mxml_node_t *node, /* I - Element node */
+ const char *name) /* I - Name of attribute */
+{
+ int i; /* Looping var */
+ mxml_attr_t *attr; /* Cirrent attribute */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlElementGetAttr(node=%p, name=\"%s\")\n",
+ node, name ? name : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT || !name)
+ return (NULL);
+
+ /*
+ * Look for the attribute...
+ */
+
+ for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
+ i > 0;
+ i --, attr ++)
+ {
+#ifdef DEBUG
+ printf(" %s=\"%s\"\n", attr->name, attr->value);
+#endif /* DEBUG */
+
+ if (!strcmp(attr->name, name))
+ {
+#ifdef DEBUG
+ printf(" Returning \"%s\"!\n", attr->value);
+#endif /* DEBUG */
+ return (attr->value);
+ }
+ }
+
+ /*
+ * Didn't find attribute, so return NULL...
+ */
+
+#ifdef DEBUG
+ puts(" Returning NULL!\n");
+#endif /* DEBUG */
+
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlElementSetAttr()' - Set an attribute.
+ *
+ * If the named attribute already exists, the value of the attribute
+ * is replaced by the new string value. The string value is copied
+ * into the element node. This function does nothing if the node is
+ * not an element.
+ */
+
+void
+mxmlElementSetAttr(mxml_node_t *node, /* I - Element node */
+ const char *name, /* I - Name of attribute */
+ const char *value) /* I - Attribute value */
+{
+ char *valuec; /* Copy of value */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlElementSetAttr(node=%p, name=\"%s\", value=\"%s\")\n",
+ node, name ? name : "(null)", value ? value : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT || !name)
+ return;
+
+ if (value)
+ valuec = strdup(value);
+ else
+ valuec = NULL;
+
+ if (mxml_set_attr(node, name, valuec))
+ free(valuec);
+}
+
+
+/*
+ * 'mxmlElementSetAttrf()' - Set an attribute with a formatted value.
+ *
+ * If the named attribute already exists, the value of the attribute
+ * is replaced by the new formatted string. The formatted string value is
+ * copied into the element node. This function does nothing if the node
+ * is not an element.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+void
+mxmlElementSetAttrf(mxml_node_t *node, /* I - Element node */
+ const char *name, /* I - Name of attribute */
+ const char *format,/* I - Printf-style attribute value */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Argument pointer */
+ char *value; /* Value */
+
+
+#ifdef DEBUG
+ fprintf(stderr,
+ "mxmlElementSetAttrf(node=%p, name=\"%s\", format=\"%s\", ...)\n",
+ node, name ? name : "(null)", format ? format : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT || !name || !format)
+ return;
+
+ /*
+ * Format the value...
+ */
+
+ va_start(ap, format);
+ value = _mxml_vstrdupf(format, ap);
+ va_end(ap);
+
+ if (!value)
+ mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
+ name, node->value.element.name);
+ else if (mxml_set_attr(node, name, value))
+ free(value);
+}
+
+
+/*
+ * 'mxml_set_attr()' - Set or add an attribute name/value pair.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+mxml_set_attr(mxml_node_t *node, /* I - Element node */
+ const char *name, /* I - Attribute name */
+ char *value) /* I - Attribute value */
+{
+ int i; /* Looping var */
+ mxml_attr_t *attr; /* New attribute */
+
+
+ /*
+ * Look for the attribute...
+ */
+
+ for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
+ i > 0;
+ i --, attr ++)
+ if (!strcmp(attr->name, name))
+ {
+ /*
+ * Free the old value as needed...
+ */
+
+ if (attr->value)
+ free(attr->value);
+
+ attr->value = value;
+
+ return (0);
+ }
+
+ /*
+ * Add a new attribute...
+ */
+
+ if (node->value.element.num_attrs == 0)
+ attr = malloc(sizeof(mxml_attr_t));
+ else
+ attr = realloc(node->value.element.attrs,
+ (node->value.element.num_attrs + 1) * sizeof(mxml_attr_t));
+
+ if (!attr)
+ {
+ mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
+ name, node->value.element.name);
+ return (-1);
+ }
+
+ node->value.element.attrs = attr;
+ attr += node->value.element.num_attrs;
+
+ if ((attr->name = strdup(name)) == NULL)
+ {
+ mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
+ name, node->value.element.name);
+ return (-1);
+ }
+
+ attr->value = value;
+
+ node->value.element.num_attrs ++;
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-entity.c b/tools/gator/daemon/mxml/mxml-entity.c
new file mode 100644
index 000000000000..c5c9f61f73c2
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-entity.c
@@ -0,0 +1,460 @@
+/*
+ * "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $"
+ *
+ * Character entity support code for Mini-XML, a small XML-like
+ * file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlEntityAddCallback() - Add a callback to convert entities to
+ * Unicode.
+ * mxmlEntityGetName() - Get the name that corresponds to the
+ * character value.
+ * mxmlEntityGetValue() - Get the character corresponding to a named
+ * entity.
+ * mxmlEntityRemoveCallback() - Remove a callback.
+ * _mxml_entity_cb() - Lookup standard (X)HTML entities.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "mxml-private.h"
+
+
+/*
+ * 'mxmlEntityAddCallback()' - Add a callback to convert entities to Unicode.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlEntityAddCallback(
+ mxml_entity_cb_t cb) /* I - Callback function to add */
+{
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ if (global->num_entity_cbs < (int)(sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0])))
+ {
+ global->entity_cbs[global->num_entity_cbs] = cb;
+ global->num_entity_cbs ++;
+
+ return (0);
+ }
+ else
+ {
+ mxml_error("Unable to add entity callback!");
+
+ return (-1);
+ }
+}
+
+
+/*
+ * 'mxmlEntityGetName()' - Get the name that corresponds to the character value.
+ *
+ * If val does not need to be represented by a named entity, NULL is returned.
+ */
+
+const char * /* O - Entity name or NULL */
+mxmlEntityGetName(int val) /* I - Character value */
+{
+ switch (val)
+ {
+ case '&' :
+ return ("amp");
+
+ case '<' :
+ return ("lt");
+
+ case '>' :
+ return ("gt");
+
+ case '\"' :
+ return ("quot");
+
+ default :
+ return (NULL);
+ }
+}
+
+
+/*
+ * 'mxmlEntityGetValue()' - Get the character corresponding to a named entity.
+ *
+ * The entity name can also be a numeric constant. -1 is returned if the
+ * name is not known.
+ */
+
+int /* O - Character value or -1 on error */
+mxmlEntityGetValue(const char *name) /* I - Entity name */
+{
+ int i; /* Looping var */
+ int ch; /* Character value */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ for (i = 0; i < global->num_entity_cbs; i ++)
+ if ((ch = (global->entity_cbs[i])(name)) >= 0)
+ return (ch);
+
+ return (-1);
+}
+
+
+/*
+ * 'mxmlEntityRemoveCallback()' - Remove a callback.
+ */
+
+void
+mxmlEntityRemoveCallback(
+ mxml_entity_cb_t cb) /* I - Callback function to remove */
+{
+ int i; /* Looping var */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ for (i = 0; i < global->num_entity_cbs; i ++)
+ if (cb == global->entity_cbs[i])
+ {
+ /*
+ * Remove the callback...
+ */
+
+ global->num_entity_cbs --;
+
+ if (i < global->num_entity_cbs)
+ memmove(global->entity_cbs + i, global->entity_cbs + i + 1,
+ (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0]));
+
+ return;
+ }
+}
+
+
+/*
+ * '_mxml_entity_cb()' - Lookup standard (X)HTML entities.
+ */
+
+int /* O - Unicode value or -1 */
+_mxml_entity_cb(const char *name) /* I - Entity name */
+{
+ int diff, /* Difference between names */
+ current, /* Current entity in search */
+ first, /* First entity in search */
+ last; /* Last entity in search */
+ static const struct
+ {
+ const char *name; /* Entity name */
+ int val; /* Character value */
+ } entities[] =
+ {
+ { "AElig", 198 },
+ { "Aacute", 193 },
+ { "Acirc", 194 },
+ { "Agrave", 192 },
+ { "Alpha", 913 },
+ { "Aring", 197 },
+ { "Atilde", 195 },
+ { "Auml", 196 },
+ { "Beta", 914 },
+ { "Ccedil", 199 },
+ { "Chi", 935 },
+ { "Dagger", 8225 },
+ { "Delta", 916 },
+ { "Dstrok", 208 },
+ { "ETH", 208 },
+ { "Eacute", 201 },
+ { "Ecirc", 202 },
+ { "Egrave", 200 },
+ { "Epsilon", 917 },
+ { "Eta", 919 },
+ { "Euml", 203 },
+ { "Gamma", 915 },
+ { "Iacute", 205 },
+ { "Icirc", 206 },
+ { "Igrave", 204 },
+ { "Iota", 921 },
+ { "Iuml", 207 },
+ { "Kappa", 922 },
+ { "Lambda", 923 },
+ { "Mu", 924 },
+ { "Ntilde", 209 },
+ { "Nu", 925 },
+ { "OElig", 338 },
+ { "Oacute", 211 },
+ { "Ocirc", 212 },
+ { "Ograve", 210 },
+ { "Omega", 937 },
+ { "Omicron", 927 },
+ { "Oslash", 216 },
+ { "Otilde", 213 },
+ { "Ouml", 214 },
+ { "Phi", 934 },
+ { "Pi", 928 },
+ { "Prime", 8243 },
+ { "Psi", 936 },
+ { "Rho", 929 },
+ { "Scaron", 352 },
+ { "Sigma", 931 },
+ { "THORN", 222 },
+ { "Tau", 932 },
+ { "Theta", 920 },
+ { "Uacute", 218 },
+ { "Ucirc", 219 },
+ { "Ugrave", 217 },
+ { "Upsilon", 933 },
+ { "Uuml", 220 },
+ { "Xi", 926 },
+ { "Yacute", 221 },
+ { "Yuml", 376 },
+ { "Zeta", 918 },
+ { "aacute", 225 },
+ { "acirc", 226 },
+ { "acute", 180 },
+ { "aelig", 230 },
+ { "agrave", 224 },
+ { "alefsym", 8501 },
+ { "alpha", 945 },
+ { "amp", '&' },
+ { "and", 8743 },
+ { "ang", 8736 },
+ { "apos", '\'' },
+ { "aring", 229 },
+ { "asymp", 8776 },
+ { "atilde", 227 },
+ { "auml", 228 },
+ { "bdquo", 8222 },
+ { "beta", 946 },
+ { "brkbar", 166 },
+ { "brvbar", 166 },
+ { "bull", 8226 },
+ { "cap", 8745 },
+ { "ccedil", 231 },
+ { "cedil", 184 },
+ { "cent", 162 },
+ { "chi", 967 },
+ { "circ", 710 },
+ { "clubs", 9827 },
+ { "cong", 8773 },
+ { "copy", 169 },
+ { "crarr", 8629 },
+ { "cup", 8746 },
+ { "curren", 164 },
+ { "dArr", 8659 },
+ { "dagger", 8224 },
+ { "darr", 8595 },
+ { "deg", 176 },
+ { "delta", 948 },
+ { "diams", 9830 },
+ { "die", 168 },
+ { "divide", 247 },
+ { "eacute", 233 },
+ { "ecirc", 234 },
+ { "egrave", 232 },
+ { "empty", 8709 },
+ { "emsp", 8195 },
+ { "ensp", 8194 },
+ { "epsilon", 949 },
+ { "equiv", 8801 },
+ { "eta", 951 },
+ { "eth", 240 },
+ { "euml", 235 },
+ { "euro", 8364 },
+ { "exist", 8707 },
+ { "fnof", 402 },
+ { "forall", 8704 },
+ { "frac12", 189 },
+ { "frac14", 188 },
+ { "frac34", 190 },
+ { "frasl", 8260 },
+ { "gamma", 947 },
+ { "ge", 8805 },
+ { "gt", '>' },
+ { "hArr", 8660 },
+ { "harr", 8596 },
+ { "hearts", 9829 },
+ { "hellip", 8230 },
+ { "hibar", 175 },
+ { "iacute", 237 },
+ { "icirc", 238 },
+ { "iexcl", 161 },
+ { "igrave", 236 },
+ { "image", 8465 },
+ { "infin", 8734 },
+ { "int", 8747 },
+ { "iota", 953 },
+ { "iquest", 191 },
+ { "isin", 8712 },
+ { "iuml", 239 },
+ { "kappa", 954 },
+ { "lArr", 8656 },
+ { "lambda", 955 },
+ { "lang", 9001 },
+ { "laquo", 171 },
+ { "larr", 8592 },
+ { "lceil", 8968 },
+ { "ldquo", 8220 },
+ { "le", 8804 },
+ { "lfloor", 8970 },
+ { "lowast", 8727 },
+ { "loz", 9674 },
+ { "lrm", 8206 },
+ { "lsaquo", 8249 },
+ { "lsquo", 8216 },
+ { "lt", '<' },
+ { "macr", 175 },
+ { "mdash", 8212 },
+ { "micro", 181 },
+ { "middot", 183 },
+ { "minus", 8722 },
+ { "mu", 956 },
+ { "nabla", 8711 },
+ { "nbsp", 160 },
+ { "ndash", 8211 },
+ { "ne", 8800 },
+ { "ni", 8715 },
+ { "not", 172 },
+ { "notin", 8713 },
+ { "nsub", 8836 },
+ { "ntilde", 241 },
+ { "nu", 957 },
+ { "oacute", 243 },
+ { "ocirc", 244 },
+ { "oelig", 339 },
+ { "ograve", 242 },
+ { "oline", 8254 },
+ { "omega", 969 },
+ { "omicron", 959 },
+ { "oplus", 8853 },
+ { "or", 8744 },
+ { "ordf", 170 },
+ { "ordm", 186 },
+ { "oslash", 248 },
+ { "otilde", 245 },
+ { "otimes", 8855 },
+ { "ouml", 246 },
+ { "para", 182 },
+ { "part", 8706 },
+ { "permil", 8240 },
+ { "perp", 8869 },
+ { "phi", 966 },
+ { "pi", 960 },
+ { "piv", 982 },
+ { "plusmn", 177 },
+ { "pound", 163 },
+ { "prime", 8242 },
+ { "prod", 8719 },
+ { "prop", 8733 },
+ { "psi", 968 },
+ { "quot", '\"' },
+ { "rArr", 8658 },
+ { "radic", 8730 },
+ { "rang", 9002 },
+ { "raquo", 187 },
+ { "rarr", 8594 },
+ { "rceil", 8969 },
+ { "rdquo", 8221 },
+ { "real", 8476 },
+ { "reg", 174 },
+ { "rfloor", 8971 },
+ { "rho", 961 },
+ { "rlm", 8207 },
+ { "rsaquo", 8250 },
+ { "rsquo", 8217 },
+ { "sbquo", 8218 },
+ { "scaron", 353 },
+ { "sdot", 8901 },
+ { "sect", 167 },
+ { "shy", 173 },
+ { "sigma", 963 },
+ { "sigmaf", 962 },
+ { "sim", 8764 },
+ { "spades", 9824 },
+ { "sub", 8834 },
+ { "sube", 8838 },
+ { "sum", 8721 },
+ { "sup", 8835 },
+ { "sup1", 185 },
+ { "sup2", 178 },
+ { "sup3", 179 },
+ { "supe", 8839 },
+ { "szlig", 223 },
+ { "tau", 964 },
+ { "there4", 8756 },
+ { "theta", 952 },
+ { "thetasym", 977 },
+ { "thinsp", 8201 },
+ { "thorn", 254 },
+ { "tilde", 732 },
+ { "times", 215 },
+ { "trade", 8482 },
+ { "uArr", 8657 },
+ { "uacute", 250 },
+ { "uarr", 8593 },
+ { "ucirc", 251 },
+ { "ugrave", 249 },
+ { "uml", 168 },
+ { "upsih", 978 },
+ { "upsilon", 965 },
+ { "uuml", 252 },
+ { "weierp", 8472 },
+ { "xi", 958 },
+ { "yacute", 253 },
+ { "yen", 165 },
+ { "yuml", 255 },
+ { "zeta", 950 },
+ { "zwj", 8205 },
+ { "zwnj", 8204 }
+ };
+
+
+ /*
+ * Do a binary search for the named entity...
+ */
+
+ first = 0;
+ last = (int)(sizeof(entities) / sizeof(entities[0]) - 1);
+
+ while ((last - first) > 1)
+ {
+ current = (first + last) / 2;
+
+ if ((diff = strcmp(name, entities[current].name)) == 0)
+ return (entities[current].val);
+ else if (diff < 0)
+ last = current;
+ else
+ first = current;
+ }
+
+ /*
+ * If we get here, there is a small chance that there is still
+ * a match; check first and last...
+ */
+
+ if (!strcmp(name, entities[first].name))
+ return (entities[first].val);
+ else if (!strcmp(name, entities[last].name))
+ return (entities[last].val);
+ else
+ return (-1);
+}
+
+
+/*
+ * End of "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-file.c b/tools/gator/daemon/mxml/mxml-file.c
new file mode 100644
index 000000000000..7860ee5f8370
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-file.c
@@ -0,0 +1,3082 @@
+/*
+ * "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $"
+ *
+ * File loading code for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlLoadFd() - Load a file descriptor into an XML node tree.
+ * mxmlLoadFile() - Load a file into an XML node tree.
+ * mxmlLoadString() - Load a string into an XML node tree.
+ * mxmlSaveAllocString() - Save an XML tree to an allocated string.
+ * mxmlSaveFd() - Save an XML tree to a file descriptor.
+ * mxmlSaveFile() - Save an XML tree to a file.
+ * mxmlSaveString() - Save an XML node tree to a string.
+ * mxmlSAXLoadFd() - Load a file descriptor into an XML node tree
+ * using a SAX callback.
+ * mxmlSAXLoadFile() - Load a file into an XML node tree
+ * using a SAX callback.
+ * mxmlSAXLoadString() - Load a string into an XML node tree
+ * using a SAX callback.
+ * mxmlSetCustomHandlers() - Set the handling functions for custom data.
+ * mxmlSetErrorCallback() - Set the error message callback.
+ * mxmlSetWrapMargin() - Set the wrap margin when saving XML data.
+ * mxml_add_char() - Add a character to a buffer, expanding as needed.
+ * mxml_fd_getc() - Read a character from a file descriptor.
+ * mxml_fd_putc() - Write a character to a file descriptor.
+ * mxml_fd_read() - Read a buffer of data from a file descriptor.
+ * mxml_fd_write() - Write a buffer of data to a file descriptor.
+ * mxml_file_getc() - Get a character from a file.
+ * mxml_file_putc() - Write a character to a file.
+ * mxml_get_entity() - Get the character corresponding to an entity...
+ * mxml_load_data() - Load data into an XML node tree.
+ * mxml_parse_element() - Parse an element for any attributes...
+ * mxml_string_getc() - Get a character from a string.
+ * mxml_string_putc() - Write a character to a string.
+ * mxml_write_name() - Write a name string.
+ * mxml_write_node() - Save an XML node to a file.
+ * mxml_write_string() - Write a string, escaping & and < as needed.
+ * mxml_write_ws() - Do whitespace callback...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#ifndef WIN32
+# include <unistd.h>
+#endif /* !WIN32 */
+#include "mxml-private.h"
+
+
+/*
+ * Character encoding...
+ */
+
+#define ENCODE_UTF8 0 /* UTF-8 */
+#define ENCODE_UTF16BE 1 /* UTF-16 Big-Endian */
+#define ENCODE_UTF16LE 2 /* UTF-16 Little-Endian */
+
+
+/*
+ * Macro to test for a bad XML character...
+ */
+
+#define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t')
+
+
+/*
+ * Types and structures...
+ */
+
+typedef int (*_mxml_getc_cb_t)(void *, int *);
+typedef int (*_mxml_putc_cb_t)(int, void *);
+
+typedef struct _mxml_fdbuf_s /**** File descriptor buffer ****/
+{
+ int fd; /* File descriptor */
+ unsigned char *current, /* Current position in buffer */
+ *end, /* End of buffer */
+ buffer[8192]; /* Character buffer */
+} _mxml_fdbuf_t;
+
+
+/*
+ * Local functions...
+ */
+
+static int mxml_add_char(int ch, char **ptr, char **buffer,
+ int *bufsize);
+static int mxml_fd_getc(void *p, int *encoding);
+static int mxml_fd_putc(int ch, void *p);
+static int mxml_fd_read(_mxml_fdbuf_t *buf);
+static int mxml_fd_write(_mxml_fdbuf_t *buf);
+static int mxml_file_getc(void *p, int *encoding);
+static int mxml_file_putc(int ch, void *p);
+static int mxml_get_entity(mxml_node_t *parent, void *p,
+ int *encoding,
+ _mxml_getc_cb_t getc_cb);
+static inline int mxml_isspace(int ch)
+ {
+ return (ch == ' ' || ch == '\t' || ch == '\r' ||
+ ch == '\n');
+ }
+static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p,
+ mxml_load_cb_t cb,
+ _mxml_getc_cb_t getc_cb,
+ mxml_sax_cb_t sax_cb, void *sax_data);
+static int mxml_parse_element(mxml_node_t *node, void *p,
+ int *encoding,
+ _mxml_getc_cb_t getc_cb);
+static int mxml_string_getc(void *p, int *encoding);
+static int mxml_string_putc(int ch, void *p);
+static int mxml_write_name(const char *s, void *p,
+ _mxml_putc_cb_t putc_cb);
+static int mxml_write_node(mxml_node_t *node, void *p,
+ mxml_save_cb_t cb, int col,
+ _mxml_putc_cb_t putc_cb,
+ _mxml_global_t *global);
+static int mxml_write_string(const char *s, void *p,
+ _mxml_putc_cb_t putc_cb);
+static int mxml_write_ws(mxml_node_t *node, void *p,
+ mxml_save_cb_t cb, int ws,
+ int col, _mxml_putc_cb_t putc_cb);
+
+
+/*
+ * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree.
+ *
+ * The nodes in the specified file are added to the specified top node.
+ * If no top node is provided, the XML file MUST be well-formed with a
+ * single parent node like <?xml> for the entire file. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ */
+
+mxml_node_t * /* O - First node or NULL if the file could not be read. */
+mxmlLoadFd(mxml_node_t *top, /* I - Top node */
+ int fd, /* I - File descriptor to read from */
+ mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
+{
+ _mxml_fdbuf_t buf; /* File descriptor buffer */
+
+
+ /*
+ * Initialize the file descriptor buffer...
+ */
+
+ buf.fd = fd;
+ buf.current = buf.buffer;
+ buf.end = buf.buffer;
+
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, &buf, cb, mxml_fd_getc, MXML_NO_CALLBACK, NULL));
+}
+
+
+/*
+ * 'mxmlLoadFile()' - Load a file into an XML node tree.
+ *
+ * The nodes in the specified file are added to the specified top node.
+ * If no top node is provided, the XML file MUST be well-formed with a
+ * single parent node like <?xml> for the entire file. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ */
+
+mxml_node_t * /* O - First node or NULL if the file could not be read. */
+mxmlLoadFile(mxml_node_t *top, /* I - Top node */
+ FILE *fp, /* I - File to read from */
+ mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
+{
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, fp, cb, mxml_file_getc, MXML_NO_CALLBACK, NULL));
+}
+
+
+/*
+ * 'mxmlLoadString()' - Load a string into an XML node tree.
+ *
+ * The nodes in the specified string are added to the specified top node.
+ * If no top node is provided, the XML string MUST be well-formed with a
+ * single parent node like <?xml> for the entire string. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ */
+
+mxml_node_t * /* O - First node or NULL if the string has errors. */
+mxmlLoadString(mxml_node_t *top, /* I - Top node */
+ const char *s, /* I - String to load */
+ mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
+{
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, MXML_NO_CALLBACK,
+ NULL));
+}
+
+
+/*
+ * 'mxmlSaveAllocString()' - Save an XML tree to an allocated string.
+ *
+ * This function returns a pointer to a string containing the textual
+ * representation of the XML node tree. The string should be freed
+ * using the free() function when you are done with it. NULL is returned
+ * if the node would produce an empty string or if the string cannot be
+ * allocated.
+ *
+ * The callback argument specifies a function that returns a whitespace
+ * string or NULL before and after each element. If MXML_NO_CALLBACK
+ * is specified, whitespace will only be added before MXML_TEXT nodes
+ * with leading whitespace and before attribute names inside opening
+ * element tags.
+ */
+
+char * /* O - Allocated string or NULL */
+mxmlSaveAllocString(
+ mxml_node_t *node, /* I - Node to write */
+ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
+{
+ int bytes; /* Required bytes */
+ char buffer[8192]; /* Temporary buffer */
+ char *s; /* Allocated string */
+
+
+ /*
+ * Write the node to the temporary buffer...
+ */
+
+ bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb);
+
+ if (bytes <= 0)
+ return (NULL);
+
+ if (bytes < (int)(sizeof(buffer) - 1))
+ {
+ /*
+ * Node fit inside the buffer, so just duplicate that string and
+ * return...
+ */
+
+ return (strdup(buffer));
+ }
+
+ /*
+ * Allocate a buffer of the required size and save the node to the
+ * new buffer...
+ */
+
+ if ((s = malloc(bytes + 1)) == NULL)
+ return (NULL);
+
+ mxmlSaveString(node, s, bytes + 1, cb);
+
+ /*
+ * Return the allocated string...
+ */
+
+ return (s);
+}
+
+
+/*
+ * 'mxmlSaveFd()' - Save an XML tree to a file descriptor.
+ *
+ * The callback argument specifies a function that returns a whitespace
+ * string or NULL before and after each element. If MXML_NO_CALLBACK
+ * is specified, whitespace will only be added before MXML_TEXT nodes
+ * with leading whitespace and before attribute names inside opening
+ * element tags.
+ */
+
+int /* O - 0 on success, -1 on error. */
+mxmlSaveFd(mxml_node_t *node, /* I - Node to write */
+ int fd, /* I - File descriptor to write to */
+ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
+{
+ int col; /* Final column */
+ _mxml_fdbuf_t buf; /* File descriptor buffer */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ /*
+ * Initialize the file descriptor buffer...
+ */
+
+ buf.fd = fd;
+ buf.current = buf.buffer;
+ buf.end = buf.buffer + sizeof(buf.buffer);
+
+ /*
+ * Write the node...
+ */
+
+ if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc, global)) < 0)
+ return (-1);
+
+ if (col > 0)
+ if (mxml_fd_putc('\n', &buf) < 0)
+ return (-1);
+
+ /*
+ * Flush and return...
+ */
+
+ return (mxml_fd_write(&buf));
+}
+
+
+/*
+ * 'mxmlSaveFile()' - Save an XML tree to a file.
+ *
+ * The callback argument specifies a function that returns a whitespace
+ * string or NULL before and after each element. If MXML_NO_CALLBACK
+ * is specified, whitespace will only be added before MXML_TEXT nodes
+ * with leading whitespace and before attribute names inside opening
+ * element tags.
+ */
+
+int /* O - 0 on success, -1 on error. */
+mxmlSaveFile(mxml_node_t *node, /* I - Node to write */
+ FILE *fp, /* I - File to write to */
+ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
+{
+ int col; /* Final column */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ /*
+ * Write the node...
+ */
+
+ if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc, global)) < 0)
+ return (-1);
+
+ if (col > 0)
+ if (putc('\n', fp) < 0)
+ return (-1);
+
+ /*
+ * Return 0 (success)...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSaveString()' - Save an XML node tree to a string.
+ *
+ * This function returns the total number of bytes that would be
+ * required for the string but only copies (bufsize - 1) characters
+ * into the specified buffer.
+ *
+ * The callback argument specifies a function that returns a whitespace
+ * string or NULL before and after each element. If MXML_NO_CALLBACK
+ * is specified, whitespace will only be added before MXML_TEXT nodes
+ * with leading whitespace and before attribute names inside opening
+ * element tags.
+ */
+
+int /* O - Size of string */
+mxmlSaveString(mxml_node_t *node, /* I - Node to write */
+ char *buffer, /* I - String buffer */
+ int bufsize, /* I - Size of string buffer */
+ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
+{
+ int col; /* Final column */
+ char *ptr[2]; /* Pointers for putc_cb */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ /*
+ * Write the node...
+ */
+
+ ptr[0] = buffer;
+ ptr[1] = buffer + bufsize;
+
+ if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc, global)) < 0)
+ return (-1);
+
+ if (col > 0)
+ mxml_string_putc('\n', ptr);
+
+ /*
+ * Nul-terminate the buffer...
+ */
+
+ if (ptr[0] >= ptr[1])
+ buffer[bufsize - 1] = '\0';
+ else
+ ptr[0][0] = '\0';
+
+ /*
+ * Return the number of characters...
+ */
+
+ return (ptr[0] - buffer);
+}
+
+
+/*
+ * 'mxmlSAXLoadFd()' - Load a file descriptor into an XML node tree
+ * using a SAX callback.
+ *
+ * The nodes in the specified file are added to the specified top node.
+ * If no top node is provided, the XML file MUST be well-formed with a
+ * single parent node like <?xml> for the entire file. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ *
+ * The SAX callback must call mxmlRetain() for any nodes that need to
+ * be kept for later use. Otherwise, nodes are deleted when the parent
+ * node is closed or after each data, comment, CDATA, or directive node.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+mxml_node_t * /* O - First node or NULL if the file could not be read. */
+mxmlSAXLoadFd(mxml_node_t *top, /* I - Top node */
+ int fd, /* I - File descriptor to read from */
+ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
+ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
+ void *sax_data) /* I - SAX user data */
+{
+ _mxml_fdbuf_t buf; /* File descriptor buffer */
+
+
+ /*
+ * Initialize the file descriptor buffer...
+ */
+
+ buf.fd = fd;
+ buf.current = buf.buffer;
+ buf.end = buf.buffer;
+
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, &buf, cb, mxml_fd_getc, sax_cb, sax_data));
+}
+
+
+/*
+ * 'mxmlSAXLoadFile()' - Load a file into an XML node tree
+ * using a SAX callback.
+ *
+ * The nodes in the specified file are added to the specified top node.
+ * If no top node is provided, the XML file MUST be well-formed with a
+ * single parent node like <?xml> for the entire file. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ *
+ * The SAX callback must call mxmlRetain() for any nodes that need to
+ * be kept for later use. Otherwise, nodes are deleted when the parent
+ * node is closed or after each data, comment, CDATA, or directive node.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+mxml_node_t * /* O - First node or NULL if the file could not be read. */
+mxmlSAXLoadFile(
+ mxml_node_t *top, /* I - Top node */
+ FILE *fp, /* I - File to read from */
+ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
+ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
+ void *sax_data) /* I - SAX user data */
+{
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, fp, cb, mxml_file_getc, sax_cb, sax_data));
+}
+
+
+/*
+ * 'mxmlSAXLoadString()' - Load a string into an XML node tree
+ * using a SAX callback.
+ *
+ * The nodes in the specified string are added to the specified top node.
+ * If no top node is provided, the XML string MUST be well-formed with a
+ * single parent node like <?xml> for the entire string. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ *
+ * The SAX callback must call mxmlRetain() for any nodes that need to
+ * be kept for later use. Otherwise, nodes are deleted when the parent
+ * node is closed or after each data, comment, CDATA, or directive node.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+mxml_node_t * /* O - First node or NULL if the string has errors. */
+mxmlSAXLoadString(
+ mxml_node_t *top, /* I - Top node */
+ const char *s, /* I - String to load */
+ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
+ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
+ void *sax_data) /* I - SAX user data */
+{
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, sax_cb, sax_data));
+}
+
+
+/*
+ * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data.
+ *
+ * The load function accepts a node pointer and a data string and must
+ * return 0 on success and non-zero on error.
+ *
+ * The save function accepts a node pointer and must return a malloc'd
+ * string on success and NULL on error.
+ *
+ */
+
+void
+mxmlSetCustomHandlers(
+ mxml_custom_load_cb_t load, /* I - Load function */
+ mxml_custom_save_cb_t save) /* I - Save function */
+{
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ global->custom_load_cb = load;
+ global->custom_save_cb = save;
+}
+
+
+/*
+ * 'mxmlSetErrorCallback()' - Set the error message callback.
+ */
+
+void
+mxmlSetErrorCallback(mxml_error_cb_t cb)/* I - Error callback function */
+{
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ global->error_cb = cb;
+}
+
+
+/*
+ * 'mxmlSetWrapMargin()' - Set the wrap margin when saving XML data.
+ *
+ * Wrapping is disabled when "column" is 0.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+void
+mxmlSetWrapMargin(int column) /* I - Column for wrapping, 0 to disable wrapping */
+{
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ global->wrap = column;
+}
+
+
+/*
+ * 'mxml_add_char()' - Add a character to a buffer, expanding as needed.
+ */
+
+static int /* O - 0 on success, -1 on error */
+mxml_add_char(int ch, /* I - Character to add */
+ char **bufptr, /* IO - Current position in buffer */
+ char **buffer, /* IO - Current buffer */
+ int *bufsize) /* IO - Current buffer size */
+{
+ char *newbuffer; /* New buffer value */
+
+
+ if (*bufptr >= (*buffer + *bufsize - 4))
+ {
+ /*
+ * Increase the size of the buffer...
+ */
+
+ if (*bufsize < 1024)
+ (*bufsize) *= 2;
+ else
+ (*bufsize) += 1024;
+
+ if ((newbuffer = realloc(*buffer, *bufsize)) == NULL)
+ {
+ free(*buffer);
+
+ mxml_error("Unable to expand string buffer to %d bytes!", *bufsize);
+
+ return (-1);
+ }
+
+ *bufptr = newbuffer + (*bufptr - *buffer);
+ *buffer = newbuffer;
+ }
+
+ if (ch < 0x80)
+ {
+ /*
+ * Single byte ASCII...
+ */
+
+ *(*bufptr)++ = ch;
+ }
+ else if (ch < 0x800)
+ {
+ /*
+ * Two-byte UTF-8...
+ */
+
+ *(*bufptr)++ = 0xc0 | (ch >> 6);
+ *(*bufptr)++ = 0x80 | (ch & 0x3f);
+ }
+ else if (ch < 0x10000)
+ {
+ /*
+ * Three-byte UTF-8...
+ */
+
+ *(*bufptr)++ = 0xe0 | (ch >> 12);
+ *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
+ *(*bufptr)++ = 0x80 | (ch & 0x3f);
+ }
+ else
+ {
+ /*
+ * Four-byte UTF-8...
+ */
+
+ *(*bufptr)++ = 0xf0 | (ch >> 18);
+ *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f);
+ *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
+ *(*bufptr)++ = 0x80 | (ch & 0x3f);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_fd_getc()' - Read a character from a file descriptor.
+ */
+
+static int /* O - Character or EOF */
+mxml_fd_getc(void *p, /* I - File descriptor buffer */
+ int *encoding) /* IO - Encoding */
+{
+ _mxml_fdbuf_t *buf; /* File descriptor buffer */
+ int ch, /* Current character */
+ temp; /* Temporary character */
+
+
+ /*
+ * Grab the next character in the buffer...
+ */
+
+ buf = (_mxml_fdbuf_t *)p;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ ch = *(buf->current)++;
+
+ switch (*encoding)
+ {
+ case ENCODE_UTF8 :
+ /*
+ * Got a UTF-8 character; convert UTF-8 to Unicode and return...
+ */
+
+ if (!(ch & 0x80))
+ {
+#if DEBUG > 1
+ printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+
+ return (ch);
+ }
+ else if (ch == 0xfe)
+ {
+ /*
+ * UTF-16 big-endian BOM?
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ ch = *(buf->current)++;
+
+ if (ch != 0xff)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16BE;
+
+ return (mxml_fd_getc(p, encoding));
+ }
+ else if (ch == 0xff)
+ {
+ /*
+ * UTF-16 little-endian BOM?
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ ch = *(buf->current)++;
+
+ if (ch != 0xfe)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16LE;
+
+ return (mxml_fd_getc(p, encoding));
+ }
+ else if ((ch & 0xe0) == 0xc0)
+ {
+ /*
+ * Two-byte value...
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
+
+ if (ch < 0x80)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+ }
+ else if ((ch & 0xf0) == 0xe0)
+ {
+ /*
+ * Three-byte value...
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if (ch < 0x800)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+
+ /*
+ * Ignore (strip) Byte Order Mark (BOM)...
+ */
+
+ if (ch == 0xfeff)
+ return (mxml_fd_getc(p, encoding));
+ }
+ else if ((ch & 0xf8) == 0xf0)
+ {
+ /*
+ * Four-byte value...
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x07) << 6) | (temp & 0x3f);
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if (ch < 0x10000)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+ }
+ else
+ return (EOF);
+ break;
+
+ case ENCODE_UTF16BE :
+ /*
+ * Read UTF-16 big-endian char...
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ ch = (ch << 8) | temp;
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ lch = *(buf->current)++;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ lch = (lch << 8) | temp;
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+ break;
+
+ case ENCODE_UTF16LE :
+ /*
+ * Read UTF-16 little-endian char...
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ ch |= (temp << 8);
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ lch = *(buf->current)++;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ lch |= (temp << 8);
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+ break;
+ }
+
+#if DEBUG > 1
+ printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+}
+
+
+/*
+ * 'mxml_fd_putc()' - Write a character to a file descriptor.
+ */
+
+static int /* O - 0 on success, -1 on error */
+mxml_fd_putc(int ch, /* I - Character */
+ void *p) /* I - File descriptor buffer */
+{
+ _mxml_fdbuf_t *buf; /* File descriptor buffer */
+
+
+ /*
+ * Flush the write buffer as needed...
+ */
+
+ buf = (_mxml_fdbuf_t *)p;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_write(buf) < 0)
+ return (-1);
+
+ *(buf->current)++ = ch;
+
+ /*
+ * Return successfully...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_fd_read()' - Read a buffer of data from a file descriptor.
+ */
+
+static int /* O - 0 on success, -1 on error */
+mxml_fd_read(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */
+{
+ int bytes; /* Bytes read... */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!buf)
+ return (-1);
+
+ /*
+ * Read from the file descriptor...
+ */
+
+ while ((bytes = read(buf->fd, buf->buffer, sizeof(buf->buffer))) < 0)
+#ifdef EINTR
+ if (errno != EAGAIN && errno != EINTR)
+#else
+ if (errno != EAGAIN)
+#endif /* EINTR */
+ return (-1);
+
+ if (bytes == 0)
+ return (-1);
+
+ /*
+ * Update the pointers and return success...
+ */
+
+ buf->current = buf->buffer;
+ buf->end = buf->buffer + bytes;
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_fd_write()' - Write a buffer of data to a file descriptor.
+ */
+
+static int /* O - 0 on success, -1 on error */
+mxml_fd_write(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */
+{
+ int bytes; /* Bytes written */
+ unsigned char *ptr; /* Pointer into buffer */
+
+
+ /*
+ * Range check...
+ */
+
+ if (!buf)
+ return (-1);
+
+ /*
+ * Return 0 if there is nothing to write...
+ */
+
+ if (buf->current == buf->buffer)
+ return (0);
+
+ /*
+ * Loop until we have written everything...
+ */
+
+ for (ptr = buf->buffer; ptr < buf->current; ptr += bytes)
+ if ((bytes = write(buf->fd, ptr, buf->current - ptr)) < 0)
+ return (-1);
+
+ /*
+ * All done, reset pointers and return success...
+ */
+
+ buf->current = buf->buffer;
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_file_getc()' - Get a character from a file.
+ */
+
+static int /* O - Character or EOF */
+mxml_file_getc(void *p, /* I - Pointer to file */
+ int *encoding) /* IO - Encoding */
+{
+ int ch, /* Character from file */
+ temp; /* Temporary character */
+ FILE *fp; /* Pointer to file */
+
+
+ /*
+ * Read a character from the file and see if it is EOF or ASCII...
+ */
+
+ fp = (FILE *)p;
+ ch = getc(fp);
+
+ if (ch == EOF)
+ return (EOF);
+
+ switch (*encoding)
+ {
+ case ENCODE_UTF8 :
+ /*
+ * Got a UTF-8 character; convert UTF-8 to Unicode and return...
+ */
+
+ if (!(ch & 0x80))
+ {
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+
+#if DEBUG > 1
+ printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+ }
+ else if (ch == 0xfe)
+ {
+ /*
+ * UTF-16 big-endian BOM?
+ */
+
+ ch = getc(fp);
+ if (ch != 0xff)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16BE;
+
+ return (mxml_file_getc(p, encoding));
+ }
+ else if (ch == 0xff)
+ {
+ /*
+ * UTF-16 little-endian BOM?
+ */
+
+ ch = getc(fp);
+ if (ch != 0xfe)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16LE;
+
+ return (mxml_file_getc(p, encoding));
+ }
+ else if ((ch & 0xe0) == 0xc0)
+ {
+ /*
+ * Two-byte value...
+ */
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
+
+ if (ch < 0x80)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+ }
+ else if ((ch & 0xf0) == 0xe0)
+ {
+ /*
+ * Three-byte value...
+ */
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if (ch < 0x800)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+
+ /*
+ * Ignore (strip) Byte Order Mark (BOM)...
+ */
+
+ if (ch == 0xfeff)
+ return (mxml_file_getc(p, encoding));
+ }
+ else if ((ch & 0xf8) == 0xf0)
+ {
+ /*
+ * Four-byte value...
+ */
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x07) << 6) | (temp & 0x3f);
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if (ch < 0x10000)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+ }
+ else
+ return (EOF);
+ break;
+
+ case ENCODE_UTF16BE :
+ /*
+ * Read UTF-16 big-endian char...
+ */
+
+ ch = (ch << 8) | getc(fp);
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch = (getc(fp) << 8);
+ lch |= getc(fp);
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+ break;
+
+ case ENCODE_UTF16LE :
+ /*
+ * Read UTF-16 little-endian char...
+ */
+
+ ch |= (getc(fp) << 8);
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch = getc(fp);
+ lch |= (getc(fp) << 8);
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+ break;
+ }
+
+#if DEBUG > 1
+ printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+}
+
+
+/*
+ * 'mxml_file_putc()' - Write a character to a file.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+mxml_file_putc(int ch, /* I - Character to write */
+ void *p) /* I - Pointer to file */
+{
+ return (putc(ch, (FILE *)p) == EOF ? -1 : 0);
+}
+
+
+/*
+ * 'mxml_get_entity()' - Get the character corresponding to an entity...
+ */
+
+static int /* O - Character value or EOF on error */
+mxml_get_entity(mxml_node_t *parent, /* I - Parent node */
+ void *p, /* I - Pointer to source */
+ int *encoding, /* IO - Character encoding */
+ int (*getc_cb)(void *, int *))
+ /* I - Get character function */
+{
+ int ch; /* Current character */
+ char entity[64], /* Entity string */
+ *entptr; /* Pointer into entity */
+
+
+ entptr = entity;
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ if (ch > 126 || (!isalnum(ch) && ch != '#'))
+ break;
+ else if (entptr < (entity + sizeof(entity) - 1))
+ *entptr++ = ch;
+ else
+ {
+ mxml_error("Entity name too long under parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ break;
+ }
+
+ *entptr = '\0';
+
+ if (ch != ';')
+ {
+ mxml_error("Character entity \"%s\" not terminated under parent <%s>!",
+ entity, parent ? parent->value.element.name : "null");
+ return (EOF);
+ }
+
+ if (entity[0] == '#')
+ {
+ if (entity[1] == 'x')
+ ch = strtol(entity + 2, NULL, 16);
+ else
+ ch = strtol(entity + 1, NULL, 10);
+ }
+ else if ((ch = mxmlEntityGetValue(entity)) < 0)
+ mxml_error("Entity name \"%s;\" not supported under parent <%s>!",
+ entity, parent ? parent->value.element.name : "null");
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!",
+ ch, parent ? parent->value.element.name : "null");
+ return (EOF);
+ }
+
+ return (ch);
+}
+
+
+/*
+ * 'mxml_load_data()' - Load data into an XML node tree.
+ */
+
+static mxml_node_t * /* O - First node or NULL if the file could not be read. */
+mxml_load_data(
+ mxml_node_t *top, /* I - Top node */
+ void *p, /* I - Pointer to data */
+ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
+ _mxml_getc_cb_t getc_cb, /* I - Read function */
+ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
+ void *sax_data) /* I - SAX user data */
+{
+ mxml_node_t *node, /* Current node */
+ *first, /* First node added */
+ *parent; /* Current parent node */
+ int ch, /* Character from file */
+ whitespace; /* Non-zero if whitespace seen */
+ char *buffer, /* String buffer */
+ *bufptr; /* Pointer into buffer */
+ int bufsize; /* Size of buffer */
+ mxml_type_t type; /* Current node type */
+ int encoding; /* Character encoding */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+ static const char * const types[] = /* Type strings... */
+ {
+ "MXML_ELEMENT", /* XML element with attributes */
+ "MXML_INTEGER", /* Integer value */
+ "MXML_OPAQUE", /* Opaque string */
+ "MXML_REAL", /* Real value */
+ "MXML_TEXT", /* Text fragment */
+ "MXML_CUSTOM" /* Custom data */
+ };
+
+
+ /*
+ * Read elements and other nodes from the file...
+ */
+
+ if ((buffer = malloc(64)) == NULL)
+ {
+ mxml_error("Unable to allocate string buffer!");
+ return (NULL);
+ }
+
+ bufsize = 64;
+ bufptr = buffer;
+ parent = top;
+ first = NULL;
+ whitespace = 0;
+ encoding = ENCODE_UTF8;
+
+ if (cb && parent)
+ type = (*cb)(parent);
+ else
+ type = MXML_TEXT;
+
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF)
+ {
+ if ((ch == '<' ||
+ (mxml_isspace(ch) && type != MXML_OPAQUE && type != MXML_CUSTOM)) &&
+ bufptr > buffer)
+ {
+ /*
+ * Add a new value node...
+ */
+
+ *bufptr = '\0';
+
+ switch (type)
+ {
+ case MXML_INTEGER :
+ node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0));
+ break;
+
+ case MXML_OPAQUE :
+ node = mxmlNewOpaque(parent, buffer);
+ break;
+
+ case MXML_REAL :
+ node = mxmlNewReal(parent, strtod(buffer, &bufptr));
+ break;
+
+ case MXML_TEXT :
+ node = mxmlNewText(parent, whitespace, buffer);
+ break;
+
+ case MXML_CUSTOM :
+ if (global->custom_load_cb)
+ {
+ /*
+ * Use the callback to fill in the custom data...
+ */
+
+ node = mxmlNewCustom(parent, NULL, NULL);
+
+ if ((*global->custom_load_cb)(node, buffer))
+ {
+ mxml_error("Bad custom value '%s' in parent <%s>!",
+ buffer, parent ? parent->value.element.name : "null");
+ mxmlDelete(node);
+ node = NULL;
+ }
+ break;
+ }
+
+ default : /* Ignore... */
+ node = NULL;
+ break;
+ }
+
+ if (*bufptr)
+ {
+ /*
+ * Bad integer/real number value...
+ */
+
+ mxml_error("Bad %s value '%s' in parent <%s>!",
+ type == MXML_INTEGER ? "integer" : "real", buffer,
+ parent ? parent->value.element.name : "null");
+ break;
+ }
+
+ bufptr = buffer;
+ whitespace = mxml_isspace(ch) && type == MXML_TEXT;
+
+ if (!node && type != MXML_IGNORE)
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Unable to add value node of type %s to parent <%s>!",
+ types[type], parent ? parent->value.element.name : "null");
+ goto error;
+ }
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_DATA, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (!first && node)
+ first = node;
+ }
+ else if (mxml_isspace(ch) && type == MXML_TEXT)
+ whitespace = 1;
+
+ /*
+ * Add lone whitespace node if we have an element and existing
+ * whitespace...
+ */
+
+ if (ch == '<' && whitespace && type == MXML_TEXT)
+ {
+ if (parent)
+ {
+ node = mxmlNewText(parent, whitespace, "");
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_DATA, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (!first && node)
+ first = node;
+ }
+
+ whitespace = 0;
+ }
+
+ if (ch == '<')
+ {
+ /*
+ * Start of open/close tag...
+ */
+
+ bufptr = buffer;
+
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF)
+ if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer))
+ break;
+ else if (ch == '<')
+ {
+ mxml_error("Bare < in element!");
+ goto error;
+ }
+ else if (ch == '&')
+ {
+ if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+ else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ else if (((bufptr - buffer) == 1 && buffer[0] == '?') ||
+ ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) ||
+ ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8)))
+ break;
+
+ *bufptr = '\0';
+
+ if (!strcmp(buffer, "!--"))
+ {
+ /*
+ * Gather rest of comment...
+ */
+
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF)
+ {
+ if (ch == '>' && bufptr > (buffer + 4) &&
+ bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-')
+ break;
+ else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+
+ /*
+ * Error out if we didn't get the whole comment...
+ */
+
+ if (ch != '>')
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Early EOF in comment node!");
+ goto error;
+ }
+
+
+ /*
+ * Otherwise add this as an element under the current parent...
+ */
+
+ *bufptr = '\0';
+
+ if (!parent && first)
+ {
+ /*
+ * There can only be one root element!
+ */
+
+ mxml_error("<%s> cannot be a second root node after <%s>",
+ buffer, first->value.element.name);
+ goto error;
+ }
+
+ if ((node = mxmlNewElement(parent, buffer)) == NULL)
+ {
+ /*
+ * Just print error for now...
+ */
+
+ mxml_error("Unable to add comment node to parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ break;
+ }
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_COMMENT, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (node && !first)
+ first = node;
+ }
+ else if (!strcmp(buffer, "![CDATA["))
+ {
+ /*
+ * Gather CDATA section...
+ */
+
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF)
+ {
+ if (ch == '>' && !strncmp(bufptr - 2, "]]", 2))
+ break;
+ else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+
+ /*
+ * Error out if we didn't get the whole comment...
+ */
+
+ if (ch != '>')
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Early EOF in CDATA node!");
+ goto error;
+ }
+
+
+ /*
+ * Otherwise add this as an element under the current parent...
+ */
+
+ *bufptr = '\0';
+
+ if (!parent && first)
+ {
+ /*
+ * There can only be one root element!
+ */
+
+ mxml_error("<%s> cannot be a second root node after <%s>",
+ buffer, first->value.element.name);
+ goto error;
+ }
+
+ if ((node = mxmlNewElement(parent, buffer)) == NULL)
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Unable to add CDATA node to parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ goto error;
+ }
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_CDATA, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (node && !first)
+ first = node;
+ }
+ else if (buffer[0] == '?')
+ {
+ /*
+ * Gather rest of processing instruction...
+ */
+
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF)
+ {
+ if (ch == '>' && bufptr > buffer && bufptr[-1] == '?')
+ break;
+ else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+
+ /*
+ * Error out if we didn't get the whole processing instruction...
+ */
+
+ if (ch != '>')
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Early EOF in processing instruction node!");
+ goto error;
+ }
+
+ /*
+ * Otherwise add this as an element under the current parent...
+ */
+
+ *bufptr = '\0';
+
+ if (!parent && first)
+ {
+ /*
+ * There can only be one root element!
+ */
+
+ mxml_error("<%s> cannot be a second root node after <%s>",
+ buffer, first->value.element.name);
+ goto error;
+ }
+
+ if ((node = mxmlNewElement(parent, buffer)) == NULL)
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Unable to add processing instruction node to parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ goto error;
+ }
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (node)
+ {
+ if (!first)
+ first = node;
+
+ if (!parent)
+ {
+ parent = node;
+
+ if (cb)
+ type = (*cb)(parent);
+ }
+ }
+ }
+ else if (buffer[0] == '!')
+ {
+ /*
+ * Gather rest of declaration...
+ */
+
+ do
+ {
+ if (ch == '>')
+ break;
+ else
+ {
+ if (ch == '&')
+ if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+ }
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF);
+
+ /*
+ * Error out if we didn't get the whole declaration...
+ */
+
+ if (ch != '>')
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Early EOF in declaration node!");
+ goto error;
+ }
+
+ /*
+ * Otherwise add this as an element under the current parent...
+ */
+
+ *bufptr = '\0';
+
+ if (!parent && first)
+ {
+ /*
+ * There can only be one root element!
+ */
+
+ mxml_error("<%s> cannot be a second root node after <%s>",
+ buffer, first->value.element.name);
+ goto error;
+ }
+
+ if ((node = mxmlNewElement(parent, buffer)) == NULL)
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Unable to add declaration node to parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ goto error;
+ }
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (node)
+ {
+ if (!first)
+ first = node;
+
+ if (!parent)
+ {
+ parent = node;
+
+ if (cb)
+ type = (*cb)(parent);
+ }
+ }
+ }
+ else if (buffer[0] == '/')
+ {
+ /*
+ * Handle close tag...
+ */
+
+ if (!parent || strcmp(buffer + 1, parent->value.element.name))
+ {
+ /*
+ * Close tag doesn't match tree; print an error for now...
+ */
+
+ mxml_error("Mismatched close tag <%s> under parent <%s>!",
+ buffer, parent ? parent->value.element.name : "(null)");
+ goto error;
+ }
+
+ /*
+ * Keep reading until we see >...
+ */
+
+ while (ch != '>' && ch != EOF)
+ ch = (*getc_cb)(p, &encoding);
+
+ node = parent;
+ parent = parent->parent;
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data);
+
+ if (!mxmlRelease(node) && first == node)
+ first = NULL;
+ }
+
+ /*
+ * Ascend into the parent and set the value type as needed...
+ */
+
+ if (cb && parent)
+ type = (*cb)(parent);
+ }
+ else
+ {
+ /*
+ * Handle open tag...
+ */
+
+ if (!parent && first)
+ {
+ /*
+ * There can only be one root element!
+ */
+
+ mxml_error("<%s> cannot be a second root node after <%s>",
+ buffer, first->value.element.name);
+ goto error;
+ }
+
+ if ((node = mxmlNewElement(parent, buffer)) == NULL)
+ {
+ /*
+ * Just print error for now...
+ */
+
+ mxml_error("Unable to add element node to parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ goto error;
+ }
+
+ if (mxml_isspace(ch))
+ {
+ if ((ch = mxml_parse_element(node, p, &encoding, getc_cb)) == EOF)
+ goto error;
+ }
+ else if (ch == '/')
+ {
+ if ((ch = (*getc_cb)(p, &encoding)) != '>')
+ {
+ mxml_error("Expected > but got '%c' instead for element <%s/>!",
+ ch, buffer);
+ mxmlDelete(node);
+ goto error;
+ }
+
+ ch = '/';
+ }
+
+ if (sax_cb)
+ (*sax_cb)(node, MXML_SAX_ELEMENT_OPEN, sax_data);
+
+ if (!first)
+ first = node;
+
+ if (ch == EOF)
+ break;
+
+ if (ch != '/')
+ {
+ /*
+ * Descend into this node, setting the value type as needed...
+ */
+
+ parent = node;
+
+ if (cb && parent)
+ type = (*cb)(parent);
+ }
+ else if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data);
+
+ if (!mxmlRelease(node) && first == node)
+ first = NULL;
+ }
+ }
+
+ bufptr = buffer;
+ }
+ else if (ch == '&')
+ {
+ /*
+ * Add character entity to current buffer...
+ */
+
+ if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+ else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !mxml_isspace(ch))
+ {
+ /*
+ * Add character to current buffer...
+ */
+
+ if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+ }
+
+ /*
+ * Free the string buffer - we don't need it anymore...
+ */
+
+ free(buffer);
+
+ /*
+ * Find the top element and return it...
+ */
+
+ if (parent)
+ {
+ node = parent;
+
+ while (parent->parent != top && parent->parent)
+ parent = parent->parent;
+
+ if (node != parent)
+ {
+ mxml_error("Missing close tag </%s> under parent <%s>!",
+ node->value.element.name,
+ node->parent ? node->parent->value.element.name : "(null)");
+
+ mxmlDelete(first);
+
+ return (NULL);
+ }
+ }
+
+ if (parent)
+ return (parent);
+ else
+ return (first);
+
+ /*
+ * Common error return...
+ */
+
+error:
+
+ mxmlDelete(first);
+
+ free(buffer);
+
+ return (NULL);
+}
+
+
+/*
+ * 'mxml_parse_element()' - Parse an element for any attributes...
+ */
+
+static int /* O - Terminating character */
+mxml_parse_element(
+ mxml_node_t *node, /* I - Element node */
+ void *p, /* I - Data to read from */
+ int *encoding, /* IO - Encoding */
+ _mxml_getc_cb_t getc_cb) /* I - Data callback */
+{
+ int ch, /* Current character in file */
+ quote; /* Quoting character */
+ char *name, /* Attribute name */
+ *value, /* Attribute value */
+ *ptr; /* Pointer into name/value */
+ int namesize, /* Size of name string */
+ valsize; /* Size of value string */
+
+
+ /*
+ * Initialize the name and value buffers...
+ */
+
+ if ((name = malloc(64)) == NULL)
+ {
+ mxml_error("Unable to allocate memory for name!");
+ return (EOF);
+ }
+
+ namesize = 64;
+
+ if ((value = malloc(64)) == NULL)
+ {
+ free(name);
+ mxml_error("Unable to allocate memory for value!");
+ return (EOF);
+ }
+
+ valsize = 64;
+
+ /*
+ * Loop until we hit a >, /, ?, or EOF...
+ */
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ {
+#if DEBUG > 1
+ fprintf(stderr, "parse_element: ch='%c'\n", ch);
+#endif /* DEBUG > 1 */
+
+ /*
+ * Skip leading whitespace...
+ */
+
+ if (mxml_isspace(ch))
+ continue;
+
+ /*
+ * Stop at /, ?, or >...
+ */
+
+ if (ch == '/' || ch == '?')
+ {
+ /*
+ * Grab the > character and print an error if it isn't there...
+ */
+
+ quote = (*getc_cb)(p, encoding);
+
+ if (quote != '>')
+ {
+ mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
+ ch, node->value.element.name, quote);
+ goto error;
+ }
+
+ break;
+ }
+ else if (ch == '<')
+ {
+ mxml_error("Bare < in element %s!", node->value.element.name);
+ goto error;
+ }
+ else if (ch == '>')
+ break;
+
+ /*
+ * Read the attribute name...
+ */
+
+ name[0] = ch;
+ ptr = name + 1;
+
+ if (ch == '\"' || ch == '\'')
+ {
+ /*
+ * Name is in quotes, so get a quoted string...
+ */
+
+ quote = ch;
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ {
+ if (ch == '&')
+ if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &ptr, &name, &namesize))
+ goto error;
+
+ if (ch == quote)
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Grab an normal, non-quoted name...
+ */
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' ||
+ ch == '?')
+ break;
+ else
+ {
+ if (ch == '&')
+ if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &ptr, &name, &namesize))
+ goto error;
+ }
+ }
+
+ *ptr = '\0';
+
+ if (mxmlElementGetAttr(node, name))
+ goto error;
+
+ while (ch != EOF && mxml_isspace(ch))
+ ch = (*getc_cb)(p, encoding);
+
+ if (ch == '=')
+ {
+ /*
+ * Read the attribute value...
+ */
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch));
+
+ if (ch == EOF)
+ {
+ mxml_error("Missing value for attribute '%s' in element %s!",
+ name, node->value.element.name);
+ goto error;
+ }
+
+ if (ch == '\'' || ch == '\"')
+ {
+ /*
+ * Read quoted value...
+ */
+
+ quote = ch;
+ ptr = value;
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ if (ch == quote)
+ break;
+ else
+ {
+ if (ch == '&')
+ if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &ptr, &value, &valsize))
+ goto error;
+ }
+
+ *ptr = '\0';
+ }
+ else
+ {
+ /*
+ * Read unquoted value...
+ */
+
+ value[0] = ch;
+ ptr = value + 1;
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>')
+ break;
+ else
+ {
+ if (ch == '&')
+ if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &ptr, &value, &valsize))
+ goto error;
+ }
+
+ *ptr = '\0';
+ }
+
+ /*
+ * Set the attribute with the given string value...
+ */
+
+ mxmlElementSetAttr(node, name, value);
+ }
+ else
+ {
+ mxml_error("Missing value for attribute '%s' in element %s!",
+ name, node->value.element.name);
+ goto error;
+ }
+
+ /*
+ * Check the end character...
+ */
+
+ if (ch == '/' || ch == '?')
+ {
+ /*
+ * Grab the > character and print an error if it isn't there...
+ */
+
+ quote = (*getc_cb)(p, encoding);
+
+ if (quote != '>')
+ {
+ mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
+ ch, node->value.element.name, quote);
+ ch = EOF;
+ }
+
+ break;
+ }
+ else if (ch == '>')
+ break;
+ }
+
+ /*
+ * Free the name and value buffers and return...
+ */
+
+ free(name);
+ free(value);
+
+ return (ch);
+
+ /*
+ * Common error return point...
+ */
+
+error:
+
+ free(name);
+ free(value);
+
+ return (EOF);
+}
+
+
+/*
+ * 'mxml_string_getc()' - Get a character from a string.
+ */
+
+static int /* O - Character or EOF */
+mxml_string_getc(void *p, /* I - Pointer to file */
+ int *encoding) /* IO - Encoding */
+{
+ int ch; /* Character */
+ const char **s; /* Pointer to string pointer */
+
+
+ s = (const char **)p;
+
+ if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE)
+ {
+ /*
+ * Got character; convert UTF-8 to integer and return...
+ */
+
+ (*s)++;
+
+ switch (*encoding)
+ {
+ case ENCODE_UTF8 :
+ if (!(ch & 0x80))
+ {
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+
+ return (ch);
+ }
+ else if (ch == 0xfe)
+ {
+ /*
+ * UTF-16 big-endian BOM?
+ */
+
+ if (((*s)[0] & 255) != 0xff)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16BE;
+ (*s)++;
+
+ return (mxml_string_getc(p, encoding));
+ }
+ else if (ch == 0xff)
+ {
+ /*
+ * UTF-16 little-endian BOM?
+ */
+
+ if (((*s)[0] & 255) != 0xfe)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16LE;
+ (*s)++;
+
+ return (mxml_string_getc(p, encoding));
+ }
+ else if ((ch & 0xe0) == 0xc0)
+ {
+ /*
+ * Two-byte value...
+ */
+
+ if (((*s)[0] & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f);
+
+ (*s)++;
+
+ if (ch < 0x80)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+ }
+ else if ((ch & 0xf0) == 0xe0)
+ {
+ /*
+ * Three-byte value...
+ */
+
+ if (((*s)[0] & 0xc0) != 0x80 ||
+ ((*s)[1] & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f);
+
+ (*s) += 2;
+
+ if (ch < 0x800)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+
+ /*
+ * Ignore (strip) Byte Order Mark (BOM)...
+ */
+
+ if (ch == 0xfeff)
+ return (mxml_string_getc(p, encoding));
+
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+ }
+ else if ((ch & 0xf8) == 0xf0)
+ {
+ /*
+ * Four-byte value...
+ */
+
+ if (((*s)[0] & 0xc0) != 0x80 ||
+ ((*s)[1] & 0xc0) != 0x80 ||
+ ((*s)[2] & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) |
+ ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f);
+
+ (*s) += 3;
+
+ if (ch < 0x10000)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+ }
+ else
+ return (EOF);
+
+ case ENCODE_UTF16BE :
+ /*
+ * Read UTF-16 big-endian char...
+ */
+
+ ch = (ch << 8) | ((*s)[0] & 255);
+ (*s) ++;
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch; /* Lower word */
+
+
+ if (!(*s)[0])
+ return (EOF);
+
+ lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255);
+ (*s) += 2;
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+
+ case ENCODE_UTF16LE :
+ /*
+ * Read UTF-16 little-endian char...
+ */
+
+ ch = ch | (((*s)[0] & 255) << 8);
+
+ if (!ch)
+ {
+ (*s) --;
+ return (EOF);
+ }
+
+ (*s) ++;
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch; /* Lower word */
+
+
+ if (!(*s)[1])
+ return (EOF);
+
+ lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255);
+ (*s) += 2;
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+ }
+ }
+
+ return (EOF);
+}
+
+
+/*
+ * 'mxml_string_putc()' - Write a character to a string.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+mxml_string_putc(int ch, /* I - Character to write */
+ void *p) /* I - Pointer to string pointers */
+{
+ char **pp; /* Pointer to string pointers */
+
+
+ pp = (char **)p;
+
+ if (pp[0] < pp[1])
+ pp[0][0] = ch;
+
+ pp[0] ++;
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_write_name()' - Write a name string.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+mxml_write_name(const char *s, /* I - Name to write */
+ void *p, /* I - Write pointer */
+ int (*putc_cb)(int, void *))
+ /* I - Write callback */
+{
+ char quote; /* Quote character */
+ const char *name; /* Entity name */
+
+
+ if (*s == '\"' || *s == '\'')
+ {
+ /*
+ * Write a quoted name string...
+ */
+
+ if ((*putc_cb)(*s, p) < 0)
+ return (-1);
+
+ quote = *s++;
+
+ while (*s && *s != quote)
+ {
+ if ((name = mxmlEntityGetName(*s)) != NULL)
+ {
+ if ((*putc_cb)('&', p) < 0)
+ return (-1);
+
+ while (*name)
+ {
+ if ((*putc_cb)(*name, p) < 0)
+ return (-1);
+
+ name ++;
+ }
+
+ if ((*putc_cb)(';', p) < 0)
+ return (-1);
+ }
+ else if ((*putc_cb)(*s, p) < 0)
+ return (-1);
+
+ s ++;
+ }
+
+ /*
+ * Write the end quote...
+ */
+
+ if ((*putc_cb)(quote, p) < 0)
+ return (-1);
+ }
+ else
+ {
+ /*
+ * Write a non-quoted name string...
+ */
+
+ while (*s)
+ {
+ if ((*putc_cb)(*s, p) < 0)
+ return (-1);
+
+ s ++;
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_write_node()' - Save an XML node to a file.
+ */
+
+static int /* O - Column or -1 on error */
+mxml_write_node(mxml_node_t *node, /* I - Node to write */
+ void *p, /* I - File to write to */
+ mxml_save_cb_t cb, /* I - Whitespace callback */
+ int col, /* I - Current column */
+ _mxml_putc_cb_t putc_cb,/* I - Output callback */
+ _mxml_global_t *global)/* I - Global data */
+{
+ int i, /* Looping var */
+ width; /* Width of attr + value */
+ mxml_attr_t *attr; /* Current attribute */
+ char s[255]; /* Temporary string */
+
+
+ /*
+ * Print the node value...
+ */
+
+ switch (node->type)
+ {
+ case MXML_ELEMENT :
+ col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
+
+ if ((*putc_cb)('<', p) < 0)
+ return (-1);
+ if (node->value.element.name[0] == '?' ||
+ !strncmp(node->value.element.name, "!--", 3) ||
+ !strncmp(node->value.element.name, "![CDATA[", 8))
+ {
+ /*
+ * Comments, CDATA, and processing instructions do not
+ * use character entities.
+ */
+
+ const char *ptr; /* Pointer into name */
+
+
+ for (ptr = node->value.element.name; *ptr; ptr ++)
+ if ((*putc_cb)(*ptr, p) < 0)
+ return (-1);
+ }
+ else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0)
+ return (-1);
+
+ col += strlen(node->value.element.name) + 1;
+
+ for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
+ i > 0;
+ i --, attr ++)
+ {
+ width = strlen(attr->name);
+
+ if (attr->value)
+ width += strlen(attr->value) + 3;
+
+ if (global->wrap > 0 && (col + width) > global->wrap)
+ {
+ if ((*putc_cb)('\n', p) < 0)
+ return (-1);
+
+ col = 0;
+ }
+ else
+ {
+ if ((*putc_cb)(' ', p) < 0)
+ return (-1);
+
+ col ++;
+ }
+
+ if (mxml_write_name(attr->name, p, putc_cb) < 0)
+ return (-1);
+
+ if (attr->value)
+ {
+ if ((*putc_cb)('=', p) < 0)
+ return (-1);
+ if ((*putc_cb)('\"', p) < 0)
+ return (-1);
+ if (mxml_write_string(attr->value, p, putc_cb) < 0)
+ return (-1);
+ if ((*putc_cb)('\"', p) < 0)
+ return (-1);
+ }
+
+ col += width;
+ }
+
+ if (node->child)
+ {
+ /*
+ * Write children...
+ */
+
+ mxml_node_t *child; /* Current child */
+
+
+ if ((*putc_cb)('>', p) < 0)
+ return (-1);
+ else
+ col ++;
+
+ col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
+
+ for (child = node->child; child; child = child->next)
+ {
+ if ((col = mxml_write_node(child, p, cb, col, putc_cb, global)) < 0)
+ return (-1);
+ }
+
+ /*
+ * The ? and ! elements are special-cases and have no end tags...
+ */
+
+ if (node->value.element.name[0] != '!' &&
+ node->value.element.name[0] != '?')
+ {
+ col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb);
+
+ if ((*putc_cb)('<', p) < 0)
+ return (-1);
+ if ((*putc_cb)('/', p) < 0)
+ return (-1);
+ if (mxml_write_string(node->value.element.name, p, putc_cb) < 0)
+ return (-1);
+ if ((*putc_cb)('>', p) < 0)
+ return (-1);
+
+ col += strlen(node->value.element.name) + 3;
+
+ col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb);
+ }
+ }
+ else if (node->value.element.name[0] == '!' ||
+ node->value.element.name[0] == '?')
+ {
+ /*
+ * The ? and ! elements are special-cases...
+ */
+
+ if ((*putc_cb)('>', p) < 0)
+ return (-1);
+ else
+ col ++;
+
+ col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
+ }
+ else
+ {
+ if ((*putc_cb)(' ', p) < 0)
+ return (-1);
+ if ((*putc_cb)('/', p) < 0)
+ return (-1);
+ if ((*putc_cb)('>', p) < 0)
+ return (-1);
+
+ col += 3;
+
+ col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
+ }
+ break;
+
+ case MXML_INTEGER :
+ if (node->prev)
+ {
+ if (global->wrap > 0 && col > global->wrap)
+ {
+ if ((*putc_cb)('\n', p) < 0)
+ return (-1);
+
+ col = 0;
+ }
+ else if ((*putc_cb)(' ', p) < 0)
+ return (-1);
+ else
+ col ++;
+ }
+
+ sprintf(s, "%d", node->value.integer);
+ if (mxml_write_string(s, p, putc_cb) < 0)
+ return (-1);
+
+ col += strlen(s);
+ break;
+
+ case MXML_OPAQUE :
+ if (mxml_write_string(node->value.opaque, p, putc_cb) < 0)
+ return (-1);
+
+ col += strlen(node->value.opaque);
+ break;
+
+ case MXML_REAL :
+ if (node->prev)
+ {
+ if (global->wrap > 0 && col > global->wrap)
+ {
+ if ((*putc_cb)('\n', p) < 0)
+ return (-1);
+
+ col = 0;
+ }
+ else if ((*putc_cb)(' ', p) < 0)
+ return (-1);
+ else
+ col ++;
+ }
+
+ sprintf(s, "%f", node->value.real);
+ if (mxml_write_string(s, p, putc_cb) < 0)
+ return (-1);
+
+ col += strlen(s);
+ break;
+
+ case MXML_TEXT :
+ if (node->value.text.whitespace && col > 0)
+ {
+ if (global->wrap > 0 && col > global->wrap)
+ {
+ if ((*putc_cb)('\n', p) < 0)
+ return (-1);
+
+ col = 0;
+ }
+ else if ((*putc_cb)(' ', p) < 0)
+ return (-1);
+ else
+ col ++;
+ }
+
+ if (mxml_write_string(node->value.text.string, p, putc_cb) < 0)
+ return (-1);
+
+ col += strlen(node->value.text.string);
+ break;
+
+ case MXML_CUSTOM :
+ if (global->custom_save_cb)
+ {
+ char *data; /* Custom data string */
+ const char *newline; /* Last newline in string */
+
+
+ if ((data = (*global->custom_save_cb)(node)) == NULL)
+ return (-1);
+
+ if (mxml_write_string(data, p, putc_cb) < 0)
+ return (-1);
+
+ if ((newline = strrchr(data, '\n')) == NULL)
+ col += strlen(data);
+ else
+ col = strlen(newline);
+
+ free(data);
+ break;
+ }
+
+ default : /* Should never happen */
+ return (-1);
+ }
+
+ return (col);
+}
+
+
+/*
+ * 'mxml_write_string()' - Write a string, escaping & and < as needed.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+mxml_write_string(
+ const char *s, /* I - String to write */
+ void *p, /* I - Write pointer */
+ _mxml_putc_cb_t putc_cb) /* I - Write callback */
+{
+ const char *name; /* Entity name, if any */
+
+
+ while (*s)
+ {
+ if ((name = mxmlEntityGetName(*s)) != NULL)
+ {
+ if ((*putc_cb)('&', p) < 0)
+ return (-1);
+
+ while (*name)
+ {
+ if ((*putc_cb)(*name, p) < 0)
+ return (-1);
+ name ++;
+ }
+
+ if ((*putc_cb)(';', p) < 0)
+ return (-1);
+ }
+ else if ((*putc_cb)(*s, p) < 0)
+ return (-1);
+
+ s ++;
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_write_ws()' - Do whitespace callback...
+ */
+
+static int /* O - New column */
+mxml_write_ws(mxml_node_t *node, /* I - Current node */
+ void *p, /* I - Write pointer */
+ mxml_save_cb_t cb, /* I - Callback function */
+ int ws, /* I - Where value */
+ int col, /* I - Current column */
+ _mxml_putc_cb_t putc_cb) /* I - Write callback */
+{
+ const char *s; /* Whitespace string */
+
+
+ if (cb && (s = (*cb)(node, ws)) != NULL)
+ {
+ while (*s)
+ {
+ if ((*putc_cb)(*s, p) < 0)
+ return (-1);
+ else if (*s == '\n')
+ col = 0;
+ else if (*s == '\t')
+ {
+ col += MXML_TAB;
+ col = col - (col % MXML_TAB);
+ }
+ else
+ col ++;
+
+ s ++;
+ }
+ }
+
+ return (col);
+}
+
+
+/*
+ * End of "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-get.c b/tools/gator/daemon/mxml/mxml-get.c
new file mode 100644
index 000000000000..a5356d57e186
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-get.c
@@ -0,0 +1,471 @@
+/*
+ * "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $"
+ *
+ * Node get functions for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlGetCDATA() - Get the value for a CDATA node.
+ * mxmlGetCustom() - Get the value for a custom node.
+ * mxmlGetElement() - Get the name for an element node.
+ * mxmlGetFirstChild() - Get the first child of an element node.
+ * mxmlGetInteger() - Get the integer value from the specified node or its
+ * first child.
+ * mxmlGetLastChild() - Get the last child of an element node.
+ * mxmlGetNextSibling() - Get the next node for the current parent.
+ * mxmlGetOpaque() - Get an opaque string value for a node or its first
+ * child.
+ * mxmlGetParent() - Get the parent node.
+ * mxmlGetPrevSibling() - Get the previous node for the current parent.
+ * mxmlGetReal() - Get the real value for a node or its first child.
+ * mxmlGetText() - Get the text value for a node or its first child.
+ * mxmlGetType() - Get the node type.
+ * mxmlGetUserData() - Get the user data pointer for a node.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * 'mxmlGetCDATA()' - Get the value for a CDATA node.
+ *
+ * @code NULL@ is returned if the node is not a CDATA element.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+const char * /* O - CDATA value or NULL */
+mxmlGetCDATA(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT ||
+ strncmp(node->value.element.name, "![CDATA[", 8))
+ return (NULL);
+
+ /*
+ * Return the text following the CDATA declaration...
+ */
+
+ return (node->value.element.name + 8);
+}
+
+
+/*
+ * 'mxmlGetCustom()' - Get the value for a custom node.
+ *
+ * @code NULL@ is returned if the node (or its first child) is not a custom
+ * value node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+const void * /* O - Custom value or NULL */
+mxmlGetCustom(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the integer value...
+ */
+
+ if (node->type == MXML_CUSTOM)
+ return (node->value.custom.data);
+ else if (node->type == MXML_ELEMENT &&
+ node->child &&
+ node->child->type == MXML_CUSTOM)
+ return (node->child->value.custom.data);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlGetElement()' - Get the name for an element node.
+ *
+ * @code NULL@ is returned if the node is not an element node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+const char * /* O - Element name or NULL */
+mxmlGetElement(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT)
+ return (NULL);
+
+ /*
+ * Return the element name...
+ */
+
+ return (node->value.element.name);
+}
+
+
+/*
+ * 'mxmlGetFirstChild()' - Get the first child of an element node.
+ *
+ * @code NULL@ is returned if the node is not an element node or if the node
+ * has no children.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t * /* O - First child or NULL */
+mxmlGetFirstChild(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT)
+ return (NULL);
+
+ /*
+ * Return the first child node...
+ */
+
+ return (node->child);
+}
+
+
+/*
+ * 'mxmlGetInteger()' - Get the integer value from the specified node or its
+ * first child.
+ *
+ * 0 is returned if the node (or its first child) is not an integer value node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+int /* O - Integer value or 0 */
+mxmlGetInteger(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (0);
+
+ /*
+ * Return the integer value...
+ */
+
+ if (node->type == MXML_INTEGER)
+ return (node->value.integer);
+ else if (node->type == MXML_ELEMENT &&
+ node->child &&
+ node->child->type == MXML_INTEGER)
+ return (node->child->value.integer);
+ else
+ return (0);
+}
+
+
+/*
+ * 'mxmlGetLastChild()' - Get the last child of an element node.
+ *
+ * @code NULL@ is returned if the node is not an element node or if the node
+ * has no children.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t * /* O - Last child or NULL */
+mxmlGetLastChild(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT)
+ return (NULL);
+
+ /*
+ * Return the node type...
+ */
+
+ return (node->last_child);
+}
+
+
+/*
+ * 'mxmlGetNextSibling()' - Get the next node for the current parent.
+ *
+ * @code NULL@ is returned if this is the last child for the current parent.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t *
+mxmlGetNextSibling(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the node type...
+ */
+
+ return (node->next);
+}
+
+
+/*
+ * 'mxmlGetOpaque()' - Get an opaque string value for a node or its first child.
+ *
+ * @code NULL@ is returned if the node (or its first child) is not an opaque
+ * value node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+const char * /* O - Opaque string or NULL */
+mxmlGetOpaque(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the integer value...
+ */
+
+ if (node->type == MXML_OPAQUE)
+ return (node->value.opaque);
+ else if (node->type == MXML_ELEMENT &&
+ node->child &&
+ node->child->type == MXML_OPAQUE)
+ return (node->child->value.opaque);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlGetParent()' - Get the parent node.
+ *
+ * @code NULL@ is returned for a root node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t * /* O - Parent node or NULL */
+mxmlGetParent(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the node type...
+ */
+
+ return (node->parent);
+}
+
+
+/*
+ * 'mxmlGetPrevSibling()' - Get the previous node for the current parent.
+ *
+ * @code NULL@ is returned if this is the first child for the current parent.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t * /* O - Previous node or NULL */
+mxmlGetPrevSibling(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the node type...
+ */
+
+ return (node->prev);
+}
+
+
+/*
+ * 'mxmlGetReal()' - Get the real value for a node or its first child.
+ *
+ * 0.0 is returned if the node (or its first child) is not a real value node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+double /* O - Real value or 0.0 */
+mxmlGetReal(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (0.0);
+
+ /*
+ * Return the integer value...
+ */
+
+ if (node->type == MXML_REAL)
+ return (node->value.real);
+ else if (node->type == MXML_ELEMENT &&
+ node->child &&
+ node->child->type == MXML_REAL)
+ return (node->child->value.real);
+ else
+ return (0.0);
+}
+
+
+/*
+ * 'mxmlGetText()' - Get the text value for a node or its first child.
+ *
+ * @code NULL@ is returned if the node (or its first child) is not a text node.
+ * The "whitespace" argument can be NULL.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+const char * /* O - Text string or NULL */
+mxmlGetText(mxml_node_t *node, /* I - Node to get */
+ int *whitespace) /* O - 1 if string is preceded by whitespace, 0 otherwise */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ {
+ if (whitespace)
+ *whitespace = 0;
+
+ return (NULL);
+ }
+
+ /*
+ * Return the integer value...
+ */
+
+ if (node->type == MXML_TEXT)
+ {
+ if (whitespace)
+ *whitespace = node->value.text.whitespace;
+
+ return (node->value.text.string);
+ }
+ else if (node->type == MXML_ELEMENT &&
+ node->child &&
+ node->child->type == MXML_TEXT)
+ {
+ if (whitespace)
+ *whitespace = node->child->value.text.whitespace;
+
+ return (node->child->value.text.string);
+ }
+ else
+ {
+ if (whitespace)
+ *whitespace = 0;
+
+ return (NULL);
+ }
+}
+
+
+/*
+ * 'mxmlGetType()' - Get the node type.
+ *
+ * @code MXML_IGNORE@ is returned if "node" is @code NULL@.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_type_t /* O - Type of node */
+mxmlGetType(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (MXML_IGNORE);
+
+ /*
+ * Return the node type...
+ */
+
+ return (node->type);
+}
+
+
+/*
+ * 'mxmlGetUserData()' - Get the user data pointer for a node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+void * /* O - User data pointer */
+mxmlGetUserData(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the user data pointer...
+ */
+
+ return (node->user_data);
+}
+
+
+/*
+ * End of "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-index.c b/tools/gator/daemon/mxml/mxml-index.c
new file mode 100644
index 000000000000..b6efc66f055c
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-index.c
@@ -0,0 +1,662 @@
+/*
+ * "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $"
+ *
+ * Index support code for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * Sort functions...
+ */
+
+static int index_compare(mxml_index_t *ind, mxml_node_t *first,
+ mxml_node_t *second);
+static int index_find(mxml_index_t *ind, const char *element,
+ const char *value, mxml_node_t *node);
+static void index_sort(mxml_index_t *ind, int left, int right);
+
+
+/*
+ * 'mxmlIndexDelete()' - Delete an index.
+ */
+
+void
+mxmlIndexDelete(mxml_index_t *ind) /* I - Index to delete */
+{
+ /*
+ * Range check input..
+ */
+
+ if (!ind)
+ return;
+
+ /*
+ * Free memory...
+ */
+
+ if (ind->attr)
+ free(ind->attr);
+
+ if (ind->alloc_nodes)
+ free(ind->nodes);
+
+ free(ind);
+}
+
+
+/*
+ * 'mxmlIndexEnum()' - Return the next node in the index.
+ *
+ * Nodes are returned in the sorted order of the index.
+ */
+
+mxml_node_t * /* O - Next node or NULL if there is none */
+mxmlIndexEnum(mxml_index_t *ind) /* I - Index to enumerate */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ind)
+ return (NULL);
+
+ /*
+ * Return the next node...
+ */
+
+ if (ind->cur_node < ind->num_nodes)
+ return (ind->nodes[ind->cur_node ++]);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlIndexFind()' - Find the next matching node.
+ *
+ * You should call mxmlIndexReset() prior to using this function for
+ * the first time with a particular set of "element" and "value"
+ * strings. Passing NULL for both "element" and "value" is equivalent
+ * to calling mxmlIndexEnum().
+ */
+
+mxml_node_t * /* O - Node or NULL if none found */
+mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */
+ const char *element, /* I - Element name to find, if any */
+ const char *value) /* I - Attribute value, if any */
+{
+ int diff, /* Difference between names */
+ current, /* Current entity in search */
+ first, /* First entity in search */
+ last; /* Last entity in search */
+
+
+#ifdef DEBUG
+ printf("mxmlIndexFind(ind=%p, element=\"%s\", value=\"%s\")\n",
+ ind, element ? element : "(null)", value ? value : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!ind || (!ind->attr && value))
+ {
+#ifdef DEBUG
+ puts(" returning NULL...");
+ printf(" ind->attr=\"%s\"\n", ind->attr ? ind->attr : "(null)");
+#endif /* DEBUG */
+
+ return (NULL);
+ }
+
+ /*
+ * If both element and value are NULL, just enumerate the nodes in the
+ * index...
+ */
+
+ if (!element && !value)
+ return (mxmlIndexEnum(ind));
+
+ /*
+ * If there are no nodes in the index, return NULL...
+ */
+
+ if (!ind->num_nodes)
+ {
+#ifdef DEBUG
+ puts(" returning NULL...");
+ puts(" no nodes!");
+#endif /* DEBUG */
+
+ return (NULL);
+ }
+
+ /*
+ * If cur_node == 0, then find the first matching node...
+ */
+
+ if (ind->cur_node == 0)
+ {
+ /*
+ * Find the first node using a modified binary search algorithm...
+ */
+
+ first = 0;
+ last = ind->num_nodes - 1;
+
+#ifdef DEBUG
+ printf(" find first time, num_nodes=%d...\n", ind->num_nodes);
+#endif /* DEBUG */
+
+ while ((last - first) > 1)
+ {
+ current = (first + last) / 2;
+
+#ifdef DEBUG
+ printf(" first=%d, last=%d, current=%d\n", first, last, current);
+#endif /* DEBUG */
+
+ if ((diff = index_find(ind, element, value, ind->nodes[current])) == 0)
+ {
+ /*
+ * Found a match, move back to find the first...
+ */
+
+#ifdef DEBUG
+ puts(" match!");
+#endif /* DEBUG */
+
+ while (current > 0 &&
+ !index_find(ind, element, value, ind->nodes[current - 1]))
+ current --;
+
+#ifdef DEBUG
+ printf(" returning first match=%d\n", current);
+#endif /* DEBUG */
+
+ /*
+ * Return the first match and save the index to the next...
+ */
+
+ ind->cur_node = current + 1;
+
+ return (ind->nodes[current]);
+ }
+ else if (diff < 0)
+ last = current;
+ else
+ first = current;
+
+#ifdef DEBUG
+ printf(" diff=%d\n", diff);
+#endif /* DEBUG */
+ }
+
+ /*
+ * If we get this far, then we found exactly 0 or 1 matches...
+ */
+
+ for (current = first; current <= last; current ++)
+ if (!index_find(ind, element, value, ind->nodes[current]))
+ {
+ /*
+ * Found exactly one (or possibly two) match...
+ */
+
+#ifdef DEBUG
+ printf(" returning only match %d...\n", current);
+#endif /* DEBUG */
+
+ ind->cur_node = current + 1;
+
+ return (ind->nodes[current]);
+ }
+
+ /*
+ * No matches...
+ */
+
+ ind->cur_node = ind->num_nodes;
+
+#ifdef DEBUG
+ puts(" returning NULL...");
+#endif /* DEBUG */
+
+ return (NULL);
+ }
+ else if (ind->cur_node < ind->num_nodes &&
+ !index_find(ind, element, value, ind->nodes[ind->cur_node]))
+ {
+ /*
+ * Return the next matching node...
+ */
+
+#ifdef DEBUG
+ printf(" returning next match %d...\n", ind->cur_node);
+#endif /* DEBUG */
+
+ return (ind->nodes[ind->cur_node ++]);
+ }
+
+ /*
+ * If we get this far, then we have no matches...
+ */
+
+ ind->cur_node = ind->num_nodes;
+
+#ifdef DEBUG
+ puts(" returning NULL...");
+#endif /* DEBUG */
+
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlIndexGetCount()' - Get the number of nodes in an index.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+int /* I - Number of nodes in index */
+mxmlIndexGetCount(mxml_index_t *ind) /* I - Index of nodes */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ind)
+ return (0);
+
+ /*
+ * Return the number of nodes in the index...
+ */
+
+ return (ind->num_nodes);
+}
+
+
+/*
+ * 'mxmlIndexNew()' - Create a new index.
+ *
+ * The index will contain all nodes that contain the named element and/or
+ * attribute. If both "element" and "attr" are NULL, then the index will
+ * contain a sorted list of the elements in the node tree. Nodes are
+ * sorted by element name and optionally by attribute value if the "attr"
+ * argument is not NULL.
+ */
+
+mxml_index_t * /* O - New index */
+mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */
+ const char *element, /* I - Element to index or NULL for all */
+ const char *attr) /* I - Attribute to index or NULL for none */
+{
+ mxml_index_t *ind; /* New index */
+ mxml_node_t *current, /* Current node in index */
+ **temp; /* Temporary node pointer array */
+
+
+ /*
+ * Range check input...
+ */
+
+#ifdef DEBUG
+ printf("mxmlIndexNew(node=%p, element=\"%s\", attr=\"%s\")\n",
+ node, element ? element : "(null)", attr ? attr : "(null)");
+#endif /* DEBUG */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Create a new index...
+ */
+
+ if ((ind = calloc(1, sizeof(mxml_index_t))) == NULL)
+ {
+ mxml_error("Unable to allocate %d bytes for index - %s",
+ sizeof(mxml_index_t), strerror(errno));
+ return (NULL);
+ }
+
+ if (attr)
+ ind->attr = strdup(attr);
+
+ if (!element && !attr)
+ current = node;
+ else
+ current = mxmlFindElement(node, node, element, attr, NULL, MXML_DESCEND);
+
+ while (current)
+ {
+ if (ind->num_nodes >= ind->alloc_nodes)
+ {
+ if (!ind->alloc_nodes)
+ temp = malloc(64 * sizeof(mxml_node_t *));
+ else
+ temp = realloc(ind->nodes, (ind->alloc_nodes + 64) * sizeof(mxml_node_t *));
+
+ if (!temp)
+ {
+ /*
+ * Unable to allocate memory for the index, so abort...
+ */
+
+ mxml_error("Unable to allocate %d bytes for index: %s",
+ (ind->alloc_nodes + 64) * sizeof(mxml_node_t *),
+ strerror(errno));
+
+ mxmlIndexDelete(ind);
+ return (NULL);
+ }
+
+ ind->nodes = temp;
+ ind->alloc_nodes += 64;
+ }
+
+ ind->nodes[ind->num_nodes ++] = current;
+
+ current = mxmlFindElement(current, node, element, attr, NULL, MXML_DESCEND);
+ }
+
+ /*
+ * Sort nodes based upon the search criteria...
+ */
+
+#ifdef DEBUG
+ {
+ int i; /* Looping var */
+
+
+ printf("%d node(s) in index.\n\n", ind->num_nodes);
+
+ if (attr)
+ {
+ printf("Node Address Element %s\n", attr);
+ puts("-------- -------- -------------- ------------------------------");
+
+ for (i = 0; i < ind->num_nodes; i ++)
+ printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i],
+ ind->nodes[i]->value.element.name,
+ mxmlElementGetAttr(ind->nodes[i], attr));
+ }
+ else
+ {
+ puts("Node Address Element");
+ puts("-------- -------- --------------");
+
+ for (i = 0; i < ind->num_nodes; i ++)
+ printf("%8d %-8p %s\n", i, ind->nodes[i],
+ ind->nodes[i]->value.element.name);
+ }
+
+ putchar('\n');
+ }
+#endif /* DEBUG */
+
+ if (ind->num_nodes > 1)
+ index_sort(ind, 0, ind->num_nodes - 1);
+
+#ifdef DEBUG
+ {
+ int i; /* Looping var */
+
+
+ puts("After sorting:\n");
+
+ if (attr)
+ {
+ printf("Node Address Element %s\n", attr);
+ puts("-------- -------- -------------- ------------------------------");
+
+ for (i = 0; i < ind->num_nodes; i ++)
+ printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i],
+ ind->nodes[i]->value.element.name,
+ mxmlElementGetAttr(ind->nodes[i], attr));
+ }
+ else
+ {
+ puts("Node Address Element");
+ puts("-------- -------- --------------");
+
+ for (i = 0; i < ind->num_nodes; i ++)
+ printf("%8d %-8p %s\n", i, ind->nodes[i],
+ ind->nodes[i]->value.element.name);
+ }
+
+ putchar('\n');
+ }
+#endif /* DEBUG */
+
+ /*
+ * Return the new index...
+ */
+
+ return (ind);
+}
+
+
+/*
+ * 'mxmlIndexReset()' - Reset the enumeration/find pointer in the index and
+ * return the first node in the index.
+ *
+ * This function should be called prior to using mxmlIndexEnum() or
+ * mxmlIndexFind() for the first time.
+ */
+
+mxml_node_t * /* O - First node or NULL if there is none */
+mxmlIndexReset(mxml_index_t *ind) /* I - Index to reset */
+{
+#ifdef DEBUG
+ printf("mxmlIndexReset(ind=%p)\n", ind);
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!ind)
+ return (NULL);
+
+ /*
+ * Set the index to the first element...
+ */
+
+ ind->cur_node = 0;
+
+ /*
+ * Return the first node...
+ */
+
+ if (ind->num_nodes)
+ return (ind->nodes[0]);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'index_compare()' - Compare two nodes.
+ */
+
+static int /* O - Result of comparison */
+index_compare(mxml_index_t *ind, /* I - Index */
+ mxml_node_t *first, /* I - First node */
+ mxml_node_t *second) /* I - Second node */
+{
+ int diff; /* Difference */
+
+
+ /*
+ * Check the element name...
+ */
+
+ if ((diff = strcmp(first->value.element.name,
+ second->value.element.name)) != 0)
+ return (diff);
+
+ /*
+ * Check the attribute value...
+ */
+
+ if (ind->attr)
+ {
+ if ((diff = strcmp(mxmlElementGetAttr(first, ind->attr),
+ mxmlElementGetAttr(second, ind->attr))) != 0)
+ return (diff);
+ }
+
+ /*
+ * No difference, return 0...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'index_find()' - Compare a node with index values.
+ */
+
+static int /* O - Result of comparison */
+index_find(mxml_index_t *ind, /* I - Index */
+ const char *element, /* I - Element name or NULL */
+ const char *value, /* I - Attribute value or NULL */
+ mxml_node_t *node) /* I - Node */
+{
+ int diff; /* Difference */
+
+
+ /*
+ * Check the element name...
+ */
+
+ if (element)
+ {
+ if ((diff = strcmp(element, node->value.element.name)) != 0)
+ return (diff);
+ }
+
+ /*
+ * Check the attribute value...
+ */
+
+ if (value)
+ {
+ if ((diff = strcmp(value, mxmlElementGetAttr(node, ind->attr))) != 0)
+ return (diff);
+ }
+
+ /*
+ * No difference, return 0...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'index_sort()' - Sort the nodes in the index...
+ *
+ * This function implements the classic quicksort algorithm...
+ */
+
+static void
+index_sort(mxml_index_t *ind, /* I - Index to sort */
+ int left, /* I - Left node in partition */
+ int right) /* I - Right node in partition */
+{
+ mxml_node_t *pivot, /* Pivot node */
+ *temp; /* Swap node */
+ int templ, /* Temporary left node */
+ tempr; /* Temporary right node */
+
+
+ /*
+ * Loop until we have sorted all the way to the right...
+ */
+
+ do
+ {
+ /*
+ * Sort the pivot in the current partition...
+ */
+
+ pivot = ind->nodes[left];
+
+ for (templ = left, tempr = right; templ < tempr;)
+ {
+ /*
+ * Move left while left node <= pivot node...
+ */
+
+ while ((templ < right) &&
+ index_compare(ind, ind->nodes[templ], pivot) <= 0)
+ templ ++;
+
+ /*
+ * Move right while right node > pivot node...
+ */
+
+ while ((tempr > left) &&
+ index_compare(ind, ind->nodes[tempr], pivot) > 0)
+ tempr --;
+
+ /*
+ * Swap nodes if needed...
+ */
+
+ if (templ < tempr)
+ {
+ temp = ind->nodes[templ];
+ ind->nodes[templ] = ind->nodes[tempr];
+ ind->nodes[tempr] = temp;
+ }
+ }
+
+ /*
+ * When we get here, the right (tempr) node is the new position for the
+ * pivot node...
+ */
+
+ if (index_compare(ind, pivot, ind->nodes[tempr]) > 0)
+ {
+ ind->nodes[left] = ind->nodes[tempr];
+ ind->nodes[tempr] = pivot;
+ }
+
+ /*
+ * Recursively sort the left partition as needed...
+ */
+
+ if (left < (tempr - 1))
+ index_sort(ind, left, tempr - 1);
+ }
+ while (right > (left = tempr + 1));
+}
+
+
+/*
+ * End of "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-node.c b/tools/gator/daemon/mxml/mxml-node.c
new file mode 100644
index 000000000000..44af759f9de3
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-node.c
@@ -0,0 +1,807 @@
+/*
+ * "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $"
+ *
+ * Node support code for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlAdd() - Add a node to a tree.
+ * mxmlDelete() - Delete a node and all of its children.
+ * mxmlGetRefCount() - Get the current reference (use) count for a node.
+ * mxmlNewCDATA() - Create a new CDATA node.
+ * mxmlNewCustom() - Create a new custom data node.
+ * mxmlNewElement() - Create a new element node.
+ * mxmlNewInteger() - Create a new integer node.
+ * mxmlNewOpaque() - Create a new opaque string.
+ * mxmlNewReal() - Create a new real number node.
+ * mxmlNewText() - Create a new text fragment node.
+ * mxmlNewTextf() - Create a new formatted text fragment node.
+ * mxmlRemove() - Remove a node from its parent.
+ * mxmlNewXML() - Create a new XML document tree.
+ * mxmlRelease() - Release a node.
+ * mxmlRetain() - Retain a node.
+ * mxml_new() - Create a new node.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * Local functions...
+ */
+
+static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type);
+
+
+/*
+ * 'mxmlAdd()' - Add a node to a tree.
+ *
+ * Adds the specified node to the parent. If the child argument is not
+ * NULL, puts the new node before or after the specified child depending
+ * on the value of the where argument. If the child argument is NULL,
+ * puts the new node at the beginning of the child list (MXML_ADD_BEFORE)
+ * or at the end of the child list (MXML_ADD_AFTER). The constant
+ * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer.
+ */
+
+void
+mxmlAdd(mxml_node_t *parent, /* I - Parent node */
+ int where, /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */
+ mxml_node_t *child, /* I - Child node for where or MXML_ADD_TO_PARENT */
+ mxml_node_t *node) /* I - Node to add */
+{
+#ifdef DEBUG
+ fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent,
+ where, child, node);
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!parent || !node)
+ return;
+
+#if DEBUG > 1
+ fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent);
+ if (parent)
+ {
+ fprintf(stderr, " BEFORE: parent->child=%p\n", parent->child);
+ fprintf(stderr, " BEFORE: parent->last_child=%p\n", parent->last_child);
+ fprintf(stderr, " BEFORE: parent->prev=%p\n", parent->prev);
+ fprintf(stderr, " BEFORE: parent->next=%p\n", parent->next);
+ }
+#endif /* DEBUG > 1 */
+
+ /*
+ * Remove the node from any existing parent...
+ */
+
+ if (node->parent)
+ mxmlRemove(node);
+
+ /*
+ * Reset pointers...
+ */
+
+ node->parent = parent;
+
+ switch (where)
+ {
+ case MXML_ADD_BEFORE :
+ if (!child || child == parent->child || child->parent != parent)
+ {
+ /*
+ * Insert as first node under parent...
+ */
+
+ node->next = parent->child;
+
+ if (parent->child)
+ parent->child->prev = node;
+ else
+ parent->last_child = node;
+
+ parent->child = node;
+ }
+ else
+ {
+ /*
+ * Insert node before this child...
+ */
+
+ node->next = child;
+ node->prev = child->prev;
+
+ if (child->prev)
+ child->prev->next = node;
+ else
+ parent->child = node;
+
+ child->prev = node;
+ }
+ break;
+
+ case MXML_ADD_AFTER :
+ if (!child || child == parent->last_child || child->parent != parent)
+ {
+ /*
+ * Insert as last node under parent...
+ */
+
+ node->parent = parent;
+ node->prev = parent->last_child;
+
+ if (parent->last_child)
+ parent->last_child->next = node;
+ else
+ parent->child = node;
+
+ parent->last_child = node;
+ }
+ else
+ {
+ /*
+ * Insert node after this child...
+ */
+
+ node->prev = child;
+ node->next = child->next;
+
+ if (child->next)
+ child->next->prev = node;
+ else
+ parent->last_child = node;
+
+ child->next = node;
+ }
+ break;
+ }
+
+#if DEBUG > 1
+ fprintf(stderr, " AFTER: node->parent=%p\n", node->parent);
+ if (parent)
+ {
+ fprintf(stderr, " AFTER: parent->child=%p\n", parent->child);
+ fprintf(stderr, " AFTER: parent->last_child=%p\n", parent->last_child);
+ fprintf(stderr, " AFTER: parent->prev=%p\n", parent->prev);
+ fprintf(stderr, " AFTER: parent->next=%p\n", parent->next);
+ }
+#endif /* DEBUG > 1 */
+}
+
+
+/*
+ * 'mxmlDelete()' - Delete a node and all of its children.
+ *
+ * If the specified node has a parent, this function first removes the
+ * node from its parent using the mxmlRemove() function.
+ */
+
+void
+mxmlDelete(mxml_node_t *node) /* I - Node to delete */
+{
+ int i; /* Looping var */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlDelete(node=%p)\n", node);
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return;
+
+ /*
+ * Remove the node from its parent, if any...
+ */
+
+ mxmlRemove(node);
+
+ /*
+ * Delete children...
+ */
+
+ while (node->child)
+ mxmlDelete(node->child);
+
+ /*
+ * Now delete any node data...
+ */
+
+ switch (node->type)
+ {
+ case MXML_ELEMENT :
+ if (node->value.element.name)
+ free(node->value.element.name);
+
+ if (node->value.element.num_attrs)
+ {
+ for (i = 0; i < node->value.element.num_attrs; i ++)
+ {
+ if (node->value.element.attrs[i].name)
+ free(node->value.element.attrs[i].name);
+ if (node->value.element.attrs[i].value)
+ free(node->value.element.attrs[i].value);
+ }
+
+ free(node->value.element.attrs);
+ }
+ break;
+ case MXML_INTEGER :
+ /* Nothing to do */
+ break;
+ case MXML_OPAQUE :
+ if (node->value.opaque)
+ free(node->value.opaque);
+ break;
+ case MXML_REAL :
+ /* Nothing to do */
+ break;
+ case MXML_TEXT :
+ if (node->value.text.string)
+ free(node->value.text.string);
+ break;
+ case MXML_CUSTOM :
+ if (node->value.custom.data &&
+ node->value.custom.destroy)
+ (*(node->value.custom.destroy))(node->value.custom.data);
+ break;
+ default :
+ break;
+ }
+
+ /*
+ * Free this node...
+ */
+
+ free(node);
+}
+
+
+/*
+ * 'mxmlGetRefCount()' - Get the current reference (use) count for a node.
+ *
+ * The initial reference count of new nodes is 1. Use the @link mxmlRetain@
+ * and @link mxmlRelease@ functions to increment and decrement a node's
+ * reference count.
+ *
+ * @since Mini-XML 2.7@.
+ */
+
+int /* O - Reference count */
+mxmlGetRefCount(mxml_node_t *node) /* I - Node */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (0);
+
+ /*
+ * Return the reference count...
+ */
+
+ return (node->ref_count);
+}
+
+
+/*
+ * 'mxmlNewCDATA()' - Create a new CDATA node.
+ *
+ * The new CDATA node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * CDATA node has no parent. The data string must be nul-terminated and
+ * is copied into the new node. CDATA nodes use the MXML_ELEMENT type.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ const char *data) /* I - Data string */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n",
+ parent, data ? data : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!data)
+ return (NULL);
+
+ /*
+ * Create the node and set the name value...
+ */
+
+ if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
+ node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data);
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewCustom()' - Create a new custom data node.
+ *
+ * The new custom node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * element node has no parent. NULL can be passed when the data in the
+ * node is not dynamically allocated or is separately managed.
+ *
+ * @since Mini-XML 2.1@
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewCustom(
+ mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ void *data, /* I - Pointer to data */
+ mxml_custom_destroy_cb_t destroy) /* I - Function to destroy data */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent,
+ data, destroy);
+#endif /* DEBUG */
+
+ /*
+ * Create the node and set the value...
+ */
+
+ if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL)
+ {
+ node->value.custom.data = data;
+ node->value.custom.destroy = destroy;
+ }
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewElement()' - Create a new element node.
+ *
+ * The new element node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * element node has no parent.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ const char *name) /* I - Name of element */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent,
+ name ? name : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!name)
+ return (NULL);
+
+ /*
+ * Create the node and set the element name...
+ */
+
+ if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
+ node->value.element.name = strdup(name);
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewInteger()' - Create a new integer node.
+ *
+ * The new integer node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * integer node has no parent.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ int integer) /* I - Integer value */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer);
+#endif /* DEBUG */
+
+ /*
+ * Create the node and set the element name...
+ */
+
+ if ((node = mxml_new(parent, MXML_INTEGER)) != NULL)
+ node->value.integer = integer;
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewOpaque()' - Create a new opaque string.
+ *
+ * The new opaque node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * opaque node has no parent. The opaque string must be nul-terminated and
+ * is copied into the new node.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ const char *opaque) /* I - Opaque string */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent,
+ opaque ? opaque : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!opaque)
+ return (NULL);
+
+ /*
+ * Create the node and set the element name...
+ */
+
+ if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL)
+ node->value.opaque = strdup(opaque);
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewReal()' - Create a new real number node.
+ *
+ * The new real number node is added to the end of the specified parent's
+ * child list. The constant MXML_NO_PARENT can be used to specify that
+ * the new real number node has no parent.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ double real) /* I - Real number value */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real);
+#endif /* DEBUG */
+
+ /*
+ * Create the node and set the element name...
+ */
+
+ if ((node = mxml_new(parent, MXML_REAL)) != NULL)
+ node->value.real = real;
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewText()' - Create a new text fragment node.
+ *
+ * The new text node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * text node has no parent. The whitespace parameter is used to specify
+ * whether leading whitespace is present before the node. The text
+ * string must be nul-terminated and is copied into the new node.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewText(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
+ const char *string) /* I - String */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n",
+ parent, whitespace, string ? string : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!string)
+ return (NULL);
+
+ /*
+ * Create the node and set the text value...
+ */
+
+ if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
+ {
+ node->value.text.whitespace = whitespace;
+ node->value.text.string = strdup(string);
+ }
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewTextf()' - Create a new formatted text fragment node.
+ *
+ * The new text node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * text node has no parent. The whitespace parameter is used to specify
+ * whether leading whitespace is present before the node. The format
+ * string must be nul-terminated and is formatted into the new node.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
+ const char *format, /* I - Printf-style frmat string */
+ ...) /* I - Additional args as needed */
+{
+ mxml_node_t *node; /* New node */
+ va_list ap; /* Pointer to arguments */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n",
+ parent, whitespace, format ? format : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!format)
+ return (NULL);
+
+ /*
+ * Create the node and set the text value...
+ */
+
+ if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
+ {
+ va_start(ap, format);
+
+ node->value.text.whitespace = whitespace;
+ node->value.text.string = _mxml_vstrdupf(format, ap);
+
+ va_end(ap);
+ }
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlRemove()' - Remove a node from its parent.
+ *
+ * Does not free memory used by the node - use mxmlDelete() for that.
+ * This function does nothing if the node has no parent.
+ */
+
+void
+mxmlRemove(mxml_node_t *node) /* I - Node to remove */
+{
+#ifdef DEBUG
+ fprintf(stderr, "mxmlRemove(node=%p)\n", node);
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || !node->parent)
+ return;
+
+ /*
+ * Remove from parent...
+ */
+
+#if DEBUG > 1
+ fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent);
+ if (node->parent)
+ {
+ fprintf(stderr, " BEFORE: node->parent->child=%p\n", node->parent->child);
+ fprintf(stderr, " BEFORE: node->parent->last_child=%p\n", node->parent->last_child);
+ }
+ fprintf(stderr, " BEFORE: node->child=%p\n", node->child);
+ fprintf(stderr, " BEFORE: node->last_child=%p\n", node->last_child);
+ fprintf(stderr, " BEFORE: node->prev=%p\n", node->prev);
+ fprintf(stderr, " BEFORE: node->next=%p\n", node->next);
+#endif /* DEBUG > 1 */
+
+ if (node->prev)
+ node->prev->next = node->next;
+ else
+ node->parent->child = node->next;
+
+ if (node->next)
+ node->next->prev = node->prev;
+ else
+ node->parent->last_child = node->prev;
+
+ node->parent = NULL;
+ node->prev = NULL;
+ node->next = NULL;
+
+#if DEBUG > 1
+ fprintf(stderr, " AFTER: node->parent=%p\n", node->parent);
+ if (node->parent)
+ {
+ fprintf(stderr, " AFTER: node->parent->child=%p\n", node->parent->child);
+ fprintf(stderr, " AFTER: node->parent->last_child=%p\n", node->parent->last_child);
+ }
+ fprintf(stderr, " AFTER: node->child=%p\n", node->child);
+ fprintf(stderr, " AFTER: node->last_child=%p\n", node->last_child);
+ fprintf(stderr, " AFTER: node->prev=%p\n", node->prev);
+ fprintf(stderr, " AFTER: node->next=%p\n", node->next);
+#endif /* DEBUG > 1 */
+}
+
+
+/*
+ * 'mxmlNewXML()' - Create a new XML document tree.
+ *
+ * The "version" argument specifies the version number to put in the
+ * ?xml element node. If NULL, version 1.0 is assumed.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+mxml_node_t * /* O - New ?xml node */
+mxmlNewXML(const char *version) /* I - Version number to use */
+{
+ char element[1024]; /* Element text */
+
+
+ snprintf(element, sizeof(element), "?xml version=\"%s\" encoding=\"utf-8\"?",
+ version ? version : "1.0");
+
+ return (mxmlNewElement(NULL, element));
+}
+
+
+/*
+ * 'mxmlRelease()' - Release a node.
+ *
+ * When the reference count reaches zero, the node (and any children)
+ * is deleted via mxmlDelete().
+ *
+ * @since Mini-XML 2.3@
+ */
+
+int /* O - New reference count */
+mxmlRelease(mxml_node_t *node) /* I - Node */
+{
+ if (node)
+ {
+ if ((-- node->ref_count) <= 0)
+ {
+ mxmlDelete(node);
+ return (0);
+ }
+ else
+ return (node->ref_count);
+ }
+ else
+ return (-1);
+}
+
+
+/*
+ * 'mxmlRetain()' - Retain a node.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+int /* O - New reference count */
+mxmlRetain(mxml_node_t *node) /* I - Node */
+{
+ if (node)
+ return (++ node->ref_count);
+ else
+ return (-1);
+}
+
+
+/*
+ * 'mxml_new()' - Create a new node.
+ */
+
+static mxml_node_t * /* O - New node */
+mxml_new(mxml_node_t *parent, /* I - Parent node */
+ mxml_type_t type) /* I - Node type */
+{
+ mxml_node_t *node; /* New node */
+
+
+#if DEBUG > 1
+ fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type);
+#endif /* DEBUG > 1 */
+
+ /*
+ * Allocate memory for the node...
+ */
+
+ if ((node = calloc(1, sizeof(mxml_node_t))) == NULL)
+ {
+#if DEBUG > 1
+ fputs(" returning NULL\n", stderr);
+#endif /* DEBUG > 1 */
+
+ return (NULL);
+ }
+
+#if DEBUG > 1
+ fprintf(stderr, " returning %p\n", node);
+#endif /* DEBUG > 1 */
+
+ /*
+ * Set the node type...
+ */
+
+ node->type = type;
+ node->ref_count = 1;
+
+ /*
+ * Add to the parent if present...
+ */
+
+ if (parent)
+ mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
+
+ /*
+ * Return the new node...
+ */
+
+ return (node);
+}
+
+
+/*
+ * End of "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-private.c b/tools/gator/daemon/mxml/mxml-private.c
new file mode 100644
index 000000000000..72f3e2320c7c
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-private.c
@@ -0,0 +1,331 @@
+/*
+ * "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $"
+ *
+ * Private functions for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxml_error() - Display an error message.
+ * mxml_integer_cb() - Default callback for integer values.
+ * mxml_opaque_cb() - Default callback for opaque values.
+ * mxml_real_cb() - Default callback for real number values.
+ * _mxml_global() - Get global data.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "mxml-private.h"
+
+
+/*
+ * Some crazy people think that unloading a shared object is a good or safe
+ * thing to do. Unfortunately, most objects are simply *not* safe to unload
+ * and bad things *will* happen.
+ *
+ * The following mess of conditional code allows us to provide a destructor
+ * function in Mini-XML for our thread-global storage so that it can possibly
+ * be unloaded safely, although since there is no standard way to do so I
+ * can't even provide any guarantees that you can do it safely on all platforms.
+ *
+ * This code currently supports AIX, HP-UX, Linux, Mac OS X, Solaris, and
+ * Windows. It might work on the BSDs and IRIX, but I haven't tested that.
+ */
+
+#if defined(__sun) || defined(_AIX)
+# pragma fini(_mxml_fini)
+# define _MXML_FINI _mxml_fini
+#elif defined(__hpux)
+# pragma FINI _mxml_fini
+# define _MXML_FINI _mxml_fini
+#elif defined(__GNUC__) /* Linux and Mac OS X */
+# define _MXML_FINI __attribute((destructor)) _mxml_fini
+#else
+# define _MXML_FINI _fini
+#endif /* __sun */
+
+
+/*
+ * 'mxml_error()' - Display an error message.
+ */
+
+void
+mxml_error(const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Pointer to arguments */
+ char s[1024]; /* Message string */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!format)
+ return;
+
+ /*
+ * Format the error message string...
+ */
+
+ va_start(ap, format);
+
+ vsnprintf(s, sizeof(s), format, ap);
+
+ va_end(ap);
+
+ /*
+ * And then display the error message...
+ */
+
+ if (global->error_cb)
+ (*global->error_cb)(s);
+ else
+ fprintf(stderr, "mxml: %s\n", s);
+}
+
+
+/*
+ * 'mxml_ignore_cb()' - Default callback for ignored values.
+ */
+
+mxml_type_t /* O - Node type */
+mxml_ignore_cb(mxml_node_t *node) /* I - Current node */
+{
+ (void)node;
+
+ return (MXML_IGNORE);
+}
+
+
+/*
+ * 'mxml_integer_cb()' - Default callback for integer values.
+ */
+
+mxml_type_t /* O - Node type */
+mxml_integer_cb(mxml_node_t *node) /* I - Current node */
+{
+ (void)node;
+
+ return (MXML_INTEGER);
+}
+
+
+/*
+ * 'mxml_opaque_cb()' - Default callback for opaque values.
+ */
+
+mxml_type_t /* O - Node type */
+mxml_opaque_cb(mxml_node_t *node) /* I - Current node */
+{
+ (void)node;
+
+ return (MXML_OPAQUE);
+}
+
+
+/*
+ * 'mxml_real_cb()' - Default callback for real number values.
+ */
+
+mxml_type_t /* O - Node type */
+mxml_real_cb(mxml_node_t *node) /* I - Current node */
+{
+ (void)node;
+
+ return (MXML_REAL);
+}
+
+
+#ifdef HAVE_PTHREAD_H /**** POSIX threading ****/
+# include <pthread.h>
+
+static pthread_key_t _mxml_key = -1; /* Thread local storage key */
+static pthread_once_t _mxml_key_once = PTHREAD_ONCE_INIT;
+ /* One-time initialization object */
+static void _mxml_init(void);
+static void _mxml_destructor(void *g);
+
+
+/*
+ * '_mxml_destructor()' - Free memory used for globals...
+ */
+
+static void
+_mxml_destructor(void *g) /* I - Global data */
+{
+ free(g);
+}
+
+
+/*
+ * '_mxml_fini()' - Clean up when unloaded.
+ */
+
+static void
+_MXML_FINI(void)
+{
+ _mxml_global_t *global; /* Global data */
+
+
+ if (_mxml_key != -1)
+ {
+ if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) != NULL)
+ _mxml_destructor(global);
+
+ pthread_key_delete(_mxml_key);
+ _mxml_key = -1;
+ }
+}
+
+
+/*
+ * '_mxml_global()' - Get global data.
+ */
+
+_mxml_global_t * /* O - Global data */
+_mxml_global(void)
+{
+ _mxml_global_t *global; /* Global data */
+
+
+ pthread_once(&_mxml_key_once, _mxml_init);
+
+ if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) == NULL)
+ {
+ global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t));
+ pthread_setspecific(_mxml_key, global);
+
+ global->num_entity_cbs = 1;
+ global->entity_cbs[0] = _mxml_entity_cb;
+ global->wrap = 72;
+ }
+
+ return (global);
+}
+
+
+/*
+ * '_mxml_init()' - Initialize global data...
+ */
+
+static void
+_mxml_init(void)
+{
+ pthread_key_create(&_mxml_key, _mxml_destructor);
+}
+
+
+#elif defined(WIN32) && defined(MXML1_EXPORTS) /**** WIN32 threading ****/
+# include <windows.h>
+
+static DWORD _mxml_tls_index; /* Index for global storage */
+
+
+/*
+ * 'DllMain()' - Main entry for library.
+ */
+
+BOOL WINAPI /* O - Success/failure */
+DllMain(HINSTANCE hinst, /* I - DLL module handle */
+ DWORD reason, /* I - Reason */
+ LPVOID reserved) /* I - Unused */
+{
+ _mxml_global_t *global; /* Global data */
+
+
+ (void)hinst;
+ (void)reserved;
+
+ switch (reason)
+ {
+ case DLL_PROCESS_ATTACH : /* Called on library initialization */
+ if ((_mxml_tls_index = TlsAlloc()) == TLS_OUT_OF_INDEXES)
+ return (FALSE);
+ break;
+
+ case DLL_THREAD_DETACH : /* Called when a thread terminates */
+ if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL)
+ free(global);
+ break;
+
+ case DLL_PROCESS_DETACH : /* Called when library is unloaded */
+ if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL)
+ free(global);
+
+ TlsFree(_mxml_tls_index);
+ break;
+
+ default:
+ break;
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * '_mxml_global()' - Get global data.
+ */
+
+_mxml_global_t * /* O - Global data */
+_mxml_global(void)
+{
+ _mxml_global_t *global; /* Global data */
+
+
+ if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) == NULL)
+ {
+ global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t));
+
+ global->num_entity_cbs = 1;
+ global->entity_cbs[0] = _mxml_entity_cb;
+ global->wrap = 72;
+
+ TlsSetValue(_mxml_tls_index, (LPVOID)global);
+ }
+
+ return (global);
+}
+
+
+#else /**** No threading ****/
+/*
+ * '_mxml_global()' - Get global data.
+ */
+
+_mxml_global_t * /* O - Global data */
+_mxml_global(void)
+{
+ static _mxml_global_t global = /* Global data */
+ {
+ NULL, /* error_cb */
+ 1, /* num_entity_cbs */
+ { _mxml_entity_cb }, /* entity_cbs */
+ 72, /* wrap */
+ NULL, /* custom_load_cb */
+ NULL /* custom_save_cb */
+ };
+
+
+ return (&global);
+}
+#endif /* HAVE_PTHREAD_H */
+
+
+/*
+ * End of "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-private.h b/tools/gator/daemon/mxml/mxml-private.h
new file mode 100644
index 000000000000..8789e6c52cbd
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-private.h
@@ -0,0 +1,50 @@
+/*
+ * "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $"
+ *
+ * Private definitions for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * Global, per-thread data...
+ */
+
+typedef struct _mxml_global_s
+{
+ void (*error_cb)(const char *);
+ int num_entity_cbs;
+ int (*entity_cbs[100])(const char *name);
+ int wrap;
+ mxml_custom_load_cb_t custom_load_cb;
+ mxml_custom_save_cb_t custom_save_cb;
+} _mxml_global_t;
+
+
+/*
+ * Functions...
+ */
+
+extern _mxml_global_t *_mxml_global(void);
+extern int _mxml_entity_cb(const char *name);
+
+
+/*
+ * End of "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-search.c b/tools/gator/daemon/mxml/mxml-search.c
new file mode 100644
index 000000000000..f975af1543ca
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-search.c
@@ -0,0 +1,287 @@
+/*
+ * "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $"
+ *
+ * Search/navigation functions for Mini-XML, a small XML-like file
+ * parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlFindElement() - Find the named element.
+ * mxmlFindValue() - Find a value with the given path.
+ * mxmlWalkNext() - Walk to the next logical node in the tree.
+ * mxmlWalkPrev() - Walk to the previous logical node in the tree.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * 'mxmlFindElement()' - Find the named element.
+ *
+ * The search is constrained by the name, attribute name, and value; any
+ * NULL names or values are treated as wildcards, so different kinds of
+ * searches can be implemented by looking for all elements of a given name
+ * or all elements with a specific attribute. The descend argument determines
+ * whether the search descends into child nodes; normally you will use
+ * MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find
+ * additional direct descendents of the node. The top node argument
+ * constrains the search to a particular node's children.
+ */
+
+mxml_node_t * /* O - Element node or NULL */
+mxmlFindElement(mxml_node_t *node, /* I - Current node */
+ mxml_node_t *top, /* I - Top node */
+ const char *name, /* I - Element name or NULL for any */
+ const char *attr, /* I - Attribute name, or NULL for none */
+ const char *value, /* I - Attribute value, or NULL for any */
+ int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
+{
+ const char *temp; /* Current attribute value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || !top || (!attr && value))
+ return (NULL);
+
+ /*
+ * Start with the next node...
+ */
+
+ node = mxmlWalkNext(node, top, descend);
+
+ /*
+ * Loop until we find a matching element...
+ */
+
+ while (node != NULL)
+ {
+ /*
+ * See if this node matches...
+ */
+
+ if (node->type == MXML_ELEMENT &&
+ node->value.element.name &&
+ (!name || !strcmp(node->value.element.name, name)))
+ {
+ /*
+ * See if we need to check for an attribute...
+ */
+
+ if (!attr)
+ return (node); /* No attribute search, return it... */
+
+ /*
+ * Check for the attribute...
+ */
+
+ if ((temp = mxmlElementGetAttr(node, attr)) != NULL)
+ {
+ /*
+ * OK, we have the attribute, does it match?
+ */
+
+ if (!value || !strcmp(value, temp))
+ return (node); /* Yes, return it... */
+ }
+ }
+
+ /*
+ * No match, move on to the next node...
+ */
+
+ if (descend == MXML_DESCEND)
+ node = mxmlWalkNext(node, top, MXML_DESCEND);
+ else
+ node = node->next;
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlFindPath()' - Find a node with the given path.
+ *
+ * The "path" is a slash-separated list of element names. The name "*" is
+ * considered a wildcard for one or more levels of elements. For example,
+ * "foo/one/two", "bar/two/one", "*\/one", and so forth.
+ *
+ * The first child node of the found node is returned if the given node has
+ * children and the first child is a value node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t * /* O - Found node or NULL */
+mxmlFindPath(mxml_node_t *top, /* I - Top node */
+ const char *path) /* I - Path to element */
+{
+ mxml_node_t *node; /* Current node */
+ char element[256]; /* Current element name */
+ const char *pathsep; /* Separator in path */
+ int descend; /* mxmlFindElement option */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!top || !path || !*path)
+ return (NULL);
+
+ /*
+ * Search each element in the path...
+ */
+
+ node = top;
+ while (*path)
+ {
+ /*
+ * Handle wildcards...
+ */
+
+ if (!strncmp(path, "*/", 2))
+ {
+ path += 2;
+ descend = MXML_DESCEND;
+ }
+ else
+ descend = MXML_DESCEND_FIRST;
+
+ /*
+ * Get the next element in the path...
+ */
+
+ if ((pathsep = strchr(path, '/')) == NULL)
+ pathsep = path + strlen(path);
+
+ if (pathsep == path || (pathsep - path) >= sizeof(element))
+ return (NULL);
+
+ memcpy(element, path, pathsep - path);
+ element[pathsep - path] = '\0';
+
+ if (*pathsep)
+ path = pathsep + 1;
+ else
+ path = pathsep;
+
+ /*
+ * Search for the element...
+ */
+
+ if ((node = mxmlFindElement(node, node, element, NULL, NULL,
+ descend)) == NULL)
+ return (NULL);
+ }
+
+ /*
+ * If we get this far, return the node or its first child...
+ */
+
+ if (node->child && node->child->type != MXML_ELEMENT)
+ return (node->child);
+ else
+ return (node);
+}
+
+
+/*
+ * 'mxmlWalkNext()' - Walk to the next logical node in the tree.
+ *
+ * The descend argument controls whether the first child is considered
+ * to be the next node. The top node argument constrains the walk to
+ * the node's children.
+ */
+
+mxml_node_t * /* O - Next node or NULL */
+mxmlWalkNext(mxml_node_t *node, /* I - Current node */
+ mxml_node_t *top, /* I - Top node */
+ int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
+{
+ if (!node)
+ return (NULL);
+ else if (node->child && descend)
+ return (node->child);
+ else if (node == top)
+ return (NULL);
+ else if (node->next)
+ return (node->next);
+ else if (node->parent && node->parent != top)
+ {
+ node = node->parent;
+
+ while (!node->next)
+ if (node->parent == top || !node->parent)
+ return (NULL);
+ else
+ node = node->parent;
+
+ return (node->next);
+ }
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree.
+ *
+ * The descend argument controls whether the previous node's last child
+ * is considered to be the previous node. The top node argument constrains
+ * the walk to the node's children.
+ */
+
+mxml_node_t * /* O - Previous node or NULL */
+mxmlWalkPrev(mxml_node_t *node, /* I - Current node */
+ mxml_node_t *top, /* I - Top node */
+ int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
+{
+ if (!node || node == top)
+ return (NULL);
+ else if (node->prev)
+ {
+ if (node->prev->last_child && descend)
+ {
+ /*
+ * Find the last child under the previous node...
+ */
+
+ node = node->prev->last_child;
+
+ while (node->last_child)
+ node = node->last_child;
+
+ return (node);
+ }
+ else
+ return (node->prev);
+ }
+ else if (node->parent != top)
+ return (node->parent);
+ else
+ return (NULL);
+}
+
+
+/*
+ * End of "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-set.c b/tools/gator/daemon/mxml/mxml-set.c
new file mode 100644
index 000000000000..b0bd52790b2e
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-set.c
@@ -0,0 +1,349 @@
+/*
+ * "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $"
+ *
+ * Node set functions for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlSetCDATA() - Set the element name of a CDATA node.
+ * mxmlSetCustom() - Set the data and destructor of a custom data node.
+ * mxmlSetElement() - Set the name of an element node.
+ * mxmlSetInteger() - Set the value of an integer node.
+ * mxmlSetOpaque() - Set the value of an opaque node.
+ * mxmlSetReal() - Set the value of a real number node.
+ * mxmlSetText() - Set the value of a text node.
+ * mxmlSetTextf() - Set the value of a text node to a formatted string.
+ * mxmlSetUserData() - Set the user data pointer for a node.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * 'mxmlSetCDATA()' - Set the element name of a CDATA node.
+ *
+ * The node is not changed if it (or its first child) is not a CDATA element node.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetCDATA(mxml_node_t *node, /* I - Node to set */
+ const char *data) /* I - New data string */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ strncmp(node->value.element.name, "![CDATA[", 8) &&
+ node->child && node->child->type == MXML_ELEMENT &&
+ !strncmp(node->child->value.element.name, "![CDATA[", 8))
+ node = node->child;
+
+ if (!node || node->type != MXML_ELEMENT || !data ||
+ strncmp(node->value.element.name, "![CDATA[", 8))
+ return (-1);
+
+ /*
+ * Free any old element value and set the new value...
+ */
+
+ if (node->value.element.name)
+ free(node->value.element.name);
+
+ node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data);
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetCustom()' - Set the data and destructor of a custom data node.
+ *
+ * The node is not changed if it (or its first child) is not a custom node.
+ *
+ * @since Mini-XML 2.1@
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetCustom(
+ mxml_node_t *node, /* I - Node to set */
+ void *data, /* I - New data pointer */
+ mxml_custom_destroy_cb_t destroy) /* I - New destructor function */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_CUSTOM)
+ node = node->child;
+
+ if (!node || node->type != MXML_CUSTOM)
+ return (-1);
+
+ /*
+ * Free any old element value and set the new value...
+ */
+
+ if (node->value.custom.data && node->value.custom.destroy)
+ (*(node->value.custom.destroy))(node->value.custom.data);
+
+ node->value.custom.data = data;
+ node->value.custom.destroy = destroy;
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetElement()' - Set the name of an element node.
+ *
+ * The node is not changed if it is not an element node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetElement(mxml_node_t *node, /* I - Node to set */
+ const char *name) /* I - New name string */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT || !name)
+ return (-1);
+
+ /*
+ * Free any old element value and set the new value...
+ */
+
+ if (node->value.element.name)
+ free(node->value.element.name);
+
+ node->value.element.name = strdup(name);
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetInteger()' - Set the value of an integer node.
+ *
+ * The node is not changed if it (or its first child) is not an integer node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetInteger(mxml_node_t *node, /* I - Node to set */
+ int integer) /* I - Integer value */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_INTEGER)
+ node = node->child;
+
+ if (!node || node->type != MXML_INTEGER)
+ return (-1);
+
+ /*
+ * Set the new value and return...
+ */
+
+ node->value.integer = integer;
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetOpaque()' - Set the value of an opaque node.
+ *
+ * The node is not changed if it (or its first child) is not an opaque node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetOpaque(mxml_node_t *node, /* I - Node to set */
+ const char *opaque) /* I - Opaque string */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_OPAQUE)
+ node = node->child;
+
+ if (!node || node->type != MXML_OPAQUE || !opaque)
+ return (-1);
+
+ /*
+ * Free any old opaque value and set the new value...
+ */
+
+ if (node->value.opaque)
+ free(node->value.opaque);
+
+ node->value.opaque = strdup(opaque);
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetReal()' - Set the value of a real number node.
+ *
+ * The node is not changed if it (or its first child) is not a real number node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetReal(mxml_node_t *node, /* I - Node to set */
+ double real) /* I - Real number value */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_REAL)
+ node = node->child;
+
+ if (!node || node->type != MXML_REAL)
+ return (-1);
+
+ /*
+ * Set the new value and return...
+ */
+
+ node->value.real = real;
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetText()' - Set the value of a text node.
+ *
+ * The node is not changed if it (or its first child) is not a text node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetText(mxml_node_t *node, /* I - Node to set */
+ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
+ const char *string) /* I - String */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_TEXT)
+ node = node->child;
+
+ if (!node || node->type != MXML_TEXT || !string)
+ return (-1);
+
+ /*
+ * Free any old string value and set the new value...
+ */
+
+ if (node->value.text.string)
+ free(node->value.text.string);
+
+ node->value.text.whitespace = whitespace;
+ node->value.text.string = strdup(string);
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetTextf()' - Set the value of a text node to a formatted string.
+ *
+ * The node is not changed if it (or its first child) is not a text node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetTextf(mxml_node_t *node, /* I - Node to set */
+ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
+ const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Pointer to arguments */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_TEXT)
+ node = node->child;
+
+ if (!node || node->type != MXML_TEXT || !format)
+ return (-1);
+
+ /*
+ * Free any old string value and set the new value...
+ */
+
+ if (node->value.text.string)
+ free(node->value.text.string);
+
+ va_start(ap, format);
+
+ node->value.text.whitespace = whitespace;
+ node->value.text.string = _mxml_strdupf(format, ap);
+
+ va_end(ap);
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetUserData()' - Set the user data pointer for a node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetUserData(mxml_node_t *node, /* I - Node to set */
+ void *data) /* I - User data pointer */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (-1);
+
+ /*
+ * Set the user data pointer and return...
+ */
+
+ node->user_data = data;
+ return (0);
+}
+
+
+/*
+ * End of "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-string.c b/tools/gator/daemon/mxml/mxml-string.c
new file mode 100644
index 000000000000..6be42523f95c
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-string.c
@@ -0,0 +1,476 @@
+/*
+ * "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $"
+ *
+ * String functions for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * _mxml_snprintf() - Format a string.
+ * _mxml_strdup() - Duplicate a string.
+ * _mxml_strdupf() - Format and duplicate a string.
+ * _mxml_vsnprintf() - Format a string into a fixed size buffer.
+ * _mxml_vstrdupf() - Format and duplicate a string.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+
+
+/*
+ * The va_copy macro is part of C99, but many compilers don't implement it.
+ * Provide a "direct assignment" implmentation when va_copy isn't defined...
+ */
+
+#ifndef va_copy
+# ifdef __va_copy
+# define va_copy(dst,src) __va_copy(dst,src)
+# else
+# define va_copy(dst,src) memcpy(&dst, &src, sizeof(va_list))
+# endif /* __va_copy */
+#endif /* va_copy */
+
+
+#ifndef HAVE_SNPRINTF
+/*
+ * '_mxml_snprintf()' - Format a string.
+ */
+
+int /* O - Number of bytes formatted */
+_mxml_snprintf(char *buffer, /* I - Output buffer */
+ size_t bufsize, /* I - Size of output buffer */
+ const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Argument list */
+ int bytes; /* Number of bytes formatted */
+
+
+ va_start(ap, format);
+ bytes = vsnprintf(buffer, bufsize, format, ap);
+ va_end(ap);
+
+ return (bytes);
+}
+#endif /* !HAVE_SNPRINTF */
+
+
+/*
+ * '_mxml_strdup()' - Duplicate a string.
+ */
+
+#ifndef HAVE_STRDUP
+char * /* O - New string pointer */
+_mxml_strdup(const char *s) /* I - String to duplicate */
+{
+ char *t; /* New string pointer */
+
+
+ if (s == NULL)
+ return (NULL);
+
+ if ((t = malloc(strlen(s) + 1)) == NULL)
+ return (NULL);
+
+ return (strcpy(t, s));
+}
+#endif /* !HAVE_STRDUP */
+
+
+/*
+ * '_mxml_strdupf()' - Format and duplicate a string.
+ */
+
+char * /* O - New string pointer */
+_mxml_strdupf(const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Pointer to additional arguments */
+ char *s; /* Pointer to formatted string */
+
+
+ /*
+ * Get a pointer to the additional arguments, format the string,
+ * and return it...
+ */
+
+ va_start(ap, format);
+ s = _mxml_vstrdupf(format, ap);
+ va_end(ap);
+
+ return (s);
+}
+
+
+#ifndef HAVE_VSNPRINTF
+/*
+ * '_mxml_vsnprintf()' - Format a string into a fixed size buffer.
+ */
+
+int /* O - Number of bytes formatted */
+_mxml_vsnprintf(char *buffer, /* O - Output buffer */
+ size_t bufsize, /* O - Size of output buffer */
+ const char *format, /* I - Printf-style format string */
+ va_list ap) /* I - Pointer to additional arguments */
+{
+ char *bufptr, /* Pointer to position in buffer */
+ *bufend, /* Pointer to end of buffer */
+ sign, /* Sign of format width */
+ size, /* Size character (h, l, L) */
+ type; /* Format type character */
+ int width, /* Width of field */
+ prec; /* Number of characters of precision */
+ char tformat[100], /* Temporary format string for sprintf() */
+ *tptr, /* Pointer into temporary format */
+ temp[1024]; /* Buffer for formatted numbers */
+ char *s; /* Pointer to string */
+ int slen; /* Length of string */
+ int bytes; /* Total number of bytes needed */
+
+
+ /*
+ * Loop through the format string, formatting as needed...
+ */
+
+ bufptr = buffer;
+ bufend = buffer + bufsize - 1;
+ bytes = 0;
+
+ while (*format)
+ {
+ if (*format == '%')
+ {
+ tptr = tformat;
+ *tptr++ = *format++;
+
+ if (*format == '%')
+ {
+ if (bufptr && bufptr < bufend) *bufptr++ = *format;
+ bytes ++;
+ format ++;
+ continue;
+ }
+ else if (strchr(" -+#\'", *format))
+ {
+ *tptr++ = *format;
+ sign = *format++;
+ }
+ else
+ sign = 0;
+
+ if (*format == '*')
+ {
+ /*
+ * Get width from argument...
+ */
+
+ format ++;
+ width = va_arg(ap, int);
+
+ snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
+ tptr += strlen(tptr);
+ }
+ else
+ {
+ width = 0;
+
+ while (isdigit(*format & 255))
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ width = width * 10 + *format++ - '0';
+ }
+ }
+
+ if (*format == '.')
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ format ++;
+
+ if (*format == '*')
+ {
+ /*
+ * Get precision from argument...
+ */
+
+ format ++;
+ prec = va_arg(ap, int);
+
+ snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
+ tptr += strlen(tptr);
+ }
+ else
+ {
+ prec = 0;
+
+ while (isdigit(*format & 255))
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ prec = prec * 10 + *format++ - '0';
+ }
+ }
+ }
+ else
+ prec = -1;
+
+ if (*format == 'l' && format[1] == 'l')
+ {
+ size = 'L';
+
+ if (tptr < (tformat + sizeof(tformat) - 2))
+ {
+ *tptr++ = 'l';
+ *tptr++ = 'l';
+ }
+
+ format += 2;
+ }
+ else if (*format == 'h' || *format == 'l' || *format == 'L')
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ size = *format++;
+ }
+
+ if (!*format)
+ break;
+
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ type = *format++;
+ *tptr = '\0';
+
+ switch (type)
+ {
+ case 'E' : /* Floating point formats */
+ case 'G' :
+ case 'e' :
+ case 'f' :
+ case 'g' :
+ if ((width + 2) > sizeof(temp))
+ break;
+
+ sprintf(temp, tformat, va_arg(ap, double));
+
+ bytes += strlen(temp);
+
+ if (bufptr)
+ {
+ if ((bufptr + strlen(temp)) > bufend)
+ {
+ strncpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr = bufend;
+ }
+ else
+ {
+ strcpy(bufptr, temp);
+ bufptr += strlen(temp);
+ }
+ }
+ break;
+
+ case 'B' : /* Integer formats */
+ case 'X' :
+ case 'b' :
+ case 'd' :
+ case 'i' :
+ case 'o' :
+ case 'u' :
+ case 'x' :
+ if ((width + 2) > sizeof(temp))
+ break;
+
+#ifdef HAVE_LONG_LONG
+ if (size == 'L')
+ sprintf(temp, tformat, va_arg(ap, long long));
+ else
+#endif /* HAVE_LONG_LONG */
+ sprintf(temp, tformat, va_arg(ap, int));
+
+ bytes += strlen(temp);
+
+ if (bufptr)
+ {
+ if ((bufptr + strlen(temp)) > bufend)
+ {
+ strncpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr = bufend;
+ }
+ else
+ {
+ strcpy(bufptr, temp);
+ bufptr += strlen(temp);
+ }
+ }
+ break;
+
+ case 'p' : /* Pointer value */
+ if ((width + 2) > sizeof(temp))
+ break;
+
+ sprintf(temp, tformat, va_arg(ap, void *));
+
+ bytes += strlen(temp);
+
+ if (bufptr)
+ {
+ if ((bufptr + strlen(temp)) > bufend)
+ {
+ strncpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr = bufend;
+ }
+ else
+ {
+ strcpy(bufptr, temp);
+ bufptr += strlen(temp);
+ }
+ }
+ break;
+
+ case 'c' : /* Character or character array */
+ bytes += width;
+
+ if (bufptr)
+ {
+ if (width <= 1)
+ *bufptr++ = va_arg(ap, int);
+ else
+ {
+ if ((bufptr + width) > bufend)
+ width = bufend - bufptr;
+
+ memcpy(bufptr, va_arg(ap, char *), (size_t)width);
+ bufptr += width;
+ }
+ }
+ break;
+
+ case 's' : /* String */
+ if ((s = va_arg(ap, char *)) == NULL)
+ s = "(null)";
+
+ slen = strlen(s);
+ if (slen > width && prec != width)
+ width = slen;
+
+ bytes += width;
+
+ if (bufptr)
+ {
+ if ((bufptr + width) > bufend)
+ width = bufend - bufptr;
+
+ if (slen > width)
+ slen = width;
+
+ if (sign == '-')
+ {
+ strncpy(bufptr, s, (size_t)slen);
+ memset(bufptr + slen, ' ', (size_t)(width - slen));
+ }
+ else
+ {
+ memset(bufptr, ' ', (size_t)(width - slen));
+ strncpy(bufptr + width - slen, s, (size_t)slen);
+ }
+
+ bufptr += width;
+ }
+ break;
+
+ case 'n' : /* Output number of chars so far */
+ *(va_arg(ap, int *)) = bytes;
+ break;
+ }
+ }
+ else
+ {
+ bytes ++;
+
+ if (bufptr && bufptr < bufend)
+ *bufptr++ = *format;
+
+ format ++;
+ }
+ }
+
+ /*
+ * Nul-terminate the string and return the number of characters needed.
+ */
+
+ *bufptr = '\0';
+
+ return (bytes);
+}
+#endif /* !HAVE_VSNPRINTF */
+
+
+/*
+ * '_mxml_vstrdupf()' - Format and duplicate a string.
+ */
+
+char * /* O - New string pointer */
+_mxml_vstrdupf(const char *format, /* I - Printf-style format string */
+ va_list ap) /* I - Pointer to additional arguments */
+{
+ int bytes; /* Number of bytes required */
+ char *buffer, /* String buffer */
+ temp[256]; /* Small buffer for first vsnprintf */
+ va_list apcopy; /* Copy of argument list */
+
+
+ /*
+ * First format with a tiny buffer; this will tell us how many bytes are
+ * needed...
+ */
+
+ va_copy(apcopy, ap);
+ bytes = vsnprintf(temp, sizeof(temp), format, apcopy);
+
+ if (bytes < sizeof(temp))
+ {
+ /*
+ * Hey, the formatted string fits in the tiny buffer, so just dup that...
+ */
+
+ return (strdup(temp));
+ }
+
+ /*
+ * Allocate memory for the whole thing and reformat to the new, larger
+ * buffer...
+ */
+
+ if ((buffer = calloc(1, bytes + 1)) != NULL)
+ vsnprintf(buffer, bytes + 1, format, ap);
+
+ /*
+ * Return the new string...
+ */
+
+ return (buffer);
+}
+
+
+/*
+ * End of "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml.h b/tools/gator/daemon/mxml/mxml.h
new file mode 100644
index 000000000000..79c711f4c80f
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml.h
@@ -0,0 +1,329 @@
+/*
+ * "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $"
+ *
+ * Header file for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ */
+
+/*
+ * Prevent multiple inclusion...
+ */
+
+#ifndef _mxml_h_
+# define _mxml_h_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <stdio.h>
+# include <stdlib.h>
+# include <string.h>
+# include <ctype.h>
+# include <errno.h>
+
+
+/*
+ * Constants...
+ */
+
+# define MXML_TAB 8 /* Tabs every N columns */
+
+# define MXML_NO_CALLBACK 0 /* Don't use a type callback */
+# define MXML_INTEGER_CALLBACK mxml_integer_cb
+ /* Treat all data as integers */
+# define MXML_OPAQUE_CALLBACK mxml_opaque_cb
+ /* Treat all data as opaque */
+# define MXML_REAL_CALLBACK mxml_real_cb
+ /* Treat all data as real numbers */
+# define MXML_TEXT_CALLBACK 0 /* Treat all data as text */
+# define MXML_IGNORE_CALLBACK mxml_ignore_cb
+ /* Ignore all non-element content */
+
+# define MXML_NO_PARENT 0 /* No parent for the node */
+
+# define MXML_DESCEND 1 /* Descend when finding/walking */
+# define MXML_NO_DESCEND 0 /* Don't descend when finding/walking */
+# define MXML_DESCEND_FIRST -1 /* Descend for first find */
+
+# define MXML_WS_BEFORE_OPEN 0 /* Callback for before open tag */
+# define MXML_WS_AFTER_OPEN 1 /* Callback for after open tag */
+# define MXML_WS_BEFORE_CLOSE 2 /* Callback for before close tag */
+# define MXML_WS_AFTER_CLOSE 3 /* Callback for after close tag */
+
+# define MXML_ADD_BEFORE 0 /* Add node before specified node */
+# define MXML_ADD_AFTER 1 /* Add node after specified node */
+# define MXML_ADD_TO_PARENT NULL /* Add node relative to parent */
+
+
+/*
+ * Data types...
+ */
+
+typedef enum mxml_sax_event_e /**** SAX event type. ****/
+{
+ MXML_SAX_CDATA, /* CDATA node */
+ MXML_SAX_COMMENT, /* Comment node */
+ MXML_SAX_DATA, /* Data node */
+ MXML_SAX_DIRECTIVE, /* Processing directive node */
+ MXML_SAX_ELEMENT_CLOSE, /* Element closed */
+ MXML_SAX_ELEMENT_OPEN /* Element opened */
+} mxml_sax_event_t;
+
+typedef enum mxml_type_e /**** The XML node type. ****/
+{
+ MXML_IGNORE = -1, /* Ignore/throw away node @since Mini-XML 2.3@ */
+ MXML_ELEMENT, /* XML element with attributes */
+ MXML_INTEGER, /* Integer value */
+ MXML_OPAQUE, /* Opaque string */
+ MXML_REAL, /* Real value */
+ MXML_TEXT, /* Text fragment */
+ MXML_CUSTOM /* Custom data @since Mini-XML 2.1@ */
+} mxml_type_t;
+
+typedef void (*mxml_custom_destroy_cb_t)(void *);
+ /**** Custom data destructor ****/
+
+typedef void (*mxml_error_cb_t)(const char *);
+ /**** Error callback function ****/
+
+typedef struct mxml_attr_s /**** An XML element attribute value. @private@ ****/
+{
+ char *name; /* Attribute name */
+ char *value; /* Attribute value */
+} mxml_attr_t;
+
+typedef struct mxml_element_s /**** An XML element value. @private@ ****/
+{
+ char *name; /* Name of element */
+ int num_attrs; /* Number of attributes */
+ mxml_attr_t *attrs; /* Attributes */
+} mxml_element_t;
+
+typedef struct mxml_text_s /**** An XML text value. @private@ ****/
+{
+ int whitespace; /* Leading whitespace? */
+ char *string; /* Fragment string */
+} mxml_text_t;
+
+typedef struct mxml_custom_s /**** An XML custom value. @private@ ****/
+{
+ void *data; /* Pointer to (allocated) custom data */
+ mxml_custom_destroy_cb_t destroy; /* Pointer to destructor function */
+} mxml_custom_t;
+
+typedef union mxml_value_u /**** An XML node value. @private@ ****/
+{
+ mxml_element_t element; /* Element */
+ int integer; /* Integer number */
+ char *opaque; /* Opaque string */
+ double real; /* Real number */
+ mxml_text_t text; /* Text fragment */
+ mxml_custom_t custom; /* Custom data @since Mini-XML 2.1@ */
+} mxml_value_t;
+
+struct mxml_node_s /**** An XML node. @private@ ****/
+{
+ mxml_type_t type; /* Node type */
+ struct mxml_node_s *next; /* Next node under same parent */
+ struct mxml_node_s *prev; /* Previous node under same parent */
+ struct mxml_node_s *parent; /* Parent node */
+ struct mxml_node_s *child; /* First child node */
+ struct mxml_node_s *last_child; /* Last child node */
+ mxml_value_t value; /* Node value */
+ int ref_count; /* Use count */
+ void *user_data; /* User data */
+};
+
+typedef struct mxml_node_s mxml_node_t; /**** An XML node. ****/
+
+struct mxml_index_s /**** An XML node index. @private@ ****/
+{
+ char *attr; /* Attribute used for indexing or NULL */
+ int num_nodes; /* Number of nodes in index */
+ int alloc_nodes; /* Allocated nodes in index */
+ int cur_node; /* Current node */
+ mxml_node_t **nodes; /* Node array */
+};
+
+typedef struct mxml_index_s mxml_index_t;
+ /**** An XML node index. ****/
+
+typedef int (*mxml_custom_load_cb_t)(mxml_node_t *, const char *);
+ /**** Custom data load callback function ****/
+
+typedef char *(*mxml_custom_save_cb_t)(mxml_node_t *);
+ /**** Custom data save callback function ****/
+
+typedef int (*mxml_entity_cb_t)(const char *);
+ /**** Entity callback function */
+
+typedef mxml_type_t (*mxml_load_cb_t)(mxml_node_t *);
+ /**** Load callback function ****/
+
+typedef const char *(*mxml_save_cb_t)(mxml_node_t *, int);
+ /**** Save callback function ****/
+
+typedef void (*mxml_sax_cb_t)(mxml_node_t *, mxml_sax_event_t, void *);
+ /**** SAX callback function ****/
+
+
+/*
+ * C++ support...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+/*
+ * Prototypes...
+ */
+
+extern void mxmlAdd(mxml_node_t *parent, int where,
+ mxml_node_t *child, mxml_node_t *node);
+extern void mxmlDelete(mxml_node_t *node);
+extern void mxmlElementDeleteAttr(mxml_node_t *node,
+ const char *name);
+extern const char *mxmlElementGetAttr(mxml_node_t *node, const char *name);
+extern void mxmlElementSetAttr(mxml_node_t *node, const char *name,
+ const char *value);
+extern void mxmlElementSetAttrf(mxml_node_t *node, const char *name,
+ const char *format, ...)
+# ifdef __GNUC__
+__attribute__ ((__format__ (__printf__, 3, 4)))
+# endif /* __GNUC__ */
+;
+extern int mxmlEntityAddCallback(mxml_entity_cb_t cb);
+extern const char *mxmlEntityGetName(int val);
+extern int mxmlEntityGetValue(const char *name);
+extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb);
+extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top,
+ const char *name, const char *attr,
+ const char *value, int descend);
+extern mxml_node_t *mxmlFindPath(mxml_node_t *node, const char *path);
+extern const char *mxmlGetCDATA(mxml_node_t *node);
+extern const void *mxmlGetCustom(mxml_node_t *node);
+extern const char *mxmlGetElement(mxml_node_t *node);
+extern mxml_node_t *mxmlGetFirstChild(mxml_node_t *node);
+extern int mxmlGetInteger(mxml_node_t *node);
+extern mxml_node_t *mxmlGetLastChild(mxml_node_t *node);
+extern mxml_node_t *mxmlGetNextSibling(mxml_node_t *node);
+extern const char *mxmlGetOpaque(mxml_node_t *node);
+extern mxml_node_t *mxmlGetParent(mxml_node_t *node);
+extern mxml_node_t *mxmlGetPrevSibling(mxml_node_t *node);
+extern double mxmlGetReal(mxml_node_t *node);
+extern int mxmlGetRefCount(mxml_node_t *node);
+extern const char *mxmlGetText(mxml_node_t *node, int *whitespace);
+extern mxml_type_t mxmlGetType(mxml_node_t *node);
+extern void *mxmlGetUserData(mxml_node_t *node);
+extern void mxmlIndexDelete(mxml_index_t *ind);
+extern mxml_node_t *mxmlIndexEnum(mxml_index_t *ind);
+extern mxml_node_t *mxmlIndexFind(mxml_index_t *ind,
+ const char *element,
+ const char *value);
+extern int mxmlIndexGetCount(mxml_index_t *ind);
+extern mxml_index_t *mxmlIndexNew(mxml_node_t *node, const char *element,
+ const char *attr);
+extern mxml_node_t *mxmlIndexReset(mxml_index_t *ind);
+extern mxml_node_t *mxmlLoadFd(mxml_node_t *top, int fd,
+ mxml_type_t (*cb)(mxml_node_t *));
+extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, FILE *fp,
+ mxml_type_t (*cb)(mxml_node_t *));
+extern mxml_node_t *mxmlLoadString(mxml_node_t *top, const char *s,
+ mxml_type_t (*cb)(mxml_node_t *));
+extern mxml_node_t *mxmlNewCDATA(mxml_node_t *parent, const char *string);
+extern mxml_node_t *mxmlNewCustom(mxml_node_t *parent, void *data,
+ mxml_custom_destroy_cb_t destroy);
+extern mxml_node_t *mxmlNewElement(mxml_node_t *parent, const char *name);
+extern mxml_node_t *mxmlNewInteger(mxml_node_t *parent, int integer);
+extern mxml_node_t *mxmlNewOpaque(mxml_node_t *parent, const char *opaque);
+extern mxml_node_t *mxmlNewReal(mxml_node_t *parent, double real);
+extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace,
+ const char *string);
+extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, int whitespace,
+ const char *format, ...)
+# ifdef __GNUC__
+__attribute__ ((__format__ (__printf__, 3, 4)))
+# endif /* __GNUC__ */
+;
+extern mxml_node_t *mxmlNewXML(const char *version);
+extern int mxmlRelease(mxml_node_t *node);
+extern void mxmlRemove(mxml_node_t *node);
+extern int mxmlRetain(mxml_node_t *node);
+extern char *mxmlSaveAllocString(mxml_node_t *node,
+ mxml_save_cb_t cb);
+extern int mxmlSaveFd(mxml_node_t *node, int fd,
+ mxml_save_cb_t cb);
+extern int mxmlSaveFile(mxml_node_t *node, FILE *fp,
+ mxml_save_cb_t cb);
+extern int mxmlSaveString(mxml_node_t *node, char *buffer,
+ int bufsize, mxml_save_cb_t cb);
+extern mxml_node_t *mxmlSAXLoadFd(mxml_node_t *top, int fd,
+ mxml_type_t (*cb)(mxml_node_t *),
+ mxml_sax_cb_t sax, void *sax_data);
+extern mxml_node_t *mxmlSAXLoadFile(mxml_node_t *top, FILE *fp,
+ mxml_type_t (*cb)(mxml_node_t *),
+ mxml_sax_cb_t sax, void *sax_data);
+extern mxml_node_t *mxmlSAXLoadString(mxml_node_t *top, const char *s,
+ mxml_type_t (*cb)(mxml_node_t *),
+ mxml_sax_cb_t sax, void *sax_data);
+extern int mxmlSetCDATA(mxml_node_t *node, const char *data);
+extern int mxmlSetCustom(mxml_node_t *node, void *data,
+ mxml_custom_destroy_cb_t destroy);
+extern void mxmlSetCustomHandlers(mxml_custom_load_cb_t load,
+ mxml_custom_save_cb_t save);
+extern int mxmlSetElement(mxml_node_t *node, const char *name);
+extern void mxmlSetErrorCallback(mxml_error_cb_t cb);
+extern int mxmlSetInteger(mxml_node_t *node, int integer);
+extern int mxmlSetOpaque(mxml_node_t *node, const char *opaque);
+extern int mxmlSetReal(mxml_node_t *node, double real);
+extern int mxmlSetText(mxml_node_t *node, int whitespace,
+ const char *string);
+extern int mxmlSetTextf(mxml_node_t *node, int whitespace,
+ const char *format, ...)
+# ifdef __GNUC__
+__attribute__ ((__format__ (__printf__, 3, 4)))
+# endif /* __GNUC__ */
+;
+extern int mxmlSetUserData(mxml_node_t *node, void *data);
+extern void mxmlSetWrapMargin(int column);
+extern mxml_node_t *mxmlWalkNext(mxml_node_t *node, mxml_node_t *top,
+ int descend);
+extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top,
+ int descend);
+
+
+/*
+ * Semi-private functions...
+ */
+
+extern void mxml_error(const char *format, ...);
+extern mxml_type_t mxml_ignore_cb(mxml_node_t *node);
+extern mxml_type_t mxml_integer_cb(mxml_node_t *node);
+extern mxml_type_t mxml_opaque_cb(mxml_node_t *node);
+extern mxml_type_t mxml_real_cb(mxml_node_t *node);
+
+
+/*
+ * C++ support...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_mxml_h_ */
+
+
+/*
+ * End of "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $".
+ */
diff --git a/tools/lib/lk/debugfs.c b/tools/lib/lk/debugfs.c
index 099e7cd022e4..7c4347962353 100644
--- a/tools/lib/lk/debugfs.c
+++ b/tools/lib/lk/debugfs.c
@@ -5,7 +5,6 @@
#include <stdbool.h>
#include <sys/vfs.h>
#include <sys/mount.h>
-#include <linux/magic.h>
#include <linux/kernel.h>
#include "debugfs.h"
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 6fcb9de62340..8bcdf9e54089 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -21,6 +21,7 @@ const char *map_type__name[MAP__NR_TYPES] = {
static inline int is_anon_memory(const char *filename)
{
return !strcmp(filename, "//anon") ||
+ !strcmp(filename, "/dev/zero (deleted)") ||
!strcmp(filename, "/anon_hugepage (deleted)");
}